Commit 94470ad726
Unsigned
Layout: unified · split
.gitignore added +7
| @@ -0,0 +1,7 @@ | |||
| 1 | .DS_Store | ||
| 2 | .build/ | ||
| 3 | .swiftpm/ | ||
| 4 | node_modules/ | ||
| 5 | .wrangler/ | ||
| 6 | .dev.vars | ||
| 7 | *.p8 | ||
PLAN.org added +358
| @@ -0,0 +1,358 @@ | |||
| 1 | #+title: Moonrise | ||
| 2 | #+subtitle: concept and plan | ||
| 3 | |||
| 4 | * name | ||
| 5 | *Moonrise.* App Store name with a descriptor, e.g. "Moonrise: Sky and | ||
| 6 | Space", since the bare name is shared and reads as moon-only. | ||
| 7 | |||
| 8 | Existing apps (Oct 2026 search), all moon-times utilities: AVIA Moonrise | ||
| 9 | (id390606204, last updated 2021), Moonrise Moment (id6788237210), | ||
| 10 | Mooncaster. The descriptor and screenshots need to show the app covers | ||
| 11 | far more than the moon. | ||
| 12 | |||
| 13 | Reserved in App Store Connect 2026-10-17. USPTO search not done. | ||
| 14 | |||
| 15 | * Apple identifiers | ||
| 16 | | Item | Value | | ||
| 17 | |- | ||
| 18 | | Team ID | =ZCNAX3VL9D= | | ||
| 19 | | Bundle ID | =sh.krz.moonrise= | | ||
| 20 | | APNs key | "Moonrise APNs", key ID =644XS4F2RG=, Sandbox and Production. The .p8 is held outside the repo. | | ||
| 21 | |||
| 22 | * concept | ||
| 23 | An iPhone app for everything in space: what is overhead tonight, what the | ||
| 24 | sun is doing, what is launching, where our spacecraft are, and a | ||
| 25 | reference library for the planets, stars, galaxies and missions. | ||
| 26 | |||
| 27 | The lead on every tab is a picture computed from real data or a real | ||
| 28 | image: the sky above you, the solar system today, the sun this hour, a | ||
| 29 | rocket on its pad. Text sits underneath, a swipe or tap away. | ||
| 30 | |||
| 31 | Free of accounts. Works offline for anything that can be computed; | ||
| 32 | fetches only live feeds. | ||
| 33 | |||
| 34 | * design | ||
| 35 | After NASA's 1975 Graphics Standards Manual. | ||
| 36 | |||
| 37 | - Helvetica Neue (ships with iOS). Tabular figures for all numbers. | ||
| 38 | - Strict grid, flush left, black hairlines, numbered modules. | ||
| 39 | - Off-white and ink by day. Near-black with white line work at night. | ||
| 40 | - One red accent for alerts and emphasis. Not NASA's exact red, no NASA | ||
| 41 | insignia or worm logotype, nothing that reads as an official NASA app. | ||
| 42 | - Drawings are technical: orbits as orbits, dimension lines, to-scale | ||
| 43 | comparisons, Gantt-style mission timelines. | ||
| 44 | - Night mode: red-on-black for dark-adapted eyes, switched on | ||
| 45 | automatically after astronomical dusk while the Sky tab is open. | ||
| 46 | - Motion only where it carries data: planets moving at true relative | ||
| 47 | speeds, the moon's phase easing while scrubbing time. | ||
| 48 | |||
| 49 | Avoid: neon on black, glow, scan lines, hexagons, crosshairs, | ||
| 50 | monospaced readouts as decoration. | ||
| 51 | |||
| 52 | * screens | ||
| 53 | Five tabs. | ||
| 54 | |||
| 55 | ** 1. Sky (home) | ||
| 56 | - Full-screen sky for your location and time: horizon, stars to about | ||
| 57 | magnitude 6, constellation lines, planets, the moon at its true phase, | ||
| 58 | the sun below the horizon as a glow at the edge. | ||
| 59 | - Pans with the device (compass and gyroscope) or by drag. Pinch to zoom. | ||
| 60 | - Tap any object for a card: name, magnitude, rise and set, distance, | ||
| 61 | link to its Library page. | ||
| 62 | - Time scrubber: drag hours forward or back; tap to return to now. | ||
| 63 | - Alert chips across the top when relevant: aurora (Kp threshold for | ||
| 64 | your latitude), ISS pass within the hour, launch within the hour, | ||
| 65 | eclipse or conjunction today. | ||
| 66 | - Today sheet, pulled up from the bottom: | ||
| 67 | - Moon: phase, illumination, next quarter. | ||
| 68 | - Visible tonight: planets with direction, altitude, rise and set. | ||
| 69 | - ISS passes over the next 3 days. | ||
| 70 | - Next launch with countdown. | ||
| 71 | - Space weather: Kp now and forecast. | ||
| 72 | - Deep space: Voyager 1 distance and light time. | ||
| 73 | |||
| 74 | ** 2. System | ||
| 75 | - Top-down orrery of the solar system for today. Log-scaled distances by | ||
| 76 | default; toggle to true scale. | ||
| 77 | - Time slider from 1957 to 2050. Planets and spacecraft move together. | ||
| 78 | - Spacecraft drawn with their trajectories: Voyager 1 and 2, New | ||
| 79 | Horizons, Juno, Europa Clipper, JUICE, Psyche, Parker Solar Probe, | ||
| 80 | Lucy, and missions at Mars. | ||
| 81 | - Tap a planet or spacecraft for its Library page. | ||
| 82 | - Inset for Earth–Moon space: ISS, JWST at L2, lunar missions. | ||
| 83 | |||
| 84 | ** 3. Sun | ||
| 85 | - Latest SDO full-disk image, full width. Swipe between wavelengths: | ||
| 86 | 171 Å (corona), 304 Å (chromosphere), 193 Å, HMI visible light. | ||
| 87 | - Kp index: bar chart for the last 3 days and the 3-day forecast. | ||
| 88 | - Aurora oval for your hemisphere (OVATION model) with your location | ||
| 89 | marked. | ||
| 90 | - Solar flares: GOES X-ray flux for the last 24 hours, flare classes | ||
| 91 | marked. | ||
| 92 | - Plain-language summary in one line: "Quiet", "Minor storm likely | ||
| 93 | tonight", and similar. | ||
| 94 | |||
| 95 | ** 4. Launches | ||
| 96 | - Upcoming launches, each led by the rocket's silhouette drawn to a | ||
| 97 | common scale. | ||
| 98 | - Detail: vehicle, mission, payload, orbit, pad on a map, countdown, | ||
| 99 | launch window, webcast link. | ||
| 100 | - Live Activity from T−1 hour through orbit insertion. | ||
| 101 | - Past launches with outcome. | ||
| 102 | |||
| 103 | ** 5. Library | ||
| 104 | Every page leads with a drawing or image; facts follow as a spec sheet. | ||
| 105 | |||
| 106 | - Solar system: the sun, 8 planets, dwarf planets, major moons. Each has | ||
| 107 | a dimensioned drawing, a to-scale comparison with Earth, a facts grid, | ||
| 108 | the missions that visited on a timeline, and a short essay. | ||
| 109 | - Constellations: all 88. Modern line figure, the plate from a | ||
| 110 | historical atlas (Bayer, Hevelius, Bode), bright stars, best season, | ||
| 111 | where it is tonight. | ||
| 112 | - Stars: bright named stars with type, distance, colour and the origin | ||
| 113 | of the name. | ||
| 114 | - Deep sky: the 110 Messier objects, then well-known NGC objects, with | ||
| 115 | Hubble, Webb or ground images and a scale bar. | ||
| 116 | - Spacecraft and missions: active and historic. Status, location, | ||
| 117 | instruments, findings, timeline. | ||
| 118 | - Later sections: exoplanets, rockets side by side to scale, the history | ||
| 119 | of exploration as a browsable timeline, a scale view from Earth to the | ||
| 120 | observable universe. | ||
| 121 | |||
| 122 | Search across all of it from the Library tab. | ||
| 123 | |||
| 124 | * widgets, Live Activities, notifications | ||
| 125 | - Lock Screen: moon phase; Kp; next launch countdown. | ||
| 126 | - Home Screen: tonight's planets; Voyager 1 distance; today's sun image. | ||
| 127 | - Live Activity: launch countdown through orbit. | ||
| 128 | - Notifications, each off by default and opt-in: aurora possible at your | ||
| 129 | latitude; launch in one hour; ISS visible pass in 15 minutes. | ||
| 130 | |||
| 131 | * data sources | ||
| 132 | Checked 2026-10-17 against primary sources unless marked unconfirmed. | ||
| 133 | |||
| 134 | | Source | Use | Terms and limits | | ||
| 135 | |- | ||
| 136 | | Astronomy Engine v2.1.19 (Don Cross) | Sun, moon, planet positions; rise, set, phases, eclipses, conjunctions | MIT, keep notice. No Swift package; C source vendored as an SPM target. Last release 2023-12. | | ||
| 137 | | Yale Bright Star Catalogue (VizieR V/50) | 9,096 stars with V mag, bundled | No licence in ReadMe; VizieR says "free in a scientific context", check per catalogue. US-government origin (NSSDC); public domain unconfirmed. | | ||
| 138 | | HYG database | Star names, distances | CC BY-SA 4.0; use only if BSC5 is not enough | | ||
| 139 | | IAU boundaries (VizieR VI/49) | Boundaries | Same VizieR ambiguity as BSC5 | | ||
| 140 | | Constellation lines: van der Sluys ConstellationLines v1.4 | Stick figures by HR number, bundled | CC BY 4.0. Credit: "Constellation lines: Marc van der Sluys, ConstellationLines (doi:10.5281/zenodo.10397192), CC BY 4.0." Stellarium (CC BY-SA, AGPL repo) and d3-celestial (no data licence) rejected. | | ||
| 141 | | OpenNGC | Messier and NGC object data | CC BY-SA 4.0; ShareAlike on the data file. Decide before M5. | | ||
| 142 | | NOAA SWPC JSON | Kp (=json/planetary_k_index_1m.json=), Kp forecast (=products/noaa-planetary-k-index-forecast.json=, array of objects), OVATION (=json/ovation_aurora_latest.json=, ~900 KB), GOES X-ray (=json/goes/primary/xrays-6-hour.json=), alerts (=products/alerts.json=) | No terms page; US government. =cache-control: max-age=60=: poll at most once a minute. | | ||
| 143 | | NASA SDO | =assets/img/latest/latest_{512,1024,...}_{0171,0193,0304,HMIIC}.jpg= | Not copyrighted unless noted. Credit: "Courtesy of NASA/SDO and the AIA, EVE, and HMI science teams." | | ||
| 144 | | JPL Horizons API | Spacecraft positions (Voyager 1 = -31) | No published limit; errors come back as HTTP 200, check payload. Cache server-side. | | ||
| 145 | | CelesTrak GP | =celestrak.org/NORAD/elements/gp.php?CATNR=25544&FORMAT=JSON= | Updates every 2 h; one download per update. >50 HTTP errors in 2 h blocks the IP. Fetch only from the Worker; stop on any non-200. | | ||
| 146 | | Launch Library 2 v2.3.0 | =ll.thespacedevs.com/2.3.0/= | 15 requests/hour anonymous (confirmed). Higher tiers via Patreon; price, commercial and attribution terms unpublished. Ask The Space Devs before launch. | | ||
| 147 | | NASA Image and Video Library | Planet, mission and spacecraft images | Generally not copyrighted; acknowledge NASA. Insignia and logotype protected; no implied endorsement; no current astronauts in promotion. | | ||
| 148 | | ESA/Hubble, ESA/Webb, ESO | Deep-sky images | CC BY 4.0; full credit line visible next to each image; note modifications. ESO: no commercial use of identifiable people. | | ||
| 149 | | USGS Astrogeology | Planet and moon maps | Public domain | | ||
| 150 | | NASA Exoplanet Archive | Exoplanets (later) | Public; bundle a snapshot, refresh periodically | | ||
| 151 | | Historical star atlases (LoC, Linda Hall Library, Wikimedia scans) | Constellation plates | Works public domain; check each scan's terms | | ||
| 152 | |||
| 153 | Track source and licence per image in the content bundle from day one. | ||
| 154 | |||
| 155 | * architecture | ||
| 156 | - iOS 26+, SwiftUI, Swift 6 language mode. iPhone first; iPad layout in | ||
| 157 | v1.1. | ||
| 158 | - Packages: | ||
| 159 | - =SkyEngine=: Swift wrapper over Astronomy Engine's C library. | ||
| 160 | Coordinates, rise and set, phases, events, kō-style solar longitude | ||
| 161 | (shared with the parked seventy-two app). | ||
| 162 | - =SkyRenderer=: SwiftUI Canvas first; move to Metal if 9,000 stars | ||
| 163 | plus lines cannot hold 60 fps while panning. | ||
| 164 | - =Feeds=: typed clients for SWPC, SDO, CelesTrak, the backend. Cached | ||
| 165 | on disk with per-feed lifetimes. | ||
| 166 | - =Library=: bundled content (JSON plus images), versioned, updatable | ||
| 167 | from the backend without an app release. | ||
| 168 | - Backend on Cloudflare; see [[*backend][backend]]. | ||
| 169 | - No accounts. Device registers an APNs token and a coarse latitude band | ||
| 170 | for aurora alerts, nothing else. | ||
| 171 | |||
| 172 | * backend | ||
| 173 | Only what the app cannot do alone: cache rate-limited feeds (Launch | ||
| 174 | Library 2, Horizons), send pushes (aurora, launches), drive launch Live | ||
| 175 | Activities, ship Library content updates. Positions, phases and ISS passes | ||
| 176 | are computed on device; ISS pass alerts are local notifications. SWPC and | ||
| 177 | SDO are public, so the app can fetch them directly if the backend is down. | ||
| 178 | |||
| 179 | ** Cloudflare products | ||
| 180 | | Product | Role | | ||
| 181 | |- | ||
| 182 | | Workers (TypeScript) | JSON API; APNs sender | | ||
| 183 | | Cron Triggers | Feed polling | | ||
| 184 | | D1 | Devices, launches, alert dedupe, Live Activity tokens, ephemeris, content versions | | ||
| 185 | | Workers Cache | Cache GET responses (=/v1/today= etc.) | | ||
| 186 | | Queues | Push fan-out with retries, decoupled from polling | | ||
| 187 | | R2 | Library content packs, widget-sized SDO thumbnails | | ||
| 188 | | Workers secrets | APNs .p8 key, key ID, team ID; Launch Library 2 key | | ||
| 189 | | Workers Rate Limiting | Per-IP limits on device registration | | ||
| 190 | |||
| 191 | ** APNs from Workers | ||
| 192 | APNs requires HTTP/2. A Cloudflare engineer stated in workerd issue #4841 | ||
| 193 | (2025-08-20) that production requests pass through Cloudflare's proxy, | ||
| 194 | which upgrades them to HTTP/2; workerd itself, and so =wrangler dev=, does | ||
| 195 | not support HTTP/2 and there are no plans to add it. Cloudflare's docs do | ||
| 196 | not state this. Open-source senders rely on it (e.g. | ||
| 197 | FiveSheepCo/cloudflare-apns2). Containers (Workers Paid) can run an HTTP/2 | ||
| 198 | sender as a fallback. Spike: =spikes/apns-worker=. | ||
| 199 | |||
| 200 | - Sign the ES256 provider JWT with WebCrypto; reuse it for 20–60 minutes | ||
| 201 | as Apple requires. | ||
| 202 | - Test pushes against a deployed staging Worker using the sandbox APNs | ||
| 203 | host, never from local dev. | ||
| 204 | - Keep the sender behind the push Queue so it can move to Cloudflare | ||
| 205 | Containers (a small Go or Python sender) if Workers' HTTP/2 stops | ||
| 206 | working, without touching the rest. | ||
| 207 | - Delete tokens on HTTP 410. | ||
| 208 | |||
| 209 | ** jobs | ||
| 210 | | Cron | Job | | ||
| 211 | |- | ||
| 212 | | =*/5 * * * *= | SWPC: 1-minute Kp, Kp forecast, OVATION aurora, alerts; run aurora engine | | ||
| 213 | | =*/10 * * * *= | Launch Library 2 upcoming list | | ||
| 214 | | =* * * * *= | If a launch is within 1 h: poll it (needs paid LL2 key); push Live Activity updates on status change | | ||
| 215 | | =0 */4 * * *= | CelesTrak ISS and bright satellites | | ||
| 216 | | =15 3 * * *= | JPL Horizons daily positions for ~20 spacecraft | | ||
| 217 | |||
| 218 | The anonymous Launch Library 2 tier is 15 requests/hour; the 1-minute job | ||
| 219 | needs a key. CelesTrak: never more than once per 2-hour update. | ||
| 220 | |||
| 221 | ** data (D1) | ||
| 222 | - =devices=: APNs token, environment (sandbox/production), 5° | ||
| 223 | geomagnetic latitude band, UTC offset, preferences (aurora threshold, | ||
| 224 | launch alerts, quiet hours), last seen. | ||
| 225 | - =launches=: normalized LL2 cache plus raw JSON. | ||
| 226 | - =feed_snapshots=: latest result per feed with fetch time and status. | ||
| 227 | - =alerts_sent=: dedupe key (type, band, storm event). | ||
| 228 | - =live_activity_tokens=: launch, update token, device; push-to-start | ||
| 229 | tokens separately. | ||
| 230 | - =spacecraft_ephemeris=: spacecraft, date, position. | ||
| 231 | - =content_versions=: pack, version, hash, R2 key. | ||
| 232 | |||
| 233 | ** API | ||
| 234 | | Method and path | Purpose | | ||
| 235 | |- | ||
| 236 | | =GET /v1/today= | One small payload: next launches, space weather summary, active alerts, spacecraft positions, content version. ETag. | | ||
| 237 | | =GET /v1/launches=, =GET /v1/launches/{id}= | List, detail | | ||
| 238 | | =GET /v1/spacecraft?ids=&from=&to== | Recent positions; full history ships in the app | | ||
| 239 | | =GET /v1/content/manifest=, =GET /v1/content/{pack}= | Library updates from R2 | | ||
| 240 | | =POST/PATCH/DELETE /v1/devices[/{token}]= | Register, preferences, unregister | | ||
| 241 | | =POST /v1/live-activities= | Register an update token for a launch | | ||
| 242 | | =GET /healthz= | Liveness and per-feed staleness | | ||
| 243 | |||
| 244 | ** alerts | ||
| 245 | - Aurora: from Kp forecast and OVATION, find the latitude bands that can | ||
| 246 | see aurora; notify devices in those bands where it is dark, outside | ||
| 247 | quiet hours; at most one per band per storm. | ||
| 248 | - Launches: "launch in one hour" to opted-in devices; push-to-start the | ||
| 249 | Live Activity (iOS 17.2+); update on countdown, hold, liftoff, staging, | ||
| 250 | orbit or scrub; end 15 minutes after the outcome. | ||
| 251 | |||
| 252 | ** privacy and abuse | ||
| 253 | - Stored per device: push token, 5° latitude band, UTC offset, | ||
| 254 | preferences. No location, no accounts. | ||
| 255 | - v1: per-IP rate limits. Later: App Attest verification in the Worker. | ||
| 256 | |||
| 257 | ** operations | ||
| 258 | - Repo =moonrise-server= on gitbay; deploy with Wrangler from CI. | ||
| 259 | - Staging and production Workers with separate D1 databases and APNs | ||
| 260 | environments. | ||
| 261 | - Workers Logs for errors; feed staleness in =/healthz=. | ||
| 262 | - Likely Workers Paid plan (about $5/month) plus an optional Launch | ||
| 263 | Library 2 key. Check current pricing for Queues and D1 before launch. | ||
| 264 | |||
| 265 | * content | ||
| 266 | The Library is the largest piece of work. | ||
| 267 | |||
| 268 | - v1: sun, 8 planets, 5 dwarf planets, ~25 major moons, 88 | ||
| 269 | constellations, ~50 named stars, 110 Messier objects, ~40 missions. | ||
| 270 | About 320 pages. | ||
| 271 | - Write in one voice: short essay plus spec sheet per page. Start from | ||
| 272 | NASA public-domain text where useful, rewritten. | ||
| 273 | - Drawings for every planet and moon in the house style, generated from | ||
| 274 | data (radius, oblateness, rings, axial tilt) rather than drawn by hand. | ||
| 275 | |||
| 276 | * milestones | ||
| 277 | - *M0, spike.* Confirm every source, licence and rate limit above. | ||
| 278 | Astronomy Engine building from Swift with tests against Horizons. | ||
| 279 | Static sky render for one place and time. Reserve the name. | ||
| 280 | - *M1, Sky.* Live sky with device motion, object cards, time scrubber, | ||
| 281 | night mode, Today sheet. Moon and planets widgets. | ||
| 282 | - *M2, Sun.* SDO images, Kp, aurora oval, flares. Backend up; aurora | ||
| 283 | push. | ||
| 284 | - *M3, Launches.* List, detail, rocket silhouettes, Live Activity. | ||
| 285 | - *M4, System.* Orrery with time slider and spacecraft trajectories. | ||
| 286 | - *M5, Library v1.* Solar system, constellations, stars, Messier, | ||
| 287 | missions. Search. | ||
| 288 | - *1.0* = M1–M5, TestFlight, App Store. | ||
| 289 | |||
| 290 | ** M0 results (2026-10-17) | ||
| 291 | - Data sources: checked; see [[*data sources][data sources]]. Open: Launch Library 2 | ||
| 292 | commercial terms, OpenNGC ShareAlike, BSC5 licence statement. | ||
| 293 | - =Packages/MoonriseKit=: | ||
| 294 | - =CAstronomy=: Astronomy Engine v2.1.19 C source. | ||
| 295 | - =SkyEngine=: Swift wrapper. Tests against JPL Horizons (Chicago, | ||
| 296 | 2026-10-17 02:00 UT): apparent RA/Dec and airless alt/az for the Moon, | ||
| 297 | Mars, Jupiter, Saturn within 1′; heliocentric Jupiter and Saturn within | ||
| 298 | 1′ angular (Saturn 0.00045 AU, about 10″). | ||
| 299 | - =SkyRenderer=: stereographic projection, 8,404 stars to V 6.5, 88 | ||
| 300 | constellations, planets to mag 6, moon with phase, horizon. Scene | ||
| 301 | build 0.03 s, Canvas render of a 1179×2556 frame 0.09 s on an Apple | ||
| 302 | silicon Mac via =ImageRenderer=. Not measured on a device; panning at | ||
| 303 | 60 fps (16 ms per frame) is unproven and likely needs culling, | ||
| 304 | pre-rendered star layers or Metal. | ||
| 305 | - =spikes/apns-worker=: ES256 provider token via WebCrypto (unit-tested), | ||
| 306 | =/push= and a credential-free =/probe=. Deployed as | ||
| 307 | =moonrise-apns-spike=; =/probe= got APNs JSON responses (403 | ||
| 308 | =InvalidProviderToken= with an =apns-id=) on 3 of 3 calls, so the | ||
| 309 | deployed Worker reaches the APNs sandbox over HTTP/2. Worker deleted | ||
| 310 | after the test. A real push still needs an APNs key and a device token. | ||
| 311 | - Name reserved in App Store Connect; APNs key created (key ID 644XS4F2RG). | ||
| 312 | |||
| 313 | * roadmap | ||
| 314 | ** v1: now | ||
| 315 | Sky, System, Sun, Launches, Library core (above). | ||
| 316 | |||
| 317 | ** v2: plan, track, more devices | ||
| 318 | - Events: eclipses, meteor showers, conjunctions, oppositions, each with | ||
| 319 | an observing plan. Eclipse path maps with local timings; the 2 August | ||
| 320 | 2027 total eclipse (Spain, North Africa, Egypt) is a launch target. | ||
| 321 | - Deep Space Network Now: which DSN dishes are talking to which | ||
| 322 | spacecraft. | ||
| 323 | - More satellites: Tiangong, Hubble, Starlink trains, bright passes. | ||
| 324 | - Observing log stored as plain text or Org. | ||
| 325 | - Mars: latest rover raw images, local time at each rover. | ||
| 326 | - Library: exoplanets, rockets to scale, exploration timeline, scale | ||
| 327 | view. | ||
| 328 | - iPad, Mac, Apple Watch complications. | ||
| 329 | |||
| 330 | ** v3: across time and cultures | ||
| 331 | - Sky cultures: Chinese, Polynesian, Arabic, Norse and other | ||
| 332 | constellations over the same stars. | ||
| 333 | - Historical sky for any date and place, with precession. | ||
| 334 | - 3D planets from USGS maps; immersive sky and orrery on Vision Pro. | ||
| 335 | - Guided seasonal sky tours and mission stories. | ||
| 336 | - Live coverage of major events (e.g. an Artemis landing) on the orrery | ||
| 337 | with a Live Activity. | ||
| 338 | - Maybe: smart-telescope pointing (Seestar and similar). | ||
| 339 | |||
| 340 | * risks | ||
| 341 | - Library scope: 320 pages is most of the effort. Ship 1.0 with fewer | ||
| 342 | pages before adding more. | ||
| 343 | - Sky render performance and compass accuracy; indoor and urban magnetic | ||
| 344 | interference makes device-motion panning unreliable, so drag must | ||
| 345 | always work. | ||
| 346 | - Third-party feed limits (Launch Library 2) and feed outages: show the | ||
| 347 | last good data with its age. | ||
| 348 | - NASA brand rules: the style is borrowed, the branding must not be. | ||
| 349 | - A crowded category (SkySafari, Night Sky, Stellarium, Next Spaceflight, | ||
| 350 | SpaceWeatherLive). The case for this app is all of it in one place, | ||
| 351 | designed well, not depth in any one area. | ||
| 352 | |||
| 353 | * open questions | ||
| 354 | - Pricing: free, paid up front, or free with a one-time unlock for the | ||
| 355 | Library and alerts. | ||
| 356 | - Fallback for APNs if Workers' HTTP/2 stops working: Containers sender | ||
| 357 | vs. a separate small VPS. The M0 probe worked, so this is a contingency. | ||
| 358 | - Whether the Sky tab needs AR camera overlay at all (current lean: no). | ||
Packages/MoonriseKit/Package.swift added +27
| @@ -0,0 +1,27 @@ | |||
| 1 | // swift-tools-version: 6.2 | ||
| 2 | import PackageDescription | ||
| 3 | |||
| 4 | let package = Package( | ||
| 5 | name: "MoonriseKit", | ||
| 6 | platforms: [.iOS(.v26), .macOS(.v26)], | ||
| 7 | products: [ | ||
| 8 | .library(name: "SkyEngine", targets: ["SkyEngine"]), | ||
| 9 | .library(name: "SkyRenderer", targets: ["SkyRenderer"]), | ||
| 10 | ], | ||
| 11 | targets: [ | ||
| 12 | // Astronomy Engine v2.1.19 (MIT), https://github.com/cosinekitty/astronomy | ||
| 13 | .target( | ||
| 14 | name: "CAstronomy", | ||
| 15 | exclude: ["LICENSE"], | ||
| 16 | cSettings: [.unsafeFlags(["-w"])] | ||
| 17 | ), | ||
| 18 | .target(name: "SkyEngine", dependencies: ["CAstronomy"]), | ||
| 19 | .target( | ||
| 20 | name: "SkyRenderer", | ||
| 21 | dependencies: ["SkyEngine"], | ||
| 22 | resources: [.copy("Resources/bsc5.json"), .copy("Resources/constellations.json")] | ||
| 23 | ), | ||
| 24 | .testTarget(name: "SkyEngineTests", dependencies: ["SkyEngine"]), | ||
| 25 | .testTarget(name: "SkyRendererTests", dependencies: ["SkyRenderer"]), | ||
| 26 | ] | ||
| 27 | ) | ||
Packages/MoonriseKit/Sources/CAstronomy/LICENSE added +21
| @@ -0,0 +1,21 @@ | |||
| 1 | MIT License | ||
| 2 | |||
| 3 | Copyright (c) 2019-2023 Don Cross <cosinekitty@gmail.com> | ||
| 4 | |||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | of this software and associated documentation files (the "Software"), to deal | ||
| 7 | in the Software without restriction, including without limitation the rights | ||
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | copies of the Software, and to permit persons to whom the Software is | ||
| 10 | furnished to do so, subject to the following conditions: | ||
| 11 | |||
| 12 | The above copyright notice and this permission notice shall be included in all | ||
| 13 | copies or substantial portions of the Software. | ||
| 14 | |||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 21 | SOFTWARE. | ||
Packages/MoonriseKit/Sources/CAstronomy/astronomy.c added +13058
| @@ -0,0 +1,13058 @@ | |||
| 1 | /* | ||
| 2 | Astronomy Engine for C/C++. | ||
| 3 | https://github.com/cosinekitty/astronomy | ||
| 4 | |||
| 5 | MIT License | ||
| 6 | |||
| 7 | Copyright (c) 2019-2023 Don Cross <cosinekitty@gmail.com> | ||
| 8 | |||
| 9 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 10 | of this software and associated documentation files (the "Software"), to deal | ||
| 11 | in the Software without restriction, including without limitation the rights | ||
| 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 13 | copies of the Software, and to permit persons to whom the Software is | ||
| 14 | furnished to do so, subject to the following conditions: | ||
| 15 | |||
| 16 | The above copyright notice and this permission notice shall be included in all | ||
| 17 | copies or substantial portions of the Software. | ||
| 18 | |||
| 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 25 | SOFTWARE. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <math.h> | ||
| 29 | #include <stdint.h> | ||
| 30 | #include <stdio.h> | ||
| 31 | #include <stdlib.h> | ||
| 32 | #include <string.h> | ||
| 33 | |||
| 34 | #if !defined(ASTRONOMY_ENGINE_NO_CURRENT_TIME) | ||
| 35 | #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) | ||
| 36 | #include <sys/time.h> | ||
| 37 | #elif defined(_WIN32) | ||
| 38 | #define WIN32_LEAN_AND_MEAN | ||
| 39 | #include <windows.h> | ||
| 40 | #else | ||
| 41 | #include <time.h> | ||
| 42 | #endif | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #include "astronomy.h" | ||
| 46 | |||
| 47 | #ifdef __FAST_MATH__ | ||
| 48 | #error Astronomy Engine does not support "fast math" optimization because it causes incorrect behavior. See: https://github.com/cosinekitty/astronomy/issues/245 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | #ifdef __cplusplus | ||
| 52 | extern "C" { | ||
| 53 | #endif | ||
| 54 | |||
| 55 | /** @cond DOXYGEN_SKIP */ | ||
| 56 | #define PI 3.14159265358979323846 | ||
| 57 | |||
| 58 | #define PLUTO_NUM_STATES 51 | ||
| 59 | #define PLUTO_TIME_STEP 29200 | ||
| 60 | #define PLUTO_DT 146 | ||
| 61 | |||
| 62 | #define PLUTO_NSTEPS 201 | ||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | typedef enum | ||
| 67 | { | ||
| 68 | FROM_2000, | ||
| 69 | INTO_2000 | ||
| 70 | } | ||
| 71 | precess_dir_t; | ||
| 72 | |||
| 73 | typedef struct | ||
| 74 | { | ||
| 75 | double x; | ||
| 76 | double y; | ||
| 77 | double z; | ||
| 78 | } | ||
| 79 | terse_vector_t; | ||
| 80 | |||
| 81 | typedef struct | ||
| 82 | { | ||
| 83 | double tt; /* J2000 terrestrial time [days] */ | ||
| 84 | terse_vector_t r; /* position [au] */ | ||
| 85 | terse_vector_t v; /* velocity [au/day] */ | ||
| 86 | terse_vector_t a; /* acceleration [au/day^2] */ | ||
| 87 | } | ||
| 88 | body_grav_calc_t; | ||
| 89 | |||
| 90 | typedef struct | ||
| 91 | { | ||
| 92 | body_grav_calc_t step[PLUTO_NSTEPS]; | ||
| 93 | } | ||
| 94 | body_segment_t; | ||
| 95 | |||
| 96 | typedef struct | ||
| 97 | { | ||
| 98 | double tt; /* Terrestrial Time in J2000 days */ | ||
| 99 | terse_vector_t r; /* position [au] */ | ||
| 100 | terse_vector_t v; /* velocity [au/day] */ | ||
| 101 | } | ||
| 102 | body_state_t; | ||
| 103 | |||
| 104 | typedef struct | ||
| 105 | { | ||
| 106 | body_state_t Sun; | ||
| 107 | body_state_t Jupiter; | ||
| 108 | body_state_t Saturn; | ||
| 109 | body_state_t Uranus; | ||
| 110 | body_state_t Neptune; | ||
| 111 | } | ||
| 112 | major_bodies_t; | ||
| 113 | |||
| 114 | typedef struct | ||
| 115 | { | ||
| 116 | astro_time_t time; | ||
| 117 | body_state_t gravitators[1 + BODY_SUN]; | ||
| 118 | body_grav_calc_t *bodies; | ||
| 119 | } | ||
| 120 | gravsim_endpoint_t; | ||
| 121 | |||
| 122 | struct astro_grav_sim_s | ||
| 123 | { | ||
| 124 | astro_body_t originBody; | ||
| 125 | int numBodies; | ||
| 126 | gravsim_endpoint_t endpoint[2]; | ||
| 127 | gravsim_endpoint_t *prev; | ||
| 128 | gravsim_endpoint_t *curr; | ||
| 129 | }; | ||
| 130 | |||
| 131 | typedef struct | ||
| 132 | { | ||
| 133 | double ra; | ||
| 134 | double dec; | ||
| 135 | double dist; | ||
| 136 | } | ||
| 137 | stardef_t; | ||
| 138 | |||
| 139 | /* Mean obliquity of the J2000 ecliptic in radians. */ | ||
| 140 | #define OBLIQ_2000 0.40909260059599012 | ||
| 141 | #define COS_OBLIQ_2000 0.9174821430670688 | ||
| 142 | #define SIN_OBLIQ_2000 0.3977769691083922 | ||
| 143 | |||
| 144 | /** @endcond */ | ||
| 145 | |||
| 146 | #define NSTARS 8 | ||
| 147 | static stardef_t StarTable[NSTARS]; | ||
| 148 | |||
| 149 | #define GetStarPointer(body) (((body) >= BODY_STAR1) && ((body) <= BODY_STAR8) ? &StarTable[(body) - BODY_STAR1] : NULL) | ||
| 150 | |||
| 151 | static stardef_t *UserDefinedStar(astro_body_t body) | ||
| 152 | { | ||
| 153 | stardef_t *star = GetStarPointer(body); | ||
| 154 | if (star != NULL && star->dist > 0.0) | ||
| 155 | return star; | ||
| 156 | return NULL; | ||
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | /** | ||
| 161 | * @brief Assign equatorial coordinates to a user-defined star. | ||
| 162 | * | ||
| 163 | * Some Astronomy Engine functions allow their `body` parameter to | ||
| 164 | * be a user-defined fixed point in the sky, loosely called a "star". | ||
| 165 | * This function assigns a right ascension, declination, and distance | ||
| 166 | * to one of the eight user-defined stars `BODY_STAR1` .. `BODY_STAR8`. | ||
| 167 | * | ||
| 168 | * Stars are not valid until defined. Once defined, they retain their | ||
| 169 | * definition until re-defined by another call to `Astronomy_DefineStar`. | ||
| 170 | * | ||
| 171 | * @param body | ||
| 172 | * One of the eight user-defined star identifiers: `BODY_STAR1` .. `BODY_STAR8`. | ||
| 173 | * | ||
| 174 | * @param ra | ||
| 175 | * The right ascension to be assigned to the star, expressed in J2000 equatorial coordinates (EQJ). | ||
| 176 | * The value is in units of sidereal hours, and must be within the half-open range [0, 24). | ||
| 177 | * | ||
| 178 | * @param dec | ||
| 179 | * The declination to be assigned to the star, expressed in J2000 equatorial coordinates (EQJ). | ||
| 180 | * The value is in units of degrees north (positive) or south (negative) of the J2000 equator, | ||
| 181 | * and must be within the closed range [-90, +90]. | ||
| 182 | * | ||
| 183 | * @param distanceLightYears | ||
| 184 | * The distance between the star and the Sun, expressed in light-years. | ||
| 185 | * This value is used to calculate the tiny parallax shift as seen by an observer on Earth. | ||
| 186 | * If you don't know the distance to the star, using a large value like 1000 will generally work well. | ||
| 187 | * The minimum allowed distance is 1 light-year, which is required to provide certain internal optimizations. | ||
| 188 | * | ||
| 189 | * @return | ||
| 190 | * `ASTRO_SUCCESS` indicates the star has been defined. Any other value indicates an error, | ||
| 191 | * in which case no change has taken place to any of the star definitions. | ||
| 192 | */ | ||
| 193 | astro_status_t Astronomy_DefineStar(astro_body_t body, double ra, double dec, double distanceLightYears) | ||
| 194 | { | ||
| 195 | stardef_t *star = GetStarPointer(body); | ||
| 196 | |||
| 197 | if (star == NULL) | ||
| 198 | return ASTRO_INVALID_BODY; | ||
| 199 | |||
| 200 | if (!isfinite(ra) || ra < 0.0 || ra >= 24.0) | ||
| 201 | return ASTRO_INVALID_PARAMETER; | ||
| 202 | |||
| 203 | if (!isfinite(dec) || dec < -90.0 || dec > +90.0) | ||
| 204 | return ASTRO_INVALID_PARAMETER; | ||
| 205 | |||
| 206 | if (!isfinite(distanceLightYears) || distanceLightYears < 1.0) | ||
| 207 | return ASTRO_INVALID_PARAMETER; | ||
| 208 | |||
| 209 | star->ra = ra; | ||
| 210 | star->dec = dec; | ||
| 211 | star->dist = distanceLightYears * AU_PER_LY; | ||
| 212 | |||
| 213 | return ASTRO_SUCCESS; | ||
| 214 | } | ||
| 215 | |||
| 216 | static const terse_vector_t VecZero = { 0.0, 0.0, 0.0 }; | ||
| 217 | |||
| 218 | static terse_vector_t VecAdd(terse_vector_t a, terse_vector_t b) | ||
| 219 | { | ||
| 220 | terse_vector_t c; | ||
| 221 | c.x = a.x + b.x; | ||
| 222 | c.y = a.y + b.y; | ||
| 223 | c.z = a.z + b.z; | ||
| 224 | return c; | ||
| 225 | } | ||
| 226 | |||
| 227 | static void VecIncr(terse_vector_t *target, terse_vector_t source) | ||
| 228 | { | ||
| 229 | target->x += source.x; | ||
| 230 | target->y += source.y; | ||
| 231 | target->z += source.z; | ||
| 232 | } | ||
| 233 | |||
| 234 | static void VecDecr(terse_vector_t *target, terse_vector_t source) | ||
| 235 | { | ||
| 236 | target->x -= source.x; | ||
| 237 | target->y -= source.y; | ||
| 238 | target->z -= source.z; | ||
| 239 | } | ||
| 240 | |||
| 241 | static terse_vector_t VecMul(double s, terse_vector_t v) | ||
| 242 | { | ||
| 243 | terse_vector_t p; | ||
| 244 | p.x = s * v.x; | ||
| 245 | p.y = s * v.y; | ||
| 246 | p.z = s * v.z; | ||
| 247 | return p; | ||
| 248 | } | ||
| 249 | |||
| 250 | static void VecScale(terse_vector_t *target, double scalar) | ||
| 251 | { | ||
| 252 | target->x *= scalar; | ||
| 253 | target->y *= scalar; | ||
| 254 | target->z *= scalar; | ||
| 255 | } | ||
| 256 | |||
| 257 | static terse_vector_t VecRamp(terse_vector_t a, terse_vector_t b, double ramp) | ||
| 258 | { | ||
| 259 | terse_vector_t c; | ||
| 260 | c.x = (1-ramp)*a.x + ramp*b.x; | ||
| 261 | c.y = (1-ramp)*a.y + ramp*b.y; | ||
| 262 | c.z = (1-ramp)*a.z + ramp*b.z; | ||
| 263 | return c; | ||
| 264 | } | ||
| 265 | |||
| 266 | static terse_vector_t VecMean(terse_vector_t a, terse_vector_t b) | ||
| 267 | { | ||
| 268 | terse_vector_t c; | ||
| 269 | c.x = (a.x + b.x) / 2; | ||
| 270 | c.y = (a.y + b.y) / 2; | ||
| 271 | c.z = (a.z + b.z) / 2; | ||
| 272 | return c; | ||
| 273 | } | ||
| 274 | |||
| 275 | static astro_state_vector_t ExportState(body_state_t terse, astro_time_t time) | ||
| 276 | { | ||
| 277 | astro_state_vector_t state; | ||
| 278 | |||
| 279 | state.status = ASTRO_SUCCESS; | ||
| 280 | state.x = terse.r.x; | ||
| 281 | state.y = terse.r.y; | ||
| 282 | state.z = terse.r.z; | ||
| 283 | state.vx = terse.v.x; | ||
| 284 | state.vy = terse.v.y; | ||
| 285 | state.vz = terse.v.z; | ||
| 286 | state.t = time; | ||
| 287 | |||
| 288 | return state; | ||
| 289 | } | ||
| 290 | |||
| 291 | static astro_state_vector_t ExportGravCalc(body_grav_calc_t calc, astro_time_t time) | ||
| 292 | { | ||
| 293 | astro_state_vector_t state; | ||
| 294 | |||
| 295 | state.status = ASTRO_SUCCESS; | ||
| 296 | state.x = calc.r.x; | ||
| 297 | state.y = calc.r.y; | ||
| 298 | state.z = calc.r.z; | ||
| 299 | state.vx = calc.v.x; | ||
| 300 | state.vy = calc.v.y; | ||
| 301 | state.vz = calc.v.z; | ||
| 302 | state.t = time; | ||
| 303 | |||
| 304 | return state; | ||
| 305 | } | ||
| 306 | |||
| 307 | static const double DAYS_PER_TROPICAL_YEAR = 365.24217; | ||
| 308 | static const double ASEC360 = 1296000.0; | ||
| 309 | static const double ASEC2RAD = 4.848136811095359935899141e-6; | ||
| 310 | static const double PI2 = 2.0 * PI; | ||
| 311 | static const double ARC = 3600.0 * 180.0 / PI; /* arcseconds per radian */ | ||
| 312 | static const double SECONDS_PER_DAY = 24.0 * 3600.0; | ||
| 313 | static const double SOLAR_DAYS_PER_SIDEREAL_DAY = 0.9972695717592592; | ||
| 314 | static const double MEAN_SYNODIC_MONTH = 29.530588; /* average number of days for Moon to return to the same phase */ | ||
| 315 | static const double EARTH_ORBITAL_PERIOD = 365.256; | ||
| 316 | static const double NEPTUNE_ORBITAL_PERIOD = 60189.0; | ||
| 317 | |||
| 318 | /* | ||
| 319 | Degrees of refractive "lift" seen for objects near horizon. | ||
| 320 | More precisely, the angle below the horizon a point has to be, at sea level, | ||
| 321 | to appear to be exactly on the horizon. | ||
| 322 | If the ground plane is higher than sea level, this angle | ||
| 323 | needs to be corrected for decreased atmospheric density. | ||
| 324 | */ | ||
| 325 | static const double REFRACTION_NEAR_HORIZON = 34.0 / 60.0; | ||
| 326 | |||
| 327 | |||
| 328 | #define SUN_RADIUS_AU (SUN_RADIUS_KM / KM_PER_AU) | ||
| 329 | |||
| 330 | #define EARTH_MEAN_RADIUS_KM 6371.0 /* mean radius of the Earth's geoid, without atmosphere */ | ||
| 331 | #define EARTH_ATMOSPHERE_KM 88.0 /* effective atmosphere thickness for lunar eclipses. see: https://eclipse.gsfc.nasa.gov/LEcat5/shadow.html */ | ||
| 332 | #define EARTH_ECLIPSE_RADIUS_KM (EARTH_MEAN_RADIUS_KM + EARTH_ATMOSPHERE_KM) | ||
| 333 | #define EARTH_EQUATORIAL_RADIUS_AU (EARTH_EQUATORIAL_RADIUS_KM / KM_PER_AU) | ||
| 334 | |||
| 335 | #define MOON_MEAN_RADIUS_KM 1737.4 | ||
| 336 | #define MOON_EQUATORIAL_RADIUS_AU (MOON_EQUATORIAL_RADIUS_KM / KM_PER_AU) | ||
| 337 | #define MOON_POLAR_RADIUS_AU (MOON_POLAR_RADIUS_KM / KM_PER_AU) | ||
| 338 | |||
| 339 | /* The inclination of the moon's rotation axis to the ecliptic plane, in radians. */ | ||
| 340 | #define MOON_AXIS_INCLINATION_RADIANS (DEG2RAD * 1.543) | ||
| 341 | |||
| 342 | static const double ASEC180 = 180.0 * 60.0 * 60.0; /* arcseconds per 180 degrees (or pi radians) */ | ||
| 343 | static const double EARTH_MOON_MASS_RATIO = 81.30056; | ||
| 344 | |||
| 345 | /* | ||
| 346 | Masses of the Sun and planets, used for: | ||
| 347 | (1) Calculating the Solar System Barycenter | ||
| 348 | (2) Integrating the movement of Pluto | ||
| 349 | |||
| 350 | https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf | ||
| 351 | |||
| 352 | Page 10 in the above document describes the constants used in the DE405 ephemeris. | ||
| 353 | The following are GM values (gravity constant * mass) in [au^3 / day^2]. | ||
| 354 | This side-steps issues of not knowing the exact values of G and masses M[i]; | ||
| 355 | the products GM[i] are known extremely accurately. | ||
| 356 | */ | ||
| 357 | static const double SUN_GM = 0.2959122082855911e-03; | ||
| 358 | static const double MERCURY_GM = 0.4912547451450812e-10; | ||
| 359 | static const double VENUS_GM = 0.7243452486162703e-09; | ||
| 360 | static const double EARTH_GM = 0.8887692390113509e-09; | ||
| 361 | static const double MARS_GM = 0.9549535105779258e-10; | ||
| 362 | static const double JUPITER_GM = 0.2825345909524226e-06; | ||
| 363 | static const double SATURN_GM = 0.8459715185680659e-07; | ||
| 364 | static const double URANUS_GM = 0.1292024916781969e-07; | ||
| 365 | static const double NEPTUNE_GM = 0.1524358900784276e-07; | ||
| 366 | static const double PLUTO_GM = 0.2188699765425970e-11; | ||
| 367 | |||
| 368 | #define MOON_GM (EARTH_GM / EARTH_MOON_MASS_RATIO) | ||
| 369 | |||
| 370 | /** @cond DOXYGEN_SKIP */ | ||
| 371 | #define ASTRO_ARRAYSIZE(x) (sizeof(x) / sizeof(x[0])) | ||
| 372 | #define AU_PER_PARSEC (ASEC180 / PI) /* exact definition of how many AU = one parsec */ | ||
| 373 | #define Y2000_IN_MJD (T0 - MJD_BASIS) | ||
| 374 | /** @endcond */ | ||
| 375 | |||
| 376 | static astro_ecliptic_t RotateEquatorialToEcliptic(const double pos[3], double obliq_radians, astro_time_t time); | ||
| 377 | static int QuadInterp( | ||
| 378 | double tm, double dt, double fa, double fm, double fb, | ||
| 379 | double *t, double *df_dt); | ||
| 380 | |||
| 381 | static double LongitudeOffset(double diff) | ||
| 382 | { | ||
| 383 | double offset = diff; | ||
| 384 | |||
| 385 | while (offset <= -180.0) | ||
| 386 | offset += 360.0; | ||
| 387 | |||
| 388 | while (offset > 180.0) | ||
| 389 | offset -= 360.0; | ||
| 390 | |||
| 391 | return offset; | ||
| 392 | } | ||
| 393 | |||
| 394 | static double NormalizeLongitude(double lon) | ||
| 395 | { | ||
| 396 | while (lon < 0.0) | ||
| 397 | lon += 360.0; | ||
| 398 | |||
| 399 | while (lon >= 360.0) | ||
| 400 | lon -= 360.0; | ||
| 401 | |||
| 402 | return lon; | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | * @brief Calculates the length of the given vector. | ||
| 407 | * | ||
| 408 | * Calculates the non-negative length of the given vector. | ||
| 409 | * The length is expressed in the same units as the vector's components, | ||
| 410 | * usually astronomical units (AU). | ||
| 411 | * | ||
| 412 | * @param vector The vector whose length is to be calculated. | ||
| 413 | * @return The length of the vector. | ||
| 414 | */ | ||
| 415 | double Astronomy_VectorLength(astro_vector_t vector) | ||
| 416 | { | ||
| 417 | return sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z); | ||
| 418 | } | ||
| 419 | |||
| 420 | /** | ||
| 421 | * @brief Finds the name of a celestial body. | ||
| 422 | * @param body The celestial body whose name is to be found. | ||
| 423 | * @return The English-language name of the celestial body, or "" if the body is not valid. | ||
| 424 | */ | ||
| 425 | const char *Astronomy_BodyName(astro_body_t body) | ||
| 426 | { | ||
| 427 | switch (body) | ||
| 428 | { | ||
| 429 | case BODY_MERCURY: return "Mercury"; | ||
| 430 | case BODY_VENUS: return "Venus"; | ||
| 431 | case BODY_EARTH: return "Earth"; | ||
| 432 | case BODY_MARS: return "Mars"; | ||
| 433 | case BODY_JUPITER: return "Jupiter"; | ||
| 434 | case BODY_SATURN: return "Saturn"; | ||
| 435 | case BODY_URANUS: return "Uranus"; | ||
| 436 | case BODY_NEPTUNE: return "Neptune"; | ||
| 437 | case BODY_PLUTO: return "Pluto"; | ||
| 438 | case BODY_SUN: return "Sun"; | ||
| 439 | case BODY_MOON: return "Moon"; | ||
| 440 | case BODY_EMB: return "EMB"; | ||
| 441 | case BODY_SSB: return "SSB"; | ||
| 442 | default: return ""; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | /** | ||
| 447 | * @brief Returns the #astro_body_t value corresponding to the given English name. | ||
| 448 | * @param name One of the following strings: Sun, Moon, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, EMB, SSB. | ||
| 449 | * @return If `name` is one of the listed strings (case-sensitive), the returned value is the corresponding #astro_body_t value, otherwise it is `BODY_INVALID`. | ||
| 450 | */ | ||
| 451 | astro_body_t Astronomy_BodyCode(const char *name) | ||
| 452 | { | ||
| 453 | if (name != NULL) | ||
| 454 | { | ||
| 455 | if (!strcmp(name, "Mercury")) return BODY_MERCURY; | ||
| 456 | if (!strcmp(name, "Venus")) return BODY_VENUS; | ||
| 457 | if (!strcmp(name, "Earth")) return BODY_EARTH; | ||
| 458 | if (!strcmp(name, "Mars")) return BODY_MARS; | ||
| 459 | if (!strcmp(name, "Jupiter")) return BODY_JUPITER; | ||
| 460 | if (!strcmp(name, "Saturn")) return BODY_SATURN; | ||
| 461 | if (!strcmp(name, "Uranus")) return BODY_URANUS; | ||
| 462 | if (!strcmp(name, "Neptune")) return BODY_NEPTUNE; | ||
| 463 | if (!strcmp(name, "Pluto")) return BODY_PLUTO; | ||
| 464 | if (!strcmp(name, "Sun")) return BODY_SUN; | ||
| 465 | if (!strcmp(name, "Moon")) return BODY_MOON; | ||
| 466 | if (!strcmp(name, "EMB")) return BODY_EMB; | ||
| 467 | if (!strcmp(name, "SSB")) return BODY_SSB; | ||
| 468 | } | ||
| 469 | return BODY_INVALID; | ||
| 470 | } | ||
| 471 | |||
| 472 | /** | ||
| 473 | * @brief Returns 1 for planets that are farther from the Sun than the Earth is, 0 otherwise. | ||
| 474 | */ | ||
| 475 | static int IsSuperiorPlanet(astro_body_t body) | ||
| 476 | { | ||
| 477 | switch (body) | ||
| 478 | { | ||
| 479 | case BODY_MARS: | ||
| 480 | case BODY_JUPITER: | ||
| 481 | case BODY_SATURN: | ||
| 482 | case BODY_URANUS: | ||
| 483 | case BODY_NEPTUNE: | ||
| 484 | case BODY_PLUTO: | ||
| 485 | return 1; | ||
| 486 | |||
| 487 | default: | ||
| 488 | return 0; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | /** | ||
| 493 | * @brief Returns the average number of days it takes for a planet to orbit the Sun. | ||
| 494 | * @param body One of the planets: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, or Pluto. | ||
| 495 | * @return The mean orbital period of the body, or 0.0 if the `body` parameter is not valid. | ||
| 496 | */ | ||
| 497 | double Astronomy_PlanetOrbitalPeriod(astro_body_t body) | ||
| 498 | { | ||
| 499 | switch (body) | ||
| 500 | { | ||
| 501 | case BODY_MERCURY: return 87.969; | ||
| 502 | case BODY_VENUS: return 224.701; | ||
| 503 | case BODY_EARTH: return EARTH_ORBITAL_PERIOD; | ||
| 504 | case BODY_MARS: return 686.980; | ||
| 505 | case BODY_JUPITER: return 4332.589; | ||
| 506 | case BODY_SATURN: return 10759.22; | ||
| 507 | case BODY_URANUS: return 30685.4; | ||
| 508 | case BODY_NEPTUNE: return NEPTUNE_ORBITAL_PERIOD; | ||
| 509 | case BODY_PLUTO: return 90560.0; | ||
| 510 | default: return 0.0; /* invalid body */ | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | static astro_vector_t VecError(astro_status_t status, astro_time_t time) | ||
| 515 | { | ||
| 516 | astro_vector_t vec; | ||
| 517 | vec.x = vec.y = vec.z = NAN; | ||
| 518 | vec.t = time; | ||
| 519 | vec.status = status; | ||
| 520 | return vec; | ||
| 521 | } | ||
| 522 | |||
| 523 | static astro_state_vector_t StateVecError(astro_status_t status, astro_time_t time) | ||
| 524 | { | ||
| 525 | astro_state_vector_t vec; | ||
| 526 | vec.x = vec.y = vec.z = NAN; | ||
| 527 | vec.vx = vec.vy = vec.vz = NAN; | ||
| 528 | vec.t = time; | ||
| 529 | vec.status = status; | ||
| 530 | return vec; | ||
| 531 | } | ||
| 532 | |||
| 533 | static astro_spherical_t SphereError(astro_status_t status) | ||
| 534 | { | ||
| 535 | astro_spherical_t sphere; | ||
| 536 | sphere.status = status; | ||
| 537 | sphere.dist = sphere.lat = sphere.lon = NAN; | ||
| 538 | return sphere; | ||
| 539 | } | ||
| 540 | |||
| 541 | static astro_time_t TimeError(void) | ||
| 542 | { | ||
| 543 | astro_time_t time; | ||
| 544 | time.tt = time.ut = time.eps = time.psi = time.st = NAN; | ||
| 545 | return time; | ||
| 546 | } | ||
| 547 | |||
| 548 | static astro_equatorial_t EquError(astro_status_t status) | ||
| 549 | { | ||
| 550 | astro_equatorial_t equ; | ||
| 551 | equ.vec = VecError(status, TimeError()); | ||
| 552 | equ.ra = equ.dec = equ.dist = NAN; | ||
| 553 | equ.status = status; | ||
| 554 | return equ; | ||
| 555 | } | ||
| 556 | |||
| 557 | static astro_ecliptic_t EclError(astro_status_t status) | ||
| 558 | { | ||
| 559 | astro_ecliptic_t ecl; | ||
| 560 | ecl.status = status; | ||
| 561 | ecl.elon = ecl.elat = NAN; | ||
| 562 | ecl.vec = VecError(status, TimeError()); | ||
| 563 | return ecl; | ||
| 564 | } | ||
| 565 | |||
| 566 | static astro_angle_result_t AngleError(astro_status_t status) | ||
| 567 | { | ||
| 568 | astro_angle_result_t result; | ||
| 569 | result.status = status; | ||
| 570 | result.angle = NAN; | ||
| 571 | return result; | ||
| 572 | } | ||
| 573 | |||
| 574 | static astro_func_result_t FuncError(astro_status_t status) | ||
| 575 | { | ||
| 576 | astro_func_result_t result; | ||
| 577 | result.status = status; | ||
| 578 | result.value = NAN; | ||
| 579 | return result; | ||
| 580 | } | ||
| 581 | |||
| 582 | static astro_rotation_t RotationErr(astro_status_t status) | ||
| 583 | { | ||
| 584 | astro_rotation_t rotation; | ||
| 585 | int i, j; | ||
| 586 | |||
| 587 | rotation.status = status; | ||
| 588 | for (i=0; i<3; ++i) | ||
| 589 | for (j=0; j<3; ++j) | ||
| 590 | rotation.rot[i][j] = NAN; | ||
| 591 | |||
| 592 | return rotation; | ||
| 593 | } | ||
| 594 | |||
| 595 | static astro_moon_quarter_t MoonQuarterError(astro_status_t status) | ||
| 596 | { | ||
| 597 | astro_moon_quarter_t result; | ||
| 598 | result.status = status; | ||
| 599 | result.quarter = -1; | ||
| 600 | result.time = TimeError(); | ||
| 601 | return result; | ||
| 602 | } | ||
| 603 | |||
| 604 | static astro_elongation_t ElongError(astro_status_t status) | ||
| 605 | { | ||
| 606 | astro_elongation_t result; | ||
| 607 | |||
| 608 | result.status = status; | ||
| 609 | result.elongation = NAN; | ||
| 610 | result.ecliptic_separation = NAN; | ||
| 611 | result.time = TimeError(); | ||
| 612 | result.visibility = (astro_visibility_t)(-1); | ||
| 613 | |||
| 614 | return result; | ||
| 615 | } | ||
| 616 | |||
| 617 | static astro_hour_angle_t HourAngleError(astro_status_t status) | ||
| 618 | { | ||
| 619 | astro_hour_angle_t result; | ||
| 620 | |||
| 621 | result.status = status; | ||
| 622 | result.time = TimeError(); | ||
| 623 | result.hor.altitude = result.hor.azimuth = result.hor.dec = result.hor.ra = NAN; | ||
| 624 | |||
| 625 | return result; | ||
| 626 | } | ||
| 627 | |||
| 628 | static astro_illum_t IllumError(astro_status_t status) | ||
| 629 | { | ||
| 630 | astro_illum_t result; | ||
| 631 | |||
| 632 | result.status = status; | ||
| 633 | result.time = TimeError(); | ||
| 634 | result.mag = NAN; | ||
| 635 | result.phase_angle = NAN; | ||
| 636 | result.phase_fraction = NAN; | ||
| 637 | result.helio_dist = NAN; | ||
| 638 | result.ring_tilt = NAN; | ||
| 639 | |||
| 640 | return result; | ||
| 641 | } | ||
| 642 | |||
| 643 | static astro_apsis_t ApsisError(astro_status_t status) | ||
| 644 | { | ||
| 645 | astro_apsis_t result; | ||
| 646 | |||
| 647 | result.status = status; | ||
| 648 | result.time = TimeError(); | ||
| 649 | result.kind = APSIS_INVALID; | ||
| 650 | result.dist_km = result.dist_au = NAN; | ||
| 651 | |||
| 652 | return result; | ||
| 653 | } | ||
| 654 | |||
| 655 | static astro_search_result_t SearchError(astro_status_t status) | ||
| 656 | { | ||
| 657 | astro_search_result_t result; | ||
| 658 | result.time = TimeError(); | ||
| 659 | result.status = status; | ||
| 660 | return result; | ||
| 661 | } | ||
| 662 | |||
| 663 | static astro_constellation_t ConstelErr(astro_status_t status) | ||
| 664 | { | ||
| 665 | astro_constellation_t constel; | ||
| 666 | constel.status = status; | ||
| 667 | constel.symbol = constel.name = NULL; | ||
| 668 | constel.ra_1875 = constel.dec_1875 = NAN; | ||
| 669 | return constel; | ||
| 670 | } | ||
| 671 | |||
| 672 | static astro_transit_t TransitErr(astro_status_t status) | ||
| 673 | { | ||
| 674 | astro_transit_t transit; | ||
| 675 | transit.status = status; | ||
| 676 | transit.start = transit.peak = transit.finish = TimeError(); | ||
| 677 | transit.separation = NAN; | ||
| 678 | return transit; | ||
| 679 | } | ||
| 680 | |||
| 681 | static astro_axis_t AxisErr(astro_status_t status, astro_time_t time) | ||
| 682 | { | ||
| 683 | astro_axis_t axis; | ||
| 684 | axis.status = status; | ||
| 685 | axis.ra = axis.dec = axis.spin = NAN; | ||
| 686 | axis.north = VecError(status, time); | ||
| 687 | return axis; | ||
| 688 | } | ||
| 689 | |||
| 690 | static astro_func_result_t SynodicPeriod(astro_body_t body) | ||
| 691 | { | ||
| 692 | double Tp; /* planet's orbital period in days */ | ||
| 693 | astro_func_result_t result; | ||
| 694 | |||
| 695 | /* The Earth does not have a synodic period as seen from itself. */ | ||
| 696 | if (body == BODY_EARTH) | ||
| 697 | return FuncError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 698 | |||
| 699 | if (body == BODY_MOON) | ||
| 700 | { | ||
| 701 | result.status = ASTRO_SUCCESS; | ||
| 702 | result.value = MEAN_SYNODIC_MONTH; | ||
| 703 | return result; | ||
| 704 | } | ||
| 705 | |||
| 706 | Tp = Astronomy_PlanetOrbitalPeriod(body); | ||
| 707 | if (Tp <= 0.0) | ||
| 708 | return FuncError(ASTRO_INVALID_BODY); | ||
| 709 | |||
| 710 | result.status = ASTRO_SUCCESS; | ||
| 711 | result.value = fabs(EARTH_ORBITAL_PERIOD / (EARTH_ORBITAL_PERIOD/Tp - 1.0)); | ||
| 712 | return result; | ||
| 713 | } | ||
| 714 | |||
| 715 | |||
| 716 | /** | ||
| 717 | * @brief Calculates the angle between two vectors. | ||
| 718 | * | ||
| 719 | * Given a pair of vectors, this function returns the angle in degrees | ||
| 720 | * between the two vectors in 3D space. | ||
| 721 | * The angle is measured in the plane that contains both vectors. | ||
| 722 | * | ||
| 723 | * @param a | ||
| 724 | * The first vector. | ||
| 725 | * | ||
| 726 | * @param b | ||
| 727 | * The second vector. | ||
| 728 | * | ||
| 729 | * @returns | ||
| 730 | * On success, the `status` field holds `ASTRO_SUCCESS` and `angle` holds | ||
| 731 | * a number of degrees in the range [0, 180]. | ||
| 732 | * If either vector has a zero magnitude or contains NAN (not a number) | ||
| 733 | * components, the `status` will hold the error code `ASTRO_BAD_VECTOR`. | ||
| 734 | */ | ||
| 735 | astro_angle_result_t Astronomy_AngleBetween(astro_vector_t a, astro_vector_t b) | ||
| 736 | { | ||
| 737 | double r, dot; | ||
| 738 | astro_angle_result_t result; | ||
| 739 | |||
| 740 | r = Astronomy_VectorLength(a) * Astronomy_VectorLength(b); | ||
| 741 | if (r < 1.0e-8 || !isfinite(r)) | ||
| 742 | return AngleError(ASTRO_BAD_VECTOR); | ||
| 743 | |||
| 744 | dot = (a.x*b.x + a.y*b.y + a.z*b.z) / r; | ||
| 745 | |||
| 746 | if (dot <= -1.0) | ||
| 747 | result.angle = 180.0; | ||
| 748 | else if (dot >= +1.0) | ||
| 749 | result.angle = 0.0; | ||
| 750 | else | ||
| 751 | result.angle = RAD2DEG * acos(dot); | ||
| 752 | |||
| 753 | result.status = ASTRO_SUCCESS; | ||
| 754 | return result; | ||
| 755 | } | ||
| 756 | |||
| 757 | /** | ||
| 758 | * @brief The default Delta T function used by Astronomy Engine. | ||
| 759 | * | ||
| 760 | * Espenak and Meeus use a series of piecewise polynomials to | ||
| 761 | * approximate DeltaT of the Earth in their "Five Millennium Canon of Solar Eclipses". | ||
| 762 | * See: https://eclipse.gsfc.nasa.gov/SEhelp/deltatpoly2004.html | ||
| 763 | * This is the default Delta T function used by Astronomy Engine. | ||
| 764 | * | ||
| 765 | * @param ut | ||
| 766 | * The floating point number of days since noon UTC on January 1, 2000. | ||
| 767 | * | ||
| 768 | * @returns | ||
| 769 | * The estimated difference TT-UT on the given date, expressed in seconds. | ||
| 770 | */ | ||
| 771 | double Astronomy_DeltaT_EspenakMeeus(double ut) | ||
| 772 | { | ||
| 773 | double y, u, u2, u3, u4, u5, u6, u7; | ||
| 774 | |||
| 775 | /* | ||
| 776 | Fred Espenak writes about Delta-T generically here: | ||
| 777 | https://eclipse.gsfc.nasa.gov/SEhelp/deltaT.html | ||
| 778 | https://eclipse.gsfc.nasa.gov/SEhelp/deltat2004.html | ||
| 779 | |||
| 780 | He provides polynomial approximations for distant years here: | ||
| 781 | https://eclipse.gsfc.nasa.gov/SEhelp/deltatpoly2004.html | ||
| 782 | |||
| 783 | They start with a year value 'y' such that y=2000 corresponds | ||
| 784 | to the UTC Date 15-January-2000. Convert difference in days | ||
| 785 | to mean tropical years. | ||
| 786 | */ | ||
| 787 | |||
| 788 | y = 2000 + ((ut - 14) / DAYS_PER_TROPICAL_YEAR); | ||
| 789 | |||
| 790 | if (y < -500) | ||
| 791 | { | ||
| 792 | u = (y - 1820) / 100; | ||
| 793 | return -20 + (32 * u*u); | ||
| 794 | } | ||
| 795 | if (y < 500) | ||
| 796 | { | ||
| 797 | u = y / 100; | ||
| 798 | u2 = u*u; u3 = u*u2; u4 = u2*u2; u5 = u2*u3; u6 = u3*u3; | ||
| 799 | return 10583.6 - 1014.41*u + 33.78311*u2 - 5.952053*u3 - 0.1798452*u4 + 0.022174192*u5 + 0.0090316521*u6; | ||
| 800 | } | ||
| 801 | if (y < 1600) | ||
| 802 | { | ||
| 803 | u = (y - 1000) / 100; | ||
| 804 | u2 = u*u; u3 = u*u2; u4 = u2*u2; u5 = u2*u3; u6 = u3*u3; | ||
| 805 | return 1574.2 - 556.01*u + 71.23472*u2 + 0.319781*u3 - 0.8503463*u4 - 0.005050998*u5 + 0.0083572073*u6; | ||
| 806 | } | ||
| 807 | if (y < 1700) | ||
| 808 | { | ||
| 809 | u = y - 1600; | ||
| 810 | u2 = u*u; u3 = u*u2; | ||
| 811 | return 120 - 0.9808*u - 0.01532*u2 + u3/7129.0; | ||
| 812 | } | ||
| 813 | if (y < 1800) | ||
| 814 | { | ||
| 815 | u = y - 1700; | ||
| 816 | u2 = u*u; u3 = u*u2; u4 = u2*u2; | ||
| 817 | return 8.83 + 0.1603*u - 0.0059285*u2 + 0.00013336*u3 - u4/1174000; | ||
| 818 | } | ||
| 819 | if (y < 1860) | ||
| 820 | { | ||
| 821 | u = y - 1800; | ||
| 822 | u2 = u*u; u3 = u*u2; u4 = u2*u2; u5 = u2*u3; u6 = u3*u3; u7 = u3*u4; | ||
| 823 | return 13.72 - 0.332447*u + 0.0068612*u2 + 0.0041116*u3 - 0.00037436*u4 + 0.0000121272*u5 - 0.0000001699*u6 + 0.000000000875*u7; | ||
| 824 | } | ||
| 825 | if (y < 1900) | ||
| 826 | { | ||
| 827 | u = y - 1860; | ||
| 828 | u2 = u*u; u3 = u*u2; u4 = u2*u2; u5 = u2*u3; | ||
| 829 | return 7.62 + 0.5737*u - 0.251754*u2 + 0.01680668*u3 - 0.0004473624*u4 + u5/233174; | ||
| 830 | } | ||
| 831 | if (y < 1920) | ||
| 832 | { | ||
| 833 | u = y - 1900; | ||
| 834 | u2 = u*u; u3 = u*u2; u4 = u2*u2; | ||
| 835 | return -2.79 + 1.494119*u - 0.0598939*u2 + 0.0061966*u3 - 0.000197*u4; | ||
| 836 | } | ||
| 837 | if (y < 1941) | ||
| 838 | { | ||
| 839 | u = y - 1920; | ||
| 840 | u2 = u*u; u3 = u*u2; | ||
| 841 | return 21.20 + 0.84493*u - 0.076100*u2 + 0.0020936*u3; | ||
| 842 | } | ||
| 843 | if (y < 1961) | ||
| 844 | { | ||
| 845 | u = y - 1950; | ||
| 846 | u2 = u*u; u3 = u*u2; | ||
| 847 | return 29.07 + 0.407*u - u2/233 + u3/2547; | ||
| 848 | } | ||
| 849 | if (y < 1986) | ||
| 850 | { | ||
| 851 | u = y - 1975; | ||
| 852 | u2 = u*u; u3 = u*u2; | ||
| 853 | return 45.45 + 1.067*u - u2/260 - u3/718; | ||
| 854 | } | ||
| 855 | if (y < 2005) | ||
| 856 | { | ||
| 857 | u = y - 2000; | ||
| 858 | u2 = u*u; u3 = u*u2; u4 = u2*u2; u5 = u2*u3; | ||
| 859 | return 63.86 + 0.3345*u - 0.060374*u2 + 0.0017275*u3 + 0.000651814*u4 + 0.00002373599*u5; | ||
| 860 | } | ||
| 861 | if (y < 2050) | ||
| 862 | { | ||
| 863 | u = y - 2000; | ||
| 864 | return 62.92 + 0.32217*u + 0.005589*u*u; | ||
| 865 | } | ||
| 866 | if (y < 2150) | ||
| 867 | { | ||
| 868 | u = (y-1820)/100; | ||
| 869 | return -20 + 32*u*u - 0.5628*(2150 - y); | ||
| 870 | } | ||
| 871 | |||
| 872 | /* all years after 2150 */ | ||
| 873 | u = (y - 1820) / 100; | ||
| 874 | return -20 + (32 * u*u); | ||
| 875 | } | ||
| 876 | |||
| 877 | /** | ||
| 878 | * @brief A Delta T function that approximates the one used by the JPL Horizons tool. | ||
| 879 | * | ||
| 880 | * In order to support unit tests based on data generated by the JPL Horizons online | ||
| 881 | * tool, I had to reverse engineer their Delta T function by generating a table that | ||
| 882 | * contained it. The main difference between their tool and the Espenak/Meeus function | ||
| 883 | * is that they stop extrapolating the Earth's deceleration after the year 2017. | ||
| 884 | * | ||
| 885 | * @param ut | ||
| 886 | * The floating point number of days since noon UTC on January 1, 2000. | ||
| 887 | * | ||
| 888 | * @returns | ||
| 889 | * The estimated difference TT-UT on the given date, expressed in seconds. | ||
| 890 | */ | ||
| 891 | double Astronomy_DeltaT_JplHorizons(double ut) | ||
| 892 | { | ||
| 893 | if (ut > 17.0 * DAYS_PER_TROPICAL_YEAR) | ||
| 894 | ut = 17.0 * DAYS_PER_TROPICAL_YEAR; | ||
| 895 | |||
| 896 | return Astronomy_DeltaT_EspenakMeeus(ut); | ||
| 897 | } | ||
| 898 | |||
| 899 | static astro_deltat_func DeltaTFunc = Astronomy_DeltaT_EspenakMeeus; | ||
| 900 | |||
| 901 | /** | ||
| 902 | * @brief Changes the function Astronomy Engine uses to calculate Delta T. | ||
| 903 | * | ||
| 904 | * Most programs should not call this function. It is for advanced use cases only. | ||
| 905 | * By default, Astronomy Engine uses the function #Astronomy_DeltaT_EspenakMeeus | ||
| 906 | * to estimate changes in the Earth's rotation rate over time. | ||
| 907 | * However, for the sake of unit tests that compare calculations against | ||
| 908 | * external data sources that use alternative models for Delta T, | ||
| 909 | * it is sometimes useful to replace the Delta T model to match. | ||
| 910 | * This function allows replacing the Delta T model with any other | ||
| 911 | * desired model. | ||
| 912 | * | ||
| 913 | * @param func | ||
| 914 | * A pointer to a function to convert UT values to DeltaT values. | ||
| 915 | */ | ||
| 916 | void Astronomy_SetDeltaTFunction(astro_deltat_func func) | ||
| 917 | { | ||
| 918 | DeltaTFunc = func; | ||
| 919 | } | ||
| 920 | |||
| 921 | static double TerrestrialTime(double ut) | ||
| 922 | { | ||
| 923 | return ut + DeltaTFunc(ut)/86400.0; | ||
| 924 | } | ||
| 925 | |||
| 926 | /** | ||
| 927 | * @brief Converts a J2000 day value to an #astro_time_t value. | ||
| 928 | * | ||
| 929 | * This function can be useful for reproducing an #astro_time_t structure | ||
| 930 | * from its `ut` field only. | ||
| 931 | * | ||
| 932 | * @param ut | ||
| 933 | * The floating point number of days since noon UTC on January 1, 2000. | ||
| 934 | * This time is based on UTC/UT1 civil time. | ||
| 935 | * See #Astronomy_TerrestrialTime if you instead want to create | ||
| 936 | * a time value based on atomic Terrestrial Time (TT). | ||
| 937 | * | ||
| 938 | * @returns | ||
| 939 | * An #astro_time_t value for the given `ut` value. | ||
| 940 | */ | ||
| 941 | astro_time_t Astronomy_TimeFromDays(double ut) | ||
| 942 | { | ||
| 943 | astro_time_t time; | ||
| 944 | time.ut = ut; | ||
| 945 | time.tt = TerrestrialTime(ut); | ||
| 946 | time.psi = time.eps = time.st = NAN; | ||
| 947 | return time; | ||
| 948 | } | ||
| 949 | |||
| 950 | |||
| 951 | /** | ||
| 952 | * @brief Converts a terrestrial time value into an #astro_time_t value. | ||
| 953 | * | ||
| 954 | * This function can be used in rare cases where a time must be based | ||
| 955 | * on Terrestrial Time (TT) rather than Universal Time (UT). | ||
| 956 | * Most developers will want to call #Astronomy_TimeFromDays instead of | ||
| 957 | * this function, because usually time is based on civil time adjusted | ||
| 958 | * by leap seconds to match the Earth's rotation, rather than the uniformly | ||
| 959 | * flowing TT used to calculate solar system dynamics. In rare cases | ||
| 960 | * where the caller already knows TT, this function is provided to create | ||
| 961 | * an #astro_time_t value that can be passed to Astronomy Engine functions. | ||
| 962 | * | ||
| 963 | * @param tt | ||
| 964 | * The floating point number of days of uniformly flowing | ||
| 965 | * Terrestrial Time since the J2000 epoch. | ||
| 966 | * | ||
| 967 | * @returns | ||
| 968 | * An #astro_time_t value for the given `tt` value. | ||
| 969 | */ | ||
| 970 | astro_time_t Astronomy_TerrestrialTime(double tt) | ||
| 971 | { | ||
| 972 | /* Iterate to solve to find the correct ut for a given tt, and create an astro_time_t for that time. */ | ||
| 973 | astro_time_t time = Astronomy_TimeFromDays(tt); | ||
| 974 | for(;;) | ||
| 975 | { | ||
| 976 | double err = tt - time.tt; | ||
| 977 | if (fabs(err) < 1.0e-12) | ||
| 978 | return time; | ||
| 979 | time = Astronomy_AddDays(time, err); | ||
| 980 | } | ||
| 981 | } | ||
| 982 | |||
| 983 | #if !defined(ASTRONOMY_ENGINE_NO_CURRENT_TIME) | ||
| 984 | /** | ||
| 985 | * @brief Returns the computer's current date and time in the form of an #astro_time_t. | ||
| 986 | * | ||
| 987 | * Uses the computer's system clock to find the current UTC date and time. | ||
| 988 | * Converts that date and time to an #astro_time_t value and returns the result. | ||
| 989 | * Callers can pass this value to other Astronomy Engine functions to calculate | ||
| 990 | * current observational conditions. | ||
| 991 | * | ||
| 992 | * On supported platforms (Linux/Unix, Mac, Windows), the time is measured with | ||
| 993 | * microsecond resolution. | ||
| 994 | * | ||
| 995 | * On unsupported platforms, a compiler error will occur due to lack of | ||
| 996 | * microsecond resolution support. However, if whole second resolution is good | ||
| 997 | * enough for your application, you can define the preprocessor symbol | ||
| 998 | * `ASTRONOMY_ENGINE_WHOLE_SECOND` to use the portable function `time(NULL)`. | ||
| 999 | * Alternatively, if you do not need to use `Astronomy_CurrentTime`, you can | ||
| 1000 | * define the preprocessor symbol `ASTRONOMY_ENGINE_NO_CURRENT_TIME` to | ||
| 1001 | * exclude this function from your code. | ||
| 1002 | */ | ||
| 1003 | astro_time_t Astronomy_CurrentTime(void) | ||
| 1004 | { | ||
| 1005 | astro_time_t t; | ||
| 1006 | double sec; /* Seconds since midnight January 1, 1970. */ | ||
| 1007 | |||
| 1008 | #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) | ||
| 1009 | struct timeval tv; | ||
| 1010 | gettimeofday(&tv, NULL); | ||
| 1011 | sec = (double)tv.tv_sec + tv.tv_usec/1.0e+6; | ||
| 1012 | #elif defined(_WIN32) | ||
| 1013 | FILETIME ft; | ||
| 1014 | ULARGE_INTEGER large; | ||
| 1015 | /* Get time in 100-nanosecond units from January 1, 1601. */ | ||
| 1016 | GetSystemTimePreciseAsFileTime(&ft); | ||
| 1017 | large.u.LowPart = ft.dwLowDateTime; | ||
| 1018 | large.u.HighPart = ft.dwHighDateTime; | ||
| 1019 | sec = (large.QuadPart - 116444736000000000ULL) / 1.0e+7; | ||
| 1020 | #elif defined(ASTRONOMY_ENGINE_WHOLE_SECOND) | ||
| 1021 | sec = time(NULL); | ||
| 1022 | #else | ||
| 1023 | #error Microsecond time resolution is not supported on this platform. Define ASTRONOMY_ENGINE_WHOLE_SECOND to use second resolution instead. | ||
| 1024 | #endif | ||
| 1025 | |||
| 1026 | /* Convert seconds to days, then subtract to get days since noon on January 1, 2000. */ | ||
| 1027 | t.ut = (sec / SECONDS_PER_DAY) - 10957.5; | ||
| 1028 | t.tt = TerrestrialTime(t.ut); | ||
| 1029 | t.psi = t.eps = t.st = NAN; | ||
| 1030 | return t; | ||
| 1031 | } | ||
| 1032 | #endif | ||
| 1033 | |||
| 1034 | /** | ||
| 1035 | * @brief Creates an #astro_time_t value from a given calendar date and time. | ||
| 1036 | * | ||
| 1037 | * Given a UTC calendar date and time, calculates an #astro_time_t value that can | ||
| 1038 | * be passed to other Astronomy Engine functions for performing various calculations | ||
| 1039 | * relating to that date and time. | ||
| 1040 | * | ||
| 1041 | * It is the caller's responsibility to ensure that the parameter values are correct. | ||
| 1042 | * The parameters are not checked for validity, | ||
| 1043 | * and this function never returns any indication of an error. | ||
| 1044 | * Invalid values, for example passing in February 31, may cause unexpected return values. | ||
| 1045 | * | ||
| 1046 | * @param year The UTC calendar year, e.g. 2019. | ||
| 1047 | * @param month The UTC calendar month in the range 1..12. | ||
| 1048 | * @param day The UTC calendar day in the range 1..31. | ||
| 1049 | * @param hour The UTC hour of the day in the range 0..23. | ||
| 1050 | * @param minute The UTC minute in the range 0..59. | ||
| 1051 | * @param second The UTC floating-point second in the range [0, 60). | ||
| 1052 | * | ||
| 1053 | * @return An #astro_time_t value that represents the given calendar date and time. | ||
| 1054 | */ | ||
| 1055 | astro_time_t Astronomy_MakeTime(int year, int month, int day, int hour, int minute, double second) | ||
| 1056 | { | ||
| 1057 | astro_time_t time; | ||
| 1058 | int64_t y = (int64_t)year; | ||
| 1059 | int64_t m = (int64_t)month; | ||
| 1060 | int64_t d = (int64_t)day; | ||
| 1061 | int64_t f = (14 - m) / 12; | ||
| 1062 | |||
| 1063 | /* | ||
| 1064 | This formula is adapted from NOVAS C 3.1 function julian_date(), | ||
| 1065 | which in turn comes from Henry F. Fliegel & Thomas C. Van Flendern: | ||
| 1066 | Communications of the ACM, Vol 11, No 10, October 1968, p. 657. | ||
| 1067 | See: https://dl.acm.org/doi/pdf/10.1145/364096.364097 | ||
| 1068 | |||
| 1069 | [Don Cross - 2023-02-25] I modified the formula so that it will | ||
| 1070 | work correctly with years as far back as -999999. | ||
| 1071 | */ | ||
| 1072 | int64_t y2000 = ( | ||
| 1073 | (d - 365972956) | ||
| 1074 | + (1461*(y + 1000000 - f))/4 | ||
| 1075 | + (367*(m - 2 + 12*f))/12 | ||
| 1076 | - (3*((y + 1000100 - f) / 100))/4 | ||
| 1077 | ); | ||
| 1078 | |||
| 1079 | time.ut = (y2000 - 0.5) + (hour / 24.0) + (minute / 1440.0) + (second / 86400.0); | ||
| 1080 | time.tt = TerrestrialTime(time.ut); | ||
| 1081 | time.psi = time.eps = time.st = NAN; | ||
| 1082 | |||
| 1083 | return time; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | /** | ||
| 1087 | * @brief Calculates the sum or difference of an #astro_time_t with a specified floating point number of days. | ||
| 1088 | * | ||
| 1089 | * Sometimes we need to adjust a given #astro_time_t value by a certain amount of time. | ||
| 1090 | * This function adds the given real number of days in `days` to the date and time in `time`. | ||
| 1091 | * | ||
| 1092 | * More precisely, the result's Universal Time field `ut` is exactly adjusted by `days` and | ||
| 1093 | * the Terrestrial Time field `tt` is adjusted correctly for the resulting UTC date and time, | ||
| 1094 | * according to the historical and predictive Delta-T model provided by the | ||
| 1095 | * [United States Naval Observatory](http://maia.usno.navy.mil/ser7/). | ||
| 1096 | * | ||
| 1097 | * The value stored in `time` will not be modified; it is passed by value. | ||
| 1098 | * | ||
| 1099 | * @param time A date and time for which to calculate an adjusted date and time. | ||
| 1100 | * @param days A floating point number of days by which to adjust `time`. May be negative, 0, or positive. | ||
| 1101 | * @return A date and time that is conceptually equal to `time + days`. | ||
| 1102 | */ | ||
| 1103 | astro_time_t Astronomy_AddDays(astro_time_t time, double days) | ||
| 1104 | { | ||
| 1105 | /* | ||
| 1106 | This is slightly wrong, but the error is tiny. | ||
| 1107 | We really should be adding to TT, not to UT. | ||
| 1108 | But using TT would require creating an inverse function for DeltaT, | ||
| 1109 | which would be quite a bit of extra calculation. | ||
| 1110 | I estimate the error is in practice on the order of 10^(-7) | ||
| 1111 | times the value of 'days'. | ||
| 1112 | This is based on a typical drift of 1 second per year between UT and TT. | ||
| 1113 | */ | ||
| 1114 | |||
| 1115 | astro_time_t sum; | ||
| 1116 | |||
| 1117 | sum.ut = time.ut + days; | ||
| 1118 | sum.tt = TerrestrialTime(sum.ut); | ||
| 1119 | sum.eps = sum.psi = sum.st = NAN; | ||
| 1120 | |||
| 1121 | return sum; | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | /** | ||
| 1125 | * @brief Creates an #astro_time_t value from a given calendar date and time. | ||
| 1126 | * | ||
| 1127 | * This function is similar to #Astronomy_MakeTime, only it receives a | ||
| 1128 | * UTC calendar date and time in the form of an #astro_utc_t structure instead of | ||
| 1129 | * as separate numeric parameters. Astronomy_TimeFromUtc is the inverse of | ||
| 1130 | * #Astronomy_UtcFromTime. | ||
| 1131 | * | ||
| 1132 | * @param utc The UTC calendar date and time to be converted to #astro_time_t. | ||
| 1133 | * @return A value that can be used for astronomical calculations for the given date and time. | ||
| 1134 | */ | ||
| 1135 | astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc) | ||
| 1136 | { | ||
| 1137 | return Astronomy_MakeTime(utc.year, utc.month, utc.day, utc.hour, utc.minute, utc.second); | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | /** | ||
| 1141 | * @brief Determines the calendar year, month, day, and time from an #astro_time_t value. | ||
| 1142 | * | ||
| 1143 | * After calculating the date and time of an astronomical event in the form of | ||
| 1144 | * an #astro_time_t value, it is often useful to display the result in a human-readable | ||
| 1145 | * form. This function converts the linear time scales in the `ut` field of #astro_time_t | ||
| 1146 | * into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed | ||
| 1147 | * in UTC. | ||
| 1148 | * | ||
| 1149 | * @param time The astronomical time value to be converted to calendar date and time. | ||
| 1150 | * @return A date and time broken out into conventional year, month, day, hour, minute, and second. | ||
| 1151 | */ | ||
| 1152 | astro_utc_t Astronomy_UtcFromTime(astro_time_t time) | ||
| 1153 | { | ||
| 1154 | /* Adapted from the NOVAS C 3.1 function cal_date() */ | ||
| 1155 | astro_utc_t utc; | ||
| 1156 | int64_t jd, k, m, n; | ||
| 1157 | double djd, x; | ||
| 1158 | const int64_t c = 2500; | ||
| 1159 | |||
| 1160 | djd = time.ut + 2451545.5; | ||
| 1161 | jd = (int64_t)floor(djd); | ||
| 1162 | |||
| 1163 | x = 24.0 * fmod(djd, 1.0); | ||
| 1164 | if (x < 0.0) | ||
| 1165 | x += 24.0; | ||
| 1166 | utc.hour = (int)x; | ||
| 1167 | x = 60.0 * fmod(x, 1.0); | ||
| 1168 | utc.minute = (int)x; | ||
| 1169 | utc.second = 60.0 * fmod(x, 1.0); | ||
| 1170 | |||
| 1171 | /* | ||
| 1172 | This is my own adjustment to the NOVAS cal_date logic | ||
| 1173 | so that it can handle dates much farther back in the past. | ||
| 1174 | I add c*400 years worth of days at the front, | ||
| 1175 | then subtract c*400 years at the back, | ||
| 1176 | which avoids negative values in the formulas that mess up | ||
| 1177 | the calendar date calculations. | ||
| 1178 | Any multiple of 400 years has the same number of days, | ||
| 1179 | because it eliminates all the special cases for leap years. | ||
| 1180 | */ | ||
| 1181 | k = jd + (68569 + c*146097); | ||
| 1182 | n = (4 * k) / 146097; | ||
| 1183 | k = k - (146097 * n + 3)/4; | ||
| 1184 | m = (4000 * (k+1)) / 1461001; | ||
| 1185 | k = k - (1461 * m)/4 + 31; | ||
| 1186 | |||
| 1187 | utc.month = (int) ((80 * k) / 2447); | ||
| 1188 | utc.day = (int) (k - (2447*utc.month)/80); | ||
| 1189 | k = utc.month / 11; | ||
| 1190 | |||
| 1191 | utc.month = (int) (utc.month + 2 - 12*k); | ||
| 1192 | utc.year = (int) (100 * (n - 49) + m + k - 400*c); | ||
| 1193 | |||
| 1194 | return utc; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | |||
| 1198 | /** | ||
| 1199 | * @brief Formats an #astro_time_t value as an ISO 8601 string. | ||
| 1200 | * | ||
| 1201 | * Given an #astro_time_t value `time`, formats it as an ISO 8601 | ||
| 1202 | * string to the resolution specified by the `format` parameter. | ||
| 1203 | * The result is stored in the `text` buffer whose capacity in bytes | ||
| 1204 | * is specified by `size`. | ||
| 1205 | * | ||
| 1206 | * @param time | ||
| 1207 | * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. | ||
| 1208 | * If the civil time is outside the year range -999999 to +999999, the function fails | ||
| 1209 | * and returns `ASTRO_BAD_TIME`. Years prior to 1583 are treated as if they are | ||
| 1210 | * using the modern Gregorian calendar, even when the Julian calendar was actually in effect. | ||
| 1211 | * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. | ||
| 1212 | * The year 2 BC is represented by -1, etc. | ||
| 1213 | * | ||
| 1214 | * @param format | ||
| 1215 | * Specifies the resolution to which the date and time should be formatted, | ||
| 1216 | * as explained at #astro_time_format_t. | ||
| 1217 | * If the value of `format` is not recognized, the function fails and | ||
| 1218 | * returns `ASTRO_INVALID_PARAMETER`. | ||
| 1219 | * | ||
| 1220 | * @param text | ||
| 1221 | * A pointer to a text buffer to receive the output. | ||
| 1222 | * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. | ||
| 1223 | * If the function fails for any reason, and `text` is not `NULL`, | ||
| 1224 | * and `size` is greater than 0, the `text` buffer is set to an empty string. | ||
| 1225 | * | ||
| 1226 | * @param size | ||
| 1227 | * The size in bytes of the buffer pointed to by `text`. The buffer must | ||
| 1228 | * be large enough to accomodate the output format selected by the | ||
| 1229 | * `format` parameter, as specified at #astro_time_format_t. | ||
| 1230 | * If `size` is too small to hold the string as specified by `format`, | ||
| 1231 | * the `text` buffer is set to `""` (if possible) | ||
| 1232 | * and the function returns `ASTRO_BUFFER_TOO_SMALL`. | ||
| 1233 | * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. | ||
| 1234 | * | ||
| 1235 | * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. | ||
| 1236 | */ | ||
| 1237 | astro_status_t Astronomy_FormatTime( | ||
| 1238 | astro_time_t time, | ||
| 1239 | astro_time_format_t format, | ||
| 1240 | char *text, | ||
| 1241 | size_t size) | ||
| 1242 | { | ||
| 1243 | int nprinted; | ||
| 1244 | double rounding; | ||
| 1245 | size_t min_size; | ||
| 1246 | astro_utc_t utc; | ||
| 1247 | char ytext[20]; /* worst case: "+999999" = 8 characters including terminal '\0'. But gcc 12.2 still complains! */ | ||
| 1248 | |||
| 1249 | if (text == NULL) | ||
| 1250 | return ASTRO_INVALID_PARAMETER; | ||
| 1251 | |||
| 1252 | if (size == 0) | ||
| 1253 | return ASTRO_BUFFER_TOO_SMALL; | ||
| 1254 | |||
| 1255 | text[0] = '\0'; /* initialize to empty string, in case an error occurs */ | ||
| 1256 | |||
| 1257 | /* Validate 'size' parameter and perform date/time rounding. */ | ||
| 1258 | switch (format) | ||
| 1259 | { | ||
| 1260 | case TIME_FORMAT_DAY: | ||
| 1261 | min_size = 11; /* "2020-12-31" */ | ||
| 1262 | rounding = 0.0; /* no rounding */ | ||
| 1263 | break; | ||
| 1264 | |||
| 1265 | case TIME_FORMAT_MINUTE: | ||
| 1266 | min_size = 18; /* "2020-12-31T15:47Z" */ | ||
| 1267 | rounding = 0.5 / (24.0 * 60.0); /* round to nearest minute */ | ||
| 1268 | break; | ||
| 1269 | |||
| 1270 | case TIME_FORMAT_SECOND: | ||
| 1271 | min_size = 21; /* "2020-12-31T15:47:59Z" */ | ||
| 1272 | rounding = 0.5 / (24.0 * 3600.0); /* round to nearest second */ | ||
| 1273 | break; | ||
| 1274 | |||
| 1275 | case TIME_FORMAT_MILLI: | ||
| 1276 | min_size = 25; /* "2020-12-31T15:47:59.123Z" */ | ||
| 1277 | rounding = 0.5 / (24.0 * 3600000.0); /* round to nearest millisecond */ | ||
| 1278 | break; | ||
| 1279 | |||
| 1280 | default: | ||
| 1281 | return ASTRO_INVALID_PARAMETER; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | /* Perform rounding. */ | ||
| 1285 | time.ut += rounding; | ||
| 1286 | |||
| 1287 | /* Convert linear J2000 days to Gregorian UTC date/time. */ | ||
| 1288 | utc = Astronomy_UtcFromTime(time); | ||
| 1289 | if (utc.year < -999999 || utc.year > +999999) | ||
| 1290 | return ASTRO_BAD_TIME; | ||
| 1291 | |||
| 1292 | if (utc.year < 0) | ||
| 1293 | { | ||
| 1294 | snprintf(ytext, sizeof(ytext), "-%06d", -utc.year); | ||
| 1295 | min_size += 3; /* '-' prefix and two extra year digits. */ | ||
| 1296 | } | ||
| 1297 | else if (utc.year <= 9999) | ||
| 1298 | { | ||
| 1299 | snprintf(ytext, sizeof(ytext), "%04d", utc.year); | ||
| 1300 | } | ||
| 1301 | else | ||
| 1302 | { | ||
| 1303 | snprintf(ytext, sizeof(ytext), "+%06d", utc.year); | ||
| 1304 | min_size += 3; /* '+' prefix and two extra year digits. */ | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | /* Check for insufficient buffer size. */ | ||
| 1308 | if (size < min_size) | ||
| 1309 | return ASTRO_BUFFER_TOO_SMALL; | ||
| 1310 | |||
| 1311 | /* Format the string. */ | ||
| 1312 | switch (format) | ||
| 1313 | { | ||
| 1314 | case TIME_FORMAT_DAY: | ||
| 1315 | nprinted = snprintf(text, size, "%s-%02d-%02d", | ||
| 1316 | ytext, utc.month, utc.day); | ||
| 1317 | break; | ||
| 1318 | |||
| 1319 | case TIME_FORMAT_MINUTE: | ||
| 1320 | nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02dZ", | ||
| 1321 | ytext, utc.month, utc.day, | ||
| 1322 | utc.hour, utc.minute); | ||
| 1323 | break; | ||
| 1324 | |||
| 1325 | case TIME_FORMAT_SECOND: | ||
| 1326 | nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%02.0lfZ", | ||
| 1327 | ytext, utc.month, utc.day, | ||
| 1328 | utc.hour, utc.minute, floor(utc.second)); | ||
| 1329 | break; | ||
| 1330 | |||
| 1331 | case TIME_FORMAT_MILLI: | ||
| 1332 | nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%06.3lfZ", | ||
| 1333 | ytext, utc.month, utc.day, | ||
| 1334 | utc.hour, utc.minute, floor(1000.0 * utc.second) / 1000.0); | ||
| 1335 | break; | ||
| 1336 | |||
| 1337 | default: | ||
| 1338 | /* We should have already failed for any unknown 'format' value. */ | ||
| 1339 | return ASTRO_INTERNAL_ERROR; | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | if (nprinted < 0) | ||
| 1343 | return ASTRO_INTERNAL_ERROR; /* should not be possible for snprintf to return a negative number */ | ||
| 1344 | |||
| 1345 | if ((size_t)(1+nprinted) != min_size) | ||
| 1346 | return ASTRO_INTERNAL_ERROR; /* there must be a bug calculating min_size or formatting the string */ | ||
| 1347 | |||
| 1348 | return ASTRO_SUCCESS; | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | |||
| 1352 | /** | ||
| 1353 | * @brief Creates an observer object that represents a location on or near the surface of the Earth. | ||
| 1354 | * | ||
| 1355 | * Some Astronomy Engine functions calculate values pertaining to an observer on the Earth. | ||
| 1356 | * These functions require a value of type #astro_observer_t that represents the location | ||
| 1357 | * of such an observer. | ||
| 1358 | * | ||
| 1359 | * @param latitude The geographic latitude of the observer in degrees north (positive) or south (negative) of the equator. | ||
| 1360 | * @param longitude The geographic longitude of the observer in degrees east (positive) or west (negative) of the prime meridian at Greenwich, England. | ||
| 1361 | * @param height The height of the observer in meters above mean sea level. | ||
| 1362 | * @return An observer object that can be passed to astronomy functions that require a geographic location. | ||
| 1363 | */ | ||
| 1364 | astro_observer_t Astronomy_MakeObserver(double latitude, double longitude, double height) | ||
| 1365 | { | ||
| 1366 | astro_observer_t observer; | ||
| 1367 | |||
| 1368 | observer.latitude = latitude; | ||
| 1369 | observer.longitude = longitude; | ||
| 1370 | observer.height = height; | ||
| 1371 | |||
| 1372 | return observer; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | static void iau2000b(astro_time_t *time) | ||
| 1376 | { | ||
| 1377 | /* Truncated and hand-optimized nutation model. */ | ||
| 1378 | |||
| 1379 | if ((time != NULL) && isnan(time->psi)) | ||
| 1380 | { | ||
| 1381 | double t, elp, f, d, om, arg, dp, de, sarg, carg; | ||
| 1382 | |||
| 1383 | t = time->tt / 36525.0; | ||
| 1384 | elp = fmod(1287104.79305 + t * 129596581.0481, ASEC360) * ASEC2RAD; | ||
| 1385 | f = fmod(335779.526232 + t * 1739527262.8478, ASEC360) * ASEC2RAD; | ||
| 1386 | d = fmod(1072260.70369 + t * 1602961601.2090, ASEC360) * ASEC2RAD; | ||
| 1387 | om = fmod(450160.398036 - t * 6962890.5431, ASEC360) * ASEC2RAD; | ||
| 1388 | |||
| 1389 | sarg = sin(om); | ||
| 1390 | carg = cos(om); | ||
| 1391 | dp = (-172064161.0 - 174666.0*t)*sarg + 33386.0*carg; | ||
| 1392 | de = (92052331.0 + 9086.0*t)*carg + 15377.0*sarg; | ||
| 1393 | |||
| 1394 | arg = 2.0*(f - d + om); | ||
| 1395 | sarg = sin(arg); | ||
| 1396 | carg = cos(arg); | ||
| 1397 | dp += (-13170906.0 - 1675.0*t)*sarg - 13696.0*carg; | ||
| 1398 | de += (5730336.0 - 3015.0*t)*carg - 4587.0*sarg; | ||
| 1399 | |||
| 1400 | arg = 2.0*(f + om); | ||
| 1401 | sarg = sin(arg); | ||
| 1402 | carg = cos(arg); | ||
| 1403 | dp += (-2276413.0 - 234.0*t)*sarg + 2796.0*carg; | ||
| 1404 | de += (978459.0 - 485.0*t)*carg + 1374.0*sarg; | ||
| 1405 | |||
| 1406 | arg = 2.0*om; | ||
| 1407 | sarg = sin(arg); | ||
| 1408 | carg = cos(arg); | ||
| 1409 | dp += (2074554.0 + 207.0*t)*sarg - 698.0*carg; | ||
| 1410 | de += (-897492.0 + 470.0*t)*carg - 291.0*sarg; | ||
| 1411 | |||
| 1412 | sarg = sin(elp); | ||
| 1413 | carg = cos(elp); | ||
| 1414 | dp += (1475877.0 - 3633.0*t)*sarg + 11817.0*carg; | ||
| 1415 | de += (73871.0 - 184.0*t)*carg - 1924.0*sarg; | ||
| 1416 | |||
| 1417 | time->psi = -0.000135 + (dp * 1.0e-7); | ||
| 1418 | time->eps = +0.000388 + (de * 1.0e-7); | ||
| 1419 | } | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | static double mean_obliq(double tt) | ||
| 1423 | { | ||
| 1424 | double t = tt / 36525.0; | ||
| 1425 | double asec = | ||
| 1426 | (((( - 0.0000000434 * t | ||
| 1427 | - 0.000000576 ) * t | ||
| 1428 | + 0.00200340 ) * t | ||
| 1429 | - 0.0001831 ) * t | ||
| 1430 | - 46.836769 ) * t + 84381.406; | ||
| 1431 | |||
| 1432 | return asec / 3600.0; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | /** @cond DOXYGEN_SKIP */ | ||
| 1436 | typedef struct | ||
| 1437 | { | ||
| 1438 | double tt; | ||
| 1439 | double dpsi; | ||
| 1440 | double deps; | ||
| 1441 | double ee; | ||
| 1442 | double mobl; | ||
| 1443 | double tobl; | ||
| 1444 | } | ||
| 1445 | earth_tilt_t; | ||
| 1446 | /** @endcond */ | ||
| 1447 | |||
| 1448 | static earth_tilt_t e_tilt(astro_time_t *time) | ||
| 1449 | { | ||
| 1450 | earth_tilt_t et; | ||
| 1451 | |||
| 1452 | /* There is no good answer for what to do if time==NULL. Callers must prevent! */ | ||
| 1453 | |||
| 1454 | iau2000b(time); | ||
| 1455 | et.dpsi = time->psi; | ||
| 1456 | et.deps = time->eps; | ||
| 1457 | et.mobl = mean_obliq(time->tt); | ||
| 1458 | et.tobl = et.mobl + (et.deps / 3600.0); | ||
| 1459 | et.tt = time->tt; | ||
| 1460 | et.ee = et.dpsi * cos(et.mobl * DEG2RAD) / 15.0; | ||
| 1461 | |||
| 1462 | return et; | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | static void obl_ecl2equ_vec(double obl, astro_time_t time, const double ecl[3], double equ[3]) | ||
| 1466 | { | ||
| 1467 | double obl_rad = obl * DEG2RAD; | ||
| 1468 | double cos_obl = cos(obl_rad); | ||
| 1469 | double sin_obl = sin(obl_rad); | ||
| 1470 | |||
| 1471 | equ[0] = ecl[0]; | ||
| 1472 | equ[1] = ecl[1]*cos_obl - ecl[2]*sin_obl; | ||
| 1473 | equ[2] = ecl[1]*sin_obl + ecl[2]*cos_obl; | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | static void ecl2equ_vec(astro_time_t time, const double ecl[3], double equ[3]) | ||
| 1477 | { | ||
| 1478 | double obl = mean_obliq(time.tt); | ||
| 1479 | obl_ecl2equ_vec(obl, time, ecl, equ); | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | static astro_rotation_t precession_rot(astro_time_t time, precess_dir_t dir) | ||
| 1483 | { | ||
| 1484 | /* | ||
| 1485 | dir==INTO_2000: converts mean equator of date (EQM) to J2000 mean equator (EQJ). | ||
| 1486 | dir==FROM_2000: converts J2000 mean equator (EQJ) to mean equator of date (EQM). | ||
| 1487 | */ | ||
| 1488 | |||
| 1489 | astro_rotation_t rotation; | ||
| 1490 | double xx, yx, zx, xy, yy, zy, xz, yz, zz; | ||
| 1491 | double t, psia, omegaa, chia, sa, ca, sb, cb, sc, cc, sd, cd; | ||
| 1492 | double eps0 = 84381.406; | ||
| 1493 | |||
| 1494 | t = time.tt / 36525; | ||
| 1495 | |||
| 1496 | psia = (((((- 0.0000000951 * t | ||
| 1497 | + 0.000132851 ) * t | ||
| 1498 | - 0.00114045 ) * t | ||
| 1499 | - 1.0790069 ) * t | ||
| 1500 | + 5038.481507 ) * t); | ||
| 1501 | |||
| 1502 | omegaa = (((((+ 0.0000003337 * t | ||
| 1503 | - 0.000000467 ) * t | ||
| 1504 | - 0.00772503 ) * t | ||
| 1505 | + 0.0512623 ) * t | ||
| 1506 | - 0.025754 ) * t + eps0); | ||
| 1507 | |||
| 1508 | chia = (((((- 0.0000000560 * t | ||
| 1509 | + 0.000170663 ) * t | ||
| 1510 | - 0.00121197 ) * t | ||
| 1511 | - 2.3814292 ) * t | ||
| 1512 | + 10.556403 ) * t); | ||
| 1513 | |||
| 1514 | eps0 = eps0 * ASEC2RAD; | ||
| 1515 | psia = psia * ASEC2RAD; | ||
| 1516 | omegaa = omegaa * ASEC2RAD; | ||
| 1517 | chia = chia * ASEC2RAD; | ||
| 1518 | |||
| 1519 | sa = sin(eps0); | ||
| 1520 | ca = cos(eps0); | ||
| 1521 | sb = sin(-psia); | ||
| 1522 | cb = cos(-psia); | ||
| 1523 | sc = sin(-omegaa); | ||
| 1524 | cc = cos(-omegaa); | ||
| 1525 | sd = sin(chia); | ||
| 1526 | cd = cos(chia); | ||
| 1527 | |||
| 1528 | xx = cd * cb - sb * sd * cc; | ||
| 1529 | yx = cd * sb * ca + sd * cc * cb * ca - sa * sd * sc; | ||
| 1530 | zx = cd * sb * sa + sd * cc * cb * sa + ca * sd * sc; | ||
| 1531 | xy = -sd * cb - sb * cd * cc; | ||
| 1532 | yy = -sd * sb * ca + cd * cc * cb * ca - sa * cd * sc; | ||
| 1533 | zy = -sd * sb * sa + cd * cc * cb * sa + ca * cd * sc; | ||
| 1534 | xz = sb * sc; | ||
| 1535 | yz = -sc * cb * ca - sa * cc; | ||
| 1536 | zz = -sc * cb * sa + cc * ca; | ||
| 1537 | |||
| 1538 | if (dir == INTO_2000) | ||
| 1539 | { | ||
| 1540 | /* Perform rotation from other epoch to J2000.0. */ | ||
| 1541 | rotation.rot[0][0] = xx; | ||
| 1542 | rotation.rot[0][1] = yx; | ||
| 1543 | rotation.rot[0][2] = zx; | ||
| 1544 | rotation.rot[1][0] = xy; | ||
| 1545 | rotation.rot[1][1] = yy; | ||
| 1546 | rotation.rot[1][2] = zy; | ||
| 1547 | rotation.rot[2][0] = xz; | ||
| 1548 | rotation.rot[2][1] = yz; | ||
| 1549 | rotation.rot[2][2] = zz; | ||
| 1550 | } | ||
| 1551 | else | ||
| 1552 | { | ||
| 1553 | /* Perform rotation from J2000.0 to other epoch. */ | ||
| 1554 | rotation.rot[0][0] = xx; | ||
| 1555 | rotation.rot[0][1] = xy; | ||
| 1556 | rotation.rot[0][2] = xz; | ||
| 1557 | rotation.rot[1][0] = yx; | ||
| 1558 | rotation.rot[1][1] = yy; | ||
| 1559 | rotation.rot[1][2] = yz; | ||
| 1560 | rotation.rot[2][0] = zx; | ||
| 1561 | rotation.rot[2][1] = zy; | ||
| 1562 | rotation.rot[2][2] = zz; | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | rotation.status = ASTRO_SUCCESS; | ||
| 1566 | return rotation; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | |||
| 1570 | static void rotate(const double invec[3], const double rot[3][3], double outvec[3]) | ||
| 1571 | { | ||
| 1572 | outvec[0] = rot[0][0]*invec[0] + rot[1][0]*invec[1] + rot[2][0]*invec[2]; | ||
| 1573 | outvec[1] = rot[0][1]*invec[0] + rot[1][1]*invec[1] + rot[2][1]*invec[2]; | ||
| 1574 | outvec[2] = rot[0][2]*invec[0] + rot[1][2]*invec[1] + rot[2][2]*invec[2]; | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | static void precession( | ||
| 1578 | const double pos1[3], | ||
| 1579 | astro_time_t time, | ||
| 1580 | precess_dir_t dir, | ||
| 1581 | double pos2[3]) | ||
| 1582 | { | ||
| 1583 | astro_rotation_t r = precession_rot(time, dir); | ||
| 1584 | rotate(pos1, r.rot, pos2); | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | static void precession_posvel( | ||
| 1588 | const double pos1[3], | ||
| 1589 | const double vel1[3], | ||
| 1590 | astro_time_t time, | ||
| 1591 | precess_dir_t dir, | ||
| 1592 | double pos2[3], | ||
| 1593 | double vel2[3]) | ||
| 1594 | { | ||
| 1595 | astro_rotation_t r = precession_rot(time, dir); | ||
| 1596 | rotate(pos1, r.rot, pos2); | ||
| 1597 | rotate(vel1, r.rot, vel2); | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | |||
| 1601 | static astro_equatorial_t vector2radec(const double pos[3], astro_time_t time) | ||
| 1602 | { | ||
| 1603 | astro_equatorial_t equ; | ||
| 1604 | double xyproj; | ||
| 1605 | |||
| 1606 | /* Copy the cartesian coordinates from the input into the returned structure. */ | ||
| 1607 | equ.vec.status = ASTRO_SUCCESS; | ||
| 1608 | equ.vec.t = time; | ||
| 1609 | equ.vec.x = pos[0]; | ||
| 1610 | equ.vec.y = pos[1]; | ||
| 1611 | equ.vec.z = pos[2]; | ||
| 1612 | |||
| 1613 | /* Calculate spherical coordinates: RA, DEC, distance. */ | ||
| 1614 | xyproj = pos[0]*pos[0] + pos[1]*pos[1]; | ||
| 1615 | equ.dist = sqrt(xyproj + pos[2]*pos[2]); | ||
| 1616 | equ.status = ASTRO_SUCCESS; | ||
| 1617 | if (xyproj == 0.0) | ||
| 1618 | { | ||
| 1619 | if (pos[2] == 0.0) | ||
| 1620 | { | ||
| 1621 | /* Indeterminate coordinates; pos vector has zero length. */ | ||
| 1622 | equ = EquError(ASTRO_BAD_VECTOR); | ||
| 1623 | } | ||
| 1624 | else if (pos[2] < 0) | ||
| 1625 | { | ||
| 1626 | equ.ra = 0.0; | ||
| 1627 | equ.dec = -90.0; | ||
| 1628 | } | ||
| 1629 | else | ||
| 1630 | { | ||
| 1631 | equ.ra = 0.0; | ||
| 1632 | equ.dec = +90.0; | ||
| 1633 | } | ||
| 1634 | } | ||
| 1635 | else | ||
| 1636 | { | ||
| 1637 | equ.ra = RAD2HOUR * atan2(pos[1], pos[0]); | ||
| 1638 | if (equ.ra < 0) | ||
| 1639 | equ.ra += 24.0; | ||
| 1640 | |||
| 1641 | equ.dec = RAD2DEG * atan2(pos[2], sqrt(xyproj)); | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | return equ; | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | |||
| 1648 | static astro_rotation_t nutation_rot(astro_time_t *time, precess_dir_t dir) | ||
| 1649 | { | ||
| 1650 | /* | ||
| 1651 | Creates a rotation matrix that adds/removes nutation from | ||
| 1652 | an equatorial vector of date: | ||
| 1653 | |||
| 1654 | The `dir` parameter is a little misleading, but reflects the | ||
| 1655 | common task of working together with precession_rot() to | ||
| 1656 | convert mean equator of 2000 to/from true equator of date. | ||
| 1657 | Here is the actual result of `dir`: | ||
| 1658 | |||
| 1659 | dir==INTO_2000: Subtract nutation from true equator of date (EQD) | ||
| 1660 | to produce mean equator of date (EQM). | ||
| 1661 | |||
| 1662 | dir==FROM_2000: Add nutation from mean equator of date (EQM) to | ||
| 1663 | produce true equator of date (EQD). | ||
| 1664 | */ | ||
| 1665 | |||
| 1666 | astro_rotation_t rotation; | ||
| 1667 | earth_tilt_t tilt; | ||
| 1668 | double oblm, oblt, psi, cobm, sobm, cobt, sobt, cpsi, spsi; | ||
| 1669 | double xx, yx, zx, xy, yy, zy, xz, yz, zz; | ||
| 1670 | |||
| 1671 | if (time == NULL) | ||
| 1672 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 1673 | |||
| 1674 | tilt = e_tilt(time); | ||
| 1675 | oblm = tilt.mobl * DEG2RAD; | ||
| 1676 | oblt = tilt.tobl * DEG2RAD; | ||
| 1677 | psi = tilt.dpsi * ASEC2RAD; | ||
| 1678 | cobm = cos(oblm); | ||
| 1679 | sobm = sin(oblm); | ||
| 1680 | cobt = cos(oblt); | ||
| 1681 | sobt = sin(oblt); | ||
| 1682 | cpsi = cos(psi); | ||
| 1683 | spsi = sin(psi); | ||
| 1684 | |||
| 1685 | xx = cpsi; | ||
| 1686 | yx = -spsi * cobm; | ||
| 1687 | zx = -spsi * sobm; | ||
| 1688 | xy = spsi * cobt; | ||
| 1689 | yy = cpsi * cobm * cobt + sobm * sobt; | ||
| 1690 | zy = cpsi * sobm * cobt - cobm * sobt; | ||
| 1691 | xz = spsi * sobt; | ||
| 1692 | yz = cpsi * cobm * sobt - sobm * cobt; | ||
| 1693 | zz = cpsi * sobm * sobt + cobm * cobt; | ||
| 1694 | |||
| 1695 | if (dir == FROM_2000) | ||
| 1696 | { | ||
| 1697 | /* convert J2000 to of-date */ | ||
| 1698 | rotation.rot[0][0] = xx; | ||
| 1699 | rotation.rot[0][1] = xy; | ||
| 1700 | rotation.rot[0][2] = xz; | ||
| 1701 | rotation.rot[1][0] = yx; | ||
| 1702 | rotation.rot[1][1] = yy; | ||
| 1703 | rotation.rot[1][2] = yz; | ||
| 1704 | rotation.rot[2][0] = zx; | ||
| 1705 | rotation.rot[2][1] = zy; | ||
| 1706 | rotation.rot[2][2] = zz; | ||
| 1707 | } | ||
| 1708 | else | ||
| 1709 | { | ||
| 1710 | /* convert of-date to J2000 */ | ||
| 1711 | rotation.rot[0][0] = xx; | ||
| 1712 | rotation.rot[0][1] = yx; | ||
| 1713 | rotation.rot[0][2] = zx; | ||
| 1714 | rotation.rot[1][0] = xy; | ||
| 1715 | rotation.rot[1][1] = yy; | ||
| 1716 | rotation.rot[1][2] = zy; | ||
| 1717 | rotation.rot[2][0] = xz; | ||
| 1718 | rotation.rot[2][1] = yz; | ||
| 1719 | rotation.rot[2][2] = zz; | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | rotation.status = ASTRO_SUCCESS; | ||
| 1723 | return rotation; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | static void nutation( | ||
| 1727 | const double inpos[3], | ||
| 1728 | astro_time_t *time, | ||
| 1729 | precess_dir_t dir, | ||
| 1730 | double outpos[3]) | ||
| 1731 | { | ||
| 1732 | astro_rotation_t r = nutation_rot(time, dir); | ||
| 1733 | rotate(inpos, r.rot, outpos); | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | static void nutation_posvel( | ||
| 1737 | const double inpos[3], | ||
| 1738 | const double invel[3], | ||
| 1739 | astro_time_t *time, | ||
| 1740 | precess_dir_t dir, | ||
| 1741 | double outpos[3], | ||
| 1742 | double outvel[3]) | ||
| 1743 | { | ||
| 1744 | astro_rotation_t r = nutation_rot(time, dir); | ||
| 1745 | rotate(inpos, r.rot, outpos); | ||
| 1746 | rotate(invel, r.rot, outvel); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | static double era(double ut) /* Earth Rotation Angle */ | ||
| 1750 | { | ||
| 1751 | double thet1 = 0.7790572732640 + 0.00273781191135448 * ut; | ||
| 1752 | double thet3 = fmod(ut, 1.0); | ||
| 1753 | double theta = 360.0 * fmod(thet1 + thet3, 1.0); | ||
| 1754 | if (theta < 0.0) | ||
| 1755 | theta += 360.0; | ||
| 1756 | |||
| 1757 | return theta; | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | /** | ||
| 1761 | * @brief Calculates Greenwich Apparent Sidereal Time (GAST). | ||
| 1762 | * | ||
| 1763 | * Given a date and time, this function calculates the rotation of the | ||
| 1764 | * Earth, represented by the equatorial angle of the Greenwich prime meridian | ||
| 1765 | * with respect to distant stars (not the Sun, which moves relative to background | ||
| 1766 | * stars by almost one degree per day). | ||
| 1767 | * This angle is called Greenwich Apparent Sidereal Time (GAST). | ||
| 1768 | * GAST is measured in sidereal hours in the half-open range [0, 24). | ||
| 1769 | * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, | ||
| 1770 | * corrected at that time for precession and nutation of the Earth's axis. | ||
| 1771 | * In this context, the "equinox" is the direction in space where the Earth's | ||
| 1772 | * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, | ||
| 1773 | * at the location on the Earth's orbit of the (seasonal) March equinox. | ||
| 1774 | * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, | ||
| 1775 | * then starts over at 0. | ||
| 1776 | * To convert to degrees, multiply the return value by 15. | ||
| 1777 | * | ||
| 1778 | * @param time | ||
| 1779 | * The date and time for which to find GAST. | ||
| 1780 | * The parameter is passed by address because it can be modified by the call: | ||
| 1781 | * As an optimization, this function caches the sidereal time value in `time`, | ||
| 1782 | * unless it has already been cached, in which case the cached value is reused. | ||
| 1783 | * If the `time` pointer is NULL, this function returns a NAN value. | ||
| 1784 | * | ||
| 1785 | * @returns {number} | ||
| 1786 | */ | ||
| 1787 | double Astronomy_SiderealTime(astro_time_t *time) | ||
| 1788 | { | ||
| 1789 | if (time == NULL) | ||
| 1790 | return NAN; | ||
| 1791 | |||
| 1792 | if (isnan(time->st)) | ||
| 1793 | { | ||
| 1794 | double t = time->tt / 36525.0; | ||
| 1795 | double eqeq = 15.0 * e_tilt(time).ee; /* Replace with eqeq=0 to get GMST instead of GAST (if we ever need it) */ | ||
| 1796 | double theta = era(time->ut); | ||
| 1797 | double st = (eqeq + 0.014506 + | ||
| 1798 | (((( - 0.0000000368 * t | ||
| 1799 | - 0.000029956 ) * t | ||
| 1800 | - 0.00000044 ) * t | ||
| 1801 | + 1.3915817 ) * t | ||
| 1802 | + 4612.156534 ) * t); | ||
| 1803 | |||
| 1804 | double gst = fmod(st/3600.0 + theta, 360.0) / 15.0; | ||
| 1805 | if (gst < 0.0) | ||
| 1806 | gst += 24.0; | ||
| 1807 | |||
| 1808 | time->st = gst; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | return time->st; /* return sidereal hours in the half-open range [0, 24). */ | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | static astro_observer_t inverse_terra(const double ovec[3], double st) | ||
| 1815 | { | ||
| 1816 | double x, y, z, p, F, W, D, c, s, c2, s2; | ||
| 1817 | double lon_deg, lat_deg, lat, radicand, factor, denom, adjust; | ||
| 1818 | double height_km, stlocl; | ||
| 1819 | astro_observer_t observer; | ||
| 1820 | int count; | ||
| 1821 | |||
| 1822 | /* Convert from AU to kilometers. */ | ||
| 1823 | x = ovec[0] * KM_PER_AU; | ||
| 1824 | y = ovec[1] * KM_PER_AU; | ||
| 1825 | z = ovec[2] * KM_PER_AU; | ||
| 1826 | p = hypot(x, y); | ||
| 1827 | if (p < 1.0e-6) | ||
| 1828 | { | ||
| 1829 | /* Special case: within 1 millimeter of a pole! */ | ||
| 1830 | /* Use arbitrary longitude, and latitude determined by polarity of z. */ | ||
| 1831 | lon_deg = 0.0; | ||
| 1832 | lat_deg = (z > 0.0) ? +90.0 : -90.0; | ||
| 1833 | /* Elevation is calculated directly from z */ | ||
| 1834 | height_km = fabs(z) - EARTH_POLAR_RADIUS_KM; | ||
| 1835 | } | ||
| 1836 | else | ||
| 1837 | { | ||
| 1838 | stlocl = atan2(y, x); | ||
| 1839 | /* Calculate exact longitude. */ | ||
| 1840 | lon_deg = RAD2DEG*stlocl - (15.0 * st); | ||
| 1841 | /* Normalize longitude to the range (-180, +180]. */ | ||
| 1842 | while (lon_deg <= -180.0) | ||
| 1843 | lon_deg += 360.0; | ||
| 1844 | while (lon_deg > +180.0) | ||
| 1845 | lon_deg -= 360.0; | ||
| 1846 | /* Numerically solve for exact latitude, using Newton's Method. */ | ||
| 1847 | F = EARTH_FLATTENING * EARTH_FLATTENING; | ||
| 1848 | /* Start with initial latitude estimate, based on a spherical Earth. */ | ||
| 1849 | lat = atan2(z, p); | ||
| 1850 | for (count = 0; ; ++count) | ||
| 1851 | { | ||
| 1852 | if (count > 10) | ||
| 1853 | { | ||
| 1854 | fprintf(stderr, "\nFATAL(inverse_terra): did not converge!\n"); | ||
| 1855 | exit(1); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | /* Calculate the error function W(lat). */ | ||
| 1859 | /* We try to find the root of W, meaning where the error is 0. */ | ||
| 1860 | c = cos(lat); | ||
| 1861 | s = sin(lat); | ||
| 1862 | factor = (F-1)*EARTH_EQUATORIAL_RADIUS_KM; | ||
| 1863 | c2 = c*c; | ||
| 1864 | s2 = s*s; | ||
| 1865 | radicand = c2 + F*s2; | ||
| 1866 | denom = sqrt(radicand); | ||
| 1867 | W = (factor*s*c)/denom - z*c + p*s; | ||
| 1868 | if (fabs(W) < 1.0e-8) | ||
| 1869 | break; /* The error is now negligible. */ | ||
| 1870 | /* Error is still too large. Find the next estimate. */ | ||
| 1871 | /* Calculate D = the derivative of W with respect to lat. */ | ||
| 1872 | D = factor*((c2 - s2)/denom - s2*c2*(F-1)/(factor*radicand)) + z*s + p*c; | ||
| 1873 | lat -= W/D; | ||
| 1874 | } | ||
| 1875 | /* We now have a solution for the latitude in radians. */ | ||
| 1876 | lat_deg = lat * RAD2DEG; | ||
| 1877 | /* Solve for exact height in meters. */ | ||
| 1878 | /* There are two formulas I can use. Use whichever has the less risky denominator. */ | ||
| 1879 | adjust = EARTH_EQUATORIAL_RADIUS_KM / denom; | ||
| 1880 | if (fabs(s) > fabs(c)) | ||
| 1881 | height_km = z/s - F*adjust; | ||
| 1882 | else | ||
| 1883 | height_km = p/c - adjust; | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | observer.latitude = lat_deg; | ||
| 1887 | observer.longitude = lon_deg; | ||
| 1888 | observer.height = 1000.0 * height_km; | ||
| 1889 | return observer; | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | static void terra(astro_observer_t observer, double st, double pos[3], double vel[3]) | ||
| 1893 | { | ||
| 1894 | static const double ANGVEL = 7.2921150e-5; | ||
| 1895 | |||
| 1896 | double phi = observer.latitude * DEG2RAD; | ||
| 1897 | double sinphi = sin(phi); | ||
| 1898 | double cosphi = cos(phi); | ||
| 1899 | double c = 1.0 / hypot(cosphi, sinphi*EARTH_FLATTENING); | ||
| 1900 | double s = c * (EARTH_FLATTENING * EARTH_FLATTENING); | ||
| 1901 | double ht_km = observer.height / 1000.0; | ||
| 1902 | double ach = EARTH_EQUATORIAL_RADIUS_KM*c + ht_km; | ||
| 1903 | double ash = EARTH_EQUATORIAL_RADIUS_KM*s + ht_km; | ||
| 1904 | double stlocl = (15.0*st + observer.longitude) * DEG2RAD; | ||
| 1905 | double sinst = sin(stlocl); | ||
| 1906 | double cosst = cos(stlocl); | ||
| 1907 | |||
| 1908 | if (pos != NULL) | ||
| 1909 | { | ||
| 1910 | pos[0] = ach * cosphi * cosst / KM_PER_AU; | ||
| 1911 | pos[1] = ach * cosphi * sinst / KM_PER_AU; | ||
| 1912 | pos[2] = ash * sinphi / KM_PER_AU; | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | if (vel != NULL) | ||
| 1916 | { | ||
| 1917 | vel[0] = -(ANGVEL * 86400.0 / KM_PER_AU) * ach * cosphi * sinst; | ||
| 1918 | vel[1] = +(ANGVEL * 86400.0 / KM_PER_AU) * ach * cosphi * cosst; | ||
| 1919 | vel[2] = 0.0; | ||
| 1920 | } | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | static void geo_pos(astro_time_t *time, astro_observer_t observer, double pos[3]) | ||
| 1924 | { | ||
| 1925 | double gast; | ||
| 1926 | double pos1[3], pos2[3]; | ||
| 1927 | |||
| 1928 | if (time == NULL) | ||
| 1929 | { | ||
| 1930 | pos[0] = pos[1] = pos[2] = NAN; | ||
| 1931 | } | ||
| 1932 | else | ||
| 1933 | { | ||
| 1934 | gast = Astronomy_SiderealTime(time); | ||
| 1935 | terra(observer, gast, pos1, NULL); | ||
| 1936 | nutation(pos1, time, INTO_2000, pos2); | ||
| 1937 | precession(pos2, *time, INTO_2000, pos); | ||
| 1938 | } | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | static void spin(double angle, const double pos1[3], double vec2[3]) | ||
| 1942 | { | ||
| 1943 | double angr = angle * DEG2RAD; | ||
| 1944 | double cosang = cos(angr); | ||
| 1945 | double sinang = sin(angr); | ||
| 1946 | vec2[0] = +cosang*pos1[0] + sinang*pos1[1]; | ||
| 1947 | vec2[1] = -sinang*pos1[0] + cosang*pos1[1]; | ||
| 1948 | vec2[2] = pos1[2]; | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | /*------------------ CalcMoon ------------------*/ | ||
| 1952 | |||
| 1953 | /** @cond DOXYGEN_SKIP */ | ||
| 1954 | |||
| 1955 | #define DECLARE_PASCAL_ARRAY_1(elemtype,name,xmin,xmax) \ | ||
| 1956 | elemtype name[(xmax)-(xmin)+1] | ||
| 1957 | |||
| 1958 | #define DECLARE_PASCAL_ARRAY_2(elemtype,name,xmin,xmax,ymin,ymax) \ | ||
| 1959 | elemtype name[(xmax)-(xmin)+1][(ymax)-(ymin)+1] | ||
| 1960 | |||
| 1961 | #define ACCESS_PASCAL_ARRAY_1(name,xmin,x) \ | ||
| 1962 | ((name)[(x)-(xmin)]) | ||
| 1963 | |||
| 1964 | #define ACCESS_PASCAL_ARRAY_2(name,xmin,ymin,x,y) \ | ||
| 1965 | ((name)[(x)-(xmin)][(y)-(ymin)]) | ||
| 1966 | |||
| 1967 | typedef struct | ||
| 1968 | { | ||
| 1969 | double t; | ||
| 1970 | double dgam; | ||
| 1971 | double dlam, n, gam1c, sinpi; | ||
| 1972 | double l0, l, ls, f, d, s; | ||
| 1973 | double dl0, dl, dls, df, dd, ds; | ||
| 1974 | DECLARE_PASCAL_ARRAY_2(double,co,-6,6,1,4); /* ARRAY[-6..6,1..4] OF REAL */ | ||
| 1975 | DECLARE_PASCAL_ARRAY_2(double,si,-6,6,1,4); /* ARRAY[-6..6,1..4] OF REAL */ | ||
| 1976 | } | ||
| 1977 | MoonContext; | ||
| 1978 | |||
| 1979 | #define T (ctx->t) | ||
| 1980 | #define DGAM (ctx->dgam) | ||
| 1981 | #define DLAM (ctx->dlam) | ||
| 1982 | #define N (ctx->n) | ||
| 1983 | #define GAM1C (ctx->gam1c) | ||
| 1984 | #define SINPI (ctx->sinpi) | ||
| 1985 | #define L0 (ctx->l0) | ||
| 1986 | #define L (ctx->l) | ||
| 1987 | #define LS (ctx->ls) | ||
| 1988 | #define F (ctx->f) | ||
| 1989 | #define D (ctx->d) | ||
| 1990 | #define S (ctx->s) | ||
| 1991 | #define DL0 (ctx->dl0) | ||
| 1992 | #define DL (ctx->dl) | ||
| 1993 | #define DLS (ctx->dls) | ||
| 1994 | #define DF (ctx->df) | ||
| 1995 | #define DD (ctx->dd) | ||
| 1996 | #define DS (ctx->ds) | ||
| 1997 | #define CO(x,y) ACCESS_PASCAL_ARRAY_2(ctx->co,-6,1,x,y) | ||
| 1998 | #define SI(x,y) ACCESS_PASCAL_ARRAY_2(ctx->si,-6,1,x,y) | ||
| 1999 | |||
| 2000 | static double Frac(double x) | ||
| 2001 | { | ||
| 2002 | return x - floor(x); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | static void AddThe( | ||
| 2006 | double c1, double s1, double c2, double s2, | ||
| 2007 | double *c, double *s) | ||
| 2008 | { | ||
| 2009 | *c = c1*c2 - s1*s2; | ||
| 2010 | *s = s1*c2 + c1*s2; | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | static double Sine(double phi) | ||
| 2014 | { | ||
| 2015 | /* sine, of phi in revolutions, not radians */ | ||
| 2016 | return sin(PI2 * phi); | ||
| 2017 | } | ||
| 2018 | |||
| 2019 | static void LongPeriodic(MoonContext *ctx) | ||
| 2020 | { | ||
| 2021 | double S1 = Sine(0.19833+0.05611*T); | ||
| 2022 | double S2 = Sine(0.27869+0.04508*T); | ||
| 2023 | double S3 = Sine(0.16827-0.36903*T); | ||
| 2024 | double S4 = Sine(0.34734-5.37261*T); | ||
| 2025 | double S5 = Sine(0.10498-5.37899*T); | ||
| 2026 | double S6 = Sine(0.42681-0.41855*T); | ||
| 2027 | double S7 = Sine(0.14943-5.37511*T); | ||
| 2028 | |||
| 2029 | DL0 = 0.84*S1+0.31*S2+14.27*S3+ 7.26*S4+ 0.28*S5+0.24*S6; | ||
| 2030 | DL = 2.94*S1+0.31*S2+14.27*S3+ 9.34*S4+ 1.12*S5+0.83*S6; | ||
| 2031 | DLS =-6.40*S1 -1.89*S6; | ||
| 2032 | DF = 0.21*S1+0.31*S2+14.27*S3-88.70*S4-15.30*S5+0.24*S6-1.86*S7; | ||
| 2033 | DD = DL0-DLS; | ||
| 2034 | DGAM = -3332E-9 * Sine(0.59734-5.37261*T) | ||
| 2035 | -539E-9 * Sine(0.35498-5.37899*T) | ||
| 2036 | -64E-9 * Sine(0.39943-5.37511*T); | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | static void Init(MoonContext *ctx) | ||
| 2040 | { | ||
| 2041 | int I, J, MAX; | ||
| 2042 | double T2, ARG, FAC; | ||
| 2043 | |||
| 2044 | T2 = T*T; | ||
| 2045 | DLAM = 0; | ||
| 2046 | DS = 0; | ||
| 2047 | GAM1C = 0; | ||
| 2048 | SINPI = 3422.7000; | ||
| 2049 | LongPeriodic(ctx); | ||
| 2050 | L0 = PI2*Frac(0.60643382+1336.85522467*T-0.00000313*T2) + DL0/ARC; | ||
| 2051 | L = PI2*Frac(0.37489701+1325.55240982*T+0.00002565*T2) + DL /ARC; | ||
| 2052 | LS = PI2*Frac(0.99312619+ 99.99735956*T-0.00000044*T2) + DLS/ARC; | ||
| 2053 | F = PI2*Frac(0.25909118+1342.22782980*T-0.00000892*T2) + DF /ARC; | ||
| 2054 | D = PI2*Frac(0.82736186+1236.85308708*T-0.00000397*T2) + DD /ARC; | ||
| 2055 | for (I=1; I<=4; ++I) | ||
| 2056 | { | ||
| 2057 | switch(I) | ||
| 2058 | { | ||
| 2059 | case 1: ARG=L; MAX=4; FAC=1.000002208; break; | ||
| 2060 | case 2: ARG=LS; MAX=3; FAC=0.997504612-0.002495388*T; break; | ||
| 2061 | case 3: ARG=F; MAX=4; FAC=1.000002708+139.978*DGAM; break; | ||
| 2062 | default: ARG=D; MAX=6; FAC=1.0; break; | ||
| 2063 | } | ||
| 2064 | CO(0,I) = 1.0; | ||
| 2065 | CO(1,I) = cos(ARG)*FAC; | ||
| 2066 | SI(0,I) = 0.0; | ||
| 2067 | SI(1,I) = sin(ARG)*FAC; | ||
| 2068 | for (J=2; J<=MAX; ++J) | ||
| 2069 | AddThe(CO(J-1,I), SI(J-1,I), CO(1,I), SI(1,I), &CO(J,I), &SI(J,I)); | ||
| 2070 | |||
| 2071 | for (J=1; J<=MAX; ++J) | ||
| 2072 | { | ||
| 2073 | CO(-J,I) = CO(J,I); | ||
| 2074 | SI(-J,I) = -SI(J,I); | ||
| 2075 | } | ||
| 2076 | } | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | static void Term(const MoonContext *ctx, int p, int q, int r, int s, double *x, double *y) | ||
| 2080 | { | ||
| 2081 | int k; | ||
| 2082 | DECLARE_PASCAL_ARRAY_1(int, i, 1, 4); | ||
| 2083 | #define I(n) ACCESS_PASCAL_ARRAY_1(i,1,n) | ||
| 2084 | |||
| 2085 | I(1) = p; | ||
| 2086 | I(2) = q; | ||
| 2087 | I(3) = r; | ||
| 2088 | I(4) = s; | ||
| 2089 | *x = 1.0; | ||
| 2090 | *y = 0.0; | ||
| 2091 | |||
| 2092 | for (k=1; k<=4; ++k) | ||
| 2093 | if (I(k) != 0.0) | ||
| 2094 | AddThe(*x, *y, CO(I(k), k), SI(I(k), k), x, y); | ||
| 2095 | |||
| 2096 | #undef I | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | static void AddSol( | ||
| 2100 | MoonContext *ctx, | ||
| 2101 | double coeffl, | ||
| 2102 | double coeffs, | ||
| 2103 | double coeffg, | ||
| 2104 | double coeffp, | ||
| 2105 | int p, | ||
| 2106 | int q, | ||
| 2107 | int r, | ||
| 2108 | int s) | ||
| 2109 | { | ||
| 2110 | double x, y; | ||
| 2111 | Term(ctx, p, q, r, s, &x, &y); | ||
| 2112 | DLAM += coeffl*y; | ||
| 2113 | DS += coeffs*y; | ||
| 2114 | GAM1C += coeffg*x; | ||
| 2115 | SINPI += coeffp*x; | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | #define ADDN(coeffn,p,q,r,s) ( Term(ctx, (p),(q),(r),(s),&x,&y), (N += (coeffn)*y) ) | ||
| 2119 | |||
| 2120 | static void SolarN(MoonContext *ctx) | ||
| 2121 | { | ||
| 2122 | double x, y; | ||
| 2123 | |||
| 2124 | N = 0.0; | ||
| 2125 | ADDN(-526.069, 0, 0,1,-2); | ||
| 2126 | ADDN( -3.352, 0, 0,1,-4); | ||
| 2127 | ADDN( +44.297,+1, 0,1,-2); | ||
| 2128 | ADDN( -6.000,+1, 0,1,-4); | ||
| 2129 | ADDN( +20.599,-1, 0,1, 0); | ||
| 2130 | ADDN( -30.598,-1, 0,1,-2); | ||
| 2131 | ADDN( -24.649,-2, 0,1, 0); | ||
| 2132 | ADDN( -2.000,-2, 0,1,-2); | ||
| 2133 | ADDN( -22.571, 0,+1,1,-2); | ||
| 2134 | ADDN( +10.985, 0,-1,1,-2); | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | static void Planetary(MoonContext *ctx) | ||
| 2138 | { | ||
| 2139 | DLAM += | ||
| 2140 | +0.82*Sine(0.7736 -62.5512*T)+0.31*Sine(0.0466 -125.1025*T) | ||
| 2141 | +0.35*Sine(0.5785 -25.1042*T)+0.66*Sine(0.4591+1335.8075*T) | ||
| 2142 | +0.64*Sine(0.3130 -91.5680*T)+1.14*Sine(0.1480+1331.2898*T) | ||
| 2143 | +0.21*Sine(0.5918+1056.5859*T)+0.44*Sine(0.5784+1322.8595*T) | ||
| 2144 | +0.24*Sine(0.2275 -5.7374*T)+0.28*Sine(0.2965 +2.6929*T) | ||
| 2145 | +0.33*Sine(0.3132 +6.3368*T); | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | int _CalcMoonCount; /* Undocumented global for performance tuning. */ | ||
| 2149 | |||
| 2150 | static void CalcMoon( | ||
| 2151 | double centuries_since_j2000, | ||
| 2152 | double *geo_eclip_lon, /* (LAMBDA) equinox of date */ | ||
| 2153 | double *geo_eclip_lat, /* (BETA) equinox of date */ | ||
| 2154 | double *distance_au) /* (R) */ | ||
| 2155 | { | ||
| 2156 | double lat_seconds; | ||
| 2157 | MoonContext context; | ||
| 2158 | MoonContext *ctx = &context; /* goofy, but makes macros work inside this function */ | ||
| 2159 | |||
| 2160 | context.t = centuries_since_j2000; | ||
| 2161 | Init(ctx); | ||
| 2162 | |||
| 2163 | AddSol(ctx, 13.9020, 14.0600, -0.0010, 0.2607, 0, 0, 0, 4); | ||
| 2164 | AddSol(ctx, 0.4030, -4.0100, 0.3940, 0.0023, 0, 0, 0, 3); | ||
| 2165 | AddSol(ctx, 2369.9120, 2373.3600, 0.6010, 28.2333, 0, 0, 0, 2); | ||
| 2166 | AddSol(ctx, -125.1540, -112.7900, -0.7250, -0.9781, 0, 0, 0, 1); | ||
| 2167 | AddSol(ctx, 1.9790, 6.9800, -0.4450, 0.0433, 1, 0, 0, 4); | ||
| 2168 | AddSol(ctx, 191.9530, 192.7200, 0.0290, 3.0861, 1, 0, 0, 2); | ||
| 2169 | AddSol(ctx, -8.4660, -13.5100, 0.4550, -0.1093, 1, 0, 0, 1); | ||
| 2170 | AddSol(ctx, 22639.5000, 22609.0700, 0.0790, 186.5398, 1, 0, 0, 0); | ||
| 2171 | AddSol(ctx, 18.6090, 3.5900, -0.0940, 0.0118, 1, 0, 0,-1); | ||
| 2172 | AddSol(ctx, -4586.4650, -4578.1300, -0.0770, 34.3117, 1, 0, 0,-2); | ||
| 2173 | AddSol(ctx, 3.2150, 5.4400, 0.1920, -0.0386, 1, 0, 0,-3); | ||
| 2174 | AddSol(ctx, -38.4280, -38.6400, 0.0010, 0.6008, 1, 0, 0,-4); | ||
| 2175 | AddSol(ctx, -0.3930, -1.4300, -0.0920, 0.0086, 1, 0, 0,-6); | ||
| 2176 | AddSol(ctx, -0.2890, -1.5900, 0.1230, -0.0053, 0, 1, 0, 4); | ||
| 2177 | AddSol(ctx, -24.4200, -25.1000, 0.0400, -0.3000, 0, 1, 0, 2); | ||
| 2178 | AddSol(ctx, 18.0230, 17.9300, 0.0070, 0.1494, 0, 1, 0, 1); | ||
| 2179 | AddSol(ctx, -668.1460, -126.9800, -1.3020, -0.3997, 0, 1, 0, 0); | ||
| 2180 | AddSol(ctx, 0.5600, 0.3200, -0.0010, -0.0037, 0, 1, 0,-1); | ||
| 2181 | AddSol(ctx, -165.1450, -165.0600, 0.0540, 1.9178, 0, 1, 0,-2); | ||
| 2182 | AddSol(ctx, -1.8770, -6.4600, -0.4160, 0.0339, 0, 1, 0,-4); | ||
| 2183 | AddSol(ctx, 0.2130, 1.0200, -0.0740, 0.0054, 2, 0, 0, 4); | ||
| 2184 | AddSol(ctx, 14.3870, 14.7800, -0.0170, 0.2833, 2, 0, 0, 2); | ||
| 2185 | AddSol(ctx, -0.5860, -1.2000, 0.0540, -0.0100, 2, 0, 0, 1); | ||
| 2186 | AddSol(ctx, 769.0160, 767.9600, 0.1070, 10.1657, 2, 0, 0, 0); | ||
| 2187 | AddSol(ctx, 1.7500, 2.0100, -0.0180, 0.0155, 2, 0, 0,-1); | ||
| 2188 | AddSol(ctx, -211.6560, -152.5300, 5.6790, -0.3039, 2, 0, 0,-2); | ||
| 2189 | AddSol(ctx, 1.2250, 0.9100, -0.0300, -0.0088, 2, 0, 0,-3); | ||
| 2190 | AddSol(ctx, -30.7730, -34.0700, -0.3080, 0.3722, 2, 0, 0,-4); | ||
| 2191 | AddSol(ctx, -0.5700, -1.4000, -0.0740, 0.0109, 2, 0, 0,-6); | ||
| 2192 | AddSol(ctx, -2.9210, -11.7500, 0.7870, -0.0484, 1, 1, 0, 2); | ||
| 2193 | AddSol(ctx, 1.2670, 1.5200, -0.0220, 0.0164, 1, 1, 0, 1); | ||
| 2194 | AddSol(ctx, -109.6730, -115.1800, 0.4610, -0.9490, 1, 1, 0, 0); | ||
| 2195 | AddSol(ctx, -205.9620, -182.3600, 2.0560, 1.4437, 1, 1, 0,-2); | ||
| 2196 | AddSol(ctx, 0.2330, 0.3600, 0.0120, -0.0025, 1, 1, 0,-3); | ||
| 2197 | AddSol(ctx, -4.3910, -9.6600, -0.4710, 0.0673, 1, 1, 0,-4); | ||
| 2198 | AddSol(ctx, 0.2830, 1.5300, -0.1110, 0.0060, 1,-1, 0, 4); | ||
| 2199 | AddSol(ctx, 14.5770, 31.7000, -1.5400, 0.2302, 1,-1, 0, 2); | ||
| 2200 | AddSol(ctx, 147.6870, 138.7600, 0.6790, 1.1528, 1,-1, 0, 0); | ||
| 2201 | AddSol(ctx, -1.0890, 0.5500, 0.0210, 0.0000, 1,-1, 0,-1); | ||
| 2202 | AddSol(ctx, 28.4750, 23.5900, -0.4430, -0.2257, 1,-1, 0,-2); | ||
| 2203 | AddSol(ctx, -0.2760, -0.3800, -0.0060, -0.0036, 1,-1, 0,-3); | ||
| 2204 | AddSol(ctx, 0.6360, 2.2700, 0.1460, -0.0102, 1,-1, 0,-4); | ||
| 2205 | AddSol(ctx, -0.1890, -1.6800, 0.1310, -0.0028, 0, 2, 0, 2); | ||
| 2206 | AddSol(ctx, -7.4860, -0.6600, -0.0370, -0.0086, 0, 2, 0, 0); | ||
| 2207 | AddSol(ctx, -8.0960, -16.3500, -0.7400, 0.0918, 0, 2, 0,-2); | ||
| 2208 | AddSol(ctx, -5.7410, -0.0400, 0.0000, -0.0009, 0, 0, 2, 2); | ||
| 2209 | AddSol(ctx, 0.2550, 0.0000, 0.0000, 0.0000, 0, 0, 2, 1); | ||
| 2210 | AddSol(ctx, -411.6080, -0.2000, 0.0000, -0.0124, 0, 0, 2, 0); | ||
| 2211 | AddSol(ctx, 0.5840, 0.8400, 0.0000, 0.0071, 0, 0, 2,-1); | ||
| 2212 | AddSol(ctx, -55.1730, -52.1400, 0.0000, -0.1052, 0, 0, 2,-2); | ||
| 2213 | AddSol(ctx, 0.2540, 0.2500, 0.0000, -0.0017, 0, 0, 2,-3); | ||
| 2214 | AddSol(ctx, 0.0250, -1.6700, 0.0000, 0.0031, 0, 0, 2,-4); | ||
| 2215 | AddSol(ctx, 1.0600, 2.9600, -0.1660, 0.0243, 3, 0, 0, 2); | ||
| 2216 | AddSol(ctx, 36.1240, 50.6400, -1.3000, 0.6215, 3, 0, 0, 0); | ||
| 2217 | AddSol(ctx, -13.1930, -16.4000, 0.2580, -0.1187, 3, 0, 0,-2); | ||
| 2218 | AddSol(ctx, -1.1870, -0.7400, 0.0420, 0.0074, 3, 0, 0,-4); | ||
| 2219 | AddSol(ctx, -0.2930, -0.3100, -0.0020, 0.0046, 3, 0, 0,-6); | ||
| 2220 | AddSol(ctx, -0.2900, -1.4500, 0.1160, -0.0051, 2, 1, 0, 2); | ||
| 2221 | AddSol(ctx, -7.6490, -10.5600, 0.2590, -0.1038, 2, 1, 0, 0); | ||
| 2222 | AddSol(ctx, -8.6270, -7.5900, 0.0780, -0.0192, 2, 1, 0,-2); | ||
| 2223 | AddSol(ctx, -2.7400, -2.5400, 0.0220, 0.0324, 2, 1, 0,-4); | ||
| 2224 | AddSol(ctx, 1.1810, 3.3200, -0.2120, 0.0213, 2,-1, 0, 2); | ||
| 2225 | AddSol(ctx, 9.7030, 11.6700, -0.1510, 0.1268, 2,-1, 0, 0); | ||
| 2226 | AddSol(ctx, -0.3520, -0.3700, 0.0010, -0.0028, 2,-1, 0,-1); | ||
| 2227 | AddSol(ctx, -2.4940, -1.1700, -0.0030, -0.0017, 2,-1, 0,-2); | ||
| 2228 | AddSol(ctx, 0.3600, 0.2000, -0.0120, -0.0043, 2,-1, 0,-4); | ||
| 2229 | AddSol(ctx, -1.1670, -1.2500, 0.0080, -0.0106, 1, 2, 0, 0); | ||
| 2230 | AddSol(ctx, -7.4120, -6.1200, 0.1170, 0.0484, 1, 2, 0,-2); | ||
| 2231 | AddSol(ctx, -0.3110, -0.6500, -0.0320, 0.0044, 1, 2, 0,-4); | ||
| 2232 | AddSol(ctx, 0.7570, 1.8200, -0.1050, 0.0112, 1,-2, 0, 2); | ||
| 2233 | AddSol(ctx, 2.5800, 2.3200, 0.0270, 0.0196, 1,-2, 0, 0); | ||
| 2234 | AddSol(ctx, 2.5330, 2.4000, -0.0140, -0.0212, 1,-2, 0,-2); | ||
| 2235 | AddSol(ctx, -0.3440, -0.5700, -0.0250, 0.0036, 0, 3, 0,-2); | ||
| 2236 | AddSol(ctx, -0.9920, -0.0200, 0.0000, 0.0000, 1, 0, 2, 2); | ||
| 2237 | AddSol(ctx, -45.0990, -0.0200, 0.0000, -0.0010, 1, 0, 2, 0); | ||
| 2238 | AddSol(ctx, -0.1790, -9.5200, 0.0000, -0.0833, 1, 0, 2,-2); | ||
| 2239 | AddSol(ctx, -0.3010, -0.3300, 0.0000, 0.0014, 1, 0, 2,-4); | ||
| 2240 | AddSol(ctx, -6.3820, -3.3700, 0.0000, -0.0481, 1, 0,-2, 2); | ||
| 2241 | AddSol(ctx, 39.5280, 85.1300, 0.0000, -0.7136, 1, 0,-2, 0); | ||
| 2242 | AddSol(ctx, 9.3660, 0.7100, 0.0000, -0.0112, 1, 0,-2,-2); | ||
| 2243 | AddSol(ctx, 0.2020, 0.0200, 0.0000, 0.0000, 1, 0,-2,-4); | ||
| 2244 | AddSol(ctx, 0.4150, 0.1000, 0.0000, 0.0013, 0, 1, 2, 0); | ||
| 2245 | AddSol(ctx, -2.1520, -2.2600, 0.0000, -0.0066, 0, 1, 2,-2); | ||
| 2246 | AddSol(ctx, -1.4400, -1.3000, 0.0000, 0.0014, 0, 1,-2, 2); | ||
| 2247 | AddSol(ctx, 0.3840, -0.0400, 0.0000, 0.0000, 0, 1,-2,-2); | ||
| 2248 | AddSol(ctx, 1.9380, 3.6000, -0.1450, 0.0401, 4, 0, 0, 0); | ||
| 2249 | AddSol(ctx, -0.9520, -1.5800, 0.0520, -0.0130, 4, 0, 0,-2); | ||
| 2250 | AddSol(ctx, -0.5510, -0.9400, 0.0320, -0.0097, 3, 1, 0, 0); | ||
| 2251 | AddSol(ctx, -0.4820, -0.5700, 0.0050, -0.0045, 3, 1, 0,-2); | ||
| 2252 | AddSol(ctx, 0.6810, 0.9600, -0.0260, 0.0115, 3,-1, 0, 0); | ||
| 2253 | AddSol(ctx, -0.2970, -0.2700, 0.0020, -0.0009, 2, 2, 0,-2); | ||
| 2254 | AddSol(ctx, 0.2540, 0.2100, -0.0030, 0.0000, 2,-2, 0,-2); | ||
| 2255 | AddSol(ctx, -0.2500, -0.2200, 0.0040, 0.0014, 1, 3, 0,-2); | ||
| 2256 | AddSol(ctx, -3.9960, 0.0000, 0.0000, 0.0004, 2, 0, 2, 0); | ||
| 2257 | AddSol(ctx, 0.5570, -0.7500, 0.0000, -0.0090, 2, 0, 2,-2); | ||
| 2258 | AddSol(ctx, -0.4590, -0.3800, 0.0000, -0.0053, 2, 0,-2, 2); | ||
| 2259 | AddSol(ctx, -1.2980, 0.7400, 0.0000, 0.0004, 2, 0,-2, 0); | ||
| 2260 | AddSol(ctx, 0.5380, 1.1400, 0.0000, -0.0141, 2, 0,-2,-2); | ||
| 2261 | AddSol(ctx, 0.2630, 0.0200, 0.0000, 0.0000, 1, 1, 2, 0); | ||
| 2262 | AddSol(ctx, 0.4260, 0.0700, 0.0000, -0.0006, 1, 1,-2,-2); | ||
| 2263 | AddSol(ctx, -0.3040, 0.0300, 0.0000, 0.0003, 1,-1, 2, 0); | ||
| 2264 | AddSol(ctx, -0.3720, -0.1900, 0.0000, -0.0027, 1,-1,-2, 2); | ||
| 2265 | AddSol(ctx, 0.4180, 0.0000, 0.0000, 0.0000, 0, 0, 4, 0); | ||
| 2266 | AddSol(ctx, -0.3300, -0.0400, 0.0000, 0.0000, 3, 0, 2, 0); | ||
| 2267 | |||
| 2268 | SolarN(ctx); | ||
| 2269 | Planetary(ctx); | ||
| 2270 | S = F + DS/ARC; | ||
| 2271 | |||
| 2272 | lat_seconds = (1.000002708 + 139.978*DGAM)*(18518.511+1.189+GAM1C)*sin(S)-6.24*sin(3*S) + N; | ||
| 2273 | |||
| 2274 | *geo_eclip_lon = PI2 * Frac((L0+DLAM/ARC) / PI2); | ||
| 2275 | *geo_eclip_lat = lat_seconds * (DEG2RAD / 3600.0); | ||
| 2276 | *distance_au = (ARC * EARTH_EQUATORIAL_RADIUS_AU) / (0.999953253 * SINPI); | ||
| 2277 | ++_CalcMoonCount; | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | #undef T | ||
| 2281 | #undef DGAM | ||
| 2282 | #undef DLAM | ||
| 2283 | #undef N | ||
| 2284 | #undef GAM1C | ||
| 2285 | #undef SINPI | ||
| 2286 | #undef L0 | ||
| 2287 | #undef L | ||
| 2288 | #undef LS | ||
| 2289 | #undef F | ||
| 2290 | #undef D | ||
| 2291 | #undef S | ||
| 2292 | #undef DL0 | ||
| 2293 | #undef DL | ||
| 2294 | #undef DLS | ||
| 2295 | #undef DF | ||
| 2296 | #undef DD | ||
| 2297 | #undef DS | ||
| 2298 | #undef CO | ||
| 2299 | #undef SI | ||
| 2300 | |||
| 2301 | /** @endcond */ | ||
| 2302 | |||
| 2303 | /** | ||
| 2304 | * @brief Calculates equatorial geocentric position of the Moon at a given time. | ||
| 2305 | * | ||
| 2306 | * Given a time of observation, calculates the Moon's position as a vector. | ||
| 2307 | * The vector gives the location of the Moon's center relative to the Earth's center | ||
| 2308 | * with x-, y-, and z-components measured in astronomical units. | ||
| 2309 | * The coordinates are oriented with respect to the Earth's equator at the J2000 epoch. | ||
| 2310 | * In Astronomy Engine, this orientation is called EQJ. | ||
| 2311 | * | ||
| 2312 | * This algorithm is based on the Nautical Almanac Office's *Improved Lunar Ephemeris* of 1954, | ||
| 2313 | * which in turn derives from E. W. Brown's lunar theories from the early twentieth century. | ||
| 2314 | * It is adapted from Turbo Pascal code from the book | ||
| 2315 | * [Astronomy on the Personal Computer](https://www.springer.com/us/book/9783540672210) | ||
| 2316 | * by Montenbruck and Pfleger. | ||
| 2317 | * | ||
| 2318 | * To calculate ecliptic spherical coordinates instead, see #Astronomy_EclipticGeoMoon. | ||
| 2319 | * | ||
| 2320 | * @param time The date and time for which to calculate the Moon's position. | ||
| 2321 | * @return The Moon's position as a vector in J2000 Cartesian equatorial (EQJ) coordinates. | ||
| 2322 | */ | ||
| 2323 | astro_vector_t Astronomy_GeoMoon(astro_time_t time) | ||
| 2324 | { | ||
| 2325 | double geo_eclip_lon, geo_eclip_lat, distance_au; | ||
| 2326 | double dist_cos_lat; | ||
| 2327 | astro_vector_t vector; | ||
| 2328 | double gepos[3]; | ||
| 2329 | double mpos1[3]; | ||
| 2330 | double mpos2[3]; | ||
| 2331 | |||
| 2332 | CalcMoon(time.tt / 36525.0, &geo_eclip_lon, &geo_eclip_lat, &distance_au); | ||
| 2333 | |||
| 2334 | /* Convert geocentric ecliptic spherical coordinates to Cartesian coordinates. */ | ||
| 2335 | dist_cos_lat = distance_au * cos(geo_eclip_lat); | ||
| 2336 | gepos[0] = dist_cos_lat * cos(geo_eclip_lon); | ||
| 2337 | gepos[1] = dist_cos_lat * sin(geo_eclip_lon); | ||
| 2338 | gepos[2] = distance_au * sin(geo_eclip_lat); | ||
| 2339 | |||
| 2340 | /* Convert ecliptic coordinates to equatorial coordinates, both in mean equinox of date. */ | ||
| 2341 | ecl2equ_vec(time, gepos, mpos1); | ||
| 2342 | |||
| 2343 | /* Convert equatorial coordinates from mean equinox of date to J2000 mean equinox. */ | ||
| 2344 | precession(mpos1, time, INTO_2000, mpos2); | ||
| 2345 | |||
| 2346 | vector.status = ASTRO_SUCCESS; | ||
| 2347 | vector.x = mpos2[0]; | ||
| 2348 | vector.y = mpos2[1]; | ||
| 2349 | vector.z = mpos2[2]; | ||
| 2350 | vector.t = time; | ||
| 2351 | return vector; | ||
| 2352 | } | ||
| 2353 | |||
| 2354 | |||
| 2355 | /** | ||
| 2356 | * @brief Calculates spherical ecliptic geocentric position of the Moon. | ||
| 2357 | * | ||
| 2358 | * Given a time of observation, calculates the Moon's geocentric position | ||
| 2359 | * in ecliptic spherical coordinates. Provides the ecliptic latitude and | ||
| 2360 | * longitude in degrees, and the geocentric distance in astronomical units (AU). | ||
| 2361 | * | ||
| 2362 | * The ecliptic angles are measured in "ECT": relative to the true ecliptic plane and | ||
| 2363 | * equatorial plane at the specified time. This means the Earth's equator | ||
| 2364 | * is corrected for precession and nutation, and the plane of the Earth's | ||
| 2365 | * orbit is corrected for gradual obliquity drift. | ||
| 2366 | * | ||
| 2367 | * This algorithm is based on the Nautical Almanac Office's *Improved Lunar Ephemeris* of 1954, | ||
| 2368 | * which in turn derives from E. W. Brown's lunar theories from the early twentieth century. | ||
| 2369 | * It is adapted from Turbo Pascal code from the book | ||
| 2370 | * [Astronomy on the Personal Computer](https://www.springer.com/us/book/9783540672210) | ||
| 2371 | * by Montenbruck and Pfleger. | ||
| 2372 | * | ||
| 2373 | * To calculate a J2000 mean equator vector instead, use #Astronomy_GeoMoon. | ||
| 2374 | * | ||
| 2375 | * @param time The date and time for which to calculate the Moon's position. | ||
| 2376 | * @return The Moon's position expressed in ecliptic coordinates using the true equinox of date (ECT). | ||
| 2377 | */ | ||
| 2378 | astro_spherical_t Astronomy_EclipticGeoMoon(astro_time_t time) | ||
| 2379 | { | ||
| 2380 | astro_spherical_t sphere; | ||
| 2381 | astro_ecliptic_t eclip; | ||
| 2382 | earth_tilt_t et; | ||
| 2383 | double dist_cos_lat, ecm[3], eqm[3], eqd[3]; | ||
| 2384 | |||
| 2385 | /* CalcMoon produces ecliptic coordinates in mean equinox of date (ECM). */ | ||
| 2386 | CalcMoon(time.tt / 36525.0, &sphere.lon, &sphere.lat, &sphere.dist); | ||
| 2387 | |||
| 2388 | /* Calculate vector in ecliptic coordinates (ECM). */ | ||
| 2389 | dist_cos_lat = sphere.dist * cos(sphere.lat); | ||
| 2390 | ecm[0] = dist_cos_lat * cos(sphere.lon); | ||
| 2391 | ecm[1] = dist_cos_lat * sin(sphere.lon); | ||
| 2392 | ecm[2] = sphere.dist * sin(sphere.lat); | ||
| 2393 | |||
| 2394 | /* Obtain true and mean obliquity angles for the given time. */ | ||
| 2395 | /* This serves to pre-calculate the nutation also, and cache it in `time`. */ | ||
| 2396 | et = e_tilt(&time); | ||
| 2397 | |||
| 2398 | /* Convert ecliptic coordinates to equatorial coordinates, both in mean equinox of date. */ | ||
| 2399 | /* In other words, convert ECM to EQM. */ | ||
| 2400 | obl_ecl2equ_vec(et.mobl, time, ecm, eqm); | ||
| 2401 | |||
| 2402 | /* Add nutation to convert ECM to true equatorial coordinates of date (EQD). */ | ||
| 2403 | nutation(eqm, &time, FROM_2000, eqd); | ||
| 2404 | |||
| 2405 | /* Convert back to ecliptic, this time in true equinox of date (ECT). */ | ||
| 2406 | eclip = RotateEquatorialToEcliptic(eqd, DEG2RAD * et.tobl, time); | ||
| 2407 | |||
| 2408 | /* Package the return value. */ | ||
| 2409 | /* CalcMoon() already set sphere.dist to the correct value. */ | ||
| 2410 | sphere.status = eclip.status; | ||
| 2411 | sphere.lat = eclip.elat; | ||
| 2412 | sphere.lon = eclip.elon; | ||
| 2413 | return sphere; | ||
| 2414 | } | ||
| 2415 | |||
| 2416 | |||
| 2417 | /** | ||
| 2418 | * @brief Calculates equatorial geocentric position and velocity of the Moon at a given time. | ||
| 2419 | * | ||
| 2420 | * Given a time of observation, calculates the Moon's position and velocity vectors. | ||
| 2421 | * The position and velocity are of the Moon's center relative to the Earth's center. | ||
| 2422 | * The position (x, y, z) components are expressed in AU (astronomical units). | ||
| 2423 | * The velocity (vx, vy, vz) components are expressed in AU/day. | ||
| 2424 | * The coordinates are oriented with respect to the Earth's equator at the J2000 epoch. | ||
| 2425 | * In Astronomy Engine, this orientation is called EQJ. | ||
| 2426 | * | ||
| 2427 | * If you need the Moon's position only, and not its velocity, | ||
| 2428 | * it is much more efficient to use #Astronomy_GeoMoon instead. | ||
| 2429 | * | ||
| 2430 | * @param time The date and time for which to calculate the Moon's position and velocity. | ||
| 2431 | * @return The Moon's position and velocity vectors in J2000 equatorial coordinates (EQJ). | ||
| 2432 | */ | ||
| 2433 | astro_state_vector_t Astronomy_GeoMoonState(astro_time_t time) | ||
| 2434 | { | ||
| 2435 | /* | ||
| 2436 | This is a hack, because trying to figure out how to derive a time | ||
| 2437 | derivative for CalcMoon() would be extremely painful! | ||
| 2438 | Calculate just before and just after the given time. | ||
| 2439 | Average to find position, subtract to find velocity. | ||
| 2440 | */ | ||
| 2441 | const double dt = 1.0e-5; /* 0.864 seconds */ | ||
| 2442 | astro_vector_t r1, r2; | ||
| 2443 | astro_time_t t1, t2; | ||
| 2444 | astro_state_vector_t s; | ||
| 2445 | |||
| 2446 | t1 = Astronomy_AddDays(time, -dt); | ||
| 2447 | t2 = Astronomy_AddDays(time, +dt); | ||
| 2448 | |||
| 2449 | r1 = Astronomy_GeoMoon(t1); | ||
| 2450 | r2 = Astronomy_GeoMoon(t2); | ||
| 2451 | |||
| 2452 | /* The desired position is the average of the two calculated positions. */ | ||
| 2453 | s.x = (r1.x + r2.x) / 2; | ||
| 2454 | s.y = (r1.y + r2.y) / 2; | ||
| 2455 | s.z = (r1.z + r2.z) / 2; | ||
| 2456 | |||
| 2457 | /* The difference of the position vectors divided by the time span gives the velocity vector. */ | ||
| 2458 | s.vx = (r2.x - r1.x) / (2 * dt); | ||
| 2459 | s.vy = (r2.y - r1.y) / (2 * dt); | ||
| 2460 | s.vz = (r2.z - r1.z) / (2 * dt); | ||
| 2461 | s.t = time; | ||
| 2462 | s.status = ASTRO_SUCCESS; | ||
| 2463 | |||
| 2464 | return s; | ||
| 2465 | } | ||
| 2466 | |||
| 2467 | |||
| 2468 | /** | ||
| 2469 | * @brief Calculates the geocentric position and velocity of the Earth/Moon barycenter. | ||
| 2470 | * | ||
| 2471 | * Given a time of observation, calculates the geocentric position and velocity vectors | ||
| 2472 | * of the Earth/Moon barycenter (EMB). | ||
| 2473 | * The position (x, y, z) components are expressed in AU (astronomical units). | ||
| 2474 | * The velocity (vx, vy, vz) components are expressed in AU/day. | ||
| 2475 | * The coordinates are oriented with respect to the Earth's equator at the J2000 epoch. | ||
| 2476 | * In Astronomy Engine, this orientation is called EQJ. | ||
| 2477 | * | ||
| 2478 | * @param time The date and time for which to calculate the EMB vectors. | ||
| 2479 | * @return The EMB's position and velocity vectors in geocentric J2000 equatorial coordinates. | ||
| 2480 | */ | ||
| 2481 | astro_state_vector_t Astronomy_GeoEmbState(astro_time_t time) | ||
| 2482 | { | ||
| 2483 | astro_state_vector_t s = Astronomy_GeoMoonState(time); | ||
| 2484 | const double d = 1.0 + EARTH_MOON_MASS_RATIO; | ||
| 2485 | s.x /= d; | ||
| 2486 | s.y /= d; | ||
| 2487 | s.z /= d; | ||
| 2488 | s.vx /= d; | ||
| 2489 | s.vy /= d; | ||
| 2490 | s.vz /= d; | ||
| 2491 | return s; | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | |||
| 2495 | /** | ||
| 2496 | * @brief Calculates the Moon's libration angles at a given moment in time. | ||
| 2497 | * | ||
| 2498 | * Libration is an observed back-and-forth wobble of the portion of the | ||
| 2499 | * Moon visible from the Earth. It is caused by the imperfect tidal locking | ||
| 2500 | * of the Moon's fixed rotation rate, compared to its variable angular speed | ||
| 2501 | * of orbit around the Earth. | ||
| 2502 | * | ||
| 2503 | * This function calculates a pair of perpendicular libration angles, | ||
| 2504 | * one representing rotation of the Moon in ecliptic longitude `elon`, the other | ||
| 2505 | * in ecliptic latitude `elat`, both relative to the Moon's mean Earth-facing position. | ||
| 2506 | * | ||
| 2507 | * This function also returns the geocentric position of the Moon | ||
| 2508 | * expressed in ecliptic longitude `mlon`, ecliptic latitude `mlat`, the | ||
| 2509 | * distance `dist_km` between the centers of the Earth and Moon expressed in kilometers, | ||
| 2510 | * and the apparent angular diameter of the Moon `diam_deg`. | ||
| 2511 | * | ||
| 2512 | * @param time The date and time for which to calculate libration angles. | ||
| 2513 | * @return The Moon's ecliptic position and libration angles as seen from the Earth. | ||
| 2514 | */ | ||
| 2515 | astro_libration_t Astronomy_Libration(astro_time_t time) | ||
| 2516 | { | ||
| 2517 | astro_libration_t lib; | ||
| 2518 | double t, t2, t3, t4; | ||
| 2519 | double f, omega, w, a, ldash, ldash2, bdash, bdash2; | ||
| 2520 | double k1, k2, m, mdash, d, e, rho, sigma, tau; | ||
| 2521 | const double sin_I = sin(MOON_AXIS_INCLINATION_RADIANS); | ||
| 2522 | const double cos_I = cos(MOON_AXIS_INCLINATION_RADIANS); | ||
| 2523 | |||
| 2524 | t = time.tt / 36525.0; | ||
| 2525 | t2 = t * t; | ||
| 2526 | t3 = t2 * t; | ||
| 2527 | t4 = t2 * t2; | ||
| 2528 | |||
| 2529 | double mlon; /* Moon's ecliptic longitude in radians. */ | ||
| 2530 | double mlat; /* Moon's ecliptic latitude in radians. */ | ||
| 2531 | CalcMoon(t, &mlon, &mlat, &lib.dist_km); | ||
| 2532 | lib.mlon = RAD2DEG * mlon; | ||
| 2533 | lib.mlat = RAD2DEG * mlat; | ||
| 2534 | lib.dist_km *= KM_PER_AU; | ||
| 2535 | lib.diam_deg = (2.0 * RAD2DEG) * atan(MOON_MEAN_RADIUS_KM / sqrt(lib.dist_km*lib.dist_km - MOON_MEAN_RADIUS_KM*MOON_MEAN_RADIUS_KM)); | ||
| 2536 | |||
| 2537 | /* Moon's argument of latitude in radians. */ | ||
| 2538 | f = DEG2RAD * NormalizeLongitude(93.2720950 + 483202.0175233*t - 0.0036539*t2 - t3/3526000 + t4/863310000); | ||
| 2539 | |||
| 2540 | /* Moon's ascending node's mean longitude in radians. */ | ||
| 2541 | omega = DEG2RAD * NormalizeLongitude(125.0445479 - 1934.1362891*t + 0.0020754*t2 + t3/467441 - t4/60616000); | ||
| 2542 | |||
| 2543 | /* Sun's mean anomaly. */ | ||
| 2544 | m = DEG2RAD * NormalizeLongitude(357.5291092 + 35999.0502909*t - 0.0001536*t2 + t3/24490000); | ||
| 2545 | |||
| 2546 | /* Moon's mean anomaly. */ | ||
| 2547 | mdash = DEG2RAD * NormalizeLongitude(134.9633964 + 477198.8675055*t + 0.0087414*t2 + t3/69699 - t4/14712000); | ||
| 2548 | |||
| 2549 | /* Moon's mean elongation. */ | ||
| 2550 | d = DEG2RAD * NormalizeLongitude(297.8501921 + 445267.1114034*t - 0.0018819*t2 + t3/545868 - t4/113065000); | ||
| 2551 | |||
| 2552 | /* Eccentricity of the Earth's orbit. */ | ||
| 2553 | e = 1.0 - 0.002516*t - 0.0000074*t2; | ||
| 2554 | |||
| 2555 | /* Optical librations */ | ||
| 2556 | w = mlon - omega; | ||
| 2557 | a = atan2(sin(w)*cos(mlat)*cos_I - sin(mlat)*sin_I, cos(w)*cos(mlat)); | ||
| 2558 | ldash = LongitudeOffset(RAD2DEG * (a - f)); | ||
| 2559 | bdash = asin(-sin(w)*cos(mlat)*sin_I - sin(mlat)*cos_I); | ||
| 2560 | |||
| 2561 | /* Physical librations */ | ||
| 2562 | k1 = DEG2RAD*(119.75 + 131.849*t); | ||
| 2563 | k2 = DEG2RAD*(72.56 + 20.186*t); | ||
| 2564 | |||
| 2565 | rho = ( | ||
| 2566 | -0.02752*cos(mdash) + | ||
| 2567 | -0.02245*sin(f) + | ||
| 2568 | +0.00684*cos(mdash - 2*f) + | ||
| 2569 | -0.00293*cos(2*f) + | ||
| 2570 | -0.00085*cos(2*f - 2*d) + | ||
| 2571 | -0.00054*cos(mdash - 2*d) + | ||
| 2572 | -0.00020*sin(mdash + f) + | ||
| 2573 | -0.00020*cos(mdash + 2*f) + | ||
| 2574 | -0.00020*cos(mdash - f) + | ||
| 2575 | +0.00014*cos(mdash + 2*f - 2*d) | ||
| 2576 | ); | ||
| 2577 | |||
| 2578 | sigma = ( | ||
| 2579 | -0.02816*sin(mdash) + | ||
| 2580 | +0.02244*cos(f) + | ||
| 2581 | -0.00682*sin(mdash - 2*f) + | ||
| 2582 | -0.00279*sin(2*f) + | ||
| 2583 | -0.00083*sin(2*f - 2*d) + | ||
| 2584 | +0.00069*sin(mdash - 2*d) + | ||
| 2585 | +0.00040*cos(mdash + f) + | ||
| 2586 | -0.00025*sin(2*mdash) + | ||
| 2587 | -0.00023*sin(mdash + 2*f) + | ||
| 2588 | +0.00020*cos(mdash - f) + | ||
| 2589 | +0.00019*sin(mdash - f) + | ||
| 2590 | +0.00013*sin(mdash + 2*f - 2*d) + | ||
| 2591 | -0.00010*cos(mdash - 3*f) | ||
| 2592 | ); | ||
| 2593 | |||
| 2594 | tau = ( | ||
| 2595 | +0.02520*e*sin(m) + | ||
| 2596 | +0.00473*sin(2*mdash - 2*f) + | ||
| 2597 | -0.00467*sin(mdash) + | ||
| 2598 | +0.00396*sin(k1) + | ||
| 2599 | +0.00276*sin(2*mdash - 2*d) + | ||
| 2600 | +0.00196*sin(omega) + | ||
| 2601 | -0.00183*cos(mdash - f) + | ||
| 2602 | +0.00115*sin(mdash - 2*d) + | ||
| 2603 | -0.00096*sin(mdash - d) + | ||
| 2604 | +0.00046*sin(2*f - 2*d) + | ||
| 2605 | -0.00039*sin(mdash - f) + | ||
| 2606 | -0.00032*sin(mdash - m - d) + | ||
| 2607 | +0.00027*sin(2*mdash - m - 2*d) + | ||
| 2608 | +0.00023*sin(k2) + | ||
| 2609 | -0.00014*sin(2*d) + | ||
| 2610 | +0.00014*cos(2*mdash - 2*f) + | ||
| 2611 | -0.00012*sin(mdash - 2*f) + | ||
| 2612 | -0.00012*sin(2*mdash) + | ||
| 2613 | +0.00011*sin(2*mdash - 2*m - 2*d) | ||
| 2614 | ); | ||
| 2615 | |||
| 2616 | ldash2 = -tau + (rho*cos(a) + sigma*sin(a))*tan(bdash); | ||
| 2617 | bdash *= RAD2DEG; | ||
| 2618 | bdash2 = sigma*cos(a) - rho*sin(a); | ||
| 2619 | |||
| 2620 | lib.elon = ldash + ldash2; | ||
| 2621 | lib.elat = bdash + bdash2; | ||
| 2622 | |||
| 2623 | return lib; | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | |||
| 2627 | /*------------------ VSOP ------------------*/ | ||
| 2628 | |||
| 2629 | /** @cond DOXYGEN_SKIP */ | ||
| 2630 | typedef struct | ||
| 2631 | { | ||
| 2632 | double amplitude; | ||
| 2633 | double phase; | ||
| 2634 | double frequency; | ||
| 2635 | } | ||
| 2636 | vsop_term_t; | ||
| 2637 | |||
| 2638 | typedef struct | ||
| 2639 | { | ||
| 2640 | int nterms; | ||
| 2641 | const vsop_term_t *term; | ||
| 2642 | } | ||
| 2643 | vsop_series_t; | ||
| 2644 | |||
| 2645 | typedef struct | ||
| 2646 | { | ||
| 2647 | int nseries; | ||
| 2648 | const vsop_series_t *series; | ||
| 2649 | } | ||
| 2650 | vsop_formula_t; | ||
| 2651 | |||
| 2652 | typedef struct | ||
| 2653 | { | ||
| 2654 | const vsop_formula_t formula[3]; | ||
| 2655 | } | ||
| 2656 | vsop_model_t; | ||
| 2657 | |||
| 2658 | typedef struct | ||
| 2659 | { | ||
| 2660 | double mu; | ||
| 2661 | double al[2]; | ||
| 2662 | vsop_series_t a; | ||
| 2663 | vsop_series_t l; | ||
| 2664 | vsop_series_t z; | ||
| 2665 | vsop_series_t zeta; | ||
| 2666 | } | ||
| 2667 | jupiter_moon_t; | ||
| 2668 | /** @endcond */ | ||
| 2669 | |||
| 2670 | static const vsop_term_t vsop_lon_Mercury_0[] = | ||
| 2671 | { | ||
| 2672 | { 4.40250710144, 0.00000000000, 0.00000000000 }, | ||
| 2673 | { 0.40989414977, 1.48302034195, 26087.90314157420 }, | ||
| 2674 | { 0.05046294200, 4.47785489551, 52175.80628314840 }, | ||
| 2675 | { 0.00855346844, 1.16520322459, 78263.70942472259 }, | ||
| 2676 | { 0.00165590362, 4.11969163423, 104351.61256629678 }, | ||
| 2677 | { 0.00034561897, 0.77930768443, 130439.51570787099 }, | ||
| 2678 | { 0.00007583476, 3.71348404924, 156527.41884944518 } | ||
| 2679 | }; | ||
| 2680 | |||
| 2681 | static const vsop_term_t vsop_lon_Mercury_1[] = | ||
| 2682 | { | ||
| 2683 | { 26087.90313685529, 0.00000000000, 0.00000000000 }, | ||
| 2684 | { 0.01131199811, 6.21874197797, 26087.90314157420 }, | ||
| 2685 | { 0.00292242298, 3.04449355541, 52175.80628314840 }, | ||
| 2686 | { 0.00075775081, 6.08568821653, 78263.70942472259 }, | ||
| 2687 | { 0.00019676525, 2.80965111777, 104351.61256629678 } | ||
| 2688 | }; | ||
| 2689 | |||
| 2690 | static const vsop_series_t vsop_lon_Mercury[] = | ||
| 2691 | { | ||
| 2692 | { 7, vsop_lon_Mercury_0 }, | ||
| 2693 | { 5, vsop_lon_Mercury_1 } | ||
| 2694 | }; | ||
| 2695 | |||
| 2696 | static const vsop_term_t vsop_lat_Mercury_0[] = | ||
| 2697 | { | ||
| 2698 | { 0.11737528961, 1.98357498767, 26087.90314157420 }, | ||
| 2699 | { 0.02388076996, 5.03738959686, 52175.80628314840 }, | ||
| 2700 | { 0.01222839532, 3.14159265359, 0.00000000000 }, | ||
| 2701 | { 0.00543251810, 1.79644363964, 78263.70942472259 }, | ||
| 2702 | { 0.00129778770, 4.83232503958, 104351.61256629678 }, | ||
| 2703 | { 0.00031866927, 1.58088495658, 130439.51570787099 }, | ||
| 2704 | { 0.00007963301, 4.60972126127, 156527.41884944518 } | ||
| 2705 | }; | ||
| 2706 | |||
| 2707 | static const vsop_term_t vsop_lat_Mercury_1[] = | ||
| 2708 | { | ||
| 2709 | { 0.00274646065, 3.95008450011, 26087.90314157420 }, | ||
| 2710 | { 0.00099737713, 3.14159265359, 0.00000000000 } | ||
| 2711 | }; | ||
| 2712 | |||
| 2713 | static const vsop_series_t vsop_lat_Mercury[] = | ||
| 2714 | { | ||
| 2715 | { 7, vsop_lat_Mercury_0 }, | ||
| 2716 | { 2, vsop_lat_Mercury_1 } | ||
| 2717 | }; | ||
| 2718 | |||
| 2719 | static const vsop_term_t vsop_rad_Mercury_0[] = | ||
| 2720 | { | ||
| 2721 | { 0.39528271651, 0.00000000000, 0.00000000000 }, | ||
| 2722 | { 0.07834131818, 6.19233722598, 26087.90314157420 }, | ||
| 2723 | { 0.00795525558, 2.95989690104, 52175.80628314840 }, | ||
| 2724 | { 0.00121281764, 6.01064153797, 78263.70942472259 }, | ||
| 2725 | { 0.00021921969, 2.77820093972, 104351.61256629678 }, | ||
| 2726 | { 0.00004354065, 5.82894543774, 130439.51570787099 } | ||
| 2727 | }; | ||
| 2728 | |||
| 2729 | static const vsop_term_t vsop_rad_Mercury_1[] = | ||
| 2730 | { | ||
| 2731 | { 0.00217347740, 4.65617158665, 26087.90314157420 }, | ||
| 2732 | { 0.00044141826, 1.42385544001, 52175.80628314840 } | ||
| 2733 | }; | ||
| 2734 | |||
| 2735 | static const vsop_series_t vsop_rad_Mercury[] = | ||
| 2736 | { | ||
| 2737 | { 6, vsop_rad_Mercury_0 }, | ||
| 2738 | { 2, vsop_rad_Mercury_1 } | ||
| 2739 | }; | ||
| 2740 | |||
| 2741 | ; | ||
| 2742 | static const vsop_term_t vsop_lon_Venus_0[] = | ||
| 2743 | { | ||
| 2744 | { 3.17614666774, 0.00000000000, 0.00000000000 }, | ||
| 2745 | { 0.01353968419, 5.59313319619, 10213.28554621100 }, | ||
| 2746 | { 0.00089891645, 5.30650047764, 20426.57109242200 }, | ||
| 2747 | { 0.00005477194, 4.41630661466, 7860.41939243920 }, | ||
| 2748 | { 0.00003455741, 2.69964447820, 11790.62908865880 }, | ||
| 2749 | { 0.00002372061, 2.99377542079, 3930.20969621960 }, | ||
| 2750 | { 0.00001317168, 5.18668228402, 26.29831979980 }, | ||
| 2751 | { 0.00001664146, 4.25018630147, 1577.34354244780 }, | ||
| 2752 | { 0.00001438387, 4.15745084182, 9683.59458111640 }, | ||
| 2753 | { 0.00001200521, 6.15357116043, 30639.85663863300 } | ||
| 2754 | }; | ||
| 2755 | |||
| 2756 | static const vsop_term_t vsop_lon_Venus_1[] = | ||
| 2757 | { | ||
| 2758 | { 10213.28554621638, 0.00000000000, 0.00000000000 }, | ||
| 2759 | { 0.00095617813, 2.46406511110, 10213.28554621100 }, | ||
| 2760 | { 0.00007787201, 0.62478482220, 20426.57109242200 } | ||
| 2761 | }; | ||
| 2762 | |||
| 2763 | static const vsop_series_t vsop_lon_Venus[] = | ||
| 2764 | { | ||
| 2765 | { 10, vsop_lon_Venus_0 }, | ||
| 2766 | { 3, vsop_lon_Venus_1 } | ||
| 2767 | }; | ||
| 2768 | |||
| 2769 | static const vsop_term_t vsop_lat_Venus_0[] = | ||
| 2770 | { | ||
| 2771 | { 0.05923638472, 0.26702775812, 10213.28554621100 }, | ||
| 2772 | { 0.00040107978, 1.14737178112, 20426.57109242200 }, | ||
| 2773 | { 0.00032814918, 3.14159265359, 0.00000000000 } | ||
| 2774 | }; | ||
| 2775 | |||
| 2776 | static const vsop_term_t vsop_lat_Venus_1[] = | ||
| 2777 | { | ||
| 2778 | { 0.00287821243, 1.88964962838, 10213.28554621100 } | ||
| 2779 | }; | ||
| 2780 | |||
| 2781 | static const vsop_series_t vsop_lat_Venus[] = | ||
| 2782 | { | ||
| 2783 | { 3, vsop_lat_Venus_0 }, | ||
| 2784 | { 1, vsop_lat_Venus_1 } | ||
| 2785 | }; | ||
| 2786 | |||
| 2787 | static const vsop_term_t vsop_rad_Venus_0[] = | ||
| 2788 | { | ||
| 2789 | { 0.72334820891, 0.00000000000, 0.00000000000 }, | ||
| 2790 | { 0.00489824182, 4.02151831717, 10213.28554621100 }, | ||
| 2791 | { 0.00001658058, 4.90206728031, 20426.57109242200 }, | ||
| 2792 | { 0.00001378043, 1.12846591367, 11790.62908865880 }, | ||
| 2793 | { 0.00001632096, 2.84548795207, 7860.41939243920 }, | ||
| 2794 | { 0.00000498395, 2.58682193892, 9683.59458111640 }, | ||
| 2795 | { 0.00000221985, 2.01346696541, 19367.18916223280 }, | ||
| 2796 | { 0.00000237454, 2.55136053886, 15720.83878487840 } | ||
| 2797 | }; | ||
| 2798 | |||
| 2799 | static const vsop_term_t vsop_rad_Venus_1[] = | ||
| 2800 | { | ||
| 2801 | { 0.00034551041, 0.89198706276, 10213.28554621100 } | ||
| 2802 | }; | ||
| 2803 | |||
| 2804 | static const vsop_series_t vsop_rad_Venus[] = | ||
| 2805 | { | ||
| 2806 | { 8, vsop_rad_Venus_0 }, | ||
| 2807 | { 1, vsop_rad_Venus_1 } | ||
| 2808 | }; | ||
| 2809 | |||
| 2810 | ; | ||
| 2811 | static const vsop_term_t vsop_lon_Earth_0[] = | ||
| 2812 | { | ||
| 2813 | { 1.75347045673, 0.00000000000, 0.00000000000 }, | ||
| 2814 | { 0.03341656453, 4.66925680415, 6283.07584999140 }, | ||
| 2815 | { 0.00034894275, 4.62610242189, 12566.15169998280 }, | ||
| 2816 | { 0.00003417572, 2.82886579754, 3.52311834900 }, | ||
| 2817 | { 0.00003497056, 2.74411783405, 5753.38488489680 }, | ||
| 2818 | { 0.00003135899, 3.62767041756, 77713.77146812050 }, | ||
| 2819 | { 0.00002676218, 4.41808345438, 7860.41939243920 }, | ||
| 2820 | { 0.00002342691, 6.13516214446, 3930.20969621960 }, | ||
| 2821 | { 0.00001273165, 2.03709657878, 529.69096509460 }, | ||
| 2822 | { 0.00001324294, 0.74246341673, 11506.76976979360 }, | ||
| 2823 | { 0.00000901854, 2.04505446477, 26.29831979980 }, | ||
| 2824 | { 0.00001199167, 1.10962946234, 1577.34354244780 }, | ||
| 2825 | { 0.00000857223, 3.50849152283, 398.14900340820 }, | ||
| 2826 | { 0.00000779786, 1.17882681962, 5223.69391980220 }, | ||
| 2827 | { 0.00000990250, 5.23268072088, 5884.92684658320 }, | ||
| 2828 | { 0.00000753141, 2.53339052847, 5507.55323866740 }, | ||
| 2829 | { 0.00000505267, 4.58292599973, 18849.22754997420 }, | ||
| 2830 | { 0.00000492392, 4.20505711826, 775.52261132400 }, | ||
| 2831 | { 0.00000356672, 2.91954114478, 0.06731030280 }, | ||
| 2832 | { 0.00000284125, 1.89869240932, 796.29800681640 }, | ||
| 2833 | { 0.00000242879, 0.34481445893, 5486.77784317500 }, | ||
| 2834 | { 0.00000317087, 5.84901948512, 11790.62908865880 }, | ||
| 2835 | { 0.00000271112, 0.31486255375, 10977.07880469900 }, | ||
| 2836 | { 0.00000206217, 4.80646631478, 2544.31441988340 }, | ||
| 2837 | { 0.00000205478, 1.86953770281, 5573.14280143310 }, | ||
| 2838 | { 0.00000202318, 2.45767790232, 6069.77675455340 }, | ||
| 2839 | { 0.00000126225, 1.08295459501, 20.77539549240 }, | ||
| 2840 | { 0.00000155516, 0.83306084617, 213.29909543800 } | ||
| 2841 | }; | ||
| 2842 | |||
| 2843 | static const vsop_term_t vsop_lon_Earth_1[] = | ||
| 2844 | { | ||
| 2845 | { 6283.07584999140, 0.00000000000, 0.00000000000 }, | ||
| 2846 | { 0.00206058863, 2.67823455808, 6283.07584999140 }, | ||
| 2847 | { 0.00004303419, 2.63512233481, 12566.15169998280 } | ||
| 2848 | }; | ||
| 2849 | |||
| 2850 | static const vsop_term_t vsop_lon_Earth_2[] = | ||
| 2851 | { | ||
| 2852 | { 0.00008721859, 1.07253635559, 6283.07584999140 } | ||
| 2853 | }; | ||
| 2854 | |||
| 2855 | static const vsop_series_t vsop_lon_Earth[] = | ||
| 2856 | { | ||
| 2857 | { 28, vsop_lon_Earth_0 }, | ||
| 2858 | { 3, vsop_lon_Earth_1 }, | ||
| 2859 | { 1, vsop_lon_Earth_2 } | ||
| 2860 | }; | ||
| 2861 | |||
| 2862 | static const vsop_term_t vsop_lat_Earth_1[] = | ||
| 2863 | { | ||
| 2864 | { 0.00227777722, 3.41376620530, 6283.07584999140 }, | ||
| 2865 | { 0.00003805678, 3.37063423795, 12566.15169998280 } | ||
| 2866 | }; | ||
| 2867 | |||
| 2868 | static const vsop_series_t vsop_lat_Earth[] = | ||
| 2869 | { | ||
| 2870 | { 0, NULL }, | ||
| 2871 | { 2, vsop_lat_Earth_1 } | ||
| 2872 | }; | ||
| 2873 | |||
| 2874 | static const vsop_term_t vsop_rad_Earth_0[] = | ||
| 2875 | { | ||
| 2876 | { 1.00013988784, 0.00000000000, 0.00000000000 }, | ||
| 2877 | { 0.01670699632, 3.09846350258, 6283.07584999140 }, | ||
| 2878 | { 0.00013956024, 3.05524609456, 12566.15169998280 }, | ||
| 2879 | { 0.00003083720, 5.19846674381, 77713.77146812050 }, | ||
| 2880 | { 0.00001628463, 1.17387558054, 5753.38488489680 }, | ||
| 2881 | { 0.00001575572, 2.84685214877, 7860.41939243920 }, | ||
| 2882 | { 0.00000924799, 5.45292236722, 11506.76976979360 }, | ||
| 2883 | { 0.00000542439, 4.56409151453, 3930.20969621960 }, | ||
| 2884 | { 0.00000472110, 3.66100022149, 5884.92684658320 }, | ||
| 2885 | { 0.00000085831, 1.27079125277, 161000.68573767410 }, | ||
| 2886 | { 0.00000057056, 2.01374292245, 83996.84731811189 }, | ||
| 2887 | { 0.00000055736, 5.24159799170, 71430.69561812909 }, | ||
| 2888 | { 0.00000174844, 3.01193636733, 18849.22754997420 }, | ||
| 2889 | { 0.00000243181, 4.27349530790, 11790.62908865880 } | ||
| 2890 | }; | ||
| 2891 | |||
| 2892 | static const vsop_term_t vsop_rad_Earth_1[] = | ||
| 2893 | { | ||
| 2894 | { 0.00103018607, 1.10748968172, 6283.07584999140 }, | ||
| 2895 | { 0.00001721238, 1.06442300386, 12566.15169998280 } | ||
| 2896 | }; | ||
| 2897 | |||
| 2898 | static const vsop_term_t vsop_rad_Earth_2[] = | ||
| 2899 | { | ||
| 2900 | { 0.00004359385, 5.78455133808, 6283.07584999140 } | ||
| 2901 | }; | ||
| 2902 | |||
| 2903 | static const vsop_series_t vsop_rad_Earth[] = | ||
| 2904 | { | ||
| 2905 | { 14, vsop_rad_Earth_0 }, | ||
| 2906 | { 2, vsop_rad_Earth_1 }, | ||
| 2907 | { 1, vsop_rad_Earth_2 } | ||
| 2908 | }; | ||
| 2909 | |||
| 2910 | ; | ||
| 2911 | static const vsop_term_t vsop_lon_Mars_0[] = | ||
| 2912 | { | ||
| 2913 | { 6.20347711581, 0.00000000000, 0.00000000000 }, | ||
| 2914 | { 0.18656368093, 5.05037100270, 3340.61242669980 }, | ||
| 2915 | { 0.01108216816, 5.40099836344, 6681.22485339960 }, | ||
| 2916 | { 0.00091798406, 5.75478744667, 10021.83728009940 }, | ||
| 2917 | { 0.00027744987, 5.97049513147, 3.52311834900 }, | ||
| 2918 | { 0.00010610235, 2.93958560338, 2281.23049651060 }, | ||
| 2919 | { 0.00012315897, 0.84956094002, 2810.92146160520 }, | ||
| 2920 | { 0.00008926784, 4.15697846427, 0.01725365220 }, | ||
| 2921 | { 0.00008715691, 6.11005153139, 13362.44970679920 }, | ||
| 2922 | { 0.00006797556, 0.36462229657, 398.14900340820 }, | ||
| 2923 | { 0.00007774872, 3.33968761376, 5621.84292321040 }, | ||
| 2924 | { 0.00003575078, 1.66186505710, 2544.31441988340 }, | ||
| 2925 | { 0.00004161108, 0.22814971327, 2942.46342329160 }, | ||
| 2926 | { 0.00003075252, 0.85696614132, 191.44826611160 }, | ||
| 2927 | { 0.00002628117, 0.64806124465, 3337.08930835080 }, | ||
| 2928 | { 0.00002937546, 6.07893711402, 0.06731030280 }, | ||
| 2929 | { 0.00002389414, 5.03896442664, 796.29800681640 }, | ||
| 2930 | { 0.00002579844, 0.02996736156, 3344.13554504880 }, | ||
| 2931 | { 0.00001528141, 1.14979301996, 6151.53388830500 }, | ||
| 2932 | { 0.00001798806, 0.65634057445, 529.69096509460 }, | ||
| 2933 | { 0.00001264357, 3.62275122593, 5092.15195811580 }, | ||
| 2934 | { 0.00001286228, 3.06796065034, 2146.16541647520 }, | ||
| 2935 | { 0.00001546404, 2.91579701718, 1751.53953141600 }, | ||
| 2936 | { 0.00001024902, 3.69334099279, 8962.45534991020 }, | ||
| 2937 | { 0.00000891566, 0.18293837498, 16703.06213349900 }, | ||
| 2938 | { 0.00000858759, 2.40093811940, 2914.01423582380 }, | ||
| 2939 | { 0.00000832715, 2.46418619474, 3340.59517304760 }, | ||
| 2940 | { 0.00000832720, 4.49495782139, 3340.62968035200 }, | ||
| 2941 | { 0.00000712902, 3.66335473479, 1059.38193018920 }, | ||
| 2942 | { 0.00000748723, 3.82248614017, 155.42039943420 }, | ||
| 2943 | { 0.00000723861, 0.67497311481, 3738.76143010800 }, | ||
| 2944 | { 0.00000635548, 2.92182225127, 8432.76438481560 }, | ||
| 2945 | { 0.00000655162, 0.48864064125, 3127.31333126180 }, | ||
| 2946 | { 0.00000550474, 3.81001042328, 0.98032106820 }, | ||
| 2947 | { 0.00000552750, 4.47479317037, 1748.01641306700 }, | ||
| 2948 | { 0.00000425966, 0.55364317304, 6283.07584999140 }, | ||
| 2949 | { 0.00000415131, 0.49662285038, 213.29909543800 }, | ||
| 2950 | { 0.00000472167, 3.62547124025, 1194.44701022460 }, | ||
| 2951 | { 0.00000306551, 0.38052848348, 6684.74797174860 }, | ||
| 2952 | { 0.00000312141, 0.99853944405, 6677.70173505060 }, | ||
| 2953 | { 0.00000293198, 4.22131299634, 20.77539549240 }, | ||
| 2954 | { 0.00000302375, 4.48618007156, 3532.06069281140 }, | ||
| 2955 | { 0.00000274027, 0.54222167059, 3340.54511639700 }, | ||
| 2956 | { 0.00000281079, 5.88163521788, 1349.86740965880 }, | ||
| 2957 | { 0.00000231183, 1.28242156993, 3870.30339179440 }, | ||
| 2958 | { 0.00000283602, 5.76885434940, 3149.16416058820 }, | ||
| 2959 | { 0.00000236117, 5.75503217933, 3333.49887969900 }, | ||
| 2960 | { 0.00000274033, 0.13372524985, 3340.67973700260 }, | ||
| 2961 | { 0.00000299395, 2.78323740866, 6254.62666252360 } | ||
| 2962 | }; | ||
| 2963 | |||
| 2964 | static const vsop_term_t vsop_lon_Mars_1[] = | ||
| 2965 | { | ||
| 2966 | { 3340.61242700512, 0.00000000000, 0.00000000000 }, | ||
| 2967 | { 0.01457554523, 3.60433733236, 3340.61242669980 }, | ||
| 2968 | { 0.00168414711, 3.92318567804, 6681.22485339960 }, | ||
| 2969 | { 0.00020622975, 4.26108844583, 10021.83728009940 }, | ||
| 2970 | { 0.00003452392, 4.73210393190, 3.52311834900 }, | ||
| 2971 | { 0.00002586332, 4.60670058555, 13362.44970679920 }, | ||
| 2972 | { 0.00000841535, 4.45864030426, 2281.23049651060 } | ||
| 2973 | }; | ||
| 2974 | |||
| 2975 | static const vsop_term_t vsop_lon_Mars_2[] = | ||
| 2976 | { | ||
| 2977 | { 0.00058152577, 2.04961712429, 3340.61242669980 }, | ||
| 2978 | { 0.00013459579, 2.45738706163, 6681.22485339960 } | ||
| 2979 | }; | ||
| 2980 | |||
| 2981 | static const vsop_series_t vsop_lon_Mars[] = | ||
| 2982 | { | ||
| 2983 | { 49, vsop_lon_Mars_0 }, | ||
| 2984 | { 7, vsop_lon_Mars_1 }, | ||
| 2985 | { 2, vsop_lon_Mars_2 } | ||
| 2986 | }; | ||
| 2987 | |||
| 2988 | static const vsop_term_t vsop_lat_Mars_0[] = | ||
| 2989 | { | ||
| 2990 | { 0.03197134986, 3.76832042431, 3340.61242669980 }, | ||
| 2991 | { 0.00298033234, 4.10616996305, 6681.22485339960 }, | ||
| 2992 | { 0.00289104742, 0.00000000000, 0.00000000000 }, | ||
| 2993 | { 0.00031365539, 4.44651053090, 10021.83728009940 }, | ||
| 2994 | { 0.00003484100, 4.78812549260, 13362.44970679920 } | ||
| 2995 | }; | ||
| 2996 | |||
| 2997 | static const vsop_term_t vsop_lat_Mars_1[] = | ||
| 2998 | { | ||
| 2999 | { 0.00217310991, 6.04472194776, 3340.61242669980 }, | ||
| 3000 | { 0.00020976948, 3.14159265359, 0.00000000000 }, | ||
| 3001 | { 0.00012834709, 1.60810667915, 6681.22485339960 } | ||
| 3002 | }; | ||
| 3003 | |||
| 3004 | static const vsop_series_t vsop_lat_Mars[] = | ||
| 3005 | { | ||
| 3006 | { 5, vsop_lat_Mars_0 }, | ||
| 3007 | { 3, vsop_lat_Mars_1 } | ||
| 3008 | }; | ||
| 3009 | |||
| 3010 | static const vsop_term_t vsop_rad_Mars_0[] = | ||
| 3011 | { | ||
| 3012 | { 1.53033488271, 0.00000000000, 0.00000000000 }, | ||
| 3013 | { 0.14184953160, 3.47971283528, 3340.61242669980 }, | ||
| 3014 | { 0.00660776362, 3.81783443019, 6681.22485339960 }, | ||
| 3015 | { 0.00046179117, 4.15595316782, 10021.83728009940 }, | ||
| 3016 | { 0.00008109733, 5.55958416318, 2810.92146160520 }, | ||
| 3017 | { 0.00007485318, 1.77239078402, 5621.84292321040 }, | ||
| 3018 | { 0.00005523191, 1.36436303770, 2281.23049651060 }, | ||
| 3019 | { 0.00003825160, 4.49407183687, 13362.44970679920 }, | ||
| 3020 | { 0.00002306537, 0.09081579001, 2544.31441988340 }, | ||
| 3021 | { 0.00001999396, 5.36059617709, 3337.08930835080 }, | ||
| 3022 | { 0.00002484394, 4.92545639920, 2942.46342329160 }, | ||
| 3023 | { 0.00001960195, 4.74249437639, 3344.13554504880 }, | ||
| 3024 | { 0.00001167119, 2.11260868341, 5092.15195811580 }, | ||
| 3025 | { 0.00001102816, 5.00908403998, 398.14900340820 }, | ||
| 3026 | { 0.00000899066, 4.40791133207, 529.69096509460 }, | ||
| 3027 | { 0.00000992252, 5.83861961952, 6151.53388830500 }, | ||
| 3028 | { 0.00000807354, 2.10217065501, 1059.38193018920 }, | ||
| 3029 | { 0.00000797915, 3.44839203899, 796.29800681640 }, | ||
| 3030 | { 0.00000740975, 1.49906336885, 2146.16541647520 } | ||
| 3031 | }; | ||
| 3032 | |||
| 3033 | static const vsop_term_t vsop_rad_Mars_1[] = | ||
| 3034 | { | ||
| 3035 | { 0.01107433345, 2.03250524857, 3340.61242669980 }, | ||
| 3036 | { 0.00103175887, 2.37071847807, 6681.22485339960 }, | ||
| 3037 | { 0.00012877200, 0.00000000000, 0.00000000000 }, | ||
| 3038 | { 0.00010815880, 2.70888095665, 10021.83728009940 } | ||
| 3039 | }; | ||
| 3040 | |||
| 3041 | static const vsop_term_t vsop_rad_Mars_2[] = | ||
| 3042 | { | ||
| 3043 | { 0.00044242249, 0.47930604954, 3340.61242669980 }, | ||
| 3044 | { 0.00008138042, 0.86998389204, 6681.22485339960 } | ||
| 3045 | }; | ||
| 3046 | |||
| 3047 | static const vsop_series_t vsop_rad_Mars[] = | ||
| 3048 | { | ||
| 3049 | { 19, vsop_rad_Mars_0 }, | ||
| 3050 | { 4, vsop_rad_Mars_1 }, | ||
| 3051 | { 2, vsop_rad_Mars_2 } | ||
| 3052 | }; | ||
| 3053 | |||
| 3054 | ; | ||
| 3055 | static const vsop_term_t vsop_lon_Jupiter_0[] = | ||
| 3056 | { | ||
| 3057 | { 0.59954691494, 0.00000000000, 0.00000000000 }, | ||
| 3058 | { 0.09695898719, 5.06191793158, 529.69096509460 }, | ||
| 3059 | { 0.00573610142, 1.44406205629, 7.11354700080 }, | ||
| 3060 | { 0.00306389205, 5.41734730184, 1059.38193018920 }, | ||
| 3061 | { 0.00097178296, 4.14264726552, 632.78373931320 }, | ||
| 3062 | { 0.00072903078, 3.64042916389, 522.57741809380 }, | ||
| 3063 | { 0.00064263975, 3.41145165351, 103.09277421860 }, | ||
| 3064 | { 0.00039806064, 2.29376740788, 419.48464387520 }, | ||
| 3065 | { 0.00038857767, 1.27231755835, 316.39186965660 }, | ||
| 3066 | { 0.00027964629, 1.78454591820, 536.80451209540 }, | ||
| 3067 | { 0.00013589730, 5.77481040790, 1589.07289528380 }, | ||
| 3068 | { 0.00008246349, 3.58227925840, 206.18554843720 }, | ||
| 3069 | { 0.00008768704, 3.63000308199, 949.17560896980 }, | ||
| 3070 | { 0.00007368042, 5.08101194270, 735.87651353180 }, | ||
| 3071 | { 0.00006263150, 0.02497628807, 213.29909543800 }, | ||
| 3072 | { 0.00006114062, 4.51319998626, 1162.47470440780 }, | ||
| 3073 | { 0.00004905396, 1.32084470588, 110.20632121940 }, | ||
| 3074 | { 0.00005305285, 1.30671216791, 14.22709400160 }, | ||
| 3075 | { 0.00005305441, 4.18625634012, 1052.26838318840 }, | ||
| 3076 | { 0.00004647248, 4.69958103684, 3.93215326310 }, | ||
| 3077 | { 0.00003045023, 4.31676431084, 426.59819087600 }, | ||
| 3078 | { 0.00002609999, 1.56667394063, 846.08283475120 }, | ||
| 3079 | { 0.00002028191, 1.06376530715, 3.18139373770 }, | ||
| 3080 | { 0.00001764763, 2.14148655117, 1066.49547719000 }, | ||
| 3081 | { 0.00001722972, 3.88036268267, 1265.56747862640 }, | ||
| 3082 | { 0.00001920945, 0.97168196472, 639.89728631400 }, | ||
| 3083 | { 0.00001633223, 3.58201833555, 515.46387109300 }, | ||
| 3084 | { 0.00001431999, 4.29685556046, 625.67019231240 }, | ||
| 3085 | { 0.00000973272, 4.09764549134, 95.97922721780 } | ||
| 3086 | }; | ||
| 3087 | |||
| 3088 | static const vsop_term_t vsop_lon_Jupiter_1[] = | ||
| 3089 | { | ||
| 3090 | { 529.69096508814, 0.00000000000, 0.00000000000 }, | ||
| 3091 | { 0.00489503243, 4.22082939470, 529.69096509460 }, | ||
| 3092 | { 0.00228917222, 6.02646855621, 7.11354700080 }, | ||
| 3093 | { 0.00030099479, 4.54540782858, 1059.38193018920 }, | ||
| 3094 | { 0.00020720920, 5.45943156902, 522.57741809380 }, | ||
| 3095 | { 0.00012103653, 0.16994816098, 536.80451209540 }, | ||
| 3096 | { 0.00006067987, 4.42422292017, 103.09277421860 }, | ||
| 3097 | { 0.00005433968, 3.98480737746, 419.48464387520 }, | ||
| 3098 | { 0.00004237744, 5.89008707199, 14.22709400160 } | ||
| 3099 | }; | ||
| 3100 | |||
| 3101 | static const vsop_term_t vsop_lon_Jupiter_2[] = | ||
| 3102 | { | ||
| 3103 | { 0.00047233601, 4.32148536482, 7.11354700080 }, | ||
| 3104 | { 0.00030649436, 2.92977788700, 529.69096509460 }, | ||
| 3105 | { 0.00014837605, 3.14159265359, 0.00000000000 } | ||
| 3106 | }; | ||
| 3107 | |||
| 3108 | static const vsop_series_t vsop_lon_Jupiter[] = | ||
| 3109 | { | ||
| 3110 | { 29, vsop_lon_Jupiter_0 }, | ||
| 3111 | { 9, vsop_lon_Jupiter_1 }, | ||
| 3112 | { 3, vsop_lon_Jupiter_2 } | ||
| 3113 | }; | ||
| 3114 | |||
| 3115 | static const vsop_term_t vsop_lat_Jupiter_0[] = | ||
| 3116 | { | ||
| 3117 | { 0.02268615702, 3.55852606721, 529.69096509460 }, | ||
| 3118 | { 0.00109971634, 3.90809347197, 1059.38193018920 }, | ||
| 3119 | { 0.00110090358, 0.00000000000, 0.00000000000 }, | ||
| 3120 | { 0.00008101428, 3.60509572885, 522.57741809380 }, | ||
| 3121 | { 0.00006043996, 4.25883108339, 1589.07289528380 }, | ||
| 3122 | { 0.00006437782, 0.30627119215, 536.80451209540 } | ||
| 3123 | }; | ||
| 3124 | |||
| 3125 | static const vsop_term_t vsop_lat_Jupiter_1[] = | ||
| 3126 | { | ||
| 3127 | { 0.00078203446, 1.52377859742, 529.69096509460 } | ||
| 3128 | }; | ||
| 3129 | |||
| 3130 | static const vsop_series_t vsop_lat_Jupiter[] = | ||
| 3131 | { | ||
| 3132 | { 6, vsop_lat_Jupiter_0 }, | ||
| 3133 | { 1, vsop_lat_Jupiter_1 } | ||
| 3134 | }; | ||
| 3135 | |||
| 3136 | static const vsop_term_t vsop_rad_Jupiter_0[] = | ||
| 3137 | { | ||
| 3138 | { 5.20887429326, 0.00000000000, 0.00000000000 }, | ||
| 3139 | { 0.25209327119, 3.49108639871, 529.69096509460 }, | ||
| 3140 | { 0.00610599976, 3.84115365948, 1059.38193018920 }, | ||
| 3141 | { 0.00282029458, 2.57419881293, 632.78373931320 }, | ||
| 3142 | { 0.00187647346, 2.07590383214, 522.57741809380 }, | ||
| 3143 | { 0.00086792905, 0.71001145545, 419.48464387520 }, | ||
| 3144 | { 0.00072062974, 0.21465724607, 536.80451209540 }, | ||
| 3145 | { 0.00065517248, 5.97995884790, 316.39186965660 }, | ||
| 3146 | { 0.00029134542, 1.67759379655, 103.09277421860 }, | ||
| 3147 | { 0.00030135335, 2.16132003734, 949.17560896980 }, | ||
| 3148 | { 0.00023453271, 3.54023522184, 735.87651353180 }, | ||
| 3149 | { 0.00022283743, 4.19362594399, 1589.07289528380 }, | ||
| 3150 | { 0.00023947298, 0.27458037480, 7.11354700080 }, | ||
| 3151 | { 0.00013032614, 2.96042965363, 1162.47470440780 }, | ||
| 3152 | { 0.00009703360, 1.90669633585, 206.18554843720 }, | ||
| 3153 | { 0.00012749023, 2.71550286592, 1052.26838318840 }, | ||
| 3154 | { 0.00007057931, 2.18184839926, 1265.56747862640 }, | ||
| 3155 | { 0.00006137703, 6.26418240033, 846.08283475120 }, | ||
| 3156 | { 0.00002616976, 2.00994012876, 1581.95934828300 } | ||
| 3157 | }; | ||
| 3158 | |||
| 3159 | static const vsop_term_t vsop_rad_Jupiter_1[] = | ||
| 3160 | { | ||
| 3161 | { 0.01271801520, 2.64937512894, 529.69096509460 }, | ||
| 3162 | { 0.00061661816, 3.00076460387, 1059.38193018920 }, | ||
| 3163 | { 0.00053443713, 3.89717383175, 522.57741809380 }, | ||
| 3164 | { 0.00031185171, 4.88276958012, 536.80451209540 }, | ||
| 3165 | { 0.00041390269, 0.00000000000, 0.00000000000 } | ||
| 3166 | }; | ||
| 3167 | |||
| 3168 | static const vsop_series_t vsop_rad_Jupiter[] = | ||
| 3169 | { | ||
| 3170 | { 19, vsop_rad_Jupiter_0 }, | ||
| 3171 | { 5, vsop_rad_Jupiter_1 } | ||
| 3172 | }; | ||
| 3173 | |||
| 3174 | ; | ||
| 3175 | static const vsop_term_t vsop_lon_Saturn_0[] = | ||
| 3176 | { | ||
| 3177 | { 0.87401354025, 0.00000000000, 0.00000000000 }, | ||
| 3178 | { 0.11107659762, 3.96205090159, 213.29909543800 }, | ||
| 3179 | { 0.01414150957, 4.58581516874, 7.11354700080 }, | ||
| 3180 | { 0.00398379389, 0.52112032699, 206.18554843720 }, | ||
| 3181 | { 0.00350769243, 3.30329907896, 426.59819087600 }, | ||
| 3182 | { 0.00206816305, 0.24658372002, 103.09277421860 }, | ||
| 3183 | { 0.00079271300, 3.84007056878, 220.41264243880 }, | ||
| 3184 | { 0.00023990355, 4.66976924553, 110.20632121940 }, | ||
| 3185 | { 0.00016573588, 0.43719228296, 419.48464387520 }, | ||
| 3186 | { 0.00014906995, 5.76903183869, 316.39186965660 }, | ||
| 3187 | { 0.00015820290, 0.93809155235, 632.78373931320 }, | ||
| 3188 | { 0.00014609559, 1.56518472000, 3.93215326310 }, | ||
| 3189 | { 0.00013160301, 4.44891291899, 14.22709400160 }, | ||
| 3190 | { 0.00015053543, 2.71669915667, 639.89728631400 }, | ||
| 3191 | { 0.00013005299, 5.98119023644, 11.04570026390 }, | ||
| 3192 | { 0.00010725067, 3.12939523827, 202.25339517410 }, | ||
| 3193 | { 0.00005863206, 0.23656938524, 529.69096509460 }, | ||
| 3194 | { 0.00005227757, 4.20783365759, 3.18139373770 }, | ||
| 3195 | { 0.00006126317, 1.76328667907, 277.03499374140 }, | ||
| 3196 | { 0.00005019687, 3.17787728405, 433.71173787680 }, | ||
| 3197 | { 0.00004592550, 0.61977744975, 199.07200143640 }, | ||
| 3198 | { 0.00004005867, 2.24479718502, 63.73589830340 }, | ||
| 3199 | { 0.00002953796, 0.98280366998, 95.97922721780 }, | ||
| 3200 | { 0.00003873670, 3.22283226966, 138.51749687070 }, | ||
| 3201 | { 0.00002461186, 2.03163875071, 735.87651353180 }, | ||
| 3202 | { 0.00003269484, 0.77492638211, 949.17560896980 }, | ||
| 3203 | { 0.00001758145, 3.26580109940, 522.57741809380 }, | ||
| 3204 | { 0.00001640172, 5.50504453050, 846.08283475120 }, | ||
| 3205 | { 0.00001391327, 4.02333150505, 323.50541665740 }, | ||
| 3206 | { 0.00001580648, 4.37265307169, 309.27832265580 }, | ||
| 3207 | { 0.00001123498, 2.83726798446, 415.55249061210 }, | ||
| 3208 | { 0.00001017275, 3.71700135395, 227.52618943960 }, | ||
| 3209 | { 0.00000848642, 3.19150170830, 209.36694217490 } | ||
| 3210 | }; | ||
| 3211 | |||
| 3212 | static const vsop_term_t vsop_lon_Saturn_1[] = | ||
| 3213 | { | ||
| 3214 | { 213.29909521690, 0.00000000000, 0.00000000000 }, | ||
| 3215 | { 0.01297370862, 1.82834923978, 213.29909543800 }, | ||
| 3216 | { 0.00564345393, 2.88499717272, 7.11354700080 }, | ||
| 3217 | { 0.00093734369, 1.06311793502, 426.59819087600 }, | ||
| 3218 | { 0.00107674962, 2.27769131009, 206.18554843720 }, | ||
| 3219 | { 0.00040244455, 2.04108104671, 220.41264243880 }, | ||
| 3220 | { 0.00019941774, 1.27954390470, 103.09277421860 }, | ||
| 3221 | { 0.00010511678, 2.74880342130, 14.22709400160 }, | ||
| 3222 | { 0.00006416106, 0.38238295041, 639.89728631400 }, | ||
| 3223 | { 0.00004848994, 2.43037610229, 419.48464387520 }, | ||
| 3224 | { 0.00004056892, 2.92133209468, 110.20632121940 }, | ||
| 3225 | { 0.00003768635, 3.64965330780, 3.93215326310 } | ||
| 3226 | }; | ||
| 3227 | |||
| 3228 | static const vsop_term_t vsop_lon_Saturn_2[] = | ||
| 3229 | { | ||
| 3230 | { 0.00116441330, 1.17988132879, 7.11354700080 }, | ||
| 3231 | { 0.00091841837, 0.07325195840, 213.29909543800 }, | ||
| 3232 | { 0.00036661728, 0.00000000000, 0.00000000000 }, | ||
| 3233 | { 0.00015274496, 4.06493179167, 206.18554843720 } | ||
| 3234 | }; | ||
| 3235 | |||
| 3236 | static const vsop_series_t vsop_lon_Saturn[] = | ||
| 3237 | { | ||
| 3238 | { 33, vsop_lon_Saturn_0 }, | ||
| 3239 | { 12, vsop_lon_Saturn_1 }, | ||
| 3240 | { 4, vsop_lon_Saturn_2 } | ||
| 3241 | }; | ||
| 3242 | |||
| 3243 | static const vsop_term_t vsop_lat_Saturn_0[] = | ||
| 3244 | { | ||
| 3245 | { 0.04330678039, 3.60284428399, 213.29909543800 }, | ||
| 3246 | { 0.00240348302, 2.85238489373, 426.59819087600 }, | ||
| 3247 | { 0.00084745939, 0.00000000000, 0.00000000000 }, | ||
| 3248 | { 0.00030863357, 3.48441504555, 220.41264243880 }, | ||
| 3249 | { 0.00034116062, 0.57297307557, 206.18554843720 }, | ||
| 3250 | { 0.00014734070, 2.11846596715, 639.89728631400 }, | ||
| 3251 | { 0.00009916667, 5.79003188904, 419.48464387520 }, | ||
| 3252 | { 0.00006993564, 4.73604689720, 7.11354700080 }, | ||
| 3253 | { 0.00004807588, 5.43305312061, 316.39186965660 } | ||
| 3254 | }; | ||
| 3255 | |||
| 3256 | static const vsop_term_t vsop_lat_Saturn_1[] = | ||
| 3257 | { | ||
| 3258 | { 0.00198927992, 4.93901017903, 213.29909543800 }, | ||
| 3259 | { 0.00036947916, 3.14159265359, 0.00000000000 }, | ||
| 3260 | { 0.00017966989, 0.51979431110, 426.59819087600 } | ||
| 3261 | }; | ||
| 3262 | |||
| 3263 | static const vsop_series_t vsop_lat_Saturn[] = | ||
| 3264 | { | ||
| 3265 | { 9, vsop_lat_Saturn_0 }, | ||
| 3266 | { 3, vsop_lat_Saturn_1 } | ||
| 3267 | }; | ||
| 3268 | |||
| 3269 | static const vsop_term_t vsop_rad_Saturn_0[] = | ||
| 3270 | { | ||
| 3271 | { 9.55758135486, 0.00000000000, 0.00000000000 }, | ||
| 3272 | { 0.52921382865, 2.39226219573, 213.29909543800 }, | ||
| 3273 | { 0.01873679867, 5.23549604660, 206.18554843720 }, | ||
| 3274 | { 0.01464663929, 1.64763042902, 426.59819087600 }, | ||
| 3275 | { 0.00821891141, 5.93520042303, 316.39186965660 }, | ||
| 3276 | { 0.00547506923, 5.01532618980, 103.09277421860 }, | ||
| 3277 | { 0.00371684650, 2.27114821115, 220.41264243880 }, | ||
| 3278 | { 0.00361778765, 3.13904301847, 7.11354700080 }, | ||
| 3279 | { 0.00140617506, 5.70406606781, 632.78373931320 }, | ||
| 3280 | { 0.00108974848, 3.29313390175, 110.20632121940 }, | ||
| 3281 | { 0.00069006962, 5.94099540992, 419.48464387520 }, | ||
| 3282 | { 0.00061053367, 0.94037691801, 639.89728631400 }, | ||
| 3283 | { 0.00048913294, 1.55733638681, 202.25339517410 }, | ||
| 3284 | { 0.00034143772, 0.19519102597, 277.03499374140 }, | ||
| 3285 | { 0.00032401773, 5.47084567016, 949.17560896980 }, | ||
| 3286 | { 0.00020936596, 0.46349251129, 735.87651353180 }, | ||
| 3287 | { 0.00009796004, 5.20477537945, 1265.56747862640 }, | ||
| 3288 | { 0.00011993338, 5.98050967385, 846.08283475120 }, | ||
| 3289 | { 0.00020839300, 1.52102476129, 433.71173787680 }, | ||
| 3290 | { 0.00015298404, 3.05943814940, 529.69096509460 }, | ||
| 3291 | { 0.00006465823, 0.17732249942, 1052.26838318840 }, | ||
| 3292 | { 0.00011380257, 1.73105427040, 522.57741809380 }, | ||
| 3293 | { 0.00003419618, 4.94550542171, 1581.95934828300 } | ||
| 3294 | }; | ||
| 3295 | |||
| 3296 | static const vsop_term_t vsop_rad_Saturn_1[] = | ||
| 3297 | { | ||
| 3298 | { 0.06182981340, 0.25843511480, 213.29909543800 }, | ||
| 3299 | { 0.00506577242, 0.71114625261, 206.18554843720 }, | ||
| 3300 | { 0.00341394029, 5.79635741658, 426.59819087600 }, | ||
| 3301 | { 0.00188491195, 0.47215589652, 220.41264243880 }, | ||
| 3302 | { 0.00186261486, 3.14159265359, 0.00000000000 }, | ||
| 3303 | { 0.00143891146, 1.40744822888, 7.11354700080 } | ||
| 3304 | }; | ||
| 3305 | |||
| 3306 | static const vsop_term_t vsop_rad_Saturn_2[] = | ||
| 3307 | { | ||
| 3308 | { 0.00436902572, 4.78671677509, 213.29909543800 } | ||
| 3309 | }; | ||
| 3310 | |||
| 3311 | static const vsop_series_t vsop_rad_Saturn[] = | ||
| 3312 | { | ||
| 3313 | { 23, vsop_rad_Saturn_0 }, | ||
| 3314 | { 6, vsop_rad_Saturn_1 }, | ||
| 3315 | { 1, vsop_rad_Saturn_2 } | ||
| 3316 | }; | ||
| 3317 | |||
| 3318 | ; | ||
| 3319 | static const vsop_term_t vsop_lon_Uranus_0[] = | ||
| 3320 | { | ||
| 3321 | { 5.48129294297, 0.00000000000, 0.00000000000 }, | ||
| 3322 | { 0.09260408234, 0.89106421507, 74.78159856730 }, | ||
| 3323 | { 0.01504247898, 3.62719260920, 1.48447270830 }, | ||
| 3324 | { 0.00365981674, 1.89962179044, 73.29712585900 }, | ||
| 3325 | { 0.00272328168, 3.35823706307, 149.56319713460 }, | ||
| 3326 | { 0.00070328461, 5.39254450063, 63.73589830340 }, | ||
| 3327 | { 0.00068892678, 6.09292483287, 76.26607127560 }, | ||
| 3328 | { 0.00061998615, 2.26952066061, 2.96894541660 }, | ||
| 3329 | { 0.00061950719, 2.85098872691, 11.04570026390 }, | ||
| 3330 | { 0.00026468770, 3.14152083966, 71.81265315070 }, | ||
| 3331 | { 0.00025710476, 6.11379840493, 454.90936652730 }, | ||
| 3332 | { 0.00021078850, 4.36059339067, 148.07872442630 }, | ||
| 3333 | { 0.00017818647, 1.74436930289, 36.64856292950 }, | ||
| 3334 | { 0.00014613507, 4.73732166022, 3.93215326310 }, | ||
| 3335 | { 0.00011162509, 5.82681796350, 224.34479570190 }, | ||
| 3336 | { 0.00010997910, 0.48865004018, 138.51749687070 }, | ||
| 3337 | { 0.00009527478, 2.95516862826, 35.16409022120 }, | ||
| 3338 | { 0.00007545601, 5.23626582400, 109.94568878850 }, | ||
| 3339 | { 0.00004220241, 3.23328220918, 70.84944530420 }, | ||
| 3340 | { 0.00004051900, 2.27755017300, 151.04766984290 }, | ||
| 3341 | { 0.00003354596, 1.06549007380, 4.45341812490 }, | ||
| 3342 | { 0.00002926718, 4.62903718891, 9.56122755560 }, | ||
| 3343 | { 0.00003490340, 5.48306144511, 146.59425171800 }, | ||
| 3344 | { 0.00003144069, 4.75199570434, 77.75054398390 }, | ||
| 3345 | { 0.00002922333, 5.35235361027, 85.82729883120 }, | ||
| 3346 | { 0.00002272788, 4.36600400036, 70.32818044240 }, | ||
| 3347 | { 0.00002051219, 1.51773566586, 0.11187458460 }, | ||
| 3348 | { 0.00002148602, 0.60745949945, 38.13303563780 }, | ||
| 3349 | { 0.00001991643, 4.92437588682, 277.03499374140 }, | ||
| 3350 | { 0.00001376226, 2.04283539351, 65.22037101170 }, | ||
| 3351 | { 0.00001666902, 3.62744066769, 380.12776796000 }, | ||
| 3352 | { 0.00001284107, 3.11347961505, 202.25339517410 }, | ||
| 3353 | { 0.00001150429, 0.93343589092, 3.18139373770 }, | ||
| 3354 | { 0.00001533221, 2.58594681212, 52.69019803950 }, | ||
| 3355 | { 0.00001281604, 0.54271272721, 222.86032299360 }, | ||
| 3356 | { 0.00001372139, 4.19641530878, 111.43016149680 }, | ||
| 3357 | { 0.00001221029, 0.19900650030, 108.46121608020 }, | ||
| 3358 | { 0.00000946181, 1.19253165736, 127.47179660680 }, | ||
| 3359 | { 0.00001150989, 4.17898916639, 33.67961751290 } | ||
| 3360 | }; | ||
| 3361 | |||
| 3362 | static const vsop_term_t vsop_lon_Uranus_1[] = | ||
| 3363 | { | ||
| 3364 | { 74.78159860910, 0.00000000000, 0.00000000000 }, | ||
| 3365 | { 0.00154332863, 5.24158770553, 74.78159856730 }, | ||
| 3366 | { 0.00024456474, 1.71260334156, 1.48447270830 }, | ||
| 3367 | { 0.00009258442, 0.42829732350, 11.04570026390 }, | ||
| 3368 | { 0.00008265977, 1.50218091379, 63.73589830340 }, | ||
| 3369 | { 0.00009150160, 1.41213765216, 149.56319713460 } | ||
| 3370 | }; | ||
| 3371 | |||
| 3372 | static const vsop_series_t vsop_lon_Uranus[] = | ||
| 3373 | { | ||
| 3374 | { 39, vsop_lon_Uranus_0 }, | ||
| 3375 | { 6, vsop_lon_Uranus_1 } | ||
| 3376 | }; | ||
| 3377 | |||
| 3378 | static const vsop_term_t vsop_lat_Uranus_0[] = | ||
| 3379 | { | ||
| 3380 | { 0.01346277648, 2.61877810547, 74.78159856730 }, | ||
| 3381 | { 0.00062341400, 5.08111189648, 149.56319713460 }, | ||
| 3382 | { 0.00061601196, 3.14159265359, 0.00000000000 }, | ||
| 3383 | { 0.00009963722, 1.61603805646, 76.26607127560 }, | ||
| 3384 | { 0.00009926160, 0.57630380333, 73.29712585900 } | ||
| 3385 | }; | ||
| 3386 | |||
| 3387 | static const vsop_term_t vsop_lat_Uranus_1[] = | ||
| 3388 | { | ||
| 3389 | { 0.00034101978, 0.01321929936, 74.78159856730 } | ||
| 3390 | }; | ||
| 3391 | |||
| 3392 | static const vsop_series_t vsop_lat_Uranus[] = | ||
| 3393 | { | ||
| 3394 | { 5, vsop_lat_Uranus_0 }, | ||
| 3395 | { 1, vsop_lat_Uranus_1 } | ||
| 3396 | }; | ||
| 3397 | |||
| 3398 | static const vsop_term_t vsop_rad_Uranus_0[] = | ||
| 3399 | { | ||
| 3400 | { 19.21264847206, 0.00000000000, 0.00000000000 }, | ||
| 3401 | { 0.88784984413, 5.60377527014, 74.78159856730 }, | ||
| 3402 | { 0.03440836062, 0.32836099706, 73.29712585900 }, | ||
| 3403 | { 0.02055653860, 1.78295159330, 149.56319713460 }, | ||
| 3404 | { 0.00649322410, 4.52247285911, 76.26607127560 }, | ||
| 3405 | { 0.00602247865, 3.86003823674, 63.73589830340 }, | ||
| 3406 | { 0.00496404167, 1.40139935333, 454.90936652730 }, | ||
| 3407 | { 0.00338525369, 1.58002770318, 138.51749687070 }, | ||
| 3408 | { 0.00243509114, 1.57086606044, 71.81265315070 }, | ||
| 3409 | { 0.00190522303, 1.99809394714, 1.48447270830 }, | ||
| 3410 | { 0.00161858838, 2.79137786799, 148.07872442630 }, | ||
| 3411 | { 0.00143706183, 1.38368544947, 11.04570026390 }, | ||
| 3412 | { 0.00093192405, 0.17437220467, 36.64856292950 }, | ||
| 3413 | { 0.00071424548, 4.24509236074, 224.34479570190 }, | ||
| 3414 | { 0.00089806014, 3.66105364565, 109.94568878850 }, | ||
| 3415 | { 0.00039009723, 1.66971401684, 70.84944530420 }, | ||
| 3416 | { 0.00046677296, 1.39976401694, 35.16409022120 }, | ||
| 3417 | { 0.00039025624, 3.36234773834, 277.03499374140 }, | ||
| 3418 | { 0.00036755274, 3.88649278513, 146.59425171800 }, | ||
| 3419 | { 0.00030348723, 0.70100838798, 151.04766984290 }, | ||
| 3420 | { 0.00029156413, 3.18056336700, 77.75054398390 }, | ||
| 3421 | { 0.00022637073, 0.72518687029, 529.69096509460 }, | ||
| 3422 | { 0.00011959076, 1.75043392140, 984.60033162190 }, | ||
| 3423 | { 0.00025620756, 5.25656086672, 380.12776796000 } | ||
| 3424 | }; | ||
| 3425 | |||
| 3426 | static const vsop_term_t vsop_rad_Uranus_1[] = | ||
| 3427 | { | ||
| 3428 | { 0.01479896629, 3.67205697578, 74.78159856730 } | ||
| 3429 | }; | ||
| 3430 | |||
| 3431 | static const vsop_series_t vsop_rad_Uranus[] = | ||
| 3432 | { | ||
| 3433 | { 24, vsop_rad_Uranus_0 }, | ||
| 3434 | { 1, vsop_rad_Uranus_1 } | ||
| 3435 | }; | ||
| 3436 | |||
| 3437 | ; | ||
| 3438 | static const vsop_term_t vsop_lon_Neptune_0[] = | ||
| 3439 | { | ||
| 3440 | { 5.31188633046, 0.00000000000, 0.00000000000 }, | ||
| 3441 | { 0.01798475530, 2.90101273890, 38.13303563780 }, | ||
| 3442 | { 0.01019727652, 0.48580922867, 1.48447270830 }, | ||
| 3443 | { 0.00124531845, 4.83008090676, 36.64856292950 }, | ||
| 3444 | { 0.00042064466, 5.41054993053, 2.96894541660 }, | ||
| 3445 | { 0.00037714584, 6.09221808686, 35.16409022120 }, | ||
| 3446 | { 0.00033784738, 1.24488874087, 76.26607127560 }, | ||
| 3447 | { 0.00016482741, 0.00007727998, 491.55792945680 }, | ||
| 3448 | { 0.00009198584, 4.93747051954, 39.61750834610 }, | ||
| 3449 | { 0.00008994250, 0.27462171806, 175.16605980020 } | ||
| 3450 | }; | ||
| 3451 | |||
| 3452 | static const vsop_term_t vsop_lon_Neptune_1[] = | ||
| 3453 | { | ||
| 3454 | { 38.13303563957, 0.00000000000, 0.00000000000 }, | ||
| 3455 | { 0.00016604172, 4.86323329249, 1.48447270830 }, | ||
| 3456 | { 0.00015744045, 2.27887427527, 38.13303563780 } | ||
| 3457 | }; | ||
| 3458 | |||
| 3459 | static const vsop_series_t vsop_lon_Neptune[] = | ||
| 3460 | { | ||
| 3461 | { 10, vsop_lon_Neptune_0 }, | ||
| 3462 | { 3, vsop_lon_Neptune_1 } | ||
| 3463 | }; | ||
| 3464 | |||
| 3465 | static const vsop_term_t vsop_lat_Neptune_0[] = | ||
| 3466 | { | ||
| 3467 | { 0.03088622933, 1.44104372644, 38.13303563780 }, | ||
| 3468 | { 0.00027780087, 5.91271884599, 76.26607127560 }, | ||
| 3469 | { 0.00027623609, 0.00000000000, 0.00000000000 }, | ||
| 3470 | { 0.00015355489, 2.52123799551, 36.64856292950 }, | ||
| 3471 | { 0.00015448133, 3.50877079215, 39.61750834610 } | ||
| 3472 | }; | ||
| 3473 | |||
| 3474 | static const vsop_series_t vsop_lat_Neptune[] = | ||
| 3475 | { | ||
| 3476 | { 5, vsop_lat_Neptune_0 } | ||
| 3477 | }; | ||
| 3478 | |||
| 3479 | static const vsop_term_t vsop_rad_Neptune_0[] = | ||
| 3480 | { | ||
| 3481 | { 30.07013205828, 0.00000000000, 0.00000000000 }, | ||
| 3482 | { 0.27062259632, 1.32999459377, 38.13303563780 }, | ||
| 3483 | { 0.01691764014, 3.25186135653, 36.64856292950 }, | ||
| 3484 | { 0.00807830553, 5.18592878704, 1.48447270830 }, | ||
| 3485 | { 0.00537760510, 4.52113935896, 35.16409022120 }, | ||
| 3486 | { 0.00495725141, 1.57105641650, 491.55792945680 }, | ||
| 3487 | { 0.00274571975, 1.84552258866, 175.16605980020 }, | ||
| 3488 | { 0.00012012320, 1.92059384991, 1021.24889455140 }, | ||
| 3489 | { 0.00121801746, 5.79754470298, 76.26607127560 }, | ||
| 3490 | { 0.00100896068, 0.37702724930, 73.29712585900 }, | ||
| 3491 | { 0.00135134092, 3.37220609835, 39.61750834610 }, | ||
| 3492 | { 0.00007571796, 1.07149207335, 388.46515523820 } | ||
| 3493 | }; | ||
| 3494 | |||
| 3495 | static const vsop_series_t vsop_rad_Neptune[] = | ||
| 3496 | { | ||
| 3497 | { 12, vsop_rad_Neptune_0 } | ||
| 3498 | }; | ||
| 3499 | |||
| 3500 | ; | ||
| 3501 | |||
| 3502 | /** @cond DOXYGEN_SKIP */ | ||
| 3503 | #define VSOPFORMULA(x) { ASTRO_ARRAYSIZE(x), x } | ||
| 3504 | /** @endcond */ | ||
| 3505 | |||
| 3506 | static const vsop_model_t vsop[] = | ||
| 3507 | { | ||
| 3508 | { { VSOPFORMULA(vsop_lon_Mercury), VSOPFORMULA(vsop_lat_Mercury), VSOPFORMULA(vsop_rad_Mercury) } }, | ||
| 3509 | { { VSOPFORMULA(vsop_lon_Venus), VSOPFORMULA(vsop_lat_Venus), VSOPFORMULA(vsop_rad_Venus) } }, | ||
| 3510 | { { VSOPFORMULA(vsop_lon_Earth), VSOPFORMULA(vsop_lat_Earth), VSOPFORMULA(vsop_rad_Earth) } }, | ||
| 3511 | { { VSOPFORMULA(vsop_lon_Mars), VSOPFORMULA(vsop_lat_Mars), VSOPFORMULA(vsop_rad_Mars) } }, | ||
| 3512 | { { VSOPFORMULA(vsop_lon_Jupiter), VSOPFORMULA(vsop_lat_Jupiter), VSOPFORMULA(vsop_rad_Jupiter) } }, | ||
| 3513 | { { VSOPFORMULA(vsop_lon_Saturn), VSOPFORMULA(vsop_lat_Saturn), VSOPFORMULA(vsop_rad_Saturn) } }, | ||
| 3514 | { { VSOPFORMULA(vsop_lon_Uranus), VSOPFORMULA(vsop_lat_Uranus), VSOPFORMULA(vsop_rad_Uranus) } }, | ||
| 3515 | { { VSOPFORMULA(vsop_lon_Neptune), VSOPFORMULA(vsop_lat_Neptune), VSOPFORMULA(vsop_rad_Neptune) } } | ||
| 3516 | }; | ||
| 3517 | |||
| 3518 | /** @cond DOXYGEN_SKIP */ | ||
| 3519 | #define CalcEarth(time) CalcVsop(&vsop[BODY_EARTH], (time)) | ||
| 3520 | #define LON_INDEX 0 | ||
| 3521 | #define LAT_INDEX 1 | ||
| 3522 | #define RAD_INDEX 2 | ||
| 3523 | /** @endcond */ | ||
| 3524 | |||
| 3525 | static void VsopCoords(const vsop_model_t *model, double t, double sphere[3]) | ||
| 3526 | { | ||
| 3527 | int k, s, i; | ||
| 3528 | double incr; | ||
| 3529 | |||
| 3530 | for (k=0; k < 3; ++k) | ||
| 3531 | { | ||
| 3532 | double tpower = 1.0; | ||
| 3533 | const vsop_formula_t *formula = &model->formula[k]; | ||
| 3534 | sphere[k] = 0.0; | ||
| 3535 | for (s=0; s < formula->nseries; ++s) | ||
| 3536 | { | ||
| 3537 | double sum = 0.0; | ||
| 3538 | const vsop_series_t *series = &formula->series[s]; | ||
| 3539 | for (i=0; i < series->nterms; ++i) | ||
| 3540 | { | ||
| 3541 | const vsop_term_t *term = &series->term[i]; | ||
| 3542 | sum += term->amplitude * cos(term->phase + (t * term->frequency)); | ||
| 3543 | } | ||
| 3544 | incr = tpower * sum; | ||
| 3545 | if (k == LON_INDEX) | ||
| 3546 | incr = fmod(incr, PI2); /* improve precision for longitudes, which can be hundreds of radians */ | ||
| 3547 | sphere[k] += incr; | ||
| 3548 | tpower *= t; | ||
| 3549 | } | ||
| 3550 | } | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | |||
| 3554 | static terse_vector_t VsopRotate(const double ecl[3]) | ||
| 3555 | { | ||
| 3556 | terse_vector_t equ; | ||
| 3557 | |||
| 3558 | /* | ||
| 3559 | X +1.000000000000 +0.000000440360 -0.000000190919 X | ||
| 3560 | Y = -0.000000479966 +0.917482137087 -0.397776982902 Y | ||
| 3561 | Z FK5 0.000000000000 +0.397776982902 +0.917482137087 Z VSOP87A | ||
| 3562 | */ | ||
| 3563 | |||
| 3564 | equ.x = ecl[0] + 0.000000440360*ecl[1] - 0.000000190919*ecl[2]; | ||
| 3565 | equ.y = -0.000000479966*ecl[0] + 0.917482137087*ecl[1] - 0.397776982902*ecl[2]; | ||
| 3566 | equ.z = 0.397776982902*ecl[1] + 0.917482137087*ecl[2]; | ||
| 3567 | |||
| 3568 | return equ; | ||
| 3569 | } | ||
| 3570 | |||
| 3571 | |||
| 3572 | static void VsopSphereToRect(double lon, double lat, double radius, double pos[3]) | ||
| 3573 | { | ||
| 3574 | double r_coslat = radius * cos(lat); | ||
| 3575 | double coslon = cos(lon); | ||
| 3576 | double sinlon = sin(lon); | ||
| 3577 | pos[0] = r_coslat * coslon; | ||
| 3578 | pos[1] = r_coslat * sinlon; | ||
| 3579 | pos[2] = radius * sin(lat); | ||
| 3580 | } | ||
| 3581 | |||
| 3582 | static const double DAYS_PER_MILLENNIUM = 365250.0; | ||
| 3583 | |||
| 3584 | |||
| 3585 | static astro_vector_t CalcVsop(const vsop_model_t *model, astro_time_t time) | ||
| 3586 | { | ||
| 3587 | double t = time.tt / DAYS_PER_MILLENNIUM; | ||
| 3588 | double sphere[3]; /* lon, lat, rad */ | ||
| 3589 | double eclip[3]; | ||
| 3590 | astro_vector_t vector; | ||
| 3591 | terse_vector_t pos; | ||
| 3592 | |||
| 3593 | /* Calculate the VSOP "B" trigonometric series to obtain ecliptic spherical coordinates. */ | ||
| 3594 | VsopCoords(model, t, sphere); | ||
| 3595 | |||
| 3596 | /* Convert ecliptic spherical coordinates to ecliptic Cartesian coordinates. */ | ||
| 3597 | VsopSphereToRect(sphere[LON_INDEX], sphere[LAT_INDEX], sphere[RAD_INDEX], eclip); | ||
| 3598 | |||
| 3599 | /* Convert ecliptic Cartesian coordinates to equatorial Cartesian coordinates. */ | ||
| 3600 | pos = VsopRotate(eclip); | ||
| 3601 | |||
| 3602 | /* Package the position as astro_vector_t. */ | ||
| 3603 | vector.status = ASTRO_SUCCESS; | ||
| 3604 | vector.t = time; | ||
| 3605 | vector.x = pos.x; | ||
| 3606 | vector.y = pos.y; | ||
| 3607 | vector.z = pos.z; | ||
| 3608 | |||
| 3609 | return vector; | ||
| 3610 | } | ||
| 3611 | |||
| 3612 | |||
| 3613 | static void VsopDeriv(const vsop_model_t *model, double t, double deriv[3]) | ||
| 3614 | { | ||
| 3615 | int k, s, i; | ||
| 3616 | |||
| 3617 | for (k=0; k < 3; ++k) | ||
| 3618 | { | ||
| 3619 | double tpower = 1.0; /* t^s */ | ||
| 3620 | double dpower = 0.0; /* t^(s-1) */ | ||
| 3621 | const vsop_formula_t *formula = &model->formula[k]; | ||
| 3622 | deriv[k] = 0.0; | ||
| 3623 | for (s=0; s < formula->nseries; ++s) | ||
| 3624 | { | ||
| 3625 | double sin_sum = 0.0; | ||
| 3626 | double cos_sum = 0.0; | ||
| 3627 | const vsop_series_t *series = &formula->series[s]; | ||
| 3628 | for (i=0; i < series->nterms; ++i) | ||
| 3629 | { | ||
| 3630 | const vsop_term_t *term = &series->term[i]; | ||
| 3631 | double angle = term->phase + (t * term->frequency); | ||
| 3632 | sin_sum += term->amplitude * term->frequency * sin(angle); | ||
| 3633 | if (s > 0) | ||
| 3634 | cos_sum += term->amplitude * cos(angle); | ||
| 3635 | } | ||
| 3636 | deriv[k] += (s * dpower * cos_sum) - (tpower * sin_sum); | ||
| 3637 | dpower = tpower; | ||
| 3638 | tpower *= t; | ||
| 3639 | } | ||
| 3640 | } | ||
| 3641 | } | ||
| 3642 | |||
| 3643 | |||
| 3644 | static body_state_t CalcVsopPosVel(const vsop_model_t *model, double tt) | ||
| 3645 | { | ||
| 3646 | body_state_t state; | ||
| 3647 | double t = tt / DAYS_PER_MILLENNIUM; | ||
| 3648 | double sphere[3]; /* lon, lat, r */ | ||
| 3649 | double deriv[3]; /* d(lon)/dt, d(lat)/dt, dr/dt */ | ||
| 3650 | double eclip[3]; | ||
| 3651 | double dr_dt, dlat_dt, dlon_dt; | ||
| 3652 | double r, coslat, coslon, sinlat, sinlon; | ||
| 3653 | |||
| 3654 | state.tt = tt; | ||
| 3655 | VsopCoords(model, t, sphere); | ||
| 3656 | VsopSphereToRect(sphere[LON_INDEX], sphere[LAT_INDEX], sphere[RAD_INDEX], eclip); | ||
| 3657 | state.r = VsopRotate(eclip); | ||
| 3658 | |||
| 3659 | VsopDeriv(model, t, deriv); | ||
| 3660 | |||
| 3661 | /* Use spherical coords and spherical derivatives to calculate */ | ||
| 3662 | /* the velocity vector in rectangular coordinates. */ | ||
| 3663 | |||
| 3664 | /* Calculate mnemonic variables to help keep the math straight. */ | ||
| 3665 | coslon = cos(sphere[LON_INDEX]); | ||
| 3666 | sinlon = sin(sphere[LON_INDEX]); | ||
| 3667 | coslat = cos(sphere[LAT_INDEX]); | ||
| 3668 | sinlat = sin(sphere[LAT_INDEX]); | ||
| 3669 | r = sphere[RAD_INDEX]; | ||
| 3670 | dlon_dt = deriv[LON_INDEX]; | ||
| 3671 | dlat_dt = deriv[LAT_INDEX]; | ||
| 3672 | dr_dt = deriv[RAD_INDEX]; | ||
| 3673 | |||
| 3674 | /* vx = dx/dt */ | ||
| 3675 | eclip[0] = (dr_dt * coslat * coslon) - (r * sinlat * coslon * dlat_dt) - (r * coslat * sinlon * dlon_dt); | ||
| 3676 | |||
| 3677 | /* vy = dy/dt */ | ||
| 3678 | eclip[1] = (dr_dt * coslat * sinlon) - (r * sinlat * sinlon * dlat_dt) + (r * coslat * coslon * dlon_dt); | ||
| 3679 | |||
| 3680 | /* vz = dz/dt */ | ||
| 3681 | eclip[2] = (dr_dt * sinlat) + (r * coslat * dlat_dt); | ||
| 3682 | |||
| 3683 | /* Rotate the velocity vector from ecliptic to equatorial coordinates. */ | ||
| 3684 | state.v = VsopRotate(eclip); | ||
| 3685 | |||
| 3686 | /* Convert speed units from [AU/millennium] to [AU/day]. */ | ||
| 3687 | VecScale(&state.v, 1 / DAYS_PER_MILLENNIUM); | ||
| 3688 | |||
| 3689 | return state; | ||
| 3690 | } | ||
| 3691 | |||
| 3692 | |||
| 3693 | static double VsopHelioDistance(const vsop_model_t *model, astro_time_t time) | ||
| 3694 | { | ||
| 3695 | int s, i; | ||
| 3696 | double t = time.tt / DAYS_PER_MILLENNIUM; | ||
| 3697 | double distance = 0.0; | ||
| 3698 | double tpower = 1.0; | ||
| 3699 | const vsop_formula_t *formula = &model->formula[2]; /* [2] is the distance part of the formula */ | ||
| 3700 | |||
| 3701 | /* | ||
| 3702 | The caller only wants to know the distance between the planet and the Sun. | ||
| 3703 | So we only need to calculate the radial component of the spherical coordinates. | ||
| 3704 | */ | ||
| 3705 | |||
| 3706 | for (s=0; s < formula->nseries; ++s) | ||
| 3707 | { | ||
| 3708 | double sum = 0.0; | ||
| 3709 | const vsop_series_t *series = &formula->series[s]; | ||
| 3710 | for (i=0; i < series->nterms; ++i) | ||
| 3711 | { | ||
| 3712 | const vsop_term_t *term = &series->term[i]; | ||
| 3713 | sum += term->amplitude * cos(term->phase + (t * term->frequency)); | ||
| 3714 | } | ||
| 3715 | distance += tpower * sum; | ||
| 3716 | tpower *= t; | ||
| 3717 | } | ||
| 3718 | |||
| 3719 | return distance; | ||
| 3720 | } | ||
| 3721 | |||
| 3722 | |||
| 3723 | static void AdjustBarycenter(astro_vector_t *ssb, astro_time_t time, astro_body_t body, double planet_gm) | ||
| 3724 | { | ||
| 3725 | astro_vector_t planet; | ||
| 3726 | double shift; | ||
| 3727 | |||
| 3728 | shift = planet_gm / (planet_gm + SUN_GM); | ||
| 3729 | planet = CalcVsop(&vsop[body], time); | ||
| 3730 | ssb->x += shift * planet.x; | ||
| 3731 | ssb->y += shift * planet.y; | ||
| 3732 | ssb->z += shift * planet.z; | ||
| 3733 | } | ||
| 3734 | |||
| 3735 | |||
| 3736 | static astro_vector_t CalcSolarSystemBarycenter(astro_time_t time) | ||
| 3737 | { | ||
| 3738 | astro_vector_t ssb; | ||
| 3739 | |||
| 3740 | ssb.status = ASTRO_SUCCESS; | ||
| 3741 | ssb.t = time; | ||
| 3742 | ssb.x = ssb.y = ssb.z = 0.0; | ||
| 3743 | |||
| 3744 | AdjustBarycenter(&ssb, time, BODY_JUPITER, JUPITER_GM); | ||
| 3745 | AdjustBarycenter(&ssb, time, BODY_SATURN, SATURN_GM); | ||
| 3746 | AdjustBarycenter(&ssb, time, BODY_URANUS, URANUS_GM); | ||
| 3747 | AdjustBarycenter(&ssb, time, BODY_NEPTUNE, NEPTUNE_GM); | ||
| 3748 | |||
| 3749 | return ssb; | ||
| 3750 | } | ||
| 3751 | |||
| 3752 | /*------------------ begin general gravity simulator ------------------*/ | ||
| 3753 | |||
| 3754 | static terse_vector_t UpdatePosition(double dt, terse_vector_t r, terse_vector_t v, terse_vector_t a) | ||
| 3755 | { | ||
| 3756 | r.x += (v.x + a.x*dt/2) * dt; | ||
| 3757 | r.y += (v.y + a.y*dt/2) * dt; | ||
| 3758 | r.z += (v.z + a.z*dt/2) * dt; | ||
| 3759 | return r; | ||
| 3760 | } | ||
| 3761 | |||
| 3762 | |||
| 3763 | static terse_vector_t UpdateVelocity(double dt, terse_vector_t v, terse_vector_t a) | ||
| 3764 | { | ||
| 3765 | v.x += dt * a.x; | ||
| 3766 | v.y += dt * a.y; | ||
| 3767 | v.z += dt * a.z; | ||
| 3768 | return v; | ||
| 3769 | } | ||
| 3770 | |||
| 3771 | |||
| 3772 | static body_state_t AdjustBarycenterPosVel(body_state_t *ssb, double tt, astro_body_t body, double planet_gm) | ||
| 3773 | { | ||
| 3774 | body_state_t planet; | ||
| 3775 | double shift; | ||
| 3776 | |||
| 3777 | /* | ||
| 3778 | This function does 2 important things: | ||
| 3779 | 1. Adjusts 'ssb' by the effect of one major body on the Solar System Barycenter. | ||
| 3780 | 2, Returns the heliocentric position of that major body. | ||
| 3781 | */ | ||
| 3782 | |||
| 3783 | shift = planet_gm / (planet_gm + SUN_GM); | ||
| 3784 | planet = CalcVsopPosVel(&vsop[body], tt); | ||
| 3785 | VecIncr(&ssb->r, VecMul(shift, planet.r)); | ||
| 3786 | VecIncr(&ssb->v, VecMul(shift, planet.v)); | ||
| 3787 | |||
| 3788 | return planet; | ||
| 3789 | } | ||
| 3790 | |||
| 3791 | |||
| 3792 | static void MajorBodyBary(major_bodies_t *bary, double tt) | ||
| 3793 | { | ||
| 3794 | /* Initialize the Sun's position as a zero vector, then adjust it from pulls from the planets. */ | ||
| 3795 | bary->Sun.tt = tt; | ||
| 3796 | bary->Sun.r = VecZero; | ||
| 3797 | bary->Sun.v = VecZero; | ||
| 3798 | |||
| 3799 | /* Calculate heliocentric planet positions and SSB. */ | ||
| 3800 | bary->Jupiter = AdjustBarycenterPosVel(&bary->Sun, tt, BODY_JUPITER, JUPITER_GM); | ||
| 3801 | bary->Saturn = AdjustBarycenterPosVel(&bary->Sun, tt, BODY_SATURN, SATURN_GM); | ||
| 3802 | bary->Uranus = AdjustBarycenterPosVel(&bary->Sun, tt, BODY_URANUS, URANUS_GM); | ||
| 3803 | bary->Neptune = AdjustBarycenterPosVel(&bary->Sun, tt, BODY_NEPTUNE, NEPTUNE_GM); | ||
| 3804 | |||
| 3805 | /* Convert planet [pos, vel] from heliocentric to barycentric. */ | ||
| 3806 | VecDecr(&bary->Jupiter.r, bary->Sun.r); VecDecr(&bary->Jupiter.v, bary->Sun.v); | ||
| 3807 | VecDecr(&bary->Saturn.r, bary->Sun.r); VecDecr(&bary->Saturn.v, bary->Sun.v); | ||
| 3808 | VecDecr(&bary->Uranus.r, bary->Sun.r); VecDecr(&bary->Uranus.v, bary->Sun.v); | ||
| 3809 | VecDecr(&bary->Neptune.r, bary->Sun.r); VecDecr(&bary->Neptune.v, bary->Sun.v); | ||
| 3810 | |||
| 3811 | /* Convert heliocentric SSB to barycentric Sun. */ | ||
| 3812 | VecScale(&bary->Sun.r, -1.0); | ||
| 3813 | VecScale(&bary->Sun.v, -1.0); | ||
| 3814 | } | ||
| 3815 | |||
| 3816 | |||
| 3817 | static void AddAcceleration(terse_vector_t *acc, terse_vector_t small_pos, double gm, terse_vector_t major_pos) | ||
| 3818 | { | ||
| 3819 | double dx, dy, dz, r2, pull; | ||
| 3820 | |||
| 3821 | dx = major_pos.x - small_pos.x; | ||
| 3822 | dy = major_pos.y - small_pos.y; | ||
| 3823 | dz = major_pos.z - small_pos.z; | ||
| 3824 | |||
| 3825 | r2 = dx*dx + dy*dy + dz*dz; | ||
| 3826 | pull = gm / (r2 * sqrt(r2)); | ||
| 3827 | |||
| 3828 | acc->x += dx * pull; | ||
| 3829 | acc->y += dy * pull; | ||
| 3830 | acc->z += dz * pull; | ||
| 3831 | } | ||
| 3832 | |||
| 3833 | |||
| 3834 | static terse_vector_t SmallBodyAcceleration(terse_vector_t small_pos, const major_bodies_t *bary) | ||
| 3835 | { | ||
| 3836 | terse_vector_t acc = VecZero; | ||
| 3837 | |||
| 3838 | /* Use barycentric coordinates of the Sun and major planets to calculate gravitational accelerations. */ | ||
| 3839 | AddAcceleration(&acc, small_pos, SUN_GM, bary->Sun.r); | ||
| 3840 | AddAcceleration(&acc, small_pos, JUPITER_GM, bary->Jupiter.r); | ||
| 3841 | AddAcceleration(&acc, small_pos, SATURN_GM, bary->Saturn.r); | ||
| 3842 | AddAcceleration(&acc, small_pos, URANUS_GM, bary->Uranus.r); | ||
| 3843 | AddAcceleration(&acc, small_pos, NEPTUNE_GM, bary->Neptune.r); | ||
| 3844 | |||
| 3845 | return acc; | ||
| 3846 | } | ||
| 3847 | |||
| 3848 | |||
| 3849 | body_grav_calc_t GravSim( /* out: [pos, vel, acc] of the simulated body at time tt2 */ | ||
| 3850 | major_bodies_t *bary2, /* temp: work area for major body barycentric state */ | ||
| 3851 | double tt2, /* in: a target time to be calculated (either before or after tt1) */ | ||
| 3852 | const body_grav_calc_t *calc1) /* in: [pos, vel, acc] of the simulated body at time tt1 */ | ||
| 3853 | { | ||
| 3854 | body_grav_calc_t calc2; | ||
| 3855 | terse_vector_t approx_pos; | ||
| 3856 | terse_vector_t acc; | ||
| 3857 | const double dt = tt2 - calc1->tt; | ||
| 3858 | |||
| 3859 | /* Calculate where the major bodies (Sun, Jupiter...Neptune) will be at the next time step. */ | ||
| 3860 | MajorBodyBary(bary2, tt2); | ||
| 3861 | |||
| 3862 | /* Estimate position of small body as if current acceleration applies across the whole time interval. */ | ||
| 3863 | /* approx_pos = pos1 + vel1*dt + (1/2)acc*dt^2 */ | ||
| 3864 | approx_pos = UpdatePosition(dt, calc1->r, calc1->v, calc1->a); | ||
| 3865 | |||
| 3866 | /* Calculate acceleration experienced by small body at approximate next location. */ | ||
| 3867 | acc = SmallBodyAcceleration(approx_pos, bary2); | ||
| 3868 | |||
| 3869 | /* Calculate the average acceleration of the endpoints. */ | ||
| 3870 | /* This becomes our estimate of the mean effective acceleration over the whole interval. */ | ||
| 3871 | acc = VecMean(acc, calc1->a); | ||
| 3872 | |||
| 3873 | /* Refine the estimates of [pos, vel, acc] at tt2 using the mean acceleration. */ | ||
| 3874 | calc2.r = UpdatePosition(dt, calc1->r, calc1->v, acc); | ||
| 3875 | calc2.v = UpdateVelocity(dt, calc1->v, acc); | ||
| 3876 | calc2.a = SmallBodyAcceleration(calc2.r, bary2); | ||
| 3877 | calc2.tt = tt2; | ||
| 3878 | return calc2; | ||
| 3879 | } | ||
| 3880 | |||
| 3881 | |||
| 3882 | static body_grav_calc_t GravFromState(major_bodies_t *bary, const body_state_t *state) | ||
| 3883 | { | ||
| 3884 | body_grav_calc_t calc; | ||
| 3885 | |||
| 3886 | MajorBodyBary(bary, state->tt); | ||
| 3887 | |||
| 3888 | calc.tt = state->tt; | ||
| 3889 | calc.r = VecAdd(state->r, bary->Sun.r); /* convert heliocentric to barycentric */ | ||
| 3890 | calc.v = VecAdd(state->v, bary->Sun.v); /* convert heliocentric to barycentric */ | ||
| 3891 | calc.a = SmallBodyAcceleration(calc.r, bary); | ||
| 3892 | |||
| 3893 | return calc; | ||
| 3894 | } | ||
| 3895 | |||
| 3896 | |||
| 3897 | static void CalcSolarSystem(astro_grav_sim_t *sim) | ||
| 3898 | { | ||
| 3899 | int body; | ||
| 3900 | double tt = sim->curr->time.tt; | ||
| 3901 | body_state_t *grav = sim->curr->gravitators; | ||
| 3902 | body_state_t *sun = &grav[BODY_SUN]; | ||
| 3903 | |||
| 3904 | /* Initialize the Sun's position/velocity as zero vectors, then adjust from pulls from the planets. */ | ||
| 3905 | sun->tt = tt; | ||
| 3906 | sun->r = VecZero; | ||
| 3907 | sun->v = VecZero; | ||
| 3908 | |||
| 3909 | /* Calculate the position of each planet, and adjust the SSB position accordingly. */ | ||
| 3910 | grav[BODY_MERCURY] = AdjustBarycenterPosVel(sun, tt, BODY_MERCURY, MERCURY_GM); | ||
| 3911 | grav[BODY_VENUS ] = AdjustBarycenterPosVel(sun, tt, BODY_VENUS, VENUS_GM); | ||
| 3912 | grav[BODY_EARTH ] = AdjustBarycenterPosVel(sun, tt, BODY_EARTH, EARTH_GM + MOON_GM); | ||
| 3913 | grav[BODY_MARS ] = AdjustBarycenterPosVel(sun, tt, BODY_MARS, MARS_GM); | ||
| 3914 | grav[BODY_JUPITER] = AdjustBarycenterPosVel(sun, tt, BODY_JUPITER, JUPITER_GM); | ||
| 3915 | grav[BODY_SATURN ] = AdjustBarycenterPosVel(sun, tt, BODY_SATURN, SATURN_GM); | ||
| 3916 | grav[BODY_URANUS ] = AdjustBarycenterPosVel(sun, tt, BODY_URANUS, URANUS_GM); | ||
| 3917 | grav[BODY_NEPTUNE] = AdjustBarycenterPosVel(sun, tt, BODY_NEPTUNE, NEPTUNE_GM); | ||
| 3918 | |||
| 3919 | /* Convert planet state vectors from heliocentric to barycentric. */ | ||
| 3920 | for (body = BODY_MERCURY; body <= BODY_NEPTUNE; ++body) | ||
| 3921 | { | ||
| 3922 | VecDecr(&grav[body].r, sun->r); | ||
| 3923 | VecDecr(&grav[body].v, sun->v); | ||
| 3924 | } | ||
| 3925 | |||
| 3926 | /* Convert heliocentric SSB to barycentric Sun. */ | ||
| 3927 | VecScale(&sun->r, -1.0); | ||
| 3928 | VecScale(&sun->v, -1.0); | ||
| 3929 | } | ||
| 3930 | |||
| 3931 | |||
| 3932 | static void CalcBodyAccelerations(astro_grav_sim_t *sim) | ||
| 3933 | { | ||
| 3934 | int i; | ||
| 3935 | const body_state_t *grav = sim->curr->gravitators; | ||
| 3936 | |||
| 3937 | /* Calculate the gravitational acceleration experienced by the simulated bodies. */ | ||
| 3938 | for (i = 0; i < sim->numBodies; ++i) | ||
| 3939 | { | ||
| 3940 | body_grav_calc_t *calc = &sim->curr->bodies[i]; | ||
| 3941 | |||
| 3942 | calc->a = VecZero; | ||
| 3943 | |||
| 3944 | AddAcceleration(&calc->a, calc->r, SUN_GM, grav[BODY_SUN ].r); | ||
| 3945 | AddAcceleration(&calc->a, calc->r, MERCURY_GM, grav[BODY_MERCURY].r); | ||
| 3946 | AddAcceleration(&calc->a, calc->r, VENUS_GM, grav[BODY_VENUS ].r); | ||
| 3947 | AddAcceleration(&calc->a, calc->r, EARTH_GM + MOON_GM, grav[BODY_EARTH ].r); | ||
| 3948 | AddAcceleration(&calc->a, calc->r, MARS_GM, grav[BODY_MARS ].r); | ||
| 3949 | AddAcceleration(&calc->a, calc->r, JUPITER_GM, grav[BODY_JUPITER].r); | ||
| 3950 | AddAcceleration(&calc->a, calc->r, SATURN_GM, grav[BODY_SATURN ].r); | ||
| 3951 | AddAcceleration(&calc->a, calc->r, URANUS_GM, grav[BODY_URANUS ].r); | ||
| 3952 | AddAcceleration(&calc->a, calc->r, NEPTUNE_GM, grav[BODY_NEPTUNE].r); | ||
| 3953 | } | ||
| 3954 | } | ||
| 3955 | |||
| 3956 | |||
| 3957 | static body_state_t *GravSimBodyStatePtr(astro_grav_sim_t *sim, astro_body_t body) | ||
| 3958 | { | ||
| 3959 | /* | ||
| 3960 | Return a pointer to the place where we cache the barycentric | ||
| 3961 | state of the given body, or NULL if this body is not one | ||
| 3962 | that we use for calculating gravitational interactions. | ||
| 3963 | */ | ||
| 3964 | |||
| 3965 | if ((body == BODY_SUN) || (body >= BODY_MERCURY && body <= BODY_NEPTUNE)) | ||
| 3966 | return &sim->curr->gravitators[body]; | ||
| 3967 | |||
| 3968 | return NULL; | ||
| 3969 | } | ||
| 3970 | |||
| 3971 | |||
| 3972 | static astro_state_vector_t GravSimOriginState(astro_grav_sim_t *sim) | ||
| 3973 | { | ||
| 3974 | body_state_t *optr; | ||
| 3975 | astro_time_t time = sim->curr->time; | ||
| 3976 | |||
| 3977 | if (sim->originBody == BODY_SSB) | ||
| 3978 | { | ||
| 3979 | /* The barycentric state of the SSB is zero, by definition. */ | ||
| 3980 | astro_state_vector_t state; | ||
| 3981 | state.status = ASTRO_SUCCESS; | ||
| 3982 | state.t = time; | ||
| 3983 | state.x = 0.0; | ||
| 3984 | state.y = 0.0; | ||
| 3985 | state.z = 0.0; | ||
| 3986 | state.vx = 0.0; | ||
| 3987 | state.vy = 0.0; | ||
| 3988 | state.vz = 0.0; | ||
| 3989 | return state; | ||
| 3990 | } | ||
| 3991 | |||
| 3992 | optr = GravSimBodyStatePtr(sim, sim->originBody); | ||
| 3993 | if (optr != NULL) | ||
| 3994 | return ExportState(*optr, time); | ||
| 3995 | |||
| 3996 | /* We only support the VSOP bodies, for efficiency. */ | ||
| 3997 | return StateVecError(ASTRO_INVALID_BODY, time); | ||
| 3998 | } | ||
| 3999 | |||
| 4000 | |||
| 4001 | static void GravSimDuplicate(astro_grav_sim_t *sim) | ||
| 4002 | { | ||
| 4003 | /* Copy the current state into the previous state, so that both become the same moment in time. */ | ||
| 4004 | sim->prev->time = sim->curr->time; | ||
| 4005 | memcpy(sim->prev->gravitators, sim->curr->gravitators, sizeof(sim->prev->gravitators)); | ||
| 4006 | memcpy(sim->prev->bodies, sim->curr->bodies, ((size_t)sim->numBodies) * sizeof(sim->prev->bodies[0])); | ||
| 4007 | } | ||
| 4008 | |||
| 4009 | |||
| 4010 | /** | ||
| 4011 | * @brief Allocate and initialize a gravity step simulator. | ||
| 4012 | * | ||
| 4013 | * Prepares to simulate a series of incremental time steps, | ||
| 4014 | * simulating the movement of zero or more small bodies through the Solar System | ||
| 4015 | * acting under gravitational attraction from the Sun and planets. | ||
| 4016 | * | ||
| 4017 | * After calling this function, you can call #Astronomy_GravSimUpdate | ||
| 4018 | * as many times as desired to advance the simulation by small time steps. | ||
| 4019 | * | ||
| 4020 | * If this function succeeds (returns `ASTRO_SUCCESS`), `sim` | ||
| 4021 | * will be set to a dynamically allocated object. The caller is | ||
| 4022 | * then responsible for eventually calling #Astronomy_GravSimFree | ||
| 4023 | * to release the memory. | ||
| 4024 | * | ||
| 4025 | * @param simOut | ||
| 4026 | * The address of a pointer to store the newly allocated simulation object. | ||
| 4027 | * The type #astro_grav_sim_t is an opaque type, so its internal structure is not documented. | ||
| 4028 | * | ||
| 4029 | * @param originBody | ||
| 4030 | * Specifies the origin of the reference frame. | ||
| 4031 | * All position vectors and velocity vectors will use `originBody` | ||
| 4032 | * as the origin of the coordinate system. | ||
| 4033 | * This origin applies to all the input vectors provided in the | ||
| 4034 | * `bodyStateArray` parameter of this function, along with all | ||
| 4035 | * output vectors returned by #Astronomy_GravSimUpdate. | ||
| 4036 | * Most callers will want to provide one of the following: | ||
| 4037 | * `BODY_SUN` for heliocentric coordinates, | ||
| 4038 | * `BODY_SSB` for solar system barycentric coordinates, | ||
| 4039 | * or `BODY_EARTH` for geocentric coordinates. Note that the | ||
| 4040 | * gravity simulator does not correct for light travel time; | ||
| 4041 | * all state vectors are tied to a Newtonian "instantaneous" time. | ||
| 4042 | * | ||
| 4043 | * @param time | ||
| 4044 | * The initial time at which to start the simulation. | ||
| 4045 | * | ||
| 4046 | * @param numBodies | ||
| 4047 | * The number of small bodies to be simulated. This may be any non-negative integer. | ||
| 4048 | * | ||
| 4049 | * @param bodyStateArray | ||
| 4050 | * An array of initial state vectors (positions and velocities) of the small bodies to be simulated. | ||
| 4051 | * The caller must know the positions and velocities of the small bodies at an initial moment in time. | ||
| 4052 | * Their positions and velocities are expressed with respect to `originBody`, using J2000 mean equator orientation (EQJ). | ||
| 4053 | * Positions are expressed in astronomical units (AU). Velocities are expressed in AU/day. | ||
| 4054 | * All the times embedded within the state vectors must be exactly equal to `time`, | ||
| 4055 | * or this function will fail with the error `ASTRO_INCONSISTENT_TIMES`. | ||
| 4056 | * | ||
| 4057 | * @return | ||
| 4058 | * `ASTRO_SUCCESS` on success, with `*sim` set to a non-NULL value. Otherwise an error code with `*sim` set to NULL. | ||
| 4059 | */ | ||
| 4060 | astro_status_t Astronomy_GravSimInit( | ||
| 4061 | astro_grav_sim_t **simOut, | ||
| 4062 | astro_body_t originBody, | ||
| 4063 | astro_time_t time, | ||
| 4064 | int numBodies, | ||
| 4065 | const astro_state_vector_t *bodyStateArray) | ||
| 4066 | { | ||
| 4067 | astro_grav_sim_t *sim; | ||
| 4068 | astro_status_t status; | ||
| 4069 | body_grav_calc_t *array; | ||
| 4070 | int i; | ||
| 4071 | |||
| 4072 | /* Validate parameters before attempting to allocate memory. */ | ||
| 4073 | |||
| 4074 | if (simOut == NULL) | ||
| 4075 | return ASTRO_INVALID_PARAMETER; | ||
| 4076 | |||
| 4077 | *simOut = NULL; | ||
| 4078 | |||
| 4079 | if (numBodies < 0) | ||
| 4080 | return ASTRO_INVALID_PARAMETER; | ||
| 4081 | |||
| 4082 | if (numBodies > 0 && bodyStateArray == NULL) | ||
| 4083 | return ASTRO_INVALID_PARAMETER; | ||
| 4084 | |||
| 4085 | if (originBody < BODY_MERCURY || originBody > BODY_SSB) | ||
| 4086 | return ASTRO_INVALID_BODY; | ||
| 4087 | |||
| 4088 | /* Verify that all the state vectors are valid and have matching times. */ | ||
| 4089 | for (i = 0; i < numBodies; ++i) | ||
| 4090 | { | ||
| 4091 | if (bodyStateArray[i].status != ASTRO_SUCCESS) | ||
| 4092 | return ASTRO_INVALID_PARAMETER; | ||
| 4093 | |||
| 4094 | if (bodyStateArray[i].t.tt != time.tt) | ||
| 4095 | return ASTRO_INCONSISTENT_TIMES; | ||
| 4096 | } | ||
| 4097 | |||
| 4098 | *simOut = sim = (astro_grav_sim_t *) calloc(1, sizeof(astro_grav_sim_t)); | ||
| 4099 | if (sim == NULL) | ||
| 4100 | return ASTRO_OUT_OF_MEMORY; | ||
| 4101 | |||
| 4102 | sim->originBody = originBody; | ||
| 4103 | sim->numBodies = numBodies; | ||
| 4104 | sim->prev = &(sim->endpoint[0]); | ||
| 4105 | sim->curr = &(sim->endpoint[1]); | ||
| 4106 | sim->curr->time = time; | ||
| 4107 | |||
| 4108 | if (numBodies > 0) | ||
| 4109 | { | ||
| 4110 | sim->prev->bodies = (body_grav_calc_t *) calloc(numBodies, sizeof(body_grav_calc_t)); | ||
| 4111 | sim->curr->bodies = (body_grav_calc_t *) calloc(numBodies, sizeof(body_grav_calc_t)); | ||
| 4112 | if (sim->prev->bodies == NULL || sim->curr->bodies == NULL) | ||
| 4113 | { | ||
| 4114 | status = ASTRO_OUT_OF_MEMORY; | ||
| 4115 | goto fail; | ||
| 4116 | } | ||
| 4117 | } | ||
| 4118 | |||
| 4119 | /* Remember the initial states of all the bodies as "current". */ | ||
| 4120 | /* Convert from the public type astro_state_t to our internal type body_grav_calc_t. */ | ||
| 4121 | array = sim->curr->bodies; | ||
| 4122 | for (i = 0; i < numBodies; ++i) | ||
| 4123 | { | ||
| 4124 | array[i].tt = bodyStateArray[i].t.tt; | ||
| 4125 | array[i].r.x = bodyStateArray[i].x; | ||
| 4126 | array[i].r.y = bodyStateArray[i].y; | ||
| 4127 | array[i].r.z = bodyStateArray[i].z; | ||
| 4128 | array[i].v.x = bodyStateArray[i].vx; | ||
| 4129 | array[i].v.y = bodyStateArray[i].vy; | ||
| 4130 | array[i].v.z = bodyStateArray[i].vz; | ||
| 4131 | } | ||
| 4132 | |||
| 4133 | /* Calculate the state of the Sun and planets. */ | ||
| 4134 | CalcSolarSystem(sim); | ||
| 4135 | |||
| 4136 | /* | ||
| 4137 | We need to do all the physics calculations in barycentric coordinates. | ||
| 4138 | But the caller can provide the input vectors with respect to any body. | ||
| 4139 | Correct the input body state vectors for the specified coordinate origin. | ||
| 4140 | */ | ||
| 4141 | if (originBody != BODY_SSB) | ||
| 4142 | { | ||
| 4143 | /* Determine the barycentric state of the origin body. */ | ||
| 4144 | astro_state_vector_t originState = GravSimOriginState(sim); | ||
| 4145 | if (originState.status != ASTRO_SUCCESS) | ||
| 4146 | { | ||
| 4147 | status = originState.status; | ||
| 4148 | goto fail; | ||
| 4149 | } | ||
| 4150 | |||
| 4151 | /* Add barycentric origin to origin-centric body to obtain barycentric body. */ | ||
| 4152 | for (i = 0; i < numBodies; ++i) | ||
| 4153 | { | ||
| 4154 | array[i].r.x += originState.x; | ||
| 4155 | array[i].r.y += originState.y; | ||
| 4156 | array[i].r.z += originState.z; | ||
| 4157 | array[i].v.x += originState.vx; | ||
| 4158 | array[i].v.y += originState.vy; | ||
| 4159 | array[i].v.z += originState.vz; | ||
| 4160 | } | ||
| 4161 | } | ||
| 4162 | |||
| 4163 | /* Calculate the net acceleration experienced by the small bodies. */ | ||
| 4164 | CalcBodyAccelerations(sim); | ||
| 4165 | |||
| 4166 | /* To prepare for a possible swap operation, duplicate the current state into the previous state. */ | ||
| 4167 | GravSimDuplicate(sim); | ||
| 4168 | |||
| 4169 | return ASTRO_SUCCESS; | ||
| 4170 | |||
| 4171 | fail: | ||
| 4172 | Astronomy_GravSimFree(sim); | ||
| 4173 | *simOut = NULL; | ||
| 4174 | return status; | ||
| 4175 | } | ||
| 4176 | |||
| 4177 | |||
| 4178 | /** | ||
| 4179 | * @brief Advances a gravity simulation by a small time step. | ||
| 4180 | * | ||
| 4181 | * @param sim | ||
| 4182 | * A simulation object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4183 | * | ||
| 4184 | * @param time | ||
| 4185 | * A time that is a small increment away from the current simulation time. | ||
| 4186 | * It is up to the developer to figure out an appropriate time increment. | ||
| 4187 | * Depending on the trajectories, a smaller or larger increment | ||
| 4188 | * may be needed for the desired accuracy. Some experimentation may be needed. | ||
| 4189 | * Generally, bodies that stay in the outer Solar System and move slowly can | ||
| 4190 | * use larger time steps. Bodies that pass into the inner Solar System and | ||
| 4191 | * move faster will need a smaller time step to maintain accuracy. | ||
| 4192 | * The `time` value may be after or before the current simulation time | ||
| 4193 | * to move forward or backward in time. | ||
| 4194 | * | ||
| 4195 | * @param numBodies | ||
| 4196 | * The number of bodies whose state vectors are to be updated. | ||
| 4197 | * This is the number of elements in the `bodyStateArray`. | ||
| 4198 | * This parameter is passed as a sanity check, and must be equal | ||
| 4199 | * to the value passed to #Astronomy_GravSimInit when `sim` was created. | ||
| 4200 | * | ||
| 4201 | * @param bodyStateArray | ||
| 4202 | * An array big enough to hold `numBodies` state vectors, to receive | ||
| 4203 | * the updated positions and velocities of the simulated small bodies. | ||
| 4204 | * Alternatively, `bodyStateArray` may be NULL if the output of this | ||
| 4205 | * simulation step is not needed. This makes the call slightly faster. | ||
| 4206 | * | ||
| 4207 | * @return | ||
| 4208 | * `ASTRO_SUCCESS` if the calculation was successful. | ||
| 4209 | * Otherwise, an error code if something went wrong, in which case | ||
| 4210 | * the simulation should be considered "broken". This means there | ||
| 4211 | * is no reliable output in `bodyStateArray` and that no more calculations | ||
| 4212 | * can be performed with `sim`. | ||
| 4213 | */ | ||
| 4214 | astro_status_t Astronomy_GravSimUpdate( | ||
| 4215 | astro_grav_sim_t *sim, | ||
| 4216 | astro_time_t time, | ||
| 4217 | int numBodies, | ||
| 4218 | astro_state_vector_t *bodyStateArray) | ||
| 4219 | { | ||
| 4220 | terse_vector_t acc; | ||
| 4221 | double dt; /* terrestrial time increment */ | ||
| 4222 | int i; | ||
| 4223 | |||
| 4224 | /* | ||
| 4225 | The caller's understanding of the number of bodies must match the actual | ||
| 4226 | array size in `sim`, or we risk corrupting/accessing invalid memory. | ||
| 4227 | */ | ||
| 4228 | if (numBodies != sim->numBodies) | ||
| 4229 | return ASTRO_INVALID_PARAMETER; | ||
| 4230 | |||
| 4231 | dt = time.tt - sim->curr->time.tt; | ||
| 4232 | |||
| 4233 | if (dt == 0.0) | ||
| 4234 | { | ||
| 4235 | /* | ||
| 4236 | Special case: the time has not changed, so skip the usual physics calculations. | ||
| 4237 | This allows a way for the caller to query the current state if desired. | ||
| 4238 | It is also necessary to avoid dividing by `dt` if `dt` is zero. | ||
| 4239 | To prepare for a possible swap operation, duplicate the current state into the previous state. | ||
| 4240 | */ | ||
| 4241 | GravSimDuplicate(sim); | ||
| 4242 | } | ||
| 4243 | else | ||
| 4244 | { | ||
| 4245 | /* Swap the current state and the previous state. Then calculate the new current state. */ | ||
| 4246 | Astronomy_GravSimSwap(sim); | ||
| 4247 | |||
| 4248 | /* Update the current time. This is the only place we have a full (tt,ut) pair. */ | ||
| 4249 | /* All of the Newtonian dynamics are calculated using tt only. */ | ||
| 4250 | sim->curr->time = time; | ||
| 4251 | |||
| 4252 | /* Now that sim->time is set, it is safe to call `CalcSolarSystem`. */ | ||
| 4253 | CalcSolarSystem(sim); | ||
| 4254 | |||
| 4255 | for (i = 0; i < numBodies; ++i) | ||
| 4256 | { | ||
| 4257 | /* | ||
| 4258 | Estimate the positions of the small bodies as if their | ||
| 4259 | current accelerations apply across the whole time interval. | ||
| 4260 | approx_pos = pos1 + vel1*dt + (1/2)acc*dt^2 | ||
| 4261 | */ | ||
| 4262 | const body_grav_calc_t *prev = &sim->prev->bodies[i]; | ||
| 4263 | sim->curr->bodies[i].r = UpdatePosition(dt, prev->r, prev->v, prev->a); | ||
| 4264 | } | ||
| 4265 | |||
| 4266 | /* | ||
| 4267 | Calculate the acceleration experienced by the small bodies | ||
| 4268 | at their respective approximate next locations. | ||
| 4269 | */ | ||
| 4270 | CalcBodyAccelerations(sim); | ||
| 4271 | |||
| 4272 | for (i = 0; i < numBodies; ++i) | ||
| 4273 | { | ||
| 4274 | const body_grav_calc_t *prev = &sim->prev->bodies[i]; | ||
| 4275 | body_grav_calc_t *curr = &sim->curr->bodies[i]; | ||
| 4276 | |||
| 4277 | /* | ||
| 4278 | Calculate the average of the acceleration vectors | ||
| 4279 | experienced by the previous body positions and | ||
| 4280 | their estimated next positions. | ||
| 4281 | These become estimates of the mean effective accelerations over the whole interval. | ||
| 4282 | */ | ||
| 4283 | acc = VecMean(prev->a, curr->a); | ||
| 4284 | |||
| 4285 | /* | ||
| 4286 | Refine the estimates of position and velocity at the next time step, | ||
| 4287 | using the mean acceleration as a better approximation of the | ||
| 4288 | continuously changing acceleration acting on each body. | ||
| 4289 | */ | ||
| 4290 | curr->tt = time.tt; | ||
| 4291 | curr->r = UpdatePosition(dt, prev->r, prev->v, acc); | ||
| 4292 | curr->v = UpdateVelocity(dt, prev->v, acc); | ||
| 4293 | } | ||
| 4294 | |||
| 4295 | /* | ||
| 4296 | Re-calculate accelerations experienced by each body. | ||
| 4297 | These will be needed for the next simulation step (if any). | ||
| 4298 | Also, they will be potentially useful if some day we add | ||
| 4299 | a function to query the acceleration vectors for the bodies. | ||
| 4300 | */ | ||
| 4301 | CalcBodyAccelerations(sim); | ||
| 4302 | } | ||
| 4303 | |||
| 4304 | /* | ||
| 4305 | Translate our internal calculations of body positions | ||
| 4306 | and velocities into state vectors that the caller can understand. | ||
| 4307 | But if the output buffer `bodyStateArray` is NULL, it means | ||
| 4308 | the caller wanted us to update the simulation state without | ||
| 4309 | returning any output. | ||
| 4310 | */ | ||
| 4311 | if (bodyStateArray != NULL) | ||
| 4312 | { | ||
| 4313 | for (i = 0; i < numBodies; ++i) | ||
| 4314 | bodyStateArray[i] = ExportGravCalc(sim->curr->bodies[i], time); | ||
| 4315 | |||
| 4316 | if (sim->originBody != BODY_SSB) | ||
| 4317 | { | ||
| 4318 | /* Determine the barycentric state of the origin body. */ | ||
| 4319 | astro_state_vector_t originState = GravSimOriginState(sim); | ||
| 4320 | if (originState.status != ASTRO_SUCCESS) | ||
| 4321 | return originState.status; | ||
| 4322 | |||
| 4323 | /* Subtract vectors to convert barycentric states to origin-centric states. */ | ||
| 4324 | for (i = 0; i < numBodies; ++i) | ||
| 4325 | { | ||
| 4326 | bodyStateArray[i].x -= originState.x; | ||
| 4327 | bodyStateArray[i].y -= originState.y; | ||
| 4328 | bodyStateArray[i].z -= originState.z; | ||
| 4329 | bodyStateArray[i].vx -= originState.vx; | ||
| 4330 | bodyStateArray[i].vy -= originState.vy; | ||
| 4331 | bodyStateArray[i].vz -= originState.vz; | ||
| 4332 | } | ||
| 4333 | } | ||
| 4334 | } | ||
| 4335 | |||
| 4336 | return ASTRO_SUCCESS; | ||
| 4337 | } | ||
| 4338 | |||
| 4339 | |||
| 4340 | /** | ||
| 4341 | * @brief Get the position and velocity of a Solar System body included in the simulation. | ||
| 4342 | * | ||
| 4343 | * In order to simulate the movement of small bodies through the Solar System, | ||
| 4344 | * the simulator needs to calculate the state vectors for the Sun and planets. | ||
| 4345 | * | ||
| 4346 | * If an application wants to know the positions of one or more of the planets | ||
| 4347 | * in addition to the small bodies, this function provides a way to obtain | ||
| 4348 | * their state vectors. This is provided for the sake of efficiency, to avoid | ||
| 4349 | * redundant calculations. | ||
| 4350 | * | ||
| 4351 | * @param sim | ||
| 4352 | * A gravity simulator object created by a successful call to #Astronomy_GravSimInit. | ||
| 4353 | * | ||
| 4354 | * @param body | ||
| 4355 | * The Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, or Neptune. | ||
| 4356 | * | ||
| 4357 | * @return | ||
| 4358 | * If the given body is part of the set of calculated bodies | ||
| 4359 | * (Sun and planets), returns the current time step's state | ||
| 4360 | * vector for that body, expressed in the coordinate system | ||
| 4361 | * that was specified by the `originBody` parameter to #Astronomy_GravSimInit. | ||
| 4362 | * Success is indicated by the returned structure's `status` field holding | ||
| 4363 | * `ASTRO_SUCCESS`. Any other `status` value indicates an error, meaning | ||
| 4364 | * the returned state vector is invalid. | ||
| 4365 | */ | ||
| 4366 | astro_state_vector_t Astronomy_GravSimBodyState( | ||
| 4367 | astro_grav_sim_t *sim, | ||
| 4368 | astro_body_t body) | ||
| 4369 | { | ||
| 4370 | astro_state_vector_t state; | ||
| 4371 | body_state_t *bptr; | ||
| 4372 | |||
| 4373 | bptr = GravSimBodyStatePtr(sim, body); | ||
| 4374 | if (bptr == NULL) | ||
| 4375 | return StateVecError(ASTRO_INVALID_BODY, sim->curr->time); | ||
| 4376 | |||
| 4377 | state = GravSimOriginState(sim); | ||
| 4378 | if (state.status != ASTRO_SUCCESS) | ||
| 4379 | return state; | ||
| 4380 | |||
| 4381 | /* | ||
| 4382 | Subtract the origin's state from the body's barycentric | ||
| 4383 | state to get the body in the desired reference frame. | ||
| 4384 | state.t and state.status are already initialized correctly. | ||
| 4385 | */ | ||
| 4386 | |||
| 4387 | state.x = bptr->r.x - state.x; | ||
| 4388 | state.y = bptr->r.y - state.y; | ||
| 4389 | state.z = bptr->r.z - state.z; | ||
| 4390 | state.vx = bptr->v.x - state.vx; | ||
| 4391 | state.vy = bptr->v.y - state.vy; | ||
| 4392 | state.vz = bptr->v.z - state.vz; | ||
| 4393 | |||
| 4394 | return state; | ||
| 4395 | } | ||
| 4396 | |||
| 4397 | |||
| 4398 | /** | ||
| 4399 | * @brief Exchange the current time step with the previous time step. | ||
| 4400 | * | ||
| 4401 | * Sometimes it is helpful to "explore" various times near a given | ||
| 4402 | * simulation time step, while repeatedly returning to the original | ||
| 4403 | * time step. For example, when backdating a position for light travel | ||
| 4404 | * time, the caller may wish to repeatedly try different amounts of | ||
| 4405 | * backdating. When the backdating solver has converged, the caller | ||
| 4406 | * wants to leave the simulation in its original state. | ||
| 4407 | * | ||
| 4408 | * This function allows a single "undo" of a simulation, and does so | ||
| 4409 | * very efficiently. | ||
| 4410 | * | ||
| 4411 | * Usually this function will be called immediately after a matching | ||
| 4412 | * call to #Astronomy_GravSimUpdate. It has the effect of rolling | ||
| 4413 | * back the most recent update. If called twice in a row, it reverts | ||
| 4414 | * the swap and thus has no net effect. | ||
| 4415 | * | ||
| 4416 | * #Astronomy_GravSimInit initializes the current state and previous | ||
| 4417 | * state to be identical. Both states represent the `time` parameter that was | ||
| 4418 | * passed into the initializer. Therefore, `Astronomy_GravSimSwap` will | ||
| 4419 | * have no effect from the caller's point of view when passed a simulator | ||
| 4420 | * that has not yet been updated by a call to #Astronomy_GravSimUpdate. | ||
| 4421 | * | ||
| 4422 | * @param sim | ||
| 4423 | * A gravity simulator object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4424 | */ | ||
| 4425 | void Astronomy_GravSimSwap(astro_grav_sim_t *sim) | ||
| 4426 | { | ||
| 4427 | gravsim_endpoint_t *swap = sim->prev; | ||
| 4428 | sim->prev = sim->curr; | ||
| 4429 | sim->curr = swap; | ||
| 4430 | } | ||
| 4431 | |||
| 4432 | |||
| 4433 | /** | ||
| 4434 | * @brief Returns the time of the current simulation step. | ||
| 4435 | * | ||
| 4436 | * @param sim | ||
| 4437 | * A gravity simulator object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4438 | */ | ||
| 4439 | astro_time_t Astronomy_GravSimTime(const astro_grav_sim_t *sim) | ||
| 4440 | { | ||
| 4441 | return sim->curr->time; | ||
| 4442 | } | ||
| 4443 | |||
| 4444 | |||
| 4445 | /** | ||
| 4446 | * @brief Returns the number of small bodies represented in this simulation. | ||
| 4447 | * | ||
| 4448 | * When a simulation is created by a call to #Astronomy_GravSimInit, | ||
| 4449 | * the caller specifies the number of small bodies. | ||
| 4450 | * This function returns that same number, which may be convenient for a caller, | ||
| 4451 | * so that it does not need to track the body count separately. | ||
| 4452 | * | ||
| 4453 | * @param sim | ||
| 4454 | * A gravity simulator object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4455 | */ | ||
| 4456 | int Astronomy_GravSimNumBodies(const astro_grav_sim_t *sim) | ||
| 4457 | { | ||
| 4458 | return sim->numBodies; | ||
| 4459 | } | ||
| 4460 | |||
| 4461 | |||
| 4462 | /** | ||
| 4463 | * @brief Returns the body whose center is the coordinate origin that small bodies are referenced to. | ||
| 4464 | * | ||
| 4465 | * When a simulation is created by a call to #Astronomy_GravSimInit, | ||
| 4466 | * the caller specifies an `originBody` to indicate the coordinate origin | ||
| 4467 | * used to represent the small bodies being simulated. | ||
| 4468 | * This function returns that same #astro_body_t value. | ||
| 4469 | * | ||
| 4470 | * @param sim | ||
| 4471 | * A gravity simulator object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4472 | */ | ||
| 4473 | astro_body_t Astronomy_GravSimOrigin(const astro_grav_sim_t *sim) | ||
| 4474 | { | ||
| 4475 | return sim->originBody; | ||
| 4476 | } | ||
| 4477 | |||
| 4478 | |||
| 4479 | /** | ||
| 4480 | * @brief Releases memory allocated to a gravity simulator object. | ||
| 4481 | * | ||
| 4482 | * To avoid memory leaks, any successful call to #Astronomy_GravSimInit | ||
| 4483 | * must be paired with a matching call to `Astronomy_GravSimFree`. | ||
| 4484 | * | ||
| 4485 | * @param sim | ||
| 4486 | * A gravity simulator object that was created by a prior call to #Astronomy_GravSimInit. | ||
| 4487 | */ | ||
| 4488 | void Astronomy_GravSimFree(astro_grav_sim_t *sim) | ||
| 4489 | { | ||
| 4490 | if (sim != NULL) | ||
| 4491 | { | ||
| 4492 | free(sim->endpoint[0].bodies); | ||
| 4493 | free(sim->endpoint[1].bodies); | ||
| 4494 | free(sim); | ||
| 4495 | } | ||
| 4496 | } | ||
| 4497 | |||
| 4498 | |||
| 4499 | /*------------------ begin Pluto integrator ------------------*/ | ||
| 4500 | |||
| 4501 | static const body_state_t PlutoStateTable[] = | ||
| 4502 | { | ||
| 4503 | { -730000.0, {-26.118207232108, -14.376168177825, 3.384402515299}, { 1.6339372163656e-03, -2.7861699588508e-03, -1.3585880229445e-03} } | ||
| 4504 | , { -700800.0, { 41.974905202127, -0.448502952929, -12.770351505989}, { 7.3458569351457e-04, 2.2785014891658e-03, 4.8619778602049e-04} } | ||
| 4505 | , { -671600.0, { 14.706930780744, 44.269110540027, 9.353698474772}, {-2.1000147999800e-03, 2.2295915939915e-04, 7.0143443551414e-04} } | ||
| 4506 | , { -642400.0, {-29.441003929957, -6.430161530570, 6.858481011305}, { 8.4495803960544e-04, -3.0783914758711e-03, -1.2106305981192e-03} } | ||
| 4507 | , { -613200.0, { 39.444396946234, -6.557989760571, -13.913760296463}, { 1.1480029005873e-03, 2.2400006880665e-03, 3.5168075922288e-04} } | ||
| 4508 | , { -584000.0, { 20.230380950700, 43.266966657189, 7.382966091923}, {-1.9754081700585e-03, 5.3457141292226e-04, 7.5929169129793e-04} } | ||
| 4509 | , { -554800.0, {-30.658325364620, 2.093818874552, 9.880531138071}, { 6.1010603013347e-05, -3.1326500935382e-03, -9.9346125151067e-04} } | ||
| 4510 | , { -525600.0, { 35.737703251673, -12.587706024764, -14.677847247563}, { 1.5802939375649e-03, 2.1347678412429e-03, 1.9074436384343e-04} } | ||
| 4511 | , { -496400.0, { 25.466295188546, 41.367478338417, 5.216476873382}, {-1.8054401046468e-03, 8.3283083599510e-04, 8.0260156912107e-04} } | ||
| 4512 | , { -467200.0, {-29.847174904071, 10.636426313081, 12.297904180106}, {-6.3257063052907e-04, -2.9969577578221e-03, -7.4476074151596e-04} } | ||
| 4513 | , { -438000.0, { 30.774692107687, -18.236637015304, -14.945535879896}, { 2.0113162005465e-03, 1.9353827024189e-03, -2.0937793168297e-06} } | ||
| 4514 | , { -408800.0, { 30.243153324028, 38.656267888503, 2.938501750218}, {-1.6052508674468e-03, 1.1183495337525e-03, 8.3333973416824e-04} } | ||
| 4515 | , { -379600.0, {-27.288984772533, 18.643162147874, 14.023633623329}, {-1.1856388898191e-03, -2.7170609282181e-03, -4.9015526126399e-04} } | ||
| 4516 | , { -350400.0, { 24.519605196774, -23.245756064727, -14.626862367368}, { 2.4322321483154e-03, 1.6062008146048e-03, -2.3369181613312e-04} } | ||
| 4517 | , { -321200.0, { 34.505274805875, 35.125338586954, 0.557361475637}, {-1.3824391637782e-03, 1.3833397561817e-03, 8.4823598806262e-04} } | ||
| 4518 | , { -292000.0, {-23.275363915119, 25.818514298769, 15.055381588598}, {-1.6062295460975e-03, -2.3395961498533e-03, -2.4377362639479e-04} } | ||
| 4519 | , { -262800.0, { 17.050384798092, -27.180376290126, -13.608963321694}, { 2.8175521080578e-03, 1.1358749093955e-03, -4.9548725258825e-04} } | ||
| 4520 | , { -233600.0, { 38.093671910285, 30.880588383337, -1.843688067413}, {-1.1317697153459e-03, 1.6128814698472e-03, 8.4177586176055e-04} } | ||
| 4521 | , { -204400.0, {-18.197852930878, 31.932869934309, 15.438294826279}, {-1.9117272501813e-03, -1.9146495909842e-03, -1.9657304369835e-05} } | ||
| 4522 | , { -175200.0, { 8.528924039997, -29.618422200048, -11.805400994258}, { 3.1034370787005e-03, 5.1393633292430e-04, -7.7293066202546e-04} } | ||
| 4523 | , { -146000.0, { 40.946857258640, 25.904973592021, -4.256336240499}, {-8.3652705194051e-04, 1.8129497136404e-03, 8.1564228273060e-04} } | ||
| 4524 | , { -116800.0, {-12.326958895325, 36.881883446292, 15.217158258711}, {-2.1166103705038e-03, -1.4814420035990e-03, 1.7401209844705e-04} } | ||
| 4525 | , { -87600.0, { -0.633258375909, -30.018759794709, -9.171932874950}, { 3.2016994581737e-03, -2.5279858672148e-04, -1.0411088271861e-03} } | ||
| 4526 | , { -58400.0, { 42.936048423883, 20.344685584452, -6.588027007912}, {-5.0525450073192e-04, 1.9910074335507e-03, 7.7440196540269e-04} } | ||
| 4527 | , { -29200.0, { -5.975910552974, 40.611809958460, 14.470131723673}, {-2.2184202156107e-03, -1.0562361130164e-03, 3.3652250216211e-04} } | ||
| 4528 | , { 0.0, { -9.875369580774, -27.978926224737, -5.753711824704}, { 3.0287533248818e-03, -1.1276087003636e-03, -1.2651326732361e-03} } | ||
| 4529 | , { 29200.0, { 43.958831986165, 14.214147973292, -8.808306227163}, {-1.4717608981871e-04, 2.1404187242141e-03, 7.1486567806614e-04} } | ||
| 4530 | , { 58400.0, { 0.678136763520, 43.094461639362, 13.243238780721}, {-2.2358226110718e-03, -6.3233636090933e-04, 4.7664798895648e-04} } | ||
| 4531 | , { 87600.0, {-18.282602096834, -23.305039586660, -1.766620508028}, { 2.5567245263557e-03, -1.9902940754171e-03, -1.3943491701082e-03} } | ||
| 4532 | , { 116800.0, { 43.873338744526, 7.700705617215, -10.814273666425}, { 2.3174803055677e-04, 2.2402163127924e-03, 6.2988756452032e-04} } | ||
| 4533 | , { 146000.0, { 7.392949027906, 44.382678951534, 11.629500214854}, {-2.1932815453830e-03, -2.1751799585364e-04, 5.9556516201114e-04} } | ||
| 4534 | , { 175200.0, {-24.981690229261, -16.204012851426, 2.466457544298}, { 1.8193989149580e-03, -2.6765419531201e-03, -1.3848283502247e-03} } | ||
| 4535 | , { 204400.0, { 42.530187039511, 0.845935508021, -12.554907527683}, { 6.5059779150669e-04, 2.2725657282262e-03, 5.1133743202822e-04} } | ||
| 4536 | , { 233600.0, { 13.999526486822, 44.462363044894, 9.669418486465}, {-2.1079296569252e-03, 1.7533423831993e-04, 6.9128485798076e-04} } | ||
| 4537 | , { 262800.0, {-29.184024803031, -7.371243995762, 6.493275957928}, { 9.3581363109681e-04, -3.0610357109184e-03, -1.2364201089345e-03} } | ||
| 4538 | , { 292000.0, { 39.831980671753, -6.078405766765, -13.909815358656}, { 1.1117769689167e-03, 2.2362097830152e-03, 3.6230548231153e-04} } | ||
| 4539 | , { 321200.0, { 20.294955108476, 43.417190420251, 7.450091985932}, {-1.9742157451535e-03, 5.3102050468554e-04, 7.5938408813008e-04} } | ||
| 4540 | , { 350400.0, {-30.669992302160, 2.318743558955, 9.973480913858}, { 4.5605107450676e-05, -3.1308219926928e-03, -9.9066533301924e-04} } | ||
| 4541 | , { 379600.0, { 35.626122155983, -12.897647509224, -14.777586508444}, { 1.6015684949743e-03, 2.1171931182284e-03, 1.8002516202204e-04} } | ||
| 4542 | , { 408800.0, { 26.133186148561, 41.232139187599, 5.006401326220}, {-1.7857704419579e-03, 8.6046232702817e-04, 8.0614690298954e-04} } | ||
| 4543 | , { 438000.0, {-29.576740229230, 11.863535943587, 12.631323039872}, {-7.2292830060955e-04, -2.9587820140709e-03, -7.0824296450300e-04} } | ||
| 4544 | , { 467200.0, { 29.910805787391, -19.159019294000, -15.013363865194}, { 2.0871080437997e-03, 1.8848372554514e-03, -3.8528655083926e-05} } | ||
| 4545 | , { 496400.0, { 31.375957451819, 38.050372720763, 2.433138343754}, {-1.5546055556611e-03, 1.1699815465629e-03, 8.3565439266001e-04} } | ||
| 4546 | , { 525600.0, {-26.360071336928, 20.662505904952, 14.414696258958}, {-1.3142373118349e-03, -2.6236647854842e-03, -4.2542017598193e-04} } | ||
| 4547 | , { 554800.0, { 22.599441488648, -24.508879898306, -14.484045731468}, { 2.5454108304806e-03, 1.4917058755191e-03, -3.0243665086079e-04} } | ||
| 4548 | , { 584000.0, { 35.877864013014, 33.894226366071, -0.224524636277}, {-1.2941245730845e-03, 1.4560427668319e-03, 8.4762160640137e-04} } | ||
| 4549 | , { 613200.0, {-21.538149762417, 28.204068269761, 15.321973799534}, {-1.7312117409010e-03, -2.1939631314577e-03, -1.6316913275180e-04} } | ||
| 4550 | , { 642400.0, { 13.971521374415, -28.339941764789, -13.083792871886}, { 2.9334630526035e-03, 9.1860931752944e-04, -5.9939422488627e-04} } | ||
| 4551 | , { 671600.0, { 39.526942044143, 28.939897360110, -2.872799527539}, {-1.0068481658095e-03, 1.7021132888090e-03, 8.3578230511981e-04} } | ||
| 4552 | , { 700800.0, {-15.576200701394, 34.399412961275, 15.466033737854}, {-2.0098814612884e-03, -1.7191109825989e-03, 7.0414782780416e-05} } | ||
| 4553 | , { 730000.0, { 4.243252837090, -30.118201690825, -10.707441231349}, { 3.1725847067411e-03, 1.6098461202270e-04, -9.0672150593868e-04} } | ||
| 4554 | }; | ||
| 4555 | |||
| 4556 | /* FIXFIXFIX - Using a global is not thread-safe. Either add thread-locks or change API to accept a cache pointer. */ | ||
| 4557 | static body_segment_t *pluto_cache[PLUTO_NUM_STATES-1]; | ||
| 4558 | |||
| 4559 | |||
| 4560 | static int ClampIndex(double frac, int nsteps) | ||
| 4561 | { | ||
| 4562 | int index = (int) floor(frac); | ||
| 4563 | if (index < 0) | ||
| 4564 | return 0; | ||
| 4565 | if (index >= nsteps) | ||
| 4566 | return nsteps-1; | ||
| 4567 | return index; | ||
| 4568 | } | ||
| 4569 | |||
| 4570 | |||
| 4571 | static astro_status_t GetSegment(int *seg_index, body_segment_t *cache[], double tt) | ||
| 4572 | { | ||
| 4573 | int i; | ||
| 4574 | body_segment_t reverse; | ||
| 4575 | body_segment_t *seg; | ||
| 4576 | major_bodies_t bary; | ||
| 4577 | double step_tt, ramp; | ||
| 4578 | |||
| 4579 | if (tt < PlutoStateTable[0].tt || tt > PlutoStateTable[PLUTO_NUM_STATES-1].tt) | ||
| 4580 | { | ||
| 4581 | /* We don't bother calculating a segment. Let the caller crawl backward/forward to this time. */ | ||
| 4582 | *seg_index = -1; | ||
| 4583 | return ASTRO_SUCCESS; | ||
| 4584 | } | ||
| 4585 | |||
| 4586 | /* See if we have a segment that straddles the requested time. */ | ||
| 4587 | /* If so, return it. Otherwise, calculate it and return it. */ | ||
| 4588 | |||
| 4589 | *seg_index = ClampIndex((tt - PlutoStateTable[0].tt) / PLUTO_TIME_STEP, PLUTO_NUM_STATES-1); | ||
| 4590 | if (cache[*seg_index] == NULL) | ||
| 4591 | { | ||
| 4592 | /* Allocate memory for the segment (about 11K each). */ | ||
| 4593 | seg = cache[*seg_index] = (body_segment_t *) calloc(1, sizeof(body_segment_t)); | ||
| 4594 | if (seg == NULL) | ||
| 4595 | return ASTRO_OUT_OF_MEMORY; | ||
| 4596 | |||
| 4597 | /* Calculate the segment. */ | ||
| 4598 | /* Pick the pair of bracketing body states to fill the segment. */ | ||
| 4599 | |||
| 4600 | /* Each endpoint is exact. */ | ||
| 4601 | seg->step[0] = GravFromState(&bary, &PlutoStateTable[*seg_index]); | ||
| 4602 | seg->step[PLUTO_NSTEPS-1] = GravFromState(&bary, &PlutoStateTable[*seg_index + 1]); | ||
| 4603 | |||
| 4604 | /* Simulate forwards from the lower time bound. */ | ||
| 4605 | step_tt = seg->step[0].tt; | ||
| 4606 | for (i=1; i < PLUTO_NSTEPS-1; ++i) | ||
| 4607 | seg->step[i] = GravSim(&bary, step_tt += PLUTO_DT, &seg->step[i-1]); | ||
| 4608 | |||
| 4609 | /* Simulate backwards from the upper time bound. */ | ||
| 4610 | step_tt = seg->step[PLUTO_NSTEPS-1].tt; | ||
| 4611 | reverse.step[PLUTO_NSTEPS-1] = seg->step[PLUTO_NSTEPS-1]; | ||
| 4612 | for (i=PLUTO_NSTEPS-2; i > 0; --i) | ||
| 4613 | reverse.step[i] = GravSim(&bary, step_tt -= PLUTO_DT, &reverse.step[i+1]); | ||
| 4614 | |||
| 4615 | /* Fade-mix the two series so that there are no discontinuities. */ | ||
| 4616 | for (i=PLUTO_NSTEPS-2; i > 0; --i) | ||
| 4617 | { | ||
| 4618 | ramp = (double)i / (PLUTO_NSTEPS-1); | ||
| 4619 | seg->step[i].r = VecRamp(seg->step[i].r, reverse.step[i].r, ramp); | ||
| 4620 | seg->step[i].v = VecRamp(seg->step[i].v, reverse.step[i].v, ramp); | ||
| 4621 | seg->step[i].a = VecRamp(seg->step[i].a, reverse.step[i].a, ramp); | ||
| 4622 | } | ||
| 4623 | } | ||
| 4624 | |||
| 4625 | return ASTRO_SUCCESS; | ||
| 4626 | } | ||
| 4627 | |||
| 4628 | |||
| 4629 | static body_grav_calc_t CalcPlutoOneWay(major_bodies_t *bary, const body_state_t *init_state, double target_tt, double dt) | ||
| 4630 | { | ||
| 4631 | body_grav_calc_t calc; | ||
| 4632 | int i, n; | ||
| 4633 | |||
| 4634 | calc = GravFromState(bary, init_state); | ||
| 4635 | n = (int) ceil((target_tt - calc.tt) / dt); | ||
| 4636 | for (i=0; i < n; ++i) | ||
| 4637 | calc = GravSim(bary, (i+1 == n) ? target_tt : (calc.tt + dt), &calc); | ||
| 4638 | |||
| 4639 | return calc; | ||
| 4640 | } | ||
| 4641 | |||
| 4642 | |||
| 4643 | static astro_status_t CalcPluto(body_state_t *bstate, astro_time_t time, int helio) | ||
| 4644 | { | ||
| 4645 | terse_vector_t acc, ra, rb, va, vb; | ||
| 4646 | major_bodies_t bary; | ||
| 4647 | const body_segment_t *seg; | ||
| 4648 | int seg_index, left; | ||
| 4649 | const body_grav_calc_t *s1; | ||
| 4650 | const body_grav_calc_t *s2; | ||
| 4651 | body_grav_calc_t calc; | ||
| 4652 | astro_status_t status; | ||
| 4653 | double ramp; | ||
| 4654 | |||
| 4655 | memset(bstate, 0, sizeof(body_state_t)); | ||
| 4656 | bstate->tt = time.tt; | ||
| 4657 | |||
| 4658 | status = GetSegment(&seg_index, pluto_cache, time.tt); | ||
| 4659 | if (status != ASTRO_SUCCESS) | ||
| 4660 | return status; | ||
| 4661 | |||
| 4662 | if (seg_index < 0) | ||
| 4663 | { | ||
| 4664 | /* The target time is outside the year range 0000..4000. */ | ||
| 4665 | /* Calculate it by crawling backward from 0000 or forward from 4000. */ | ||
| 4666 | /* FIXFIXFIX - This is super slow. Could optimize this with extra caching if needed. */ | ||
| 4667 | if (time.tt < PlutoStateTable[0].tt) | ||
| 4668 | calc = CalcPlutoOneWay(&bary, &PlutoStateTable[0], time.tt, -PLUTO_DT); | ||
| 4669 | else | ||
| 4670 | calc = CalcPlutoOneWay(&bary, &PlutoStateTable[PLUTO_NUM_STATES-1], time.tt, +PLUTO_DT); | ||
| 4671 | |||
| 4672 | bstate->r = calc.r; | ||
| 4673 | bstate->v = calc.v; | ||
| 4674 | } | ||
| 4675 | else | ||
| 4676 | { | ||
| 4677 | seg = pluto_cache[seg_index]; | ||
| 4678 | left = ClampIndex((time.tt - seg->step[0].tt) / PLUTO_DT, PLUTO_NSTEPS-1); | ||
| 4679 | s1 = &seg->step[left]; | ||
| 4680 | s2 = &seg->step[left+1]; | ||
| 4681 | |||
| 4682 | /* Find mean acceleration vector over the interval. */ | ||
| 4683 | acc = VecMean(s1->a, s2->a); | ||
| 4684 | |||
| 4685 | /* Use Newtonian mechanics to extrapolate away from t1 in the positive time direction. */ | ||
| 4686 | ra = UpdatePosition(time.tt - s1->tt, s1->r, s1->v, acc); | ||
| 4687 | va = UpdateVelocity(time.tt - s1->tt, s1->v, acc); | ||
| 4688 | |||
| 4689 | /* Use Newtonian mechanics to extrapolate away from t2 in the negative time direction. */ | ||
| 4690 | rb = UpdatePosition(time.tt - s2->tt, s2->r, s2->v, acc); | ||
| 4691 | vb = UpdateVelocity(time.tt - s2->tt, s2->v, acc); | ||
| 4692 | |||
| 4693 | /* Use fade in/out idea to blend the two position estimates. */ | ||
| 4694 | ramp = (time.tt - s1->tt)/PLUTO_DT; | ||
| 4695 | bstate->r = VecRamp(ra, rb, ramp); | ||
| 4696 | bstate->v = VecRamp(va, vb, ramp); | ||
| 4697 | |||
| 4698 | if (helio) | ||
| 4699 | MajorBodyBary(&bary, time.tt); | ||
| 4700 | } | ||
| 4701 | |||
| 4702 | if (helio) | ||
| 4703 | { | ||
| 4704 | /* Convert barycentric coordinates back to heliocentric coordinates. */ | ||
| 4705 | VecDecr(&bstate->r, bary.Sun.r); | ||
| 4706 | VecDecr(&bstate->v, bary.Sun.v); | ||
| 4707 | } | ||
| 4708 | |||
| 4709 | return ASTRO_SUCCESS; | ||
| 4710 | } | ||
| 4711 | |||
| 4712 | /*------------------ end Pluto integrator ------------------*/ | ||
| 4713 | |||
| 4714 | |||
| 4715 | /*---------------------- begin Jupiter moons ----------------------*/ | ||
| 4716 | |||
| 4717 | static const astro_rotation_t Rotation_JUP_EQJ = | ||
| 4718 | { | ||
| 4719 | ASTRO_SUCCESS, | ||
| 4720 | { | ||
| 4721 | { 9.99432765338654e-01, -3.36771074697641e-02, 0.00000000000000e+00 }, | ||
| 4722 | { 3.03959428906285e-02, 9.02057912352809e-01, 4.30543388542295e-01 }, | ||
| 4723 | { -1.44994559663353e-02, -4.30299169409101e-01, 9.02569881273754e-01 } | ||
| 4724 | } | ||
| 4725 | }; | ||
| 4726 | |||
| 4727 | static const vsop_term_t jm_Io_a[] = | ||
| 4728 | { | ||
| 4729 | { 0.0028210960212903, 0.0000000000000000e+00, 0.0000000000000000e+00 } | ||
| 4730 | }; | ||
| 4731 | |||
| 4732 | static const vsop_term_t jm_Io_l[] = | ||
| 4733 | { | ||
| 4734 | { -0.0001925258348666, 4.9369589722644998e+00, 1.3584836583050000e-02 }, | ||
| 4735 | { -0.0000970803596076, 4.3188796477322002e+00, 1.3034138432430000e-02 }, | ||
| 4736 | { -0.0000898817416500, 1.9080016428616999e+00, 3.0506486715799999e-03 }, | ||
| 4737 | { -0.0000553101050262, 1.4936156681568999e+00, 1.2938928911549999e-02 } | ||
| 4738 | }; | ||
| 4739 | |||
| 4740 | static const vsop_term_t jm_Io_z[] = | ||
| 4741 | { | ||
| 4742 | { 0.0041510849668155, 4.0899396355450000e+00, -1.2906864146660001e-02 }, | ||
| 4743 | { 0.0006260521444113, 1.4461888986270000e+00, 3.5515522949801999e+00 }, | ||
| 4744 | { 0.0000352747346169, 2.1256287034577999e+00, 1.2727416566999999e-04 } | ||
| 4745 | }; | ||
| 4746 | |||
| 4747 | static const vsop_term_t jm_Io_zeta[] = | ||
| 4748 | { | ||
| 4749 | { 0.0003142172466014, 2.7964219722923001e+00, -2.3150960980000000e-03 }, | ||
| 4750 | { 0.0000904169207946, 1.0477061879627001e+00, -5.6920638196000003e-04 } | ||
| 4751 | }; | ||
| 4752 | |||
| 4753 | static const vsop_term_t jm_Europa_a[] = | ||
| 4754 | { | ||
| 4755 | { 0.0044871037804314, 0.0000000000000000e+00, 0.0000000000000000e+00 }, | ||
| 4756 | { 0.0000004324367498, 1.8196456062910000e+00, 1.7822295777568000e+00 } | ||
| 4757 | }; | ||
| 4758 | |||
| 4759 | static const vsop_term_t jm_Europa_l[] = | ||
| 4760 | { | ||
| 4761 | { 0.0008576433172936, 4.3188693178264002e+00, 1.3034138308049999e-02 }, | ||
| 4762 | { 0.0004549582875086, 1.4936531751079001e+00, 1.2938928819619999e-02 }, | ||
| 4763 | { 0.0003248939825174, 1.8196494533458001e+00, 1.7822295777568000e+00 }, | ||
| 4764 | { -0.0003074250079334, 4.9377037005910998e+00, 1.3584832867240000e-02 }, | ||
| 4765 | { 0.0001982386144784, 1.9079869054759999e+00, 3.0510121286900001e-03 }, | ||
| 4766 | { 0.0001834063551804, 2.1402853388529000e+00, 1.4500978933800000e-03 }, | ||
| 4767 | { -0.0001434383188452, 5.6222140366630002e+00, 8.9111478887838003e-01 }, | ||
| 4768 | { -0.0000771939140944, 4.3002724372349999e+00, 2.6733443704265998e+00 } | ||
| 4769 | }; | ||
| 4770 | |||
| 4771 | static const vsop_term_t jm_Europa_z[] = | ||
| 4772 | { | ||
| 4773 | { -0.0093589104136341, 4.0899396509038999e+00, -1.2906864146660001e-02 }, | ||
| 4774 | { 0.0002988994545555, 5.9097265185595003e+00, 1.7693227079461999e+00 }, | ||
| 4775 | { 0.0002139036390350, 2.1256289300016000e+00, 1.2727418406999999e-04 }, | ||
| 4776 | { 0.0001980963564781, 2.7435168292649998e+00, 6.7797343008999997e-04 }, | ||
| 4777 | { 0.0001210388158965, 5.5839943711203004e+00, 3.2056614899999997e-05 }, | ||
| 4778 | { 0.0000837042048393, 1.6094538368039000e+00, -9.0402165808846002e-01 }, | ||
| 4779 | { 0.0000823525166369, 1.4461887708689001e+00, 3.5515522949801999e+00 } | ||
| 4780 | }; | ||
| 4781 | |||
| 4782 | static const vsop_term_t jm_Europa_zeta[] = | ||
| 4783 | { | ||
| 4784 | { 0.0040404917832303, 1.0477063169425000e+00, -5.6920640539999997e-04 }, | ||
| 4785 | { 0.0002200421034564, 3.3368857864364001e+00, -1.2491307306999999e-04 }, | ||
| 4786 | { 0.0001662544744719, 2.4134862374710999e+00, 0.0000000000000000e+00 }, | ||
| 4787 | { 0.0000590282470983, 5.9719930968366004e+00, -3.0561602250000000e-05 } | ||
| 4788 | }; | ||
| 4789 | |||
| 4790 | static const vsop_term_t jm_Ganymede_a[] = | ||
| 4791 | { | ||
| 4792 | { 0.0071566594572575, 0.0000000000000000e+00, 0.0000000000000000e+00 }, | ||
| 4793 | { 0.0000013930299110, 1.1586745884981000e+00, 2.6733443704265998e+00 } | ||
| 4794 | }; | ||
| 4795 | |||
| 4796 | static const vsop_term_t jm_Ganymede_l[] = | ||
| 4797 | { | ||
| 4798 | { 0.0002310797886226, 2.1402987195941998e+00, 1.4500978438400001e-03 }, | ||
| 4799 | { -0.0001828635964118, 4.3188672736968003e+00, 1.3034138282630000e-02 }, | ||
| 4800 | { 0.0001512378778204, 4.9373102372298003e+00, 1.3584834812520000e-02 }, | ||
| 4801 | { -0.0001163720969778, 4.3002659861490002e+00, 2.6733443704265998e+00 }, | ||
| 4802 | { -0.0000955478069846, 1.4936612842567001e+00, 1.2938928798570001e-02 }, | ||
| 4803 | { 0.0000815246854464, 5.6222137132535002e+00, 8.9111478887838003e-01 }, | ||
| 4804 | { -0.0000801219679602, 1.2995922951532000e+00, 1.0034433456728999e+00 }, | ||
| 4805 | { -0.0000607017260182, 6.4978769669238001e-01, 5.0172167043264004e-01 } | ||
| 4806 | }; | ||
| 4807 | |||
| 4808 | static const vsop_term_t jm_Ganymede_z[] = | ||
| 4809 | { | ||
| 4810 | { 0.0014289811307319, 2.1256295942738999e+00, 1.2727413029000001e-04 }, | ||
| 4811 | { 0.0007710931226760, 5.5836330003496002e+00, 3.2064341100000001e-05 }, | ||
| 4812 | { 0.0005925911780766, 4.0899396636447998e+00, -1.2906864146660001e-02 }, | ||
| 4813 | { 0.0002045597496146, 5.2713683670371996e+00, -1.2523544076106000e-01 }, | ||
| 4814 | { 0.0001785118648258, 2.8743156721063001e-01, 8.7820792442520001e-01 }, | ||
| 4815 | { 0.0001131999784893, 1.4462127277818000e+00, 3.5515522949801999e+00 }, | ||
| 4816 | { -0.0000658778169210, 2.2702423990985001e+00, -1.7951364394536999e+00 }, | ||
| 4817 | { 0.0000497058888328, 5.9096792204858000e+00, 1.7693227129285001e+00 } | ||
| 4818 | }; | ||
| 4819 | |||
| 4820 | static const vsop_term_t jm_Ganymede_zeta[] = | ||
| 4821 | { | ||
| 4822 | { 0.0015932721570848, 3.3368862796665000e+00, -1.2491307058000000e-04 }, | ||
| 4823 | { 0.0008533093128905, 2.4133881688166001e+00, 0.0000000000000000e+00 }, | ||
| 4824 | { 0.0003513347911037, 5.9720789850126996e+00, -3.0561017709999999e-05 }, | ||
| 4825 | { -0.0001441929255483, 1.0477061764435001e+00, -5.6920632124000004e-04 } | ||
| 4826 | }; | ||
| 4827 | |||
| 4828 | static const vsop_term_t jm_Callisto_a[] = | ||
| 4829 | { | ||
| 4830 | { 0.0125879701715314, 0.0000000000000000e+00, 0.0000000000000000e+00 }, | ||
| 4831 | { 0.0000035952049470, 6.4965776007116005e-01, 5.0172168165034003e-01 }, | ||
| 4832 | { 0.0000027580210652, 1.8084235781510001e+00, 3.1750660413359002e+00 } | ||
| 4833 | }; | ||
| 4834 | |||
| 4835 | static const vsop_term_t jm_Callisto_l[] = | ||
| 4836 | { | ||
| 4837 | { 0.0005586040123824, 2.1404207189814999e+00, 1.4500979323100001e-03 }, | ||
| 4838 | { -0.0003805813868176, 2.7358844897852999e+00, 2.9729650620000000e-05 }, | ||
| 4839 | { 0.0002205152863262, 6.4979652596399995e-01, 5.0172167243580001e-01 }, | ||
| 4840 | { 0.0001877895151158, 1.8084787604004999e+00, 3.1750660413359002e+00 }, | ||
| 4841 | { 0.0000766916975242, 6.2720114319754998e+00, 1.3928364636651001e+00 }, | ||
| 4842 | { 0.0000747056855106, 1.2995916202344000e+00, 1.0034433456728999e+00 } | ||
| 4843 | }; | ||
| 4844 | |||
| 4845 | static const vsop_term_t jm_Callisto_z[] = | ||
| 4846 | { | ||
| 4847 | { 0.0073755808467977, 5.5836071576083999e+00, 3.2065099140000001e-05 }, | ||
| 4848 | { 0.0002065924169942, 5.9209831565786004e+00, 3.7648624194703001e-01 }, | ||
| 4849 | { 0.0001589869764021, 2.8744006242622999e-01, 8.7820792442520001e-01 }, | ||
| 4850 | { -0.0001561131605348, 2.1257397865089001e+00, 1.2727441285000001e-04 }, | ||
| 4851 | { 0.0001486043380971, 1.4462134301023000e+00, 3.5515522949801999e+00 }, | ||
| 4852 | { 0.0000635073108731, 5.9096803285953996e+00, 1.7693227129285001e+00 }, | ||
| 4853 | { 0.0000599351698525, 4.1125517584797997e+00, -2.7985797954588998e+00 }, | ||
| 4854 | { 0.0000540660842731, 5.5390350845569003e+00, 2.8683408228299999e-03 }, | ||
| 4855 | { -0.0000489596900866, 4.6218149483337996e+00, -6.2695712529518999e-01 } | ||
| 4856 | }; | ||
| 4857 | |||
| 4858 | static const vsop_term_t jm_Callisto_zeta[] = | ||
| 4859 | { | ||
| 4860 | { 0.0038422977898495, 2.4133922085556998e+00, 0.0000000000000000e+00 }, | ||
| 4861 | { 0.0022453891791894, 5.9721736773277003e+00, -3.0561255249999997e-05 }, | ||
| 4862 | { -0.0002604479450559, 3.3368746306408998e+00, -1.2491309972000001e-04 }, | ||
| 4863 | { 0.0000332112143230, 5.5604137742336999e+00, 2.9003768850700000e-03 } | ||
| 4864 | }; | ||
| 4865 | |||
| 4866 | static const jupiter_moon_t JupiterMoonModel[] = | ||
| 4867 | { | ||
| 4868 | { 2.8248942843381399e-07, { 1.4462132960212239e+00, 3.5515522861824000e+00 } | ||
| 4869 | , { 1, jm_Io_a } | ||
| 4870 | , { 4, jm_Io_l } | ||
| 4871 | , { 3, jm_Io_z } | ||
| 4872 | , { 2, jm_Io_zeta } }, | ||
| 4873 | |||
| 4874 | { 2.8248327439289299e-07, { -3.7352634374713622e-01, 1.7693227111234699e+00 } | ||
| 4875 | , { 2, jm_Europa_a } | ||
| 4876 | , { 8, jm_Europa_l } | ||
| 4877 | , { 7, jm_Europa_z } | ||
| 4878 | , { 4, jm_Europa_zeta } }, | ||
| 4879 | |||
| 4880 | { 2.8249818418472298e-07, { 2.8740893911433479e-01, 8.7820792358932798e-01 } | ||
| 4881 | , { 2, jm_Ganymede_a } | ||
| 4882 | , { 8, jm_Ganymede_l } | ||
| 4883 | , { 8, jm_Ganymede_z } | ||
| 4884 | , { 4, jm_Ganymede_zeta } }, | ||
| 4885 | |||
| 4886 | { 2.8249214488990899e-07, { -3.6203412913757038e-01, 3.7648623343382798e-01 } | ||
| 4887 | , { 3, jm_Callisto_a } | ||
| 4888 | , { 6, jm_Callisto_l } | ||
| 4889 | , { 9, jm_Callisto_z } | ||
| 4890 | , { 4, jm_Callisto_zeta } } | ||
| 4891 | }; | ||
| 4892 | |||
| 4893 | static astro_state_vector_t JupiterMoon_elem2pv(astro_time_t time, double mu, const double elem[6]) | ||
| 4894 | { | ||
| 4895 | /* Translation of FORTRAN subroutine ELEM2PV from: */ | ||
| 4896 | /* https://ftp.imcce.fr/pub/ephem/satel/galilean/L1/L1.2/ */ | ||
| 4897 | astro_state_vector_t state; | ||
| 4898 | double EE, DE, CE, SE, DLE, RSAM1, ASR, PHI, PSI, X1, Y1, VX1, VY1, F2, P2, Q2, PQ; | ||
| 4899 | |||
| 4900 | const double A = elem[0]; | ||
| 4901 | const double AL = elem[1]; | ||
| 4902 | const double K = elem[2]; | ||
| 4903 | const double H = elem[3]; | ||
| 4904 | const double Q = elem[4]; | ||
| 4905 | const double P = elem[5]; | ||
| 4906 | |||
| 4907 | const double AN = sqrt(mu / (A*A*A)); | ||
| 4908 | |||
| 4909 | EE = AL + K*sin(AL) - H*cos(AL); | ||
| 4910 | do | ||
| 4911 | { | ||
| 4912 | CE = cos(EE); | ||
| 4913 | SE = sin(EE); | ||
| 4914 | DE = (AL - EE + K*SE - H*CE) / (1.0 - K*CE - H*SE); | ||
| 4915 | EE += DE; | ||
| 4916 | } | ||
| 4917 | while (fabs(DE) >= 1.0e-12); | ||
| 4918 | |||
| 4919 | CE = cos(EE); | ||
| 4920 | SE = sin(EE); | ||
| 4921 | DLE = H*CE - K*SE; | ||
| 4922 | RSAM1 = -K*CE - H*SE; | ||
| 4923 | ASR = 1.0/(1.0 + RSAM1); | ||
| 4924 | PHI = sqrt(1.0 - K*K - H*H); | ||
| 4925 | PSI = 1.0/(1.0 + PHI); | ||
| 4926 | X1 = A*(CE - K - PSI*H*DLE); | ||
| 4927 | Y1 = A*(SE - H + PSI*K*DLE); | ||
| 4928 | VX1 = AN*ASR*A*(-SE - PSI*H*RSAM1); | ||
| 4929 | VY1 = AN*ASR*A*(+CE + PSI*K*RSAM1); | ||
| 4930 | F2 = 2.0*sqrt(1.0 - Q*Q - P*P); | ||
| 4931 | P2 = 1.0 - 2.0*P*P; | ||
| 4932 | Q2 = 1.0 - 2.0*Q*Q; | ||
| 4933 | PQ = 2.0*P*Q; | ||
| 4934 | |||
| 4935 | state.x = X1*P2 + Y1*PQ; | ||
| 4936 | state.y = X1*PQ + Y1*Q2; | ||
| 4937 | state.z = (Q*Y1 - X1*P)*F2; | ||
| 4938 | |||
| 4939 | state.vx = VX1*P2 + VY1*PQ; | ||
| 4940 | state.vy = VX1*PQ + VY1*Q2; | ||
| 4941 | state.vz = (Q*VY1 - VX1*P)*F2; | ||
| 4942 | |||
| 4943 | state.t = time; | ||
| 4944 | state.status = ASTRO_SUCCESS; | ||
| 4945 | return state; | ||
| 4946 | } | ||
| 4947 | |||
| 4948 | static astro_state_vector_t CalcJupiterMoon(astro_time_t time, int mindex) | ||
| 4949 | { | ||
| 4950 | /* This is a translation of FORTRAN code by Duriez, Lainey, and Vienne: */ | ||
| 4951 | /* https://ftp.imcce.fr/pub/ephem/satel/galilean/L1/L1.2/ */ | ||
| 4952 | |||
| 4953 | astro_state_vector_t state; | ||
| 4954 | int k; | ||
| 4955 | double arg; | ||
| 4956 | double elem[6]; | ||
| 4957 | const jupiter_moon_t *m = &JupiterMoonModel[mindex]; | ||
| 4958 | const double t = time.tt + 18262.5; /* t = time since 1950-01-01T00:00:00Z */ | ||
| 4959 | |||
| 4960 | /* Calculate 6 orbital elements at the given time t. */ | ||
| 4961 | |||
| 4962 | elem[0] = 0.0; | ||
| 4963 | for (k = 0; k < m->a.nterms; ++k) | ||
| 4964 | { | ||
| 4965 | arg = m->a.term[k].phase + (t * m->a.term[k].frequency); | ||
| 4966 | elem[0] += m->a.term[k].amplitude * cos(arg); | ||
| 4967 | } | ||
| 4968 | |||
| 4969 | elem[1] = m->al[0] + (t * m->al[1]); | ||
| 4970 | for (k = 0; k < m->l.nterms; ++k) | ||
| 4971 | { | ||
| 4972 | arg = m->l.term[k].phase + (t * m->l.term[k].frequency); | ||
| 4973 | elem[1] += m->l.term[k].amplitude * sin(arg); | ||
| 4974 | } | ||
| 4975 | elem[1] = fmod(elem[1], PI2); | ||
| 4976 | if (elem[1] < 0.0) | ||
| 4977 | elem[1] += PI2; | ||
| 4978 | |||
| 4979 | elem[2] = elem[3] = 0.0; | ||
| 4980 | for (k = 0; k < m->z.nterms; ++k) | ||
| 4981 | { | ||
| 4982 | arg = m->z.term[k].phase + (t * m->z.term[k].frequency); | ||
| 4983 | elem[2] += m->z.term[k].amplitude * cos(arg); | ||
| 4984 | elem[3] += m->z.term[k].amplitude * sin(arg); | ||
| 4985 | } | ||
| 4986 | |||
| 4987 | elem[4] = elem[5] = 0.0; | ||
| 4988 | for (k = 0; k < m->zeta.nterms; ++k) | ||
| 4989 | { | ||
| 4990 | arg = m->zeta.term[k].phase + (t * m->zeta.term[k].frequency); | ||
| 4991 | elem[4] += m->zeta.term[k].amplitude * cos(arg); | ||
| 4992 | elem[5] += m->zeta.term[k].amplitude * sin(arg); | ||
| 4993 | } | ||
| 4994 | |||
| 4995 | /* Convert the oribital elements into position vectors in the Jupiter equatorial system (JUP). */ | ||
| 4996 | state = JupiterMoon_elem2pv(time, m->mu, elem); | ||
| 4997 | |||
| 4998 | /* Re-orient position and velocity vectors from Jupiter-equatorial (JUP) to Earth-equatorial in J2000 (EQJ). */ | ||
| 4999 | return Astronomy_RotateState(Rotation_JUP_EQJ, state); | ||
| 5000 | } | ||
| 5001 | |||
| 5002 | |||
| 5003 | /** | ||
| 5004 | * @brief Calculates jovicentric positions and velocities of Jupiter's largest 4 moons. | ||
| 5005 | * | ||
| 5006 | * Calculates position and velocity vectors for Jupiter's moons | ||
| 5007 | * Io, Europa, Ganymede, and Callisto, at the given date and time. | ||
| 5008 | * The vectors are jovicentric (relative to the center of Jupiter). | ||
| 5009 | * Their orientation is the Earth's equatorial system at the J2000 epoch (EQJ). | ||
| 5010 | * The position components are expressed in astronomical units (AU), and the | ||
| 5011 | * velocity components are in AU/day. | ||
| 5012 | * | ||
| 5013 | * To convert to heliocentric position vectors, call #Astronomy_HelioVector | ||
| 5014 | * with `BODY_JUPITER` to get Jupiter's heliocentric position, then | ||
| 5015 | * add the jovicentric positions. | ||
| 5016 | * | ||
| 5017 | * Likewise, you can call #Astronomy_GeoVector | ||
| 5018 | * with `BODY_JUPITER` to convert to geocentric positions. | ||
| 5019 | * However, you will have to manually correct for light travel time | ||
| 5020 | * from the Jupiter system to Earth to figure out what time to pass | ||
| 5021 | * to `Astronomy_JupiterMoons` to get an accurate picture | ||
| 5022 | * of how Jupiter and its moons look from Earth. | ||
| 5023 | * | ||
| 5024 | * @param time The date and time for which to calculate the position vectors. | ||
| 5025 | * @return Position vectors of Jupiter's largest 4 moons, as described above. | ||
| 5026 | */ | ||
| 5027 | astro_jupiter_moons_t Astronomy_JupiterMoons(astro_time_t time) | ||
| 5028 | { | ||
| 5029 | astro_jupiter_moons_t jm; | ||
| 5030 | |||
| 5031 | jm.io = CalcJupiterMoon(time, 0); | ||
| 5032 | jm.europa = CalcJupiterMoon(time, 1); | ||
| 5033 | jm.ganymede = CalcJupiterMoon(time, 2); | ||
| 5034 | jm.callisto = CalcJupiterMoon(time, 3); | ||
| 5035 | |||
| 5036 | return jm; | ||
| 5037 | } | ||
| 5038 | |||
| 5039 | /*---------------------- end Jupiter moons ----------------------*/ | ||
| 5040 | |||
| 5041 | |||
| 5042 | /** | ||
| 5043 | * @brief Calculates heliocentric Cartesian coordinates of a body in the J2000 equatorial system. | ||
| 5044 | * | ||
| 5045 | * This function calculates the position of the given celestial body as a vector, | ||
| 5046 | * using the center of the Sun as the origin. The result is expressed as a Cartesian | ||
| 5047 | * vector in the J2000 equatorial system: the coordinates are based on the mean equator | ||
| 5048 | * of the Earth at noon UTC on 1 January 2000. | ||
| 5049 | * | ||
| 5050 | * The position is not corrected for light travel time or aberration. | ||
| 5051 | * This is different from the behavior of #Astronomy_GeoVector. | ||
| 5052 | * | ||
| 5053 | * If given an invalid value for `body`, this function will fail. The caller should always check | ||
| 5054 | * the `status` field inside the returned #astro_vector_t for `ASTRO_SUCCESS` (success) | ||
| 5055 | * or any other value (failure) before trusting the resulting vector. | ||
| 5056 | * | ||
| 5057 | * @param body | ||
| 5058 | * A body for which to calculate a heliocentric position: the Sun, Moon, any of the planets, | ||
| 5059 | * the Solar System Barycenter (SSB), or the Earth Moon Barycenter (EMB). | ||
| 5060 | * Can also be a star defined by #Astronomy_DefineStar. | ||
| 5061 | * @param time The date and time for which to calculate the position. | ||
| 5062 | * @return A heliocentric position vector of the center of the given body. | ||
| 5063 | */ | ||
| 5064 | astro_vector_t Astronomy_HelioVector(astro_body_t body, astro_time_t time) | ||
| 5065 | { | ||
| 5066 | astro_vector_t vector, earth; | ||
| 5067 | body_state_t bstate; | ||
| 5068 | stardef_t *star; | ||
| 5069 | |||
| 5070 | star = UserDefinedStar(body); | ||
| 5071 | if (star != NULL) | ||
| 5072 | { | ||
| 5073 | astro_spherical_t sphere; | ||
| 5074 | sphere.lat = star->dec; | ||
| 5075 | sphere.lon = 15.0 * star->ra; | ||
| 5076 | sphere.dist = star->dist; | ||
| 5077 | sphere.status = ASTRO_SUCCESS; | ||
| 5078 | return Astronomy_VectorFromSphere(sphere, time); | ||
| 5079 | } | ||
| 5080 | |||
| 5081 | switch (body) | ||
| 5082 | { | ||
| 5083 | case BODY_SUN: | ||
| 5084 | vector.status = ASTRO_SUCCESS; | ||
| 5085 | vector.x = 0.0; | ||
| 5086 | vector.y = 0.0; | ||
| 5087 | vector.z = 0.0; | ||
| 5088 | vector.t = time; | ||
| 5089 | return vector; | ||
| 5090 | |||
| 5091 | case BODY_MERCURY: | ||
| 5092 | case BODY_VENUS: | ||
| 5093 | case BODY_EARTH: | ||
| 5094 | case BODY_MARS: | ||
| 5095 | case BODY_JUPITER: | ||
| 5096 | case BODY_SATURN: | ||
| 5097 | case BODY_URANUS: | ||
| 5098 | case BODY_NEPTUNE: | ||
| 5099 | return CalcVsop(&vsop[body], time); | ||
| 5100 | |||
| 5101 | case BODY_PLUTO: | ||
| 5102 | vector.t = time; | ||
| 5103 | vector.status = CalcPluto(&bstate, time, 1); | ||
| 5104 | if (vector.status != ASTRO_SUCCESS) | ||
| 5105 | { | ||
| 5106 | vector.x = vector.y = vector.z = NAN; | ||
| 5107 | } | ||
| 5108 | else | ||
| 5109 | { | ||
| 5110 | vector.x = bstate.r.x; | ||
| 5111 | vector.y = bstate.r.y; | ||
| 5112 | vector.z = bstate.r.z; | ||
| 5113 | } | ||
| 5114 | return vector; | ||
| 5115 | |||
| 5116 | case BODY_MOON: | ||
| 5117 | vector = Astronomy_GeoMoon(time); | ||
| 5118 | earth = CalcEarth(time); | ||
| 5119 | vector.x += earth.x; | ||
| 5120 | vector.y += earth.y; | ||
| 5121 | vector.z += earth.z; | ||
| 5122 | return vector; | ||
| 5123 | |||
| 5124 | case BODY_EMB: | ||
| 5125 | vector = Astronomy_GeoMoon(time); | ||
| 5126 | earth = CalcEarth(time); | ||
| 5127 | vector.x = earth.x + (vector.x / (1.0 + EARTH_MOON_MASS_RATIO)); | ||
| 5128 | vector.y = earth.y + (vector.y / (1.0 + EARTH_MOON_MASS_RATIO)); | ||
| 5129 | vector.z = earth.z + (vector.z / (1.0 + EARTH_MOON_MASS_RATIO)); | ||
| 5130 | return vector; | ||
| 5131 | |||
| 5132 | case BODY_SSB: | ||
| 5133 | return CalcSolarSystemBarycenter(time); | ||
| 5134 | |||
| 5135 | default: | ||
| 5136 | return VecError(ASTRO_INVALID_BODY, time); | ||
| 5137 | } | ||
| 5138 | } | ||
| 5139 | |||
| 5140 | /** | ||
| 5141 | * @brief Calculates the distance from a body to the Sun at a given time. | ||
| 5142 | * | ||
| 5143 | * Given a date and time, this function calculates the distance between | ||
| 5144 | * the center of `body` and the center of the Sun. | ||
| 5145 | * For the planets Mercury through Neptune, this function is significantly | ||
| 5146 | * more efficient than calling #Astronomy_HelioVector followed by #Astronomy_VectorLength. | ||
| 5147 | * | ||
| 5148 | * @param body | ||
| 5149 | * A body for which to calculate a heliocentric distance: | ||
| 5150 | * the Sun, Moon, any of the planets, or a user-defined star. | ||
| 5151 | * | ||
| 5152 | * @param time | ||
| 5153 | * The date and time for which to calculate the heliocentric distance. | ||
| 5154 | * | ||
| 5155 | * @return | ||
| 5156 | * If successful, an #astro_func_result_t structure whose `status` is `ASTRO_SUCCESS` | ||
| 5157 | * and whose `value` holds the heliocentric distance in AU. | ||
| 5158 | * Otherwise, `status` reports an error condition. | ||
| 5159 | */ | ||
| 5160 | astro_func_result_t Astronomy_HelioDistance(astro_body_t body, astro_time_t time) | ||
| 5161 | { | ||
| 5162 | astro_vector_t vector; | ||
| 5163 | astro_func_result_t result; | ||
| 5164 | stardef_t *star; | ||
| 5165 | |||
| 5166 | star = UserDefinedStar(body); | ||
| 5167 | if (star != NULL) | ||
| 5168 | { | ||
| 5169 | result.status = ASTRO_SUCCESS; | ||
| 5170 | result.value = star->dist; | ||
| 5171 | return result; | ||
| 5172 | } | ||
| 5173 | |||
| 5174 | switch (body) | ||
| 5175 | { | ||
| 5176 | case BODY_SUN: | ||
| 5177 | result.status = ASTRO_SUCCESS; | ||
| 5178 | result.value = 0.0; | ||
| 5179 | return result; | ||
| 5180 | |||
| 5181 | case BODY_MERCURY: | ||
| 5182 | case BODY_VENUS: | ||
| 5183 | case BODY_EARTH: | ||
| 5184 | case BODY_MARS: | ||
| 5185 | case BODY_JUPITER: | ||
| 5186 | case BODY_SATURN: | ||
| 5187 | case BODY_URANUS: | ||
| 5188 | case BODY_NEPTUNE: | ||
| 5189 | result.status = ASTRO_SUCCESS; | ||
| 5190 | result.value = VsopHelioDistance(&vsop[body], time); | ||
| 5191 | return result; | ||
| 5192 | |||
| 5193 | default: | ||
| 5194 | /* For non-VSOP objects, fall back to taking the length of the heliocentric vector. */ | ||
| 5195 | vector = Astronomy_HelioVector(body, time); | ||
| 5196 | if (vector.status != ASTRO_SUCCESS) | ||
| 5197 | return FuncError(vector.status); | ||
| 5198 | result.status = ASTRO_SUCCESS; | ||
| 5199 | result.value = Astronomy_VectorLength(vector); | ||
| 5200 | return result; | ||
| 5201 | } | ||
| 5202 | } | ||
| 5203 | |||
| 5204 | |||
| 5205 | /** | ||
| 5206 | * @brief Solve for light travel time of a vector function. | ||
| 5207 | * | ||
| 5208 | * When observing a distant object, for example Jupiter as seen from Earth, | ||
| 5209 | * the amount of time it takes for light to travel from the object to the | ||
| 5210 | * observer can significantly affect the object's apparent position. | ||
| 5211 | * This function is a generic solver that figures out how long in the | ||
| 5212 | * past light must have left the observed object to reach the observer | ||
| 5213 | * at the specified observation time. It uses a context/function pair | ||
| 5214 | * as a generic interface that expresses an arbitrary position vector | ||
| 5215 | * as a function of time. | ||
| 5216 | * | ||
| 5217 | * This function repeatedly calls `func`, passing `context` and a series of time | ||
| 5218 | * estimates in the past. Then `func` must return a relative position vector between | ||
| 5219 | * the observer and the target. `Astronomy_CorrectLightTravel` keeps calling | ||
| 5220 | * `func` with more and more refined estimates of the time light must have | ||
| 5221 | * left the target to arrive at the observer. | ||
| 5222 | * | ||
| 5223 | * For common use cases, it is simpler to use #Astronomy_BackdatePosition | ||
| 5224 | * for calculating the light travel time correction of one body observing another body. | ||
| 5225 | * | ||
| 5226 | * For geocentric calculations, #Astronomy_GeoVector also backdates the returned | ||
| 5227 | * position vector for light travel time, only it returns the observation time in | ||
| 5228 | * the returned vector's `t` field rather than the backdated time. | ||
| 5229 | * | ||
| 5230 | * @param context Holds any parameters needed by `func`. | ||
| 5231 | * @param func Pointer to a function that returns a relative position vector as a function of time. | ||
| 5232 | * @param time The observation time for which to solve for light travel delay. | ||
| 5233 | * @return | ||
| 5234 | * The position vector returned by `func` at the solved backdated time. | ||
| 5235 | * On success, the vector will hold `ASTRO_SUCCESS` in its `status` field, | ||
| 5236 | * the backdated time in its `t` field, along with the apparent relative position. | ||
| 5237 | * If an error occurs, `status` will hold an error code and the remaining fields | ||
| 5238 | * should be ignored. | ||
| 5239 | */ | ||
| 5240 | astro_vector_t Astronomy_CorrectLightTravel( | ||
| 5241 | void *context, | ||
| 5242 | astro_position_func_t func, | ||
| 5243 | astro_time_t time) | ||
| 5244 | { | ||
| 5245 | int iter; | ||
| 5246 | astro_time_t ltime, ltime2; | ||
| 5247 | astro_vector_t pos; | ||
| 5248 | double distance, dt; | ||
| 5249 | |||
| 5250 | ltime = time; | ||
| 5251 | for (iter = 0; iter < 10; ++iter) | ||
| 5252 | { | ||
| 5253 | pos = func(context, ltime); | ||
| 5254 | if (pos.status != ASTRO_SUCCESS) | ||
| 5255 | return pos; | ||
| 5256 | |||
| 5257 | distance = Astronomy_VectorLength(pos); | ||
| 5258 | |||
| 5259 | /* | ||
| 5260 | This solver does not support more than one light-day of distance, | ||
| 5261 | because that would cause convergence problems and inaccurate | ||
| 5262 | values for stellar aberration angles. | ||
| 5263 | */ | ||
| 5264 | if (distance > C_AUDAY) | ||
| 5265 | return VecError(ASTRO_INVALID_PARAMETER, time); | ||
| 5266 | |||
| 5267 | ltime2 = Astronomy_AddDays(time, -distance/C_AUDAY); | ||
| 5268 | dt = fabs(ltime2.tt - ltime.tt); | ||
| 5269 | if (dt < 1.0e-9) /* 86.4 microseconds */ | ||
| 5270 | return pos; | ||
| 5271 | |||
| 5272 | ltime = ltime2; | ||
| 5273 | } | ||
| 5274 | return VecError(ASTRO_NO_CONVERGE, time); /* light travel time solver did not converge */ | ||
| 5275 | } | ||
| 5276 | |||
| 5277 | |||
| 5278 | /** @cond DOXYGEN_SKIP */ | ||
| 5279 | typedef struct | ||
| 5280 | { | ||
| 5281 | astro_body_t observerBody; | ||
| 5282 | astro_body_t targetBody; | ||
| 5283 | astro_aberration_t aberration; | ||
| 5284 | astro_vector_t observerPos; /* used only when aberration == NO_ABERRATION */ | ||
| 5285 | } | ||
| 5286 | backdate_context_t; | ||
| 5287 | /** @endcond */ | ||
| 5288 | |||
| 5289 | |||
| 5290 | static astro_vector_t BodyPosition(void *context, astro_time_t time) | ||
| 5291 | { | ||
| 5292 | const backdate_context_t *b = (const backdate_context_t *)context; | ||
| 5293 | astro_vector_t observerPos, pos; | ||
| 5294 | |||
| 5295 | if (b->aberration == NO_ABERRATION) | ||
| 5296 | { | ||
| 5297 | /* No aberration, so use the pre-calculated initial position of the observer body. */ | ||
| 5298 | observerPos = b->observerPos; | ||
| 5299 | } | ||
| 5300 | else | ||
| 5301 | { | ||
| 5302 | /* | ||
| 5303 | The following discussion is worded with the observer body being the Earth, | ||
| 5304 | which is often the case. However, the same reasoning applies to any observer body | ||
| 5305 | without loss of generality. | ||
| 5306 | |||
| 5307 | To include aberration, make a good first-order approximation | ||
| 5308 | by backdating the Earth's position also. | ||
| 5309 | This is confusing, but it works for objects within the Solar System | ||
| 5310 | because the distance the Earth moves in that small amount of light | ||
| 5311 | travel time (a few minutes to a few hours) is well approximated | ||
| 5312 | by a line segment that substends the angle seen from the remote | ||
| 5313 | body viewing Earth. That angle is pretty close to the aberration | ||
| 5314 | angle of the moving Earth viewing the remote body. | ||
| 5315 | In other words, both of the following approximate the aberration angle: | ||
| 5316 | (transverse distance Earth moves) / (distance to body) | ||
| 5317 | (transverse speed of Earth) / (speed of light). | ||
| 5318 | */ | ||
| 5319 | observerPos = Astronomy_HelioVector(b->observerBody, time); | ||
| 5320 | } | ||
| 5321 | |||
| 5322 | if (observerPos.status != ASTRO_SUCCESS) | ||
| 5323 | return observerPos; | ||
| 5324 | |||
| 5325 | pos = Astronomy_HelioVector(b->targetBody, time); | ||
| 5326 | if (pos.status == ASTRO_SUCCESS) | ||
| 5327 | { | ||
| 5328 | /* Convert heliocentric body position to observer-centric position. */ | ||
| 5329 | pos.x -= observerPos.x; | ||
| 5330 | pos.y -= observerPos.y; | ||
| 5331 | pos.z -= observerPos.z; | ||
| 5332 | } | ||
| 5333 | return pos; | ||
| 5334 | } | ||
| 5335 | |||
| 5336 | |||
| 5337 | /** | ||
| 5338 | * @brief Solve for light travel time correction of apparent position. | ||
| 5339 | * | ||
| 5340 | * When observing a distant object, for example Jupiter as seen from Earth, | ||
| 5341 | * the amount of time it takes for light to travel from the object to the | ||
| 5342 | * observer can significantly affect the object's apparent position. | ||
| 5343 | * | ||
| 5344 | * This function solves the light travel time correction for the apparent | ||
| 5345 | * relative position vector of a target body as seen by an observer body | ||
| 5346 | * at a given observation time. | ||
| 5347 | * | ||
| 5348 | * For geocentric calculations, #Astronomy_GeoVector also includes light | ||
| 5349 | * travel time correction, but the time `t` embedded in its returned vector | ||
| 5350 | * refers to the observation time, not the backdated time that light left | ||
| 5351 | * the observed body. Thus `Astronomy_BackdatePosition` provides direct | ||
| 5352 | * access to the light departure time for callers that need it. | ||
| 5353 | * | ||
| 5354 | * For a more generalized light travel correction solver, see #Astronomy_CorrectLightTravel. | ||
| 5355 | * | ||
| 5356 | * @param time The time of observation. | ||
| 5357 | * @param observerBody The body to be used as the observation location. | ||
| 5358 | * @param targetBody The body to be observed. | ||
| 5359 | * @param aberration `ABERRATION` to correct for aberration, or `NO_ABERRATION` to leave uncorrected. | ||
| 5360 | * | ||
| 5361 | * @return | ||
| 5362 | * On success, the position vector at the solved backdated time. | ||
| 5363 | * The returned vector will hold `ASTRO_SUCCESS` in its `status` field, | ||
| 5364 | * the backdated time in its `t` field, along with the apparent relative position. | ||
| 5365 | * If an error occurs, `status` will hold an error code and the remaining fields should be ignored. | ||
| 5366 | */ | ||
| 5367 | astro_vector_t Astronomy_BackdatePosition( | ||
| 5368 | astro_time_t time, | ||
| 5369 | astro_body_t observerBody, | ||
| 5370 | astro_body_t targetBody, | ||
| 5371 | astro_aberration_t aberration) | ||
| 5372 | { | ||
| 5373 | if (UserDefinedStar(targetBody)) | ||
| 5374 | { | ||
| 5375 | /* | ||
| 5376 | This is a user-defined star, which must be treated as a special case. | ||
| 5377 | First, we assume its heliocentric position does not change with time. | ||
| 5378 | Second, we assume its heliocentric position has already been corrected | ||
| 5379 | for light-travel time, its coordinates given as it appears on Earth at the present. | ||
| 5380 | Therefore, no backdating is applied. | ||
| 5381 | */ | ||
| 5382 | astro_vector_t ovec, tvec, vec; | ||
| 5383 | double rx, ry, rz, s; | ||
| 5384 | astro_state_vector_t ostate; | ||
| 5385 | |||
| 5386 | tvec = Astronomy_HelioVector(targetBody, time); | ||
| 5387 | if (tvec.status != ASTRO_SUCCESS) | ||
| 5388 | return tvec; | ||
| 5389 | |||
| 5390 | switch (aberration) | ||
| 5391 | { | ||
| 5392 | case NO_ABERRATION: | ||
| 5393 | /* Return the star's position as seen from the observer. */ | ||
| 5394 | ovec = Astronomy_HelioVector(observerBody, time); | ||
| 5395 | if (ovec.status != ASTRO_SUCCESS) | ||
| 5396 | return ovec; | ||
| 5397 | vec.x = tvec.x - ovec.x; | ||
| 5398 | vec.y = tvec.y - ovec.y; | ||
| 5399 | vec.z = tvec.z - ovec.z; | ||
| 5400 | vec.t = time; | ||
| 5401 | vec.status = ASTRO_SUCCESS; | ||
| 5402 | return vec; | ||
| 5403 | |||
| 5404 | case ABERRATION: | ||
| 5405 | /* | ||
| 5406 | (Observer velocity) - (light vector) = (Aberration-corrected direction to target body). | ||
| 5407 | Note that this is an approximation, because technically the light vector should | ||
| 5408 | be measured in barycentric coordinates, not heliocentric. The error is very small. | ||
| 5409 | */ | ||
| 5410 | ostate = Astronomy_HelioState(observerBody, time); | ||
| 5411 | if (ostate.status != ASTRO_SUCCESS) | ||
| 5412 | return VecError(ostate.status, time); | ||
| 5413 | |||
| 5414 | rx = tvec.x - ostate.x; | ||
| 5415 | ry = tvec.y - ostate.y; | ||
| 5416 | rz = tvec.z - ostate.z; | ||
| 5417 | s = C_AUDAY / sqrt(rx*rx + ry*ry + rz*rz); | ||
| 5418 | |||
| 5419 | vec.x = rx + ostate.vx/s; | ||
| 5420 | vec.y = ry + ostate.vy/s; | ||
| 5421 | vec.z = rz + ostate.vz/s; | ||
| 5422 | vec.t = time; | ||
| 5423 | vec.status = ASTRO_SUCCESS; | ||
| 5424 | return vec; | ||
| 5425 | |||
| 5426 | default: | ||
| 5427 | return VecError(ASTRO_INVALID_PARAMETER, time); | ||
| 5428 | } | ||
| 5429 | } | ||
| 5430 | else | ||
| 5431 | { | ||
| 5432 | backdate_context_t context; | ||
| 5433 | |||
| 5434 | context.observerBody = observerBody; | ||
| 5435 | context.targetBody = targetBody; | ||
| 5436 | context.aberration = aberration; | ||
| 5437 | switch (aberration) | ||
| 5438 | { | ||
| 5439 | case NO_ABERRATION: | ||
| 5440 | /* Without aberration, we need the observer body position at the observation time only. */ | ||
| 5441 | /* For efficiency, calculate it once and hold onto it, so `BodyPosition` can keep using it. */ | ||
| 5442 | context.observerPos = Astronomy_HelioVector(observerBody, time); | ||
| 5443 | break; | ||
| 5444 | |||
| 5445 | case ABERRATION: | ||
| 5446 | /* With aberration, `BackdatePosition` will calculate the observer body state at different times. */ | ||
| 5447 | /* Therefore, do not waste time calculating it at the observation time. */ | ||
| 5448 | /* Initialize the memory with an explicitly invalid value. */ | ||
| 5449 | context.observerPos = VecError(ASTRO_NOT_INITIALIZED, time); | ||
| 5450 | break; | ||
| 5451 | |||
| 5452 | default: | ||
| 5453 | return VecError(ASTRO_INVALID_PARAMETER, time); | ||
| 5454 | } | ||
| 5455 | |||
| 5456 | return Astronomy_CorrectLightTravel(&context, BodyPosition, time); | ||
| 5457 | } | ||
| 5458 | } | ||
| 5459 | |||
| 5460 | |||
| 5461 | /** | ||
| 5462 | * @brief Calculates geocentric Cartesian coordinates of a body in the J2000 equatorial system. | ||
| 5463 | * | ||
| 5464 | * This function calculates the position of the given celestial body as a vector, | ||
| 5465 | * using the center of the Earth as the origin. The result is expressed as a Cartesian | ||
| 5466 | * vector in the J2000 equatorial system: the coordinates are based on the mean equator | ||
| 5467 | * of the Earth at noon UTC on 1 January 2000. | ||
| 5468 | * | ||
| 5469 | * If given an invalid value for `body`, this function will fail. The caller should always check | ||
| 5470 | * the `status` field inside the returned #astro_vector_t for `ASTRO_SUCCESS` (success) | ||
| 5471 | * or any other value (failure) before trusting the resulting vector. | ||
| 5472 | * | ||
| 5473 | * Unlike #Astronomy_HelioVector, this function corrects for light travel time. | ||
| 5474 | * This means the position of the body is "back-dated" by the amount of time it takes | ||
| 5475 | * light to travel from that body to an observer on the Earth. | ||
| 5476 | * | ||
| 5477 | * Also, the position can optionally be corrected for | ||
| 5478 | * [aberration](https://en.wikipedia.org/wiki/Aberration_of_light), an effect | ||
| 5479 | * causing the apparent direction of the body to be shifted due to transverse | ||
| 5480 | * movement of the Earth with respect to the rays of light coming from that body. | ||
| 5481 | * | ||
| 5482 | * @param body | ||
| 5483 | * A body for which to calculate a heliocentric position: the Sun, Moon, or any of the planets. | ||
| 5484 | * Can also be a star defined by #Astronomy_DefineStar. | ||
| 5485 | * @param time The date and time for which to calculate the position. | ||
| 5486 | * @param aberration `ABERRATION` to correct for aberration, or `NO_ABERRATION` to leave uncorrected. | ||
| 5487 | * @return A geocentric position vector of the center of the given body. | ||
| 5488 | */ | ||
| 5489 | astro_vector_t Astronomy_GeoVector(astro_body_t body, astro_time_t time, astro_aberration_t aberration) | ||
| 5490 | { | ||
| 5491 | astro_vector_t vector; | ||
| 5492 | |||
| 5493 | switch (body) | ||
| 5494 | { | ||
| 5495 | case BODY_EARTH: | ||
| 5496 | /* The Earth's geocentric coordinates are always (0,0,0). */ | ||
| 5497 | vector.status = ASTRO_SUCCESS; | ||
| 5498 | vector.x = 0.0; | ||
| 5499 | vector.y = 0.0; | ||
| 5500 | vector.z = 0.0; | ||
| 5501 | break; | ||
| 5502 | |||
| 5503 | case BODY_MOON: | ||
| 5504 | /* The moon is so close, aberration and light travel time don't matter. */ | ||
| 5505 | vector = Astronomy_GeoMoon(time); | ||
| 5506 | break; | ||
| 5507 | |||
| 5508 | default: | ||
| 5509 | /* For all other bodies, apply light travel time correction. */ | ||
| 5510 | vector = Astronomy_BackdatePosition(time, BODY_EARTH, body, aberration); | ||
| 5511 | break; | ||
| 5512 | } | ||
| 5513 | |||
| 5514 | vector.t = time; /* tricky: return the observation time, not the backdated time */ | ||
| 5515 | return vector; | ||
| 5516 | } | ||
| 5517 | |||
| 5518 | |||
| 5519 | /** | ||
| 5520 | * @brief Calculates barycentric position and velocity vectors for the given body. | ||
| 5521 | * | ||
| 5522 | * Given a body and a time, calculates the barycentric position and velocity | ||
| 5523 | * vectors for the center of that body at that time. | ||
| 5524 | * The vectors are expressed in J2000 mean equator coordinates (EQJ). | ||
| 5525 | * | ||
| 5526 | * @param body | ||
| 5527 | * The celestial body whose barycentric state vector is to be calculated. | ||
| 5528 | * Supported values are `BODY_SUN`, `BODY_MOON`, `BODY_EMB`, `BODY_SSB`, and all planets: | ||
| 5529 | * `BODY_MERCURY`, `BODY_VENUS`, `BODY_EARTH`, `BODY_MARS`, `BODY_JUPITER`, | ||
| 5530 | * `BODY_SATURN`, `BODY_URANUS`, `BODY_NEPTUNE`, `BODY_PLUTO`. | ||
| 5531 | * @param time | ||
| 5532 | * The date and time for which to calculate position and velocity. | ||
| 5533 | * @return | ||
| 5534 | * A structure that contains barycentric position and velocity vectors. | ||
| 5535 | */ | ||
| 5536 | astro_state_vector_t Astronomy_BaryState(astro_body_t body, astro_time_t time) | ||
| 5537 | { | ||
| 5538 | astro_state_vector_t state; | ||
| 5539 | major_bodies_t bary; | ||
| 5540 | body_state_t planet, earth; | ||
| 5541 | |||
| 5542 | if (body == BODY_SSB) | ||
| 5543 | { | ||
| 5544 | /* Trivial case: the solar system barycenter itself. */ | ||
| 5545 | state.status = ASTRO_SUCCESS; | ||
| 5546 | state.x = state.y = state.z = 0.0; | ||
| 5547 | state.vx = state.vy = state.vz = 0.0; | ||
| 5548 | state.t = time; | ||
| 5549 | return state; | ||
| 5550 | } | ||
| 5551 | |||
| 5552 | if (body == BODY_PLUTO) | ||
| 5553 | { | ||
| 5554 | astro_status_t status = CalcPluto(&planet, time, 0); | ||
| 5555 | if (status != ASTRO_SUCCESS) | ||
| 5556 | return StateVecError(status, time); | ||
| 5557 | return ExportState(planet, time); | ||
| 5558 | } | ||
| 5559 | |||
| 5560 | MajorBodyBary(&bary, time.tt); | ||
| 5561 | |||
| 5562 | switch (body) | ||
| 5563 | { | ||
| 5564 | /* If the caller is asking for one of the major bodies, we can immediately return the answer. */ | ||
| 5565 | case BODY_SUN: return ExportState(bary.Sun, time); | ||
| 5566 | case BODY_JUPITER: return ExportState(bary.Jupiter, time); | ||
| 5567 | case BODY_SATURN: return ExportState(bary.Saturn, time); | ||
| 5568 | case BODY_URANUS: return ExportState(bary.Uranus, time); | ||
| 5569 | case BODY_NEPTUNE: return ExportState(bary.Neptune, time); | ||
| 5570 | |||
| 5571 | /* Handle the remaining VSOP bodies: Mercury, Venus, Earth, Mars. */ | ||
| 5572 | case BODY_MERCURY: | ||
| 5573 | case BODY_VENUS: | ||
| 5574 | case BODY_EARTH: | ||
| 5575 | case BODY_MARS: | ||
| 5576 | planet = CalcVsopPosVel(&vsop[body], time.tt); | ||
| 5577 | /* BarySun + HelioBody = BaryBody */ | ||
| 5578 | state.x = bary.Sun.r.x + planet.r.x; | ||
| 5579 | state.y = bary.Sun.r.y + planet.r.y; | ||
| 5580 | state.z = bary.Sun.r.z + planet.r.z; | ||
| 5581 | state.vx = bary.Sun.v.x + planet.v.x; | ||
| 5582 | state.vy = bary.Sun.v.y + planet.v.y; | ||
| 5583 | state.vz = bary.Sun.v.z + planet.v.z; | ||
| 5584 | state.t = time; | ||
| 5585 | state.status = ASTRO_SUCCESS; | ||
| 5586 | return state; | ||
| 5587 | |||
| 5588 | case BODY_MOON: | ||
| 5589 | case BODY_EMB: | ||
| 5590 | earth = CalcVsopPosVel(&vsop[BODY_EARTH], time.tt); | ||
| 5591 | if (body == BODY_MOON) | ||
| 5592 | state = Astronomy_GeoMoonState(time); | ||
| 5593 | else | ||
| 5594 | state = Astronomy_GeoEmbState(time); | ||
| 5595 | state.x += bary.Sun.r.x + earth.r.x; | ||
| 5596 | state.y += bary.Sun.r.y + earth.r.y; | ||
| 5597 | state.z += bary.Sun.r.z + earth.r.z; | ||
| 5598 | state.vx += bary.Sun.v.x + earth.v.x; | ||
| 5599 | state.vy += bary.Sun.v.y + earth.v.y; | ||
| 5600 | state.vz += bary.Sun.v.z + earth.v.z; | ||
| 5601 | return state; | ||
| 5602 | |||
| 5603 | default: | ||
| 5604 | return StateVecError(ASTRO_INVALID_BODY, time); | ||
| 5605 | } | ||
| 5606 | } | ||
| 5607 | |||
| 5608 | |||
| 5609 | /** | ||
| 5610 | * @brief Calculates heliocentric position and velocity vectors for the given body. | ||
| 5611 | * | ||
| 5612 | * Given a body and a time, calculates the position and velocity | ||
| 5613 | * vectors for the center of that body at that time, relative to the center of the Sun. | ||
| 5614 | * The vectors are expressed in J2000 mean equator coordinates (EQJ). | ||
| 5615 | * If you need the position vector only, it is more efficient to call #Astronomy_HelioVector. | ||
| 5616 | * The Sun's center is a non-inertial frame of reference. In other words, the Sun | ||
| 5617 | * experiences acceleration due to gravitational forces, mostly from the larger | ||
| 5618 | * planets (Jupiter, Saturn, Uranus, and Neptune). If you want to calculate momentum, | ||
| 5619 | * kinetic energy, or other quantities that require a non-accelerating frame | ||
| 5620 | * of reference, consider using #Astronomy_BaryState instead. | ||
| 5621 | * | ||
| 5622 | * @param body | ||
| 5623 | * The celestial body whose heliocentric state vector is to be calculated. | ||
| 5624 | * Supported values are `BODY_SUN`, `BODY_MOON`, `BODY_EMB`, `BODY_SSB`, and all planets: | ||
| 5625 | * `BODY_MERCURY`, `BODY_VENUS`, `BODY_EARTH`, `BODY_MARS`, `BODY_JUPITER`, | ||
| 5626 | * `BODY_SATURN`, `BODY_URANUS`, `BODY_NEPTUNE`, `BODY_PLUTO`. | ||
| 5627 | * Also allowed to be a user-defined star created by #Astronomy_DefineStar. | ||
| 5628 | * @param time | ||
| 5629 | * The date and time for which to calculate position and velocity. | ||
| 5630 | * @return | ||
| 5631 | * A structure that contains heliocentric position and velocity vectors. | ||
| 5632 | */ | ||
| 5633 | astro_state_vector_t Astronomy_HelioState(astro_body_t body, astro_time_t time) | ||
| 5634 | { | ||
| 5635 | astro_status_t status; | ||
| 5636 | astro_state_vector_t state; | ||
| 5637 | major_bodies_t bary; | ||
| 5638 | body_state_t planet, earth; | ||
| 5639 | |||
| 5640 | if (UserDefinedStar(body)) | ||
| 5641 | { | ||
| 5642 | astro_vector_t vec = Astronomy_HelioVector(body, time); | ||
| 5643 | state.x = vec.x; | ||
| 5644 | state.y = vec.y; | ||
| 5645 | state.z = vec.z; | ||
| 5646 | state.vx = state.vy = state.vz = 0.0; | ||
| 5647 | state.t = time; | ||
| 5648 | state.status = vec.status; | ||
| 5649 | return state; | ||
| 5650 | } | ||
| 5651 | |||
| 5652 | switch (body) | ||
| 5653 | { | ||
| 5654 | case BODY_SUN: | ||
| 5655 | /* Trivial case: the Sun is the origin of the heliocentric frame. */ | ||
| 5656 | state.status = ASTRO_SUCCESS; | ||
| 5657 | state.x = state.y = state.z = 0.0; | ||
| 5658 | state.vx = state.vy = state.vz = 0.0; | ||
| 5659 | state.t = time; | ||
| 5660 | return state; | ||
| 5661 | |||
| 5662 | case BODY_SSB: | ||
| 5663 | /* Calculate the barycentric Sun. Then the negative of that is the heliocentric SSB. */ | ||
| 5664 | MajorBodyBary(&bary, time.tt); | ||
| 5665 | state.x = -bary.Sun.r.x; | ||
| 5666 | state.y = -bary.Sun.r.y; | ||
| 5667 | state.z = -bary.Sun.r.z; | ||
| 5668 | state.vx = -bary.Sun.v.x; | ||
| 5669 | state.vy = -bary.Sun.v.y; | ||
| 5670 | state.vz = -bary.Sun.v.z; | ||
| 5671 | state.t = time; | ||
| 5672 | state.status = ASTRO_SUCCESS; | ||
| 5673 | return state; | ||
| 5674 | |||
| 5675 | case BODY_MERCURY: | ||
| 5676 | case BODY_VENUS: | ||
| 5677 | case BODY_EARTH: | ||
| 5678 | case BODY_MARS: | ||
| 5679 | case BODY_JUPITER: | ||
| 5680 | case BODY_SATURN: | ||
| 5681 | case BODY_URANUS: | ||
| 5682 | case BODY_NEPTUNE: | ||
| 5683 | /* Planets included in the VSOP87 model. */ | ||
| 5684 | planet = CalcVsopPosVel(&vsop[body], time.tt); | ||
| 5685 | return ExportState(planet, time); | ||
| 5686 | |||
| 5687 | case BODY_PLUTO: | ||
| 5688 | status = CalcPluto(&planet, time, 1); | ||
| 5689 | if (status != ASTRO_SUCCESS) | ||
| 5690 | return StateVecError(status, time); | ||
| 5691 | return ExportState(planet, time); | ||
| 5692 | |||
| 5693 | case BODY_MOON: | ||
| 5694 | case BODY_EMB: | ||
| 5695 | earth = CalcVsopPosVel(&vsop[BODY_EARTH], time.tt); | ||
| 5696 | if (body == BODY_MOON) | ||
| 5697 | state = Astronomy_GeoMoonState(time); | ||
| 5698 | else | ||
| 5699 | state = Astronomy_GeoEmbState(time); | ||
| 5700 | state.x += earth.r.x; | ||
| 5701 | state.y += earth.r.y; | ||
| 5702 | state.z += earth.r.z; | ||
| 5703 | state.vx += earth.v.x; | ||
| 5704 | state.vy += earth.v.y; | ||
| 5705 | state.vz += earth.v.z; | ||
| 5706 | return state; | ||
| 5707 | |||
| 5708 | default: | ||
| 5709 | return StateVecError(ASTRO_INVALID_BODY, time); | ||
| 5710 | } | ||
| 5711 | } | ||
| 5712 | |||
| 5713 | |||
| 5714 | /** | ||
| 5715 | * @brief Returns the product of mass and universal gravitational constant of a Solar System body. | ||
| 5716 | * | ||
| 5717 | * For problems involving the gravitational interactions of Solar System bodies, | ||
| 5718 | * it is helpful to know the product GM, where G = the universal gravitational constant | ||
| 5719 | * and M = the mass of the body. In practice, GM is known to a higher precision than | ||
| 5720 | * either G or M alone, and thus using the product results in the most accurate results. | ||
| 5721 | * This function returns the product GM in the units au^3/day^2, or 0 for invalid bodies. | ||
| 5722 | * The values come from page 10 of a | ||
| 5723 | * [JPL memorandum regarding the DE405/LE405 ephemeris](https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf). | ||
| 5724 | * | ||
| 5725 | * @param body The body for which to find the GM product. | ||
| 5726 | * @return The mass product of the given body in au^3/day^2. | ||
| 5727 | */ | ||
| 5728 | double Astronomy_MassProduct(astro_body_t body) | ||
| 5729 | { | ||
| 5730 | switch (body) | ||
| 5731 | { | ||
| 5732 | case BODY_SUN: return SUN_GM; | ||
| 5733 | case BODY_MERCURY: return MERCURY_GM; | ||
| 5734 | case BODY_VENUS: return VENUS_GM; | ||
| 5735 | case BODY_EARTH: return EARTH_GM; | ||
| 5736 | case BODY_MOON: return MOON_GM; | ||
| 5737 | case BODY_EMB: return EARTH_GM + MOON_GM; | ||
| 5738 | case BODY_MARS: return MARS_GM; | ||
| 5739 | case BODY_JUPITER: return JUPITER_GM; | ||
| 5740 | case BODY_SATURN: return SATURN_GM; | ||
| 5741 | case BODY_URANUS: return URANUS_GM; | ||
| 5742 | case BODY_NEPTUNE: return NEPTUNE_GM; | ||
| 5743 | case BODY_PLUTO: return PLUTO_GM; | ||
| 5744 | default: return 0.0; /* invalid body */ | ||
| 5745 | } | ||
| 5746 | } | ||
| 5747 | |||
| 5748 | |||
| 5749 | /** | ||
| 5750 | * @brief Calculates one of the 5 Lagrange points for a pair of co-orbiting bodies. | ||
| 5751 | * | ||
| 5752 | * Given a more massive "major" body and a much less massive "minor" body, | ||
| 5753 | * calculates one of the five Lagrange points in relation to the minor body's | ||
| 5754 | * orbit around the major body. The parameter `point` is an integer that | ||
| 5755 | * selects the Lagrange point as follows: | ||
| 5756 | * | ||
| 5757 | * 1 = the Lagrange point between the major body and minor body. | ||
| 5758 | * 2 = the Lagrange point on the far side of the minor body. | ||
| 5759 | * 3 = the Lagrange point on the far side of the major body. | ||
| 5760 | * 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. | ||
| 5761 | * 5 = the Lagrange point 60 degrees behind the minor body's orbital position. | ||
| 5762 | * | ||
| 5763 | * The function returns the state vector for the selected Lagrange point | ||
| 5764 | * in J2000 mean equator coordinates (EQJ), with respect to the center of the | ||
| 5765 | * major body. | ||
| 5766 | * | ||
| 5767 | * To calculate Sun/Earth Lagrange points, pass in `BODY_SUN` for `major_body` | ||
| 5768 | * and `BODY_EMB` (Earth/Moon barycenter) for `minor_body`. | ||
| 5769 | * For Lagrange points of the Sun and any other planet, pass in that planet | ||
| 5770 | * (e.g. `BODY_JUPITER`) for `minor_body`. | ||
| 5771 | * To calculate Earth/Moon Lagrange points, pass in `BODY_EARTH` and `BODY_MOON` | ||
| 5772 | * for the major and minor bodies respectively. | ||
| 5773 | * | ||
| 5774 | * In some cases, it may be more efficient to call #Astronomy_LagrangePointFast, | ||
| 5775 | * especially when the state vectors have already been calculated, or are needed | ||
| 5776 | * for some other purpose. | ||
| 5777 | * | ||
| 5778 | * @param point A value 1..5 that selects which of the Lagrange points to calculate. | ||
| 5779 | * @param time The time at which the Lagrange point is to be calculated. | ||
| 5780 | * @param major_body The more massive of the co-orbiting bodies: `BODY_SUN` or `BODY_EARTH`. | ||
| 5781 | * @param minor_body The less massive of the co-orbiting bodies. See main remarks. | ||
| 5782 | * @return The position and velocity of the selected Lagrange point with respect to the major body's center. | ||
| 5783 | */ | ||
| 5784 | astro_state_vector_t Astronomy_LagrangePoint( | ||
| 5785 | int point, | ||
| 5786 | astro_time_t time, | ||
| 5787 | astro_body_t major_body, | ||
| 5788 | astro_body_t minor_body) | ||
| 5789 | { | ||
| 5790 | astro_state_vector_t major_state, minor_state; | ||
| 5791 | double major_mass, minor_mass; | ||
| 5792 | |||
| 5793 | major_mass = Astronomy_MassProduct(major_body); | ||
| 5794 | if (major_mass <= 0.0) | ||
| 5795 | return StateVecError(ASTRO_INVALID_BODY, time); | ||
| 5796 | |||
| 5797 | minor_mass = Astronomy_MassProduct(minor_body); | ||
| 5798 | if (minor_mass <= 0.0) | ||
| 5799 | return StateVecError(ASTRO_INVALID_BODY, time); | ||
| 5800 | |||
| 5801 | /* Calculate the state vectors for the major and minor bodies. */ | ||
| 5802 | if (major_body == BODY_EARTH && minor_body == BODY_MOON) | ||
| 5803 | { | ||
| 5804 | /* Use geocentric calculations for more precision. */ | ||
| 5805 | |||
| 5806 | /* The Earth's geocentric state is trivial. */ | ||
| 5807 | major_state.status = ASTRO_SUCCESS; | ||
| 5808 | major_state.t = time; | ||
| 5809 | major_state.x = major_state.y = major_state.z = 0.0; | ||
| 5810 | major_state.vx = major_state.vy = major_state.vz = 0.0; | ||
| 5811 | |||
| 5812 | minor_state = Astronomy_GeoMoonState(time); | ||
| 5813 | if (minor_state.status != ASTRO_SUCCESS) | ||
| 5814 | return minor_state; | ||
| 5815 | } | ||
| 5816 | else | ||
| 5817 | { | ||
| 5818 | major_state = Astronomy_HelioState(major_body, time); | ||
| 5819 | if (major_state.status != ASTRO_SUCCESS) | ||
| 5820 | return major_state; | ||
| 5821 | |||
| 5822 | minor_state = Astronomy_HelioState(minor_body, time); | ||
| 5823 | if (minor_state.status != ASTRO_SUCCESS) | ||
| 5824 | return minor_state; | ||
| 5825 | } | ||
| 5826 | |||
| 5827 | return Astronomy_LagrangePointFast( | ||
| 5828 | point, | ||
| 5829 | major_state, | ||
| 5830 | major_mass, | ||
| 5831 | minor_state, | ||
| 5832 | minor_mass | ||
| 5833 | ); | ||
| 5834 | } | ||
| 5835 | |||
| 5836 | |||
| 5837 | /** | ||
| 5838 | * @brief Calculates one of the 5 Lagrange points from body masses and state vectors. | ||
| 5839 | * | ||
| 5840 | * Given a more massive "major" body and a much less massive "minor" body, | ||
| 5841 | * calculates one of the five Lagrange points in relation to the minor body's | ||
| 5842 | * orbit around the major body. The parameter `point` is an integer that | ||
| 5843 | * selects the Lagrange point as follows: | ||
| 5844 | * | ||
| 5845 | * 1 = the Lagrange point between the major body and minor body. | ||
| 5846 | * 2 = the Lagrange point on the far side of the minor body. | ||
| 5847 | * 3 = the Lagrange point on the far side of the major body. | ||
| 5848 | * 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. | ||
| 5849 | * 5 = the Lagrange point 60 degrees behind the minor body's orbital position. | ||
| 5850 | * | ||
| 5851 | * The caller passes in the state vector and mass for both bodies. | ||
| 5852 | * The state vectors can be in any orientation and frame of reference. | ||
| 5853 | * The body masses are expressed as GM products, where G = the universal | ||
| 5854 | * gravitation constant and M = the body's mass. Thus the units for | ||
| 5855 | * `major_mass` and `minor_mass` must be au^3/day^2. | ||
| 5856 | * Use #Astronomy_MassProduct to obtain GM values for various solar system bodies. | ||
| 5857 | * | ||
| 5858 | * The function returns the state vector for the selected Lagrange point | ||
| 5859 | * using the same orientation as the state vector parameters `major_state` and `minor_state`, | ||
| 5860 | * and the position and velocity components are with respect to the major body's center. | ||
| 5861 | * | ||
| 5862 | * Consider calling #Astronomy_LagrangePoint, instead of this function, for simpler usage in most cases. | ||
| 5863 | * | ||
| 5864 | * @param point A value 1..5 that selects which of the Lagrange points to calculate. | ||
| 5865 | * @param major_state The state vector of the major (more massive) of the pair of bodies. | ||
| 5866 | * @param major_mass The mass product GM of the major body. | ||
| 5867 | * @param minor_state The state vector of the minor (less massive) of the pair of bodies. | ||
| 5868 | * @param minor_mass The mass product GM of the minor body. | ||
| 5869 | * @return The position and velocity of the selected Lagrange point with respect to the major body's center. | ||
| 5870 | */ | ||
| 5871 | astro_state_vector_t Astronomy_LagrangePointFast( | ||
| 5872 | int point, | ||
| 5873 | astro_state_vector_t major_state, | ||
| 5874 | double major_mass, | ||
| 5875 | astro_state_vector_t minor_state, | ||
| 5876 | double minor_mass) | ||
| 5877 | { | ||
| 5878 | const double cos_60 = 0.5; | ||
| 5879 | const double sin_60 = 0.8660254037844386; /* sqrt(3) / 2 */ | ||
| 5880 | double scale, dx, dy, dz; | ||
| 5881 | double vx, vy, vz; | ||
| 5882 | double R2, R, r1, r2, x, deltax, dr1, dr2, numer1, numer2, omega2, accel, deriv; | ||
| 5883 | astro_state_vector_t p; | ||
| 5884 | |||
| 5885 | if (point < 1 || point > 5) | ||
| 5886 | return StateVecError(ASTRO_INVALID_PARAMETER, major_state.t); | ||
| 5887 | |||
| 5888 | if (major_state.status != ASTRO_SUCCESS || minor_state.status != ASTRO_SUCCESS) | ||
| 5889 | return StateVecError(ASTRO_INVALID_PARAMETER, major_state.t); | ||
| 5890 | |||
| 5891 | if (!isfinite(major_mass) || major_mass <= 0.0) | ||
| 5892 | return StateVecError(ASTRO_INVALID_PARAMETER, major_state.t); | ||
| 5893 | |||
| 5894 | if (!isfinite(minor_mass) || minor_mass <= 0.0) | ||
| 5895 | return StateVecError(ASTRO_INVALID_PARAMETER, major_state.t); | ||
| 5896 | |||
| 5897 | /* Find the relative position vector <dx, dy, dz>. */ | ||
| 5898 | dx = minor_state.x - major_state.x; | ||
| 5899 | dy = minor_state.y - major_state.y; | ||
| 5900 | dz = minor_state.z - major_state.z; | ||
| 5901 | R2 = (dx*dx + dy*dy + dz*dz); | ||
| 5902 | |||
| 5903 | /* R = Total distance between the bodies. */ | ||
| 5904 | R = sqrt(R2); | ||
| 5905 | |||
| 5906 | /* Find the velocity vector <vx, vy, vz>. */ | ||
| 5907 | vx = minor_state.vx - major_state.vx; | ||
| 5908 | vy = minor_state.vy - major_state.vy; | ||
| 5909 | vz = minor_state.vz - major_state.vz; | ||
| 5910 | |||
| 5911 | if (point == 4 || point == 5) | ||
| 5912 | { | ||
| 5913 | double nx, ny, nz; | ||
| 5914 | double ux, uy, uz, U; | ||
| 5915 | double Dx, Dy, Dz; | ||
| 5916 | double Ux, Uy, Uz; | ||
| 5917 | double vert, vrad, vtan; | ||
| 5918 | |||
| 5919 | /* | ||
| 5920 | For L4 and L5, we need to find points 60 degrees away from the | ||
| 5921 | line connecting the two bodies and in the instantaneous orbital plane. | ||
| 5922 | Define the instantaneous orbital plane as the unique plane that contains | ||
| 5923 | both the relative position vector and the relative velocity vector. | ||
| 5924 | */ | ||
| 5925 | |||
| 5926 | /* Take the cross product of position and velocity to find a normal vector <nx, ny, nz>. */ | ||
| 5927 | nx = dy*vz - dz*vy; | ||
| 5928 | ny = dz*vx - dx*vz; | ||
| 5929 | nz = dx*vy - dy*vx; | ||
| 5930 | |||
| 5931 | /* Take the cross product normal*position to get a tangential vector <ux, uy, uz>. */ | ||
| 5932 | ux = ny*dz - nz*dy; | ||
| 5933 | uy = nz*dx - nx*dz; | ||
| 5934 | uz = nx*dy - ny*dx; | ||
| 5935 | |||
| 5936 | /* Convert the tangential direction vector to a unit vector. */ | ||
| 5937 | U = sqrt(ux*ux + uy*uy + uz*uz); | ||
| 5938 | ux /= U; | ||
| 5939 | uy /= U; | ||
| 5940 | uz /= U; | ||
| 5941 | |||
| 5942 | /* Convert the relative position vector into a unit vector. */ | ||
| 5943 | dx /= R; | ||
| 5944 | dy /= R; | ||
| 5945 | dz /= R; | ||
| 5946 | |||
| 5947 | /* Now we have two perpendicular unit vectors in the orbital plane: 'd' and 'u'. */ | ||
| 5948 | |||
| 5949 | /* Create new unit vectors rotated (+/-)60 degrees from the radius/tangent directions. */ | ||
| 5950 | vert = (point == 4) ? +sin_60 : -sin_60; | ||
| 5951 | |||
| 5952 | /* Rotated radial vector */ | ||
| 5953 | Dx = cos_60*dx + vert*ux; | ||
| 5954 | Dy = cos_60*dy + vert*uy; | ||
| 5955 | Dz = cos_60*dz + vert*uz; | ||
| 5956 | |||
| 5957 | /* Rotated tangent vector */ | ||
| 5958 | Ux = cos_60*ux - vert*dx; | ||
| 5959 | Uy = cos_60*uy - vert*dy; | ||
| 5960 | Uz = cos_60*uz - vert*dz; | ||
| 5961 | |||
| 5962 | /* Calculate L4/L5 positions relative to the major body. */ | ||
| 5963 | p.x = R * Dx; | ||
| 5964 | p.y = R * Dy; | ||
| 5965 | p.z = R * Dz; | ||
| 5966 | |||
| 5967 | /* Use dot products to find radial and tangential components of the relative velocity. */ | ||
| 5968 | vrad = vx*dx + vy*dy + vz*dz; | ||
| 5969 | vtan = vx*ux + vy*uy + vz*uz; | ||
| 5970 | |||
| 5971 | /* Calculate L4/L5 velocities. */ | ||
| 5972 | p.vx = vrad*Dx + vtan*Ux; | ||
| 5973 | p.vy = vrad*Dy + vtan*Uy; | ||
| 5974 | p.vz = vrad*Dz + vtan*Uz; | ||
| 5975 | } | ||
| 5976 | else | ||
| 5977 | { | ||
| 5978 | /* | ||
| 5979 | Calculate the distances of each body from their mutual barycenter. | ||
| 5980 | r1 = negative distance of major mass from barycenter (e.g. Sun to the left of barycenter) | ||
| 5981 | r2 = positive distance of minor mass from barycenter (e.g. Earth to the right of barycenter) | ||
| 5982 | */ | ||
| 5983 | r1 = -R * (minor_mass / (major_mass + minor_mass)); | ||
| 5984 | r2 = +R * (major_mass / (major_mass + minor_mass)); | ||
| 5985 | |||
| 5986 | /* Calculate the square of the angular orbital speed in [rad^2 / day^2]. */ | ||
| 5987 | omega2 = (major_mass + minor_mass) / (R2*R); | ||
| 5988 | |||
| 5989 | /* | ||
| 5990 | Use Newton's Method to numerically solve for the location where | ||
| 5991 | outward centrifugal acceleration in the rotating frame of reference | ||
| 5992 | is equal to net inward gravitational acceleration. | ||
| 5993 | First derive a good initial guess based on approximate analysis. | ||
| 5994 | */ | ||
| 5995 | if (point == 1 || point == 2) | ||
| 5996 | { | ||
| 5997 | scale = (major_mass / (major_mass + minor_mass)) * cbrt(minor_mass / (3.0 * major_mass)); | ||
| 5998 | numer1 = -major_mass; /* The major mass is to the left of L1 and L2 */ | ||
| 5999 | if (point == 1) | ||
| 6000 | { | ||
| 6001 | scale = 1.0 - scale; | ||
| 6002 | numer2 = +minor_mass; /* The minor mass is to the right of L1. */ | ||
| 6003 | } | ||
| 6004 | else | ||
| 6005 | { | ||
| 6006 | scale = 1.0 + scale; | ||
| 6007 | numer2 = -minor_mass; /* The minor mass is to the left of L2. */ | ||
| 6008 | } | ||
| 6009 | } | ||
| 6010 | else /* point == 3 */ | ||
| 6011 | { | ||
| 6012 | scale = ((7.0/12.0)*minor_mass - major_mass) / (minor_mass + major_mass); | ||
| 6013 | numer1 = +major_mass; /* major mass is to the right of L3. */ | ||
| 6014 | numer2 = +minor_mass; /* minor mass is to the right of L3. */ | ||
| 6015 | } | ||
| 6016 | |||
| 6017 | /* Iterate Newton's Method until it converges. */ | ||
| 6018 | x = R*scale - r1; | ||
| 6019 | do | ||
| 6020 | { | ||
| 6021 | dr1 = x - r1; | ||
| 6022 | dr2 = x - r2; | ||
| 6023 | accel = omega2*x + numer1/(dr1*dr1) + numer2/(dr2*dr2); | ||
| 6024 | deriv = omega2 - 2*numer1/(dr1*dr1*dr1) - 2*numer2/(dr2*dr2*dr2); | ||
| 6025 | deltax = accel/deriv; | ||
| 6026 | x -= deltax; | ||
| 6027 | } | ||
| 6028 | while (fabs(deltax/R) > 1.0e-14); | ||
| 6029 | scale = (x - r1) / R; | ||
| 6030 | |||
| 6031 | p.x = scale * dx; | ||
| 6032 | p.y = scale * dy; | ||
| 6033 | p.z = scale * dz; | ||
| 6034 | p.vx = scale * vx; | ||
| 6035 | p.vy = scale * vy; | ||
| 6036 | p.vz = scale * vz; | ||
| 6037 | } | ||
| 6038 | p.t = major_state.t; | ||
| 6039 | p.status = ASTRO_SUCCESS; | ||
| 6040 | return p; | ||
| 6041 | } | ||
| 6042 | |||
| 6043 | |||
| 6044 | /** | ||
| 6045 | * @brief Calculates equatorial coordinates of a celestial body as seen by an observer on the Earth's surface. | ||
| 6046 | * | ||
| 6047 | * Calculates topocentric equatorial coordinates in one of two different systems: | ||
| 6048 | * J2000 or true-equator-of-date, depending on the value of the `equdate` parameter. | ||
| 6049 | * Equatorial coordinates include right ascension, declination, and distance in astronomical units. | ||
| 6050 | * | ||
| 6051 | * This function corrects for light travel time: it adjusts the apparent location | ||
| 6052 | * of the observed body based on how long it takes for light to travel from the body to the Earth. | ||
| 6053 | * | ||
| 6054 | * This function corrects for *topocentric parallax*, meaning that it adjusts for the | ||
| 6055 | * angular shift depending on where the observer is located on the Earth. This is most | ||
| 6056 | * significant for the Moon, because it is so close to the Earth. However, parallax corection | ||
| 6057 | * has a small effect on the apparent positions of other bodies. | ||
| 6058 | * | ||
| 6059 | * Correction for aberration is optional, using the `aberration` parameter. | ||
| 6060 | * | ||
| 6061 | * @param body The celestial body to be observed. Not allowed to be `BODY_EARTH`. | ||
| 6062 | * @param time The date and time at which the observation takes place. | ||
| 6063 | * @param observer A location on or near the surface of the Earth. | ||
| 6064 | * @param equdate Selects the date of the Earth's equator in which to express the equatorial coordinates. | ||
| 6065 | * @param aberration Selects whether or not to correct for aberration. | ||
| 6066 | * @return Topocentric equatorial coordinates of the celestial body. | ||
| 6067 | */ | ||
| 6068 | astro_equatorial_t Astronomy_Equator( | ||
| 6069 | astro_body_t body, | ||
| 6070 | astro_time_t *time, | ||
| 6071 | astro_observer_t observer, | ||
| 6072 | astro_equator_date_t equdate, | ||
| 6073 | astro_aberration_t aberration) | ||
| 6074 | { | ||
| 6075 | astro_equatorial_t equ; | ||
| 6076 | astro_vector_t gc; | ||
| 6077 | double gc_observer[3]; | ||
| 6078 | double j2000[3]; | ||
| 6079 | double temp[3]; | ||
| 6080 | double datevect[3]; | ||
| 6081 | |||
| 6082 | if (time == NULL) | ||
| 6083 | return EquError(ASTRO_INVALID_PARAMETER); | ||
| 6084 | |||
| 6085 | /* Calculate the geocentric location of the observer. */ | ||
| 6086 | geo_pos(time, observer, gc_observer); | ||
| 6087 | |||
| 6088 | /* Calculate the geocentric location of the body. */ | ||
| 6089 | gc = Astronomy_GeoVector(body, *time, aberration); | ||
| 6090 | if (gc.status != ASTRO_SUCCESS) | ||
| 6091 | return EquError(gc.status); | ||
| 6092 | |||
| 6093 | /* Convert geocentric coordinates to topocentric coordinates. */ | ||
| 6094 | j2000[0] = gc.x - gc_observer[0]; | ||
| 6095 | j2000[1] = gc.y - gc_observer[1]; | ||
| 6096 | j2000[2] = gc.z - gc_observer[2]; | ||
| 6097 | |||
| 6098 | switch (equdate) | ||
| 6099 | { | ||
| 6100 | case EQUATOR_OF_DATE: | ||
| 6101 | precession(j2000, *time, FROM_2000, temp); | ||
| 6102 | nutation(temp, time, FROM_2000, datevect); | ||
| 6103 | equ = vector2radec(datevect, *time); | ||
| 6104 | return equ; | ||
| 6105 | |||
| 6106 | case EQUATOR_J2000: | ||
| 6107 | equ = vector2radec(j2000, *time); | ||
| 6108 | return equ; | ||
| 6109 | |||
| 6110 | default: | ||
| 6111 | return EquError(ASTRO_INVALID_PARAMETER); | ||
| 6112 | } | ||
| 6113 | } | ||
| 6114 | |||
| 6115 | /** | ||
| 6116 | * @brief Calculates geocentric equatorial coordinates of an observer on the surface of the Earth. | ||
| 6117 | * | ||
| 6118 | * This function calculates a vector from the center of the Earth to | ||
| 6119 | * a point on or near the surface of the Earth, expressed in equatorial | ||
| 6120 | * coordinates. It takes into account the rotation of the Earth at the given | ||
| 6121 | * time, along with the given latitude, longitude, and elevation of the observer. | ||
| 6122 | * | ||
| 6123 | * The caller may pass a value in `equdate` to select either `EQUATOR_J2000` | ||
| 6124 | * for using J2000 coordinates, or `EQUATOR_OF_DATE` for using coordinates relative | ||
| 6125 | * to the Earth's equator at the specified time. | ||
| 6126 | * | ||
| 6127 | * The returned vector has components expressed in astronomical units (AU). | ||
| 6128 | * To convert to kilometers, multiply the `x`, `y`, and `z` values by | ||
| 6129 | * the constant value #KM_PER_AU. | ||
| 6130 | * | ||
| 6131 | * The inverse of this function is also available: #Astronomy_VectorObserver. | ||
| 6132 | * | ||
| 6133 | * @param time | ||
| 6134 | * The date and time for which to calculate the observer's position vector. | ||
| 6135 | * | ||
| 6136 | * @param observer | ||
| 6137 | * The geographic location of a point on or near the surface of the Earth. | ||
| 6138 | * | ||
| 6139 | * @param equdate | ||
| 6140 | * Selects the date of the Earth's equator in which to express the equatorial coordinates. | ||
| 6141 | * The caller may select `EQUATOR_J2000` to use the orientation of the Earth's equator | ||
| 6142 | * at noon UTC on January 1, 2000, in which case this function corrects for precession | ||
| 6143 | * and nutation of the Earth as it was at the moment specified by the `time` parameter. | ||
| 6144 | * Or the caller may select `EQUATOR_OF_DATE` to use the Earth's equator at `time` | ||
| 6145 | * as the orientation. | ||
| 6146 | * | ||
| 6147 | * @return | ||
| 6148 | * If successful, the returned vector holds `ASTRO_SUCCESS` in its `status` field, | ||
| 6149 | * and is an equatorial vector from the center of the Earth to the specified location | ||
| 6150 | * on (or near) the Earth's surface. Otherwise, `status` holds an error code. | ||
| 6151 | */ | ||
| 6152 | astro_vector_t Astronomy_ObserverVector( | ||
| 6153 | astro_time_t *time, | ||
| 6154 | astro_observer_t observer, | ||
| 6155 | astro_equator_date_t equdate) | ||
| 6156 | { | ||
| 6157 | astro_vector_t vec; | ||
| 6158 | double gast, pos[3], temp[3]; | ||
| 6159 | |||
| 6160 | if (time == NULL) | ||
| 6161 | return VecError(ASTRO_INVALID_PARAMETER, TimeError()); | ||
| 6162 | |||
| 6163 | gast = Astronomy_SiderealTime(time); | ||
| 6164 | terra(observer, gast, pos, NULL); | ||
| 6165 | |||
| 6166 | switch (equdate) | ||
| 6167 | { | ||
| 6168 | case EQUATOR_OF_DATE: | ||
| 6169 | /* 'pos' already contains equator-of-date coordinates. */ | ||
| 6170 | break; | ||
| 6171 | |||
| 6172 | case EQUATOR_J2000: | ||
| 6173 | /* Convert 'pos' from equator-of-date to J2000. */ | ||
| 6174 | nutation(pos, time, INTO_2000, temp); | ||
| 6175 | precession(temp, *time, INTO_2000, pos); | ||
| 6176 | break; | ||
| 6177 | |||
| 6178 | default: | ||
| 6179 | /* This is not a valid value of the 'equdate' parameter. */ | ||
| 6180 | return VecError(ASTRO_INVALID_PARAMETER, *time); | ||
| 6181 | } | ||
| 6182 | |||
| 6183 | vec.x = pos[0]; | ||
| 6184 | vec.y = pos[1]; | ||
| 6185 | vec.z = pos[2]; | ||
| 6186 | vec.t = *time; | ||
| 6187 | vec.status = ASTRO_SUCCESS; | ||
| 6188 | return vec; | ||
| 6189 | } | ||
| 6190 | |||
| 6191 | |||
| 6192 | /** | ||
| 6193 | * @brief Calculates geocentric equatorial position and velocity of an observer on the surface of the Earth. | ||
| 6194 | * | ||
| 6195 | * This function calculates position and velocity vectors of an observer | ||
| 6196 | * on or near the surface of the Earth, expressed in equatorial | ||
| 6197 | * coordinates. It takes into account the rotation of the Earth at the given | ||
| 6198 | * time, along with the given latitude, longitude, and elevation of the observer. | ||
| 6199 | * | ||
| 6200 | * The caller may pass a value in `equdate` to select either `EQUATOR_J2000` | ||
| 6201 | * for using J2000 coordinates, or `EQUATOR_OF_DATE` for using coordinates relative | ||
| 6202 | * to the Earth's equator at the specified time. | ||
| 6203 | * | ||
| 6204 | * The returned position vector has components expressed in astronomical units (AU). | ||
| 6205 | * To convert to kilometers, multiply the `x`, `y`, and `z` values by | ||
| 6206 | * the constant value #KM_PER_AU. | ||
| 6207 | * | ||
| 6208 | * The returned velocity vector is measured in AU/day. | ||
| 6209 | * | ||
| 6210 | * If you need the position only, and not the velocity, #Astronomy_ObserverVector | ||
| 6211 | * is slightly more efficient than this function. | ||
| 6212 | * | ||
| 6213 | * @param time | ||
| 6214 | * The date and time for which to calculate the observer's geocentric state vector. | ||
| 6215 | * | ||
| 6216 | * @param observer | ||
| 6217 | * The geographic location of a point on or near the surface of the Earth. | ||
| 6218 | * | ||
| 6219 | * @param equdate | ||
| 6220 | * Selects the date of the Earth's equator in which to express the equatorial coordinates. | ||
| 6221 | * The caller may select `EQUATOR_J2000` to use the orientation of the Earth's equator | ||
| 6222 | * at noon UTC on January 1, 2000, in which case this function corrects for precession | ||
| 6223 | * and nutation of the Earth as it was at the moment specified by the `time` parameter. | ||
| 6224 | * Or the caller may select `EQUATOR_OF_DATE` to use the Earth's equator at `time` | ||
| 6225 | * as the orientation. | ||
| 6226 | * | ||
| 6227 | * @return | ||
| 6228 | * If successful, the returned state vector holds `ASTRO_SUCCESS` in its `status` field, | ||
| 6229 | * and the position (x, y, z) and velocity (vx, vy, vz) vectors are valid. | ||
| 6230 | * Otherwise, `status` holds an error code. | ||
| 6231 | */ | ||
| 6232 | astro_state_vector_t Astronomy_ObserverState( | ||
| 6233 | astro_time_t *time, | ||
| 6234 | astro_observer_t observer, | ||
| 6235 | astro_equator_date_t equdate) | ||
| 6236 | { | ||
| 6237 | astro_state_vector_t state; | ||
| 6238 | double gast, pos[3], vel[3], postemp[3], veltemp[3]; | ||
| 6239 | |||
| 6240 | if (time == NULL) | ||
| 6241 | return StateVecError(ASTRO_INVALID_PARAMETER, TimeError()); | ||
| 6242 | |||
| 6243 | gast = Astronomy_SiderealTime(time); | ||
| 6244 | terra(observer, gast, pos, vel); | ||
| 6245 | |||
| 6246 | switch (equdate) | ||
| 6247 | { | ||
| 6248 | case EQUATOR_OF_DATE: | ||
| 6249 | /* 'pos' and 'vel' already contain equator-of-date coordinates. */ | ||
| 6250 | break; | ||
| 6251 | |||
| 6252 | case EQUATOR_J2000: | ||
| 6253 | /* Convert 'pos' from equator-of-date to J2000. */ | ||
| 6254 | nutation_posvel(pos, vel, time, INTO_2000, postemp, veltemp); | ||
| 6255 | precession_posvel(postemp, veltemp, *time, INTO_2000, pos, vel); | ||
| 6256 | break; | ||
| 6257 | |||
| 6258 | default: | ||
| 6259 | /* This is not a valid value of the 'equdate' parameter. */ | ||
| 6260 | return StateVecError(ASTRO_INVALID_PARAMETER, *time); | ||
| 6261 | } | ||
| 6262 | |||
| 6263 | state.x = pos[0]; | ||
| 6264 | state.y = pos[1]; | ||
| 6265 | state.z = pos[2]; | ||
| 6266 | state.vx = vel[0]; | ||
| 6267 | state.vy = vel[1]; | ||
| 6268 | state.vz = vel[2]; | ||
| 6269 | state.t = *time; | ||
| 6270 | state.status = ASTRO_SUCCESS; | ||
| 6271 | return state; | ||
| 6272 | } | ||
| 6273 | |||
| 6274 | |||
| 6275 | /** | ||
| 6276 | * @brief Calculates the geographic location corresponding to an equatorial vector. | ||
| 6277 | * | ||
| 6278 | * This is the inverse function of #Astronomy_ObserverVector. | ||
| 6279 | * Given a geocentric equatorial vector, it returns the geographic | ||
| 6280 | * latitude, longitude, and elevation for that vector. | ||
| 6281 | * | ||
| 6282 | * @param vector | ||
| 6283 | * The geocentric equatorial position vector for which to find geographic coordinates. | ||
| 6284 | * The components are expressed in Astronomical Units (AU). | ||
| 6285 | * You can calculate AU by dividing kilometers by the constant #KM_PER_AU. | ||
| 6286 | * The time `vector.t` determines the Earth's rotation. The caller must set `vector.t` to a valid time. | ||
| 6287 | * The vector is passed by reference (using a pointer) so that nutation calculations | ||
| 6288 | * can be cached inside `vector.t` as an optimization. | ||
| 6289 | * | ||
| 6290 | * @param equdate | ||
| 6291 | * Selects the date of the Earth's equator in which `vector` is expressed. | ||
| 6292 | * The caller may select `EQUATOR_J2000` to use the orientation of the Earth's equator | ||
| 6293 | * at noon UTC on January 1, 2000, in which case this function corrects for precession | ||
| 6294 | * and nutation of the Earth as it was at the moment specified by `vector.t`. | ||
| 6295 | * Or the caller may select `EQUATOR_OF_DATE` to use the Earth's equator at `vector.t` | ||
| 6296 | * as the orientation. | ||
| 6297 | * | ||
| 6298 | * @return | ||
| 6299 | * The geographic latitude, longitude, and elevation above sea level | ||
| 6300 | * that corresponds to the given equatorial vector. | ||
| 6301 | */ | ||
| 6302 | astro_observer_t Astronomy_VectorObserver( | ||
| 6303 | astro_vector_t *vector, | ||
| 6304 | astro_equator_date_t equdate) | ||
| 6305 | { | ||
| 6306 | double gast; | ||
| 6307 | double pos1[3]; | ||
| 6308 | double pos2[3]; | ||
| 6309 | |||
| 6310 | gast = Astronomy_SiderealTime(&vector->t); | ||
| 6311 | pos1[0] = vector->x; | ||
| 6312 | pos1[1] = vector->y; | ||
| 6313 | pos1[2] = vector->z; | ||
| 6314 | if (equdate == EQUATOR_J2000) | ||
| 6315 | { | ||
| 6316 | precession(pos1, vector->t, FROM_2000, pos2); | ||
| 6317 | nutation(pos2, &vector->t, FROM_2000, pos1); | ||
| 6318 | } | ||
| 6319 | return inverse_terra(pos1, gast); | ||
| 6320 | } | ||
| 6321 | |||
| 6322 | |||
| 6323 | /** | ||
| 6324 | * @brief Calculates the gravitational acceleration experienced by an observer on the Earth. | ||
| 6325 | * | ||
| 6326 | * This function implements the WGS 84 Ellipsoidal Gravity Formula. | ||
| 6327 | * The result is a combination of inward gravitational acceleration | ||
| 6328 | * with outward centrifugal acceleration, as experienced by an observer | ||
| 6329 | * in the Earth's rotating frame of reference. | ||
| 6330 | * The resulting value increases toward the Earth's poles and decreases | ||
| 6331 | * toward the equator, consistent with changes of the weight measured | ||
| 6332 | * by a spring scale of a fixed mass moved to different latitudes and heights | ||
| 6333 | * on the Earth. | ||
| 6334 | * | ||
| 6335 | * @param latitude | ||
| 6336 | * The latitude of the observer in degrees north or south of the equator. | ||
| 6337 | * By formula symmetry, positive latitudes give the same answer as negative | ||
| 6338 | * latitudes, so the sign does not matter. | ||
| 6339 | * | ||
| 6340 | * @param height | ||
| 6341 | * The height above the sea level geoid in meters. | ||
| 6342 | * No range checking is done; however, accuracy is only valid in the | ||
| 6343 | * range 0 to 100000 meters. | ||
| 6344 | * | ||
| 6345 | * @return | ||
| 6346 | * The effective gravitational acceleration expressed in meters per second squared [m/s^2]. | ||
| 6347 | */ | ||
| 6348 | double Astronomy_ObserverGravity(double latitude, double height) | ||
| 6349 | { | ||
| 6350 | double s = sin(latitude * DEG2RAD); | ||
| 6351 | double s2 = s*s; | ||
| 6352 | double g0 = 9.7803253359 * (1.0 + 0.00193185265241*s2) / sqrt(1.0 - 0.00669437999013*s2); | ||
| 6353 | return g0 * (1.0 - (3.15704e-07 - 2.10269e-09*s2)*height + 7.37452e-14*height*height); | ||
| 6354 | } | ||
| 6355 | |||
| 6356 | |||
| 6357 | /** | ||
| 6358 | * @brief Calculates the apparent location of a body relative to the local horizon of an observer on the Earth. | ||
| 6359 | * | ||
| 6360 | * Given a date and time, the geographic location of an observer on the Earth, and | ||
| 6361 | * equatorial coordinates (right ascension and declination) of a celestial body, | ||
| 6362 | * this function returns horizontal coordinates (azimuth and altitude angles) for the body | ||
| 6363 | * relative to the horizon at the geographic location. | ||
| 6364 | * | ||
| 6365 | * The right ascension `ra` and declination `dec` passed in must be *equator of date* | ||
| 6366 | * coordinates, based on the Earth's true equator at the date and time of the observation. | ||
| 6367 | * Otherwise the resulting horizontal coordinates will be inaccurate. | ||
| 6368 | * Equator of date coordinates can be obtained by calling #Astronomy_Equator, passing in | ||
| 6369 | * `EQUATOR_OF_DATE` as its `equdate` parameter. It is also recommended to enable | ||
| 6370 | * aberration correction by passing in `ABERRATION` as the `aberration` parameter. | ||
| 6371 | * | ||
| 6372 | * This function optionally corrects for atmospheric refraction. | ||
| 6373 | * For most uses, it is recommended to pass `REFRACTION_NORMAL` in the `refraction` parameter to | ||
| 6374 | * correct for optical lensing of the Earth's atmosphere that causes objects | ||
| 6375 | * to appear somewhat higher above the horizon than they actually are. | ||
| 6376 | * However, callers may choose to avoid this correction by passing in `REFRACTION_NONE`. | ||
| 6377 | * If refraction correction is enabled, the azimuth, altitude, right ascension, and declination | ||
| 6378 | * in the #astro_horizon_t structure returned by this function will all be corrected for refraction. | ||
| 6379 | * If refraction is disabled, none of these four coordinates will be corrected; in that case, | ||
| 6380 | * the right ascension and declination in the returned structure will be numerically identical | ||
| 6381 | * to the respective `ra` and `dec` values passed in. | ||
| 6382 | * | ||
| 6383 | * @param time | ||
| 6384 | * The date and time of the observation. | ||
| 6385 | * | ||
| 6386 | * @param observer | ||
| 6387 | * The geographic location of the observer. | ||
| 6388 | * | ||
| 6389 | * @param ra | ||
| 6390 | * The right ascension of the body in sidereal hours. | ||
| 6391 | * See function remarks for more details. | ||
| 6392 | * | ||
| 6393 | * @param dec | ||
| 6394 | * The declination of the body in degrees. See function remarks for more details. | ||
| 6395 | * | ||
| 6396 | * @param refraction | ||
| 6397 | * Selects whether to correct for atmospheric refraction, and if so, which model to use. | ||
| 6398 | * The recommended value for most uses is `REFRACTION_NORMAL`. | ||
| 6399 | * See function remarks for more details. | ||
| 6400 | * | ||
| 6401 | * @return | ||
| 6402 | * The body's apparent horizontal coordinates and equatorial coordinates, both optionally corrected for refraction. | ||
| 6403 | */ | ||
| 6404 | astro_horizon_t Astronomy_Horizon( | ||
| 6405 | astro_time_t *time, | ||
| 6406 | astro_observer_t observer, | ||
| 6407 | double ra, | ||
| 6408 | double dec, | ||
| 6409 | astro_refraction_t refraction) | ||
| 6410 | { | ||
| 6411 | astro_horizon_t hor; | ||
| 6412 | double latrad, lonrad, decrad, rarad; | ||
| 6413 | double uze[3], une[3], uwe[3]; | ||
| 6414 | double uz[3], un[3], uw[3]; | ||
| 6415 | double p[3], pz, pn, pw, proj; | ||
| 6416 | double az, zd; | ||
| 6417 | double spin_angle; | ||
| 6418 | |||
| 6419 | if (time == NULL) | ||
| 6420 | { | ||
| 6421 | /* The best we can do is return an invalid state. */ | ||
| 6422 | /* It would break external dependencies to expect them to check for errors. */ | ||
| 6423 | hor.altitude = NAN; | ||
| 6424 | hor.azimuth = NAN; | ||
| 6425 | hor.ra = NAN; | ||
| 6426 | hor.dec = NAN; | ||
| 6427 | return hor; | ||
| 6428 | } | ||
| 6429 | |||
| 6430 | latrad = observer.latitude * DEG2RAD; | ||
| 6431 | lonrad = observer.longitude * DEG2RAD; | ||
| 6432 | decrad = dec * DEG2RAD; | ||
| 6433 | rarad = ra * HOUR2RAD; | ||
| 6434 | |||
| 6435 | double sinlat = sin(latrad); | ||
| 6436 | double coslat = cos(latrad); | ||
| 6437 | double sinlon = sin(lonrad); | ||
| 6438 | double coslon = cos(lonrad); | ||
| 6439 | double sindc = sin(decrad); | ||
| 6440 | double cosdc = cos(decrad); | ||
| 6441 | double sinra = sin(rarad); | ||
| 6442 | double cosra = cos(rarad); | ||
| 6443 | |||
| 6444 | /* | ||
| 6445 | Calculate three mutually perpendicular unit vectors | ||
| 6446 | in equatorial coordinates: uze, une, uwe. | ||
| 6447 | |||
| 6448 | uze = The direction of the observer's local zenith (straight up). | ||
| 6449 | une = The direction toward due north on the observer's horizon. | ||
| 6450 | uwe = The direction toward due west on the observer's horizon. | ||
| 6451 | |||
| 6452 | HOWEVER, these are uncorrected for the Earth's rotation due to the time of day. | ||
| 6453 | |||
| 6454 | The components of these 3 vectors are as follows: | ||
| 6455 | [0] = x = direction from center of Earth toward 0 degrees longitude (the prime meridian) on equator. | ||
| 6456 | [1] = y = direction from center of Earth toward 90 degrees west longitude on equator. | ||
| 6457 | [2] = z = direction from center of Earth toward the north pole. | ||
| 6458 | */ | ||
| 6459 | |||
| 6460 | uze[0] = coslat * coslon; | ||
| 6461 | uze[1] = coslat * sinlon; | ||
| 6462 | uze[2] = sinlat; | ||
| 6463 | |||
| 6464 | une[0] = -sinlat * coslon; | ||
| 6465 | une[1] = -sinlat * sinlon; | ||
| 6466 | une[2] = coslat; | ||
| 6467 | |||
| 6468 | uwe[0] = sinlon; | ||
| 6469 | uwe[1] = -coslon; | ||
| 6470 | uwe[2] = 0.0; | ||
| 6471 | |||
| 6472 | /* | ||
| 6473 | Correct the vectors uze, une, uwe for the Earth's rotation by calculating | ||
| 6474 | sidereal time. Call spin() for each uncorrected vector to rotate about | ||
| 6475 | the Earth's axis to yield corrected unit vectors uz, un, uw. | ||
| 6476 | Multiply sidereal hours by -15 to convert to degrees and flip eastward | ||
| 6477 | rotation of the Earth to westward apparent movement of objects with time. | ||
| 6478 | */ | ||
| 6479 | |||
| 6480 | spin_angle = -15.0 * Astronomy_SiderealTime(time); | ||
| 6481 | spin(spin_angle, uze, uz); | ||
| 6482 | spin(spin_angle, une, un); | ||
| 6483 | spin(spin_angle, uwe, uw); | ||
| 6484 | |||
| 6485 | /* | ||
| 6486 | Convert angular equatorial coordinates (RA, DEC) to | ||
| 6487 | cartesian equatorial coordinates in 'p', using the | ||
| 6488 | same orientation system as uze, une, uwe. | ||
| 6489 | */ | ||
| 6490 | |||
| 6491 | p[0] = cosdc * cosra; | ||
| 6492 | p[1] = cosdc * sinra; | ||
| 6493 | p[2] = sindc; | ||
| 6494 | |||
| 6495 | /* | ||
| 6496 | Use dot products of p with the zenith, north, and west | ||
| 6497 | vectors to obtain the cartesian coordinates of the body in | ||
| 6498 | the observer's horizontal orientation system. | ||
| 6499 | |||
| 6500 | pn = north component [-1, +1] | ||
| 6501 | pw = west component [-1, +1] | ||
| 6502 | pz = zenith component [-1, +1] | ||
| 6503 | */ | ||
| 6504 | |||
| 6505 | pn = p[0]*un[0] + p[1]*un[1] + p[2]*un[2]; | ||
| 6506 | pw = p[0]*uw[0] + p[1]*uw[1] + p[2]*uw[2]; | ||
| 6507 | pz = p[0]*uz[0] + p[1]*uz[1] + p[2]*uz[2]; | ||
| 6508 | |||
| 6509 | /* proj is the "shadow" of the body vector along the observer's flat ground. */ | ||
| 6510 | proj = hypot(pn, pw); | ||
| 6511 | if (proj > 0.0) | ||
| 6512 | { | ||
| 6513 | /* If the body is not exactly straight up/down, it has an azimuth. */ | ||
| 6514 | /* Invert the angle to produce degrees eastward from north. */ | ||
| 6515 | az = -atan2(pw, pn) * RAD2DEG; | ||
| 6516 | if (az < 0.0) | ||
| 6517 | az += 360; | ||
| 6518 | } | ||
| 6519 | else | ||
| 6520 | { | ||
| 6521 | /* The body is straight up/down, so it does not have an azimuth. */ | ||
| 6522 | /* Report an arbitrary but reasonable value. */ | ||
| 6523 | az = 0.0; | ||
| 6524 | } | ||
| 6525 | |||
| 6526 | /* zd = the angle of the body away from the observer's zenith, in degrees. */ | ||
| 6527 | zd = atan2(proj, pz) * RAD2DEG; | ||
| 6528 | hor.ra = ra; | ||
| 6529 | hor.dec = dec; | ||
| 6530 | |||
| 6531 | if (refraction == REFRACTION_NORMAL || refraction == REFRACTION_JPLHOR) | ||
| 6532 | { | ||
| 6533 | double zd0, refr; | ||
| 6534 | |||
| 6535 | zd0 = zd; | ||
| 6536 | refr = Astronomy_Refraction(refraction, 90.0 - zd); | ||
| 6537 | zd -= refr; | ||
| 6538 | |||
| 6539 | if (refr > 0.0 && zd > 3.0e-4) | ||
| 6540 | { | ||
| 6541 | int j; | ||
| 6542 | double sinzd = sin(zd * DEG2RAD); | ||
| 6543 | double coszd = cos(zd * DEG2RAD); | ||
| 6544 | double sinzd0 = sin(zd0 * DEG2RAD); | ||
| 6545 | double coszd0 = cos(zd0 * DEG2RAD); | ||
| 6546 | double pr[3]; | ||
| 6547 | |||
| 6548 | for (j=0; j<3; ++j) | ||
| 6549 | pr[j] = ((p[j] - coszd0 * uz[j]) / sinzd0)*sinzd + uz[j]*coszd; | ||
| 6550 | |||
| 6551 | proj = hypot(pr[0], pr[1]); | ||
| 6552 | if (proj > 0) | ||
| 6553 | { | ||
| 6554 | hor.ra = RAD2HOUR * atan2(pr[1], pr[0]); | ||
| 6555 | if (hor.ra < 0.0) | ||
| 6556 | hor.ra += 24.0; | ||
| 6557 | } | ||
| 6558 | else | ||
| 6559 | { | ||
| 6560 | hor.ra = 0.0; | ||
| 6561 | } | ||
| 6562 | hor.dec = RAD2DEG * atan2(pr[2], proj); | ||
| 6563 | } | ||
| 6564 | } | ||
| 6565 | |||
| 6566 | hor.azimuth = az; | ||
| 6567 | hor.altitude = 90.0 - zd; | ||
| 6568 | return hor; | ||
| 6569 | } | ||
| 6570 | |||
| 6571 | /** | ||
| 6572 | * @brief Calculates geocentric ecliptic coordinates for the Sun. | ||
| 6573 | * | ||
| 6574 | * This function calculates the position of the Sun as seen from the Earth. | ||
| 6575 | * The returned value includes both Cartesian and spherical coordinates. | ||
| 6576 | * The x-coordinate and longitude values in the returned structure are based | ||
| 6577 | * on the *true equinox of date*: one of two points in the sky where the instantaneous | ||
| 6578 | * plane of the Earth's equator at the given date and time (the *equatorial plane*) | ||
| 6579 | * intersects with the plane of the Earth's orbit around the Sun (the *ecliptic plane*). | ||
| 6580 | * By convention, the apparent location of the Sun at the March equinox is chosen | ||
| 6581 | * as the longitude origin and x-axis direction, instead of the one for September. | ||
| 6582 | * | ||
| 6583 | * `Astronomy_SunPosition` corrects for precession and nutation of the Earth's axis | ||
| 6584 | * in order to obtain the exact equatorial plane at the given time. | ||
| 6585 | * | ||
| 6586 | * This function can be used for calculating changes of seasons: equinoxes and solstices. | ||
| 6587 | * In fact, the function #Astronomy_Seasons does use this function for that purpose. | ||
| 6588 | * | ||
| 6589 | * @param time | ||
| 6590 | * The date and time for which to calculate the Sun's position. | ||
| 6591 | * | ||
| 6592 | * @return | ||
| 6593 | * The ecliptic coordinates of the Sun using the Earth's true equator of date. | ||
| 6594 | */ | ||
| 6595 | astro_ecliptic_t Astronomy_SunPosition(astro_time_t time) | ||
| 6596 | { | ||
| 6597 | astro_time_t adjusted_time; | ||
| 6598 | astro_vector_t earth2000; | ||
| 6599 | double sun2000[3]; | ||
| 6600 | double stemp[3]; | ||
| 6601 | double sun_ofdate[3]; | ||
| 6602 | double true_obliq; | ||
| 6603 | |||
| 6604 | /* Correct for light travel time from the Sun. */ | ||
| 6605 | /* Otherwise season calculations (equinox, solstice) will all be early by about 8 minutes! */ | ||
| 6606 | adjusted_time = Astronomy_AddDays(time, -1.0 / C_AUDAY); | ||
| 6607 | |||
| 6608 | earth2000 = CalcEarth(adjusted_time); | ||
| 6609 | if (earth2000.status != ASTRO_SUCCESS) | ||
| 6610 | return EclError(earth2000.status); | ||
| 6611 | |||
| 6612 | /* Convert heliocentric location of Earth to geocentric location of Sun. */ | ||
| 6613 | sun2000[0] = -earth2000.x; | ||
| 6614 | sun2000[1] = -earth2000.y; | ||
| 6615 | sun2000[2] = -earth2000.z; | ||
| 6616 | |||
| 6617 | /* Convert to equatorial Cartesian coordinates of date. */ | ||
| 6618 | precession(sun2000, adjusted_time, FROM_2000, stemp); | ||
| 6619 | nutation(stemp, &adjusted_time, FROM_2000, sun_ofdate); | ||
| 6620 | |||
| 6621 | /* Convert equatorial coordinates to ecliptic coordinates. */ | ||
| 6622 | true_obliq = DEG2RAD * e_tilt(&adjusted_time).tobl; | ||
| 6623 | return RotateEquatorialToEcliptic(sun_ofdate, true_obliq, time); | ||
| 6624 | } | ||
| 6625 | |||
| 6626 | /** | ||
| 6627 | * @brief Converts a J2000 mean equator (EQJ) vector to a true ecliptic of date (ETC) vector and angles. | ||
| 6628 | * | ||
| 6629 | * Given coordinates relative to the Earth's equator at J2000 (the instant of noon UTC | ||
| 6630 | * on 1 January 2000), this function converts those coordinates to true ecliptic coordinates | ||
| 6631 | * that are relative to the plane of the Earth's orbit around the Sun on that date. | ||
| 6632 | * | ||
| 6633 | * @param eqj | ||
| 6634 | * Equatorial coordinates in the EQJ frame of reference. | ||
| 6635 | * You can call #Astronomy_GeoVector to obtain suitable equatorial coordinates. | ||
| 6636 | * | ||
| 6637 | * @return | ||
| 6638 | * Spherical and vector coordinates expressed in true ecliptic coordinates of date (ECT). | ||
| 6639 | */ | ||
| 6640 | astro_ecliptic_t Astronomy_Ecliptic(astro_vector_t eqj) | ||
| 6641 | { | ||
| 6642 | earth_tilt_t et; | ||
| 6643 | double eqj_pos[3]; | ||
| 6644 | double mean_pos[3]; | ||
| 6645 | double eqd_pos[3]; | ||
| 6646 | |||
| 6647 | if (eqj.status != ASTRO_SUCCESS) | ||
| 6648 | return EclError(eqj.status); | ||
| 6649 | |||
| 6650 | /* Calculate nutation and obliquity for this time. */ | ||
| 6651 | /* As an optimization, the nutation angles are cached in `time`, */ | ||
| 6652 | /* and reused below when the `nutation` function is called. */ | ||
| 6653 | et = e_tilt(&eqj.t); | ||
| 6654 | |||
| 6655 | /* Convert mean J2000 equator (EQJ) to true equator of date (EQD). */ | ||
| 6656 | eqj_pos[0] = eqj.x; | ||
| 6657 | eqj_pos[1] = eqj.y; | ||
| 6658 | eqj_pos[2] = eqj.z; | ||
| 6659 | precession(eqj_pos, eqj.t, FROM_2000, mean_pos); | ||
| 6660 | nutation(mean_pos, &eqj.t, FROM_2000, eqd_pos); | ||
| 6661 | |||
| 6662 | /* Rotate from EQD to true ecliptic of date (ECT). */ | ||
| 6663 | return RotateEquatorialToEcliptic(eqd_pos, et.tobl * DEG2RAD, eqj.t); | ||
| 6664 | } | ||
| 6665 | |||
| 6666 | /** | ||
| 6667 | * @brief Calculates heliocentric ecliptic longitude of a body. | ||
| 6668 | * | ||
| 6669 | * This function calculates the angle around the plane of the Earth's orbit | ||
| 6670 | * of a celestial body, as seen from the center of the Sun. | ||
| 6671 | * The angle is measured prograde (in the direction of the Earth's orbit around the Sun) | ||
| 6672 | * in degrees from the true equinox of date. The ecliptic longitude is always in the range [0, 360). | ||
| 6673 | * | ||
| 6674 | * @param body | ||
| 6675 | * A body other than the Sun. | ||
| 6676 | * | ||
| 6677 | * @param time | ||
| 6678 | * The date and time at which the body's ecliptic longitude is to be calculated. | ||
| 6679 | * | ||
| 6680 | * @return | ||
| 6681 | * On success, returns a structure whose `status` is `ASTRO_SUCCESS` and whose | ||
| 6682 | * `angle` holds the ecliptic longitude in degrees. | ||
| 6683 | * On failure, `status` holds a value other than `ASTRO_SUCCESS`. | ||
| 6684 | */ | ||
| 6685 | astro_angle_result_t Astronomy_EclipticLongitude(astro_body_t body, astro_time_t time) | ||
| 6686 | { | ||
| 6687 | astro_vector_t hv; | ||
| 6688 | astro_ecliptic_t eclip; | ||
| 6689 | astro_angle_result_t result; | ||
| 6690 | |||
| 6691 | if (body == BODY_SUN) | ||
| 6692 | return AngleError(ASTRO_INVALID_BODY); /* cannot calculate heliocentric longitude of the Sun */ | ||
| 6693 | |||
| 6694 | hv = Astronomy_HelioVector(body, time); | ||
| 6695 | eclip = Astronomy_Ecliptic(hv); /* checks for errors in hv, so we don't have to here */ | ||
| 6696 | if (eclip.status != ASTRO_SUCCESS) | ||
| 6697 | return AngleError(eclip.status); | ||
| 6698 | |||
| 6699 | result.angle = eclip.elon; | ||
| 6700 | result.status = ASTRO_SUCCESS; | ||
| 6701 | return result; | ||
| 6702 | } | ||
| 6703 | |||
| 6704 | static astro_ecliptic_t RotateEquatorialToEcliptic(const double pos[3], double obliq_radians, astro_time_t time) | ||
| 6705 | { | ||
| 6706 | astro_ecliptic_t ecl; | ||
| 6707 | double cos_ob, sin_ob; | ||
| 6708 | double xyproj; | ||
| 6709 | |||
| 6710 | cos_ob = cos(obliq_radians); | ||
| 6711 | sin_ob = sin(obliq_radians); | ||
| 6712 | |||
| 6713 | ecl.vec.status = ASTRO_SUCCESS; | ||
| 6714 | ecl.vec.t = time; | ||
| 6715 | ecl.vec.x = +pos[0]; | ||
| 6716 | ecl.vec.y = +pos[1]*cos_ob + pos[2]*sin_ob; | ||
| 6717 | ecl.vec.z = -pos[1]*sin_ob + pos[2]*cos_ob; | ||
| 6718 | |||
| 6719 | xyproj = hypot(ecl.vec.x, ecl.vec.y); | ||
| 6720 | if (xyproj > 0.0) | ||
| 6721 | { | ||
| 6722 | ecl.elon = RAD2DEG * atan2(ecl.vec.y, ecl.vec.x); | ||
| 6723 | if (ecl.elon < 0.0) | ||
| 6724 | ecl.elon += 360.0; | ||
| 6725 | } | ||
| 6726 | else | ||
| 6727 | ecl.elon = 0.0; | ||
| 6728 | |||
| 6729 | ecl.elat = RAD2DEG * atan2(ecl.vec.z, xyproj); | ||
| 6730 | ecl.status = ASTRO_SUCCESS; | ||
| 6731 | return ecl; | ||
| 6732 | } | ||
| 6733 | |||
| 6734 | static astro_func_result_t sun_offset(void *context, astro_time_t time) | ||
| 6735 | { | ||
| 6736 | astro_func_result_t result; | ||
| 6737 | double targetLon = *((double *)context); | ||
| 6738 | astro_ecliptic_t ecl = Astronomy_SunPosition(time); | ||
| 6739 | if (ecl.status != ASTRO_SUCCESS) | ||
| 6740 | return FuncError(ecl.status); | ||
| 6741 | result.value = LongitudeOffset(ecl.elon - targetLon); | ||
| 6742 | result.status = ASTRO_SUCCESS; | ||
| 6743 | return result; | ||
| 6744 | } | ||
| 6745 | |||
| 6746 | /** | ||
| 6747 | * @brief | ||
| 6748 | * Searches for the time when the Sun reaches an apparent ecliptic longitude as seen from the Earth. | ||
| 6749 | * | ||
| 6750 | * This function finds the moment in time, if any exists in the given time window, | ||
| 6751 | * that the center of the Sun reaches a specific ecliptic longitude as seen from the center of the Earth. | ||
| 6752 | * | ||
| 6753 | * This function can be used to determine equinoxes and solstices. | ||
| 6754 | * However, it is usually more convenient and efficient to call #Astronomy_Seasons | ||
| 6755 | * to calculate all equinoxes and solstices for a given calendar year. | ||
| 6756 | * | ||
| 6757 | * The function searches the window of time specified by `startTime` and `startTime+limitDays`. | ||
| 6758 | * The search will return an error if the Sun never reaches the longitude `targetLon` or | ||
| 6759 | * if the window is so large that the longitude ranges more than 180 degrees within it. | ||
| 6760 | * It is recommended to keep the window smaller than 10 days when possible. | ||
| 6761 | * | ||
| 6762 | * @param targetLon | ||
| 6763 | * The desired ecliptic longitude in degrees, relative to the true equinox of date. | ||
| 6764 | * This may be any value in the range [0, 360), although certain values have | ||
| 6765 | * conventional meanings: | ||
| 6766 | * 0 = March equinox, 90 = June solstice, 180 = September equinox, 270 = December solstice. | ||
| 6767 | * | ||
| 6768 | * @param startTime | ||
| 6769 | * The date and time for starting the search for the desired longitude event. | ||
| 6770 | * | ||
| 6771 | * @param limitDays | ||
| 6772 | * The real-valued number of days, which when added to `startTime`, limits the | ||
| 6773 | * range of time over which the search looks. | ||
| 6774 | * It is recommended to keep this value between 1 and 10 days. | ||
| 6775 | * See function remarks for more details. | ||
| 6776 | * | ||
| 6777 | * @return | ||
| 6778 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 6779 | * and the `time` field will contain the date and time the Sun reaches the target longitude. | ||
| 6780 | * Any other value indicates an error. | ||
| 6781 | * See remarks in #Astronomy_Search (which this function calls) for more information about possible error codes. | ||
| 6782 | */ | ||
| 6783 | astro_search_result_t Astronomy_SearchSunLongitude( | ||
| 6784 | double targetLon, | ||
| 6785 | astro_time_t startTime, | ||
| 6786 | double limitDays) | ||
| 6787 | { | ||
| 6788 | astro_time_t t2 = Astronomy_AddDays(startTime, limitDays); | ||
| 6789 | return Astronomy_Search(sun_offset, &targetLon, startTime, t2, 0.01); | ||
| 6790 | } | ||
| 6791 | |||
| 6792 | /** @cond DOXYGEN_SKIP */ | ||
| 6793 | #define CALLFUNC(f,t) \ | ||
| 6794 | do { \ | ||
| 6795 | funcres = func(context, (t)); \ | ||
| 6796 | if (funcres.status != ASTRO_SUCCESS) return SearchError(funcres.status); \ | ||
| 6797 | (f) = funcres.value; \ | ||
| 6798 | } while(0) | ||
| 6799 | /** @endcond */ | ||
| 6800 | |||
| 6801 | /** | ||
| 6802 | * @brief Searches for a time at which a function's value increases through zero. | ||
| 6803 | * | ||
| 6804 | * Certain astronomy calculations involve finding a time when an event occurs. | ||
| 6805 | * Often such events can be defined as the root of a function: | ||
| 6806 | * the time at which the function's value becomes zero. | ||
| 6807 | * | ||
| 6808 | * `Astronomy_Search` finds the *ascending root* of a function: the time at which | ||
| 6809 | * the function's value becomes zero while having a positive slope. That is, as time increases, | ||
| 6810 | * the function transitions from a negative value, through zero at a specific moment, | ||
| 6811 | * to a positive value later. The goal of the search is to find that specific moment. | ||
| 6812 | * | ||
| 6813 | * The search function is specified by two parameters: `func` and `context`. | ||
| 6814 | * The `func` parameter is a pointer to the function itself, which accepts a time | ||
| 6815 | * and a context containing any other arguments needed to evaluate the function. | ||
| 6816 | * The `context` parameter supplies that context for the given search. | ||
| 6817 | * As an example, a caller may wish to find the moment a celestial body reaches a certain | ||
| 6818 | * ecliptic longitude. In that case, the caller might create a structure that contains | ||
| 6819 | * an #astro_body_t member to specify the body and a `double` to hold the target longitude. | ||
| 6820 | * The function would cast the pointer `context` passed in as a pointer to that structure type. | ||
| 6821 | * It could subtract the target longitude from the actual longitude at a given time; | ||
| 6822 | * thus the difference would equal zero at the moment in time the planet reaches the | ||
| 6823 | * desired longitude. | ||
| 6824 | * | ||
| 6825 | * The `func` returns an #astro_func_result_t structure every time it is called. | ||
| 6826 | * If the returned structure has a value of `status` other than `ASTRO_SUCCESS`, | ||
| 6827 | * the search immediately fails and reports that same error code in the `status` | ||
| 6828 | * returned by `Astronomy_Search`. Otherwise, `status` is `ASTRO_SUCCESS` and | ||
| 6829 | * `value` is the value of the function, and the search proceeds until it either | ||
| 6830 | * finds the ascending root or fails for some reason. | ||
| 6831 | * | ||
| 6832 | * The search calls `func` repeatedly to rapidly narrow in on any ascending | ||
| 6833 | * root within the time window specified by `t1` and `t2`. The search never | ||
| 6834 | * reports a solution outside this time window. | ||
| 6835 | * | ||
| 6836 | * `Astronomy_Search` uses a combination of bisection and quadratic interpolation | ||
| 6837 | * to minimize the number of function calls. However, it is critical that the | ||
| 6838 | * supplied time window be small enough that there cannot be more than one root | ||
| 6839 | * (ascedning or descending) within it; otherwise the search can fail. | ||
| 6840 | * Beyond that, it helps to make the time window as small as possible, ideally | ||
| 6841 | * such that the function itself resembles a smooth parabolic curve within that window. | ||
| 6842 | * | ||
| 6843 | * If an ascending root is not found, or more than one root | ||
| 6844 | * (ascending and/or descending) exists within the window `t1`..`t2`, | ||
| 6845 | * the search will fail with status code `ASTRO_SEARCH_FAILURE`. | ||
| 6846 | * | ||
| 6847 | * If the search does not converge within 20 iterations, it will fail | ||
| 6848 | * with status code `ASTRO_NO_CONVERGE`. | ||
| 6849 | * | ||
| 6850 | * @param func | ||
| 6851 | * The function for which to find the time of an ascending root. | ||
| 6852 | * See function remarks for more details. | ||
| 6853 | * | ||
| 6854 | * @param context | ||
| 6855 | * Any ancillary data needed by the function `func` to calculate a value. | ||
| 6856 | * The data type varies depending on the function passed in. | ||
| 6857 | * For example, the function may involve a specific celestial body that | ||
| 6858 | * must be specified somehow. | ||
| 6859 | * | ||
| 6860 | * @param t1 | ||
| 6861 | * The lower time bound of the search window. | ||
| 6862 | * See function remarks for more details. | ||
| 6863 | * | ||
| 6864 | * @param t2 | ||
| 6865 | * The upper time bound of the search window. | ||
| 6866 | * See function remarks for more details. | ||
| 6867 | * | ||
| 6868 | * @param dt_tolerance_seconds | ||
| 6869 | * Specifies an amount of time in seconds within which a bounded ascending root | ||
| 6870 | * is considered accurate enough to stop. A typical value is 1 second. | ||
| 6871 | * | ||
| 6872 | * @return | ||
| 6873 | * If successful, the returned structure has `status` equal to `ASTRO_SUCCESS` | ||
| 6874 | * and `time` set to a value within `dt_tolerance_seconds` of an ascending root. | ||
| 6875 | * On success, the `time` value will always be in the inclusive range [`t1`, `t2`]. | ||
| 6876 | * If the search fails, `status` will be set to a value other than `ASTRO_SUCCESS`. | ||
| 6877 | * See function remarks for more details. | ||
| 6878 | */ | ||
| 6879 | astro_search_result_t Astronomy_Search( | ||
| 6880 | astro_search_func_t func, | ||
| 6881 | void *context, | ||
| 6882 | astro_time_t t1, | ||
| 6883 | astro_time_t t2, | ||
| 6884 | double dt_tolerance_seconds) | ||
| 6885 | { | ||
| 6886 | astro_search_result_t result; | ||
| 6887 | astro_time_t tmid; | ||
| 6888 | astro_time_t tq; | ||
| 6889 | astro_func_result_t funcres; | ||
| 6890 | double f1, f2, fmid=0.0, fq, dt_days, dt, dt_guess; | ||
| 6891 | double q_ut, q_df_dt; | ||
| 6892 | const int iter_limit = 20; | ||
| 6893 | int iter = 0; | ||
| 6894 | int calc_fmid = 1; | ||
| 6895 | |||
| 6896 | dt_days = fabs(dt_tolerance_seconds / SECONDS_PER_DAY); | ||
| 6897 | CALLFUNC(f1, t1); | ||
| 6898 | CALLFUNC(f2, t2); | ||
| 6899 | |||
| 6900 | for(;;) | ||
| 6901 | { | ||
| 6902 | if (++iter > iter_limit) | ||
| 6903 | return SearchError(ASTRO_NO_CONVERGE); | ||
| 6904 | |||
| 6905 | dt = (t2.tt - t1.tt) / 2.0; | ||
| 6906 | tmid = Astronomy_AddDays(t1, dt); | ||
| 6907 | if (fabs(dt) < dt_days) | ||
| 6908 | { | ||
| 6909 | /* We are close enough to the event to stop the search. */ | ||
| 6910 | result.time = tmid; | ||
| 6911 | result.status = ASTRO_SUCCESS; | ||
| 6912 | return result; | ||
| 6913 | } | ||
| 6914 | |||
| 6915 | if (calc_fmid) | ||
| 6916 | CALLFUNC(fmid, tmid); | ||
| 6917 | else | ||
| 6918 | calc_fmid = 1; /* we already have the correct value of fmid from the previous loop */ | ||
| 6919 | |||
| 6920 | /* Quadratic interpolation: */ | ||
| 6921 | /* Try to find a parabola that passes through the 3 points we have sampled: */ | ||
| 6922 | /* (t1,f1), (tmid,fmid), (t2,f2) */ | ||
| 6923 | |||
| 6924 | if (QuadInterp(tmid.ut, t2.ut - tmid.ut, f1, fmid, f2, &q_ut, &q_df_dt)) | ||
| 6925 | { | ||
| 6926 | tq = Astronomy_TimeFromDays(q_ut); | ||
| 6927 | CALLFUNC(fq, tq); | ||
| 6928 | if (q_df_dt != 0.0) | ||
| 6929 | { | ||
| 6930 | dt_guess = fabs(fq / q_df_dt); | ||
| 6931 | if (dt_guess < dt_days) | ||
| 6932 | { | ||
| 6933 | /* The estimated time error is small enough that we can quit now. */ | ||
| 6934 | result.time = tq; | ||
| 6935 | result.status = ASTRO_SUCCESS; | ||
| 6936 | return result; | ||
| 6937 | } | ||
| 6938 | |||
| 6939 | /* Try guessing a tighter boundary with the interpolated root at the center. */ | ||
| 6940 | dt_guess *= 1.2; | ||
| 6941 | if (dt_guess < dt/10.0) | ||
| 6942 | { | ||
| 6943 | astro_time_t tleft = Astronomy_AddDays(tq, -dt_guess); | ||
| 6944 | astro_time_t tright = Astronomy_AddDays(tq, +dt_guess); | ||
| 6945 | if ((tleft.ut - t1.ut)*(tleft.ut - t2.ut) < 0) | ||
| 6946 | { | ||
| 6947 | if ((tright.ut - t1.ut)*(tright.ut - t2.ut) < 0) | ||
| 6948 | { | ||
| 6949 | double fleft, fright; | ||
| 6950 | CALLFUNC(fleft, tleft); | ||
| 6951 | CALLFUNC(fright, tright); | ||
| 6952 | if (fleft<0.0 && fright>=0.0) | ||
| 6953 | { | ||
| 6954 | f1 = fleft; | ||
| 6955 | f2 = fright; | ||
| 6956 | t1 = tleft; | ||
| 6957 | t2 = tright; | ||
| 6958 | fmid = fq; | ||
| 6959 | calc_fmid = 0; /* save a little work -- no need to re-calculate fmid next time around the loop */ | ||
| 6960 | continue; | ||
| 6961 | } | ||
| 6962 | } | ||
| 6963 | } | ||
| 6964 | } | ||
| 6965 | } | ||
| 6966 | } | ||
| 6967 | |||
| 6968 | /* After quadratic interpolation attempt. */ | ||
| 6969 | /* Now just divide the region in two parts and pick whichever one appears to contain a root. */ | ||
| 6970 | if (f1 < 0.0 && fmid >= 0.0) | ||
| 6971 | { | ||
| 6972 | t2 = tmid; | ||
| 6973 | f2 = fmid; | ||
| 6974 | continue; | ||
| 6975 | } | ||
| 6976 | |||
| 6977 | if (fmid < 0.0 && f2 >= 0.0) | ||
| 6978 | { | ||
| 6979 | t1 = tmid; | ||
| 6980 | f1 = fmid; | ||
| 6981 | continue; | ||
| 6982 | } | ||
| 6983 | |||
| 6984 | /* Either there is no ascending zero-crossing in this range */ | ||
| 6985 | /* or the search window is too wide (more than one zero-crossing). */ | ||
| 6986 | return SearchError(ASTRO_SEARCH_FAILURE); | ||
| 6987 | } | ||
| 6988 | } | ||
| 6989 | |||
| 6990 | static int QuadInterp( | ||
| 6991 | double tm, double dt, double fa, double fm, double fb, | ||
| 6992 | double *out_t, double *out_df_dt) | ||
| 6993 | { | ||
| 6994 | double Q, R, S; | ||
| 6995 | double x, u, ru, x1, x2; | ||
| 6996 | |||
| 6997 | Q = (fb + fa)/2.0 - fm; | ||
| 6998 | R = (fb - fa)/2.0; | ||
| 6999 | S = fm; | ||
| 7000 | |||
| 7001 | if (Q == 0.0) | ||
| 7002 | { | ||
| 7003 | /* This is a line, not a parabola. */ | ||
| 7004 | if (R == 0.0) | ||
| 7005 | return 0; /* This is a HORIZONTAL line... can't make progress! */ | ||
| 7006 | x = -S / R; | ||
| 7007 | if (x < -1.0 || x > +1.0) | ||
| 7008 | return 0; /* out of bounds */ | ||
| 7009 | } | ||
| 7010 | else | ||
| 7011 | { | ||
| 7012 | /* This really is a parabola. Find roots x1, x2. */ | ||
| 7013 | u = R*R - 4*Q*S; | ||
| 7014 | if (u <= 0.0) | ||
| 7015 | return 0; /* can't solve if imaginary, or if vertex of parabola is tangent. */ | ||
| 7016 | |||
| 7017 | ru = sqrt(u); | ||
| 7018 | x1 = (-R + ru) / (2.0 * Q); | ||
| 7019 | x2 = (-R - ru) / (2.0 * Q); | ||
| 7020 | if (-1.0 <= x1 && x1 <= +1.0) | ||
| 7021 | { | ||
| 7022 | if (-1.0 <= x2 && x2 <= +1.0) | ||
| 7023 | return 0; /* two roots are within bounds; we require a unique zero-crossing. */ | ||
| 7024 | x = x1; | ||
| 7025 | } | ||
| 7026 | else if (-1.0 <= x2 && x2 <= +1.0) | ||
| 7027 | x = x2; | ||
| 7028 | else | ||
| 7029 | return 0; /* neither root is within bounds */ | ||
| 7030 | } | ||
| 7031 | |||
| 7032 | *out_t = tm + x*dt; | ||
| 7033 | *out_df_dt = (2*Q*x + R) / dt; | ||
| 7034 | return 1; /* success */ | ||
| 7035 | } | ||
| 7036 | |||
| 7037 | static astro_status_t FindSeasonChange(double targetLon, int year, int month, int day, astro_time_t *time) | ||
| 7038 | { | ||
| 7039 | astro_time_t startTime; | ||
| 7040 | astro_search_result_t result; | ||
| 7041 | |||
| 7042 | startTime = Astronomy_MakeTime(year, month, day, 0, 0, 0.0); | ||
| 7043 | result = Astronomy_SearchSunLongitude(targetLon, startTime, 20.0); | ||
| 7044 | *time = result.time; | ||
| 7045 | return result.status; | ||
| 7046 | } | ||
| 7047 | |||
| 7048 | /** | ||
| 7049 | * @brief Finds both equinoxes and both solstices for a given calendar year. | ||
| 7050 | * | ||
| 7051 | * The changes of seasons are defined by solstices and equinoxes. | ||
| 7052 | * Given a calendar year number, this function calculates the | ||
| 7053 | * March and September equinoxes and the June and December solstices. | ||
| 7054 | * | ||
| 7055 | * The equinoxes are the moments twice each year when the plane of the | ||
| 7056 | * Earth's equator passes through the center of the Sun. In other words, | ||
| 7057 | * the Sun's declination is zero at both equinoxes. | ||
| 7058 | * The March equinox defines the beginning of spring in the northern hemisphere | ||
| 7059 | * and the beginning of autumn in the southern hemisphere. | ||
| 7060 | * The September equinox defines the beginning of autumn in the northern hemisphere | ||
| 7061 | * and the beginning of spring in the southern hemisphere. | ||
| 7062 | * | ||
| 7063 | * The solstices are the moments twice each year when one of the Earth's poles | ||
| 7064 | * is most tilted toward the Sun. More precisely, the Sun's declination reaches | ||
| 7065 | * its minimum value at the December solstice, which defines the beginning of | ||
| 7066 | * winter in the northern hemisphere and the beginning of summer in the southern | ||
| 7067 | * hemisphere. The Sun's declination reaches its maximum value at the June solstice, | ||
| 7068 | * which defines the beginning of summer in the northern hemisphere and the beginning | ||
| 7069 | * of winter in the southern hemisphere. | ||
| 7070 | * | ||
| 7071 | * @param year | ||
| 7072 | * The calendar year number for which to calculate equinoxes and solstices. | ||
| 7073 | * The value may be any integer, but only the years 1800 through 2100 have been | ||
| 7074 | * validated for accuracy: unit testing against data from the | ||
| 7075 | * United States Naval Observatory confirms that all equinoxes and solstices | ||
| 7076 | * for that range of years are within 2 minutes of the correct time. | ||
| 7077 | * | ||
| 7078 | * @return | ||
| 7079 | * The times of the four seasonal changes in the given calendar year. | ||
| 7080 | * This function should always succeed. However, to be safe, callers | ||
| 7081 | * should check the `status` field of the returned structure to make sure | ||
| 7082 | * it contains `ASTRO_SUCCESS`. Any failures indicate a bug in the algorithm | ||
| 7083 | * and should be [reported as an issue](https://github.com/cosinekitty/astronomy/issues). | ||
| 7084 | */ | ||
| 7085 | astro_seasons_t Astronomy_Seasons(int year) | ||
| 7086 | { | ||
| 7087 | astro_seasons_t seasons; | ||
| 7088 | astro_status_t status; | ||
| 7089 | |||
| 7090 | seasons.status = ASTRO_SUCCESS; | ||
| 7091 | |||
| 7092 | /* | ||
| 7093 | https://github.com/cosinekitty/astronomy/issues/187 | ||
| 7094 | Solstices and equinoxes drift over long spans of time, | ||
| 7095 | due to precession of the Earth's axis. | ||
| 7096 | Therefore, we have to search a wider range of time than | ||
| 7097 | one might expect. It turns out this has very little | ||
| 7098 | effect on efficiency, thanks to the quick convergence | ||
| 7099 | of quadratic interpolation inside Astronomy_Search(). | ||
| 7100 | */ | ||
| 7101 | |||
| 7102 | status = FindSeasonChange( 0, year, 3, 10, &seasons.mar_equinox); | ||
| 7103 | if (status != ASTRO_SUCCESS) seasons.status = status; | ||
| 7104 | |||
| 7105 | status = FindSeasonChange( 90, year, 6, 10, &seasons.jun_solstice); | ||
| 7106 | if (status != ASTRO_SUCCESS) seasons.status = status; | ||
| 7107 | |||
| 7108 | status = FindSeasonChange(180, year, 9, 10, &seasons.sep_equinox); | ||
| 7109 | if (status != ASTRO_SUCCESS) seasons.status = status; | ||
| 7110 | |||
| 7111 | status = FindSeasonChange(270, year, 12, 10, &seasons.dec_solstice); | ||
| 7112 | if (status != ASTRO_SUCCESS) seasons.status = status; | ||
| 7113 | |||
| 7114 | return seasons; | ||
| 7115 | } | ||
| 7116 | |||
| 7117 | /** | ||
| 7118 | * @brief Returns the angle between the given body and the Sun, as seen from the Earth. | ||
| 7119 | * | ||
| 7120 | * This function calculates the angular separation between the given body and the Sun, | ||
| 7121 | * as seen from the center of the Earth. This angle is helpful for determining how | ||
| 7122 | * easy it is to see the body away from the glare of the Sun. | ||
| 7123 | * | ||
| 7124 | * @param body | ||
| 7125 | * The celestial body whose angle from the Sun is to be measured. | ||
| 7126 | * Not allowed to be `BODY_EARTH`. | ||
| 7127 | * | ||
| 7128 | * @param time | ||
| 7129 | * The time at which the observation is made. | ||
| 7130 | * | ||
| 7131 | * @return | ||
| 7132 | * If successful, the returned structure contains `ASTRO_SUCCESS` in the `status` field | ||
| 7133 | * and `angle` holds the angle in degrees between the Sun and the specified body as | ||
| 7134 | * seen from the center of the Earth. | ||
| 7135 | * If an error occurs, the `status` field contains a value other than `ASTRO_SUCCESS` | ||
| 7136 | * that indicates the error condition. | ||
| 7137 | */ | ||
| 7138 | astro_angle_result_t Astronomy_AngleFromSun(astro_body_t body, astro_time_t time) | ||
| 7139 | { | ||
| 7140 | astro_vector_t sv, bv; | ||
| 7141 | |||
| 7142 | if (body == BODY_EARTH) | ||
| 7143 | return AngleError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 7144 | |||
| 7145 | sv = Astronomy_GeoVector(BODY_SUN, time, ABERRATION); | ||
| 7146 | if (sv.status != ASTRO_SUCCESS) | ||
| 7147 | return AngleError(sv.status); | ||
| 7148 | |||
| 7149 | bv = Astronomy_GeoVector(body, time, ABERRATION); | ||
| 7150 | if (bv.status != ASTRO_SUCCESS) | ||
| 7151 | return AngleError(bv.status); | ||
| 7152 | |||
| 7153 | return Astronomy_AngleBetween(sv, bv); | ||
| 7154 | } | ||
| 7155 | |||
| 7156 | /** | ||
| 7157 | * @brief | ||
| 7158 | * Determines visibility of a celestial body relative to the Sun, as seen from the Earth. | ||
| 7159 | * | ||
| 7160 | * This function returns an #astro_elongation_t structure, which provides the following | ||
| 7161 | * information about the given celestial body at the given time: | ||
| 7162 | * | ||
| 7163 | * - `visibility` is an enumerated type that specifies whether the body is more easily seen | ||
| 7164 | * in the morning before sunrise, or in the evening after sunset. | ||
| 7165 | * | ||
| 7166 | * - `elongation` is the angle in degrees between two vectors: one from the center of the Earth to the | ||
| 7167 | * center of the Sun, the other from the center of the Earth to the center of the specified body. | ||
| 7168 | * This angle indicates how far away the body is from the glare of the Sun. | ||
| 7169 | * The elongation angle is always in the range [0, 180]. | ||
| 7170 | * | ||
| 7171 | * - `ecliptic_separation` is the absolute value of the difference between the body's ecliptic longitude | ||
| 7172 | * and the Sun's ecliptic longitude, both as seen from the center of the Earth. This angle measures | ||
| 7173 | * around the plane of the Earth's orbit, and ignores how far above or below that plane the body is. | ||
| 7174 | * The ecliptic separation is measured in degrees and is always in the range [0, 180]. | ||
| 7175 | * | ||
| 7176 | * @param body | ||
| 7177 | * The celestial body whose visibility is to be calculated. | ||
| 7178 | * | ||
| 7179 | * @param time | ||
| 7180 | * The date and time of the observation. | ||
| 7181 | * | ||
| 7182 | * @return | ||
| 7183 | * If successful, the `status` field in the returned structure contains `ASTRO_SUCCESS` | ||
| 7184 | * and all the other fields in the structure are valid. On failure, `status` contains | ||
| 7185 | * some other value as an error code and the other fields contain invalid values. | ||
| 7186 | */ | ||
| 7187 | astro_elongation_t Astronomy_Elongation(astro_body_t body, astro_time_t time) | ||
| 7188 | { | ||
| 7189 | astro_elongation_t result; | ||
| 7190 | astro_angle_result_t angres; | ||
| 7191 | |||
| 7192 | angres = Astronomy_PairLongitude(body, BODY_SUN, time); | ||
| 7193 | if (angres.status != ASTRO_SUCCESS) | ||
| 7194 | return ElongError(angres.status); | ||
| 7195 | |||
| 7196 | if (angres.angle > 180.0) | ||
| 7197 | { | ||
| 7198 | result.visibility = VISIBLE_MORNING; | ||
| 7199 | result.ecliptic_separation = 360.0 - angres.angle; | ||
| 7200 | } | ||
| 7201 | else | ||
| 7202 | { | ||
| 7203 | result.visibility = VISIBLE_EVENING; | ||
| 7204 | result.ecliptic_separation = angres.angle; | ||
| 7205 | } | ||
| 7206 | |||
| 7207 | angres = Astronomy_AngleFromSun(body, time); | ||
| 7208 | if (angres.status != ASTRO_SUCCESS) | ||
| 7209 | return ElongError(angres.status); | ||
| 7210 | |||
| 7211 | result.elongation = angres.angle; | ||
| 7212 | result.time = time; | ||
| 7213 | result.status = ASTRO_SUCCESS; | ||
| 7214 | |||
| 7215 | return result; | ||
| 7216 | } | ||
| 7217 | |||
| 7218 | static astro_func_result_t neg_elong_slope(void *context, astro_time_t time) | ||
| 7219 | { | ||
| 7220 | static const double dt = 0.1; | ||
| 7221 | astro_angle_result_t e1, e2; | ||
| 7222 | astro_func_result_t result; | ||
| 7223 | astro_body_t body = *((astro_body_t *)context); | ||
| 7224 | astro_time_t t1 = Astronomy_AddDays(time, -dt/2.0); | ||
| 7225 | astro_time_t t2 = Astronomy_AddDays(time, +dt/2.0); | ||
| 7226 | |||
| 7227 | e1 = Astronomy_AngleFromSun(body, t1); | ||
| 7228 | if (e1.status != ASTRO_SUCCESS) | ||
| 7229 | return FuncError(e1.status); | ||
| 7230 | |||
| 7231 | e2 = Astronomy_AngleFromSun(body, t2); | ||
| 7232 | if (e2.status) | ||
| 7233 | return FuncError(e2.status); | ||
| 7234 | |||
| 7235 | result.value = (e1.angle - e2.angle)/dt; | ||
| 7236 | result.status = ASTRO_SUCCESS; | ||
| 7237 | return result; | ||
| 7238 | } | ||
| 7239 | |||
| 7240 | /** | ||
| 7241 | * @brief | ||
| 7242 | * Finds a date and time when Mercury or Venus reaches its maximum angle from the Sun as seen from the Earth. | ||
| 7243 | * | ||
| 7244 | * Mercury and Venus are are often difficult to observe because they are closer to the Sun than the Earth is. | ||
| 7245 | * Mercury especially is almost always impossible to see because it gets lost in the Sun's glare. | ||
| 7246 | * The best opportunities for spotting Mercury, and the best opportunities for viewing Venus through | ||
| 7247 | * a telescope without atmospheric interference, are when these planets reach maximum elongation. | ||
| 7248 | * These are events where the planets reach the maximum angle from the Sun as seen from the Earth. | ||
| 7249 | * | ||
| 7250 | * This function solves for those times, reporting the next maximum elongation event's date and time, | ||
| 7251 | * the elongation value itself, the relative longitude with the Sun, and whether the planet is best | ||
| 7252 | * observed in the morning or evening. See #Astronomy_Elongation for more details about the returned structure. | ||
| 7253 | * | ||
| 7254 | * @param body | ||
| 7255 | * Either `BODY_MERCURY` or `BODY_VENUS`. Any other value will fail with the error `ASTRO_INVALID_BODY`. | ||
| 7256 | * To find the best viewing opportunites for planets farther from the Sun than the Earth is (Mars through Pluto) | ||
| 7257 | * use #Astronomy_SearchRelativeLongitude to find the next opposition event. | ||
| 7258 | * | ||
| 7259 | * @param startTime | ||
| 7260 | * The date and time at which to begin the search. The maximum elongation event found will always | ||
| 7261 | * be the first one that occurs after this date and time. | ||
| 7262 | * | ||
| 7263 | * @return | ||
| 7264 | * If successful, the `status` field of the returned structure will be `ASTRO_SUCCESS` | ||
| 7265 | * and the other structure fields will be valid. Otherwise, `status` will contain | ||
| 7266 | * some other value indicating an error. | ||
| 7267 | */ | ||
| 7268 | astro_elongation_t Astronomy_SearchMaxElongation(astro_body_t body, astro_time_t startTime) | ||
| 7269 | { | ||
| 7270 | double s1, s2; | ||
| 7271 | int iter; | ||
| 7272 | astro_angle_result_t plon, elon; | ||
| 7273 | astro_time_t t_start; | ||
| 7274 | double rlon, rlon_lo, rlon_hi, adjust_days; | ||
| 7275 | astro_func_result_t syn; | ||
| 7276 | astro_search_result_t search1, search2, searchx; | ||
| 7277 | astro_time_t t1, t2; | ||
| 7278 | astro_func_result_t m1, m2; | ||
| 7279 | |||
| 7280 | /* Determine the range of relative longitudes within which maximum elongation can occur for this planet. */ | ||
| 7281 | switch (body) | ||
| 7282 | { | ||
| 7283 | case BODY_MERCURY: | ||
| 7284 | s1 = 50.0; | ||
| 7285 | s2 = 85.0; | ||
| 7286 | break; | ||
| 7287 | |||
| 7288 | case BODY_VENUS: | ||
| 7289 | s1 = 40.0; | ||
| 7290 | s2 = 50.0; | ||
| 7291 | break; | ||
| 7292 | |||
| 7293 | default: | ||
| 7294 | /* SearchMaxElongation works for Mercury and Venus only. */ | ||
| 7295 | return ElongError(ASTRO_INVALID_BODY); | ||
| 7296 | } | ||
| 7297 | |||
| 7298 | syn = SynodicPeriod(body); | ||
| 7299 | if (syn.status != ASTRO_SUCCESS) | ||
| 7300 | return ElongError(syn.status); | ||
| 7301 | |||
| 7302 | iter = 0; | ||
| 7303 | while (++iter <= 2) | ||
| 7304 | { | ||
| 7305 | plon = Astronomy_EclipticLongitude(body, startTime); | ||
| 7306 | if (plon.status != ASTRO_SUCCESS) | ||
| 7307 | return ElongError(plon.status); | ||
| 7308 | |||
| 7309 | elon = Astronomy_EclipticLongitude(BODY_EARTH, startTime); | ||
| 7310 | if (elon.status != ASTRO_SUCCESS) | ||
| 7311 | return ElongError(elon.status); | ||
| 7312 | |||
| 7313 | rlon = LongitudeOffset(plon.angle - elon.angle); /* clamp to (-180, +180] */ | ||
| 7314 | |||
| 7315 | /* The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees */ | ||
| 7316 | /* because there is a cusp there that causes a discontinuity in the derivative. */ | ||
| 7317 | /* So we need to guard against searching near such times. */ | ||
| 7318 | if (rlon >= -s1 && rlon < +s1) | ||
| 7319 | { | ||
| 7320 | /* Seek to the window [+s1, +s2]. */ | ||
| 7321 | adjust_days = 0.0; | ||
| 7322 | /* Search forward for the time t1 when rel lon = +s1. */ | ||
| 7323 | rlon_lo = +s1; | ||
| 7324 | /* Search forward for the time t2 when rel lon = +s2. */ | ||
| 7325 | rlon_hi = +s2; | ||
| 7326 | } | ||
| 7327 | else if (rlon > +s2 || rlon < -s2) | ||
| 7328 | { | ||
| 7329 | /* Seek to the next search window at [-s2, -s1]. */ | ||
| 7330 | adjust_days = 0.0; | ||
| 7331 | /* Search forward for the time t1 when rel lon = -s2. */ | ||
| 7332 | rlon_lo = -s2; | ||
| 7333 | /* Search forward for the time t2 when rel lon = -s1. */ | ||
| 7334 | rlon_hi = -s1; | ||
| 7335 | } | ||
| 7336 | else if (rlon >= 0.0) | ||
| 7337 | { | ||
| 7338 | /* rlon must be in the middle of the window [+s1, +s2]. */ | ||
| 7339 | /* Search BACKWARD for the time t1 when rel lon = +s1. */ | ||
| 7340 | adjust_days = -syn.value / 4.0; | ||
| 7341 | rlon_lo = +s1; | ||
| 7342 | rlon_hi = +s2; | ||
| 7343 | /* Search forward from t1 to find t2 such that rel lon = +s2. */ | ||
| 7344 | } | ||
| 7345 | else | ||
| 7346 | { | ||
| 7347 | /* rlon must be in the middle of the window [-s2, -s1]. */ | ||
| 7348 | /* Search BACKWARD for the time t1 when rel lon = -s2. */ | ||
| 7349 | adjust_days = -syn.value / 4.0; | ||
| 7350 | rlon_lo = -s2; | ||
| 7351 | /* Search forward from t1 to find t2 such that rel lon = -s1. */ | ||
| 7352 | rlon_hi = -s1; | ||
| 7353 | } | ||
| 7354 | |||
| 7355 | t_start = Astronomy_AddDays(startTime, adjust_days); | ||
| 7356 | |||
| 7357 | search1 = Astronomy_SearchRelativeLongitude(body, rlon_lo, t_start); | ||
| 7358 | if (search1.status != ASTRO_SUCCESS) | ||
| 7359 | return ElongError(search1.status); | ||
| 7360 | t1 = search1.time; | ||
| 7361 | |||
| 7362 | search2 = Astronomy_SearchRelativeLongitude(body, rlon_hi, t1); | ||
| 7363 | if (search2.status != ASTRO_SUCCESS) | ||
| 7364 | return ElongError(search2.status); | ||
| 7365 | t2 = search2.time; | ||
| 7366 | |||
| 7367 | /* Now we have a time range [t1,t2] that brackets a maximum elongation event. */ | ||
| 7368 | /* Confirm the bracketing. */ | ||
| 7369 | m1 = neg_elong_slope(&body, t1); | ||
| 7370 | if (m1.status != ASTRO_SUCCESS) | ||
| 7371 | return ElongError(m1.status); | ||
| 7372 | |||
| 7373 | if (m1.value >= 0) | ||
| 7374 | return ElongError(ASTRO_INTERNAL_ERROR); /* there is a bug in the bracketing algorithm! */ | ||
| 7375 | |||
| 7376 | m2 = neg_elong_slope(&body, t2); | ||
| 7377 | if (m2.status != ASTRO_SUCCESS) | ||
| 7378 | return ElongError(m2.status); | ||
| 7379 | |||
| 7380 | if (m2.value <= 0) | ||
| 7381 | return ElongError(ASTRO_INTERNAL_ERROR); /* there is a bug in the bracketing algorithm! */ | ||
| 7382 | |||
| 7383 | /* Use the generic search algorithm to home in on where the slope crosses from negative to positive. */ | ||
| 7384 | searchx = Astronomy_Search(neg_elong_slope, &body, t1, t2, 10.0); | ||
| 7385 | if (searchx.status != ASTRO_SUCCESS) | ||
| 7386 | return ElongError(searchx.status); | ||
| 7387 | |||
| 7388 | if (searchx.time.tt >= startTime.tt) | ||
| 7389 | return Astronomy_Elongation(body, searchx.time); | ||
| 7390 | |||
| 7391 | /* This event is in the past (earlier than startTime). */ | ||
| 7392 | /* We need to search forward from t2 to find the next possible window. */ | ||
| 7393 | /* We never need to search more than twice. */ | ||
| 7394 | startTime = Astronomy_AddDays(t2, 1.0); | ||
| 7395 | } | ||
| 7396 | |||
| 7397 | return ElongError(ASTRO_SEARCH_FAILURE); | ||
| 7398 | } | ||
| 7399 | |||
| 7400 | |||
| 7401 | /** | ||
| 7402 | * @brief Returns one body's ecliptic longitude with respect to another, as seen from the Earth. | ||
| 7403 | * | ||
| 7404 | * This function determines where one body appears around the ecliptic plane | ||
| 7405 | * (the plane of the Earth's orbit around the Sun) as seen from the Earth, | ||
| 7406 | * relative to the another body's apparent position. | ||
| 7407 | * The function returns an angle in the half-open range [0, 360) degrees. | ||
| 7408 | * The value is the ecliptic longitude of `body1` relative to the ecliptic | ||
| 7409 | * longitude of `body2`. | ||
| 7410 | * | ||
| 7411 | * The angle is 0 when the two bodies are at the same ecliptic longitude | ||
| 7412 | * as seen from the Earth. The angle increases in the prograde direction | ||
| 7413 | * (the direction that the planets orbit the Sun and the Moon orbits the Earth). | ||
| 7414 | * | ||
| 7415 | * When the angle is 180 degrees, it means the two bodies appear on opposite sides | ||
| 7416 | * of the sky for an Earthly observer. | ||
| 7417 | * | ||
| 7418 | * Neither `body1` nor `body2` is allowed to be `BODY_EARTH`. | ||
| 7419 | * If this happens, the function fails with the error code `ASTRO_EARTH_NOT_ALLOWED`. | ||
| 7420 | * | ||
| 7421 | * @param body1 | ||
| 7422 | * The first body, whose longitude is to be found relative to the second body. | ||
| 7423 | * | ||
| 7424 | * @param body2 | ||
| 7425 | * The second body, relative to which the longitude of the first body is to be found. | ||
| 7426 | * | ||
| 7427 | * @param time | ||
| 7428 | * The date and time of the observation. | ||
| 7429 | * | ||
| 7430 | * @return | ||
| 7431 | * On success, the `status` field in the returned structure holds `ASTRO_SUCCESS` and | ||
| 7432 | * the `angle` field holds a value in the range [0, 360). | ||
| 7433 | * On failure, the `status` field contains some other value indicating an error condition. | ||
| 7434 | */ | ||
| 7435 | astro_angle_result_t Astronomy_PairLongitude( | ||
| 7436 | astro_body_t body1, | ||
| 7437 | astro_body_t body2, | ||
| 7438 | astro_time_t time) | ||
| 7439 | { | ||
| 7440 | astro_vector_t vector1, vector2; | ||
| 7441 | astro_ecliptic_t eclip1, eclip2; | ||
| 7442 | astro_angle_result_t result; | ||
| 7443 | |||
| 7444 | if (body1 == BODY_EARTH || body2 == BODY_EARTH) | ||
| 7445 | return AngleError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 7446 | |||
| 7447 | vector1 = Astronomy_GeoVector(body1, time, NO_ABERRATION); | ||
| 7448 | eclip1 = Astronomy_Ecliptic(vector1); /* checks for errors in vector1 */ | ||
| 7449 | if (eclip1.status != ASTRO_SUCCESS) | ||
| 7450 | return AngleError(eclip1.status); | ||
| 7451 | |||
| 7452 | vector2 = Astronomy_GeoVector(body2, time, NO_ABERRATION); | ||
| 7453 | eclip2 = Astronomy_Ecliptic(vector2); /* checks for errors in vector2 */ | ||
| 7454 | if (eclip2.status != ASTRO_SUCCESS) | ||
| 7455 | return AngleError(eclip2.status); | ||
| 7456 | |||
| 7457 | result.status = ASTRO_SUCCESS; | ||
| 7458 | result.angle = NormalizeLongitude(eclip1.elon - eclip2.elon); | ||
| 7459 | return result; | ||
| 7460 | } | ||
| 7461 | |||
| 7462 | |||
| 7463 | /** | ||
| 7464 | * @brief | ||
| 7465 | * Returns the Moon's phase as an angle from 0 to 360 degrees. | ||
| 7466 | * | ||
| 7467 | * This function determines the phase of the Moon using its apparent | ||
| 7468 | * ecliptic longitude relative to the Sun, as seen from the center of the Earth. | ||
| 7469 | * Certain values of the angle have conventional definitions: | ||
| 7470 | * | ||
| 7471 | * - 0 = new moon | ||
| 7472 | * - 90 = first quarter | ||
| 7473 | * - 180 = full moon | ||
| 7474 | * - 270 = third quarter | ||
| 7475 | * | ||
| 7476 | * @param time | ||
| 7477 | * The date and time of the observation. | ||
| 7478 | * | ||
| 7479 | * @return | ||
| 7480 | * On success, the function returns the angle as described in the function remarks | ||
| 7481 | * in the `angle` field and `ASTRO_SUCCESS` in the `status` field. | ||
| 7482 | * The function should always succeed, but it is a good idea for callers to check | ||
| 7483 | * the `status` field in the returned structure. | ||
| 7484 | * Any other value in `status` indicates a failure that should be | ||
| 7485 | * [reported as an issue](https://github.com/cosinekitty/astronomy/issues). | ||
| 7486 | */ | ||
| 7487 | astro_angle_result_t Astronomy_MoonPhase(astro_time_t time) | ||
| 7488 | { | ||
| 7489 | return Astronomy_PairLongitude(BODY_MOON, BODY_SUN, time); | ||
| 7490 | } | ||
| 7491 | |||
| 7492 | static astro_func_result_t moon_offset(void *context, astro_time_t time) | ||
| 7493 | { | ||
| 7494 | astro_func_result_t result; | ||
| 7495 | double targetLon = *((double *)context); | ||
| 7496 | astro_angle_result_t angres = Astronomy_MoonPhase(time); | ||
| 7497 | if (angres.status != ASTRO_SUCCESS) | ||
| 7498 | return FuncError(angres.status); | ||
| 7499 | result.value = LongitudeOffset(angres.angle - targetLon); | ||
| 7500 | result.status = ASTRO_SUCCESS; | ||
| 7501 | return result; | ||
| 7502 | } | ||
| 7503 | |||
| 7504 | /** | ||
| 7505 | * @brief | ||
| 7506 | * Searches for the time that the Moon reaches a specified phase. | ||
| 7507 | * | ||
| 7508 | * Lunar phases are conventionally defined in terms of the Moon's geocentric ecliptic | ||
| 7509 | * longitude with respect to the Sun's geocentric ecliptic longitude. | ||
| 7510 | * When the Moon and the Sun have the same longitude, that is defined as a new moon. | ||
| 7511 | * When their longitudes are 180 degrees apart, that is defined as a full moon. | ||
| 7512 | * | ||
| 7513 | * This function searches for any value of the lunar phase expressed as an | ||
| 7514 | * angle in degrees in the range [0, 360). | ||
| 7515 | * | ||
| 7516 | * If you want to iterate through lunar quarters (new moon, first quarter, full moon, third quarter) | ||
| 7517 | * it is much easier to call the functions #Astronomy_SearchMoonQuarter and #Astronomy_NextMoonQuarter. | ||
| 7518 | * This function is useful for finding general phase angles outside those four quarters. | ||
| 7519 | * | ||
| 7520 | * @param targetLon | ||
| 7521 | * The difference in geocentric longitude between the Sun and Moon | ||
| 7522 | * that specifies the lunar phase being sought. This can be any value | ||
| 7523 | * in the range [0, 360). Certain values have conventional names: | ||
| 7524 | * 0 = new moon, 90 = first quarter, 180 = full moon, 270 = third quarter. | ||
| 7525 | * | ||
| 7526 | * @param startTime | ||
| 7527 | * The beginning of the time window in which to search for the Moon reaching the specified phase. | ||
| 7528 | * | ||
| 7529 | * @param limitDays | ||
| 7530 | * The number of days away from `startTime` that limits the time window for the search. | ||
| 7531 | * If the value is negative, the search is performed into the past from `startTime`. | ||
| 7532 | * Otherwise, the search is performed into the future from `startTime`. | ||
| 7533 | * | ||
| 7534 | * @return | ||
| 7535 | * On success, the `status` field in the returned structure holds `ASTRO_SUCCESS` and | ||
| 7536 | * the `time` field holds the date and time when the Moon reaches the target longitude. | ||
| 7537 | * On failure, `status` holds some other value as an error code. | ||
| 7538 | * One possible error code is `ASTRO_NO_MOON_QUARTER` if `startTime` and `limitDays` | ||
| 7539 | * do not enclose the desired event. See remarks in #Astronomy_Search for other possible | ||
| 7540 | * error codes. | ||
| 7541 | */ | ||
| 7542 | astro_search_result_t Astronomy_SearchMoonPhase(double targetLon, astro_time_t startTime, double limitDays) | ||
| 7543 | { | ||
| 7544 | /* | ||
| 7545 | To avoid discontinuities in the moon_offset function causing problems, | ||
| 7546 | we need to approximate when that function will next return 0. | ||
| 7547 | We probe it with the start time and take advantage of the fact | ||
| 7548 | that every lunar phase repeats roughly every 29.5 days. | ||
| 7549 | There is a surprising uncertainty in the quarter timing, | ||
| 7550 | due to the eccentricity of the moon's orbit. | ||
| 7551 | I have seen more than 0.9 days away from the simple prediction. | ||
| 7552 | To be safe, we take the predicted time of the event and search | ||
| 7553 | +/-1.5 days around it (a 3-day wide window). | ||
| 7554 | Return ASTRO_NO_MOON_QUARTER if the final result goes beyond limitDays after startTime. | ||
| 7555 | */ | ||
| 7556 | const double uncertainty = 1.5; | ||
| 7557 | astro_func_result_t funcres; | ||
| 7558 | double ya, est_dt, dt1, dt2; | ||
| 7559 | astro_time_t t1, t2; | ||
| 7560 | |||
| 7561 | funcres = moon_offset(&targetLon, startTime); | ||
| 7562 | if (funcres.status != ASTRO_SUCCESS) | ||
| 7563 | return SearchError(funcres.status); | ||
| 7564 | |||
| 7565 | ya = funcres.value; | ||
| 7566 | if (limitDays < 0.0) | ||
| 7567 | { | ||
| 7568 | /* Search backward in time. */ | ||
| 7569 | if (ya < 0.0) ya += 360.0; | ||
| 7570 | est_dt = -(MEAN_SYNODIC_MONTH * ya) / 360.0; | ||
| 7571 | dt1 = est_dt - uncertainty; | ||
| 7572 | dt2 = est_dt + uncertainty; | ||
| 7573 | if (dt2 < limitDays) | ||
| 7574 | return SearchError(ASTRO_NO_MOON_QUARTER); /* not possible for moon phase to occur within specified window (too short) */ | ||
| 7575 | if (dt1 < limitDays) | ||
| 7576 | dt1 = limitDays; | ||
| 7577 | } | ||
| 7578 | else | ||
| 7579 | { | ||
| 7580 | /* Search forward in time. */ | ||
| 7581 | if (ya > 0.0) ya -= 360.0; | ||
| 7582 | est_dt = -(MEAN_SYNODIC_MONTH * ya) / 360.0; | ||
| 7583 | dt1 = est_dt - uncertainty; | ||
| 7584 | dt2 = est_dt + uncertainty; | ||
| 7585 | if (dt1 > limitDays) | ||
| 7586 | return SearchError(ASTRO_NO_MOON_QUARTER); /* not possible for moon phase to occur within specified window (too short) */ | ||
| 7587 | if (dt2 > limitDays) | ||
| 7588 | dt2 = limitDays; | ||
| 7589 | } | ||
| 7590 | t1 = Astronomy_AddDays(startTime, dt1); | ||
| 7591 | t2 = Astronomy_AddDays(startTime, dt2); | ||
| 7592 | return Astronomy_Search(moon_offset, &targetLon, t1, t2, 0.1); | ||
| 7593 | } | ||
| 7594 | |||
| 7595 | /** | ||
| 7596 | * @brief | ||
| 7597 | * Finds the first lunar quarter after the specified date and time. | ||
| 7598 | * | ||
| 7599 | * A lunar quarter is one of the following four lunar phase events: | ||
| 7600 | * new moon, first quarter, full moon, third quarter. | ||
| 7601 | * This function finds the lunar quarter that happens soonest | ||
| 7602 | * after the specified date and time. | ||
| 7603 | * | ||
| 7604 | * To continue iterating through consecutive lunar quarters, call this function once, | ||
| 7605 | * followed by calls to #Astronomy_NextMoonQuarter as many times as desired. | ||
| 7606 | * | ||
| 7607 | * @param startTime | ||
| 7608 | * The date and time at which to start the search. | ||
| 7609 | * | ||
| 7610 | * @return | ||
| 7611 | * This function should always succeed, indicated by the `status` field | ||
| 7612 | * in the returned structure holding `ASTRO_SUCCESS`. Any other value indicates | ||
| 7613 | * an internal error, which should be [reported as an issue](https://github.com/cosinekitty/astronomy/issues). | ||
| 7614 | * To be safe, calling code should always check the `status` field for errors. | ||
| 7615 | */ | ||
| 7616 | astro_moon_quarter_t Astronomy_SearchMoonQuarter(astro_time_t startTime) | ||
| 7617 | { | ||
| 7618 | astro_moon_quarter_t mq; | ||
| 7619 | astro_angle_result_t angres; | ||
| 7620 | astro_search_result_t srchres; | ||
| 7621 | |||
| 7622 | /* Determine what the next quarter phase will be. */ | ||
| 7623 | angres = Astronomy_MoonPhase(startTime); | ||
| 7624 | if (angres.status != ASTRO_SUCCESS) | ||
| 7625 | return MoonQuarterError(angres.status); | ||
| 7626 | |||
| 7627 | mq.quarter = (1 + (int)floor(angres.angle / 90.0)) % 4; | ||
| 7628 | srchres = Astronomy_SearchMoonPhase(90.0 * mq.quarter, startTime, 10.0); | ||
| 7629 | if (srchres.status != ASTRO_SUCCESS) | ||
| 7630 | return MoonQuarterError(srchres.status); | ||
| 7631 | |||
| 7632 | mq.status = ASTRO_SUCCESS; | ||
| 7633 | mq.time = srchres.time; | ||
| 7634 | return mq; | ||
| 7635 | } | ||
| 7636 | |||
| 7637 | /** | ||
| 7638 | * @brief | ||
| 7639 | * Continues searching for lunar quarters from a previous search. | ||
| 7640 | * | ||
| 7641 | * After calling #Astronomy_SearchMoonQuarter, this function can be called | ||
| 7642 | * one or more times to continue finding consecutive lunar quarters. | ||
| 7643 | * This function finds the next consecutive moon quarter event after the one passed in as the parameter `mq`. | ||
| 7644 | * | ||
| 7645 | * @param mq | ||
| 7646 | * A value returned by a prior call to #Astronomy_SearchMoonQuarter or #Astronomy_NextMoonQuarter. | ||
| 7647 | * | ||
| 7648 | * @return | ||
| 7649 | * If `mq` is valid, this function should always succeed, indicated by the `status` field | ||
| 7650 | * in the returned structure holding `ASTRO_SUCCESS`. Any other value indicates | ||
| 7651 | * an internal error, which (after confirming that `mq` is valid) should be | ||
| 7652 | * [reported as an issue](https://github.com/cosinekitty/astronomy/issues). | ||
| 7653 | * To be safe, calling code should always check the `status` field for errors. | ||
| 7654 | */ | ||
| 7655 | astro_moon_quarter_t Astronomy_NextMoonQuarter(astro_moon_quarter_t mq) | ||
| 7656 | { | ||
| 7657 | astro_time_t time; | ||
| 7658 | astro_moon_quarter_t next_mq; | ||
| 7659 | |||
| 7660 | if (mq.status != ASTRO_SUCCESS) | ||
| 7661 | return MoonQuarterError(ASTRO_INVALID_PARAMETER); | ||
| 7662 | |||
| 7663 | /* Skip 6 days past the previous found moon quarter to find the next one. */ | ||
| 7664 | /* This is less than the minimum possible increment. */ | ||
| 7665 | /* So far I have seen the interval well contained by the range (6.5, 8.3) days. */ | ||
| 7666 | |||
| 7667 | time = Astronomy_AddDays(mq.time, 6.0); | ||
| 7668 | next_mq = Astronomy_SearchMoonQuarter(time); | ||
| 7669 | if (next_mq.status == ASTRO_SUCCESS) | ||
| 7670 | { | ||
| 7671 | /* Verify that we found the expected moon quarter. */ | ||
| 7672 | if (next_mq.quarter != (1 + mq.quarter) % 4) | ||
| 7673 | return MoonQuarterError(ASTRO_WRONG_MOON_QUARTER); /* internal error! we found the wrong moon quarter */ | ||
| 7674 | } | ||
| 7675 | return next_mq; | ||
| 7676 | } | ||
| 7677 | |||
| 7678 | static astro_func_result_t rlon_offset(astro_body_t body, astro_time_t time, int direction, double targetRelLon) | ||
| 7679 | { | ||
| 7680 | astro_func_result_t result; | ||
| 7681 | astro_angle_result_t plon, elon; | ||
| 7682 | double diff; | ||
| 7683 | |||
| 7684 | plon = Astronomy_EclipticLongitude(body, time); | ||
| 7685 | if (plon.status != ASTRO_SUCCESS) | ||
| 7686 | return FuncError(plon.status); | ||
| 7687 | |||
| 7688 | elon = Astronomy_EclipticLongitude(BODY_EARTH, time); | ||
| 7689 | if (elon.status != ASTRO_SUCCESS) | ||
| 7690 | return FuncError(elon.status); | ||
| 7691 | |||
| 7692 | diff = direction * (elon.angle - plon.angle); | ||
| 7693 | result.value = LongitudeOffset(diff - targetRelLon); | ||
| 7694 | result.status = ASTRO_SUCCESS; | ||
| 7695 | return result; | ||
| 7696 | } | ||
| 7697 | |||
| 7698 | /** | ||
| 7699 | * @brief | ||
| 7700 | * Searches for the time when the Earth and another planet are separated by a specified angle | ||
| 7701 | * in ecliptic longitude, as seen from the Sun. | ||
| 7702 | * | ||
| 7703 | * A relative longitude is the angle between two bodies measured in the plane of the Earth's orbit | ||
| 7704 | * (the ecliptic plane). The distance of the bodies above or below the ecliptic plane is ignored. | ||
| 7705 | * If you imagine the shadow of the body cast onto the ecliptic plane, and the angle measured around | ||
| 7706 | * that plane from one body to the other in the direction the planets orbit the Sun, you will get an | ||
| 7707 | * angle somewhere between 0 and 360 degrees. This is the relative longitude. | ||
| 7708 | * | ||
| 7709 | * Given a planet other than the Earth in `body` and a time to start the search in `startTime`, | ||
| 7710 | * this function searches for the next time that the relative longitude measured from the planet | ||
| 7711 | * to the Earth is `targetRelLon`. | ||
| 7712 | * | ||
| 7713 | * Certain astronomical events are defined in terms of relative longitude between the Earth and another planet: | ||
| 7714 | * | ||
| 7715 | * - When the relative longitude is 0 degrees, it means both planets are in the same direction from the Sun. | ||
| 7716 | * For planets that orbit closer to the Sun (Mercury and Venus), this is known as *inferior conjunction*, | ||
| 7717 | * a time when the other planet becomes very difficult to see because of being lost in the Sun's glare. | ||
| 7718 | * (The only exception is in the rare event of a transit, when we see the silhouette of the planet passing | ||
| 7719 | * between the Earth and the Sun.) | ||
| 7720 | * | ||
| 7721 | * - When the relative longitude is 0 degrees and the other planet orbits farther from the Sun, | ||
| 7722 | * this is known as *opposition*. Opposition is when the planet is closest to the Earth, and | ||
| 7723 | * also when it is visible for most of the night, so it is considered the best time to observe the planet. | ||
| 7724 | * | ||
| 7725 | * - When the relative longitude is 180 degrees, it means the other planet is on the opposite side of the Sun | ||
| 7726 | * from the Earth. This is called *superior conjunction*. Like inferior conjunction, the planet is | ||
| 7727 | * very difficult to see from the Earth. Superior conjunction is possible for any planet other than the Earth. | ||
| 7728 | * | ||
| 7729 | * @param body | ||
| 7730 | * A planet other than the Earth. If `body` is not a planet other than the Earth, an error occurs. | ||
| 7731 | * | ||
| 7732 | * @param targetRelLon | ||
| 7733 | * The desired relative longitude, expressed in degrees. Must be in the range [0, 360). | ||
| 7734 | * | ||
| 7735 | * @param startTime | ||
| 7736 | * The date and time at which to begin the search. | ||
| 7737 | * | ||
| 7738 | * @return | ||
| 7739 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 7740 | * and `time` will hold the date and time of the relative longitude event. | ||
| 7741 | * Otherwise `status` will hold some other value that indicates an error condition. | ||
| 7742 | */ | ||
| 7743 | astro_search_result_t Astronomy_SearchRelativeLongitude(astro_body_t body, double targetRelLon, astro_time_t startTime) | ||
| 7744 | { | ||
| 7745 | astro_search_result_t result; | ||
| 7746 | astro_func_result_t syn; | ||
| 7747 | astro_func_result_t error_angle; | ||
| 7748 | double prev_angle; | ||
| 7749 | astro_time_t time; | ||
| 7750 | int iter, direction; | ||
| 7751 | |||
| 7752 | if (body == BODY_EARTH) | ||
| 7753 | return SearchError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 7754 | |||
| 7755 | if (body == BODY_MOON || body == BODY_SUN) | ||
| 7756 | return SearchError(ASTRO_INVALID_BODY); | ||
| 7757 | |||
| 7758 | syn = SynodicPeriod(body); | ||
| 7759 | if (syn.status != ASTRO_SUCCESS) | ||
| 7760 | return SearchError(syn.status); | ||
| 7761 | |||
| 7762 | direction = IsSuperiorPlanet(body) ? +1 : -1; | ||
| 7763 | |||
| 7764 | /* Iterate until we converge on the desired event. */ | ||
| 7765 | /* Calculate the error angle, which will be a negative number of degrees, */ | ||
| 7766 | /* meaning we are "behind" the target relative longitude. */ | ||
| 7767 | |||
| 7768 | error_angle = rlon_offset(body, startTime, direction, targetRelLon); | ||
| 7769 | if (error_angle.status != ASTRO_SUCCESS) | ||
| 7770 | return SearchError(error_angle.status); | ||
| 7771 | |||
| 7772 | if (error_angle.value > 0) | ||
| 7773 | error_angle.value -= 360; /* force searching forward in time */ | ||
| 7774 | |||
| 7775 | time = startTime; | ||
| 7776 | for (iter = 0; iter < 100; ++iter) | ||
| 7777 | { | ||
| 7778 | /* Estimate how many days in the future (positive) or past (negative) */ | ||
| 7779 | /* we have to go to get closer to the target relative longitude. */ | ||
| 7780 | double day_adjust = (-error_angle.value/360.0) * syn.value; | ||
| 7781 | time = Astronomy_AddDays(time, day_adjust); | ||
| 7782 | if (fabs(day_adjust) * SECONDS_PER_DAY < 1.0) | ||
| 7783 | { | ||
| 7784 | result.time = time; | ||
| 7785 | result.status = ASTRO_SUCCESS; | ||
| 7786 | return result; | ||
| 7787 | } | ||
| 7788 | |||
| 7789 | prev_angle = error_angle.value; | ||
| 7790 | error_angle = rlon_offset(body, time, direction, targetRelLon); | ||
| 7791 | if (error_angle.status != ASTRO_SUCCESS) | ||
| 7792 | return SearchError(error_angle.status); | ||
| 7793 | |||
| 7794 | if (fabs(prev_angle) < 30.0 && (prev_angle != error_angle.value)) | ||
| 7795 | { | ||
| 7796 | /* Improve convergence for Mercury/Mars (eccentric orbits) */ | ||
| 7797 | /* by adjusting the synodic period to more closely match the */ | ||
| 7798 | /* variable speed of both planets in this part of their respective orbits. */ | ||
| 7799 | double ratio = prev_angle / (prev_angle - error_angle.value); | ||
| 7800 | if (ratio > 0.5 && ratio < 2.0) | ||
| 7801 | syn.value *= ratio; | ||
| 7802 | } | ||
| 7803 | } | ||
| 7804 | |||
| 7805 | return SearchError(ASTRO_NO_CONVERGE); | ||
| 7806 | } | ||
| 7807 | |||
| 7808 | /** | ||
| 7809 | * @brief Searches for the time when the center of a body reaches a specified hour angle as seen by an observer on the Earth. | ||
| 7810 | * | ||
| 7811 | * The *hour angle* of a celestial body indicates its position in the sky with respect | ||
| 7812 | * to the Earth's rotation. The hour angle depends on the location of the observer on the Earth. | ||
| 7813 | * The hour angle is 0 when the body's center reaches its highest angle above the horizon in a given day. | ||
| 7814 | * The hour angle increases by 1 unit for every sidereal hour that passes after that point, up | ||
| 7815 | * to 24 sidereal hours when it reaches the highest point again. So the hour angle indicates | ||
| 7816 | * the number of hours that have passed since the most recent time that the body has culminated, | ||
| 7817 | * or reached its highest point. | ||
| 7818 | * | ||
| 7819 | * This function searches for the next or previous time a celestial body reaches the given hour angle | ||
| 7820 | * relative to the date and time specified by `startTime`. | ||
| 7821 | * To find when a body culminates, pass 0 for `hourAngle`. | ||
| 7822 | * To find when a body reaches its lowest point in the sky, pass 12 for `hourAngle`. | ||
| 7823 | * | ||
| 7824 | * Note that, especially close to the Earth's poles, a body as seen on a given day | ||
| 7825 | * may always be above the horizon or always below the horizon, so the caller cannot | ||
| 7826 | * assume that a culminating object is visible nor that an object is below the horizon | ||
| 7827 | * at its minimum altitude. | ||
| 7828 | * | ||
| 7829 | * On success, the function reports the date and time, along with the horizontal coordinates | ||
| 7830 | * of the body at that time, as seen by the given observer. | ||
| 7831 | * | ||
| 7832 | * @param body | ||
| 7833 | * The Sun, Moon, any planet other than the Earth, | ||
| 7834 | * or a user-defined star that was created by a call to #Astronomy_DefineStar. | ||
| 7835 | * | ||
| 7836 | * @param observer | ||
| 7837 | * Indicates a location on or near the surface of the Earth where the observer is located. | ||
| 7838 | * Call #Astronomy_MakeObserver to create an observer structure. | ||
| 7839 | * | ||
| 7840 | * @param hourAngle | ||
| 7841 | * An hour angle value in the range [0, 24) indicating the number of sidereal hours after the | ||
| 7842 | * body's most recent culmination. | ||
| 7843 | * | ||
| 7844 | * @param startTime | ||
| 7845 | * The date and time at which to start the search. | ||
| 7846 | * | ||
| 7847 | * @param direction | ||
| 7848 | * The direction in time to perform the search: a positive value | ||
| 7849 | * searches forward in time, a negative value searches backward in time. | ||
| 7850 | * The function will fail with `ASTRO_INVALID_PARAMETER` if `direction` is zero. | ||
| 7851 | * | ||
| 7852 | * @return | ||
| 7853 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS` | ||
| 7854 | * and the other structure fields are valid. Otherwise, `status` holds some other value | ||
| 7855 | * that indicates an error condition. | ||
| 7856 | */ | ||
| 7857 | astro_hour_angle_t Astronomy_SearchHourAngleEx( | ||
| 7858 | astro_body_t body, | ||
| 7859 | astro_observer_t observer, | ||
| 7860 | double hourAngle, | ||
| 7861 | astro_time_t startTime, | ||
| 7862 | int direction) | ||
| 7863 | { | ||
| 7864 | int iter = 0; | ||
| 7865 | astro_time_t time; | ||
| 7866 | astro_equatorial_t ofdate; | ||
| 7867 | astro_hour_angle_t result; | ||
| 7868 | double delta_sidereal_hours, delta_days, gast; | ||
| 7869 | |||
| 7870 | if (body == BODY_EARTH) | ||
| 7871 | return HourAngleError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 7872 | |||
| 7873 | if (hourAngle < 0.0 || hourAngle >= 24.0) | ||
| 7874 | return HourAngleError(ASTRO_INVALID_PARAMETER); | ||
| 7875 | |||
| 7876 | if (direction == 0) | ||
| 7877 | return HourAngleError(ASTRO_INVALID_PARAMETER); | ||
| 7878 | |||
| 7879 | time = startTime; | ||
| 7880 | for(;;) | ||
| 7881 | { | ||
| 7882 | ++iter; | ||
| 7883 | |||
| 7884 | /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ | ||
| 7885 | gast = Astronomy_SiderealTime(&time); | ||
| 7886 | |||
| 7887 | /* Obtain equatorial coordinates of date for the body. */ | ||
| 7888 | ofdate = Astronomy_Equator(body, &time, observer, EQUATOR_OF_DATE, ABERRATION); | ||
| 7889 | if (ofdate.status != ASTRO_SUCCESS) | ||
| 7890 | return HourAngleError(ofdate.status); | ||
| 7891 | |||
| 7892 | /* Calculate the adjustment needed in sidereal time */ | ||
| 7893 | /* to bring the hour angle to the desired value. */ | ||
| 7894 | |||
| 7895 | delta_sidereal_hours = fmod((hourAngle + ofdate.ra - observer.longitude/15) - gast, 24.0); | ||
| 7896 | if (iter == 1) | ||
| 7897 | { | ||
| 7898 | /* On the first iteration, always search the requested time direction. */ | ||
| 7899 | if (direction > 0) | ||
| 7900 | { | ||
| 7901 | /* Search forward in time. */ | ||
| 7902 | if (delta_sidereal_hours < 0.0) | ||
| 7903 | delta_sidereal_hours += 24.0; | ||
| 7904 | } | ||
| 7905 | else | ||
| 7906 | { | ||
| 7907 | /* Search backward in time. */ | ||
| 7908 | if (delta_sidereal_hours > 0.0) | ||
| 7909 | delta_sidereal_hours -= 24.0; | ||
| 7910 | } | ||
| 7911 | } | ||
| 7912 | else | ||
| 7913 | { | ||
| 7914 | /* On subsequent iterations, we make the smallest possible adjustment, */ | ||
| 7915 | /* either forward or backward in time. */ | ||
| 7916 | if (delta_sidereal_hours < -12.0) | ||
| 7917 | delta_sidereal_hours += 24.0; | ||
| 7918 | else if (delta_sidereal_hours > +12.0) | ||
| 7919 | delta_sidereal_hours -= 24.0; | ||
| 7920 | } | ||
| 7921 | |||
| 7922 | /* If the error is tolerable (less than 0.1 seconds), the search has succeeded. */ | ||
| 7923 | if (fabs(delta_sidereal_hours) * 3600.0 < 0.1) | ||
| 7924 | { | ||
| 7925 | result.hor = Astronomy_Horizon(&time, observer, ofdate.ra, ofdate.dec, REFRACTION_NORMAL); | ||
| 7926 | result.time = time; | ||
| 7927 | result.status = ASTRO_SUCCESS; | ||
| 7928 | return result; | ||
| 7929 | } | ||
| 7930 | |||
| 7931 | /* We need to loop another time to get more accuracy. */ | ||
| 7932 | /* Update the terrestrial time (in solar days) adjusting by sidereal time (sidereal hours). */ | ||
| 7933 | delta_days = (delta_sidereal_hours / 24.0) * SOLAR_DAYS_PER_SIDEREAL_DAY; | ||
| 7934 | time = Astronomy_AddDays(time, delta_days); | ||
| 7935 | } | ||
| 7936 | } | ||
| 7937 | |||
| 7938 | |||
| 7939 | /** | ||
| 7940 | * @brief Finds the hour angle of a body for a given observer and time. | ||
| 7941 | * | ||
| 7942 | * The *hour angle* of a celestial body indicates its position in the sky with respect | ||
| 7943 | * to the Earth's rotation. The hour angle depends on the location of the observer on the Earth. | ||
| 7944 | * The hour angle is 0 when the body's center reaches its highest angle above the horizon in a given day. | ||
| 7945 | * The hour angle increases by 1 unit for every sidereal hour that passes after that point, up | ||
| 7946 | * to 24 sidereal hours when it reaches the highest point again. So the hour angle indicates | ||
| 7947 | * the number of hours that have passed since the most recent time that the body has culminated, | ||
| 7948 | * or reached its highest point. | ||
| 7949 | * | ||
| 7950 | * @param body | ||
| 7951 | * The body whose observed hour angle is to be found. | ||
| 7952 | * | ||
| 7953 | * @param time | ||
| 7954 | * The time of the observation. | ||
| 7955 | * | ||
| 7956 | * @param observer | ||
| 7957 | * The geographic location where the observation takes place. | ||
| 7958 | * | ||
| 7959 | * @return astro_func_result_t | ||
| 7960 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS` | ||
| 7961 | * and `value` holds the hour angle in the half-open range [0, 24). | ||
| 7962 | * Otherwise, `status` is an error code that indicates failure. | ||
| 7963 | */ | ||
| 7964 | astro_func_result_t Astronomy_HourAngle(astro_body_t body, astro_time_t *time, astro_observer_t observer) | ||
| 7965 | { | ||
| 7966 | astro_func_result_t result; | ||
| 7967 | astro_equatorial_t ofdate; | ||
| 7968 | double gast; | ||
| 7969 | |||
| 7970 | if (time == NULL) | ||
| 7971 | return FuncError(ASTRO_INVALID_PARAMETER); | ||
| 7972 | |||
| 7973 | /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ | ||
| 7974 | gast = Astronomy_SiderealTime(time); | ||
| 7975 | |||
| 7976 | /* Obtain equatorial coordinates of date for the body. */ | ||
| 7977 | ofdate = Astronomy_Equator(body, time, observer, EQUATOR_OF_DATE, ABERRATION); | ||
| 7978 | if (ofdate.status != ASTRO_SUCCESS) | ||
| 7979 | return FuncError(ofdate.status); | ||
| 7980 | |||
| 7981 | result.value = fmod(observer.longitude/15 + gast - ofdate.ra, 24.0); | ||
| 7982 | if (result.value < 0.0) | ||
| 7983 | result.value += 24.0; | ||
| 7984 | result.status = ASTRO_SUCCESS; | ||
| 7985 | |||
| 7986 | return result; | ||
| 7987 | } | ||
| 7988 | |||
| 7989 | |||
| 7990 | /** @cond DOXYGEN_SKIP */ | ||
| 7991 | |||
| 7992 | typedef struct | ||
| 7993 | { | ||
| 7994 | astro_body_t body; | ||
| 7995 | int direction; // search option: +1 = rise, -1 = set | ||
| 7996 | astro_observer_t observer; | ||
| 7997 | double body_radius_au; | ||
| 7998 | double target_altitude; | ||
| 7999 | } | ||
| 8000 | context_altitude_t; | ||
| 8001 | |||
| 8002 | static const double RISE_SET_DT = 0.42; /* 10.08 hours: Nyquist-safe for 22-hour period. */ | ||
| 8003 | |||
| 8004 | typedef struct | ||
| 8005 | { | ||
| 8006 | astro_status_t status; | ||
| 8007 | astro_time_t tx; | ||
| 8008 | astro_time_t ty; | ||
| 8009 | double ax; | ||
| 8010 | double ay; | ||
| 8011 | } | ||
| 8012 | ascent_t; | ||
| 8013 | |||
| 8014 | int _AltitudeDiffCallCount; | ||
| 8015 | int _FindAscentMaxRecursionDepth; | ||
| 8016 | |||
| 8017 | /** @endcond */ | ||
| 8018 | |||
| 8019 | static astro_func_result_t altitude_diff(void *context, astro_time_t time) | ||
| 8020 | { | ||
| 8021 | astro_func_result_t result; | ||
| 8022 | astro_equatorial_t ofdate; | ||
| 8023 | astro_horizon_t hor; | ||
| 8024 | double altitude; | ||
| 8025 | const context_altitude_t *p = (const context_altitude_t *)context; | ||
| 8026 | |||
| 8027 | ++_AltitudeDiffCallCount; /* for internal performance testing */ | ||
| 8028 | |||
| 8029 | ofdate = Astronomy_Equator(p->body, &time, p->observer, EQUATOR_OF_DATE, ABERRATION); | ||
| 8030 | if (ofdate.status != ASTRO_SUCCESS) | ||
| 8031 | return FuncError(ofdate.status); | ||
| 8032 | |||
| 8033 | hor = Astronomy_Horizon(&time, p->observer, ofdate.ra, ofdate.dec, REFRACTION_NONE); | ||
| 8034 | altitude = hor.altitude + RAD2DEG*asin(p->body_radius_au / ofdate.dist); | ||
| 8035 | result.value = p->direction*(altitude - p->target_altitude); | ||
| 8036 | result.status = ASTRO_SUCCESS; | ||
| 8037 | return result; | ||
| 8038 | } | ||
| 8039 | |||
| 8040 | |||
| 8041 | static ascent_t AscentError(astro_status_t status) | ||
| 8042 | { | ||
| 8043 | ascent_t ascent; | ||
| 8044 | ascent.ax = ascent.ay = NAN; | ||
| 8045 | ascent.tx = ascent.ty = TimeError(); | ||
| 8046 | ascent.status = status; | ||
| 8047 | return ascent; | ||
| 8048 | } | ||
| 8049 | |||
| 8050 | |||
| 8051 | static ascent_t FindAscent( | ||
| 8052 | int depth, | ||
| 8053 | context_altitude_t *context, | ||
| 8054 | double max_deriv_alt, | ||
| 8055 | astro_time_t t1, | ||
| 8056 | astro_time_t t2, | ||
| 8057 | double a1, | ||
| 8058 | double a2) | ||
| 8059 | { | ||
| 8060 | ascent_t ascent; | ||
| 8061 | double da, dt, abs_a1, abs_a2; | ||
| 8062 | astro_time_t tm; | ||
| 8063 | astro_func_result_t alt; | ||
| 8064 | |||
| 8065 | /* For internal performance testing. */ | ||
| 8066 | if (depth > _FindAscentMaxRecursionDepth) | ||
| 8067 | _FindAscentMaxRecursionDepth = depth; | ||
| 8068 | |||
| 8069 | /* See if we can find any time interval where the altitude-diff function */ | ||
| 8070 | /* rises from non-positive to positive. */ | ||
| 8071 | /* Return ASTRO_SUCCESS if we do, ASTRO_SEARCH_FAILURE if we don't, or some other status for error cases. */ | ||
| 8072 | |||
| 8073 | if (a1 < 0.0 && a2 >= 0.0) | ||
| 8074 | { | ||
| 8075 | /* Trivial success case: the endpoints already rise through zero. */ | ||
| 8076 | ascent.status = ASTRO_SUCCESS; | ||
| 8077 | ascent.tx = t1; | ||
| 8078 | ascent.ty = t2; | ||
| 8079 | ascent.ax = a1; | ||
| 8080 | ascent.ay = a2; | ||
| 8081 | return ascent; | ||
| 8082 | } | ||
| 8083 | |||
| 8084 | if (a1 >= 0.0 && a2 < 0.0) | ||
| 8085 | { | ||
| 8086 | /* Trivial failure case: Assume Nyquist condition prevents an ascent. */ | ||
| 8087 | return AscentError(ASTRO_SEARCH_FAILURE); | ||
| 8088 | } | ||
| 8089 | |||
| 8090 | if (depth > 17) | ||
| 8091 | { | ||
| 8092 | /* | ||
| 8093 | Safety valve: do not allow unlimited recursion. | ||
| 8094 | This should never happen if the rest of the logic is working correctly, | ||
| 8095 | so fail the whole search if it does happen. It's a bug! | ||
| 8096 | */ | ||
| 8097 | return AscentError(ASTRO_NO_CONVERGE); | ||
| 8098 | } | ||
| 8099 | |||
| 8100 | /* | ||
| 8101 | Both altitudes are on the same side of zero: both are negative, or both are non-negative. | ||
| 8102 | There could be a convex "hill" or a concave "valley" that passes through zero. | ||
| 8103 | In polar regions sometimes there is a rise/set or set/rise pair within minutes of each other. | ||
| 8104 | For example, the Moon can be below the horizon, then the very top of it becomes | ||
| 8105 | visible (moonrise) for a few minutes, then it moves sideways and down below | ||
| 8106 | the horizon again (moonset). We want to catch these cases. | ||
| 8107 | However, for efficiency and practicality concerns, because the rise/set search itself | ||
| 8108 | has a 0.1 second threshold, we do not worry about rise/set pairs that are less than | ||
| 8109 | one second apart. These are marginal cases that are rendered highly uncertain | ||
| 8110 | anyway, due to unpredictable atmospheric refraction conditions (air temperature and pressure). | ||
| 8111 | */ | ||
| 8112 | dt = (t2.ut - t1.ut) / 2; | ||
| 8113 | if (dt * SECONDS_PER_DAY < 1.0) | ||
| 8114 | return AscentError(ASTRO_SEARCH_FAILURE); | ||
| 8115 | |||
| 8116 | /* Is it possible to reach zero from the altitude that is closer to zero? */ | ||
| 8117 | abs_a1 = fabs(a1); | ||
| 8118 | abs_a2 = fabs(a2); | ||
| 8119 | da = (abs_a1 < abs_a2) ? abs_a1 : abs_a2; | ||
| 8120 | |||
| 8121 | /* | ||
| 8122 | Without loss of generality, assume |a1| <= |a2|. | ||
| 8123 | (Reverse the argument in the case |a2| < |a1|.) | ||
| 8124 | Imagine you have to "drive" from a1 to 0, then back to a2. | ||
| 8125 | You can't go faster than max_deriv_alt. If you can't reach 0 in half the time, | ||
| 8126 | you certainly don't have time to reach 0, turn around, and still make your way | ||
| 8127 | back up to a2 (which is at least as far from 0 than a1 is) in the time interval dt. | ||
| 8128 | Therefore, the time threshold is half the time interval, or dt/2. | ||
| 8129 | */ | ||
| 8130 | if (da > max_deriv_alt*(dt / 2)) | ||
| 8131 | { | ||
| 8132 | /* Prune: the altitude cannot change fast enough to reach zero. */ | ||
| 8133 | return AscentError(ASTRO_SEARCH_FAILURE); | ||
| 8134 | } | ||
| 8135 | |||
| 8136 | /* Bisect the time interval and evaluate the altitude at the midpoint. */ | ||
| 8137 | tm = Astronomy_TimeFromDays((t1.ut + t2.ut)/2); | ||
| 8138 | alt = altitude_diff(context, tm); | ||
| 8139 | if (alt.status != ASTRO_SUCCESS) | ||
| 8140 | return AscentError(ASTRO_SEARCH_FAILURE); | ||
| 8141 | |||
| 8142 | /* Recurse to the left interval. */ | ||
| 8143 | ascent = FindAscent(1+depth, context, max_deriv_alt, t1, tm, a1, alt.value); | ||
| 8144 | if (ascent.status == ASTRO_SEARCH_FAILURE) | ||
| 8145 | { | ||
| 8146 | /* Recurse to the right interval. */ | ||
| 8147 | ascent = FindAscent(1+depth, context, max_deriv_alt, tm, t2, alt.value, a2); | ||
| 8148 | } | ||
| 8149 | |||
| 8150 | return ascent; | ||
| 8151 | } | ||
| 8152 | |||
| 8153 | |||
| 8154 | static astro_func_result_t MaxAltitudeSlope(astro_body_t body, double latitude) | ||
| 8155 | { | ||
| 8156 | astro_func_result_t result; | ||
| 8157 | double deriv_ra, deriv_dec, latrad; | ||
| 8158 | |||
| 8159 | if (!isfinite(latitude) || latitude < -90.0 || latitude > +90.0) | ||
| 8160 | { | ||
| 8161 | result.value = NAN; | ||
| 8162 | result.status = ASTRO_INVALID_PARAMETER; | ||
| 8163 | return result; | ||
| 8164 | } | ||
| 8165 | |||
| 8166 | /* | ||
| 8167 | Calculate the maximum possible rate that this body's altitude | ||
| 8168 | could change [degrees/day] as seen by this observer. | ||
| 8169 | First use experimentally determined extreme bounds for this body | ||
| 8170 | of how much topocentric RA and DEC can ever change per rate of time. | ||
| 8171 | We need minimum possible d(RA)/dt, and maximum possible magnitude of d(DEC)/dt. | ||
| 8172 | Conservatively, we round d(RA)/dt down, d(DEC)/dt up. | ||
| 8173 | Then calculate the resulting maximum possible altitude change rate. | ||
| 8174 | */ | ||
| 8175 | |||
| 8176 | switch (body) | ||
| 8177 | { | ||
| 8178 | case BODY_MOON: | ||
| 8179 | deriv_ra = +4.5; | ||
| 8180 | deriv_dec = +8.2; | ||
| 8181 | break; | ||
| 8182 | |||
| 8183 | case BODY_SUN: | ||
| 8184 | deriv_ra = +0.8; | ||
| 8185 | deriv_dec = +0.5; | ||
| 8186 | break; | ||
| 8187 | |||
| 8188 | case BODY_MERCURY: | ||
| 8189 | deriv_ra = -1.6; | ||
| 8190 | deriv_dec = +1.0; | ||
| 8191 | break; | ||
| 8192 | |||
| 8193 | case BODY_VENUS: | ||
| 8194 | deriv_ra = -0.8; | ||
| 8195 | deriv_dec = +0.6; | ||
| 8196 | break; | ||
| 8197 | |||
| 8198 | case BODY_MARS: | ||
| 8199 | deriv_ra = -0.5; | ||
| 8200 | deriv_dec = +0.4; | ||
| 8201 | break; | ||
| 8202 | |||
| 8203 | case BODY_JUPITER: | ||
| 8204 | case BODY_SATURN: | ||
| 8205 | case BODY_URANUS: | ||
| 8206 | case BODY_NEPTUNE: | ||
| 8207 | case BODY_PLUTO: | ||
| 8208 | deriv_ra = -0.2; | ||
| 8209 | deriv_dec = +0.2; | ||
| 8210 | break; | ||
| 8211 | |||
| 8212 | case BODY_EARTH: | ||
| 8213 | result.value = NAN; | ||
| 8214 | result.status = ASTRO_EARTH_NOT_ALLOWED; | ||
| 8215 | return result; | ||
| 8216 | |||
| 8217 | default: | ||
| 8218 | if (UserDefinedStar(body)) | ||
| 8219 | { | ||
| 8220 | /* | ||
| 8221 | The minimum allowed heliocentric distance of a user-defined star | ||
| 8222 | is one light-year. This can cause a tiny amount of parallax (about 0.001 degrees). | ||
| 8223 | Also, including stellar aberration (22 arcsec = 0.006 degrees), we provide a | ||
| 8224 | generous safety buffer of 0.008 degrees. | ||
| 8225 | */ | ||
| 8226 | deriv_ra = -0.008; | ||
| 8227 | deriv_dec = +0.008; | ||
| 8228 | break; | ||
| 8229 | } | ||
| 8230 | result.value = NAN; | ||
| 8231 | result.status = ASTRO_INVALID_BODY; | ||
| 8232 | return result; | ||
| 8233 | } | ||
| 8234 | |||
| 8235 | latrad = DEG2RAD * latitude; | ||
| 8236 | result.value = fabs(((360.0 / SOLAR_DAYS_PER_SIDEREAL_DAY) - deriv_ra)*cos(latrad)) + fabs(deriv_dec*sin(latrad)); | ||
| 8237 | result.status = isfinite(result.value) ? ASTRO_SUCCESS : ASTRO_INTERNAL_ERROR; | ||
| 8238 | return result; | ||
| 8239 | } | ||
| 8240 | |||
| 8241 | |||
| 8242 | static astro_search_result_t InternalSearchAltitude( | ||
| 8243 | astro_body_t body, | ||
| 8244 | astro_observer_t observer, | ||
| 8245 | astro_direction_t direction, | ||
| 8246 | astro_time_t startTime, | ||
| 8247 | double limitDays, | ||
| 8248 | double bodyRadiusAu, | ||
| 8249 | double targetAltitude) | ||
| 8250 | { | ||
| 8251 | astro_search_result_t search_result; | ||
| 8252 | astro_func_result_t func_result; | ||
| 8253 | context_altitude_t context; | ||
| 8254 | ascent_t ascent; | ||
| 8255 | astro_time_t t1, t2; | ||
| 8256 | double a1, a2, max_deriv_alt; | ||
| 8257 | |||
| 8258 | if (!isfinite(targetAltitude) || targetAltitude < -90.0 || targetAltitude > +90.0) | ||
| 8259 | return SearchError(ASTRO_INVALID_PARAMETER); | ||
| 8260 | |||
| 8261 | func_result = MaxAltitudeSlope(body, observer.latitude); | ||
| 8262 | if (func_result.status != ASTRO_SUCCESS) | ||
| 8263 | return SearchError(func_result.status); | ||
| 8264 | max_deriv_alt = func_result.value; | ||
| 8265 | |||
| 8266 | context.body = body; | ||
| 8267 | context.direction = (int)direction; | ||
| 8268 | context.observer = observer; | ||
| 8269 | context.body_radius_au = bodyRadiusAu; | ||
| 8270 | context.target_altitude = targetAltitude; | ||
| 8271 | |||
| 8272 | /* We allow searching forward or backward in time. */ | ||
| 8273 | /* But we want to keep t1 < t2, so we need a few if/else statements. */ | ||
| 8274 | t1 = t2 = startTime; | ||
| 8275 | func_result = altitude_diff(&context, t2); | ||
| 8276 | if (func_result.status != ASTRO_SUCCESS) | ||
| 8277 | return SearchError(func_result.status); | ||
| 8278 | a1 = a2 = func_result.value; | ||
| 8279 | |||
| 8280 | for(;;) | ||
| 8281 | { | ||
| 8282 | if (limitDays < 0.0) | ||
| 8283 | { | ||
| 8284 | t1 = Astronomy_AddDays(t2, -RISE_SET_DT); | ||
| 8285 | func_result = altitude_diff(&context, t1); | ||
| 8286 | if (func_result.status != ASTRO_SUCCESS) | ||
| 8287 | return SearchError(func_result.status); | ||
| 8288 | a1 = func_result.value; | ||
| 8289 | } | ||
| 8290 | else | ||
| 8291 | { | ||
| 8292 | t2 = Astronomy_AddDays(t1, +RISE_SET_DT); | ||
| 8293 | func_result = altitude_diff(&context, t2); | ||
| 8294 | if (func_result.status != ASTRO_SUCCESS) | ||
| 8295 | return SearchError(func_result.status); | ||
| 8296 | a2 = func_result.value; | ||
| 8297 | } | ||
| 8298 | |||
| 8299 | ascent = FindAscent(0, &context, max_deriv_alt, t1, t2, a1, a2); | ||
| 8300 | if (ascent.status == ASTRO_SUCCESS) | ||
| 8301 | { | ||
| 8302 | /* We found a time interval [t1, t2] that contains an alt-diff */ | ||
| 8303 | /* rising from negative a1 to non-negative a2. */ | ||
| 8304 | /* Search for the time where the root occurs. */ | ||
| 8305 | search_result = Astronomy_Search(altitude_diff, &context, ascent.tx, ascent.ty, 0.1); | ||
| 8306 | if (search_result.status == ASTRO_SUCCESS) | ||
| 8307 | { | ||
| 8308 | /* Now that we have a solution, we have to check whether it goes outside the time bounds. */ | ||
| 8309 | if (limitDays < 0.0) | ||
| 8310 | { | ||
| 8311 | if (search_result.time.ut < startTime.ut + limitDays) | ||
| 8312 | return SearchError(ASTRO_SEARCH_FAILURE); | ||
| 8313 | } | ||
| 8314 | else | ||
| 8315 | { | ||
| 8316 | if (search_result.time.ut > startTime.ut + limitDays) | ||
| 8317 | return SearchError(ASTRO_SEARCH_FAILURE); | ||
| 8318 | } | ||
| 8319 | return search_result; /* success! */ | ||
| 8320 | } | ||
| 8321 | |||
| 8322 | /* The search should have succeeded. Something is wrong with FindAscent! */ | ||
| 8323 | return SearchError(ASTRO_INTERNAL_ERROR); | ||
| 8324 | } | ||
| 8325 | else if (ascent.status == ASTRO_SEARCH_FAILURE) | ||
| 8326 | { | ||
| 8327 | /* There is no ascent in this interval, so keep searching. */ | ||
| 8328 | } | ||
| 8329 | else | ||
| 8330 | { | ||
| 8331 | /* An unexpected error occurred. Fail the search. */ | ||
| 8332 | return SearchError(ascent.status); | ||
| 8333 | } | ||
| 8334 | |||
| 8335 | if (limitDays < 0.0) | ||
| 8336 | { | ||
| 8337 | if (t1.ut < startTime.ut + limitDays) | ||
| 8338 | return SearchError(ASTRO_SEARCH_FAILURE); | ||
| 8339 | t2 = t1; | ||
| 8340 | a2 = a1; | ||
| 8341 | } | ||
| 8342 | else | ||
| 8343 | { | ||
| 8344 | if (t2.ut > startTime.ut + limitDays) | ||
| 8345 | return SearchError(ASTRO_SEARCH_FAILURE); | ||
| 8346 | t1 = t2; | ||
| 8347 | a1 = a2; | ||
| 8348 | } | ||
| 8349 | } | ||
| 8350 | } | ||
| 8351 | |||
| 8352 | |||
| 8353 | /** | ||
| 8354 | * @brief Calculates U.S. Standard Atmosphere (1976) variables as a function of elevation. | ||
| 8355 | * | ||
| 8356 | * This function calculates idealized values of pressure, temperature, and density | ||
| 8357 | * using the U.S. Standard Atmosphere (1976) model. | ||
| 8358 | * | ||
| 8359 | * See: | ||
| 8360 | * https://hbcp.chemnetbase.com/faces/documents/14_12/14_12_0001.xhtml | ||
| 8361 | * https://ntrs.nasa.gov/api/citations/19770009539/downloads/19770009539.pdf | ||
| 8362 | * https://www.ngdc.noaa.gov/stp/space-weather/online-publications/miscellaneous/us-standard-atmosphere-1976/us-standard-atmosphere_st76-1562_noaa.pdf | ||
| 8363 | * | ||
| 8364 | * @param elevationMeters | ||
| 8365 | * The elevation above sea level at which to calculate atmospheric variables. | ||
| 8366 | * The value must be at least -500 to +100000, or the function will | ||
| 8367 | * fail with status `ASTRO_INVALID_PARAMETER`. | ||
| 8368 | * | ||
| 8369 | * @return astro_atmosphere_tp0 | ||
| 8370 | */ | ||
| 8371 | astro_atmosphere_t Astronomy_Atmosphere(double elevationMeters) | ||
| 8372 | { | ||
| 8373 | astro_atmosphere_t atmos; | ||
| 8374 | const double P0 = 101325.0; /* pressure at sea level [pascals] */ | ||
| 8375 | const double T0 = 288.15; /* temperature at sea level [kelvins] */ | ||
| 8376 | const double T1 = 216.65; /* temperature between 20 km and 32 km [kelvins] */ | ||
| 8377 | |||
| 8378 | /* | ||
| 8379 | Formulas for air temperature and pressure at a height of `h` meters | ||
| 8380 | were found at: | ||
| 8381 | https://hbcp.chemnetbase.com/faces/documents/14_12/14_12_0001.xhtml | ||
| 8382 | |||
| 8383 | These in turn come from: | ||
| 8384 | 1. COESA, U.S. Standard Atmosphere, 1976, U.S. Government Printing Office, Washington, DC, 1976. | ||
| 8385 | 2. Jursa, A. S., Ed., Handbook of Geophysics and the Space Environment, Air Force Geophysics Laboratory, 1985. | ||
| 8386 | */ | ||
| 8387 | |||
| 8388 | if (!isfinite(elevationMeters) || elevationMeters < -500.0 || elevationMeters > 100000.0) | ||
| 8389 | { | ||
| 8390 | /* Invalid elevation. */ | ||
| 8391 | atmos.status = ASTRO_INVALID_PARAMETER; | ||
| 8392 | atmos.pressure = atmos.temperature = atmos.density = NAN; | ||
| 8393 | } | ||
| 8394 | else | ||
| 8395 | { | ||
| 8396 | if (elevationMeters <= 11000.0) | ||
| 8397 | { | ||
| 8398 | atmos.temperature = T0 - 0.0065*elevationMeters; | ||
| 8399 | atmos.pressure = P0 * pow(T0 / atmos.temperature, -5.25577); | ||
| 8400 | } | ||
| 8401 | else if (elevationMeters <= 20000.0) | ||
| 8402 | { | ||
| 8403 | atmos.temperature = T1; | ||
| 8404 | atmos.pressure = 22632.0 * exp(-0.00015768832 * (elevationMeters - 11000.0)); | ||
| 8405 | } | ||
| 8406 | else | ||
| 8407 | { | ||
| 8408 | atmos.temperature = T1 + 0.001*(elevationMeters - 20000.0); | ||
| 8409 | atmos.pressure = 5474.87 * pow(T1 / atmos.temperature, 34.16319); | ||
| 8410 | } | ||
| 8411 | /* The density is calculated relative to the sea level value. */ | ||
| 8412 | /* Using the ideal gas law PV=nRT, we deduce that density is proportional to P/T. */ | ||
| 8413 | atmos.density = (atmos.pressure / atmos.temperature) / (P0 / T0); | ||
| 8414 | atmos.status = ASTRO_SUCCESS; | ||
| 8415 | } | ||
| 8416 | |||
| 8417 | return atmos; | ||
| 8418 | } | ||
| 8419 | |||
| 8420 | |||
| 8421 | static double HorizonDipAngle( | ||
| 8422 | astro_observer_t observer, | ||
| 8423 | double metersAboveGround) | ||
| 8424 | { | ||
| 8425 | double phi, sinphi, cosphi, c, s, ht_km, ach, ash, radius_m; | ||
| 8426 | double k, dip; | ||
| 8427 | |||
| 8428 | /* Calculate the effective radius of the Earth at ground level below the observer. */ | ||
| 8429 | /* Correct for the Earth's oblateness. */ | ||
| 8430 | phi = observer.latitude * DEG2RAD; | ||
| 8431 | sinphi = sin(phi); | ||
| 8432 | cosphi = cos(phi); | ||
| 8433 | c = 1.0 / hypot(cosphi, sinphi*EARTH_FLATTENING); | ||
| 8434 | s = c * (EARTH_FLATTENING * EARTH_FLATTENING); | ||
| 8435 | ht_km = (observer.height - metersAboveGround) / 1000.0; /* height of ground above sea level */ | ||
| 8436 | ach = EARTH_EQUATORIAL_RADIUS_KM*c + ht_km; | ||
| 8437 | ash = EARTH_EQUATORIAL_RADIUS_KM*s + ht_km; | ||
| 8438 | radius_m = 1000.0 * hypot(ach*cosphi, ash*sinphi); | ||
| 8439 | |||
| 8440 | /* | ||
| 8441 | Correct refraction of a ray of light traveling tangent to the Earth's surface. | ||
| 8442 | Based on: https://www.largeformatphotography.info/sunmooncalc/SMCalc.js | ||
| 8443 | which in turn derives from: | ||
| 8444 | Sweer, John. 1938. The Path of a Ray of Light Tangent to the Surface of the Earth. | ||
| 8445 | Journal of the Optical Society of America 28 (September):327-329. | ||
| 8446 | */ | ||
| 8447 | |||
| 8448 | /* k = refraction index */ | ||
| 8449 | k = 0.175 * pow(1.0 - (6.5e-3/283.15)*(observer.height - (2.0/3.0)*metersAboveGround), 3.256); | ||
| 8450 | |||
| 8451 | /* Calculate how far below the observer's horizontal plane the observed horizon dips. */ | ||
| 8452 | dip = RAD2DEG * -(sqrt(2*(1 - k)*metersAboveGround / radius_m) / (1 - k)); | ||
| 8453 | return dip; | ||
| 8454 | } | ||
| 8455 | |||
| 8456 | |||
| 8457 | |||
| 8458 | /** | ||
| 8459 | * @brief Searches for the next time a celestial body rises or sets as seen by an observer on the Earth. | ||
| 8460 | * | ||
| 8461 | * This function finds the next rise or set time of the Sun, Moon, or planet other than the Earth. | ||
| 8462 | * Rise time is when the body first starts to be visible above the horizon. | ||
| 8463 | * For example, sunrise is the moment that the top of the Sun first appears to peek above the horizon. | ||
| 8464 | * Set time is the moment when the body appears to vanish below the horizon. | ||
| 8465 | * Therefore, this function adjusts for the apparent angular radius of the observed body | ||
| 8466 | * (significant only for the Sun and Moon). | ||
| 8467 | * | ||
| 8468 | * This function corrects for a typical value of atmospheric refraction, which causes celestial | ||
| 8469 | * bodies to appear higher above the horizon than they would if the Earth had no atmosphere. | ||
| 8470 | * Astronomy Engine uses a correction of 34 arcminutes. Real-world refraction varies based | ||
| 8471 | * on air temperature, pressure, and humidity; such weather-based conditions are outside | ||
| 8472 | * the scope of Astronomy Engine. | ||
| 8473 | * | ||
| 8474 | * Note that rise or set may not occur in every 24 hour period. | ||
| 8475 | * For example, near the Earth's poles, there are long periods of time where | ||
| 8476 | * the Sun stays below the horizon, never rising. | ||
| 8477 | * Also, it is possible for the Moon to rise just before midnight but not set during the subsequent 24-hour day. | ||
| 8478 | * This is because the Moon sets nearly an hour later each day due to orbiting the Earth a | ||
| 8479 | * significant amount during each rotation of the Earth. | ||
| 8480 | * Therefore callers must not assume that the function will always succeed. | ||
| 8481 | * | ||
| 8482 | * @param body | ||
| 8483 | * The Sun, Moon, any planet other than the Earth, | ||
| 8484 | * or a user-defined star that was created by a call to #Astronomy_DefineStar. | ||
| 8485 | * | ||
| 8486 | * @param observer | ||
| 8487 | * The location where observation takes place. | ||
| 8488 | * You can create an observer structure by calling #Astronomy_MakeObserver. | ||
| 8489 | * | ||
| 8490 | * @param direction | ||
| 8491 | * Either `DIRECTION_RISE` to find a rise time or `DIRECTION_SET` to find a set time. | ||
| 8492 | * | ||
| 8493 | * @param startTime | ||
| 8494 | * The date and time at which to start the search. | ||
| 8495 | * | ||
| 8496 | * @param limitDays | ||
| 8497 | * Limits how many days to search for a rise or set time, and defines | ||
| 8498 | * the direction in time to search. When `limitDays` is positive, the | ||
| 8499 | * search is performed into the future, after `startTime`. | ||
| 8500 | * When negative, the search is performed into the past, before `startTime`. | ||
| 8501 | * To limit a rise or set time to the same day, you can use a value of 1 day. | ||
| 8502 | * In cases where you want to find the next rise or set time no matter how far | ||
| 8503 | * in the future (for example, for an observer near the south pole), you can | ||
| 8504 | * pass in a larger value like 365. | ||
| 8505 | * | ||
| 8506 | * @param metersAboveGround | ||
| 8507 | * Usually the observer is located at ground level. Then this parameter | ||
| 8508 | * should be zero. But if the observer is significantly higher than ground | ||
| 8509 | * level, for example in an airplane, this parameter should be a positive | ||
| 8510 | * number indicating how far above the ground the observer is. | ||
| 8511 | * An error occurs if `metersAboveGround` is negative. | ||
| 8512 | * | ||
| 8513 | * @return | ||
| 8514 | * On success, the `status` field in the returned structure contains `ASTRO_SUCCESS` | ||
| 8515 | * and the `time` field contains the date and time of the rise or set time as requested. | ||
| 8516 | * If the `status` field contains `ASTRO_SEARCH_FAILURE`, it means the rise or set | ||
| 8517 | * event does not occur within `limitDays` days of `startTime`. This is a normal condition, | ||
| 8518 | * not an error. Any other value of `status` indicates an error of some kind. | ||
| 8519 | */ | ||
| 8520 | astro_search_result_t Astronomy_SearchRiseSetEx( | ||
| 8521 | astro_body_t body, | ||
| 8522 | astro_observer_t observer, | ||
| 8523 | astro_direction_t direction, | ||
| 8524 | astro_time_t startTime, | ||
| 8525 | double limitDays, | ||
| 8526 | double metersAboveGround) | ||
| 8527 | { | ||
| 8528 | double altitude, dip; | ||
| 8529 | double body_radius_au; | ||
| 8530 | astro_atmosphere_t atmos; | ||
| 8531 | |||
| 8532 | if (!isfinite(metersAboveGround) || (metersAboveGround < 0.0)) | ||
| 8533 | return SearchError(ASTRO_INVALID_PARAMETER); | ||
| 8534 | |||
| 8535 | switch (body) | ||
| 8536 | { | ||
| 8537 | case BODY_SUN: body_radius_au = SUN_RADIUS_AU; break; | ||
| 8538 | case BODY_MOON: body_radius_au = MOON_EQUATORIAL_RADIUS_AU; break; | ||
| 8539 | default: body_radius_au = 0.0; break; | ||
| 8540 | } | ||
| 8541 | |||
| 8542 | /* Calculate atmospheric density at ground level. */ | ||
| 8543 | atmos = Astronomy_Atmosphere(observer.height - metersAboveGround); | ||
| 8544 | if (atmos.status != ASTRO_SUCCESS) | ||
| 8545 | return SearchError(atmos.status); | ||
| 8546 | |||
| 8547 | /* Calculate the apparent angular dip of the horizon. */ | ||
| 8548 | dip = HorizonDipAngle(observer, metersAboveGround); | ||
| 8549 | |||
| 8550 | /* Correct refraction for objects near the horizon, using atmospheric density at the ground. */ | ||
| 8551 | altitude = dip - (REFRACTION_NEAR_HORIZON * atmos.density); | ||
| 8552 | |||
| 8553 | /* Search for the top of the body crossing the corrected altitude angle. */ | ||
| 8554 | return InternalSearchAltitude(body, observer, direction, startTime, limitDays, body_radius_au, altitude); | ||
| 8555 | } | ||
| 8556 | |||
| 8557 | |||
| 8558 | /** | ||
| 8559 | * @brief Finds the next time the center of a body passes through a given altitude. | ||
| 8560 | * | ||
| 8561 | * Finds when the center of the given body ascends or descends through a given | ||
| 8562 | * altitude angle, as seen by an observer at the specified location on the Earth. | ||
| 8563 | * By using the appropriate combination of `direction` and `altitude` parameters, | ||
| 8564 | * this function can be used to find when civil, nautical, or astronomical twilight | ||
| 8565 | * begins (dawn) or ends (dusk). | ||
| 8566 | * | ||
| 8567 | * Civil dawn begins before sunrise when the Sun ascends through 6 degrees below | ||
| 8568 | * the horizon. To find civil dawn, pass `DIRECTION_RISE` for `direction` and -6 for `altitude`. | ||
| 8569 | * | ||
| 8570 | * Civil dusk ends after sunset when the Sun descends through 6 degrees below the horizon. | ||
| 8571 | * To find civil dusk, pass `DIRECTION_SET` for `direction` and -6 for `altitude`. | ||
| 8572 | * | ||
| 8573 | * Nautical twilight is similar to civil twilight, only the `altitude` value should be -12 degrees. | ||
| 8574 | * | ||
| 8575 | * Astronomical twilight uses -18 degrees as the `altitude` value. | ||
| 8576 | * | ||
| 8577 | * By convention for twilight time calculations, the altitude is not corrected for | ||
| 8578 | * atmospheric refraction. This is because the target altitudes are below the horizon, | ||
| 8579 | * and refraction is not directly observable. | ||
| 8580 | * | ||
| 8581 | * `Astronomy_SearchAltitude` is not intended to find rise/set times of a body for two reasons: | ||
| 8582 | * (1) Rise/set times of the Sun or Moon are defined by their topmost visible portion, not their centers. | ||
| 8583 | * (2) Rise/set times are affected significantly by atmospheric refraction. | ||
| 8584 | * Therefore, it is better to use #Astronomy_SearchRiseSetEx to find rise/set times, which | ||
| 8585 | * corrects for both of these considerations. | ||
| 8586 | * | ||
| 8587 | * `Astronomy_SearchAltitude` will not work reliably for altitudes at or near the body's | ||
| 8588 | * maximum or minimum altitudes. To find the time a body reaches minimum or maximum altitude | ||
| 8589 | * angles, use #Astronomy_SearchHourAngleEx. | ||
| 8590 | * | ||
| 8591 | * @param body | ||
| 8592 | * The Sun, Moon, any planet other than the Earth, | ||
| 8593 | * or a user-defined star that was created by a call to #Astronomy_DefineStar. | ||
| 8594 | * | ||
| 8595 | * @param observer | ||
| 8596 | * The location where observation takes place. | ||
| 8597 | * You can create an observer structure by calling #Astronomy_MakeObserver. | ||
| 8598 | * | ||
| 8599 | * @param direction | ||
| 8600 | * Either `DIRECTION_RISE` to find when the body ascends through the altitude, | ||
| 8601 | * or `DIRECTION_SET` for when the body descends through the altitude. | ||
| 8602 | * | ||
| 8603 | * @param startTime | ||
| 8604 | * The date and time at which to start the search. | ||
| 8605 | * | ||
| 8606 | * @param limitDays | ||
| 8607 | * Limits how many days to search for the body reaching the altitude angle, | ||
| 8608 | * and defines the direction in time to search. When `limitDays` is positive, the | ||
| 8609 | * search is performed into the future, after `startTime`. | ||
| 8610 | * When negative, the search is performed into the past, before `startTime`. | ||
| 8611 | * To limit the search to the same day, you can use a value of 1 day. | ||
| 8612 | * In cases where you want to find the altitude event no matter how far | ||
| 8613 | * in the future (for example, for an observer near the south pole), you can | ||
| 8614 | * pass in a larger value like 365. | ||
| 8615 | * | ||
| 8616 | * @param altitude | ||
| 8617 | * The desired altitude angle of the body's center above (positive) | ||
| 8618 | * or below (negative) the observer's local horizon, expressed in degrees. | ||
| 8619 | * Must be in the range [-90, +90]. | ||
| 8620 | * | ||
| 8621 | * @return | ||
| 8622 | * On success, the `status` field in the returned structure contains `ASTRO_SUCCESS` | ||
| 8623 | * and the `time` field contains the date and time of the requested altitude event. | ||
| 8624 | * If the `status` field contains `ASTRO_SEARCH_FAILURE`, it means the altitude | ||
| 8625 | * event does not occur within `limitDays` days of `startTime`. This is a normal condition, | ||
| 8626 | * not an error. Any other value of `status` indicates an error of some kind. | ||
| 8627 | */ | ||
| 8628 | astro_search_result_t Astronomy_SearchAltitude( | ||
| 8629 | astro_body_t body, | ||
| 8630 | astro_observer_t observer, | ||
| 8631 | astro_direction_t direction, | ||
| 8632 | astro_time_t startTime, | ||
| 8633 | double limitDays, | ||
| 8634 | double altitude) | ||
| 8635 | { | ||
| 8636 | return InternalSearchAltitude(body, observer, direction, startTime, limitDays, 0.0, altitude); | ||
| 8637 | } | ||
| 8638 | |||
| 8639 | |||
| 8640 | static double MoonMagnitude(double phase, double helio_dist, double geo_dist) | ||
| 8641 | { | ||
| 8642 | /* https://astronomy.stackexchange.com/questions/10246/is-there-a-simple-analytical-formula-for-the-lunar-phase-brightness-curve */ | ||
| 8643 | double rad = phase * DEG2RAD; | ||
| 8644 | double rad2 = rad * rad; | ||
| 8645 | double rad4 = rad2 * rad2; | ||
| 8646 | double mag = -12.717 + 1.49*fabs(rad) + 0.0431*rad4; | ||
| 8647 | double moon_mean_distance_au = 385000.6 / KM_PER_AU; | ||
| 8648 | double geo_au = geo_dist / moon_mean_distance_au; | ||
| 8649 | mag += 5*log10(helio_dist * geo_au); | ||
| 8650 | return mag; | ||
| 8651 | } | ||
| 8652 | |||
| 8653 | static astro_status_t SaturnMagnitude( | ||
| 8654 | double phase, | ||
| 8655 | double helio_dist, | ||
| 8656 | double geo_dist, | ||
| 8657 | astro_vector_t gc, | ||
| 8658 | astro_time_t time, | ||
| 8659 | double *mag, | ||
| 8660 | double *ring_tilt) | ||
| 8661 | { | ||
| 8662 | astro_ecliptic_t eclip; | ||
| 8663 | double ir, Nr, lat, lon, tilt, sin_tilt; | ||
| 8664 | |||
| 8665 | *mag = *ring_tilt = NAN; | ||
| 8666 | |||
| 8667 | /* Based on formulas by Paul Schlyter found here: */ | ||
| 8668 | /* http://www.stjarnhimlen.se/comp/ppcomp.html#15 */ | ||
| 8669 | |||
| 8670 | /* We must handle Saturn's rings as a major component of its visual magnitude. */ | ||
| 8671 | /* Find geocentric ecliptic coordinates of Saturn. */ | ||
| 8672 | eclip = Astronomy_Ecliptic(gc); | ||
| 8673 | if (eclip.status != ASTRO_SUCCESS) | ||
| 8674 | return eclip.status; | ||
| 8675 | |||
| 8676 | ir = DEG2RAD * 28.06; /* tilt of Saturn's rings to the ecliptic, in radians */ | ||
| 8677 | Nr = DEG2RAD * (169.51 + (3.82e-5 * time.tt)); /* ascending node of Saturn's rings, in radians */ | ||
| 8678 | |||
| 8679 | /* Find tilt of Saturn's rings, as seen from Earth. */ | ||
| 8680 | lat = DEG2RAD * eclip.elat; | ||
| 8681 | lon = DEG2RAD * eclip.elon; | ||
| 8682 | tilt = asin(sin(lat)*cos(ir) - cos(lat)*sin(ir)*sin(lon-Nr)); | ||
| 8683 | sin_tilt = sin(fabs(tilt)); | ||
| 8684 | |||
| 8685 | *mag = -9.0 + 0.044*phase; | ||
| 8686 | *mag += sin_tilt*(-2.6 + 1.2*sin_tilt); | ||
| 8687 | *mag += 5.0 * log10(helio_dist * geo_dist); | ||
| 8688 | |||
| 8689 | *ring_tilt = RAD2DEG * tilt; | ||
| 8690 | |||
| 8691 | return ASTRO_SUCCESS; | ||
| 8692 | } | ||
| 8693 | |||
| 8694 | static astro_status_t VisualMagnitude( | ||
| 8695 | astro_body_t body, | ||
| 8696 | double phase, | ||
| 8697 | double helio_dist, | ||
| 8698 | double geo_dist, | ||
| 8699 | double *mag) | ||
| 8700 | { | ||
| 8701 | /* For Mercury and Venus, see: https://iopscience.iop.org/article/10.1086/430212 */ | ||
| 8702 | double c0, c1=0, c2=0, c3=0, x; | ||
| 8703 | *mag = NAN; | ||
| 8704 | switch (body) | ||
| 8705 | { | ||
| 8706 | case BODY_MERCURY: c0 = -0.60, c1 = +4.98, c2 = -4.88, c3 = +3.02; break; | ||
| 8707 | case BODY_VENUS: | ||
| 8708 | if (phase < 163.6) | ||
| 8709 | c0 = -4.47, c1 = +1.03, c2 = +0.57, c3 = +0.13; | ||
| 8710 | else | ||
| 8711 | c0 = 0.98, c1 = -1.02; | ||
| 8712 | break; | ||
| 8713 | case BODY_MARS: c0 = -1.52, c1 = +1.60; break; | ||
| 8714 | case BODY_JUPITER: c0 = -9.40, c1 = +0.50; break; | ||
| 8715 | case BODY_URANUS: c0 = -7.19, c1 = +0.25; break; | ||
| 8716 | case BODY_NEPTUNE: c0 = -6.87; break; | ||
| 8717 | case BODY_PLUTO: c0 = -1.00, c1 = +4.00; break; | ||
| 8718 | default: return ASTRO_INVALID_BODY; | ||
| 8719 | } | ||
| 8720 | |||
| 8721 | x = phase / 100; | ||
| 8722 | *mag = c0 + x*(c1 + x*(c2 + x*c3)); | ||
| 8723 | *mag += 5.0 * log10(helio_dist * geo_dist); | ||
| 8724 | return ASTRO_SUCCESS; | ||
| 8725 | } | ||
| 8726 | |||
| 8727 | /** | ||
| 8728 | * @brief | ||
| 8729 | * Finds visual magnitude, phase angle, and other illumination information about a celestial body. | ||
| 8730 | * | ||
| 8731 | * This function calculates information about how bright a celestial body appears from the Earth, | ||
| 8732 | * reported as visual magnitude, which is a smaller (or even negative) number for brighter objects | ||
| 8733 | * and a larger number for dimmer objects. | ||
| 8734 | * | ||
| 8735 | * For bodies other than the Sun, it reports a phase angle, which is the angle in degrees between | ||
| 8736 | * the Sun and the Earth, as seen from the center of the body. Phase angle indicates what fraction | ||
| 8737 | * of the body appears illuminated as seen from the Earth. For example, when the phase angle is | ||
| 8738 | * near zero, it means the body appears "full" as seen from the Earth. A phase angle approaching | ||
| 8739 | * 180 degrees means the body appears as a thin crescent as seen from the Earth. A phase angle | ||
| 8740 | * of 90 degrees means the body appears "half full". | ||
| 8741 | * For the Sun, the phase angle is always reported as 0; the Sun emits light rather than reflecting it, | ||
| 8742 | * so it doesn't have a phase angle. | ||
| 8743 | * | ||
| 8744 | * When the body is Saturn, the returned structure contains a field `ring_tilt` that holds | ||
| 8745 | * the tilt angle in degrees of Saturn's rings as seen from the Earth. A value of 0 means | ||
| 8746 | * the rings appear edge-on, and are thus nearly invisible from the Earth. The `ring_tilt` holds | ||
| 8747 | * 0 for all bodies other than Saturn. | ||
| 8748 | * | ||
| 8749 | * @param body | ||
| 8750 | * The Sun, Moon, or any planet other than the Earth. | ||
| 8751 | * | ||
| 8752 | * @param time | ||
| 8753 | * The date and time of the observation. | ||
| 8754 | * | ||
| 8755 | * @return | ||
| 8756 | * On success, the `status` field of the return structure holds `ASTRO_SUCCESS` | ||
| 8757 | * and the other structure fields are valid. | ||
| 8758 | * Any other value indicates an error, in which case the remaining structure fields are not valid. | ||
| 8759 | */ | ||
| 8760 | astro_illum_t Astronomy_Illumination(astro_body_t body, astro_time_t time) | ||
| 8761 | { | ||
| 8762 | astro_vector_t earth; /* vector from Sun to Earth */ | ||
| 8763 | astro_vector_t hc; /* vector from Sun to body */ | ||
| 8764 | astro_vector_t gc; /* vector from Earth to body */ | ||
| 8765 | double mag; /* visual magnitude */ | ||
| 8766 | astro_angle_result_t phase; /* phase angle in degrees between Earth and Sun as seen from body */ | ||
| 8767 | double helio_dist; /* distance from Sun to body */ | ||
| 8768 | double geo_dist; /* distance from Earth to body */ | ||
| 8769 | double ring_tilt = 0.0; /* Saturn's ring tilt (0 for all other bodies) */ | ||
| 8770 | astro_illum_t illum; | ||
| 8771 | astro_status_t status; | ||
| 8772 | |||
| 8773 | if (body == BODY_EARTH) | ||
| 8774 | return IllumError(ASTRO_EARTH_NOT_ALLOWED); | ||
| 8775 | |||
| 8776 | earth = CalcEarth(time); | ||
| 8777 | if (earth.status != ASTRO_SUCCESS) | ||
| 8778 | return IllumError(earth.status); | ||
| 8779 | |||
| 8780 | if (body == BODY_SUN) | ||
| 8781 | { | ||
| 8782 | gc.status = ASTRO_SUCCESS; | ||
| 8783 | gc.t = time; | ||
| 8784 | gc.x = -earth.x; | ||
| 8785 | gc.y = -earth.y; | ||
| 8786 | gc.z = -earth.z; | ||
| 8787 | |||
| 8788 | hc.status = ASTRO_SUCCESS; | ||
| 8789 | hc.t = time; | ||
| 8790 | hc.x = 0.0; | ||
| 8791 | hc.y = 0.0; | ||
| 8792 | hc.z = 0.0; | ||
| 8793 | |||
| 8794 | /* The Sun emits light instead of reflecting it, */ | ||
| 8795 | /* so we report a placeholder phase angle of 0. */ | ||
| 8796 | phase.status = ASTRO_SUCCESS; | ||
| 8797 | phase.angle = 0.0; | ||
| 8798 | } | ||
| 8799 | else | ||
| 8800 | { | ||
| 8801 | if (body == BODY_MOON) | ||
| 8802 | { | ||
| 8803 | /* For extra numeric precision, use geocentric Moon formula directly. */ | ||
| 8804 | gc = Astronomy_GeoMoon(time); | ||
| 8805 | if (gc.status != ASTRO_SUCCESS) | ||
| 8806 | return IllumError(gc.status); | ||
| 8807 | |||
| 8808 | hc.status = ASTRO_SUCCESS; | ||
| 8809 | hc.t = time; | ||
| 8810 | hc.x = earth.x + gc.x; | ||
| 8811 | hc.y = earth.y + gc.y; | ||
| 8812 | hc.z = earth.z + gc.z; | ||
| 8813 | } | ||
| 8814 | else | ||
| 8815 | { | ||
| 8816 | /* For planets, the heliocentric vector is more direct to calculate. */ | ||
| 8817 | hc = Astronomy_HelioVector(body, time); | ||
| 8818 | if (hc.status != ASTRO_SUCCESS) | ||
| 8819 | return IllumError(hc.status); | ||
| 8820 | |||
| 8821 | gc.status = ASTRO_SUCCESS; | ||
| 8822 | gc.t = time; | ||
| 8823 | gc.x = hc.x - earth.x; | ||
| 8824 | gc.y = hc.y - earth.y; | ||
| 8825 | gc.z = hc.z - earth.z; | ||
| 8826 | } | ||
| 8827 | |||
| 8828 | phase = Astronomy_AngleBetween(gc, hc); | ||
| 8829 | if (phase.status != ASTRO_SUCCESS) | ||
| 8830 | return IllumError(phase.status); | ||
| 8831 | } | ||
| 8832 | |||
| 8833 | geo_dist = Astronomy_VectorLength(gc); | ||
| 8834 | helio_dist = Astronomy_VectorLength(hc); | ||
| 8835 | |||
| 8836 | switch (body) | ||
| 8837 | { | ||
| 8838 | case BODY_SUN: | ||
| 8839 | mag = -0.17 + 5.0*log10(geo_dist / AU_PER_PARSEC); | ||
| 8840 | break; | ||
| 8841 | |||
| 8842 | case BODY_MOON: | ||
| 8843 | mag = MoonMagnitude(phase.angle, helio_dist, geo_dist); | ||
| 8844 | break; | ||
| 8845 | |||
| 8846 | case BODY_SATURN: | ||
| 8847 | status = SaturnMagnitude(phase.angle, helio_dist, geo_dist, gc, time, &mag, &ring_tilt); | ||
| 8848 | if (status != ASTRO_SUCCESS) | ||
| 8849 | return IllumError(status); | ||
| 8850 | break; | ||
| 8851 | |||
| 8852 | default: | ||
| 8853 | status = VisualMagnitude(body, phase.angle, helio_dist, geo_dist, &mag); | ||
| 8854 | if (status != ASTRO_SUCCESS) | ||
| 8855 | return IllumError(status); | ||
| 8856 | break; | ||
| 8857 | } | ||
| 8858 | |||
| 8859 | illum.status = ASTRO_SUCCESS; | ||
| 8860 | illum.time = time; | ||
| 8861 | illum.mag = mag; | ||
| 8862 | illum.phase_angle = phase.angle; | ||
| 8863 | illum.phase_fraction = (1.0 + cos(DEG2RAD * phase.angle)) / 2.0; | ||
| 8864 | illum.helio_dist = helio_dist; | ||
| 8865 | illum.ring_tilt = ring_tilt; | ||
| 8866 | |||
| 8867 | return illum; | ||
| 8868 | } | ||
| 8869 | |||
| 8870 | static astro_func_result_t mag_slope(void *context, astro_time_t time) | ||
| 8871 | { | ||
| 8872 | /* | ||
| 8873 | The Search() function finds a transition from negative to positive values. | ||
| 8874 | The derivative of magnitude y with respect to time t (dy/dt) | ||
| 8875 | is negative as an object gets brighter, because the magnitude numbers | ||
| 8876 | get smaller. At peak magnitude dy/dt = 0, then as the object gets dimmer, | ||
| 8877 | dy/dt > 0. | ||
| 8878 | */ | ||
| 8879 | static const double dt = 0.01; | ||
| 8880 | astro_illum_t y1, y2; | ||
| 8881 | astro_body_t body = *((astro_body_t *)context); | ||
| 8882 | astro_time_t t1 = Astronomy_AddDays(time, -dt/2); | ||
| 8883 | astro_time_t t2 = Astronomy_AddDays(time, +dt/2); | ||
| 8884 | astro_func_result_t result; | ||
| 8885 | |||
| 8886 | y1 = Astronomy_Illumination(body, t1); | ||
| 8887 | if (y1.status != ASTRO_SUCCESS) | ||
| 8888 | return FuncError(y1.status); | ||
| 8889 | |||
| 8890 | y2 = Astronomy_Illumination(body, t2); | ||
| 8891 | if (y2.status != ASTRO_SUCCESS) | ||
| 8892 | return FuncError(y2.status); | ||
| 8893 | |||
| 8894 | result.value = (y2.mag - y1.mag) / dt; | ||
| 8895 | result.status = ASTRO_SUCCESS; | ||
| 8896 | return result; | ||
| 8897 | } | ||
| 8898 | |||
| 8899 | /** | ||
| 8900 | * @brief | ||
| 8901 | * Searches for the date and time Venus will next appear brightest as seen from the Earth. | ||
| 8902 | * | ||
| 8903 | * This function searches for the date and time Venus appears brightest as seen from the Earth. | ||
| 8904 | * Currently only Venus is supported for the `body` parameter, though this could change in the future. | ||
| 8905 | * Mercury's peak magnitude occurs at superior conjunction, when it is virtually impossible to see from the Earth, | ||
| 8906 | * so peak magnitude events have little practical value for that planet. | ||
| 8907 | * Planets other than Venus and Mercury reach peak magnitude at opposition, which can | ||
| 8908 | * be found using #Astronomy_SearchRelativeLongitude. | ||
| 8909 | * The Moon reaches peak magnitude at full moon, which can be found using | ||
| 8910 | * #Astronomy_SearchMoonQuarter or #Astronomy_SearchMoonPhase. | ||
| 8911 | * The Sun reaches peak magnitude at perihelion, which occurs each year in January. | ||
| 8912 | * However, the difference is minor and has little practical value. | ||
| 8913 | * | ||
| 8914 | * @param body | ||
| 8915 | * Currently only `BODY_VENUS` is allowed. Any other value results in the error `ASTRO_INVALID_BODY`. | ||
| 8916 | * See function remarks for more details. | ||
| 8917 | * | ||
| 8918 | * @param startTime | ||
| 8919 | * The date and time to start searching for the next peak magnitude event. | ||
| 8920 | * | ||
| 8921 | * @return | ||
| 8922 | * See documentation about the return value from #Astronomy_Illumination. | ||
| 8923 | */ | ||
| 8924 | astro_illum_t Astronomy_SearchPeakMagnitude(astro_body_t body, astro_time_t startTime) | ||
| 8925 | { | ||
| 8926 | /* s1 and s2 are relative longitudes within which peak magnitude of Venus can occur. */ | ||
| 8927 | static const double s1 = 10.0; | ||
| 8928 | static const double s2 = 30.0; | ||
| 8929 | int iter; | ||
| 8930 | astro_angle_result_t plon, elon; | ||
| 8931 | astro_search_result_t t1, t2, tx; | ||
| 8932 | astro_func_result_t syn, m1, m2; | ||
| 8933 | astro_time_t t_start; | ||
| 8934 | double rlon, rlon_lo, rlon_hi, adjust_days; | ||
| 8935 | |||
| 8936 | if (body != BODY_VENUS) | ||
| 8937 | return IllumError(ASTRO_INVALID_BODY); | ||
| 8938 | |||
| 8939 | iter = 0; | ||
| 8940 | while (++iter <= 2) | ||
| 8941 | { | ||
| 8942 | /* Find current heliocentric relative longitude between the */ | ||
| 8943 | /* inferior planet and the Earth. */ | ||
| 8944 | plon = Astronomy_EclipticLongitude(body, startTime); | ||
| 8945 | if (plon.status != ASTRO_SUCCESS) | ||
| 8946 | return IllumError(plon.status); | ||
| 8947 | |||
| 8948 | elon = Astronomy_EclipticLongitude(BODY_EARTH, startTime); | ||
| 8949 | if (elon.status != ASTRO_SUCCESS) | ||
| 8950 | return IllumError(elon.status); | ||
| 8951 | |||
| 8952 | rlon = LongitudeOffset(plon.angle - elon.angle); /* clamp to (-180, +180]. */ | ||
| 8953 | |||
| 8954 | /* The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees */ | ||
| 8955 | /* because there is a cusp there that causes a discontinuity in the derivative. */ | ||
| 8956 | /* So we need to guard against searching near such times. */ | ||
| 8957 | |||
| 8958 | if (rlon >= -s1 && rlon < +s1) | ||
| 8959 | { | ||
| 8960 | /* Seek to the window [+s1, +s2]. */ | ||
| 8961 | adjust_days = 0.0; | ||
| 8962 | /* Search forward for the time t1 when rel lon = +s1. */ | ||
| 8963 | rlon_lo = +s1; | ||
| 8964 | /* Search forward for the time t2 when rel lon = +s2. */ | ||
| 8965 | rlon_hi = +s2; | ||
| 8966 | } | ||
| 8967 | else if (rlon >= +s2 || rlon < -s2) | ||
| 8968 | { | ||
| 8969 | /* Seek to the next search window at [-s2, -s1]. */ | ||
| 8970 | adjust_days = 0.0; | ||
| 8971 | /* Search forward for the time t1 when rel lon = -s2. */ | ||
| 8972 | rlon_lo = -s2; | ||
| 8973 | /* Search forward for the time t2 when rel lon = -s1. */ | ||
| 8974 | rlon_hi = -s1; | ||
| 8975 | } | ||
| 8976 | else if (rlon >= 0) | ||
| 8977 | { | ||
| 8978 | /* rlon must be in the middle of the window [+s1, +s2]. */ | ||
| 8979 | /* Search BACKWARD for the time t1 when rel lon = +s1. */ | ||
| 8980 | syn = SynodicPeriod(body); | ||
| 8981 | if (syn.status != ASTRO_SUCCESS) | ||
| 8982 | return IllumError(syn.status); | ||
| 8983 | adjust_days = -syn.value / 4; | ||
| 8984 | rlon_lo = +s1; | ||
| 8985 | /* Search forward from t1 to find t2 such that rel lon = +s2. */ | ||
| 8986 | rlon_hi = +s2; | ||
| 8987 | } | ||
| 8988 | else | ||
| 8989 | { | ||
| 8990 | /* rlon must be in the middle of the window [-s2, -s1]. */ | ||
| 8991 | /* Search BACKWARD for the time t1 when rel lon = -s2. */ | ||
| 8992 | syn = SynodicPeriod(body); | ||
| 8993 | if (syn.status != ASTRO_SUCCESS) | ||
| 8994 | return IllumError(syn.status); | ||
| 8995 | adjust_days = -syn.value / 4; | ||
| 8996 | rlon_lo = -s2; | ||
| 8997 | /* Search forward from t1 to find t2 such that rel lon = -s1. */ | ||
| 8998 | rlon_hi = -s1; | ||
| 8999 | } | ||
| 9000 | t_start = Astronomy_AddDays(startTime, adjust_days); | ||
| 9001 | t1 = Astronomy_SearchRelativeLongitude(body, rlon_lo, t_start); | ||
| 9002 | if (t1.status != ASTRO_SUCCESS) | ||
| 9003 | return IllumError(t1.status); | ||
| 9004 | t2 = Astronomy_SearchRelativeLongitude(body, rlon_hi, t1.time); | ||
| 9005 | if (t2.status != ASTRO_SUCCESS) | ||
| 9006 | return IllumError(t2.status); | ||
| 9007 | |||
| 9008 | /* Now we have a time range [t1,t2] that brackets a maximum magnitude event. */ | ||
| 9009 | /* Confirm the bracketing. */ | ||
| 9010 | m1 = mag_slope(&body, t1.time); | ||
| 9011 | if (m1.status != ASTRO_SUCCESS) | ||
| 9012 | return IllumError(m1.status); | ||
| 9013 | if (m1.value >= 0.0) | ||
| 9014 | return IllumError(ASTRO_INTERNAL_ERROR); /* should never happen! */ | ||
| 9015 | |||
| 9016 | m2 = mag_slope(&body, t2.time); | ||
| 9017 | if (m2.status != ASTRO_SUCCESS) | ||
| 9018 | return IllumError(m2.status); | ||
| 9019 | if (m2.value <= 0.0) | ||
| 9020 | return IllumError(ASTRO_INTERNAL_ERROR); /* should never happen! */ | ||
| 9021 | |||
| 9022 | /* Use the generic search algorithm to home in on where the slope crosses from negative to positive. */ | ||
| 9023 | tx = Astronomy_Search(mag_slope, &body, t1.time, t2.time, 10.0); | ||
| 9024 | if (tx.status != ASTRO_SUCCESS) | ||
| 9025 | return IllumError(tx.status); | ||
| 9026 | |||
| 9027 | if (tx.time.tt >= startTime.tt) | ||
| 9028 | return Astronomy_Illumination(body, tx.time); | ||
| 9029 | |||
| 9030 | /* This event is in the past (earlier than startTime). */ | ||
| 9031 | /* We need to search forward from t2 to find the next possible window. */ | ||
| 9032 | /* We never need to search more than twice. */ | ||
| 9033 | startTime = Astronomy_AddDays(t2.time, 1.0); | ||
| 9034 | } | ||
| 9035 | |||
| 9036 | return IllumError(ASTRO_SEARCH_FAILURE); | ||
| 9037 | } | ||
| 9038 | |||
| 9039 | static double MoonDistance(astro_time_t t) | ||
| 9040 | { | ||
| 9041 | double lon, lat, dist; | ||
| 9042 | CalcMoon(t.tt / 36525.0, &lon, &lat, &dist); | ||
| 9043 | return dist; | ||
| 9044 | } | ||
| 9045 | |||
| 9046 | static astro_func_result_t moon_distance_slope(void *context, astro_time_t time) | ||
| 9047 | { | ||
| 9048 | static const double dt = 0.001; | ||
| 9049 | astro_time_t t1 = Astronomy_AddDays(time, -dt/2.0); | ||
| 9050 | astro_time_t t2 = Astronomy_AddDays(time, +dt/2.0); | ||
| 9051 | double dist1, dist2; | ||
| 9052 | int direction = *((int *)context); | ||
| 9053 | astro_func_result_t result; | ||
| 9054 | |||
| 9055 | dist1 = MoonDistance(t1); | ||
| 9056 | dist2 = MoonDistance(t2); | ||
| 9057 | result.value = direction * (dist2 - dist1) / dt; | ||
| 9058 | result.status = ASTRO_SUCCESS; | ||
| 9059 | return result; | ||
| 9060 | } | ||
| 9061 | |||
| 9062 | /** | ||
| 9063 | * @brief | ||
| 9064 | * Finds the date and time of the Moon's closest distance (perigee) | ||
| 9065 | * or farthest distance (apogee) with respect to the Earth. | ||
| 9066 | * | ||
| 9067 | * Given a date and time to start the search in `startTime`, this function finds the | ||
| 9068 | * next date and time that the center of the Moon reaches the closest or farthest point | ||
| 9069 | * in its orbit with respect to the center of the Earth, whichever comes first | ||
| 9070 | * after `startTime`. | ||
| 9071 | * | ||
| 9072 | * The closest point is called *perigee* and the farthest point is called *apogee*. | ||
| 9073 | * The word *apsis* refers to either event. | ||
| 9074 | * | ||
| 9075 | * To iterate through consecutive alternating perigee and apogee events, call `Astronomy_SearchLunarApsis` | ||
| 9076 | * once, then use the return value to call #Astronomy_NextLunarApsis. After that, | ||
| 9077 | * keep feeding the previous return value from `Astronomy_NextLunarApsis` into another | ||
| 9078 | * call of `Astronomy_NextLunarApsis` as many times as desired. | ||
| 9079 | * | ||
| 9080 | * @param startTime | ||
| 9081 | * The date and time at which to start searching for the next perigee or apogee. | ||
| 9082 | * | ||
| 9083 | * @return | ||
| 9084 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS`, | ||
| 9085 | * `time` holds the date and time of the next lunar apsis, `kind` holds either | ||
| 9086 | * `APSIS_PERICENTER` for perigee or `APSIS_APOCENTER` for apogee, and the distance | ||
| 9087 | * values `dist_au` (astronomical units) and `dist_km` (kilometers) are valid. | ||
| 9088 | * If the function fails, `status` holds some value other than `ASTRO_SUCCESS` that | ||
| 9089 | * indicates what went wrong, and the other structure fields are invalid. | ||
| 9090 | */ | ||
| 9091 | astro_apsis_t Astronomy_SearchLunarApsis(astro_time_t startTime) | ||
| 9092 | { | ||
| 9093 | astro_time_t t1, t2; | ||
| 9094 | astro_search_result_t search; | ||
| 9095 | astro_func_result_t m1, m2; | ||
| 9096 | int positive_direction = +1; | ||
| 9097 | int negative_direction = -1; | ||
| 9098 | const double increment = 5.0; /* number of days to skip in each iteration */ | ||
| 9099 | astro_apsis_t result; | ||
| 9100 | int iter; | ||
| 9101 | |||
| 9102 | /* | ||
| 9103 | Check the rate of change of the distance dr/dt at the start time. | ||
| 9104 | If it is positive, the Moon is currently getting farther away, | ||
| 9105 | so start looking for apogee. | ||
| 9106 | Conversely, if dr/dt < 0, start looking for perigee. | ||
| 9107 | Either way, the polarity of the slope will change, so the product will be negative. | ||
| 9108 | Handle the crazy corner case of exactly touching zero by checking for m1*m2 <= 0. | ||
| 9109 | */ | ||
| 9110 | |||
| 9111 | t1 = startTime; | ||
| 9112 | m1 = moon_distance_slope(&positive_direction, t1); | ||
| 9113 | if (m1.status != ASTRO_SUCCESS) | ||
| 9114 | return ApsisError(m1.status); | ||
| 9115 | |||
| 9116 | for (iter=0; iter * increment < 2.0 * MEAN_SYNODIC_MONTH; ++iter) | ||
| 9117 | { | ||
| 9118 | t2 = Astronomy_AddDays(t1, increment); | ||
| 9119 | m2 = moon_distance_slope(&positive_direction, t2); | ||
| 9120 | if (m2.status != ASTRO_SUCCESS) | ||
| 9121 | return ApsisError(m2.status); | ||
| 9122 | |||
| 9123 | if (m1.value * m2.value <= 0.0) | ||
| 9124 | { | ||
| 9125 | /* There is a change of slope polarity within the time range [t1, t2]. */ | ||
| 9126 | /* Therefore this time range contains an apsis. */ | ||
| 9127 | /* Figure out whether it is perigee or apogee. */ | ||
| 9128 | |||
| 9129 | if (m1.value < 0.0 || m2.value > 0.0) | ||
| 9130 | { | ||
| 9131 | /* We found a minimum-distance event: perigee. */ | ||
| 9132 | /* Search the time range for the time when the slope goes from negative to positive. */ | ||
| 9133 | search = Astronomy_Search(moon_distance_slope, &positive_direction, t1, t2, 1.0); | ||
| 9134 | result.kind = APSIS_PERICENTER; | ||
| 9135 | } | ||
| 9136 | else if (m1.value > 0.0 || m2.value < 0.0) | ||
| 9137 | { | ||
| 9138 | /* We found a maximum-distance event: apogee. */ | ||
| 9139 | /* Search the time range for the time when the slope goes from positive to negative. */ | ||
| 9140 | search = Astronomy_Search(moon_distance_slope, &negative_direction, t1, t2, 1.0); | ||
| 9141 | result.kind = APSIS_APOCENTER; | ||
| 9142 | } | ||
| 9143 | else | ||
| 9144 | { | ||
| 9145 | /* This should never happen. It should not be possible for both slopes to be zero. */ | ||
| 9146 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9147 | } | ||
| 9148 | |||
| 9149 | if (search.status != ASTRO_SUCCESS) | ||
| 9150 | return ApsisError(search.status); | ||
| 9151 | |||
| 9152 | result.status = ASTRO_SUCCESS; | ||
| 9153 | result.time = search.time; | ||
| 9154 | result.dist_au = MoonDistance(search.time); | ||
| 9155 | result.dist_km = result.dist_au * KM_PER_AU; | ||
| 9156 | return result; | ||
| 9157 | } | ||
| 9158 | |||
| 9159 | /* We have not yet found a slope polarity change. Keep searching. */ | ||
| 9160 | t1 = t2; | ||
| 9161 | m1 = m2; | ||
| 9162 | } | ||
| 9163 | |||
| 9164 | /* It should not be possible to fail to find an apsis within 2 synodic months. */ | ||
| 9165 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9166 | } | ||
| 9167 | |||
| 9168 | /** | ||
| 9169 | * @brief | ||
| 9170 | * Finds the next lunar perigee or apogee event in a series. | ||
| 9171 | * | ||
| 9172 | * This function requires an #astro_apsis_t value obtained from a call | ||
| 9173 | * to #Astronomy_SearchLunarApsis or `Astronomy_NextLunarApsis`. Given | ||
| 9174 | * an apogee event, this function finds the next perigee event, and vice versa. | ||
| 9175 | * | ||
| 9176 | * See #Astronomy_SearchLunarApsis for more details. | ||
| 9177 | * | ||
| 9178 | * @param apsis | ||
| 9179 | * An apsis event obtained from a call to #Astronomy_SearchLunarApsis or `Astronomy_NextLunarApsis`. | ||
| 9180 | * See #Astronomy_SearchLunarApsis for more details. | ||
| 9181 | * | ||
| 9182 | * @return | ||
| 9183 | * Same as the return value for #Astronomy_SearchLunarApsis. | ||
| 9184 | */ | ||
| 9185 | astro_apsis_t Astronomy_NextLunarApsis(astro_apsis_t apsis) | ||
| 9186 | { | ||
| 9187 | static const double skip = 11.0; /* number of days to skip to start looking for next apsis event */ | ||
| 9188 | astro_apsis_t next; | ||
| 9189 | astro_time_t time; | ||
| 9190 | |||
| 9191 | if (apsis.status != ASTRO_SUCCESS) | ||
| 9192 | return ApsisError(ASTRO_INVALID_PARAMETER); | ||
| 9193 | |||
| 9194 | if (apsis.kind != APSIS_APOCENTER && apsis.kind != APSIS_PERICENTER) | ||
| 9195 | return ApsisError(ASTRO_INVALID_PARAMETER); | ||
| 9196 | |||
| 9197 | time = Astronomy_AddDays(apsis.time, skip); | ||
| 9198 | next = Astronomy_SearchLunarApsis(time); | ||
| 9199 | if (next.status == ASTRO_SUCCESS) | ||
| 9200 | { | ||
| 9201 | /* Verify that we found the opposite apsis from the previous one. */ | ||
| 9202 | if (next.kind + apsis.kind != 1) | ||
| 9203 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9204 | } | ||
| 9205 | return next; | ||
| 9206 | } | ||
| 9207 | |||
| 9208 | |||
| 9209 | /** @cond DOXYGEN_SKIP */ | ||
| 9210 | typedef struct | ||
| 9211 | { | ||
| 9212 | int direction; | ||
| 9213 | astro_body_t body; | ||
| 9214 | } | ||
| 9215 | planet_distance_context_t; | ||
| 9216 | /** @endcond */ | ||
| 9217 | |||
| 9218 | |||
| 9219 | static astro_func_result_t planet_distance_slope(void *context, astro_time_t time) | ||
| 9220 | { | ||
| 9221 | static const double dt = 0.001; | ||
| 9222 | const planet_distance_context_t *pc = (const planet_distance_context_t *) context; | ||
| 9223 | astro_time_t t1 = Astronomy_AddDays(time, -dt/2.0); | ||
| 9224 | astro_time_t t2 = Astronomy_AddDays(time, +dt/2.0); | ||
| 9225 | astro_func_result_t dist1, dist2, result; | ||
| 9226 | |||
| 9227 | dist1 = Astronomy_HelioDistance(pc->body, t1); | ||
| 9228 | if (dist1.status != ASTRO_SUCCESS) | ||
| 9229 | return dist1; | ||
| 9230 | |||
| 9231 | dist2 = Astronomy_HelioDistance(pc->body, t2); | ||
| 9232 | if (dist2.status != ASTRO_SUCCESS) | ||
| 9233 | return dist2; | ||
| 9234 | |||
| 9235 | result.value = pc->direction * (dist2.value - dist1.value) / dt; | ||
| 9236 | result.status = ASTRO_SUCCESS; | ||
| 9237 | return result; | ||
| 9238 | } | ||
| 9239 | |||
| 9240 | static astro_apsis_t PlanetExtreme( | ||
| 9241 | astro_body_t body, | ||
| 9242 | astro_apsis_kind_t kind, | ||
| 9243 | astro_time_t start_time, | ||
| 9244 | double dayspan) | ||
| 9245 | { | ||
| 9246 | astro_apsis_t apsis; | ||
| 9247 | const double direction = (kind == APSIS_APOCENTER) ? +1.0 : -1.0; | ||
| 9248 | const int npoints = 10; | ||
| 9249 | int i, best_i; | ||
| 9250 | double interval; | ||
| 9251 | double dist, best_dist; | ||
| 9252 | astro_time_t time; | ||
| 9253 | astro_func_result_t result; | ||
| 9254 | |||
| 9255 | for(;;) | ||
| 9256 | { | ||
| 9257 | interval = dayspan / (npoints - 1); | ||
| 9258 | |||
| 9259 | if (interval < 1.0 / 1440.0) /* iterate until uncertainty is less than one minute */ | ||
| 9260 | { | ||
| 9261 | apsis.status = ASTRO_SUCCESS; | ||
| 9262 | apsis.kind = kind; | ||
| 9263 | apsis.time = Astronomy_AddDays(start_time, interval / 2.0); | ||
| 9264 | result = Astronomy_HelioDistance(body, apsis.time); | ||
| 9265 | if (result.status != ASTRO_SUCCESS) | ||
| 9266 | return ApsisError(result.status); | ||
| 9267 | apsis.dist_au = result.value; | ||
| 9268 | apsis.dist_km = apsis.dist_au * KM_PER_AU; | ||
| 9269 | return apsis; | ||
| 9270 | } | ||
| 9271 | |||
| 9272 | best_i = -1; | ||
| 9273 | best_dist = 0.0; | ||
| 9274 | for (i=0; i < npoints; ++i) | ||
| 9275 | { | ||
| 9276 | time = Astronomy_AddDays(start_time, i * interval); | ||
| 9277 | result = Astronomy_HelioDistance(body, time); | ||
| 9278 | if (result.status != ASTRO_SUCCESS) | ||
| 9279 | return ApsisError(result.status); | ||
| 9280 | dist = direction * result.value; | ||
| 9281 | if (i==0 || dist > best_dist) | ||
| 9282 | { | ||
| 9283 | best_i = i; | ||
| 9284 | best_dist = dist; | ||
| 9285 | } | ||
| 9286 | } | ||
| 9287 | |||
| 9288 | /* Narrow in on the extreme point. */ | ||
| 9289 | start_time = Astronomy_AddDays(start_time, (best_i - 1) * interval); | ||
| 9290 | dayspan = 2.0 * interval; | ||
| 9291 | } | ||
| 9292 | } | ||
| 9293 | |||
| 9294 | |||
| 9295 | static astro_apsis_t BruteSearchPlanetApsis(astro_body_t body, astro_time_t startTime) | ||
| 9296 | { | ||
| 9297 | const int npoints = 100; | ||
| 9298 | int i; | ||
| 9299 | astro_time_t t1, t2, time, t_min, t_max; | ||
| 9300 | double dist, max_dist, min_dist; | ||
| 9301 | astro_apsis_t perihelion, aphelion; | ||
| 9302 | double interval; | ||
| 9303 | double period; | ||
| 9304 | astro_func_result_t result; | ||
| 9305 | |||
| 9306 | /* | ||
| 9307 | Neptune is a special case for two reasons: | ||
| 9308 | 1. Its orbit is nearly circular (low orbital eccentricity). | ||
| 9309 | 2. It is so distant from the Sun that the orbital period is very long. | ||
| 9310 | Put together, this causes wobbling of the Sun around the Solar System Barycenter (SSB) | ||
| 9311 | to be so significant that there are 3 local minima in the distance-vs-time curve | ||
| 9312 | near each apsis. Therefore, unlike for other planets, we can't use an optimized | ||
| 9313 | algorithm for finding dr/dt = 0. | ||
| 9314 | Instead, we use a dumb, brute-force algorithm of sampling and finding min/max | ||
| 9315 | heliocentric distance. | ||
| 9316 | |||
| 9317 | There is a similar problem in the TOP2013 model for Pluto: | ||
| 9318 | Its position vector has high-frequency oscillations that confuse the | ||
| 9319 | slope-based determination of apsides. | ||
| 9320 | */ | ||
| 9321 | |||
| 9322 | /* | ||
| 9323 | Rewind approximately 30 degrees in the orbit, | ||
| 9324 | then search forward for 270 degrees. | ||
| 9325 | This is a very cautious way to prevent missing an apsis. | ||
| 9326 | Typically we will find two apsides, and we pick whichever | ||
| 9327 | apsis is ealier, but after startTime. | ||
| 9328 | Sample points around this orbital arc and find when the distance | ||
| 9329 | is greatest and smallest. | ||
| 9330 | */ | ||
| 9331 | period = Astronomy_PlanetOrbitalPeriod(body); | ||
| 9332 | t1 = Astronomy_AddDays(startTime, period * ( -30.0 / 360.0)); | ||
| 9333 | t2 = Astronomy_AddDays(startTime, period * (+270.0 / 360.0)); | ||
| 9334 | t_min = t_max = t1; | ||
| 9335 | min_dist = max_dist = -1.0; /* prevent warning about uninitialized variables */ | ||
| 9336 | interval = (t2.ut - t1.ut) / (npoints - 1.0); | ||
| 9337 | |||
| 9338 | for (i=0; i < npoints; ++i) | ||
| 9339 | { | ||
| 9340 | double ut = t1.ut + (i * interval); | ||
| 9341 | time = Astronomy_TimeFromDays(ut); | ||
| 9342 | result = Astronomy_HelioDistance(body, time); | ||
| 9343 | if (result.status != ASTRO_SUCCESS) | ||
| 9344 | return ApsisError(result.status); | ||
| 9345 | dist = result.value; | ||
| 9346 | if (i == 0) | ||
| 9347 | { | ||
| 9348 | max_dist = min_dist = dist; | ||
| 9349 | } | ||
| 9350 | else | ||
| 9351 | { | ||
| 9352 | if (dist > max_dist) | ||
| 9353 | { | ||
| 9354 | max_dist = dist; | ||
| 9355 | t_max = time; | ||
| 9356 | } | ||
| 9357 | if (dist < min_dist) | ||
| 9358 | { | ||
| 9359 | min_dist = dist; | ||
| 9360 | t_min = time; | ||
| 9361 | } | ||
| 9362 | } | ||
| 9363 | } | ||
| 9364 | |||
| 9365 | t1 = Astronomy_AddDays(t_min, -2 * interval); | ||
| 9366 | perihelion = PlanetExtreme(body, APSIS_PERICENTER, t1, 4 * interval); | ||
| 9367 | |||
| 9368 | t1 = Astronomy_AddDays(t_max, -2 * interval); | ||
| 9369 | aphelion = PlanetExtreme(body, APSIS_APOCENTER, t1, 4 * interval); | ||
| 9370 | |||
| 9371 | if (perihelion.status == ASTRO_SUCCESS && perihelion.time.tt >= startTime.tt) | ||
| 9372 | { | ||
| 9373 | if (aphelion.status == ASTRO_SUCCESS && aphelion.time.tt >= startTime.tt) | ||
| 9374 | { | ||
| 9375 | /* Perihelion and aphelion are both valid. Pick the one that comes first. */ | ||
| 9376 | if (aphelion.time.tt < perihelion.time.tt) | ||
| 9377 | return aphelion; | ||
| 9378 | } | ||
| 9379 | return perihelion; | ||
| 9380 | } | ||
| 9381 | |||
| 9382 | if (aphelion.status == ASTRO_SUCCESS && aphelion.time.tt >= startTime.tt) | ||
| 9383 | return aphelion; | ||
| 9384 | |||
| 9385 | return ApsisError(ASTRO_FAIL_APSIS); | ||
| 9386 | } | ||
| 9387 | |||
| 9388 | |||
| 9389 | /** | ||
| 9390 | * @brief | ||
| 9391 | * Finds the date and time of a planet's perihelion (closest approach to the Sun) | ||
| 9392 | * or aphelion (farthest distance from the Sun) after a given time. | ||
| 9393 | * | ||
| 9394 | * Given a date and time to start the search in `startTime`, this function finds the | ||
| 9395 | * next date and time that the center of the specified planet reaches the closest or farthest point | ||
| 9396 | * in its orbit with respect to the center of the Sun, whichever comes first | ||
| 9397 | * after `startTime`. | ||
| 9398 | * | ||
| 9399 | * The closest point is called *perihelion* and the farthest point is called *aphelion*. | ||
| 9400 | * The word *apsis* refers to either event. | ||
| 9401 | * | ||
| 9402 | * To iterate through consecutive alternating perihelion and aphelion events, | ||
| 9403 | * call `Astronomy_SearchPlanetApsis` once, then use the return value to call | ||
| 9404 | * #Astronomy_NextPlanetApsis. After that, keep feeding the previous return value | ||
| 9405 | * from `Astronomy_NextPlanetApsis` into another call of `Astronomy_NextPlanetApsis` | ||
| 9406 | * as many times as desired. | ||
| 9407 | * | ||
| 9408 | * @param body | ||
| 9409 | * The planet for which to find the next perihelion/aphelion event. | ||
| 9410 | * Not allowed to be `BODY_SUN` or `BODY_MOON`. | ||
| 9411 | * | ||
| 9412 | * @param startTime | ||
| 9413 | * The date and time at which to start searching for the next perihelion or aphelion. | ||
| 9414 | * | ||
| 9415 | * @return | ||
| 9416 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS`, | ||
| 9417 | * `time` holds the date and time of the next planetary apsis, `kind` holds either | ||
| 9418 | * `APSIS_PERICENTER` for perihelion or `APSIS_APOCENTER` for aphelion, and the distance | ||
| 9419 | * values `dist_au` (astronomical units) and `dist_km` (kilometers) are valid. | ||
| 9420 | * If the function fails, `status` holds some value other than `ASTRO_SUCCESS` that | ||
| 9421 | * indicates what went wrong, and the other structure fields are invalid. | ||
| 9422 | */ | ||
| 9423 | astro_apsis_t Astronomy_SearchPlanetApsis(astro_body_t body, astro_time_t startTime) | ||
| 9424 | { | ||
| 9425 | astro_time_t t1, t2; | ||
| 9426 | astro_search_result_t search; | ||
| 9427 | astro_func_result_t m1, m2; | ||
| 9428 | planet_distance_context_t context; | ||
| 9429 | astro_apsis_t result; | ||
| 9430 | int iter; | ||
| 9431 | double orbit_period_days; | ||
| 9432 | double increment; /* number of days to skip in each iteration */ | ||
| 9433 | astro_func_result_t dist; | ||
| 9434 | |||
| 9435 | if (body == BODY_NEPTUNE || body == BODY_PLUTO) | ||
| 9436 | return BruteSearchPlanetApsis(body, startTime); | ||
| 9437 | |||
| 9438 | orbit_period_days = Astronomy_PlanetOrbitalPeriod(body); | ||
| 9439 | if (orbit_period_days == 0.0) | ||
| 9440 | return ApsisError(ASTRO_INVALID_BODY); /* The body must be a planet. */ | ||
| 9441 | |||
| 9442 | increment = orbit_period_days / 6.0; | ||
| 9443 | |||
| 9444 | context.body = body; | ||
| 9445 | |||
| 9446 | t1 = startTime; | ||
| 9447 | context.direction = +1; | ||
| 9448 | m1 = planet_distance_slope(&context, t1); | ||
| 9449 | if (m1.status != ASTRO_SUCCESS) | ||
| 9450 | return ApsisError(m1.status); | ||
| 9451 | |||
| 9452 | for (iter=0; iter * increment < 2.0 * orbit_period_days; ++iter) | ||
| 9453 | { | ||
| 9454 | t2 = Astronomy_AddDays(t1, increment); | ||
| 9455 | context.direction = +1; | ||
| 9456 | m2 = planet_distance_slope(&context, t2); | ||
| 9457 | if (m2.status != ASTRO_SUCCESS) | ||
| 9458 | return ApsisError(m2.status); | ||
| 9459 | |||
| 9460 | if (m1.value * m2.value <= 0.0) | ||
| 9461 | { | ||
| 9462 | /* There is a change of slope polarity within the time range [t1, t2]. */ | ||
| 9463 | /* Therefore this time range contains an apsis. */ | ||
| 9464 | /* Figure out whether it is perihelion or aphelion. */ | ||
| 9465 | |||
| 9466 | if (m1.value < 0.0 || m2.value > 0.0) | ||
| 9467 | { | ||
| 9468 | /* We found a minimum-distance event: perihelion. */ | ||
| 9469 | /* Search the time range for the time when the slope goes from negative to positive. */ | ||
| 9470 | context.direction = +1; | ||
| 9471 | result.kind = APSIS_PERICENTER; | ||
| 9472 | } | ||
| 9473 | else if (m1.value > 0.0 || m2.value < 0.0) | ||
| 9474 | { | ||
| 9475 | /* We found a maximum-distance event: aphelion. */ | ||
| 9476 | /* Search the time range for the time when the slope goes from positive to negative. */ | ||
| 9477 | context.direction = -1; | ||
| 9478 | result.kind = APSIS_APOCENTER; | ||
| 9479 | } | ||
| 9480 | else | ||
| 9481 | { | ||
| 9482 | /* This should never happen. It should not be possible for both slopes to be zero. */ | ||
| 9483 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9484 | } | ||
| 9485 | |||
| 9486 | search = Astronomy_Search(planet_distance_slope, &context, t1, t2, 1.0); | ||
| 9487 | if (search.status != ASTRO_SUCCESS) | ||
| 9488 | return ApsisError(search.status); | ||
| 9489 | |||
| 9490 | dist = Astronomy_HelioDistance(body, search.time); | ||
| 9491 | if (dist.status != ASTRO_SUCCESS) | ||
| 9492 | return ApsisError(dist.status); | ||
| 9493 | |||
| 9494 | result.status = ASTRO_SUCCESS; | ||
| 9495 | result.time = search.time; | ||
| 9496 | result.dist_au = dist.value; | ||
| 9497 | result.dist_km = dist.value * KM_PER_AU; | ||
| 9498 | return result; | ||
| 9499 | } | ||
| 9500 | |||
| 9501 | /* We have not yet found a slope polarity change. Keep searching. */ | ||
| 9502 | t1 = t2; | ||
| 9503 | m1 = m2; | ||
| 9504 | } | ||
| 9505 | |||
| 9506 | /* It should not be possible to fail to find an apsis within 2 orbits. */ | ||
| 9507 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9508 | } | ||
| 9509 | |||
| 9510 | /** | ||
| 9511 | * @brief | ||
| 9512 | * Finds the next planetary perihelion or aphelion event in a series. | ||
| 9513 | * | ||
| 9514 | * This function requires an #astro_apsis_t value obtained from a call | ||
| 9515 | * to #Astronomy_SearchPlanetApsis or `Astronomy_NextPlanetApsis`. | ||
| 9516 | * Given an aphelion event, this function finds the next perihelion event, and vice versa. | ||
| 9517 | * | ||
| 9518 | * See #Astronomy_SearchPlanetApsis for more details. | ||
| 9519 | * | ||
| 9520 | * @param body | ||
| 9521 | * The planet for which to find the next perihelion/aphelion event. | ||
| 9522 | * Not allowed to be `BODY_SUN` or `BODY_MOON`. | ||
| 9523 | * Must match the body passed into the call that produced the `apsis` parameter. | ||
| 9524 | * | ||
| 9525 | * @param apsis | ||
| 9526 | * An apsis event obtained from a call to #Astronomy_SearchPlanetApsis or `Astronomy_NextPlanetApsis`. | ||
| 9527 | * | ||
| 9528 | * @return | ||
| 9529 | * Same as the return value for #Astronomy_SearchPlanetApsis. | ||
| 9530 | */ | ||
| 9531 | astro_apsis_t Astronomy_NextPlanetApsis(astro_body_t body, astro_apsis_t apsis) | ||
| 9532 | { | ||
| 9533 | double skip; /* number of days to skip to start looking for next apsis event */ | ||
| 9534 | astro_apsis_t next; | ||
| 9535 | astro_time_t time; | ||
| 9536 | |||
| 9537 | if (apsis.status != ASTRO_SUCCESS) | ||
| 9538 | return ApsisError(ASTRO_INVALID_PARAMETER); | ||
| 9539 | |||
| 9540 | if (apsis.kind != APSIS_APOCENTER && apsis.kind != APSIS_PERICENTER) | ||
| 9541 | return ApsisError(ASTRO_INVALID_PARAMETER); | ||
| 9542 | |||
| 9543 | skip = 0.25 * Astronomy_PlanetOrbitalPeriod(body); /* skip 1/4 of an orbit before starting search again */ | ||
| 9544 | if (skip <= 0.0) | ||
| 9545 | return ApsisError(ASTRO_INVALID_BODY); /* body must be a planet */ | ||
| 9546 | |||
| 9547 | time = Astronomy_AddDays(apsis.time, skip); | ||
| 9548 | next = Astronomy_SearchPlanetApsis(body, time); | ||
| 9549 | if (next.status == ASTRO_SUCCESS) | ||
| 9550 | { | ||
| 9551 | /* Verify that we found the opposite apsis from the previous one. */ | ||
| 9552 | if (next.kind + apsis.kind != 1) | ||
| 9553 | return ApsisError(ASTRO_INTERNAL_ERROR); | ||
| 9554 | } | ||
| 9555 | return next; | ||
| 9556 | } | ||
| 9557 | |||
| 9558 | |||
| 9559 | /** | ||
| 9560 | * @brief Calculates the inverse of a rotation matrix. | ||
| 9561 | * | ||
| 9562 | * Given a rotation matrix that performs some coordinate transform, | ||
| 9563 | * this function returns the matrix that reverses that transform. | ||
| 9564 | * | ||
| 9565 | * @param rotation | ||
| 9566 | * The rotation matrix to be inverted. | ||
| 9567 | * | ||
| 9568 | * @return | ||
| 9569 | * A rotation matrix that performs the opposite transformation. | ||
| 9570 | */ | ||
| 9571 | astro_rotation_t Astronomy_InverseRotation(astro_rotation_t rotation) | ||
| 9572 | { | ||
| 9573 | astro_rotation_t inverse; | ||
| 9574 | |||
| 9575 | if (rotation.status != ASTRO_SUCCESS) | ||
| 9576 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 9577 | |||
| 9578 | inverse.status = ASTRO_SUCCESS; | ||
| 9579 | inverse.rot[0][0] = rotation.rot[0][0]; | ||
| 9580 | inverse.rot[0][1] = rotation.rot[1][0]; | ||
| 9581 | inverse.rot[0][2] = rotation.rot[2][0]; | ||
| 9582 | inverse.rot[1][0] = rotation.rot[0][1]; | ||
| 9583 | inverse.rot[1][1] = rotation.rot[1][1]; | ||
| 9584 | inverse.rot[1][2] = rotation.rot[2][1]; | ||
| 9585 | inverse.rot[2][0] = rotation.rot[0][2]; | ||
| 9586 | inverse.rot[2][1] = rotation.rot[1][2]; | ||
| 9587 | inverse.rot[2][2] = rotation.rot[2][2]; | ||
| 9588 | |||
| 9589 | return inverse; | ||
| 9590 | } | ||
| 9591 | |||
| 9592 | /** | ||
| 9593 | * @brief Creates a rotation based on applying one rotation followed by another. | ||
| 9594 | * | ||
| 9595 | * Given two rotation matrices, returns a combined rotation matrix that is | ||
| 9596 | * equivalent to rotating based on the first matrix, followed by the second. | ||
| 9597 | * | ||
| 9598 | * @param a | ||
| 9599 | * The first rotation to apply. | ||
| 9600 | * | ||
| 9601 | * @param b | ||
| 9602 | * The second rotation to apply. | ||
| 9603 | * | ||
| 9604 | * @return | ||
| 9605 | * The combined rotation matrix. | ||
| 9606 | */ | ||
| 9607 | astro_rotation_t Astronomy_CombineRotation(astro_rotation_t a, astro_rotation_t b) | ||
| 9608 | { | ||
| 9609 | astro_rotation_t c; | ||
| 9610 | |||
| 9611 | if (a.status != ASTRO_SUCCESS || b.status != ASTRO_SUCCESS) | ||
| 9612 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 9613 | |||
| 9614 | /* | ||
| 9615 | Use matrix multiplication: c = b*a. | ||
| 9616 | We put 'b' on the left and 'a' on the right because, | ||
| 9617 | just like when you use a matrix M to rotate a vector V, | ||
| 9618 | you put the M on the left in the product M*V. | ||
| 9619 | We can think of this as 'b' rotating all the 3 column vectors in 'a'. | ||
| 9620 | */ | ||
| 9621 | c.rot[0][0] = b.rot[0][0]*a.rot[0][0] + b.rot[1][0]*a.rot[0][1] + b.rot[2][0]*a.rot[0][2]; | ||
| 9622 | c.rot[1][0] = b.rot[0][0]*a.rot[1][0] + b.rot[1][0]*a.rot[1][1] + b.rot[2][0]*a.rot[1][2]; | ||
| 9623 | c.rot[2][0] = b.rot[0][0]*a.rot[2][0] + b.rot[1][0]*a.rot[2][1] + b.rot[2][0]*a.rot[2][2]; | ||
| 9624 | c.rot[0][1] = b.rot[0][1]*a.rot[0][0] + b.rot[1][1]*a.rot[0][1] + b.rot[2][1]*a.rot[0][2]; | ||
| 9625 | c.rot[1][1] = b.rot[0][1]*a.rot[1][0] + b.rot[1][1]*a.rot[1][1] + b.rot[2][1]*a.rot[1][2]; | ||
| 9626 | c.rot[2][1] = b.rot[0][1]*a.rot[2][0] + b.rot[1][1]*a.rot[2][1] + b.rot[2][1]*a.rot[2][2]; | ||
| 9627 | c.rot[0][2] = b.rot[0][2]*a.rot[0][0] + b.rot[1][2]*a.rot[0][1] + b.rot[2][2]*a.rot[0][2]; | ||
| 9628 | c.rot[1][2] = b.rot[0][2]*a.rot[1][0] + b.rot[1][2]*a.rot[1][1] + b.rot[2][2]*a.rot[1][2]; | ||
| 9629 | c.rot[2][2] = b.rot[0][2]*a.rot[2][0] + b.rot[1][2]*a.rot[2][1] + b.rot[2][2]*a.rot[2][2]; | ||
| 9630 | |||
| 9631 | c.status = ASTRO_SUCCESS; | ||
| 9632 | return c; | ||
| 9633 | } | ||
| 9634 | |||
| 9635 | /** | ||
| 9636 | * @brief Creates an identity rotation matrix. | ||
| 9637 | * | ||
| 9638 | * Returns a rotation matrix that has no effect on orientation. | ||
| 9639 | * This matrix can be the starting point for other operations, | ||
| 9640 | * such as using a series of calls to #Astronomy_Pivot to | ||
| 9641 | * create a custom rotation matrix. | ||
| 9642 | * | ||
| 9643 | * @return | ||
| 9644 | * The identity matrix. | ||
| 9645 | */ | ||
| 9646 | astro_rotation_t Astronomy_IdentityMatrix(void) | ||
| 9647 | { | ||
| 9648 | astro_rotation_t r; | ||
| 9649 | |||
| 9650 | r.rot[0][0] = 1.0; r.rot[1][0] = 0.0; r.rot[2][0] = 0.0; | ||
| 9651 | r.rot[0][1] = 0.0; r.rot[1][1] = 1.0; r.rot[2][1] = 0.0; | ||
| 9652 | r.rot[0][2] = 0.0; r.rot[1][2] = 0.0; r.rot[2][2] = 1.0; | ||
| 9653 | |||
| 9654 | r.status = ASTRO_SUCCESS; | ||
| 9655 | |||
| 9656 | return r; | ||
| 9657 | } | ||
| 9658 | |||
| 9659 | /** | ||
| 9660 | * @brief Re-orients a rotation matrix by pivoting it by an angle around one of its axes. | ||
| 9661 | * | ||
| 9662 | * Given a rotation matrix, a selected coordinate axis, and an angle in degrees, | ||
| 9663 | * this function pivots the rotation matrix by that angle around that coordinate axis. | ||
| 9664 | * | ||
| 9665 | * For example, if you have rotation matrix that converts ecliptic coordinates (ECL) | ||
| 9666 | * to horizontal coordinates (HOR), but you really want to convert ECL to the orientation | ||
| 9667 | * of a telescope camera pointed at a given body, you can use `Astronomy_Pivot` twice: | ||
| 9668 | * (1) pivot around the zenith axis by the body's azimuth, then (2) pivot around the | ||
| 9669 | * western axis by the body's altitude angle. The resulting rotation matrix will then | ||
| 9670 | * reorient ECL coordinates to the orientation of your telescope camera. | ||
| 9671 | * | ||
| 9672 | * @param rotation | ||
| 9673 | * The input rotation matrix. | ||
| 9674 | * | ||
| 9675 | * @param axis | ||
| 9676 | * An integer that selects which coordinate axis to rotate around: | ||
| 9677 | * 0 = x, 1 = y, 2 = z. Any other value will fail with the error code | ||
| 9678 | * `ASTRO_INVALID_PARAMETER` in the `status` field of the return value. | ||
| 9679 | * | ||
| 9680 | * @param angle | ||
| 9681 | * An angle in degrees indicating the amount of rotation around the specified axis. | ||
| 9682 | * Positive angles indicate rotation counterclockwise as seen from the positive | ||
| 9683 | * direction along that axis, looking towards the origin point of the orientation system. | ||
| 9684 | * If `angle` is NAN or infinite, the function will fail with the error code | ||
| 9685 | * `ASTRO_INVALID_PARAMETER`. Any finite number of degrees is allowed, but best | ||
| 9686 | * precision will result from keeping `angle` in the range [-360, +360]. | ||
| 9687 | * | ||
| 9688 | * @return | ||
| 9689 | * If successful, the return value will have `ASTRO_SUCCESS` in the `status` | ||
| 9690 | * field, along with a pivoted rotation matrix. Otherwise, `status` holds | ||
| 9691 | * an appropriate error code and the rotation matrix is invalid. | ||
| 9692 | */ | ||
| 9693 | astro_rotation_t Astronomy_Pivot(astro_rotation_t rotation, int axis, double angle) | ||
| 9694 | { | ||
| 9695 | astro_rotation_t p; | ||
| 9696 | double radians, c, s; | ||
| 9697 | int i, j, k; | ||
| 9698 | |||
| 9699 | /* Check for an invalid input matrix. */ | ||
| 9700 | if (rotation.status != ASTRO_SUCCESS) | ||
| 9701 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 9702 | |||
| 9703 | /* Check for an invalid coordinate axis. */ | ||
| 9704 | if (axis < 0 || axis > 2) | ||
| 9705 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 9706 | |||
| 9707 | /* Check for an invalid angle value. */ | ||
| 9708 | if (!isfinite(angle)) | ||
| 9709 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 9710 | |||
| 9711 | radians = angle * DEG2RAD; | ||
| 9712 | c = cos(radians); | ||
| 9713 | s = sin(radians); | ||
| 9714 | |||
| 9715 | /* | ||
| 9716 | We need to maintain the "right-hand" rule, no matter which | ||
| 9717 | axis was selected. That means we pick (i, j, k) axis order | ||
| 9718 | such that the following vector cross product is satisfied: | ||
| 9719 | i x j = k | ||
| 9720 | */ | ||
| 9721 | i = (axis + 1) % 3; | ||
| 9722 | j = (axis + 2) % 3; | ||
| 9723 | k = axis; | ||
| 9724 | |||
| 9725 | p.rot[i][i] = c*rotation.rot[i][i] - s*rotation.rot[i][j]; | ||
| 9726 | p.rot[i][j] = s*rotation.rot[i][i] + c*rotation.rot[i][j]; | ||
| 9727 | p.rot[i][k] = rotation.rot[i][k]; | ||
| 9728 | |||
| 9729 | p.rot[j][i] = c*rotation.rot[j][i] - s*rotation.rot[j][j]; | ||
| 9730 | p.rot[j][j] = s*rotation.rot[j][i] + c*rotation.rot[j][j]; | ||
| 9731 | p.rot[j][k] = rotation.rot[j][k]; | ||
| 9732 | |||
| 9733 | p.rot[k][i] = c*rotation.rot[k][i] - s*rotation.rot[k][j]; | ||
| 9734 | p.rot[k][j] = s*rotation.rot[k][i] + c*rotation.rot[k][j]; | ||
| 9735 | p.rot[k][k] = rotation.rot[k][k]; | ||
| 9736 | |||
| 9737 | p.status = ASTRO_SUCCESS; | ||
| 9738 | return p; | ||
| 9739 | } | ||
| 9740 | |||
| 9741 | /** | ||
| 9742 | * @brief Converts spherical coordinates to Cartesian coordinates. | ||
| 9743 | * | ||
| 9744 | * Given spherical coordinates and a time at which they are valid, | ||
| 9745 | * returns a vector of Cartesian coordinates. The returned value | ||
| 9746 | * includes the time, as required by the type #astro_vector_t. | ||
| 9747 | * | ||
| 9748 | * @param sphere | ||
| 9749 | * Spherical coordinates to be converted. | ||
| 9750 | * | ||
| 9751 | * @param time | ||
| 9752 | * The time that should be included in the return value. | ||
| 9753 | * | ||
| 9754 | * @return | ||
| 9755 | * The vector form of the supplied spherical coordinates. | ||
| 9756 | */ | ||
| 9757 | astro_vector_t Astronomy_VectorFromSphere(astro_spherical_t sphere, astro_time_t time) | ||
| 9758 | { | ||
| 9759 | astro_vector_t vector; | ||
| 9760 | double radlat, radlon, rcoslat; | ||
| 9761 | |||
| 9762 | if (sphere.status != ASTRO_SUCCESS) | ||
| 9763 | return VecError(ASTRO_INVALID_PARAMETER, time); | ||
| 9764 | |||
| 9765 | radlat = sphere.lat * DEG2RAD; | ||
| 9766 | radlon = sphere.lon * DEG2RAD; | ||
| 9767 | rcoslat = sphere.dist * cos(radlat); | ||
| 9768 | |||
| 9769 | vector.status = ASTRO_SUCCESS; | ||
| 9770 | vector.t = time; | ||
| 9771 | vector.x = rcoslat * cos(radlon); | ||
| 9772 | vector.y = rcoslat * sin(radlon); | ||
| 9773 | vector.z = sphere.dist * sin(radlat); | ||
| 9774 | |||
| 9775 | return vector; | ||
| 9776 | } | ||
| 9777 | |||
| 9778 | |||
| 9779 | /** | ||
| 9780 | * @brief Converts Cartesian coordinates to spherical coordinates. | ||
| 9781 | * | ||
| 9782 | * Given a Cartesian vector, returns latitude, longitude, and distance. | ||
| 9783 | * | ||
| 9784 | * @param vector | ||
| 9785 | * Cartesian vector to be converted to spherical coordinates. | ||
| 9786 | * | ||
| 9787 | * @return | ||
| 9788 | * Spherical coordinates that are equivalent to the given vector. | ||
| 9789 | */ | ||
| 9790 | astro_spherical_t Astronomy_SphereFromVector(astro_vector_t vector) | ||
| 9791 | { | ||
| 9792 | double xyproj; | ||
| 9793 | astro_spherical_t sphere; | ||
| 9794 | |||
| 9795 | if (vector.status != ASTRO_SUCCESS) | ||
| 9796 | return SphereError(vector.status); | ||
| 9797 | |||
| 9798 | xyproj = vector.x*vector.x + vector.y*vector.y; | ||
| 9799 | sphere.dist = sqrt(xyproj + vector.z*vector.z); | ||
| 9800 | if (xyproj == 0.0) | ||
| 9801 | { | ||
| 9802 | if (vector.z == 0.0) | ||
| 9803 | { | ||
| 9804 | /* Indeterminate coordinates; pos vector has zero length. */ | ||
| 9805 | return SphereError(ASTRO_INVALID_PARAMETER); | ||
| 9806 | } | ||
| 9807 | |||
| 9808 | sphere.lon = 0.0; | ||
| 9809 | sphere.lat = (vector.z < 0.0) ? -90.0 : +90.0; | ||
| 9810 | } | ||
| 9811 | else | ||
| 9812 | { | ||
| 9813 | sphere.lon = RAD2DEG * atan2(vector.y, vector.x); | ||
| 9814 | if (sphere.lon < 0.0) | ||
| 9815 | sphere.lon += 360.0; | ||
| 9816 | |||
| 9817 | sphere.lat = RAD2DEG * atan2(vector.z, sqrt(xyproj)); | ||
| 9818 | } | ||
| 9819 | |||
| 9820 | sphere.status = ASTRO_SUCCESS; | ||
| 9821 | return sphere; | ||
| 9822 | } | ||
| 9823 | |||
| 9824 | |||
| 9825 | /** | ||
| 9826 | * @brief | ||
| 9827 | * Given an equatorial vector, calculates equatorial angular coordinates. | ||
| 9828 | * | ||
| 9829 | * @param vector | ||
| 9830 | * A vector in an equatorial coordinate system. | ||
| 9831 | * | ||
| 9832 | * @return | ||
| 9833 | * Angular coordinates expressed in the same equatorial system as `vector`. | ||
| 9834 | */ | ||
| 9835 | astro_equatorial_t Astronomy_EquatorFromVector(astro_vector_t vector) | ||
| 9836 | { | ||
| 9837 | astro_equatorial_t equ; | ||
| 9838 | astro_spherical_t sphere; | ||
| 9839 | |||
| 9840 | sphere = Astronomy_SphereFromVector(vector); | ||
| 9841 | if (sphere.status != ASTRO_SUCCESS) | ||
| 9842 | return EquError(sphere.status); | ||
| 9843 | |||
| 9844 | equ.status = ASTRO_SUCCESS; | ||
| 9845 | equ.dec = sphere.lat; | ||
| 9846 | equ.ra = sphere.lon / 15.0; /* convert degrees to sidereal hours */ | ||
| 9847 | equ.dist = sphere.dist; | ||
| 9848 | equ.vec = vector; | ||
| 9849 | |||
| 9850 | return equ; | ||
| 9851 | } | ||
| 9852 | |||
| 9853 | |||
| 9854 | static double ToggleAzimuthDirection(double az) | ||
| 9855 | { | ||
| 9856 | az = 360.0 - az; | ||
| 9857 | if (az >= 360.0) | ||
| 9858 | az -= 360.0; | ||
| 9859 | else if (az < 0.0) | ||
| 9860 | az += 360.0; | ||
| 9861 | return az; | ||
| 9862 | } | ||
| 9863 | |||
| 9864 | /** | ||
| 9865 | * @brief Converts Cartesian coordinates to horizontal coordinates. | ||
| 9866 | * | ||
| 9867 | * Given a horizontal Cartesian vector, returns horizontal azimuth and altitude. | ||
| 9868 | * | ||
| 9869 | * *IMPORTANT:* This function differs from #Astronomy_SphereFromVector in two ways: | ||
| 9870 | * - `Astronomy_SphereFromVector` returns a `lon` value that represents azimuth defined counterclockwise | ||
| 9871 | * from north (e.g., west = +90), but this function represents a clockwise rotation | ||
| 9872 | * (e.g., east = +90). The difference is because `Astronomy_SphereFromVector` is intended | ||
| 9873 | * to preserve the vector "right-hand rule", while this function defines azimuth in a more | ||
| 9874 | * traditional way as used in navigation and cartography. | ||
| 9875 | * - This function optionally corrects for atmospheric refraction, while `Astronomy_SphereFromVector` | ||
| 9876 | * does not. | ||
| 9877 | * | ||
| 9878 | * The returned structure contains the azimuth in `lon`. | ||
| 9879 | * It is measured in degrees clockwise from north: east = +90 degrees, west = +270 degrees. | ||
| 9880 | * | ||
| 9881 | * The altitude is stored in `lat`. | ||
| 9882 | * | ||
| 9883 | * The distance to the observed object is stored in `dist`, | ||
| 9884 | * and is expressed in astronomical units (AU). | ||
| 9885 | * | ||
| 9886 | * @param vector | ||
| 9887 | * Cartesian vector to be converted to horizontal coordinates. | ||
| 9888 | * | ||
| 9889 | * @param refraction | ||
| 9890 | * `REFRACTION_NORMAL`: correct altitude for atmospheric refraction (recommended). | ||
| 9891 | * `REFRACTION_NONE`: no atmospheric refraction correction is performed. | ||
| 9892 | * `REFRACTION_JPLHOR`: for JPL Horizons compatibility testing only; not recommended for normal use. | ||
| 9893 | * | ||
| 9894 | * @return | ||
| 9895 | * If successful, `status` holds `ASTRO_SUCCESS` and the other fields are valid as described | ||
| 9896 | * in the function remarks. | ||
| 9897 | * Otherwise `status` holds an error code and the other fields are undefined. | ||
| 9898 | */ | ||
| 9899 | astro_spherical_t Astronomy_HorizonFromVector(astro_vector_t vector, astro_refraction_t refraction) | ||
| 9900 | { | ||
| 9901 | astro_spherical_t sphere; | ||
| 9902 | |||
| 9903 | sphere = Astronomy_SphereFromVector(vector); | ||
| 9904 | if (sphere.status == ASTRO_SUCCESS) | ||
| 9905 | { | ||
| 9906 | /* Convert azimuth from counterclockwise-from-north to clockwise-from-north. */ | ||
| 9907 | sphere.lon = ToggleAzimuthDirection(sphere.lon); | ||
| 9908 | sphere.lat += Astronomy_Refraction(refraction, sphere.lat); | ||
| 9909 | } | ||
| 9910 | |||
| 9911 | return sphere; | ||
| 9912 | } | ||
| 9913 | |||
| 9914 | |||
| 9915 | /** | ||
| 9916 | * @brief | ||
| 9917 | * Given apparent angular horizontal coordinates in `sphere`, calculate horizontal vector. | ||
| 9918 | * | ||
| 9919 | * @param sphere | ||
| 9920 | * A structure that contains apparent horizontal coordinates: | ||
| 9921 | * `lat` holds the refracted altitude angle, | ||
| 9922 | * `lon` holds the azimuth in degrees clockwise from north, | ||
| 9923 | * and `dist` holds the distance from the observer to the object in AU. | ||
| 9924 | * | ||
| 9925 | * @param time | ||
| 9926 | * The date and time of the observation. This is needed because the returned | ||
| 9927 | * #astro_vector_t structure requires a valid time value when passed to certain other functions. | ||
| 9928 | * | ||
| 9929 | * @param refraction | ||
| 9930 | * The refraction option used to model atmospheric lensing. See #Astronomy_Refraction. | ||
| 9931 | * This specifies how refraction is to be removed from the altitude stored in `sphere.lat`. | ||
| 9932 | * | ||
| 9933 | * @return | ||
| 9934 | * A vector in the horizontal system: `x` = north, `y` = west, and `z` = zenith (up). | ||
| 9935 | */ | ||
| 9936 | astro_vector_t Astronomy_VectorFromHorizon(astro_spherical_t sphere, astro_time_t time, astro_refraction_t refraction) | ||
| 9937 | { | ||
| 9938 | if (sphere.status != ASTRO_SUCCESS) | ||
| 9939 | return VecError(ASTRO_INVALID_PARAMETER, time); | ||
| 9940 | |||
| 9941 | /* Convert azimuth from clockwise-from-north to counterclockwise-from-north. */ | ||
| 9942 | sphere.lon = ToggleAzimuthDirection(sphere.lon); | ||
| 9943 | |||
| 9944 | /* Reverse any applied refraction. */ | ||
| 9945 | sphere.lat += Astronomy_InverseRefraction(refraction, sphere.lat); | ||
| 9946 | |||
| 9947 | return Astronomy_VectorFromSphere(sphere, time); | ||
| 9948 | } | ||
| 9949 | |||
| 9950 | |||
| 9951 | /** | ||
| 9952 | * @brief | ||
| 9953 | * Calculates the amount of "lift" to an altitude angle caused by atmospheric refraction. | ||
| 9954 | * | ||
| 9955 | * Given an altitude angle and a refraction option, calculates | ||
| 9956 | * the amount of "lift" caused by atmospheric refraction. | ||
| 9957 | * This is the number of degrees higher in the sky an object appears | ||
| 9958 | * due to the lensing of the Earth's atmosphere. | ||
| 9959 | * This function works best near sea level. | ||
| 9960 | * To correct for higher elevations, call #Astronomy_Atmosphere for that | ||
| 9961 | * elevation and multiply the refraction angle by the resulting relative density. | ||
| 9962 | * | ||
| 9963 | * @param refraction | ||
| 9964 | * The option selecting which refraction correction to use. | ||
| 9965 | * If `REFRACTION_NORMAL`, uses a well-behaved refraction model that works well for | ||
| 9966 | * all valid values (-90 to +90) of `altitude`. | ||
| 9967 | * If `REFRACTION_JPLHOR`, this function returns a compatible value with the JPL Horizons tool. | ||
| 9968 | * If any other value (including `REFRACTION_NONE`), this function returns 0. | ||
| 9969 | * | ||
| 9970 | * @param altitude | ||
| 9971 | * An altitude angle in a horizontal coordinate system. Must be a value between -90 and +90. | ||
| 9972 | * | ||
| 9973 | * @return | ||
| 9974 | * The angular adjustment in degrees to be added to the altitude angle to correct for atmospheric lensing. | ||
| 9975 | */ | ||
| 9976 | double Astronomy_Refraction(astro_refraction_t refraction, double altitude) | ||
| 9977 | { | ||
| 9978 | double refr, hd; | ||
| 9979 | |||
| 9980 | if (altitude < -90.0 || altitude > +90.0) | ||
| 9981 | return 0.0; /* no attempt to correct an invalid altitude */ | ||
| 9982 | |||
| 9983 | if (refraction == REFRACTION_NORMAL || refraction == REFRACTION_JPLHOR) | ||
| 9984 | { | ||
| 9985 | /* | ||
| 9986 | http://extras.springer.com/1999/978-1-4471-0555-8/chap4/horizons/horizons.pdf | ||
| 9987 | JPL Horizons says it uses refraction algorithm from | ||
| 9988 | Meeus "Astronomical Algorithms", 1991, p. 101-102. | ||
| 9989 | I found the following Go implementation: | ||
| 9990 | https://github.com/soniakeys/meeus/blob/master/v3/refraction/refract.go | ||
| 9991 | This is a translation from the function "Saemundsson" there. | ||
| 9992 | I found experimentally that JPL Horizons clamps the angle to 1 degree below the horizon. | ||
| 9993 | This is important because the 'refr' formula below goes crazy near hd = -5.11. | ||
| 9994 | */ | ||
| 9995 | |||
| 9996 | hd = altitude; | ||
| 9997 | if (hd < -1.0) | ||
| 9998 | hd = -1.0; | ||
| 9999 | |||
| 10000 | refr = (1.02 / tan((hd+10.3/(hd+5.11))*DEG2RAD)) / 60.0; | ||
| 10001 | |||
| 10002 | if (refraction == REFRACTION_NORMAL && altitude < -1.0) | ||
| 10003 | { | ||
| 10004 | /* | ||
| 10005 | In "normal" mode we gradually reduce refraction toward the nadir | ||
| 10006 | so that we never get an altitude angle less than -90 degrees. | ||
| 10007 | When horizon angle is -1 degrees, the factor is exactly 1. | ||
| 10008 | As altitude approaches -90 (the nadir), the fraction approaches 0 linearly. | ||
| 10009 | */ | ||
| 10010 | refr *= (altitude + 90.0) / 89.0; | ||
| 10011 | } | ||
| 10012 | } | ||
| 10013 | else | ||
| 10014 | { | ||
| 10015 | /* No refraction, or the refraction option is invalid. */ | ||
| 10016 | refr = 0.0; | ||
| 10017 | } | ||
| 10018 | |||
| 10019 | return refr; | ||
| 10020 | } | ||
| 10021 | |||
| 10022 | |||
| 10023 | /** | ||
| 10024 | * @brief | ||
| 10025 | * Calculates the inverse of an atmospheric refraction angle. | ||
| 10026 | * | ||
| 10027 | * Given an observed altitude angle that includes atmospheric refraction, | ||
| 10028 | * calculates the negative angular correction to obtain the unrefracted | ||
| 10029 | * altitude. This is useful for cases where observed horizontal | ||
| 10030 | * coordinates are to be converted to another orientation system, | ||
| 10031 | * but refraction first must be removed from the observed position. | ||
| 10032 | * | ||
| 10033 | * @param refraction | ||
| 10034 | * The option selecting which refraction correction to use. | ||
| 10035 | * See #Astronomy_Refraction. | ||
| 10036 | * | ||
| 10037 | * @param bent_altitude | ||
| 10038 | * The apparent altitude that includes atmospheric refraction. | ||
| 10039 | * | ||
| 10040 | * @return | ||
| 10041 | * The angular adjustment in degrees to be added to the | ||
| 10042 | * altitude angle to correct for atmospheric lensing. | ||
| 10043 | * This will be less than or equal to zero. | ||
| 10044 | */ | ||
| 10045 | double Astronomy_InverseRefraction(astro_refraction_t refraction, double bent_altitude) | ||
| 10046 | { | ||
| 10047 | double altitude, diff; | ||
| 10048 | |||
| 10049 | if (bent_altitude < -90.0 || bent_altitude > +90.0) | ||
| 10050 | return 0.0; /* no attempt to correct an invalid altitude */ | ||
| 10051 | |||
| 10052 | /* Find the pre-adjusted altitude whose refraction correction leads to 'altitude'. */ | ||
| 10053 | altitude = bent_altitude - Astronomy_Refraction(refraction, bent_altitude); | ||
| 10054 | for(;;) | ||
| 10055 | { | ||
| 10056 | /* See how close we got. */ | ||
| 10057 | diff = (altitude + Astronomy_Refraction(refraction, altitude)) - bent_altitude; | ||
| 10058 | if (fabs(diff) < 1.0e-14) | ||
| 10059 | return altitude - bent_altitude; | ||
| 10060 | |||
| 10061 | altitude -= diff; | ||
| 10062 | } | ||
| 10063 | } | ||
| 10064 | |||
| 10065 | /** | ||
| 10066 | * @brief Applies a rotation to a vector, yielding a rotated vector. | ||
| 10067 | * | ||
| 10068 | * This function transforms a vector in one orientation to a vector | ||
| 10069 | * in another orientation. | ||
| 10070 | * | ||
| 10071 | * @param rotation | ||
| 10072 | * A rotation matrix that specifies how the orientation of the vector is to be changed. | ||
| 10073 | * | ||
| 10074 | * @param vector | ||
| 10075 | * The vector whose orientation is to be changed. | ||
| 10076 | * | ||
| 10077 | * @return | ||
| 10078 | * A vector in the orientation specified by `rotation`. | ||
| 10079 | */ | ||
| 10080 | astro_vector_t Astronomy_RotateVector(astro_rotation_t rotation, astro_vector_t vector) | ||
| 10081 | { | ||
| 10082 | astro_vector_t target; | ||
| 10083 | |||
| 10084 | if (rotation.status != ASTRO_SUCCESS || vector.status != ASTRO_SUCCESS) | ||
| 10085 | return VecError(ASTRO_INVALID_PARAMETER, vector.t); | ||
| 10086 | |||
| 10087 | target.status = ASTRO_SUCCESS; | ||
| 10088 | target.t = vector.t; | ||
| 10089 | target.x = rotation.rot[0][0]*vector.x + rotation.rot[1][0]*vector.y + rotation.rot[2][0]*vector.z; | ||
| 10090 | target.y = rotation.rot[0][1]*vector.x + rotation.rot[1][1]*vector.y + rotation.rot[2][1]*vector.z; | ||
| 10091 | target.z = rotation.rot[0][2]*vector.x + rotation.rot[1][2]*vector.y + rotation.rot[2][2]*vector.z; | ||
| 10092 | |||
| 10093 | return target; | ||
| 10094 | } | ||
| 10095 | |||
| 10096 | |||
| 10097 | /** | ||
| 10098 | * @brief Applies a rotation to a state vector, yielding a rotated vector. | ||
| 10099 | * | ||
| 10100 | * This function transforms a state vector in one orientation to a vector | ||
| 10101 | * in another orientation. | ||
| 10102 | * | ||
| 10103 | * @param rotation | ||
| 10104 | * A rotation matrix that specifies how the orientation of the state vector is to be changed. | ||
| 10105 | * | ||
| 10106 | * @param state | ||
| 10107 | * The state vector whose orientation is to be changed. | ||
| 10108 | * Both the position and velocity components are transformed. | ||
| 10109 | * | ||
| 10110 | * @return | ||
| 10111 | * A state vector in the orientation specified by `rotation`. | ||
| 10112 | */ | ||
| 10113 | astro_state_vector_t Astronomy_RotateState(astro_rotation_t rotation, astro_state_vector_t state) | ||
| 10114 | { | ||
| 10115 | astro_state_vector_t target; | ||
| 10116 | |||
| 10117 | if (rotation.status != ASTRO_SUCCESS || state.status != ASTRO_SUCCESS) | ||
| 10118 | return StateVecError(ASTRO_INVALID_PARAMETER, state.t); | ||
| 10119 | |||
| 10120 | target.status = ASTRO_SUCCESS; | ||
| 10121 | target.t = state.t; | ||
| 10122 | |||
| 10123 | target.x = rotation.rot[0][0]*state.x + rotation.rot[1][0]*state.y + rotation.rot[2][0]*state.z; | ||
| 10124 | target.y = rotation.rot[0][1]*state.x + rotation.rot[1][1]*state.y + rotation.rot[2][1]*state.z; | ||
| 10125 | target.z = rotation.rot[0][2]*state.x + rotation.rot[1][2]*state.y + rotation.rot[2][2]*state.z; | ||
| 10126 | |||
| 10127 | target.vx = rotation.rot[0][0]*state.vx + rotation.rot[1][0]*state.vy + rotation.rot[2][0]*state.vz; | ||
| 10128 | target.vy = rotation.rot[0][1]*state.vx + rotation.rot[1][1]*state.vy + rotation.rot[2][1]*state.vz; | ||
| 10129 | target.vz = rotation.rot[0][2]*state.vx + rotation.rot[1][2]*state.vy + rotation.rot[2][2]*state.vz; | ||
| 10130 | |||
| 10131 | return target; | ||
| 10132 | } | ||
| 10133 | |||
| 10134 | |||
| 10135 | /** | ||
| 10136 | * @brief | ||
| 10137 | * Calculates a rotation matrix from J2000 mean equator (EQJ) to J2000 mean ecliptic (ECL). | ||
| 10138 | * | ||
| 10139 | * This is one of the family of functions that returns a rotation matrix | ||
| 10140 | * for converting from one orientation to another. | ||
| 10141 | * Source: EQJ = equatorial system, using equator at J2000 epoch. | ||
| 10142 | * Target: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10143 | * | ||
| 10144 | * @return | ||
| 10145 | * A rotation matrix that converts EQJ to ECL. | ||
| 10146 | */ | ||
| 10147 | astro_rotation_t Astronomy_Rotation_EQJ_ECL(void) | ||
| 10148 | { | ||
| 10149 | static const double c = COS_OBLIQ_2000; | ||
| 10150 | static const double s = SIN_OBLIQ_2000; | ||
| 10151 | astro_rotation_t r; | ||
| 10152 | |||
| 10153 | r.status = ASTRO_SUCCESS; | ||
| 10154 | r.rot[0][0] = 1.0; r.rot[1][0] = 0.0; r.rot[2][0] = 0.0; | ||
| 10155 | r.rot[0][1] = 0.0; r.rot[1][1] = +c; r.rot[2][1] = +s; | ||
| 10156 | r.rot[0][2] = 0.0; r.rot[1][2] = -s; r.rot[2][2] = +c; | ||
| 10157 | return r; | ||
| 10158 | } | ||
| 10159 | |||
| 10160 | /** | ||
| 10161 | * @brief | ||
| 10162 | * Calculates a rotation matrix from J2000 mean ecliptic (ECL) to J2000 mean equator (EQJ). | ||
| 10163 | * | ||
| 10164 | * This is one of the family of functions that returns a rotation matrix | ||
| 10165 | * for converting from one orientation to another. | ||
| 10166 | * Source: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10167 | * Target: EQJ = equatorial system, using equator at J2000 epoch. | ||
| 10168 | * | ||
| 10169 | * @return | ||
| 10170 | * A rotation matrix that converts ECL to EQJ. | ||
| 10171 | */ | ||
| 10172 | astro_rotation_t Astronomy_Rotation_ECL_EQJ(void) | ||
| 10173 | { | ||
| 10174 | static const double c = COS_OBLIQ_2000; | ||
| 10175 | static const double s = SIN_OBLIQ_2000; | ||
| 10176 | astro_rotation_t r; | ||
| 10177 | |||
| 10178 | r.status = ASTRO_SUCCESS; | ||
| 10179 | r.rot[0][0] = 1.0; r.rot[1][0] = 0.0; r.rot[2][0] = 0.0; | ||
| 10180 | r.rot[0][1] = 0.0; r.rot[1][1] = +c; r.rot[2][1] = -s; | ||
| 10181 | r.rot[0][2] = 0.0; r.rot[1][2] = +s; r.rot[2][2] = +c; | ||
| 10182 | return r; | ||
| 10183 | } | ||
| 10184 | |||
| 10185 | /** | ||
| 10186 | * @brief | ||
| 10187 | * Calculates a rotation matrix from J2000 mean equator (EQJ) to equatorial of-date (EQD). | ||
| 10188 | * | ||
| 10189 | * This is one of the family of functions that returns a rotation matrix | ||
| 10190 | * for converting from one orientation to another. | ||
| 10191 | * Source: EQJ = equatorial system, using equator at J2000 epoch. | ||
| 10192 | * Target: EQD = equatorial system, using equator of the specified date/time. | ||
| 10193 | * | ||
| 10194 | * @param time | ||
| 10195 | * The date and time at which the Earth's equator defines the target orientation. | ||
| 10196 | * | ||
| 10197 | * @return | ||
| 10198 | * A rotation matrix that converts EQJ to EQD at `time`. | ||
| 10199 | */ | ||
| 10200 | astro_rotation_t Astronomy_Rotation_EQJ_EQD(astro_time_t *time) | ||
| 10201 | { | ||
| 10202 | astro_rotation_t prec, nut; | ||
| 10203 | |||
| 10204 | if (time == NULL) | ||
| 10205 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10206 | |||
| 10207 | prec = precession_rot(*time, FROM_2000); | ||
| 10208 | nut = nutation_rot(time, FROM_2000); | ||
| 10209 | return Astronomy_CombineRotation(prec, nut); | ||
| 10210 | } | ||
| 10211 | |||
| 10212 | |||
| 10213 | /** | ||
| 10214 | * @brief | ||
| 10215 | * Calculates a rotation matrix from J2000 mean equator (EQJ) to true ecliptic of date (ECT). | ||
| 10216 | * | ||
| 10217 | * This is one of the family of functions that returns a rotation matrix | ||
| 10218 | * for converting from one orientation to another. | ||
| 10219 | * Source: EQJ = equatorial system, using mean equator at J2000 epoch. | ||
| 10220 | * Target: ECT = ecliptic system, using true equinox of the specified date/time. | ||
| 10221 | * | ||
| 10222 | * @param time | ||
| 10223 | * The date and time at which the Earth's equator defines the target orientation. | ||
| 10224 | * | ||
| 10225 | * @return | ||
| 10226 | * A rotation matrix that converts EQJ to ECT at `time`. | ||
| 10227 | */ | ||
| 10228 | astro_rotation_t Astronomy_Rotation_EQJ_ECT(astro_time_t *time) | ||
| 10229 | { | ||
| 10230 | astro_rotation_t rot, step; | ||
| 10231 | |||
| 10232 | if (time == NULL) | ||
| 10233 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10234 | |||
| 10235 | rot = Astronomy_Rotation_EQJ_EQD(time); | ||
| 10236 | step = Astronomy_Rotation_EQD_ECT(time); | ||
| 10237 | return Astronomy_CombineRotation(rot, step); | ||
| 10238 | } | ||
| 10239 | |||
| 10240 | |||
| 10241 | /** | ||
| 10242 | * @brief | ||
| 10243 | * Calculates a rotation matrix from true ecliptic of date (ECT) to J2000 mean equator (EQJ). | ||
| 10244 | * | ||
| 10245 | * This is one of the family of functions that returns a rotation matrix | ||
| 10246 | * for converting from one orientation to another. | ||
| 10247 | * Source: ECT = ecliptic system, using true equinox of the specified date/time. | ||
| 10248 | * Target: EQJ = equatorial system, using mean equator at J2000 epoch. | ||
| 10249 | * | ||
| 10250 | * @param time | ||
| 10251 | * The date and time at which the Earth's equator defines the target orientation. | ||
| 10252 | * | ||
| 10253 | * @return | ||
| 10254 | * A rotation matrix that converts ECT to EQJ at `time`. | ||
| 10255 | */ | ||
| 10256 | astro_rotation_t Astronomy_Rotation_ECT_EQJ(astro_time_t *time) | ||
| 10257 | { | ||
| 10258 | astro_rotation_t rot, step; | ||
| 10259 | |||
| 10260 | if (time == NULL) | ||
| 10261 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10262 | |||
| 10263 | rot = Astronomy_Rotation_ECT_EQD(time); | ||
| 10264 | step = Astronomy_Rotation_EQD_EQJ(time); | ||
| 10265 | return Astronomy_CombineRotation(rot, step); | ||
| 10266 | } | ||
| 10267 | |||
| 10268 | |||
| 10269 | /** | ||
| 10270 | * @brief | ||
| 10271 | * Calculates a rotation matrix from equatorial of-date (EQD) to J2000 mean equator (EQJ). | ||
| 10272 | * | ||
| 10273 | * This is one of the family of functions that returns a rotation matrix | ||
| 10274 | * for converting from one orientation to another. | ||
| 10275 | * Source: EQD = equatorial system, using equator of the specified date/time. | ||
| 10276 | * Target: EQJ = equatorial system, using equator at J2000 epoch. | ||
| 10277 | * | ||
| 10278 | * @param time | ||
| 10279 | * The date and time at which the Earth's equator defines the source orientation. | ||
| 10280 | * | ||
| 10281 | * @return | ||
| 10282 | * A rotation matrix that converts EQD at `time` to EQJ. | ||
| 10283 | */ | ||
| 10284 | astro_rotation_t Astronomy_Rotation_EQD_EQJ(astro_time_t *time) | ||
| 10285 | { | ||
| 10286 | astro_rotation_t prec, nut; | ||
| 10287 | |||
| 10288 | if (time == NULL) | ||
| 10289 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10290 | |||
| 10291 | nut = nutation_rot(time, INTO_2000); | ||
| 10292 | prec = precession_rot(*time, INTO_2000); | ||
| 10293 | return Astronomy_CombineRotation(nut, prec); | ||
| 10294 | } | ||
| 10295 | |||
| 10296 | |||
| 10297 | /** | ||
| 10298 | * @brief | ||
| 10299 | * Calculates a rotation matrix from equatorial of-date (EQD) to horizontal (HOR). | ||
| 10300 | * | ||
| 10301 | * This is one of the family of functions that returns a rotation matrix | ||
| 10302 | * for converting from one orientation to another. | ||
| 10303 | * Source: EQD = equatorial system, using equator of the specified date/time. | ||
| 10304 | * Target: HOR = horizontal system. | ||
| 10305 | * | ||
| 10306 | * @param time | ||
| 10307 | * The date and time at which the Earth's equator applies. | ||
| 10308 | * | ||
| 10309 | * @param observer | ||
| 10310 | * A location near the Earth's mean sea level that defines the observer's horizon. | ||
| 10311 | * | ||
| 10312 | * @return | ||
| 10313 | * A rotation matrix that converts EQD to HOR at `time` and for `observer`. | ||
| 10314 | * The components of the horizontal vector are: | ||
| 10315 | * x = north, y = west, z = zenith (straight up from the observer). | ||
| 10316 | * These components are chosen so that the "right-hand rule" works for the vector | ||
| 10317 | * and so that north represents the direction where azimuth = 0. | ||
| 10318 | */ | ||
| 10319 | astro_rotation_t Astronomy_Rotation_EQD_HOR(astro_time_t *time, astro_observer_t observer) | ||
| 10320 | { | ||
| 10321 | astro_rotation_t rot; | ||
| 10322 | double uze[3], une[3], uwe[3]; | ||
| 10323 | double uz[3], un[3], uw[3]; | ||
| 10324 | double spin_angle; | ||
| 10325 | |||
| 10326 | if (time == NULL) | ||
| 10327 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10328 | |||
| 10329 | double sinlat = sin(observer.latitude * DEG2RAD); | ||
| 10330 | double coslat = cos(observer.latitude * DEG2RAD); | ||
| 10331 | double sinlon = sin(observer.longitude * DEG2RAD); | ||
| 10332 | double coslon = cos(observer.longitude * DEG2RAD); | ||
| 10333 | |||
| 10334 | uze[0] = coslat * coslon; | ||
| 10335 | uze[1] = coslat * sinlon; | ||
| 10336 | uze[2] = sinlat; | ||
| 10337 | |||
| 10338 | une[0] = -sinlat * coslon; | ||
| 10339 | une[1] = -sinlat * sinlon; | ||
| 10340 | une[2] = coslat; | ||
| 10341 | |||
| 10342 | uwe[0] = sinlon; | ||
| 10343 | uwe[1] = -coslon; | ||
| 10344 | uwe[2] = 0.0; | ||
| 10345 | |||
| 10346 | spin_angle = -15.0 * Astronomy_SiderealTime(time); | ||
| 10347 | spin(spin_angle, uze, uz); | ||
| 10348 | spin(spin_angle, une, un); | ||
| 10349 | spin(spin_angle, uwe, uw); | ||
| 10350 | |||
| 10351 | rot.rot[0][0] = un[0]; rot.rot[1][0] = un[1]; rot.rot[2][0] = un[2]; | ||
| 10352 | rot.rot[0][1] = uw[0]; rot.rot[1][1] = uw[1]; rot.rot[2][1] = uw[2]; | ||
| 10353 | rot.rot[0][2] = uz[0]; rot.rot[1][2] = uz[1]; rot.rot[2][2] = uz[2]; | ||
| 10354 | |||
| 10355 | rot.status = ASTRO_SUCCESS; | ||
| 10356 | return rot; | ||
| 10357 | } | ||
| 10358 | |||
| 10359 | |||
| 10360 | /** | ||
| 10361 | * @brief | ||
| 10362 | * Calculates a rotation matrix from horizontal (HOR) to equatorial of-date (EQD). | ||
| 10363 | * | ||
| 10364 | * This is one of the family of functions that returns a rotation matrix | ||
| 10365 | * for converting from one orientation to another. | ||
| 10366 | * Source: HOR = horizontal system (x=North, y=West, z=Zenith). | ||
| 10367 | * Target: EQD = equatorial system, using equator of the specified date/time. | ||
| 10368 | * | ||
| 10369 | * @param time | ||
| 10370 | * The date and time at which the Earth's equator applies. | ||
| 10371 | * | ||
| 10372 | * @param observer | ||
| 10373 | * A location near the Earth's mean sea level that defines the observer's horizon. | ||
| 10374 | * | ||
| 10375 | * @return | ||
| 10376 | * A rotation matrix that converts HOR to EQD at `time` and for `observer`. | ||
| 10377 | */ | ||
| 10378 | astro_rotation_t Astronomy_Rotation_HOR_EQD(astro_time_t *time, astro_observer_t observer) | ||
| 10379 | { | ||
| 10380 | astro_rotation_t rot = Astronomy_Rotation_EQD_HOR(time, observer); | ||
| 10381 | return Astronomy_InverseRotation(rot); | ||
| 10382 | } | ||
| 10383 | |||
| 10384 | |||
| 10385 | /** | ||
| 10386 | * @brief | ||
| 10387 | * Calculates a rotation matrix from horizontal (HOR) to J2000 equatorial (EQJ). | ||
| 10388 | * | ||
| 10389 | * This is one of the family of functions that returns a rotation matrix | ||
| 10390 | * for converting from one orientation to another. | ||
| 10391 | * Source: HOR = horizontal system (x=North, y=West, z=Zenith). | ||
| 10392 | * Target: EQJ = equatorial system, using equator at the J2000 epoch. | ||
| 10393 | * | ||
| 10394 | * @param time | ||
| 10395 | * The date and time of the observation. | ||
| 10396 | * | ||
| 10397 | * @param observer | ||
| 10398 | * A location near the Earth's mean sea level that defines the observer's horizon. | ||
| 10399 | * | ||
| 10400 | * @return | ||
| 10401 | * A rotation matrix that converts HOR to EQJ at `time` and for `observer`. | ||
| 10402 | */ | ||
| 10403 | astro_rotation_t Astronomy_Rotation_HOR_EQJ(astro_time_t *time, astro_observer_t observer) | ||
| 10404 | { | ||
| 10405 | astro_rotation_t hor_eqd, eqd_eqj; | ||
| 10406 | |||
| 10407 | if (time == NULL) | ||
| 10408 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10409 | |||
| 10410 | hor_eqd = Astronomy_Rotation_HOR_EQD(time, observer); | ||
| 10411 | eqd_eqj = Astronomy_Rotation_EQD_EQJ(time); | ||
| 10412 | return Astronomy_CombineRotation(hor_eqd, eqd_eqj); | ||
| 10413 | } | ||
| 10414 | |||
| 10415 | |||
| 10416 | /** | ||
| 10417 | * @brief | ||
| 10418 | * Calculates a rotation matrix from J2000 mean equator (EQJ) to horizontal (HOR). | ||
| 10419 | * | ||
| 10420 | * This is one of the family of functions that returns a rotation matrix | ||
| 10421 | * for converting from one orientation to another. | ||
| 10422 | * Source: EQJ = equatorial system, using the equator at the J2000 epoch. | ||
| 10423 | * Target: HOR = horizontal system. | ||
| 10424 | * | ||
| 10425 | * @param time | ||
| 10426 | * The date and time of the desired horizontal orientation. | ||
| 10427 | * | ||
| 10428 | * @param observer | ||
| 10429 | * A location near the Earth's mean sea level that defines the observer's horizon. | ||
| 10430 | * | ||
| 10431 | * @return | ||
| 10432 | * A rotation matrix that converts EQJ to HOR at `time` and for `observer`. | ||
| 10433 | * The components of the horizontal vector are: | ||
| 10434 | * x = north, y = west, z = zenith (straight up from the observer). | ||
| 10435 | * These components are chosen so that the "right-hand rule" works for the vector | ||
| 10436 | * and so that north represents the direction where azimuth = 0. | ||
| 10437 | */ | ||
| 10438 | astro_rotation_t Astronomy_Rotation_EQJ_HOR(astro_time_t *time, astro_observer_t observer) | ||
| 10439 | { | ||
| 10440 | astro_rotation_t rot = Astronomy_Rotation_HOR_EQJ(time, observer); | ||
| 10441 | return Astronomy_InverseRotation(rot); | ||
| 10442 | } | ||
| 10443 | |||
| 10444 | |||
| 10445 | /** | ||
| 10446 | * @brief | ||
| 10447 | * Calculates a rotation matrix from equatorial of-date (EQD) to J2000 mean ecliptic (ECL). | ||
| 10448 | * | ||
| 10449 | * This is one of the family of functions that returns a rotation matrix | ||
| 10450 | * for converting from one orientation to another. | ||
| 10451 | * Source: EQD = equatorial system, using equator of date. | ||
| 10452 | * Target: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10453 | * | ||
| 10454 | * @param time | ||
| 10455 | * The date and time of the source equator. | ||
| 10456 | * | ||
| 10457 | * @return | ||
| 10458 | * A rotation matrix that converts EQD to ECL. | ||
| 10459 | */ | ||
| 10460 | astro_rotation_t Astronomy_Rotation_EQD_ECL(astro_time_t *time) | ||
| 10461 | { | ||
| 10462 | astro_rotation_t eqd_eqj; | ||
| 10463 | astro_rotation_t eqj_ecl; | ||
| 10464 | |||
| 10465 | if (time == NULL) | ||
| 10466 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10467 | |||
| 10468 | eqd_eqj = Astronomy_Rotation_EQD_EQJ(time); | ||
| 10469 | eqj_ecl = Astronomy_Rotation_EQJ_ECL(); | ||
| 10470 | return Astronomy_CombineRotation(eqd_eqj, eqj_ecl); | ||
| 10471 | } | ||
| 10472 | |||
| 10473 | |||
| 10474 | /** | ||
| 10475 | * @brief | ||
| 10476 | * Calculates a rotation matrix from J2000 mean ecliptic (ECL) to equatorial of-date (EQD). | ||
| 10477 | * | ||
| 10478 | * This is one of the family of functions that returns a rotation matrix | ||
| 10479 | * for converting from one orientation to another. | ||
| 10480 | * Source: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10481 | * Target: EQD = equatorial system, using equator of date. | ||
| 10482 | * | ||
| 10483 | * @param time | ||
| 10484 | * The date and time of the desired equator. | ||
| 10485 | * | ||
| 10486 | * @return | ||
| 10487 | * A rotation matrix that converts ECL to EQD. | ||
| 10488 | */ | ||
| 10489 | astro_rotation_t Astronomy_Rotation_ECL_EQD(astro_time_t *time) | ||
| 10490 | { | ||
| 10491 | astro_rotation_t rot = Astronomy_Rotation_EQD_ECL(time); | ||
| 10492 | return Astronomy_InverseRotation(rot); | ||
| 10493 | } | ||
| 10494 | |||
| 10495 | /** | ||
| 10496 | * @brief | ||
| 10497 | * Calculates a rotation matrix from J2000 mean ecliptic (ECL) to horizontal (HOR). | ||
| 10498 | * | ||
| 10499 | * This is one of the family of functions that returns a rotation matrix | ||
| 10500 | * for converting from one orientation to another. | ||
| 10501 | * Source: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10502 | * Target: HOR = horizontal system. | ||
| 10503 | * | ||
| 10504 | * @param time | ||
| 10505 | * The date and time of the desired horizontal orientation. | ||
| 10506 | * | ||
| 10507 | * @param observer | ||
| 10508 | * A location near the Earth's mean sea level that defines the observer's horizon. | ||
| 10509 | * | ||
| 10510 | * @return | ||
| 10511 | * A rotation matrix that converts ECL to HOR at `time` and for `observer`. | ||
| 10512 | * The components of the horizontal vector are: | ||
| 10513 | * x = north, y = west, z = zenith (straight up from the observer). | ||
| 10514 | * These components are chosen so that the "right-hand rule" works for the vector | ||
| 10515 | * and so that north represents the direction where azimuth = 0. | ||
| 10516 | */ | ||
| 10517 | astro_rotation_t Astronomy_Rotation_ECL_HOR(astro_time_t *time, astro_observer_t observer) | ||
| 10518 | { | ||
| 10519 | astro_rotation_t ecl_eqd; | ||
| 10520 | astro_rotation_t eqd_hor; | ||
| 10521 | |||
| 10522 | if (time == NULL) | ||
| 10523 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10524 | |||
| 10525 | ecl_eqd = Astronomy_Rotation_ECL_EQD(time); | ||
| 10526 | eqd_hor = Astronomy_Rotation_EQD_HOR(time, observer); | ||
| 10527 | return Astronomy_CombineRotation(ecl_eqd, eqd_hor); | ||
| 10528 | } | ||
| 10529 | |||
| 10530 | /** | ||
| 10531 | * @brief | ||
| 10532 | * Calculates a rotation matrix from horizontal (HOR) to J2000 mean ecliptic (ECL). | ||
| 10533 | * | ||
| 10534 | * This is one of the family of functions that returns a rotation matrix | ||
| 10535 | * for converting from one orientation to another. | ||
| 10536 | * Source: HOR = horizontal system. | ||
| 10537 | * Target: ECL = ecliptic system, using equator at J2000 epoch. | ||
| 10538 | * | ||
| 10539 | * @param time | ||
| 10540 | * The date and time of the horizontal observation. | ||
| 10541 | * | ||
| 10542 | * @param observer | ||
| 10543 | * The location of the horizontal observer. | ||
| 10544 | * | ||
| 10545 | * @return | ||
| 10546 | * A rotation matrix that converts HOR to ECL. | ||
| 10547 | */ | ||
| 10548 | astro_rotation_t Astronomy_Rotation_HOR_ECL(astro_time_t *time, astro_observer_t observer) | ||
| 10549 | { | ||
| 10550 | astro_rotation_t rot = Astronomy_Rotation_ECL_HOR(time, observer); | ||
| 10551 | return Astronomy_InverseRotation(rot); | ||
| 10552 | } | ||
| 10553 | |||
| 10554 | /** | ||
| 10555 | * @brief | ||
| 10556 | * Returns a rotation matrix from J2000 mean ecliptic (EQJ) to galactic (GAL). | ||
| 10557 | * | ||
| 10558 | * This is one of the family of functions that returns a rotation matrix | ||
| 10559 | * for converting from one orientation to another. | ||
| 10560 | * Source: EQJ = equatorial system, using the equator at the J2000 epoch. | ||
| 10561 | * Target: GAL = galactic system (IAU 1958 definition). | ||
| 10562 | * | ||
| 10563 | * @return | ||
| 10564 | * A rotation matrix that converts EQJ to GAL. | ||
| 10565 | */ | ||
| 10566 | astro_rotation_t Astronomy_Rotation_EQJ_GAL(void) | ||
| 10567 | { | ||
| 10568 | astro_rotation_t rot; | ||
| 10569 | |||
| 10570 | /* | ||
| 10571 | This rotation matrix was calculated by the following script | ||
| 10572 | in this same source code repository: | ||
| 10573 | demo/python/galeqj_matrix.py | ||
| 10574 | */ | ||
| 10575 | |||
| 10576 | rot.rot[0][0] = -0.0548624779711344; | ||
| 10577 | rot.rot[0][1] = +0.4941095946388765; | ||
| 10578 | rot.rot[0][2] = -0.8676668813529025; | ||
| 10579 | |||
| 10580 | rot.rot[1][0] = -0.8734572784246782; | ||
| 10581 | rot.rot[1][1] = -0.4447938112296831; | ||
| 10582 | rot.rot[1][2] = -0.1980677870294097; | ||
| 10583 | |||
| 10584 | rot.rot[2][0] = -0.4838000529948520; | ||
| 10585 | rot.rot[2][1] = +0.7470034631630423; | ||
| 10586 | rot.rot[2][2] = +0.4559861124470794; | ||
| 10587 | |||
| 10588 | rot.status = ASTRO_SUCCESS; | ||
| 10589 | |||
| 10590 | return rot; | ||
| 10591 | } | ||
| 10592 | |||
| 10593 | /** | ||
| 10594 | * @brief | ||
| 10595 | * Returns a rotation matrix from ecliptic galactic (GAL) to J2000 (EQJ). | ||
| 10596 | * | ||
| 10597 | * This is one of the family of functions that returns a rotation matrix | ||
| 10598 | * for converting from one orientation to another. | ||
| 10599 | * Source: GAL = galactic system (IAU 1958 definition). | ||
| 10600 | * Target: EQJ = equatorial system, using the equator at the J2000 epoch. | ||
| 10601 | * | ||
| 10602 | * @return | ||
| 10603 | * A rotation matrix that converts GAL to EQJ. | ||
| 10604 | */ | ||
| 10605 | astro_rotation_t Astronomy_Rotation_GAL_EQJ(void) | ||
| 10606 | { | ||
| 10607 | astro_rotation_t rot; | ||
| 10608 | |||
| 10609 | /* | ||
| 10610 | This rotation matrix was calculated by the following script | ||
| 10611 | in this same source code repository: | ||
| 10612 | demo/python/galeqj_matrix.py | ||
| 10613 | */ | ||
| 10614 | |||
| 10615 | rot.rot[0][0] = -0.0548624779711344; | ||
| 10616 | rot.rot[0][1] = -0.8734572784246782; | ||
| 10617 | rot.rot[0][2] = -0.4838000529948520; | ||
| 10618 | |||
| 10619 | rot.rot[1][0] = +0.4941095946388765; | ||
| 10620 | rot.rot[1][1] = -0.4447938112296831; | ||
| 10621 | rot.rot[1][2] = +0.7470034631630423; | ||
| 10622 | |||
| 10623 | rot.rot[2][0] = -0.8676668813529025; | ||
| 10624 | rot.rot[2][1] = -0.1980677870294097; | ||
| 10625 | rot.rot[2][2] = +0.4559861124470794; | ||
| 10626 | |||
| 10627 | rot.status = ASTRO_SUCCESS; | ||
| 10628 | |||
| 10629 | return rot; | ||
| 10630 | } | ||
| 10631 | |||
| 10632 | |||
| 10633 | /** | ||
| 10634 | * @brief | ||
| 10635 | * Returns a rotation matrix from true ecliptic of date (ECT) to equator of date (EQD). | ||
| 10636 | * | ||
| 10637 | * This is one of the family of functions that returns a rotation matrix | ||
| 10638 | * for converting from one orientation to another. | ||
| 10639 | * Source: ECT = true ecliptic of date. | ||
| 10640 | * Target: EQD = equator of date. | ||
| 10641 | * | ||
| 10642 | * @param time | ||
| 10643 | * The date and time of the ecliptic/equator conversion. | ||
| 10644 | * | ||
| 10645 | * @return | ||
| 10646 | * A rotation matrix that converts ECT to EQD. | ||
| 10647 | */ | ||
| 10648 | astro_rotation_t Astronomy_Rotation_ECT_EQD(astro_time_t *time) | ||
| 10649 | { | ||
| 10650 | astro_rotation_t m; | ||
| 10651 | earth_tilt_t et; | ||
| 10652 | double tobl, cos_tobl, sin_tobl; | ||
| 10653 | |||
| 10654 | if (time == NULL) | ||
| 10655 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10656 | |||
| 10657 | /* Find true ecliptic obliquity for this time. */ | ||
| 10658 | et = e_tilt(time); | ||
| 10659 | tobl = et.tobl * DEG2RAD; | ||
| 10660 | cos_tobl = cos(tobl); | ||
| 10661 | sin_tobl = sin(tobl); | ||
| 10662 | |||
| 10663 | /* EQD.x = ECT.x */ | ||
| 10664 | m.rot[0][0] = 1.0; | ||
| 10665 | m.rot[1][0] = 0.0; | ||
| 10666 | m.rot[2][0] = 0.0; | ||
| 10667 | |||
| 10668 | /* EQD.y = +cos*ECT.y - sin*ECT.z */ | ||
| 10669 | m.rot[0][1] = 0.0; | ||
| 10670 | m.rot[1][1] = +cos_tobl; | ||
| 10671 | m.rot[2][1] = -sin_tobl; | ||
| 10672 | |||
| 10673 | /* EQD.z = +sin*ECT.y + cos*ECT.z */ | ||
| 10674 | m.rot[0][2] = 0.0; | ||
| 10675 | m.rot[1][2] = +sin_tobl; | ||
| 10676 | m.rot[2][2] = +cos_tobl; | ||
| 10677 | |||
| 10678 | m.status = ASTRO_SUCCESS; | ||
| 10679 | return m; | ||
| 10680 | } | ||
| 10681 | |||
| 10682 | |||
| 10683 | /** | ||
| 10684 | * @brief | ||
| 10685 | * Returns a rotation matrix from equator of date (EQD) to true ecliptic of date (ECT). | ||
| 10686 | * | ||
| 10687 | * This is one of the family of functions that returns a rotation matrix | ||
| 10688 | * for converting from one orientation to another. | ||
| 10689 | * Source: EQD = equator of date. | ||
| 10690 | * Target: ECT = true ecliptic of date. | ||
| 10691 | * | ||
| 10692 | * @param time | ||
| 10693 | * The date and time of the equator/ecliptic conversion. | ||
| 10694 | * | ||
| 10695 | * @return | ||
| 10696 | * A rotation matrix that converts EQD to ECT. | ||
| 10697 | */ | ||
| 10698 | astro_rotation_t Astronomy_Rotation_EQD_ECT(astro_time_t *time) | ||
| 10699 | { | ||
| 10700 | astro_rotation_t m; | ||
| 10701 | earth_tilt_t et; | ||
| 10702 | double tobl, cos_tobl, sin_tobl; | ||
| 10703 | |||
| 10704 | if (time == NULL) | ||
| 10705 | return RotationErr(ASTRO_INVALID_PARAMETER); | ||
| 10706 | |||
| 10707 | /* Find true ecliptic obliquity for this time. */ | ||
| 10708 | et = e_tilt(time); | ||
| 10709 | tobl = et.tobl * DEG2RAD; | ||
| 10710 | cos_tobl = cos(tobl); | ||
| 10711 | sin_tobl = sin(tobl); | ||
| 10712 | |||
| 10713 | /* ECT.x = EQD.x */ | ||
| 10714 | m.rot[0][0] = 1.0; | ||
| 10715 | m.rot[1][0] = 0.0; | ||
| 10716 | m.rot[2][0] = 0.0; | ||
| 10717 | |||
| 10718 | /* ECT.y = +cos*EQJ.y + sin*EQJ.z */ | ||
| 10719 | m.rot[0][1] = 0.0; | ||
| 10720 | m.rot[1][1] = +cos_tobl; | ||
| 10721 | m.rot[2][1] = +sin_tobl; | ||
| 10722 | |||
| 10723 | /* ECT.z = -sin*EQJ.y + cos*EQJ.z */ | ||
| 10724 | m.rot[0][2] = 0.0; | ||
| 10725 | m.rot[1][2] = -sin_tobl; | ||
| 10726 | m.rot[2][2] = +cos_tobl; | ||
| 10727 | |||
| 10728 | m.status = ASTRO_SUCCESS; | ||
| 10729 | return m; | ||
| 10730 | } | ||
| 10731 | |||
| 10732 | |||
| 10733 | /** @cond DOXYGEN_SKIP */ | ||
| 10734 | typedef struct | ||
| 10735 | { | ||
| 10736 | const char *symbol; | ||
| 10737 | const char *name; | ||
| 10738 | } | ||
| 10739 | constel_info_t; | ||
| 10740 | |||
| 10741 | |||
| 10742 | typedef struct | ||
| 10743 | { | ||
| 10744 | int index; | ||
| 10745 | double ra_lo; | ||
| 10746 | double ra_hi; | ||
| 10747 | double dec_lo; | ||
| 10748 | } | ||
| 10749 | constel_boundary_t; | ||
| 10750 | /** @endcond */ | ||
| 10751 | |||
| 10752 | #define NUM_CONSTELLATIONS 88 | ||
| 10753 | |||
| 10754 | static const constel_info_t ConstelInfo[] = { | ||
| 10755 | /* 0 */ { "And", "Andromeda" } | ||
| 10756 | , /* 1 */ { "Ant", "Antila" } | ||
| 10757 | , /* 2 */ { "Aps", "Apus" } | ||
| 10758 | , /* 3 */ { "Aql", "Aquila" } | ||
| 10759 | , /* 4 */ { "Aqr", "Aquarius" } | ||
| 10760 | , /* 5 */ { "Ara", "Ara" } | ||
| 10761 | , /* 6 */ { "Ari", "Aries" } | ||
| 10762 | , /* 7 */ { "Aur", "Auriga" } | ||
| 10763 | , /* 8 */ { "Boo", "Bootes" } | ||
| 10764 | , /* 9 */ { "Cae", "Caelum" } | ||
| 10765 | , /* 10 */ { "Cam", "Camelopardis" } | ||
| 10766 | , /* 11 */ { "Cap", "Capricornus" } | ||
| 10767 | , /* 12 */ { "Car", "Carina" } | ||
| 10768 | , /* 13 */ { "Cas", "Cassiopeia" } | ||
| 10769 | , /* 14 */ { "Cen", "Centaurus" } | ||
| 10770 | , /* 15 */ { "Cep", "Cepheus" } | ||
| 10771 | , /* 16 */ { "Cet", "Cetus" } | ||
| 10772 | , /* 17 */ { "Cha", "Chamaeleon" } | ||
| 10773 | , /* 18 */ { "Cir", "Circinus" } | ||
| 10774 | , /* 19 */ { "CMa", "Canis Major" } | ||
| 10775 | , /* 20 */ { "CMi", "Canis Minor" } | ||
| 10776 | , /* 21 */ { "Cnc", "Cancer" } | ||
| 10777 | , /* 22 */ { "Col", "Columba" } | ||
| 10778 | , /* 23 */ { "Com", "Coma Berenices" } | ||
| 10779 | , /* 24 */ { "CrA", "Corona Australis" } | ||
| 10780 | , /* 25 */ { "CrB", "Corona Borealis" } | ||
| 10781 | , /* 26 */ { "Crt", "Crater" } | ||
| 10782 | , /* 27 */ { "Cru", "Crux" } | ||
| 10783 | , /* 28 */ { "Crv", "Corvus" } | ||
| 10784 | , /* 29 */ { "CVn", "Canes Venatici" } | ||
| 10785 | , /* 30 */ { "Cyg", "Cygnus" } | ||
| 10786 | , /* 31 */ { "Del", "Delphinus" } | ||
| 10787 | , /* 32 */ { "Dor", "Dorado" } | ||
| 10788 | , /* 33 */ { "Dra", "Draco" } | ||
| 10789 | , /* 34 */ { "Equ", "Equuleus" } | ||
| 10790 | , /* 35 */ { "Eri", "Eridanus" } | ||
| 10791 | , /* 36 */ { "For", "Fornax" } | ||
| 10792 | , /* 37 */ { "Gem", "Gemini" } | ||
| 10793 | , /* 38 */ { "Gru", "Grus" } | ||
| 10794 | , /* 39 */ { "Her", "Hercules" } | ||
| 10795 | , /* 40 */ { "Hor", "Horologium" } | ||
| 10796 | , /* 41 */ { "Hya", "Hydra" } | ||
| 10797 | , /* 42 */ { "Hyi", "Hydrus" } | ||
| 10798 | , /* 43 */ { "Ind", "Indus" } | ||
| 10799 | , /* 44 */ { "Lac", "Lacerta" } | ||
| 10800 | , /* 45 */ { "Leo", "Leo" } | ||
| 10801 | , /* 46 */ { "Lep", "Lepus" } | ||
| 10802 | , /* 47 */ { "Lib", "Libra" } | ||
| 10803 | , /* 48 */ { "LMi", "Leo Minor" } | ||
| 10804 | , /* 49 */ { "Lup", "Lupus" } | ||
| 10805 | , /* 50 */ { "Lyn", "Lynx" } | ||
| 10806 | , /* 51 */ { "Lyr", "Lyra" } | ||
| 10807 | , /* 52 */ { "Men", "Mensa" } | ||
| 10808 | , /* 53 */ { "Mic", "Microscopium" } | ||
| 10809 | , /* 54 */ { "Mon", "Monoceros" } | ||
| 10810 | , /* 55 */ { "Mus", "Musca" } | ||
| 10811 | , /* 56 */ { "Nor", "Norma" } | ||
| 10812 | , /* 57 */ { "Oct", "Octans" } | ||
| 10813 | , /* 58 */ { "Oph", "Ophiuchus" } | ||
| 10814 | , /* 59 */ { "Ori", "Orion" } | ||
| 10815 | , /* 60 */ { "Pav", "Pavo" } | ||
| 10816 | , /* 61 */ { "Peg", "Pegasus" } | ||
| 10817 | , /* 62 */ { "Per", "Perseus" } | ||
| 10818 | , /* 63 */ { "Phe", "Phoenix" } | ||
| 10819 | , /* 64 */ { "Pic", "Pictor" } | ||
| 10820 | , /* 65 */ { "PsA", "Pisces Austrinus" } | ||
| 10821 | , /* 66 */ { "Psc", "Pisces" } | ||
| 10822 | , /* 67 */ { "Pup", "Puppis" } | ||
| 10823 | , /* 68 */ { "Pyx", "Pyxis" } | ||
| 10824 | , /* 69 */ { "Ret", "Reticulum" } | ||
| 10825 | , /* 70 */ { "Scl", "Sculptor" } | ||
| 10826 | , /* 71 */ { "Sco", "Scorpius" } | ||
| 10827 | , /* 72 */ { "Sct", "Scutum" } | ||
| 10828 | , /* 73 */ { "Ser", "Serpens" } | ||
| 10829 | , /* 74 */ { "Sex", "Sextans" } | ||
| 10830 | , /* 75 */ { "Sge", "Sagitta" } | ||
| 10831 | , /* 76 */ { "Sgr", "Sagittarius" } | ||
| 10832 | , /* 77 */ { "Tau", "Taurus" } | ||
| 10833 | , /* 78 */ { "Tel", "Telescopium" } | ||
| 10834 | , /* 79 */ { "TrA", "Triangulum Australe" } | ||
| 10835 | , /* 80 */ { "Tri", "Triangulum" } | ||
| 10836 | , /* 81 */ { "Tuc", "Tucana" } | ||
| 10837 | , /* 82 */ { "UMa", "Ursa Major" } | ||
| 10838 | , /* 83 */ { "UMi", "Ursa Minor" } | ||
| 10839 | , /* 84 */ { "Vel", "Vela" } | ||
| 10840 | , /* 85 */ { "Vir", "Virgo" } | ||
| 10841 | , /* 86 */ { "Vol", "Volans" } | ||
| 10842 | , /* 87 */ { "Vul", "Vulpecula" } | ||
| 10843 | }; | ||
| 10844 | |||
| 10845 | static const constel_boundary_t ConstelBounds[] = { | ||
| 10846 | { 83, 0, 8640, 2112 } /* UMi */ | ||
| 10847 | , { 83, 2880, 5220, 2076 } /* UMi */ | ||
| 10848 | , { 83, 7560, 8280, 2068 } /* UMi */ | ||
| 10849 | , { 83, 6480, 7560, 2064 } /* UMi */ | ||
| 10850 | , { 15, 0, 2880, 2040 } /* Cep */ | ||
| 10851 | , { 10, 3300, 3840, 1968 } /* Cam */ | ||
| 10852 | , { 15, 0, 1800, 1920 } /* Cep */ | ||
| 10853 | , { 10, 3840, 5220, 1920 } /* Cam */ | ||
| 10854 | , { 83, 6300, 6480, 1920 } /* UMi */ | ||
| 10855 | , { 33, 7260, 7560, 1920 } /* Dra */ | ||
| 10856 | , { 15, 0, 1263, 1848 } /* Cep */ | ||
| 10857 | , { 10, 4140, 4890, 1848 } /* Cam */ | ||
| 10858 | , { 83, 5952, 6300, 1800 } /* UMi */ | ||
| 10859 | , { 15, 7260, 7440, 1800 } /* Cep */ | ||
| 10860 | , { 10, 2868, 3300, 1764 } /* Cam */ | ||
| 10861 | , { 33, 3300, 4080, 1764 } /* Dra */ | ||
| 10862 | , { 83, 4680, 5952, 1680 } /* UMi */ | ||
| 10863 | , { 13, 1116, 1230, 1632 } /* Cas */ | ||
| 10864 | , { 33, 7350, 7440, 1608 } /* Dra */ | ||
| 10865 | , { 33, 4080, 4320, 1596 } /* Dra */ | ||
| 10866 | , { 15, 0, 120, 1584 } /* Cep */ | ||
| 10867 | , { 83, 5040, 5640, 1584 } /* UMi */ | ||
| 10868 | , { 15, 8490, 8640, 1584 } /* Cep */ | ||
| 10869 | , { 33, 4320, 4860, 1536 } /* Dra */ | ||
| 10870 | , { 33, 4860, 5190, 1512 } /* Dra */ | ||
| 10871 | , { 15, 8340, 8490, 1512 } /* Cep */ | ||
| 10872 | , { 10, 2196, 2520, 1488 } /* Cam */ | ||
| 10873 | , { 33, 7200, 7350, 1476 } /* Dra */ | ||
| 10874 | , { 15, 7393.2, 7416, 1462 } /* Cep */ | ||
| 10875 | , { 10, 2520, 2868, 1440 } /* Cam */ | ||
| 10876 | , { 82, 2868, 3030, 1440 } /* UMa */ | ||
| 10877 | , { 33, 7116, 7200, 1428 } /* Dra */ | ||
| 10878 | , { 15, 7200, 7393.2, 1428 } /* Cep */ | ||
| 10879 | , { 15, 8232, 8340, 1418 } /* Cep */ | ||
| 10880 | , { 13, 0, 876, 1404 } /* Cas */ | ||
| 10881 | , { 33, 6990, 7116, 1392 } /* Dra */ | ||
| 10882 | , { 13, 612, 687, 1380 } /* Cas */ | ||
| 10883 | , { 13, 876, 1116, 1368 } /* Cas */ | ||
| 10884 | , { 10, 1116, 1140, 1368 } /* Cam */ | ||
| 10885 | , { 15, 8034, 8232, 1350 } /* Cep */ | ||
| 10886 | , { 10, 1800, 2196, 1344 } /* Cam */ | ||
| 10887 | , { 82, 5052, 5190, 1332 } /* UMa */ | ||
| 10888 | , { 33, 5190, 6990, 1332 } /* Dra */ | ||
| 10889 | , { 10, 1140, 1200, 1320 } /* Cam */ | ||
| 10890 | , { 15, 7968, 8034, 1320 } /* Cep */ | ||
| 10891 | , { 15, 7416, 7908, 1316 } /* Cep */ | ||
| 10892 | , { 13, 0, 612, 1296 } /* Cas */ | ||
| 10893 | , { 50, 2196, 2340, 1296 } /* Lyn */ | ||
| 10894 | , { 82, 4350, 4860, 1272 } /* UMa */ | ||
| 10895 | , { 33, 5490, 5670, 1272 } /* Dra */ | ||
| 10896 | , { 15, 7908, 7968, 1266 } /* Cep */ | ||
| 10897 | , { 10, 1200, 1800, 1260 } /* Cam */ | ||
| 10898 | , { 13, 8232, 8400, 1260 } /* Cas */ | ||
| 10899 | , { 33, 5670, 6120, 1236 } /* Dra */ | ||
| 10900 | , { 62, 735, 906, 1212 } /* Per */ | ||
| 10901 | , { 33, 6120, 6564, 1212 } /* Dra */ | ||
| 10902 | , { 13, 0, 492, 1200 } /* Cas */ | ||
| 10903 | , { 62, 492, 600, 1200 } /* Per */ | ||
| 10904 | , { 50, 2340, 2448, 1200 } /* Lyn */ | ||
| 10905 | , { 13, 8400, 8640, 1200 } /* Cas */ | ||
| 10906 | , { 82, 4860, 5052, 1164 } /* UMa */ | ||
| 10907 | , { 13, 0, 402, 1152 } /* Cas */ | ||
| 10908 | , { 13, 8490, 8640, 1152 } /* Cas */ | ||
| 10909 | , { 39, 6543, 6564, 1140 } /* Her */ | ||
| 10910 | , { 33, 6564, 6870, 1140 } /* Dra */ | ||
| 10911 | , { 30, 6870, 6900, 1140 } /* Cyg */ | ||
| 10912 | , { 62, 600, 735, 1128 } /* Per */ | ||
| 10913 | , { 82, 3030, 3300, 1128 } /* UMa */ | ||
| 10914 | , { 13, 60, 312, 1104 } /* Cas */ | ||
| 10915 | , { 82, 4320, 4350, 1080 } /* UMa */ | ||
| 10916 | , { 50, 2448, 2652, 1068 } /* Lyn */ | ||
| 10917 | , { 30, 7887, 7908, 1056 } /* Cyg */ | ||
| 10918 | , { 30, 7875, 7887, 1050 } /* Cyg */ | ||
| 10919 | , { 30, 6900, 6984, 1044 } /* Cyg */ | ||
| 10920 | , { 82, 3300, 3660, 1008 } /* UMa */ | ||
| 10921 | , { 82, 3660, 3882, 960 } /* UMa */ | ||
| 10922 | , { 8, 5556, 5670, 960 } /* Boo */ | ||
| 10923 | , { 39, 5670, 5880, 960 } /* Her */ | ||
| 10924 | , { 50, 3330, 3450, 954 } /* Lyn */ | ||
| 10925 | , { 0, 0, 906, 882 } /* And */ | ||
| 10926 | , { 62, 906, 924, 882 } /* Per */ | ||
| 10927 | , { 51, 6969, 6984, 876 } /* Lyr */ | ||
| 10928 | , { 62, 1620, 1689, 864 } /* Per */ | ||
| 10929 | , { 30, 7824, 7875, 864 } /* Cyg */ | ||
| 10930 | , { 44, 7875, 7920, 864 } /* Lac */ | ||
| 10931 | , { 7, 2352, 2652, 852 } /* Aur */ | ||
| 10932 | , { 50, 2652, 2790, 852 } /* Lyn */ | ||
| 10933 | , { 0, 0, 720, 840 } /* And */ | ||
| 10934 | , { 44, 7920, 8214, 840 } /* Lac */ | ||
| 10935 | , { 44, 8214, 8232, 828 } /* Lac */ | ||
| 10936 | , { 0, 8232, 8460, 828 } /* And */ | ||
| 10937 | , { 62, 924, 978, 816 } /* Per */ | ||
| 10938 | , { 82, 3882, 3960, 816 } /* UMa */ | ||
| 10939 | , { 29, 4320, 4440, 816 } /* CVn */ | ||
| 10940 | , { 50, 2790, 3330, 804 } /* Lyn */ | ||
| 10941 | , { 48, 3330, 3558, 804 } /* LMi */ | ||
| 10942 | , { 0, 258, 507, 792 } /* And */ | ||
| 10943 | , { 8, 5466, 5556, 792 } /* Boo */ | ||
| 10944 | , { 0, 8460, 8550, 770 } /* And */ | ||
| 10945 | , { 29, 4440, 4770, 768 } /* CVn */ | ||
| 10946 | , { 0, 8550, 8640, 752 } /* And */ | ||
| 10947 | , { 29, 5025, 5052, 738 } /* CVn */ | ||
| 10948 | , { 80, 870, 978, 736 } /* Tri */ | ||
| 10949 | , { 62, 978, 1620, 736 } /* Per */ | ||
| 10950 | , { 7, 1620, 1710, 720 } /* Aur */ | ||
| 10951 | , { 51, 6543, 6969, 720 } /* Lyr */ | ||
| 10952 | , { 82, 3960, 4320, 696 } /* UMa */ | ||
| 10953 | , { 30, 7080, 7530, 696 } /* Cyg */ | ||
| 10954 | , { 7, 1710, 2118, 684 } /* Aur */ | ||
| 10955 | , { 48, 3558, 3780, 684 } /* LMi */ | ||
| 10956 | , { 29, 4770, 5025, 684 } /* CVn */ | ||
| 10957 | , { 0, 0, 24, 672 } /* And */ | ||
| 10958 | , { 80, 507, 600, 672 } /* Tri */ | ||
| 10959 | , { 7, 2118, 2352, 672 } /* Aur */ | ||
| 10960 | , { 37, 2838, 2880, 672 } /* Gem */ | ||
| 10961 | , { 30, 7530, 7824, 672 } /* Cyg */ | ||
| 10962 | , { 30, 6933, 7080, 660 } /* Cyg */ | ||
| 10963 | , { 80, 690, 870, 654 } /* Tri */ | ||
| 10964 | , { 25, 5820, 5880, 648 } /* CrB */ | ||
| 10965 | , { 8, 5430, 5466, 624 } /* Boo */ | ||
| 10966 | , { 25, 5466, 5820, 624 } /* CrB */ | ||
| 10967 | , { 51, 6612, 6792, 624 } /* Lyr */ | ||
| 10968 | , { 48, 3870, 3960, 612 } /* LMi */ | ||
| 10969 | , { 51, 6792, 6933, 612 } /* Lyr */ | ||
| 10970 | , { 80, 600, 690, 600 } /* Tri */ | ||
| 10971 | , { 66, 258, 306, 570 } /* Psc */ | ||
| 10972 | , { 48, 3780, 3870, 564 } /* LMi */ | ||
| 10973 | , { 87, 7650, 7710, 564 } /* Vul */ | ||
| 10974 | , { 77, 2052, 2118, 548 } /* Tau */ | ||
| 10975 | , { 0, 24, 51, 528 } /* And */ | ||
| 10976 | , { 73, 5730, 5772, 528 } /* Ser */ | ||
| 10977 | , { 37, 2118, 2238, 516 } /* Gem */ | ||
| 10978 | , { 87, 7140, 7290, 510 } /* Vul */ | ||
| 10979 | , { 87, 6792, 6930, 506 } /* Vul */ | ||
| 10980 | , { 0, 51, 306, 504 } /* And */ | ||
| 10981 | , { 87, 7290, 7404, 492 } /* Vul */ | ||
| 10982 | , { 37, 2811, 2838, 480 } /* Gem */ | ||
| 10983 | , { 87, 7404, 7650, 468 } /* Vul */ | ||
| 10984 | , { 87, 6930, 7140, 460 } /* Vul */ | ||
| 10985 | , { 6, 1182, 1212, 456 } /* Ari */ | ||
| 10986 | , { 75, 6792, 6840, 444 } /* Sge */ | ||
| 10987 | , { 59, 2052, 2076, 432 } /* Ori */ | ||
| 10988 | , { 37, 2238, 2271, 420 } /* Gem */ | ||
| 10989 | , { 75, 6840, 7140, 388 } /* Sge */ | ||
| 10990 | , { 77, 1788, 1920, 384 } /* Tau */ | ||
| 10991 | , { 39, 5730, 5790, 384 } /* Her */ | ||
| 10992 | , { 75, 7140, 7290, 378 } /* Sge */ | ||
| 10993 | , { 77, 1662, 1788, 372 } /* Tau */ | ||
| 10994 | , { 77, 1920, 2016, 372 } /* Tau */ | ||
| 10995 | , { 23, 4620, 4860, 360 } /* Com */ | ||
| 10996 | , { 39, 6210, 6570, 344 } /* Her */ | ||
| 10997 | , { 23, 4272, 4620, 336 } /* Com */ | ||
| 10998 | , { 37, 2700, 2811, 324 } /* Gem */ | ||
| 10999 | , { 39, 6030, 6210, 308 } /* Her */ | ||
| 11000 | , { 61, 0, 51, 300 } /* Peg */ | ||
| 11001 | , { 77, 2016, 2076, 300 } /* Tau */ | ||
| 11002 | , { 37, 2520, 2700, 300 } /* Gem */ | ||
| 11003 | , { 61, 7602, 7680, 300 } /* Peg */ | ||
| 11004 | , { 37, 2271, 2496, 288 } /* Gem */ | ||
| 11005 | , { 39, 6570, 6792, 288 } /* Her */ | ||
| 11006 | , { 31, 7515, 7578, 284 } /* Del */ | ||
| 11007 | , { 61, 7578, 7602, 284 } /* Peg */ | ||
| 11008 | , { 45, 4146, 4272, 264 } /* Leo */ | ||
| 11009 | , { 59, 2247, 2271, 240 } /* Ori */ | ||
| 11010 | , { 37, 2496, 2520, 240 } /* Gem */ | ||
| 11011 | , { 21, 2811, 2853, 240 } /* Cnc */ | ||
| 11012 | , { 61, 8580, 8640, 240 } /* Peg */ | ||
| 11013 | , { 6, 600, 1182, 238 } /* Ari */ | ||
| 11014 | , { 31, 7251, 7308, 204 } /* Del */ | ||
| 11015 | , { 8, 4860, 5430, 192 } /* Boo */ | ||
| 11016 | , { 61, 8190, 8580, 180 } /* Peg */ | ||
| 11017 | , { 21, 2853, 3330, 168 } /* Cnc */ | ||
| 11018 | , { 45, 3330, 3870, 168 } /* Leo */ | ||
| 11019 | , { 58, 6570, 6718.4, 150 } /* Oph */ | ||
| 11020 | , { 3, 6718.4, 6792, 150 } /* Aql */ | ||
| 11021 | , { 31, 7500, 7515, 144 } /* Del */ | ||
| 11022 | , { 20, 2520, 2526, 132 } /* CMi */ | ||
| 11023 | , { 73, 6570, 6633, 108 } /* Ser */ | ||
| 11024 | , { 39, 5790, 6030, 96 } /* Her */ | ||
| 11025 | , { 58, 6570, 6633, 72 } /* Oph */ | ||
| 11026 | , { 61, 7728, 7800, 66 } /* Peg */ | ||
| 11027 | , { 66, 0, 720, 48 } /* Psc */ | ||
| 11028 | , { 73, 6690, 6792, 48 } /* Ser */ | ||
| 11029 | , { 31, 7308, 7500, 48 } /* Del */ | ||
| 11030 | , { 34, 7500, 7680, 48 } /* Equ */ | ||
| 11031 | , { 61, 7680, 7728, 48 } /* Peg */ | ||
| 11032 | , { 61, 7920, 8190, 48 } /* Peg */ | ||
| 11033 | , { 61, 7800, 7920, 42 } /* Peg */ | ||
| 11034 | , { 20, 2526, 2592, 36 } /* CMi */ | ||
| 11035 | , { 77, 1290, 1662, 0 } /* Tau */ | ||
| 11036 | , { 59, 1662, 1680, 0 } /* Ori */ | ||
| 11037 | , { 20, 2592, 2910, 0 } /* CMi */ | ||
| 11038 | , { 85, 5280, 5430, 0 } /* Vir */ | ||
| 11039 | , { 58, 6420, 6570, 0 } /* Oph */ | ||
| 11040 | , { 16, 954, 1182, -42 } /* Cet */ | ||
| 11041 | , { 77, 1182, 1290, -42 } /* Tau */ | ||
| 11042 | , { 73, 5430, 5856, -78 } /* Ser */ | ||
| 11043 | , { 59, 1680, 1830, -96 } /* Ori */ | ||
| 11044 | , { 59, 2100, 2247, -96 } /* Ori */ | ||
| 11045 | , { 73, 6420, 6468, -96 } /* Ser */ | ||
| 11046 | , { 73, 6570, 6690, -96 } /* Ser */ | ||
| 11047 | , { 3, 6690, 6792, -96 } /* Aql */ | ||
| 11048 | , { 66, 8190, 8580, -96 } /* Psc */ | ||
| 11049 | , { 45, 3870, 4146, -144 } /* Leo */ | ||
| 11050 | , { 85, 4146, 4260, -144 } /* Vir */ | ||
| 11051 | , { 66, 0, 120, -168 } /* Psc */ | ||
| 11052 | , { 66, 8580, 8640, -168 } /* Psc */ | ||
| 11053 | , { 85, 5130, 5280, -192 } /* Vir */ | ||
| 11054 | , { 58, 5730, 5856, -192 } /* Oph */ | ||
| 11055 | , { 3, 7200, 7392, -216 } /* Aql */ | ||
| 11056 | , { 4, 7680, 7872, -216 } /* Aqr */ | ||
| 11057 | , { 58, 6180, 6468, -240 } /* Oph */ | ||
| 11058 | , { 54, 2100, 2910, -264 } /* Mon */ | ||
| 11059 | , { 35, 1770, 1830, -264 } /* Eri */ | ||
| 11060 | , { 59, 1830, 2100, -264 } /* Ori */ | ||
| 11061 | , { 41, 2910, 3012, -264 } /* Hya */ | ||
| 11062 | , { 74, 3450, 3870, -264 } /* Sex */ | ||
| 11063 | , { 85, 4260, 4620, -264 } /* Vir */ | ||
| 11064 | , { 58, 6330, 6360, -280 } /* Oph */ | ||
| 11065 | , { 3, 6792, 7200, -288.8 } /* Aql */ | ||
| 11066 | , { 35, 1740, 1770, -348 } /* Eri */ | ||
| 11067 | , { 4, 7392, 7680, -360 } /* Aqr */ | ||
| 11068 | , { 73, 6180, 6570, -384 } /* Ser */ | ||
| 11069 | , { 72, 6570, 6792, -384 } /* Sct */ | ||
| 11070 | , { 41, 3012, 3090, -408 } /* Hya */ | ||
| 11071 | , { 58, 5856, 5895, -438 } /* Oph */ | ||
| 11072 | , { 41, 3090, 3270, -456 } /* Hya */ | ||
| 11073 | , { 26, 3870, 3900, -456 } /* Crt */ | ||
| 11074 | , { 71, 5856, 5895, -462 } /* Sco */ | ||
| 11075 | , { 47, 5640, 5730, -480 } /* Lib */ | ||
| 11076 | , { 28, 4530, 4620, -528 } /* Crv */ | ||
| 11077 | , { 85, 4620, 5130, -528 } /* Vir */ | ||
| 11078 | , { 41, 3270, 3510, -576 } /* Hya */ | ||
| 11079 | , { 16, 600, 954, -585.2 } /* Cet */ | ||
| 11080 | , { 35, 954, 1350, -585.2 } /* Eri */ | ||
| 11081 | , { 26, 3900, 4260, -588 } /* Crt */ | ||
| 11082 | , { 28, 4260, 4530, -588 } /* Crv */ | ||
| 11083 | , { 47, 5130, 5370, -588 } /* Lib */ | ||
| 11084 | , { 58, 5856, 6030, -590 } /* Oph */ | ||
| 11085 | , { 16, 0, 600, -612 } /* Cet */ | ||
| 11086 | , { 11, 7680, 7872, -612 } /* Cap */ | ||
| 11087 | , { 4, 7872, 8580, -612 } /* Aqr */ | ||
| 11088 | , { 16, 8580, 8640, -612 } /* Cet */ | ||
| 11089 | , { 41, 3510, 3690, -636 } /* Hya */ | ||
| 11090 | , { 35, 1692, 1740, -654 } /* Eri */ | ||
| 11091 | , { 46, 1740, 2202, -654 } /* Lep */ | ||
| 11092 | , { 11, 7200, 7680, -672 } /* Cap */ | ||
| 11093 | , { 41, 3690, 3810, -700 } /* Hya */ | ||
| 11094 | , { 41, 4530, 5370, -708 } /* Hya */ | ||
| 11095 | , { 47, 5370, 5640, -708 } /* Lib */ | ||
| 11096 | , { 71, 5640, 5760, -708 } /* Sco */ | ||
| 11097 | , { 35, 1650, 1692, -720 } /* Eri */ | ||
| 11098 | , { 58, 6030, 6336, -720 } /* Oph */ | ||
| 11099 | , { 76, 6336, 6420, -720 } /* Sgr */ | ||
| 11100 | , { 41, 3810, 3900, -748 } /* Hya */ | ||
| 11101 | , { 19, 2202, 2652, -792 } /* CMa */ | ||
| 11102 | , { 41, 4410, 4530, -792 } /* Hya */ | ||
| 11103 | , { 41, 3900, 4410, -840 } /* Hya */ | ||
| 11104 | , { 36, 1260, 1350, -864 } /* For */ | ||
| 11105 | , { 68, 3012, 3372, -882 } /* Pyx */ | ||
| 11106 | , { 35, 1536, 1650, -888 } /* Eri */ | ||
| 11107 | , { 76, 6420, 6900, -888 } /* Sgr */ | ||
| 11108 | , { 65, 7680, 8280, -888 } /* PsA */ | ||
| 11109 | , { 70, 8280, 8400, -888 } /* Scl */ | ||
| 11110 | , { 36, 1080, 1260, -950 } /* For */ | ||
| 11111 | , { 1, 3372, 3960, -954 } /* Ant */ | ||
| 11112 | , { 70, 0, 600, -960 } /* Scl */ | ||
| 11113 | , { 36, 600, 1080, -960 } /* For */ | ||
| 11114 | , { 35, 1392, 1536, -960 } /* Eri */ | ||
| 11115 | , { 70, 8400, 8640, -960 } /* Scl */ | ||
| 11116 | , { 14, 5100, 5370, -1008 } /* Cen */ | ||
| 11117 | , { 49, 5640, 5760, -1008 } /* Lup */ | ||
| 11118 | , { 71, 5760, 5911.5, -1008 } /* Sco */ | ||
| 11119 | , { 9, 1740, 1800, -1032 } /* Cae */ | ||
| 11120 | , { 22, 1800, 2370, -1032 } /* Col */ | ||
| 11121 | , { 67, 2880, 3012, -1032 } /* Pup */ | ||
| 11122 | , { 35, 1230, 1392, -1056 } /* Eri */ | ||
| 11123 | , { 71, 5911.5, 6420, -1092 } /* Sco */ | ||
| 11124 | , { 24, 6420, 6900, -1092 } /* CrA */ | ||
| 11125 | , { 76, 6900, 7320, -1092 } /* Sgr */ | ||
| 11126 | , { 53, 7320, 7680, -1092 } /* Mic */ | ||
| 11127 | , { 35, 1080, 1230, -1104 } /* Eri */ | ||
| 11128 | , { 9, 1620, 1740, -1116 } /* Cae */ | ||
| 11129 | , { 49, 5520, 5640, -1152 } /* Lup */ | ||
| 11130 | , { 63, 0, 840, -1156 } /* Phe */ | ||
| 11131 | , { 35, 960, 1080, -1176 } /* Eri */ | ||
| 11132 | , { 40, 1470, 1536, -1176 } /* Hor */ | ||
| 11133 | , { 9, 1536, 1620, -1176 } /* Cae */ | ||
| 11134 | , { 38, 7680, 7920, -1200 } /* Gru */ | ||
| 11135 | , { 67, 2160, 2880, -1218 } /* Pup */ | ||
| 11136 | , { 84, 2880, 2940, -1218 } /* Vel */ | ||
| 11137 | , { 35, 870, 960, -1224 } /* Eri */ | ||
| 11138 | , { 40, 1380, 1470, -1224 } /* Hor */ | ||
| 11139 | , { 63, 0, 660, -1236 } /* Phe */ | ||
| 11140 | , { 12, 2160, 2220, -1260 } /* Car */ | ||
| 11141 | , { 84, 2940, 3042, -1272 } /* Vel */ | ||
| 11142 | , { 40, 1260, 1380, -1276 } /* Hor */ | ||
| 11143 | , { 32, 1380, 1440, -1276 } /* Dor */ | ||
| 11144 | , { 63, 0, 570, -1284 } /* Phe */ | ||
| 11145 | , { 35, 780, 870, -1296 } /* Eri */ | ||
| 11146 | , { 64, 1620, 1800, -1296 } /* Pic */ | ||
| 11147 | , { 49, 5418, 5520, -1296 } /* Lup */ | ||
| 11148 | , { 84, 3042, 3180, -1308 } /* Vel */ | ||
| 11149 | , { 12, 2220, 2340, -1320 } /* Car */ | ||
| 11150 | , { 14, 4260, 4620, -1320 } /* Cen */ | ||
| 11151 | , { 49, 5100, 5418, -1320 } /* Lup */ | ||
| 11152 | , { 56, 5418, 5520, -1320 } /* Nor */ | ||
| 11153 | , { 32, 1440, 1560, -1356 } /* Dor */ | ||
| 11154 | , { 84, 3180, 3960, -1356 } /* Vel */ | ||
| 11155 | , { 14, 3960, 4050, -1356 } /* Cen */ | ||
| 11156 | , { 5, 6300, 6480, -1368 } /* Ara */ | ||
| 11157 | , { 78, 6480, 7320, -1368 } /* Tel */ | ||
| 11158 | , { 38, 7920, 8400, -1368 } /* Gru */ | ||
| 11159 | , { 40, 1152, 1260, -1380 } /* Hor */ | ||
| 11160 | , { 64, 1800, 1980, -1380 } /* Pic */ | ||
| 11161 | , { 12, 2340, 2460, -1392 } /* Car */ | ||
| 11162 | , { 63, 0, 480, -1404 } /* Phe */ | ||
| 11163 | , { 35, 480, 780, -1404 } /* Eri */ | ||
| 11164 | , { 63, 8400, 8640, -1404 } /* Phe */ | ||
| 11165 | , { 32, 1560, 1650, -1416 } /* Dor */ | ||
| 11166 | , { 56, 5520, 5911.5, -1440 } /* Nor */ | ||
| 11167 | , { 43, 7320, 7680, -1440 } /* Ind */ | ||
| 11168 | , { 64, 1980, 2160, -1464 } /* Pic */ | ||
| 11169 | , { 18, 5460, 5520, -1464 } /* Cir */ | ||
| 11170 | , { 5, 5911.5, 5970, -1464 } /* Ara */ | ||
| 11171 | , { 18, 5370, 5460, -1526 } /* Cir */ | ||
| 11172 | , { 5, 5970, 6030, -1526 } /* Ara */ | ||
| 11173 | , { 64, 2160, 2460, -1536 } /* Pic */ | ||
| 11174 | , { 12, 2460, 3252, -1536 } /* Car */ | ||
| 11175 | , { 14, 4050, 4260, -1536 } /* Cen */ | ||
| 11176 | , { 27, 4260, 4620, -1536 } /* Cru */ | ||
| 11177 | , { 14, 4620, 5232, -1536 } /* Cen */ | ||
| 11178 | , { 18, 4860, 4920, -1560 } /* Cir */ | ||
| 11179 | , { 5, 6030, 6060, -1560 } /* Ara */ | ||
| 11180 | , { 40, 780, 1152, -1620 } /* Hor */ | ||
| 11181 | , { 69, 1152, 1650, -1620 } /* Ret */ | ||
| 11182 | , { 18, 5310, 5370, -1620 } /* Cir */ | ||
| 11183 | , { 5, 6060, 6300, -1620 } /* Ara */ | ||
| 11184 | , { 60, 6300, 6480, -1620 } /* Pav */ | ||
| 11185 | , { 81, 7920, 8400, -1620 } /* Tuc */ | ||
| 11186 | , { 32, 1650, 2370, -1680 } /* Dor */ | ||
| 11187 | , { 18, 4920, 5310, -1680 } /* Cir */ | ||
| 11188 | , { 79, 5310, 6120, -1680 } /* TrA */ | ||
| 11189 | , { 81, 0, 480, -1800 } /* Tuc */ | ||
| 11190 | , { 42, 1260, 1650, -1800 } /* Hyi */ | ||
| 11191 | , { 86, 2370, 3252, -1800 } /* Vol */ | ||
| 11192 | , { 12, 3252, 4050, -1800 } /* Car */ | ||
| 11193 | , { 55, 4050, 4920, -1800 } /* Mus */ | ||
| 11194 | , { 60, 6480, 7680, -1800 } /* Pav */ | ||
| 11195 | , { 43, 7680, 8400, -1800 } /* Ind */ | ||
| 11196 | , { 81, 8400, 8640, -1800 } /* Tuc */ | ||
| 11197 | , { 81, 270, 480, -1824 } /* Tuc */ | ||
| 11198 | , { 42, 0, 1260, -1980 } /* Hyi */ | ||
| 11199 | , { 17, 2760, 4920, -1980 } /* Cha */ | ||
| 11200 | , { 2, 4920, 6480, -1980 } /* Aps */ | ||
| 11201 | , { 52, 1260, 2760, -2040 } /* Men */ | ||
| 11202 | , { 57, 0, 8640, -2160 } /* Oct */ | ||
| 11203 | }; | ||
| 11204 | |||
| 11205 | #define NUM_CONSTEL_BOUNDARIES 357 | ||
| 11206 | |||
| 11207 | |||
| 11208 | |||
| 11209 | /** | ||
| 11210 | * @brief | ||
| 11211 | * Determines the constellation that contains the given point in the sky. | ||
| 11212 | * | ||
| 11213 | * Given J2000 equatorial (EQJ) coordinates of a point in the sky, determines the | ||
| 11214 | * constellation that contains that point. | ||
| 11215 | * | ||
| 11216 | * @param ra | ||
| 11217 | * The right ascension (RA) of a point in the sky, using the J2000 equatorial system. | ||
| 11218 | * | ||
| 11219 | * @param dec | ||
| 11220 | * The declination (DEC) of a point in the sky, using the J2000 equatorial system. | ||
| 11221 | * | ||
| 11222 | * @return | ||
| 11223 | * If successful, `status` holds `ASTRO_SUCCESS`, | ||
| 11224 | * `symbol` holds a pointer to a 3-character string like "Ori", and | ||
| 11225 | * `name` holds a pointer to the full constellation name like "Orion". | ||
| 11226 | */ | ||
| 11227 | astro_constellation_t Astronomy_Constellation(double ra, double dec) | ||
| 11228 | { | ||
| 11229 | static astro_time_t epoch2000; | ||
| 11230 | static astro_rotation_t rot = { ASTRO_NOT_INITIALIZED }; | ||
| 11231 | astro_constellation_t constel; | ||
| 11232 | astro_spherical_t s2000; | ||
| 11233 | astro_equatorial_t b1875; | ||
| 11234 | astro_vector_t vec2000, vec1875; | ||
| 11235 | double x_ra, x_dec; | ||
| 11236 | int i, c; | ||
| 11237 | |||
| 11238 | if (dec < -90.0 || dec > +90.0) | ||
| 11239 | return ConstelErr(ASTRO_INVALID_PARAMETER); | ||
| 11240 | |||
| 11241 | /* Allow right ascension to "wrap around". Clamp to [0, 24) sidereal hours. */ | ||
| 11242 | ra = fmod(ra, 24.0); | ||
| 11243 | if (ra < 0.0) | ||
| 11244 | ra += 24.0; | ||
| 11245 | |||
| 11246 | /* Lazy-initialize the rotation matrix for converting J2000 to B1875. */ | ||
| 11247 | if (rot.status != ASTRO_SUCCESS) | ||
| 11248 | { | ||
| 11249 | /* | ||
| 11250 | Need to calculate the B1875 epoch. Based on this: | ||
| 11251 | https://en.wikipedia.org/wiki/Epoch_(astronomy)#Besselian_years | ||
| 11252 | B = 1900 + (JD - 2415020.31352) / 365.242198781 | ||
| 11253 | I'm interested in using TT instead of JD, giving: | ||
| 11254 | B = 1900 + ((TT+2451545) - 2415020.31352) / 365.242198781 | ||
| 11255 | B = 1900 + (TT + 36524.68648) / 365.242198781 | ||
| 11256 | TT = 365.242198781*(B - 1900) - 36524.68648 = -45655.741449525 | ||
| 11257 | But Astronomy_TimeFromDays() wants UT, not TT. | ||
| 11258 | Near that date, I get a historical correction of ut-tt = 3.2 seconds. | ||
| 11259 | That gives UT = -45655.74141261017 for the B1875 epoch, | ||
| 11260 | or 1874-12-31T18:12:21.950Z. | ||
| 11261 | */ | ||
| 11262 | astro_time_t time = Astronomy_TimeFromDays(-45655.74141261017); | ||
| 11263 | rot = Astronomy_Rotation_EQJ_EQD(&time); | ||
| 11264 | if (rot.status != ASTRO_SUCCESS) | ||
| 11265 | return ConstelErr(rot.status); | ||
| 11266 | |||
| 11267 | epoch2000 = Astronomy_TimeFromDays(0.0); | ||
| 11268 | } | ||
| 11269 | |||
| 11270 | /* Convert coordinates from J2000 to year 1875. */ | ||
| 11271 | s2000.status = ASTRO_SUCCESS; | ||
| 11272 | s2000.lon = ra * 15.0; | ||
| 11273 | s2000.lat = dec; | ||
| 11274 | s2000.dist = 1.0; | ||
| 11275 | vec2000 = Astronomy_VectorFromSphere(s2000, epoch2000); | ||
| 11276 | if (vec2000.status != ASTRO_SUCCESS) | ||
| 11277 | return ConstelErr(vec2000.status); | ||
| 11278 | |||
| 11279 | vec1875 = Astronomy_RotateVector(rot, vec2000); | ||
| 11280 | if (vec1875.status != ASTRO_SUCCESS) | ||
| 11281 | return ConstelErr(vec1875.status); | ||
| 11282 | |||
| 11283 | b1875 = Astronomy_EquatorFromVector(vec1875); | ||
| 11284 | if (b1875.status != ASTRO_SUCCESS) | ||
| 11285 | return ConstelErr(b1875.status); | ||
| 11286 | |||
| 11287 | /* Convert DEC from degrees, and RA from hours, to compact angle units used in the ContelBounds table. */ | ||
| 11288 | x_ra = (24.0 * 15.0) * b1875.ra; | ||
| 11289 | x_dec = 24.0 * b1875.dec; | ||
| 11290 | |||
| 11291 | /* Search for the constellation using the B1875 coordinates. */ | ||
| 11292 | c = -1; /* constellation not (yet) found */ | ||
| 11293 | for (i=0; i < NUM_CONSTEL_BOUNDARIES; ++i) | ||
| 11294 | { | ||
| 11295 | const constel_boundary_t *b = &ConstelBounds[i]; | ||
| 11296 | if ((b->dec_lo <= x_dec) && (b->ra_hi > x_ra) && (b->ra_lo <= x_ra)) | ||
| 11297 | { | ||
| 11298 | c = b->index; | ||
| 11299 | break; | ||
| 11300 | } | ||
| 11301 | } | ||
| 11302 | |||
| 11303 | if (c < 0 || c >= NUM_CONSTELLATIONS) | ||
| 11304 | return ConstelErr(ASTRO_INTERNAL_ERROR); /* should have been able to find the constellation */ | ||
| 11305 | |||
| 11306 | constel.status = ASTRO_SUCCESS; | ||
| 11307 | constel.symbol = ConstelInfo[c].symbol; | ||
| 11308 | constel.name = ConstelInfo[c].name; | ||
| 11309 | constel.ra_1875 = b1875.ra; | ||
| 11310 | constel.dec_1875 = b1875.dec; | ||
| 11311 | return constel; | ||
| 11312 | } | ||
| 11313 | |||
| 11314 | |||
| 11315 | static astro_lunar_eclipse_t LunarEclipseError(astro_status_t status) | ||
| 11316 | { | ||
| 11317 | astro_lunar_eclipse_t eclipse; | ||
| 11318 | eclipse.status = status; | ||
| 11319 | eclipse.kind = ECLIPSE_NONE; | ||
| 11320 | eclipse.peak = TimeError(); | ||
| 11321 | eclipse.obscuration = eclipse.sd_penum = eclipse.sd_partial = eclipse.sd_total = NAN; | ||
| 11322 | return eclipse; | ||
| 11323 | } | ||
| 11324 | |||
| 11325 | |||
| 11326 | /** @cond DOXYGEN_SKIP */ | ||
| 11327 | typedef struct | ||
| 11328 | { | ||
| 11329 | astro_status_t status; | ||
| 11330 | astro_time_t time; | ||
| 11331 | double u; /* dot product of (heliocentric earth) and (geocentric moon): defines the shadow plane where the Moon is */ | ||
| 11332 | double r; /* km distance between center of Moon/Earth (shaded body) and the line passing through the centers of the Sun and Earth/Moon (casting body). */ | ||
| 11333 | double k; /* umbra radius in km, at the shadow plane */ | ||
| 11334 | double p; /* penumbra radius in km, at the shadow plane */ | ||
| 11335 | astro_vector_t target; /* coordinates of target body relative to shadow-casting body at 'time' */ | ||
| 11336 | astro_vector_t dir; /* heliocentric coordinates of shadow-casting body at 'time' */ | ||
| 11337 | } | ||
| 11338 | shadow_t; /* Represents alignment of the Moon/Earth with the Earth's/Moon's shadow, for finding eclipses. */ | ||
| 11339 | |||
| 11340 | typedef struct | ||
| 11341 | { | ||
| 11342 | double radius_limit; | ||
| 11343 | double direction; | ||
| 11344 | } | ||
| 11345 | shadow_context_t; | ||
| 11346 | /** @endcond */ | ||
| 11347 | |||
| 11348 | |||
| 11349 | static double Obscuration( /* returns area of intersection of the two discs, divided by area of first disc. */ | ||
| 11350 | double a, /* radius of first disc */ | ||
| 11351 | double b, /* radius of second disc */ | ||
| 11352 | double c) /* distance between the centers of the discs */ | ||
| 11353 | { | ||
| 11354 | double x; /* Horizontal location of intersection point on both circumferences */ | ||
| 11355 | double y; /* Positive vertical location of intersection point on both circumferences */ | ||
| 11356 | double radicand, lens1, lens2, obs; | ||
| 11357 | |||
| 11358 | if (a <= 0.0 || b <= 0.0) | ||
| 11359 | return 0.0; /* invalid radius */ | ||
| 11360 | |||
| 11361 | if (c < 0.0) | ||
| 11362 | return 0.0; /* invalid distance between centers */ | ||
| 11363 | |||
| 11364 | if (c >= a + b) | ||
| 11365 | return 0.0; /* the discs are too far apart to have any overlapping area */ | ||
| 11366 | |||
| 11367 | if (c == 0.0) | ||
| 11368 | { | ||
| 11369 | /* The discs have a common center. Therefore, one disc is inside the other. */ | ||
| 11370 | return (a <= b) ? 1.0 : (b*b)/(a*a); | ||
| 11371 | } | ||
| 11372 | |||
| 11373 | x = (a*a - b*b + c*c) / (2 * c); | ||
| 11374 | |||
| 11375 | radicand = a*a - x*x; | ||
| 11376 | if (radicand <= 0.0) | ||
| 11377 | { | ||
| 11378 | /* The circumferences do not intersect, or are tangent. */ | ||
| 11379 | /* We already ruled out the case of non-overlapping discs. */ | ||
| 11380 | /* Therefore, one disc is inside the other. */ | ||
| 11381 | return (a <= b) ? 1.0 : (b*b)/(a*a); | ||
| 11382 | } | ||
| 11383 | |||
| 11384 | /* The discs overlap fractionally in a pair of lens-shaped areas. */ | ||
| 11385 | |||
| 11386 | y = sqrt(radicand); | ||
| 11387 | |||
| 11388 | /* Return the overlapping fractional area. */ | ||
| 11389 | /* There are two lens-shaped areas, one to the left of x, the other to the right of x. */ | ||
| 11390 | /* Each part is calculated by subtracting a triangular area from a sector's area. */ | ||
| 11391 | lens1 = a*a*acos(x/a) - x*y; | ||
| 11392 | lens2 = b*b*acos((c-x)/b) - (c-x)*y; | ||
| 11393 | |||
| 11394 | /* Find the fractional area with respect to the first disc. */ | ||
| 11395 | obs = (lens1 + lens2) / (PI*a*a); | ||
| 11396 | return obs; | ||
| 11397 | } | ||
| 11398 | |||
| 11399 | |||
| 11400 | static shadow_t ShadowError(astro_status_t status) | ||
| 11401 | { | ||
| 11402 | shadow_t shadow; | ||
| 11403 | memset(&shadow, 0, sizeof(shadow)); | ||
| 11404 | shadow.status = status; | ||
| 11405 | return shadow; | ||
| 11406 | } | ||
| 11407 | |||
| 11408 | |||
| 11409 | static shadow_t CalcShadow( | ||
| 11410 | double body_radius_km, | ||
| 11411 | astro_time_t time, | ||
| 11412 | astro_vector_t target, | ||
| 11413 | astro_vector_t dir) | ||
| 11414 | { | ||
| 11415 | double dx, dy, dz; | ||
| 11416 | shadow_t shadow; | ||
| 11417 | |||
| 11418 | shadow.target = target; | ||
| 11419 | shadow.dir = dir; | ||
| 11420 | |||
| 11421 | shadow.u = (dir.x*target.x + dir.y*target.y + dir.z*target.z) / (dir.x*dir.x + dir.y*dir.y + dir.z*dir.z); | ||
| 11422 | |||
| 11423 | dx = (shadow.u * dir.x) - target.x; | ||
| 11424 | dy = (shadow.u * dir.y) - target.y; | ||
| 11425 | dz = (shadow.u * dir.z) - target.z; | ||
| 11426 | shadow.r = KM_PER_AU * sqrt(dx*dx + dy*dy + dz*dz); | ||
| 11427 | |||
| 11428 | shadow.k = +SUN_RADIUS_KM - (1.0 + shadow.u)*(SUN_RADIUS_KM - body_radius_km); | ||
| 11429 | shadow.p = -SUN_RADIUS_KM + (1.0 + shadow.u)*(SUN_RADIUS_KM + body_radius_km); | ||
| 11430 | shadow.status = ASTRO_SUCCESS; | ||
| 11431 | shadow.time = time; | ||
| 11432 | |||
| 11433 | return shadow; | ||
| 11434 | } | ||
| 11435 | |||
| 11436 | |||
| 11437 | static shadow_t PlanetShadow(astro_body_t body, double planet_radius_km, astro_time_t time) | ||
| 11438 | { | ||
| 11439 | astro_vector_t e, p, g; | ||
| 11440 | |||
| 11441 | /* Calculate light-travel-corrected vector from Earth to planet. */ | ||
| 11442 | g = Astronomy_GeoVector(body, time, ABERRATION); | ||
| 11443 | if (g.status != ASTRO_SUCCESS) | ||
| 11444 | return ShadowError(g.status); | ||
| 11445 | |||
| 11446 | /* Calculate light-travel-corrected vector from Earth to Sun. */ | ||
| 11447 | e = Astronomy_GeoVector(BODY_SUN, time, ABERRATION); | ||
| 11448 | if (e.status != ASTRO_SUCCESS) | ||
| 11449 | return ShadowError(e.status); | ||
| 11450 | |||
| 11451 | /* Deduce light-travel-corrected vector from Sun to planet. */ | ||
| 11452 | p.status = ASTRO_SUCCESS; | ||
| 11453 | p.t = time; | ||
| 11454 | p.x = g.x - e.x; | ||
| 11455 | p.y = g.y - e.y; | ||
| 11456 | p.z = g.z - e.z; | ||
| 11457 | |||
| 11458 | /* Calcluate Earth's position from the planet's point of view. */ | ||
| 11459 | e.x = -g.x; | ||
| 11460 | e.y = -g.y; | ||
| 11461 | e.z = -g.z; | ||
| 11462 | |||
| 11463 | return CalcShadow(planet_radius_km, time, e, p); | ||
| 11464 | } | ||
| 11465 | |||
| 11466 | |||
| 11467 | static shadow_t EarthShadow(astro_time_t time) | ||
| 11468 | { | ||
| 11469 | /* This function helps find when the Earth's shadow falls upon the Moon. */ | ||
| 11470 | astro_vector_t e, m; | ||
| 11471 | |||
| 11472 | /* Find geocentric Sun with aberration correction. */ | ||
| 11473 | e = Astronomy_GeoVector(BODY_SUN, time, ABERRATION); | ||
| 11474 | if (e.status != ASTRO_SUCCESS) | ||
| 11475 | return ShadowError(e.status); | ||
| 11476 | |||
| 11477 | /* Convert geocentric Sun to heliocentric Earth. */ | ||
| 11478 | /* Thus `e` points in the direction of sunlight heading toward the Earth's center. */ | ||
| 11479 | e.x = -e.x; | ||
| 11480 | e.y = -e.y; | ||
| 11481 | e.z = -e.z; | ||
| 11482 | |||
| 11483 | /* Find geocentric Moon. */ | ||
| 11484 | m = Astronomy_GeoMoon(time); /* This function never fails; no need to check return value */ | ||
| 11485 | |||
| 11486 | return CalcShadow(EARTH_ECLIPSE_RADIUS_KM, time, m, e); | ||
| 11487 | } | ||
| 11488 | |||
| 11489 | |||
| 11490 | static shadow_t MoonShadow(astro_time_t time) | ||
| 11491 | { | ||
| 11492 | /* This function helps find when the Moon's shadow falls upon the Earth. */ | ||
| 11493 | |||
| 11494 | astro_vector_t s, e, m; | ||
| 11495 | |||
| 11496 | /* Calculate geocentric Sun with aberration correction. */ | ||
| 11497 | s = Astronomy_GeoVector(BODY_SUN, time, ABERRATION); | ||
| 11498 | if (s.status != ASTRO_SUCCESS) | ||
| 11499 | return ShadowError(s.status); | ||
| 11500 | |||
| 11501 | m = Astronomy_GeoMoon(time); /* geocentric Moon */ | ||
| 11502 | |||
| 11503 | /* Calculate lunacentric Earth. */ | ||
| 11504 | e.status = m.status; | ||
| 11505 | e.x = -m.x; | ||
| 11506 | e.y = -m.y; | ||
| 11507 | e.z = -m.z; | ||
| 11508 | e.t = m.t; | ||
| 11509 | |||
| 11510 | /* Convert geocentric moon to heliocentric Moon. */ | ||
| 11511 | m.x -= s.x; | ||
| 11512 | m.y -= s.y; | ||
| 11513 | m.z -= s.z; | ||
| 11514 | |||
| 11515 | return CalcShadow(MOON_MEAN_RADIUS_KM, time, e, m); | ||
| 11516 | } | ||
| 11517 | |||
| 11518 | |||
| 11519 | /** @cond DOXYGEN_SKIP */ | ||
| 11520 | typedef shadow_t (* shadow_func_t) (astro_time_t time); | ||
| 11521 | /** @endcond */ | ||
| 11522 | |||
| 11523 | |||
| 11524 | static astro_func_result_t shadow_distance_slope(void *context, astro_time_t time) | ||
| 11525 | { | ||
| 11526 | const double dt = 1.0 / 86400.0; | ||
| 11527 | astro_time_t t1, t2; | ||
| 11528 | astro_func_result_t result; | ||
| 11529 | shadow_t shadow1, shadow2; | ||
| 11530 | shadow_func_t shadowfunc = (shadow_func_t) context; | ||
| 11531 | |||
| 11532 | t1 = Astronomy_AddDays(time, -dt); | ||
| 11533 | t2 = Astronomy_AddDays(time, +dt); | ||
| 11534 | |||
| 11535 | shadow1 = shadowfunc(t1); | ||
| 11536 | if (shadow1.status != ASTRO_SUCCESS) | ||
| 11537 | return FuncError(shadow1.status); | ||
| 11538 | |||
| 11539 | shadow2 = shadowfunc(t2); | ||
| 11540 | if (shadow2.status != ASTRO_SUCCESS) | ||
| 11541 | return FuncError(shadow2.status); | ||
| 11542 | |||
| 11543 | result.value = (shadow2.r - shadow1.r) / dt; | ||
| 11544 | result.status = ASTRO_SUCCESS; | ||
| 11545 | return result; | ||
| 11546 | } | ||
| 11547 | |||
| 11548 | |||
| 11549 | static shadow_t PeakEarthShadow(astro_time_t search_center_time) | ||
| 11550 | { | ||
| 11551 | /* Search for when the Earth's shadow axis is closest to the center of the Moon. */ | ||
| 11552 | |||
| 11553 | astro_time_t t1, t2; | ||
| 11554 | astro_search_result_t result; | ||
| 11555 | const double window = 0.03; /* days before/after full moon to search for minimum shadow distance */ | ||
| 11556 | |||
| 11557 | t1 = Astronomy_AddDays(search_center_time, -window); | ||
| 11558 | t2 = Astronomy_AddDays(search_center_time, +window); | ||
| 11559 | |||
| 11560 | result = Astronomy_Search(shadow_distance_slope, (void *)EarthShadow, t1, t2, 1.0); | ||
| 11561 | if (result.status != ASTRO_SUCCESS) | ||
| 11562 | return ShadowError(result.status); | ||
| 11563 | |||
| 11564 | return EarthShadow(result.time); | ||
| 11565 | } | ||
| 11566 | |||
| 11567 | |||
| 11568 | static shadow_t PeakMoonShadow(astro_time_t search_center_time) | ||
| 11569 | { | ||
| 11570 | /* Search for when the Moon's shadow axis is closest to the center of the Earth. */ | ||
| 11571 | |||
| 11572 | astro_time_t t1, t2; | ||
| 11573 | astro_search_result_t result; | ||
| 11574 | const double window = 0.03; /* days before/after new moon to search for minimum shadow distance */ | ||
| 11575 | |||
| 11576 | t1 = Astronomy_AddDays(search_center_time, -window); | ||
| 11577 | t2 = Astronomy_AddDays(search_center_time, +window); | ||
| 11578 | |||
| 11579 | result = Astronomy_Search(shadow_distance_slope, (void *)MoonShadow, t1, t2, 1.0); | ||
| 11580 | if (result.status != ASTRO_SUCCESS) | ||
| 11581 | return ShadowError(result.status); | ||
| 11582 | |||
| 11583 | return MoonShadow(result.time); | ||
| 11584 | } | ||
| 11585 | |||
| 11586 | |||
| 11587 | /** @cond DOXYGEN_SKIP */ | ||
| 11588 | typedef struct | ||
| 11589 | { | ||
| 11590 | astro_body_t body; | ||
| 11591 | double planet_radius_km; | ||
| 11592 | double direction; /* used for transit start/finish search only */ | ||
| 11593 | } | ||
| 11594 | planet_shadow_context_t; | ||
| 11595 | /** @endcond */ | ||
| 11596 | |||
| 11597 | |||
| 11598 | static astro_func_result_t planet_shadow_distance_slope(void *context, astro_time_t time) | ||
| 11599 | { | ||
| 11600 | const double dt = 1.0 / 86400.0; | ||
| 11601 | astro_time_t t1, t2; | ||
| 11602 | astro_func_result_t result; | ||
| 11603 | shadow_t shadow1, shadow2; | ||
| 11604 | const planet_shadow_context_t *p = (const planet_shadow_context_t *) context; | ||
| 11605 | |||
| 11606 | t1 = Astronomy_AddDays(time, -dt); | ||
| 11607 | t2 = Astronomy_AddDays(time, +dt); | ||
| 11608 | |||
| 11609 | shadow1 = PlanetShadow(p->body, p->planet_radius_km, t1); | ||
| 11610 | if (shadow1.status != ASTRO_SUCCESS) | ||
| 11611 | return FuncError(shadow1.status); | ||
| 11612 | |||
| 11613 | shadow2 = PlanetShadow(p->body, p->planet_radius_km, t2); | ||
| 11614 | if (shadow2.status != ASTRO_SUCCESS) | ||
| 11615 | return FuncError(shadow2.status); | ||
| 11616 | |||
| 11617 | result.value = (shadow2.r - shadow1.r) / dt; | ||
| 11618 | result.status = ASTRO_SUCCESS; | ||
| 11619 | return result; | ||
| 11620 | } | ||
| 11621 | |||
| 11622 | |||
| 11623 | static shadow_t PeakPlanetShadow(astro_body_t body, double planet_radius_km, astro_time_t search_center_time) | ||
| 11624 | { | ||
| 11625 | /* Search for when the body's shadow is closest to the center of the Earth. */ | ||
| 11626 | |||
| 11627 | astro_time_t t1, t2; | ||
| 11628 | astro_search_result_t result; | ||
| 11629 | planet_shadow_context_t context; | ||
| 11630 | const double window = 1.0; /* days before/after inferior conjunction to search for minimum shadow distance */ | ||
| 11631 | |||
| 11632 | t1 = Astronomy_AddDays(search_center_time, -window); | ||
| 11633 | t2 = Astronomy_AddDays(search_center_time, +window); | ||
| 11634 | |||
| 11635 | context.body = body; | ||
| 11636 | context.planet_radius_km = planet_radius_km; | ||
| 11637 | context.direction = 0.0; /* not used in this search */ | ||
| 11638 | |||
| 11639 | result = Astronomy_Search(planet_shadow_distance_slope, &context, t1, t2, 1.0); | ||
| 11640 | if (result.status != ASTRO_SUCCESS) | ||
| 11641 | return ShadowError(result.status); | ||
| 11642 | |||
| 11643 | return PlanetShadow(body, planet_radius_km, result.time); | ||
| 11644 | } | ||
| 11645 | |||
| 11646 | |||
| 11647 | static astro_func_result_t shadow_distance(void *context, astro_time_t time) | ||
| 11648 | { | ||
| 11649 | astro_func_result_t result; | ||
| 11650 | const shadow_context_t *p = (const shadow_context_t *) context; | ||
| 11651 | shadow_t shadow = EarthShadow(time); | ||
| 11652 | if (shadow.status != ASTRO_SUCCESS) | ||
| 11653 | return FuncError(shadow.status); | ||
| 11654 | |||
| 11655 | result.value = p->direction * (shadow.r - p->radius_limit); | ||
| 11656 | result.status = ASTRO_SUCCESS; | ||
| 11657 | return result; | ||
| 11658 | } | ||
| 11659 | |||
| 11660 | |||
| 11661 | static double ShadowSemiDurationMinutes(astro_time_t center_time, double radius_limit, double window_minutes) | ||
| 11662 | { | ||
| 11663 | /* Search backwards and forwards from the center time until shadow axis distance crosses radius limit. */ | ||
| 11664 | double window = window_minutes / (24.0 * 60.0); | ||
| 11665 | shadow_context_t context; | ||
| 11666 | astro_search_result_t s1, s2; | ||
| 11667 | astro_time_t before, after; | ||
| 11668 | |||
| 11669 | before = Astronomy_AddDays(center_time, -window); | ||
| 11670 | after = Astronomy_AddDays(center_time, +window); | ||
| 11671 | |||
| 11672 | context.radius_limit = radius_limit; | ||
| 11673 | context.direction = -1.0; | ||
| 11674 | s1 = Astronomy_Search(shadow_distance, &context, before, center_time, 1.0); | ||
| 11675 | |||
| 11676 | context.direction = +1.0; | ||
| 11677 | s2 = Astronomy_Search(shadow_distance, &context, center_time, after, 1.0); | ||
| 11678 | |||
| 11679 | if (s1.status != ASTRO_SUCCESS || s2.status != ASTRO_SUCCESS) | ||
| 11680 | return -1.0; /* something went wrong! */ | ||
| 11681 | |||
| 11682 | return (s2.time.ut - s1.time.ut) * ((24.0 * 60.0) / 2.0); /* convert days to minutes and average the semi-durations. */ | ||
| 11683 | } | ||
| 11684 | |||
| 11685 | |||
| 11686 | /** | ||
| 11687 | * @brief Searches for a lunar eclipse. | ||
| 11688 | * | ||
| 11689 | * This function finds the first lunar eclipse that occurs after `startTime`. | ||
| 11690 | * A lunar eclipse may be penumbral, partial, or total. | ||
| 11691 | * See #astro_lunar_eclipse_t for more information. | ||
| 11692 | * To find a series of lunar eclipses, call this function once, | ||
| 11693 | * then keep calling #Astronomy_NextLunarEclipse as many times as desired, | ||
| 11694 | * passing in the `peak` value returned from the previous call. | ||
| 11695 | * | ||
| 11696 | * @param startTime | ||
| 11697 | * The date and time for starting the search for a lunar eclipse. | ||
| 11698 | * | ||
| 11699 | * @return | ||
| 11700 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 11701 | * and the remaining structure fields will be valid. | ||
| 11702 | * Any other value indicates an error. | ||
| 11703 | */ | ||
| 11704 | astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime) | ||
| 11705 | { | ||
| 11706 | const double PruneLatitude = 1.8; /* full Moon's ecliptic latitude above which eclipse is impossible */ | ||
| 11707 | astro_time_t fmtime; | ||
| 11708 | astro_lunar_eclipse_t eclipse; | ||
| 11709 | astro_search_result_t fullmoon; | ||
| 11710 | shadow_t shadow; | ||
| 11711 | int fmcount; | ||
| 11712 | double eclip_lat, eclip_lon, distance; | ||
| 11713 | |||
| 11714 | /* Iterate through consecutive full moons until we find any kind of lunar eclipse. */ | ||
| 11715 | fmtime = startTime; | ||
| 11716 | for (fmcount=0; fmcount < 12; ++fmcount) | ||
| 11717 | { | ||
| 11718 | /* Search for the next full moon. Any eclipse will be near it. */ | ||
| 11719 | fullmoon = Astronomy_SearchMoonPhase(180.0, fmtime, 40.0); | ||
| 11720 | if (fullmoon.status != ASTRO_SUCCESS) | ||
| 11721 | return LunarEclipseError(fullmoon.status); | ||
| 11722 | |||
| 11723 | /* Pruning: if the full Moon's ecliptic latitude is too large, a lunar eclipse is not possible. */ | ||
| 11724 | CalcMoon(fullmoon.time.tt / 36525.0, &eclip_lon, &eclip_lat, &distance); | ||
| 11725 | if (RAD2DEG * fabs(eclip_lat) < PruneLatitude) | ||
| 11726 | { | ||
| 11727 | /* Search near the full moon for the time when the center of the Moon */ | ||
| 11728 | /* is closest to the line passing through the centers of the Sun and Earth. */ | ||
| 11729 | shadow = PeakEarthShadow(fullmoon.time); | ||
| 11730 | if (shadow.status != ASTRO_SUCCESS) | ||
| 11731 | return LunarEclipseError(shadow.status); | ||
| 11732 | |||
| 11733 | if (shadow.r < shadow.p + MOON_MEAN_RADIUS_KM) | ||
| 11734 | { | ||
| 11735 | /* This is at least a penumbral eclipse. We will return a result. */ | ||
| 11736 | eclipse.status = ASTRO_SUCCESS; | ||
| 11737 | eclipse.kind = ECLIPSE_PENUMBRAL; | ||
| 11738 | eclipse.obscuration = 0.0; | ||
| 11739 | eclipse.peak = shadow.time; | ||
| 11740 | eclipse.sd_total = 0.0; | ||
| 11741 | eclipse.sd_partial = 0.0; | ||
| 11742 | eclipse.sd_penum = ShadowSemiDurationMinutes(shadow.time, shadow.p + MOON_MEAN_RADIUS_KM, 200.0); | ||
| 11743 | if (eclipse.sd_penum <= 0.0) | ||
| 11744 | return LunarEclipseError(ASTRO_SEARCH_FAILURE); | ||
| 11745 | |||
| 11746 | if (shadow.r < shadow.k + MOON_MEAN_RADIUS_KM) | ||
| 11747 | { | ||
| 11748 | /* This is at least a partial eclipse. */ | ||
| 11749 | eclipse.kind = ECLIPSE_PARTIAL; | ||
| 11750 | eclipse.sd_partial = ShadowSemiDurationMinutes(shadow.time, shadow.k + MOON_MEAN_RADIUS_KM, eclipse.sd_penum); | ||
| 11751 | if (eclipse.sd_partial <= 0.0) | ||
| 11752 | return LunarEclipseError(ASTRO_SEARCH_FAILURE); | ||
| 11753 | |||
| 11754 | if (shadow.r + MOON_MEAN_RADIUS_KM < shadow.k) | ||
| 11755 | { | ||
| 11756 | /* This is a total eclipse. */ | ||
| 11757 | eclipse.kind = ECLIPSE_TOTAL; | ||
| 11758 | eclipse.obscuration = 1.0; | ||
| 11759 | eclipse.sd_total = ShadowSemiDurationMinutes(shadow.time, shadow.k - MOON_MEAN_RADIUS_KM, eclipse.sd_partial); | ||
| 11760 | if (eclipse.sd_total <= 0.0) | ||
| 11761 | return LunarEclipseError(ASTRO_SEARCH_FAILURE); | ||
| 11762 | } | ||
| 11763 | else | ||
| 11764 | { | ||
| 11765 | /* For lunar eclipses, we calculate the fraction of the Moon's disc covered by the Earth's umbra. */ | ||
| 11766 | eclipse.obscuration = Obscuration(MOON_MEAN_RADIUS_KM, shadow.k, shadow.r); | ||
| 11767 | } | ||
| 11768 | } | ||
| 11769 | return eclipse; | ||
| 11770 | } | ||
| 11771 | } | ||
| 11772 | |||
| 11773 | /* We didn't find an eclipse on this full moon, so search for the next one. */ | ||
| 11774 | fmtime = Astronomy_AddDays(fullmoon.time, 10.0); | ||
| 11775 | } | ||
| 11776 | |||
| 11777 | /* Safety valve to prevent infinite loop. */ | ||
| 11778 | /* This should never happen, because at least 2 lunar eclipses happen per year. */ | ||
| 11779 | return LunarEclipseError(ASTRO_INTERNAL_ERROR); | ||
| 11780 | } | ||
| 11781 | |||
| 11782 | /** | ||
| 11783 | * @brief Searches for the next lunar eclipse in a series. | ||
| 11784 | * | ||
| 11785 | * After using #Astronomy_SearchLunarEclipse to find the first lunar eclipse | ||
| 11786 | * in a series, you can call this function to find the next consecutive lunar eclipse. | ||
| 11787 | * Pass in the `peak` value from the #astro_lunar_eclipse_t returned by the | ||
| 11788 | * previous call to `Astronomy_SearchLunarEclipse` or `Astronomy_NextLunarEclipse` | ||
| 11789 | * to find the next lunar eclipse. | ||
| 11790 | * | ||
| 11791 | * @param prevEclipseTime | ||
| 11792 | * A date and time near a full moon. Lunar eclipse search will start at the next full moon. | ||
| 11793 | * | ||
| 11794 | * @return | ||
| 11795 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 11796 | * and the remaining structure fields will be valid. | ||
| 11797 | * Any other value indicates an error. | ||
| 11798 | */ | ||
| 11799 | astro_lunar_eclipse_t Astronomy_NextLunarEclipse(astro_time_t prevEclipseTime) | ||
| 11800 | { | ||
| 11801 | astro_time_t startTime = Astronomy_AddDays(prevEclipseTime, 10.0); | ||
| 11802 | return Astronomy_SearchLunarEclipse(startTime); | ||
| 11803 | } | ||
| 11804 | |||
| 11805 | |||
| 11806 | static astro_global_solar_eclipse_t GlobalSolarEclipseError(astro_status_t status) | ||
| 11807 | { | ||
| 11808 | astro_global_solar_eclipse_t eclipse; | ||
| 11809 | |||
| 11810 | eclipse.status = status; | ||
| 11811 | eclipse.kind = ECLIPSE_NONE; | ||
| 11812 | eclipse.peak = TimeError(); | ||
| 11813 | eclipse.obscuration = eclipse.distance = eclipse.latitude = eclipse.longitude = NAN; | ||
| 11814 | |||
| 11815 | return eclipse; | ||
| 11816 | } | ||
| 11817 | |||
| 11818 | /* The umbra radius tells us what kind of eclipse the observer sees. */ | ||
| 11819 | /* If the umbra radius is positive, this is a total eclipse. Otherwise, it's annular. */ | ||
| 11820 | /* HACK: I added a tiny bias (14 meters) to match Espenak test data. */ | ||
| 11821 | #define EclipseKindFromUmbra(k) (((k) > 0.014) ? ECLIPSE_TOTAL : ECLIPSE_ANNULAR) | ||
| 11822 | |||
| 11823 | static double SolarEclipseObscuration( | ||
| 11824 | astro_vector_t hm, /* heliocentric Moon */ | ||
| 11825 | astro_vector_t lo) /* lunacentric observer */ | ||
| 11826 | { | ||
| 11827 | astro_vector_t ho; /* heliocentric observer */ | ||
| 11828 | astro_angle_result_t sun_moon_separation; | ||
| 11829 | double obscuration, sun_au, sun_radius, moon_radius; | ||
| 11830 | |||
| 11831 | /* Find heliocentric observer. */ | ||
| 11832 | ho.status = ASTRO_SUCCESS; | ||
| 11833 | ho.t = lo.t; | ||
| 11834 | ho.x = hm.x + lo.x; | ||
| 11835 | ho.y = hm.y + lo.y; | ||
| 11836 | ho.z = hm.z + lo.z; | ||
| 11837 | |||
| 11838 | /* Find the distance from the Sun's center to the observer. */ | ||
| 11839 | sun_au = Astronomy_VectorLength(ho); | ||
| 11840 | |||
| 11841 | /* Calculate the apparent angular radius of the Sun for the observer. */ | ||
| 11842 | sun_radius = asin(SUN_RADIUS_AU / sun_au); | ||
| 11843 | |||
| 11844 | /* Calculate the apparent angular radius of the Moon for the observer. */ | ||
| 11845 | moon_radius = asin(MOON_POLAR_RADIUS_AU / Astronomy_VectorLength(lo)); | ||
| 11846 | |||
| 11847 | /* Calculate the apparent angular separation between the Sun's center and the Moon's center. */ | ||
| 11848 | sun_moon_separation = Astronomy_AngleBetween(lo, ho); | ||
| 11849 | |||
| 11850 | if (sun_moon_separation.status != ASTRO_SUCCESS) | ||
| 11851 | return -1.0; /* internal error! should never happen. */ | ||
| 11852 | |||
| 11853 | obscuration = Obscuration(sun_radius, moon_radius, sun_moon_separation.angle * DEG2RAD); | ||
| 11854 | |||
| 11855 | /* HACK: In marginal cases, we need to clamp obscuration to less than 1.0. */ | ||
| 11856 | if (obscuration > 0.9999) | ||
| 11857 | obscuration = 0.9999; | ||
| 11858 | |||
| 11859 | return obscuration; | ||
| 11860 | } | ||
| 11861 | |||
| 11862 | static astro_global_solar_eclipse_t GeoidIntersect(shadow_t shadow) | ||
| 11863 | { | ||
| 11864 | astro_global_solar_eclipse_t eclipse; | ||
| 11865 | astro_rotation_t rot, inv; | ||
| 11866 | astro_vector_t v, e, o; | ||
| 11867 | shadow_t surface; | ||
| 11868 | double A, B, C, radic, u, R; | ||
| 11869 | double px, py, pz, proj; | ||
| 11870 | double gast; | ||
| 11871 | |||
| 11872 | eclipse.status = ASTRO_SUCCESS; | ||
| 11873 | eclipse.kind = ECLIPSE_PARTIAL; | ||
| 11874 | eclipse.peak = shadow.time; | ||
| 11875 | eclipse.distance = shadow.r; | ||
| 11876 | eclipse.latitude = eclipse.longitude = NAN; | ||
| 11877 | |||
| 11878 | /* | ||
| 11879 | We want to calculate the intersection of the shadow axis with the Earth's geoid. | ||
| 11880 | First we must convert EQJ (equator of J2000) coordinates to EQD (equator of date) | ||
| 11881 | coordinates that are perfectly aligned with the Earth's equator at this | ||
| 11882 | moment in time. | ||
| 11883 | */ | ||
| 11884 | rot = Astronomy_Rotation_EQJ_EQD(&shadow.time); | ||
| 11885 | if (rot.status != ASTRO_SUCCESS) | ||
| 11886 | return GlobalSolarEclipseError(rot.status); | ||
| 11887 | |||
| 11888 | v = Astronomy_RotateVector(rot, shadow.dir); /* shadow-axis vector in equator-of-date coordinates */ | ||
| 11889 | if (v.status != ASTRO_SUCCESS) | ||
| 11890 | return GlobalSolarEclipseError(v.status); | ||
| 11891 | |||
| 11892 | e = Astronomy_RotateVector(rot, shadow.target); /* lunacentric Earth in equator-of-date coordinates */ | ||
| 11893 | if (e.status != ASTRO_SUCCESS) | ||
| 11894 | return GlobalSolarEclipseError(e.status); | ||
| 11895 | |||
| 11896 | /* | ||
| 11897 | Convert all distances from AU to km. | ||
| 11898 | But dilate the z-coordinates so that the Earth becomes a perfect sphere. | ||
| 11899 | Then find the intersection of the vector with the sphere. | ||
| 11900 | See p 184 in Montenbruck & Pfleger's "Astronomy on the Personal Computer", second edition. | ||
| 11901 | */ | ||
| 11902 | v.x *= KM_PER_AU; | ||
| 11903 | v.y *= KM_PER_AU; | ||
| 11904 | v.z *= KM_PER_AU / EARTH_FLATTENING; | ||
| 11905 | |||
| 11906 | e.x *= KM_PER_AU; | ||
| 11907 | e.y *= KM_PER_AU; | ||
| 11908 | e.z *= KM_PER_AU / EARTH_FLATTENING; | ||
| 11909 | |||
| 11910 | /* | ||
| 11911 | Solve the quadratic equation that finds whether and where | ||
| 11912 | the shadow axis intersects with the Earth in the dilated coordinate system. | ||
| 11913 | */ | ||
| 11914 | R = EARTH_EQUATORIAL_RADIUS_KM; | ||
| 11915 | A = v.x*v.x + v.y*v.y + v.z*v.z; | ||
| 11916 | B = -2.0 * (v.x*e.x + v.y*e.y + v.z*e.z); | ||
| 11917 | C = (e.x*e.x + e.y*e.y + e.z*e.z) - R*R; | ||
| 11918 | radic = B*B - 4*A*C; | ||
| 11919 | |||
| 11920 | if (radic > 0.0) | ||
| 11921 | { | ||
| 11922 | /* Calculate the closer of the two intersection points. */ | ||
| 11923 | /* This will be on the day side of the Earth. */ | ||
| 11924 | u = (-B - sqrt(radic)) / (2 * A); | ||
| 11925 | |||
| 11926 | /* Convert lunacentric dilated coordinates to geocentric coordinates. */ | ||
| 11927 | px = u*v.x - e.x; | ||
| 11928 | py = u*v.y - e.y; | ||
| 11929 | pz = (u*v.z - e.z) * EARTH_FLATTENING; | ||
| 11930 | |||
| 11931 | /* Convert cartesian coordinates into geodetic latitude/longitude. */ | ||
| 11932 | proj = hypot(px, py) * (EARTH_FLATTENING * EARTH_FLATTENING); | ||
| 11933 | if (proj == 0.0) | ||
| 11934 | eclipse.latitude = (pz > 0.0) ? +90.0 : -90.0; | ||
| 11935 | else | ||
| 11936 | eclipse.latitude = RAD2DEG * atan(pz / proj); | ||
| 11937 | |||
| 11938 | /* Adjust longitude for Earth's rotation at the given UT. */ | ||
| 11939 | gast = Astronomy_SiderealTime(&eclipse.peak); | ||
| 11940 | eclipse.longitude = fmod((RAD2DEG*atan2(py, px)) - (15*gast), 360.0); | ||
| 11941 | if (eclipse.longitude <= -180.0) | ||
| 11942 | eclipse.longitude += 360.0; | ||
| 11943 | else if (eclipse.longitude > +180.0) | ||
| 11944 | eclipse.longitude -= 360.0; | ||
| 11945 | |||
| 11946 | /* We want to determine whether the observer sees a total eclipse or an annular eclipse. */ | ||
| 11947 | /* We need to perform a series of vector calculations... */ | ||
| 11948 | /* Calculate the inverse rotation matrix, so we can convert EQD to EQJ. */ | ||
| 11949 | inv = Astronomy_InverseRotation(rot); | ||
| 11950 | if (inv.status != ASTRO_SUCCESS) | ||
| 11951 | return GlobalSolarEclipseError(inv.status); | ||
| 11952 | |||
| 11953 | /* Put the EQD geocentric coordinates of the observer into the vector 'o'. */ | ||
| 11954 | /* Also convert back from kilometers to astronomical units. */ | ||
| 11955 | o.status = ASTRO_SUCCESS; | ||
| 11956 | o.t = shadow.time; | ||
| 11957 | o.x = px / KM_PER_AU; | ||
| 11958 | o.y = py / KM_PER_AU; | ||
| 11959 | o.z = pz / KM_PER_AU; | ||
| 11960 | |||
| 11961 | /* Rotate the observer's geocentric EQD back to the EQJ system. */ | ||
| 11962 | o = Astronomy_RotateVector(inv, o); | ||
| 11963 | |||
| 11964 | /* Convert geocentric vector to lunacentric vector. */ | ||
| 11965 | o.x += shadow.target.x; | ||
| 11966 | o.y += shadow.target.y; | ||
| 11967 | o.z += shadow.target.z; | ||
| 11968 | |||
| 11969 | /* Recalculate the shadow using a vector from the Moon's center toward the observer. */ | ||
| 11970 | surface = CalcShadow(MOON_POLAR_RADIUS_KM, shadow.time, o, shadow.dir); | ||
| 11971 | |||
| 11972 | /* If we did everything right, the shadow distance should be very close to zero. */ | ||
| 11973 | /* That's because we already determined the observer 'o' is on the shadow axis! */ | ||
| 11974 | if (surface.r > 1.0e-9 || surface.r < 0.0) | ||
| 11975 | return GlobalSolarEclipseError(ASTRO_INTERNAL_ERROR); | ||
| 11976 | |||
| 11977 | eclipse.kind = EclipseKindFromUmbra(surface.k); | ||
| 11978 | if (eclipse.kind == ECLIPSE_TOTAL) | ||
| 11979 | eclipse.obscuration = 1.0; | ||
| 11980 | else | ||
| 11981 | eclipse.obscuration = SolarEclipseObscuration(shadow.dir, o); | ||
| 11982 | } | ||
| 11983 | else | ||
| 11984 | { | ||
| 11985 | /* This is a partial solar eclipse. It does not make practical sense to calculate obscuration. */ | ||
| 11986 | /* Anyone who wants obscuration should use Astronomy_SearchLocalSolarEclipse for a specific location on the Earth. */ | ||
| 11987 | eclipse.obscuration = NAN; | ||
| 11988 | } | ||
| 11989 | |||
| 11990 | return eclipse; | ||
| 11991 | } | ||
| 11992 | |||
| 11993 | |||
| 11994 | /** | ||
| 11995 | * @brief Searches for a solar eclipse visible anywhere on the Earth's surface. | ||
| 11996 | * | ||
| 11997 | * This function finds the first solar eclipse that occurs after `startTime`. | ||
| 11998 | * A solar eclipse may be partial, annular, or total. | ||
| 11999 | * See #astro_global_solar_eclipse_t for more information. | ||
| 12000 | * To find a series of solar eclipses, call this function once, | ||
| 12001 | * then keep calling #Astronomy_NextGlobalSolarEclipse as many times as desired, | ||
| 12002 | * passing in the `peak` value returned from the previous call. | ||
| 12003 | * | ||
| 12004 | * @param startTime | ||
| 12005 | * The date and time for starting the search for a solar eclipse. | ||
| 12006 | * | ||
| 12007 | * @return | ||
| 12008 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 12009 | * and the remaining structure fields are as described in #astro_global_solar_eclipse_t. | ||
| 12010 | * Any other value indicates an error. | ||
| 12011 | */ | ||
| 12012 | astro_global_solar_eclipse_t Astronomy_SearchGlobalSolarEclipse(astro_time_t startTime) | ||
| 12013 | { | ||
| 12014 | const double PruneLatitude = 1.8; /* Moon's ecliptic latitude beyond which eclipse is impossible */ | ||
| 12015 | astro_time_t nmtime; | ||
| 12016 | astro_search_result_t newmoon; | ||
| 12017 | shadow_t shadow; | ||
| 12018 | int nmcount; | ||
| 12019 | double eclip_lat, eclip_lon, distance; | ||
| 12020 | |||
| 12021 | /* Iterate through consecutive new moons until we find a solar eclipse visible somewhere on Earth. */ | ||
| 12022 | nmtime = startTime; | ||
| 12023 | for (nmcount=0; nmcount < 12; ++nmcount) | ||
| 12024 | { | ||
| 12025 | /* Search for the next new moon. Any eclipse will be near it. */ | ||
| 12026 | newmoon = Astronomy_SearchMoonPhase(0.0, nmtime, 40.0); | ||
| 12027 | if (newmoon.status != ASTRO_SUCCESS) | ||
| 12028 | return GlobalSolarEclipseError(newmoon.status); | ||
| 12029 | |||
| 12030 | /* Pruning: if the new moon's ecliptic latitude is too large, a solar eclipse is not possible. */ | ||
| 12031 | CalcMoon(newmoon.time.tt / 36525.0, &eclip_lon, &eclip_lat, &distance); | ||
| 12032 | if (RAD2DEG * fabs(eclip_lat) < PruneLatitude) | ||
| 12033 | { | ||
| 12034 | /* Search near the new moon for the time when the center of the Earth */ | ||
| 12035 | /* is closest to the line passing through the centers of the Sun and Moon. */ | ||
| 12036 | shadow = PeakMoonShadow(newmoon.time); | ||
| 12037 | if (shadow.status != ASTRO_SUCCESS) | ||
| 12038 | return GlobalSolarEclipseError(shadow.status); | ||
| 12039 | |||
| 12040 | if (shadow.r < shadow.p + EARTH_MEAN_RADIUS_KM) | ||
| 12041 | { | ||
| 12042 | /* This is at least a partial solar eclipse visible somewhere on Earth. */ | ||
| 12043 | /* Try to find an intersection between the shadow axis and the Earth's oblate geoid. */ | ||
| 12044 | return GeoidIntersect(shadow); | ||
| 12045 | } | ||
| 12046 | } | ||
| 12047 | |||
| 12048 | /* We didn't find an eclipse on this new moon, so search for the next one. */ | ||
| 12049 | nmtime = Astronomy_AddDays(newmoon.time, 10.0); | ||
| 12050 | } | ||
| 12051 | |||
| 12052 | /* Safety valve to prevent infinite loop. */ | ||
| 12053 | /* This should never happen, because at least 2 solar eclipses happen per year. */ | ||
| 12054 | return GlobalSolarEclipseError(ASTRO_INTERNAL_ERROR); | ||
| 12055 | } | ||
| 12056 | |||
| 12057 | |||
| 12058 | /** | ||
| 12059 | * @brief Searches for the next global solar eclipse in a series. | ||
| 12060 | * | ||
| 12061 | * After using #Astronomy_SearchGlobalSolarEclipse to find the first solar eclipse | ||
| 12062 | * in a series, you can call this function to find the next consecutive solar eclipse. | ||
| 12063 | * Pass in the `peak` value from the #astro_global_solar_eclipse_t returned by the | ||
| 12064 | * previous call to `Astronomy_SearchGlobalSolarEclipse` or `Astronomy_NextGlobalSolarEclipse` | ||
| 12065 | * to find the next solar eclipse. | ||
| 12066 | * | ||
| 12067 | * @param prevEclipseTime | ||
| 12068 | * A date and time near a new moon. Solar eclipse search will start at the next new moon. | ||
| 12069 | * | ||
| 12070 | * @return | ||
| 12071 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 12072 | * and the remaining structure fields are as described in #astro_global_solar_eclipse_t. | ||
| 12073 | * Any other value indicates an error. | ||
| 12074 | */ | ||
| 12075 | astro_global_solar_eclipse_t Astronomy_NextGlobalSolarEclipse(astro_time_t prevEclipseTime) | ||
| 12076 | { | ||
| 12077 | astro_time_t startTime = Astronomy_AddDays(prevEclipseTime, 10.0); | ||
| 12078 | return Astronomy_SearchGlobalSolarEclipse(startTime); | ||
| 12079 | } | ||
| 12080 | |||
| 12081 | |||
| 12082 | static astro_eclipse_event_t EclipseEventError(void) | ||
| 12083 | { | ||
| 12084 | astro_eclipse_event_t evt; | ||
| 12085 | evt.time = TimeError(); | ||
| 12086 | evt.altitude = NAN; | ||
| 12087 | return evt; | ||
| 12088 | } | ||
| 12089 | |||
| 12090 | |||
| 12091 | static astro_local_solar_eclipse_t LocalSolarEclipseError(astro_status_t status) | ||
| 12092 | { | ||
| 12093 | astro_local_solar_eclipse_t eclipse; | ||
| 12094 | |||
| 12095 | eclipse.status = status; | ||
| 12096 | eclipse.kind = ECLIPSE_NONE; | ||
| 12097 | eclipse.obscuration = NAN; | ||
| 12098 | |||
| 12099 | eclipse.partial_begin = EclipseEventError(); | ||
| 12100 | eclipse.total_begin = EclipseEventError(); | ||
| 12101 | eclipse.peak = EclipseEventError(); | ||
| 12102 | eclipse.total_end = EclipseEventError(); | ||
| 12103 | eclipse.partial_end = EclipseEventError(); | ||
| 12104 | |||
| 12105 | return eclipse; | ||
| 12106 | } | ||
| 12107 | |||
| 12108 | |||
| 12109 | static shadow_t LocalMoonShadow(astro_time_t time, astro_observer_t observer) | ||
| 12110 | { | ||
| 12111 | astro_vector_t s, o, m; | ||
| 12112 | double pos[3]; | ||
| 12113 | |||
| 12114 | /* Calculate observer's geocentric position. */ | ||
| 12115 | /* For efficiency, do this first, to populate the earth rotation parameters in 'time'. */ | ||
| 12116 | /* That way they can be recycled instead of recalculated. */ | ||
| 12117 | geo_pos(&time, observer, pos); | ||
| 12118 | |||
| 12119 | /* Calculate geocentric Sun with aberration correction. */ | ||
| 12120 | s = Astronomy_GeoVector(BODY_SUN, time, ABERRATION); | ||
| 12121 | if (s.status != ASTRO_SUCCESS) | ||
| 12122 | return ShadowError(s.status); | ||
| 12123 | |||
| 12124 | m = Astronomy_GeoMoon(time); /* geocentric Moon */ | ||
| 12125 | |||
| 12126 | /* Calculate lunacentric location of an observer on the Earth's surface. */ | ||
| 12127 | o.status = m.status; | ||
| 12128 | o.x = pos[0] - m.x; | ||
| 12129 | o.y = pos[1] - m.y; | ||
| 12130 | o.z = pos[2] - m.z; | ||
| 12131 | o.t = m.t; | ||
| 12132 | |||
| 12133 | /* Convert geocentric moon to heliocentric Moon. */ | ||
| 12134 | m.x -= s.x; | ||
| 12135 | m.y -= s.y; | ||
| 12136 | m.z -= s.z; | ||
| 12137 | |||
| 12138 | return CalcShadow(MOON_MEAN_RADIUS_KM, time, o, m); | ||
| 12139 | } | ||
| 12140 | |||
| 12141 | |||
| 12142 | static astro_func_result_t local_shadow_distance_slope(void *context, astro_time_t time) | ||
| 12143 | { | ||
| 12144 | const double dt = 1.0 / 86400.0; | ||
| 12145 | astro_time_t t1, t2; | ||
| 12146 | astro_func_result_t result; | ||
| 12147 | shadow_t shadow1, shadow2; | ||
| 12148 | const astro_observer_t *observer = (const astro_observer_t *) context; | ||
| 12149 | |||
| 12150 | t1 = Astronomy_AddDays(time, -dt); | ||
| 12151 | t2 = Astronomy_AddDays(time, +dt); | ||
| 12152 | |||
| 12153 | shadow1 = LocalMoonShadow(t1, *observer); | ||
| 12154 | if (shadow1.status != ASTRO_SUCCESS) | ||
| 12155 | return FuncError(shadow1.status); | ||
| 12156 | |||
| 12157 | shadow2 = LocalMoonShadow(t2, *observer); | ||
| 12158 | if (shadow2.status != ASTRO_SUCCESS) | ||
| 12159 | return FuncError(shadow2.status); | ||
| 12160 | |||
| 12161 | result.value = (shadow2.r - shadow1.r) / dt; | ||
| 12162 | result.status = ASTRO_SUCCESS; | ||
| 12163 | return result; | ||
| 12164 | } | ||
| 12165 | |||
| 12166 | |||
| 12167 | static shadow_t PeakLocalMoonShadow(astro_time_t search_center_time, astro_observer_t observer) | ||
| 12168 | { | ||
| 12169 | astro_time_t t1, t2; | ||
| 12170 | astro_search_result_t result; | ||
| 12171 | const double window = 0.2; | ||
| 12172 | |||
| 12173 | /* | ||
| 12174 | Search for the time near search_center_time that the Moon's shadow comes | ||
| 12175 | closest to the given observer. | ||
| 12176 | */ | ||
| 12177 | |||
| 12178 | t1 = Astronomy_AddDays(search_center_time, -window); | ||
| 12179 | t2 = Astronomy_AddDays(search_center_time, +window); | ||
| 12180 | |||
| 12181 | result = Astronomy_Search(local_shadow_distance_slope, &observer, t1, t2, 1.0); | ||
| 12182 | if (result.status != ASTRO_SUCCESS) | ||
| 12183 | return ShadowError(result.status); | ||
| 12184 | |||
| 12185 | return LocalMoonShadow(result.time, observer); | ||
| 12186 | } | ||
| 12187 | |||
| 12188 | |||
| 12189 | static double local_partial_distance(const shadow_t *shadow) | ||
| 12190 | { | ||
| 12191 | return shadow->p - shadow->r; | ||
| 12192 | } | ||
| 12193 | |||
| 12194 | static double local_total_distance(const shadow_t *shadow) | ||
| 12195 | { | ||
| 12196 | /* Must take the absolute value of the umbra radius 'k' */ | ||
| 12197 | /* because it can be negative for an annular eclipse. */ | ||
| 12198 | return fabs(shadow->k) - shadow->r; | ||
| 12199 | } | ||
| 12200 | |||
| 12201 | /** @cond DOXYGEN_SKIP */ | ||
| 12202 | typedef double (* local_distance_func) (const shadow_t *shadow); | ||
| 12203 | |||
| 12204 | typedef struct | ||
| 12205 | { | ||
| 12206 | local_distance_func func; | ||
| 12207 | double direction; | ||
| 12208 | astro_observer_t observer; | ||
| 12209 | } | ||
| 12210 | eclipse_transition_t; | ||
| 12211 | /* @endcond */ | ||
| 12212 | |||
| 12213 | |||
| 12214 | static astro_func_result_t local_eclipse_func(void *context, astro_time_t time) | ||
| 12215 | { | ||
| 12216 | const eclipse_transition_t *trans = (const eclipse_transition_t *) context; | ||
| 12217 | shadow_t shadow; | ||
| 12218 | astro_func_result_t result; | ||
| 12219 | |||
| 12220 | shadow = LocalMoonShadow(time, trans->observer); | ||
| 12221 | if (shadow.status != ASTRO_SUCCESS) | ||
| 12222 | return FuncError(shadow.status); | ||
| 12223 | |||
| 12224 | result.value = trans->direction * trans->func(&shadow); | ||
| 12225 | result.status = ASTRO_SUCCESS; | ||
| 12226 | return result; | ||
| 12227 | } | ||
| 12228 | |||
| 12229 | |||
| 12230 | astro_func_result_t SunAltitude( | ||
| 12231 | astro_time_t time, | ||
| 12232 | astro_observer_t observer) | ||
| 12233 | { | ||
| 12234 | astro_equatorial_t equ; | ||
| 12235 | astro_horizon_t hor; | ||
| 12236 | astro_func_result_t result; | ||
| 12237 | |||
| 12238 | equ = Astronomy_Equator(BODY_SUN, &time, observer, EQUATOR_OF_DATE, ABERRATION); | ||
| 12239 | if (equ.status != ASTRO_SUCCESS) | ||
| 12240 | return FuncError(equ.status); | ||
| 12241 | |||
| 12242 | hor = Astronomy_Horizon(&time, observer, equ.ra, equ.dec, REFRACTION_NORMAL); | ||
| 12243 | result.value = hor.altitude; | ||
| 12244 | result.status = ASTRO_SUCCESS; | ||
| 12245 | return result; | ||
| 12246 | } | ||
| 12247 | |||
| 12248 | |||
| 12249 | static astro_status_t CalcEvent( | ||
| 12250 | astro_observer_t observer, | ||
| 12251 | astro_time_t time, | ||
| 12252 | astro_eclipse_event_t *evt) | ||
| 12253 | { | ||
| 12254 | astro_func_result_t result; | ||
| 12255 | |||
| 12256 | result = SunAltitude(time, observer); | ||
| 12257 | if (result.status != ASTRO_SUCCESS) | ||
| 12258 | { | ||
| 12259 | evt->time = TimeError(); | ||
| 12260 | evt->altitude = NAN; | ||
| 12261 | return result.status; | ||
| 12262 | } | ||
| 12263 | |||
| 12264 | evt->time = time; | ||
| 12265 | evt->altitude = result.value; | ||
| 12266 | return ASTRO_SUCCESS; | ||
| 12267 | } | ||
| 12268 | |||
| 12269 | |||
| 12270 | static astro_status_t LocalEclipseTransition( | ||
| 12271 | astro_observer_t observer, | ||
| 12272 | double direction, | ||
| 12273 | local_distance_func func, | ||
| 12274 | astro_time_t t1, | ||
| 12275 | astro_time_t t2, | ||
| 12276 | astro_eclipse_event_t *evt) | ||
| 12277 | { | ||
| 12278 | eclipse_transition_t trans; | ||
| 12279 | astro_search_result_t search; | ||
| 12280 | |||
| 12281 | trans.func = func; | ||
| 12282 | trans.direction = direction; | ||
| 12283 | trans.observer = observer; | ||
| 12284 | |||
| 12285 | search = Astronomy_Search(local_eclipse_func, &trans, t1, t2, 1.0); | ||
| 12286 | if (search.status != ASTRO_SUCCESS) | ||
| 12287 | { | ||
| 12288 | evt->time = TimeError(); | ||
| 12289 | evt->altitude = NAN; | ||
| 12290 | return search.status; | ||
| 12291 | } | ||
| 12292 | |||
| 12293 | return CalcEvent(observer, search.time, evt); | ||
| 12294 | } | ||
| 12295 | |||
| 12296 | |||
| 12297 | static astro_local_solar_eclipse_t LocalEclipse( | ||
| 12298 | shadow_t shadow, | ||
| 12299 | astro_observer_t observer) | ||
| 12300 | { | ||
| 12301 | const double PARTIAL_WINDOW = 0.2; | ||
| 12302 | const double TOTAL_WINDOW = 0.01; | ||
| 12303 | astro_local_solar_eclipse_t eclipse; | ||
| 12304 | astro_time_t t1, t2; | ||
| 12305 | astro_status_t status; | ||
| 12306 | |||
| 12307 | status = CalcEvent(observer, shadow.time, &eclipse.peak); | ||
| 12308 | if (status != ASTRO_SUCCESS) | ||
| 12309 | return LocalSolarEclipseError(status); | ||
| 12310 | |||
| 12311 | t1 = Astronomy_AddDays(shadow.time, -PARTIAL_WINDOW); | ||
| 12312 | t2 = Astronomy_AddDays(shadow.time, +PARTIAL_WINDOW); | ||
| 12313 | |||
| 12314 | status = LocalEclipseTransition(observer, +1.0, local_partial_distance, t1, shadow.time, &eclipse.partial_begin); | ||
| 12315 | if (status != ASTRO_SUCCESS) | ||
| 12316 | return LocalSolarEclipseError(status); | ||
| 12317 | |||
| 12318 | status = LocalEclipseTransition(observer, -1.0, local_partial_distance, shadow.time, t2, &eclipse.partial_end); | ||
| 12319 | if (status != ASTRO_SUCCESS) | ||
| 12320 | return LocalSolarEclipseError(status); | ||
| 12321 | |||
| 12322 | if (shadow.r < fabs(shadow.k)) /* take absolute value of 'k' to handle annular eclipses too. */ | ||
| 12323 | { | ||
| 12324 | t1 = Astronomy_AddDays(shadow.time, -TOTAL_WINDOW); | ||
| 12325 | t2 = Astronomy_AddDays(shadow.time, +TOTAL_WINDOW); | ||
| 12326 | |||
| 12327 | status = LocalEclipseTransition(observer, +1.0, local_total_distance, t1, shadow.time, &eclipse.total_begin); | ||
| 12328 | if (status != ASTRO_SUCCESS) | ||
| 12329 | return LocalSolarEclipseError(status); | ||
| 12330 | |||
| 12331 | status = LocalEclipseTransition(observer, -1.0, local_total_distance, shadow.time, t2, &eclipse.total_end); | ||
| 12332 | if (status != ASTRO_SUCCESS) | ||
| 12333 | return LocalSolarEclipseError(status); | ||
| 12334 | |||
| 12335 | eclipse.kind = EclipseKindFromUmbra(shadow.k); | ||
| 12336 | if (eclipse.kind == ECLIPSE_TOTAL) | ||
| 12337 | eclipse.obscuration = 1.0; | ||
| 12338 | else | ||
| 12339 | eclipse.obscuration = SolarEclipseObscuration(shadow.dir, shadow.target); | ||
| 12340 | } | ||
| 12341 | else | ||
| 12342 | { | ||
| 12343 | eclipse.total_begin = eclipse.total_end = EclipseEventError(); | ||
| 12344 | eclipse.kind = ECLIPSE_PARTIAL; | ||
| 12345 | eclipse.obscuration = SolarEclipseObscuration(shadow.dir, shadow.target); | ||
| 12346 | } | ||
| 12347 | |||
| 12348 | eclipse.status = ASTRO_SUCCESS; | ||
| 12349 | return eclipse; | ||
| 12350 | } | ||
| 12351 | |||
| 12352 | |||
| 12353 | /** | ||
| 12354 | * @brief Searches for a solar eclipse visible at a specific location on the Earth's surface. | ||
| 12355 | * | ||
| 12356 | * This function finds the first solar eclipse that occurs after `startTime`. | ||
| 12357 | * A solar eclipse may be partial, annular, or total. | ||
| 12358 | * See #astro_local_solar_eclipse_t for more information. | ||
| 12359 | * To find a series of solar eclipses, call this function once, | ||
| 12360 | * then keep calling #Astronomy_NextLocalSolarEclipse as many times as desired, | ||
| 12361 | * passing in the `peak` value returned from the previous call. | ||
| 12362 | * | ||
| 12363 | * IMPORTANT: An eclipse reported by this function might be partly or | ||
| 12364 | * completely invisible to the observer due to the time of day. | ||
| 12365 | * | ||
| 12366 | * @param startTime | ||
| 12367 | * The date and time for starting the search for a solar eclipse. | ||
| 12368 | * | ||
| 12369 | * @param observer | ||
| 12370 | * The geographic location of the observer. | ||
| 12371 | * | ||
| 12372 | * @return | ||
| 12373 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 12374 | * and the remaining structure fields are as described in #astro_local_solar_eclipse_t. | ||
| 12375 | * Any other value indicates an error. | ||
| 12376 | */ | ||
| 12377 | astro_local_solar_eclipse_t Astronomy_SearchLocalSolarEclipse( | ||
| 12378 | astro_time_t startTime, | ||
| 12379 | astro_observer_t observer) | ||
| 12380 | { | ||
| 12381 | const double PruneLatitude = 1.8; /* Moon's ecliptic latitude beyond which eclipse is impossible */ | ||
| 12382 | astro_time_t nmtime; | ||
| 12383 | astro_search_result_t newmoon; | ||
| 12384 | shadow_t shadow; | ||
| 12385 | double eclip_lat, eclip_lon, distance; | ||
| 12386 | astro_local_solar_eclipse_t eclipse; | ||
| 12387 | |||
| 12388 | /* Iterate through consecutive new moons until we find a solar eclipse visible somewhere on Earth. */ | ||
| 12389 | nmtime = startTime; | ||
| 12390 | for(;;) | ||
| 12391 | { | ||
| 12392 | /* Search for the next new moon. Any eclipse will be near it. */ | ||
| 12393 | newmoon = Astronomy_SearchMoonPhase(0.0, nmtime, 40.0); | ||
| 12394 | if (newmoon.status != ASTRO_SUCCESS) | ||
| 12395 | return LocalSolarEclipseError(newmoon.status); | ||
| 12396 | |||
| 12397 | /* Pruning: if the new moon's ecliptic latitude is too large, a solar eclipse is not possible. */ | ||
| 12398 | CalcMoon(newmoon.time.tt / 36525.0, &eclip_lon, &eclip_lat, &distance); | ||
| 12399 | if (RAD2DEG * fabs(eclip_lat) < PruneLatitude) | ||
| 12400 | { | ||
| 12401 | /* Search near the new moon for the time when the observer */ | ||
| 12402 | /* is closest to the line passing through the centers of the Sun and Moon. */ | ||
| 12403 | shadow = PeakLocalMoonShadow(newmoon.time, observer); | ||
| 12404 | if (shadow.status != ASTRO_SUCCESS) | ||
| 12405 | return LocalSolarEclipseError(shadow.status); | ||
| 12406 | |||
| 12407 | if (shadow.r < shadow.p) | ||
| 12408 | { | ||
| 12409 | /* This is at least a partial solar eclipse for the observer. */ | ||
| 12410 | eclipse = LocalEclipse(shadow, observer); | ||
| 12411 | |||
| 12412 | /* If any error occurs, something is really wrong and we should bail out. */ | ||
| 12413 | if (eclipse.status != ASTRO_SUCCESS) | ||
| 12414 | return eclipse; | ||
| 12415 | |||
| 12416 | /* Ignore any eclipse that happens completely at night. */ | ||
| 12417 | /* More precisely, the center of the Sun must be above the horizon */ | ||
| 12418 | /* at the beginning or the end of the eclipse, or we skip the event. */ | ||
| 12419 | if (eclipse.partial_begin.altitude > 0.0 || eclipse.partial_end.altitude > 0.0) | ||
| 12420 | return eclipse; | ||
| 12421 | } | ||
| 12422 | } | ||
| 12423 | |||
| 12424 | /* We didn't find an eclipse on this new moon, so search for the next one. */ | ||
| 12425 | nmtime = Astronomy_AddDays(newmoon.time, 10.0); | ||
| 12426 | } | ||
| 12427 | } | ||
| 12428 | |||
| 12429 | |||
| 12430 | /** | ||
| 12431 | * @brief Searches for the next local solar eclipse in a series. | ||
| 12432 | * | ||
| 12433 | * After using #Astronomy_SearchLocalSolarEclipse to find the first solar eclipse | ||
| 12434 | * in a series, you can call this function to find the next consecutive solar eclipse. | ||
| 12435 | * Pass in the `peak` value from the #astro_local_solar_eclipse_t returned by the | ||
| 12436 | * previous call to `Astronomy_SearchLocalSolarEclipse` or `Astronomy_NextLocalSolarEclipse` | ||
| 12437 | * to find the next solar eclipse. | ||
| 12438 | * | ||
| 12439 | * @param prevEclipseTime | ||
| 12440 | * A date and time near a new moon. Solar eclipse search will start at the next new moon. | ||
| 12441 | * | ||
| 12442 | * @param observer | ||
| 12443 | * The geographic location of the observer. | ||
| 12444 | * | ||
| 12445 | * @return | ||
| 12446 | * If successful, the `status` field in the returned structure will contain `ASTRO_SUCCESS` | ||
| 12447 | * and the remaining structure fields are as described in #astro_local_solar_eclipse_t. | ||
| 12448 | * Any other value indicates an error. | ||
| 12449 | */ | ||
| 12450 | astro_local_solar_eclipse_t Astronomy_NextLocalSolarEclipse( | ||
| 12451 | astro_time_t prevEclipseTime, | ||
| 12452 | astro_observer_t observer) | ||
| 12453 | { | ||
| 12454 | astro_time_t startTime = Astronomy_AddDays(prevEclipseTime, 10.0); | ||
| 12455 | return Astronomy_SearchLocalSolarEclipse(startTime, observer); | ||
| 12456 | } | ||
| 12457 | |||
| 12458 | |||
| 12459 | static astro_func_result_t planet_transit_bound(void *context, astro_time_t time) | ||
| 12460 | { | ||
| 12461 | shadow_t shadow; | ||
| 12462 | astro_func_result_t result; | ||
| 12463 | const planet_shadow_context_t *p = (const planet_shadow_context_t *) context; | ||
| 12464 | |||
| 12465 | shadow = PlanetShadow(p->body, p->planet_radius_km, time); | ||
| 12466 | if (shadow.status != ASTRO_SUCCESS) | ||
| 12467 | return FuncError(shadow.status); | ||
| 12468 | |||
| 12469 | result.status = ASTRO_SUCCESS; | ||
| 12470 | result.value = p->direction * (shadow.r - shadow.p); | ||
| 12471 | return result; | ||
| 12472 | } | ||
| 12473 | |||
| 12474 | |||
| 12475 | static astro_search_result_t PlanetTransitBoundary( | ||
| 12476 | astro_body_t body, | ||
| 12477 | double planet_radius_km, | ||
| 12478 | astro_time_t t1, | ||
| 12479 | astro_time_t t2, | ||
| 12480 | double direction) | ||
| 12481 | { | ||
| 12482 | /* Search for the time the planet's penumbra begins/ends making contact with the center of the Earth. */ | ||
| 12483 | planet_shadow_context_t context; | ||
| 12484 | |||
| 12485 | context.body = body; | ||
| 12486 | context.planet_radius_km = planet_radius_km; | ||
| 12487 | context.direction = direction; | ||
| 12488 | |||
| 12489 | return Astronomy_Search(planet_transit_bound, &context, t1, t2, 1.0); | ||
| 12490 | } | ||
| 12491 | |||
| 12492 | |||
| 12493 | /** | ||
| 12494 | * @brief Searches for the first transit of Mercury or Venus after a given date. | ||
| 12495 | * | ||
| 12496 | * Finds the first transit of Mercury or Venus after a specified date. | ||
| 12497 | * A transit is when an inferior planet passes between the Sun and the Earth | ||
| 12498 | * so that the silhouette of the planet is visible against the Sun in the background. | ||
| 12499 | * To continue the search, pass the `finish` time in the returned structure to | ||
| 12500 | * #Astronomy_NextTransit. | ||
| 12501 | * | ||
| 12502 | * @param body | ||
| 12503 | * The planet whose transit is to be found. Must be `BODY_MERCURY` or `BODY_VENUS`. | ||
| 12504 | * | ||
| 12505 | * @param startTime | ||
| 12506 | * The date and time for starting the search for a transit. | ||
| 12507 | * | ||
| 12508 | * @return | ||
| 12509 | * If successful, the `status` field in the returned structure hold `ASTRO_SUCCESS` | ||
| 12510 | * and the other fields are as documented in #astro_transit_t. | ||
| 12511 | * Otherwise, `status` holds an error code and the other structure members are undefined. | ||
| 12512 | */ | ||
| 12513 | astro_transit_t Astronomy_SearchTransit(astro_body_t body, astro_time_t startTime) | ||
| 12514 | { | ||
| 12515 | astro_time_t search_time; | ||
| 12516 | astro_transit_t transit; | ||
| 12517 | astro_search_result_t conj, search; | ||
| 12518 | astro_angle_result_t conj_separation, min_separation; | ||
| 12519 | shadow_t shadow; | ||
| 12520 | double planet_radius_km; | ||
| 12521 | astro_time_t tx; | ||
| 12522 | const double threshold_angle = 0.4; /* maximum angular separation to attempt transit calculation */ | ||
| 12523 | const double dt_days = 1.0; | ||
| 12524 | |||
| 12525 | /* Validate the planet and find its mean radius. */ | ||
| 12526 | switch (body) | ||
| 12527 | { | ||
| 12528 | case BODY_MERCURY: planet_radius_km = 2439.7; break; | ||
| 12529 | case BODY_VENUS: planet_radius_km = 6051.8; break; | ||
| 12530 | default: | ||
| 12531 | return TransitErr(ASTRO_INVALID_BODY); | ||
| 12532 | } | ||
| 12533 | |||
| 12534 | search_time = startTime; | ||
| 12535 | for(;;) | ||
| 12536 | { | ||
| 12537 | /* | ||
| 12538 | Search for the next inferior conjunction of the given planet. | ||
| 12539 | This is the next time the Earth and the other planet have the same | ||
| 12540 | ecliptic longitude as seen from the Sun. | ||
| 12541 | */ | ||
| 12542 | conj = Astronomy_SearchRelativeLongitude(body, 0.0, search_time); | ||
| 12543 | if (conj.status != ASTRO_SUCCESS) | ||
| 12544 | return TransitErr(conj.status); | ||
| 12545 | |||
| 12546 | /* Calculate the angular separation between the body and the Sun at this time. */ | ||
| 12547 | conj_separation = Astronomy_AngleFromSun(body, conj.time); | ||
| 12548 | if (conj_separation.status != ASTRO_SUCCESS) | ||
| 12549 | return TransitErr(conj_separation.status); | ||
| 12550 | |||
| 12551 | if (conj_separation.angle < threshold_angle) | ||
| 12552 | { | ||
| 12553 | /* | ||
| 12554 | The planet's angular separation from the Sun is small enough | ||
| 12555 | to consider it a transit candidate. | ||
| 12556 | Search for the moment when the line passing through the Sun | ||
| 12557 | and planet are closest to the Earth's center. | ||
| 12558 | */ | ||
| 12559 | shadow = PeakPlanetShadow(body, planet_radius_km, conj.time); | ||
| 12560 | if (shadow.status != ASTRO_SUCCESS) | ||
| 12561 | return TransitErr(shadow.status); | ||
| 12562 | |||
| 12563 | if (shadow.r < shadow.p) /* does the planet's penumbra touch the Earth's center? */ | ||
| 12564 | { | ||
| 12565 | /* Find the beginning and end of the penumbral contact. */ | ||
| 12566 | tx = Astronomy_AddDays(shadow.time, -dt_days); | ||
| 12567 | search = PlanetTransitBoundary(body, planet_radius_km, tx, shadow.time, -1.0); | ||
| 12568 | if (search.status != ASTRO_SUCCESS) | ||
| 12569 | return TransitErr(search.status); | ||
| 12570 | transit.start = search.time; | ||
| 12571 | |||
| 12572 | tx = Astronomy_AddDays(shadow.time, +dt_days); | ||
| 12573 | search = PlanetTransitBoundary(body, planet_radius_km, shadow.time, tx, +1.0); | ||
| 12574 | if (search.status != ASTRO_SUCCESS) | ||
| 12575 | return TransitErr(search.status); | ||
| 12576 | transit.finish = search.time; | ||
| 12577 | transit.status = ASTRO_SUCCESS; | ||
| 12578 | transit.peak = shadow.time; | ||
| 12579 | |||
| 12580 | min_separation = Astronomy_AngleFromSun(body, shadow.time); | ||
| 12581 | if (min_separation.status != ASTRO_SUCCESS) | ||
| 12582 | return TransitErr(min_separation.status); | ||
| 12583 | |||
| 12584 | transit.separation = 60.0 * min_separation.angle; /* convert degrees to arcminutes */ | ||
| 12585 | return transit; | ||
| 12586 | } | ||
| 12587 | } | ||
| 12588 | |||
| 12589 | /* This inferior conjunction was not a transit. Try the next inferior conjunction. */ | ||
| 12590 | search_time = Astronomy_AddDays(conj.time, 10.0); | ||
| 12591 | } | ||
| 12592 | } | ||
| 12593 | |||
| 12594 | |||
| 12595 | /** | ||
| 12596 | * @brief Searches for another transit of Mercury or Venus. | ||
| 12597 | * | ||
| 12598 | * After calling #Astronomy_SearchTransit to find a transit of Mercury or Venus, | ||
| 12599 | * this function finds the next transit after that. | ||
| 12600 | * Keep calling this function as many times as you want to keep finding more transits. | ||
| 12601 | * | ||
| 12602 | * @param body | ||
| 12603 | * The planet whose transit is to be found. Must be `BODY_MERCURY` or `BODY_VENUS`. | ||
| 12604 | * | ||
| 12605 | * @param prevTransitTime | ||
| 12606 | * A date and time near the previous transit. | ||
| 12607 | * | ||
| 12608 | * @return | ||
| 12609 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS` | ||
| 12610 | * and the other fields are as documented in #astro_transit_t. | ||
| 12611 | * Otherwise, `status` holds an error code and the other structure members are undefined. | ||
| 12612 | */ | ||
| 12613 | astro_transit_t Astronomy_NextTransit(astro_body_t body, astro_time_t prevTransitTime) | ||
| 12614 | { | ||
| 12615 | astro_time_t startTime; | ||
| 12616 | |||
| 12617 | startTime = Astronomy_AddDays(prevTransitTime, 100.0); | ||
| 12618 | return Astronomy_SearchTransit(body, startTime); | ||
| 12619 | } | ||
| 12620 | |||
| 12621 | |||
| 12622 | static astro_node_event_t NodeError(astro_status_t status) | ||
| 12623 | { | ||
| 12624 | astro_node_event_t node; | ||
| 12625 | |||
| 12626 | node.status = status; | ||
| 12627 | node.time = TimeError(); | ||
| 12628 | node.kind = INVALID_NODE; | ||
| 12629 | |||
| 12630 | return node; | ||
| 12631 | } | ||
| 12632 | |||
| 12633 | static astro_func_result_t MoonNodeSearchFunc(void *context, astro_time_t time) | ||
| 12634 | { | ||
| 12635 | astro_func_result_t result; | ||
| 12636 | astro_spherical_t eclip; | ||
| 12637 | astro_node_kind_t kind = *((astro_node_kind_t *)context); | ||
| 12638 | |||
| 12639 | eclip = Astronomy_EclipticGeoMoon(time); | ||
| 12640 | |||
| 12641 | result.value = eclip.lat * (double)kind; | ||
| 12642 | result.status = ASTRO_SUCCESS; | ||
| 12643 | return result; | ||
| 12644 | } | ||
| 12645 | |||
| 12646 | static const double MOON_NODE_STEP_DAYS = +10.0; /* a safe number of days to step without missing a Moon node */ | ||
| 12647 | |||
| 12648 | /** | ||
| 12649 | * @brief Searches for a time when the Moon's center crosses through the ecliptic plane. | ||
| 12650 | * | ||
| 12651 | * Searches for the first ascending or descending node of the Moon after `startTime`. | ||
| 12652 | * An ascending node is when the Moon's center passes through the ecliptic plane | ||
| 12653 | * (the plane of the Earth's orbit around the Sun) from south to north. | ||
| 12654 | * A descending node is when the Moon's center passes through the ecliptic plane | ||
| 12655 | * from north to south. Nodes indicate possible times of solar or lunar eclipses, | ||
| 12656 | * if the Moon also happens to be in the correct phase (new or full, respectively). | ||
| 12657 | * | ||
| 12658 | * Call `Astronomy_SearchMoonNode` to find the first of a series of nodes. | ||
| 12659 | * Then call #Astronomy_NextMoonNode to find as many more consecutive nodes as desired. | ||
| 12660 | * | ||
| 12661 | * @param startTime | ||
| 12662 | * The date and time for starting the search for an ascending or descending node of the Moon. | ||
| 12663 | * | ||
| 12664 | * @return | ||
| 12665 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS` | ||
| 12666 | * and the other fields are as documented in #astro_node_event_t. | ||
| 12667 | * Otherwise, `status` holds an error code and the other structure members are undefined. | ||
| 12668 | */ | ||
| 12669 | astro_node_event_t Astronomy_SearchMoonNode(astro_time_t startTime) | ||
| 12670 | { | ||
| 12671 | astro_node_event_t node; | ||
| 12672 | astro_time_t time1, time2; | ||
| 12673 | astro_spherical_t eclip1, eclip2; | ||
| 12674 | astro_node_kind_t kind; | ||
| 12675 | astro_search_result_t result; | ||
| 12676 | |||
| 12677 | /* Start at the given moment in time and sample the Moon's ecliptic latitude. */ | ||
| 12678 | /* Step 10 days at a time, searching for an interval where that latitude crosses zero. */ | ||
| 12679 | time1 = startTime; | ||
| 12680 | eclip1 = Astronomy_EclipticGeoMoon(time1); /* never returns a failure code */ | ||
| 12681 | |||
| 12682 | for(;;) | ||
| 12683 | { | ||
| 12684 | time2 = Astronomy_AddDays(time1, MOON_NODE_STEP_DAYS); | ||
| 12685 | eclip2 = Astronomy_EclipticGeoMoon(time2); /* never returns a failure code */ | ||
| 12686 | if (eclip1.lat * eclip2.lat <= 0.0) | ||
| 12687 | { | ||
| 12688 | /* There is a node somewhere inside this closed time interval. */ | ||
| 12689 | /* Figure out whether it is an ascending node or a descending node. */ | ||
| 12690 | kind = (eclip2.lat > eclip1.lat) ? ASCENDING_NODE : DESCENDING_NODE; | ||
| 12691 | result = Astronomy_Search(MoonNodeSearchFunc, &kind, time1, time2, 1.0); | ||
| 12692 | if (result.status != ASTRO_SUCCESS) | ||
| 12693 | return NodeError(result.status); | ||
| 12694 | |||
| 12695 | node.status = ASTRO_SUCCESS; | ||
| 12696 | node.time = result.time; | ||
| 12697 | node.kind = kind; | ||
| 12698 | return node; | ||
| 12699 | } | ||
| 12700 | time1 = time2; | ||
| 12701 | eclip1 = eclip2; | ||
| 12702 | } | ||
| 12703 | } | ||
| 12704 | |||
| 12705 | |||
| 12706 | /** | ||
| 12707 | * @brief Searches for the next time when the Moon's center crosses through the ecliptic plane. | ||
| 12708 | * | ||
| 12709 | * Call #Astronomy_SearchMoonNode to find the first of a series of nodes. | ||
| 12710 | * Then call `Astronomy_NextMoonNode` to find as many more consecutive nodes as desired. | ||
| 12711 | * | ||
| 12712 | * @param prevNode | ||
| 12713 | * The previous node found from calling #Astronomy_SearchMoonNode or `Astronomy_NextMoonNode`. | ||
| 12714 | * | ||
| 12715 | * @return | ||
| 12716 | * If successful, the `status` field in the returned structure holds `ASTRO_SUCCESS` | ||
| 12717 | * and the other fields are as documented in #astro_node_event_t. | ||
| 12718 | * Otherwise, `status` holds an error code and the other structure members are undefined. | ||
| 12719 | */ | ||
| 12720 | astro_node_event_t Astronomy_NextMoonNode(astro_node_event_t prevNode) | ||
| 12721 | { | ||
| 12722 | astro_time_t time; | ||
| 12723 | astro_node_event_t node; | ||
| 12724 | |||
| 12725 | if (prevNode.status != ASTRO_SUCCESS) | ||
| 12726 | return NodeError(ASTRO_INVALID_PARAMETER); | ||
| 12727 | |||
| 12728 | if (prevNode.kind != ASCENDING_NODE && prevNode.kind != DESCENDING_NODE) | ||
| 12729 | return NodeError(ASTRO_INVALID_PARAMETER); | ||
| 12730 | |||
| 12731 | time = Astronomy_AddDays(prevNode.time, MOON_NODE_STEP_DAYS); | ||
| 12732 | node = Astronomy_SearchMoonNode(time); | ||
| 12733 | if (node.status == ASTRO_SUCCESS) | ||
| 12734 | { | ||
| 12735 | /* Verify nodes are alternating as expected. */ | ||
| 12736 | if (prevNode.kind == ASCENDING_NODE) | ||
| 12737 | { | ||
| 12738 | if (node.kind != DESCENDING_NODE) | ||
| 12739 | return NodeError(ASTRO_INTERNAL_ERROR); | ||
| 12740 | } | ||
| 12741 | else | ||
| 12742 | { | ||
| 12743 | if (node.kind != ASCENDING_NODE) | ||
| 12744 | return NodeError(ASTRO_INTERNAL_ERROR); | ||
| 12745 | } | ||
| 12746 | } | ||
| 12747 | return node; | ||
| 12748 | } | ||
| 12749 | |||
| 12750 | |||
| 12751 | /** | ||
| 12752 | * @brief Frees up all dynamic memory allocated by Astronomy Engine. | ||
| 12753 | * | ||
| 12754 | * Astronomy Engine uses dynamic memory allocation in only one place: | ||
| 12755 | * it makes calculation of Pluto's orbit more efficient by caching 11 KB | ||
| 12756 | * segments and recycling them. To force purging this cache and | ||
| 12757 | * freeing all the dynamic memory, you can call this function at any time. | ||
| 12758 | * It is always safe to call, although it will slow down the very next | ||
| 12759 | * calculation of Pluto's position for a nearby time value. | ||
| 12760 | * Calling this function before your program exits is optional, but | ||
| 12761 | * it will be helpful for leak-checkers like valgrind. | ||
| 12762 | */ | ||
| 12763 | void Astronomy_Reset(void) | ||
| 12764 | { | ||
| 12765 | int i; | ||
| 12766 | for (i=0; i < PLUTO_NUM_STATES-1; ++i) | ||
| 12767 | { | ||
| 12768 | free(pluto_cache[i]); | ||
| 12769 | pluto_cache[i] = NULL; | ||
| 12770 | } | ||
| 12771 | } | ||
| 12772 | |||
| 12773 | |||
| 12774 | static astro_axis_t EarthRotationAxis(astro_time_t *time) | ||
| 12775 | { | ||
| 12776 | astro_axis_t axis; | ||
| 12777 | double pos1[3]; | ||
| 12778 | double pos2[3]; | ||
| 12779 | astro_equatorial_t equ; | ||
| 12780 | |||
| 12781 | if (time == NULL) | ||
| 12782 | return AxisErr(ASTRO_INVALID_PARAMETER, TimeError()); | ||
| 12783 | |||
| 12784 | /* | ||
| 12785 | Unlike the other planets, we have a model of precession and nutation | ||
| 12786 | for the Earth's axis that provides a north pole vector. | ||
| 12787 | So calculate the vector first, then derive the (RA,DEC) angles from the vector. | ||
| 12788 | */ | ||
| 12789 | |||
| 12790 | /* Start with a north pole vector in equator-of-date coordinates: (0,0,1). */ | ||
| 12791 | pos1[0] = 0.0; | ||
| 12792 | pos1[1] = 0.0; | ||
| 12793 | pos1[2] = 1.0; | ||
| 12794 | |||
| 12795 | /* Convert the vector into J2000 coordinates. */ | ||
| 12796 | nutation(pos1, time, INTO_2000, pos2); | ||
| 12797 | precession(pos2, *time, INTO_2000, pos1); | ||
| 12798 | axis.north.x = pos1[0]; | ||
| 12799 | axis.north.y = pos1[1]; | ||
| 12800 | axis.north.z = pos1[2]; | ||
| 12801 | axis.north.t = *time; | ||
| 12802 | axis.north.status = ASTRO_SUCCESS; | ||
| 12803 | |||
| 12804 | /* Derive angular values: right ascension and declination. */ | ||
| 12805 | equ = Astronomy_EquatorFromVector(axis.north); | ||
| 12806 | if (equ.status != ASTRO_SUCCESS) | ||
| 12807 | return AxisErr(equ.status, *time); | ||
| 12808 | axis.ra = equ.ra; | ||
| 12809 | axis.dec = equ.dec; | ||
| 12810 | |||
| 12811 | /* Use a modified version of the era() function that does not trim to 0..360 degrees. */ | ||
| 12812 | /* This expression is also corrected to give the correct angle at the J2000 epoch. */ | ||
| 12813 | axis.spin = 190.41375788700253 + (360.9856122880876 * time->ut); | ||
| 12814 | |||
| 12815 | axis.status = ASTRO_SUCCESS; | ||
| 12816 | |||
| 12817 | return axis; | ||
| 12818 | } | ||
| 12819 | |||
| 12820 | |||
| 12821 | /** | ||
| 12822 | * @brief Calculates information about a body's rotation axis at a given time. | ||
| 12823 | * | ||
| 12824 | * Calculates the orientation of a body's rotation axis, along with | ||
| 12825 | * the rotation angle of its prime meridian, at a given moment in time. | ||
| 12826 | * | ||
| 12827 | * This function uses formulas standardized by the IAU Working Group | ||
| 12828 | * on Cartographics and Rotational Elements 2015 report, as described | ||
| 12829 | * in the following document: | ||
| 12830 | * | ||
| 12831 | * https://astropedia.astrogeology.usgs.gov/download/Docs/WGCCRE/WGCCRE2015reprint.pdf | ||
| 12832 | * | ||
| 12833 | * See #astro_axis_t for more detailed information. | ||
| 12834 | * | ||
| 12835 | * @param body | ||
| 12836 | * The body whose rotation axis is to be found. The supported bodies are: | ||
| 12837 | * `BODY_SUN`, `BODY_MOON`, `BODY_MERCURY`, `BODY_VENUS`, `BODY_EARTH`, `BODY_MARS`, | ||
| 12838 | * `BODY_JUPITER`, `BODY_SATURN`, `BODY_URANUS`, `BODY_NEPTUNE`, `BODY_PLUTO`. | ||
| 12839 | * | ||
| 12840 | * @param time | ||
| 12841 | * The time for which the body's rotation axis is to be found. | ||
| 12842 | * | ||
| 12843 | * @return astro_axis_t | ||
| 12844 | */ | ||
| 12845 | astro_axis_t Astronomy_RotationAxis(astro_body_t body, astro_time_t *time) | ||
| 12846 | { | ||
| 12847 | astro_axis_t axis; | ||
| 12848 | double ra, dec, w; | ||
| 12849 | double radlat, radlon, rcoslat; | ||
| 12850 | double Ja, Jb, Jc, Jd, Je, N; | ||
| 12851 | double E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13; | ||
| 12852 | double d, T; | ||
| 12853 | |||
| 12854 | if (time == NULL) | ||
| 12855 | return AxisErr(ASTRO_INVALID_PARAMETER, TimeError()); | ||
| 12856 | |||
| 12857 | d = time->tt; | ||
| 12858 | T = d / 36525.0; | ||
| 12859 | |||
| 12860 | switch (body) | ||
| 12861 | { | ||
| 12862 | case BODY_SUN: | ||
| 12863 | ra = 286.13; | ||
| 12864 | dec = 63.87; | ||
| 12865 | w = 84.176 + (14.1844 * d); | ||
| 12866 | break; | ||
| 12867 | |||
| 12868 | case BODY_MERCURY: | ||
| 12869 | ra = 281.0103 - (0.0328 * T); | ||
| 12870 | dec = 61.4155 - (0.0049 * T); | ||
| 12871 | w = ( | ||
| 12872 | 329.5988 | ||
| 12873 | + (6.1385108 * d) | ||
| 12874 | + (0.01067257 * sin(DEG2RAD*(174.7910857 + 4.092335*d))) | ||
| 12875 | - (0.00112309 * sin(DEG2RAD*(349.5821714 + 8.184670*d))) | ||
| 12876 | - (0.00011040 * sin(DEG2RAD*(164.3732571 + 12.277005*d))) | ||
| 12877 | - (0.00002539 * sin(DEG2RAD*(339.1643429 + 16.369340*d))) | ||
| 12878 | - (0.00000571 * sin(DEG2RAD*(153.9554286 + 20.461675*d))) | ||
| 12879 | ); | ||
| 12880 | break; | ||
| 12881 | |||
| 12882 | case BODY_VENUS: | ||
| 12883 | ra = 272.76; | ||
| 12884 | dec = 67.16; | ||
| 12885 | w = 160.20 - (1.4813688 * d); | ||
| 12886 | break; | ||
| 12887 | |||
| 12888 | case BODY_EARTH: | ||
| 12889 | return EarthRotationAxis(time); | ||
| 12890 | |||
| 12891 | case BODY_MOON: | ||
| 12892 | /* | ||
| 12893 | https://astropedia.astrogeology.usgs.gov/alfresco/d/d/workspace/SpacesStore/28fd9e81-1964-44d6-a58b-fbbf61e64e15/WGCCRE2009reprint.pdf | ||
| 12894 | Page 8, Table 2. | ||
| 12895 | */ | ||
| 12896 | E1 = DEG2RAD * (125.045 - 0.0529921*d); | ||
| 12897 | E2 = DEG2RAD * (250.089 - 0.1059842*d); | ||
| 12898 | E3 = DEG2RAD * (260.008 + 13.0120009*d); | ||
| 12899 | E4 = DEG2RAD * (176.625 + 13.3407154*d); | ||
| 12900 | E5 = DEG2RAD * (357.529 + 0.9856003*d); | ||
| 12901 | E6 = DEG2RAD * (311.589 + 26.4057084*d); | ||
| 12902 | E7 = DEG2RAD * (134.963 + 13.0649930*d); | ||
| 12903 | E8 = DEG2RAD * (276.617 + 0.3287146*d); | ||
| 12904 | E9 = DEG2RAD * (34.226 + 1.7484877*d); | ||
| 12905 | E10 = DEG2RAD * (15.134 - 0.1589763*d); | ||
| 12906 | E11 = DEG2RAD * (119.743 + 0.0036096*d); | ||
| 12907 | E12 = DEG2RAD * (239.961 + 0.1643573*d); | ||
| 12908 | E13 = DEG2RAD * (25.053 + 12.9590088*d); | ||
| 12909 | |||
| 12910 | ra = ( | ||
| 12911 | 269.9949 + 0.0031*T | ||
| 12912 | - 3.8787*sin(E1) | ||
| 12913 | - 0.1204*sin(E2) | ||
| 12914 | + 0.0700*sin(E3) | ||
| 12915 | - 0.0172*sin(E4) | ||
| 12916 | + 0.0072*sin(E6) | ||
| 12917 | - 0.0052*sin(E10) | ||
| 12918 | + 0.0043*sin(E13) | ||
| 12919 | ); | ||
| 12920 | |||
| 12921 | dec = ( | ||
| 12922 | 66.5392 + 0.0130*T | ||
| 12923 | + 1.5419*cos(E1) | ||
| 12924 | + 0.0239*cos(E2) | ||
| 12925 | - 0.0278*cos(E3) | ||
| 12926 | + 0.0068*cos(E4) | ||
| 12927 | - 0.0029*cos(E6) | ||
| 12928 | + 0.0009*cos(E7) | ||
| 12929 | + 0.0008*cos(E10) | ||
| 12930 | - 0.0009*cos(E13) | ||
| 12931 | ); | ||
| 12932 | |||
| 12933 | w = ( | ||
| 12934 | 38.3213 + (13.17635815 - 1.4e-12*d)*d | ||
| 12935 | + 3.5610*sin(E1) | ||
| 12936 | + 0.1208*sin(E2) | ||
| 12937 | - 0.0642*sin(E3) | ||
| 12938 | + 0.0158*sin(E4) | ||
| 12939 | + 0.0252*sin(E5) | ||
| 12940 | - 0.0066*sin(E6) | ||
| 12941 | - 0.0047*sin(E7) | ||
| 12942 | - 0.0046*sin(E8) | ||
| 12943 | + 0.0028*sin(E9) | ||
| 12944 | + 0.0052*sin(E10) | ||
| 12945 | + 0.0040*sin(E11) | ||
| 12946 | + 0.0019*sin(E12) | ||
| 12947 | - 0.0044*sin(E13) | ||
| 12948 | ); | ||
| 12949 | break; | ||
| 12950 | |||
| 12951 | case BODY_MARS: | ||
| 12952 | ra = ( | ||
| 12953 | 317.269202 - 0.10927547*T | ||
| 12954 | + 0.000068 * sin(DEG2RAD*(198.991226 + 19139.4819985*T)) | ||
| 12955 | + 0.000238 * sin(DEG2RAD*(226.292679 + 38280.8511281*T)) | ||
| 12956 | + 0.000052 * sin(DEG2RAD*(249.663391 + 57420.7251593*T)) | ||
| 12957 | + 0.000009 * sin(DEG2RAD*(266.183510 + 76560.6367950*T)) | ||
| 12958 | + 0.419057 * sin(DEG2RAD*(79.398797 + 0.5042615*T)) | ||
| 12959 | ); | ||
| 12960 | |||
| 12961 | dec = ( | ||
| 12962 | 54.432516 - 0.05827105*T | ||
| 12963 | + 0.000051*cos(DEG2RAD*(122.433576 + 19139.9407476*T)) | ||
| 12964 | + 0.000141*cos(DEG2RAD*(43.058401 + 38280.8753272*T)) | ||
| 12965 | + 0.000031*cos(DEG2RAD*(57.663379 + 57420.7517205*T)) | ||
| 12966 | + 0.000005*cos(DEG2RAD*(79.476401 + 76560.6495004*T)) | ||
| 12967 | + 1.591274*cos(DEG2RAD*(166.325722 + 0.5042615*T)) | ||
| 12968 | ); | ||
| 12969 | |||
| 12970 | w = ( | ||
| 12971 | 176.049863 + 350.891982443297*d | ||
| 12972 | + 0.000145*sin(DEG2RAD*(129.071773 + 19140.0328244*T)) | ||
| 12973 | + 0.000157*sin(DEG2RAD*(36.352167 + 38281.0473591*T)) | ||
| 12974 | + 0.000040*sin(DEG2RAD*(56.668646 + 57420.9295360*T)) | ||
| 12975 | + 0.000001*sin(DEG2RAD*(67.364003 + 76560.2552215*T)) | ||
| 12976 | + 0.000001*sin(DEG2RAD*(104.792680 + 95700.4387578*T)) | ||
| 12977 | + 0.584542*sin(DEG2RAD*(95.391654 + 0.5042615*T)) | ||
| 12978 | ); | ||
| 12979 | break; | ||
| 12980 | |||
| 12981 | case BODY_JUPITER: | ||
| 12982 | Ja = DEG2RAD*(99.360714 + 4850.4046*T); | ||
| 12983 | Jb = DEG2RAD*(175.895369 + 1191.9605*T); | ||
| 12984 | Jc = DEG2RAD*(300.323162 + 262.5475*T); | ||
| 12985 | Jd = DEG2RAD*(114.012305 + 6070.2476*T); | ||
| 12986 | Je = DEG2RAD*(49.511251 + 64.3000*T); | ||
| 12987 | |||
| 12988 | ra = ( | ||
| 12989 | 268.056595 - 0.006499*T | ||
| 12990 | + 0.000117*sin(Ja) | ||
| 12991 | + 0.000938*sin(Jb) | ||
| 12992 | + 0.001432*sin(Jc) | ||
| 12993 | + 0.000030*sin(Jd) | ||
| 12994 | + 0.002150*sin(Je) | ||
| 12995 | ); | ||
| 12996 | |||
| 12997 | dec = ( | ||
| 12998 | 64.495303 + 0.002413*T | ||
| 12999 | + 0.000050*cos(Ja) | ||
| 13000 | + 0.000404*cos(Jb) | ||
| 13001 | + 0.000617*cos(Jc) | ||
| 13002 | - 0.000013*cos(Jd) | ||
| 13003 | + 0.000926*cos(Je) | ||
| 13004 | ); | ||
| 13005 | |||
| 13006 | w = 284.95 + 870.536*d; | ||
| 13007 | break; | ||
| 13008 | |||
| 13009 | case BODY_SATURN: | ||
| 13010 | ra = 40.589 - 0.036*T; | ||
| 13011 | dec = 83.537 - 0.004*T; | ||
| 13012 | w = 38.90 + 810.7939024*d; | ||
| 13013 | break; | ||
| 13014 | |||
| 13015 | case BODY_URANUS: | ||
| 13016 | ra = 257.311; | ||
| 13017 | dec = -15.175; | ||
| 13018 | w = 203.81 - 501.1600928*d; | ||
| 13019 | break; | ||
| 13020 | |||
| 13021 | case BODY_NEPTUNE: | ||
| 13022 | N = DEG2RAD*(357.85 + 52.316*T); | ||
| 13023 | ra = 299.36 + 0.70*sin(N); | ||
| 13024 | dec = 43.46 - 0.51*cos(N); | ||
| 13025 | w = 249.978 + 541.1397757*d - 0.48*sin(N); | ||
| 13026 | break; | ||
| 13027 | |||
| 13028 | case BODY_PLUTO: | ||
| 13029 | ra = 132.993; | ||
| 13030 | dec = -6.163; | ||
| 13031 | w = 302.695 + 56.3625225*d; | ||
| 13032 | break; | ||
| 13033 | |||
| 13034 | default: | ||
| 13035 | return AxisErr(ASTRO_INVALID_BODY, *time); | ||
| 13036 | } | ||
| 13037 | |||
| 13038 | axis.ra = ra / 15.0; /* convert degrees to sidereal hours */ | ||
| 13039 | axis.dec = dec; | ||
| 13040 | axis.spin = w; | ||
| 13041 | |||
| 13042 | /* calculate the north pole vector using the given angles. */ | ||
| 13043 | radlat = dec * DEG2RAD; | ||
| 13044 | radlon = ra * DEG2RAD; | ||
| 13045 | rcoslat = cos(radlat); | ||
| 13046 | axis.north.x = rcoslat * cos(radlon); | ||
| 13047 | axis.north.y = rcoslat * sin(radlon); | ||
| 13048 | axis.north.z = sin(radlat); | ||
| 13049 | axis.north.t = *time; | ||
| 13050 | axis.north.status = ASTRO_SUCCESS; | ||
| 13051 | |||
| 13052 | axis.status = ASTRO_SUCCESS; | ||
| 13053 | return axis; | ||
| 13054 | } | ||
| 13055 | |||
| 13056 | #ifdef __cplusplus | ||
| 13057 | } | ||
| 13058 | #endif | ||
Packages/MoonriseKit/Sources/CAstronomy/include/astronomy.h added +1431
| @@ -0,0 +1,1431 @@ | |||
| 1 | /* | ||
| 2 | Astronomy Engine for C/C++. | ||
| 3 | https://github.com/cosinekitty/astronomy | ||
| 4 | |||
| 5 | MIT License | ||
| 6 | |||
| 7 | Copyright (c) 2019-2023 Don Cross <cosinekitty@gmail.com> | ||
| 8 | |||
| 9 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 10 | of this software and associated documentation files (the "Software"), to deal | ||
| 11 | in the Software without restriction, including without limitation the rights | ||
| 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 13 | copies of the Software, and to permit persons to whom the Software is | ||
| 14 | furnished to do so, subject to the following conditions: | ||
| 15 | |||
| 16 | The above copyright notice and this permission notice shall be included in all | ||
| 17 | copies or substantial portions of the Software. | ||
| 18 | |||
| 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 25 | SOFTWARE. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #ifndef __ASTRONOMY_H | ||
| 29 | #define __ASTRONOMY_H | ||
| 30 | |||
| 31 | #include <stddef.h> /* for size_t */ | ||
| 32 | |||
| 33 | #ifdef __cplusplus | ||
| 34 | extern "C" { | ||
| 35 | #endif | ||
| 36 | |||
| 37 | /*---------- numeric constants ----------*/ | ||
| 38 | |||
| 39 | /** | ||
| 40 | * \def C_AUDAY | ||
| 41 | * @brief The speed of light in AU/day. | ||
| 42 | */ | ||
| 43 | #define C_AUDAY 173.1446326846693 | ||
| 44 | |||
| 45 | /** | ||
| 46 | * \def KM_PER_AU | ||
| 47 | * @brief The number of kilometers in one astronomical unit (AU). | ||
| 48 | */ | ||
| 49 | #define KM_PER_AU 1.4959787069098932e+8 | ||
| 50 | |||
| 51 | /** | ||
| 52 | * \def AU_PER_LY | ||
| 53 | * @brief The number of astronomical units per light-year. | ||
| 54 | */ | ||
| 55 | #define AU_PER_LY 63241.07708807546 | ||
| 56 | |||
| 57 | /** | ||
| 58 | * \def DEG2RAD | ||
| 59 | * @brief The factor to convert degrees to radians = pi/180. | ||
| 60 | */ | ||
| 61 | #define DEG2RAD 0.017453292519943296 | ||
| 62 | |||
| 63 | /** | ||
| 64 | * \def HOUR2RAD | ||
| 65 | * @brief The factor to convert sidereal hours to radians = pi/12. | ||
| 66 | */ | ||
| 67 | #define HOUR2RAD 0.2617993877991494365 | ||
| 68 | |||
| 69 | /** | ||
| 70 | * \def RAD2DEG | ||
| 71 | * @brief The factor to convert radians to degrees = 180/pi. | ||
| 72 | */ | ||
| 73 | #define RAD2DEG 57.295779513082321 | ||
| 74 | |||
| 75 | /** | ||
| 76 | * \def RAD2HOUR | ||
| 77 | * @brief The factor to convert radians to sidereal hours = 12/pi. | ||
| 78 | */ | ||
| 79 | #define RAD2HOUR 3.819718634205488 | ||
| 80 | |||
| 81 | /** | ||
| 82 | * \def SUN_RADIUS_KM | ||
| 83 | * @brief The mean radius of the Sun's photosphere, expressed in kilometers. The Sun is nearly spherical. | ||
| 84 | */ | ||
| 85 | #define SUN_RADIUS_KM 695700.0 | ||
| 86 | |||
| 87 | /** | ||
| 88 | * \def MERCURY_EQUATORIAL_RADIUS_KM | ||
| 89 | * @brief The equatorial radius of Mercury, expressed in kilometers. | ||
| 90 | */ | ||
| 91 | #define MERCURY_EQUATORIAL_RADIUS_KM 2440.5 | ||
| 92 | |||
| 93 | /** | ||
| 94 | * \def MERCURY_POLAR_RADIUS_KM | ||
| 95 | * @brief The polar radius of Mercury, expressed in kilometers. | ||
| 96 | */ | ||
| 97 | #define MERCURY_POLAR_RADIUS_KM 2438.3 | ||
| 98 | |||
| 99 | /** | ||
| 100 | * \def VENUS_RADIUS_KM | ||
| 101 | * @brief The mean radius of Venus, expressed in kilometers. Venus is nearly spherical. | ||
| 102 | */ | ||
| 103 | #define VENUS_RADIUS_KM 6051.8 | ||
| 104 | |||
| 105 | /** | ||
| 106 | * \def EARTH_EQUATORIAL_RADIUS_KM | ||
| 107 | * @brief The equatorial radius of the Earth, expressed in kilometers. | ||
| 108 | */ | ||
| 109 | #define EARTH_EQUATORIAL_RADIUS_KM 6378.1366 | ||
| 110 | |||
| 111 | /** | ||
| 112 | * \def EARTH_FLATTENING | ||
| 113 | * @brief The Earth's polar radius divided by its equatorial radius. | ||
| 114 | */ | ||
| 115 | #define EARTH_FLATTENING 0.996647180302104 | ||
| 116 | |||
| 117 | /** | ||
| 118 | * \def EARTH_POLAR_RADIUS_KM | ||
| 119 | * @brief The polar radius of the Earth, expressed in kilometers. | ||
| 120 | */ | ||
| 121 | #define EARTH_POLAR_RADIUS_KM (EARTH_EQUATORIAL_RADIUS_KM * EARTH_FLATTENING) | ||
| 122 | |||
| 123 | /** | ||
| 124 | * \def MOON_EQUATORIAL_RADIUS_KM | ||
| 125 | * @brief The equatorial radius of the Moon, expressed in kilometers. | ||
| 126 | */ | ||
| 127 | #define MOON_EQUATORIAL_RADIUS_KM 1738.1 | ||
| 128 | |||
| 129 | /** | ||
| 130 | * \def MOON_POLAR_RADIUS_KM | ||
| 131 | * @brief The polar radius of the Moon, expressed in kilometers. | ||
| 132 | */ | ||
| 133 | #define MOON_POLAR_RADIUS_KM 1736.0 | ||
| 134 | |||
| 135 | /** | ||
| 136 | * \def MARS_EQUATORIAL_RADIUS_KM | ||
| 137 | * @brief The equatorial radius of Mars, expressed in kilometers. | ||
| 138 | */ | ||
| 139 | #define MARS_EQUATORIAL_RADIUS_KM 3396.2 | ||
| 140 | |||
| 141 | /** | ||
| 142 | * \def MARS_POLAR_RADIUS_KM | ||
| 143 | * @brief The polar radius of Mars, expressed in kilometers. | ||
| 144 | */ | ||
| 145 | #define MARS_POLAR_RADIUS_KM 3376.2 | ||
| 146 | |||
| 147 | /* | ||
| 148 | Jupiter radius data are nominal values obtained from: | ||
| 149 | https://www.iau.org/static/resolutions/IAU2015_English.pdf | ||
| 150 | https://nssdc.gsfc.nasa.gov/planetary/factsheet/jupiterfact.html | ||
| 151 | */ | ||
| 152 | |||
| 153 | /** | ||
| 154 | * \def JUPITER_EQUATORIAL_RADIUS_KM | ||
| 155 | * @brief The equatorial radius of Jupiter, expressed in kilometers. | ||
| 156 | */ | ||
| 157 | #define JUPITER_EQUATORIAL_RADIUS_KM 71492.0 | ||
| 158 | |||
| 159 | /** | ||
| 160 | * \def JUPITER_POLAR_RADIUS_KM | ||
| 161 | * @brief The polar radius of Jupiter, expressed in kilometers. | ||
| 162 | */ | ||
| 163 | #define JUPITER_POLAR_RADIUS_KM 66854.0 | ||
| 164 | |||
| 165 | /** | ||
| 166 | * \def JUPITER_MEAN_RADIUS_KM | ||
| 167 | * @brief The volumetric mean radius of Jupiter, expressed in kilometers. | ||
| 168 | */ | ||
| 169 | #define JUPITER_MEAN_RADIUS_KM 69911.0 | ||
| 170 | |||
| 171 | /* | ||
| 172 | The radii of Jupiter's four major moons are obtained from: | ||
| 173 | https://ssd.jpl.nasa.gov/?sat_phys_par | ||
| 174 | */ | ||
| 175 | |||
| 176 | /** | ||
| 177 | * \def IO_RADIUS_KM | ||
| 178 | * @brief The mean radius of Jupiter's moon Io, expressed in kilometers. | ||
| 179 | */ | ||
| 180 | #define IO_RADIUS_KM 1821.6 | ||
| 181 | |||
| 182 | /** | ||
| 183 | * \def EUROPA_RADIUS_KM | ||
| 184 | * @brief The mean radius of Jupiter's moon Europa, expressed in kilometers. | ||
| 185 | */ | ||
| 186 | #define EUROPA_RADIUS_KM 1560.8 | ||
| 187 | |||
| 188 | /** | ||
| 189 | * \def GANYMEDE_RADIUS_KM | ||
| 190 | * @brief The mean radius of Jupiter's moon Ganymede, expressed in kilometers. | ||
| 191 | */ | ||
| 192 | #define GANYMEDE_RADIUS_KM 2631.2 | ||
| 193 | |||
| 194 | /** | ||
| 195 | * \def CALLISTO_RADIUS_KM | ||
| 196 | * @brief The mean radius of Jupiter's moon Callisto, expressed in kilometers. | ||
| 197 | */ | ||
| 198 | #define CALLISTO_RADIUS_KM 2410.3 | ||
| 199 | |||
| 200 | /** | ||
| 201 | * \def SATURN_EQUATORIAL_RADIUS_KM | ||
| 202 | * @brief The equatorial radius of Saturn, expressed in kilometers. | ||
| 203 | */ | ||
| 204 | #define SATURN_EQUATORIAL_RADIUS_KM 60268.0 | ||
| 205 | |||
| 206 | /** | ||
| 207 | * \def SATURN_POLAR_RADIUS_KM | ||
| 208 | * @brief The polar radius of Saturn, expressed in kilometers. | ||
| 209 | */ | ||
| 210 | #define SATURN_POLAR_RADIUS_KM 54364.0 | ||
| 211 | |||
| 212 | /** | ||
| 213 | * \def URANUS_EQUATORIAL_RADIUS_KM | ||
| 214 | * @brief The equatorial radius of Uranus, expressed in kilometers. | ||
| 215 | */ | ||
| 216 | #define URANUS_EQUATORIAL_RADIUS_KM 25559.0 | ||
| 217 | |||
| 218 | /** | ||
| 219 | * \def URANUS_POLAR_RADIUS_KM | ||
| 220 | * @brief The polar radius of Uranus, expressed in kilometers. | ||
| 221 | */ | ||
| 222 | #define URANUS_POLAR_RADIUS_KM 24973.0 | ||
| 223 | |||
| 224 | /** | ||
| 225 | * \def NEPTUNE_EQUATORIAL_RADIUS_KM | ||
| 226 | * @brief The equatorial radius of Neptune, expressed in kilometers. | ||
| 227 | */ | ||
| 228 | #define NEPTUNE_EQUATORIAL_RADIUS_KM 24764.0 | ||
| 229 | |||
| 230 | /** | ||
| 231 | * \def NEPTUNE_POLAR_RADIUS_KM | ||
| 232 | * @brief The polar radius of Neptune, expressed in kilometers. | ||
| 233 | */ | ||
| 234 | #define NEPTUNE_POLAR_RADIUS_KM 24341.0 | ||
| 235 | |||
| 236 | /** | ||
| 237 | * \def PLUTO_RADIUS_KM | ||
| 238 | * @brief The mean radius of Pluto, expressed in kilometers. Pluto is nearly spherical. | ||
| 239 | */ | ||
| 240 | #define PLUTO_RADIUS_KM 1188.3 | ||
| 241 | |||
| 242 | |||
| 243 | /*---------- types ----------*/ | ||
| 244 | |||
| 245 | /** | ||
| 246 | * @brief Indicates success/failure of an Astronomy Engine function call. | ||
| 247 | */ | ||
| 248 | typedef enum | ||
| 249 | { | ||
| 250 | ASTRO_SUCCESS, /**< The operation was successful. */ | ||
| 251 | ASTRO_NOT_INITIALIZED, /**< A placeholder that can be used for data that is not yet initialized. */ | ||
| 252 | ASTRO_INVALID_BODY, /**< The celestial body was not valid. Different sets of bodies are supported depending on the function. */ | ||
| 253 | ASTRO_NO_CONVERGE, /**< A numeric solver failed to converge. This should not happen unless there is a bug in Astronomy Engine. */ | ||
| 254 | ASTRO_BAD_TIME, /**< The provided date/time is outside the range allowed by this function. */ | ||
| 255 | ASTRO_BAD_VECTOR, /**< Vector magnitude is too small to be normalized into a unit vector. */ | ||
| 256 | ASTRO_SEARCH_FAILURE, /**< Search was not able to find an ascending root crossing of the function in the specified time interval. */ | ||
| 257 | ASTRO_EARTH_NOT_ALLOWED, /**< The Earth cannot be treated as a celestial body seen from an observer on the Earth itself. */ | ||
| 258 | ASTRO_NO_MOON_QUARTER, /**< No lunar quarter occurs inside the specified time range. */ | ||
| 259 | ASTRO_WRONG_MOON_QUARTER, /**< Internal error: Astronomy_NextMoonQuarter found the wrong moon quarter. */ | ||
| 260 | ASTRO_INTERNAL_ERROR, /**< A self-check failed inside the code somewhere, indicating a bug needs to be fixed. */ | ||
| 261 | ASTRO_INVALID_PARAMETER, /**< A parameter value passed to a function was not valid. */ | ||
| 262 | ASTRO_FAIL_APSIS, /**< Special-case logic for finding Neptune/Pluto apsis failed. */ | ||
| 263 | ASTRO_BUFFER_TOO_SMALL, /**< A provided buffer's size is too small to receive the requested data. */ | ||
| 264 | ASTRO_OUT_OF_MEMORY, /**< An attempt to allocate memory failed. */ | ||
| 265 | ASTRO_INCONSISTENT_TIMES /**< The provided initial state vectors did not have matching times. */ | ||
| 266 | } | ||
| 267 | astro_status_t; | ||
| 268 | |||
| 269 | /** | ||
| 270 | * @brief A date and time used for astronomical calculations. | ||
| 271 | * | ||
| 272 | * This type is of fundamental importance to Astronomy Engine. | ||
| 273 | * It is used to represent dates and times for all astronomical calculations. | ||
| 274 | * It is also included in the values returned by many Astronomy Engine functions. | ||
| 275 | * | ||
| 276 | * To create a valid astro_time_t value from scratch, call #Astronomy_MakeTime | ||
| 277 | * (for a given calendar date and time) or #Astronomy_CurrentTime (for the system's | ||
| 278 | * current date and time). | ||
| 279 | * | ||
| 280 | * To adjust an existing astro_time_t by a certain real number of days, | ||
| 281 | * call #Astronomy_AddDays. | ||
| 282 | * | ||
| 283 | * The astro_time_t type contains `ut` to represent Universal Time (UT1/UTC) and | ||
| 284 | * `tt` to represent Terrestrial Time (TT, also known as *ephemeris time*). | ||
| 285 | * The difference `tt-ut` is known as *ΔT*, using a best-fit piecewise model devised by | ||
| 286 | * [Espenak and Meeus](https://eclipse.gsfc.nasa.gov/SEhelp/deltatpoly2004.html). | ||
| 287 | * | ||
| 288 | * Both `tt` and `ut` are necessary for performing different astronomical calculations. | ||
| 289 | * Indeed, certain calculations (such as rise/set times) require both time scales. | ||
| 290 | * See the documentation for the `ut` and `tt` fields for more detailed information. | ||
| 291 | * | ||
| 292 | * In cases where `astro_time_t` is included in a structure returned by | ||
| 293 | * a function that can fail, the `astro_status_t` field `status` will contain a value | ||
| 294 | * other than `ASTRO_SUCCESS`; in that case the `ut` and `tt` will hold `NAN` (not a number). | ||
| 295 | * In general, when there is an error code stored in a struct field `status`, the | ||
| 296 | * caller should ignore all other values in that structure, including the `ut` and `tt` | ||
| 297 | * inside `astro_time_t`. | ||
| 298 | */ | ||
| 299 | typedef struct | ||
| 300 | { | ||
| 301 | /** | ||
| 302 | * @brief UT1/UTC number of days since noon on January 1, 2000. | ||
| 303 | * | ||
| 304 | * The floating point number of days of Universal Time since noon UTC January 1, 2000. | ||
| 305 | * Astronomy Engine approximates UTC and UT1 as being the same thing, although they are | ||
| 306 | * not exactly equivalent; UTC and UT1 can disagree by up to ±0.9 seconds. | ||
| 307 | * This approximation is sufficient for the accuracy requirements of Astronomy Engine. | ||
| 308 | * | ||
| 309 | * Universal Time Coordinate (UTC) is the international standard for legal and civil | ||
| 310 | * timekeeping and replaces the older Greenwich Mean Time (GMT) standard. | ||
| 311 | * UTC is kept in sync with unpredictable observed changes in the Earth's rotation | ||
| 312 | * by occasionally adding leap seconds as needed. | ||
| 313 | * | ||
| 314 | * UT1 is an idealized time scale based on observed rotation of the Earth, which | ||
| 315 | * gradually slows down in an unpredictable way over time, due to tidal drag by the Moon and Sun, | ||
| 316 | * large scale weather events like hurricanes, and internal seismic and convection effects. | ||
| 317 | * Conceptually, UT1 drifts from atomic time continuously and erratically, whereas UTC | ||
| 318 | * is adjusted by a scheduled whole number of leap seconds as needed. | ||
| 319 | * | ||
| 320 | * The value in `ut` is appropriate for any calculation involving the Earth's rotation, | ||
| 321 | * such as calculating rise/set times, culumination, and anything involving apparent | ||
| 322 | * sidereal time. | ||
| 323 | * | ||
| 324 | * Before the era of atomic timekeeping, days based on the Earth's rotation | ||
| 325 | * were often known as *mean solar days*. | ||
| 326 | */ | ||
| 327 | double ut; | ||
| 328 | |||
| 329 | /** | ||
| 330 | * @brief Terrestrial Time days since noon on January 1, 2000. | ||
| 331 | * | ||
| 332 | * Terrestrial Time is an atomic time scale defined as a number of days since noon on January 1, 2000. | ||
| 333 | * In this system, days are not based on Earth rotations, but instead by | ||
| 334 | * the number of elapsed [SI seconds](https://physics.nist.gov/cuu/Units/second.html) | ||
| 335 | * divided by 86400. Unlike `ut`, `tt` increases uniformly without adjustments | ||
| 336 | * for changes in the Earth's rotation. | ||
| 337 | * | ||
| 338 | * The value in `tt` is used for calculations of movements not involving the Earth's rotation, | ||
| 339 | * such as the orbits of planets around the Sun, or the Moon around the Earth. | ||
| 340 | * | ||
| 341 | * Historically, Terrestrial Time has also been known by the term *Ephemeris Time* (ET). | ||
| 342 | */ | ||
| 343 | double tt; | ||
| 344 | |||
| 345 | /** | ||
| 346 | * @brief For internal use only. Used to optimize Earth tilt calculations. | ||
| 347 | */ | ||
| 348 | double psi; | ||
| 349 | |||
| 350 | /** | ||
| 351 | * @brief For internal use only. Used to optimize Earth tilt calculations. | ||
| 352 | */ | ||
| 353 | double eps; | ||
| 354 | |||
| 355 | /** | ||
| 356 | * @brief For internal use only. Lazy-caches sidereal time (Earth rotation). | ||
| 357 | */ | ||
| 358 | double st; | ||
| 359 | } | ||
| 360 | astro_time_t; | ||
| 361 | |||
| 362 | /** | ||
| 363 | * @brief A calendar date and time expressed in UTC. | ||
| 364 | */ | ||
| 365 | typedef struct | ||
| 366 | { | ||
| 367 | int year; /**< The year value, e.g. 2019. */ | ||
| 368 | int month; /**< The month value: 1=January, 2=February, ..., 12=December. */ | ||
| 369 | int day; /**< The day of the month in the range 1..31. */ | ||
| 370 | int hour; /**< The hour of the day in the range 0..23. */ | ||
| 371 | int minute; /**< The minute of the hour in the range 0..59. */ | ||
| 372 | double second; /**< The floating point number of seconds in the range [0,60). */ | ||
| 373 | } | ||
| 374 | astro_utc_t; | ||
| 375 | |||
| 376 | /** | ||
| 377 | * @brief A 3D Cartesian vector whose components are expressed in Astronomical Units (AU). | ||
| 378 | */ | ||
| 379 | typedef struct | ||
| 380 | { | ||
| 381 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 382 | double x; /**< The Cartesian x-coordinate of the vector in AU. */ | ||
| 383 | double y; /**< The Cartesian y-coordinate of the vector in AU. */ | ||
| 384 | double z; /**< The Cartesian z-coordinate of the vector in AU. */ | ||
| 385 | astro_time_t t; /**< The date and time at which this vector is valid. */ | ||
| 386 | } | ||
| 387 | astro_vector_t; | ||
| 388 | |||
| 389 | /** | ||
| 390 | * @brief A state vector that contains a position (AU) and velocity (AU/day). | ||
| 391 | */ | ||
| 392 | typedef struct | ||
| 393 | { | ||
| 394 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 395 | double x; /**< The Cartesian position x-coordinate of the vector in AU. */ | ||
| 396 | double y; /**< The Cartesian position y-coordinate of the vector in AU. */ | ||
| 397 | double z; /**< The Cartesian position z-coordinate of the vector in AU. */ | ||
| 398 | double vx; /**< The Cartesian velocity x-coordinate of the vector in AU/day. */ | ||
| 399 | double vy; /**< The Cartesian velocity y-coordinate of the vector in AU/day. */ | ||
| 400 | double vz; /**< The Cartesian velocity z-coordinate of the vector in AU/day. */ | ||
| 401 | astro_time_t t; /**< The date and time at which this state vector is valid. */ | ||
| 402 | } | ||
| 403 | astro_state_vector_t; | ||
| 404 | |||
| 405 | /** | ||
| 406 | * @brief Spherical coordinates: latitude, longitude, distance. | ||
| 407 | */ | ||
| 408 | typedef struct | ||
| 409 | { | ||
| 410 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 411 | double lat; /**< The latitude angle: -90..+90 degrees. */ | ||
| 412 | double lon; /**< The longitude angle: 0..360 degrees. */ | ||
| 413 | double dist; /**< Distance in AU. */ | ||
| 414 | } | ||
| 415 | astro_spherical_t; | ||
| 416 | |||
| 417 | /** | ||
| 418 | * @brief An angular value expressed in degrees. | ||
| 419 | */ | ||
| 420 | typedef struct | ||
| 421 | { | ||
| 422 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 423 | double angle; /**< An angle expressed in degrees. */ | ||
| 424 | } | ||
| 425 | astro_angle_result_t; | ||
| 426 | |||
| 427 | /** | ||
| 428 | * @brief A celestial body. | ||
| 429 | */ | ||
| 430 | typedef enum | ||
| 431 | { | ||
| 432 | BODY_INVALID = -1, /**< An invalid or undefined celestial body. */ | ||
| 433 | BODY_MERCURY, /**< Mercury */ | ||
| 434 | BODY_VENUS, /**< Venus */ | ||
| 435 | BODY_EARTH, /**< Earth */ | ||
| 436 | BODY_MARS, /**< Mars */ | ||
| 437 | BODY_JUPITER, /**< Jupiter */ | ||
| 438 | BODY_SATURN, /**< Saturn */ | ||
| 439 | BODY_URANUS, /**< Uranus */ | ||
| 440 | BODY_NEPTUNE, /**< Neptune */ | ||
| 441 | BODY_PLUTO, /**< Pluto */ | ||
| 442 | BODY_SUN, /**< Sun */ | ||
| 443 | BODY_MOON, /**< Moon */ | ||
| 444 | BODY_EMB, /**< Earth/Moon Barycenter */ | ||
| 445 | BODY_SSB, /**< Solar System Barycenter */ | ||
| 446 | BODY_STAR1 = 101, /**< user-defined star #1 */ | ||
| 447 | BODY_STAR2, /**< user-defined star #2 */ | ||
| 448 | BODY_STAR3, /**< user-defined star #3 */ | ||
| 449 | BODY_STAR4, /**< user-defined star #4 */ | ||
| 450 | BODY_STAR5, /**< user-defined star #5 */ | ||
| 451 | BODY_STAR6, /**< user-defined star #6 */ | ||
| 452 | BODY_STAR7, /**< user-defined star #7 */ | ||
| 453 | BODY_STAR8, /**< user-defined star #8 */ | ||
| 454 | } | ||
| 455 | astro_body_t; | ||
| 456 | |||
| 457 | /** | ||
| 458 | * @brief The location of an observer on (or near) the surface of the Earth. | ||
| 459 | * | ||
| 460 | * This structure is passed to functions that calculate phenomena as observed | ||
| 461 | * from a particular place on the Earth. | ||
| 462 | * | ||
| 463 | * You can create this structure directly, or you can call the convenience function | ||
| 464 | * #Astronomy_MakeObserver to create one for you. | ||
| 465 | */ | ||
| 466 | typedef struct | ||
| 467 | { | ||
| 468 | double latitude; /**< Geographic latitude in degrees north (positive) or south (negative) of the equator. */ | ||
| 469 | double longitude; /**< Geographic longitude in degrees east (positive) or west (negative) of the prime meridian at Greenwich, England. */ | ||
| 470 | double height; /**< The height above (positive) or below (negative) sea level, expressed in meters. */ | ||
| 471 | } | ||
| 472 | astro_observer_t; | ||
| 473 | |||
| 474 | /** | ||
| 475 | * @brief Equatorial angular and cartesian coordinates. | ||
| 476 | * | ||
| 477 | * Coordinates of a celestial body as seen from the Earth (geocentric or topocentric, depending on context), | ||
| 478 | * oriented with respect to the projection of the Earth's equator onto the sky. | ||
| 479 | */ | ||
| 480 | typedef struct | ||
| 481 | { | ||
| 482 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 483 | double ra; /**< right ascension in sidereal hours. */ | ||
| 484 | double dec; /**< declination in degrees */ | ||
| 485 | double dist; /**< distance to the celestial body in AU. */ | ||
| 486 | astro_vector_t vec; /**< equatorial coordinates in cartesian vector form: x = March equinox, y = June solstice, z = north. */ | ||
| 487 | } | ||
| 488 | astro_equatorial_t; | ||
| 489 | |||
| 490 | /** | ||
| 491 | * @brief Ecliptic angular and Cartesian coordinates. | ||
| 492 | * | ||
| 493 | * Coordinates of a celestial body as seen from the center of the Sun (heliocentric), | ||
| 494 | * oriented with respect to the plane of the Earth's orbit around the Sun (the ecliptic). | ||
| 495 | */ | ||
| 496 | typedef struct | ||
| 497 | { | ||
| 498 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 499 | astro_vector_t vec; /**< Cartesian ecliptic vector: x=equinox, y=90 degrees prograde in ecliptic plane, z=northward perpendicular to ecliptic. */ | ||
| 500 | double elat; /**< Latitude in degrees north (positive) or south (negative) of the ecliptic plane. */ | ||
| 501 | double elon; /**< Longitude in degrees around the ecliptic plane prograde from the equinox. */ | ||
| 502 | } | ||
| 503 | astro_ecliptic_t; | ||
| 504 | |||
| 505 | /** | ||
| 506 | * @brief Coordinates of a celestial body as seen by a topocentric observer. | ||
| 507 | * | ||
| 508 | * Contains horizontal and equatorial coordinates seen by an observer on or near | ||
| 509 | * the surface of the Earth (a topocentric observer). | ||
| 510 | * Optionally corrected for atmospheric refraction. | ||
| 511 | */ | ||
| 512 | typedef struct | ||
| 513 | { | ||
| 514 | double azimuth; /**< Compass direction around the horizon in degrees. 0=North, 90=East, 180=South, 270=West. */ | ||
| 515 | double altitude; /**< Angle in degrees above (positive) or below (negative) the observer's horizon. */ | ||
| 516 | double ra; /**< Right ascension in sidereal hours. */ | ||
| 517 | double dec; /**< Declination in degrees. */ | ||
| 518 | } | ||
| 519 | astro_horizon_t; | ||
| 520 | |||
| 521 | /** | ||
| 522 | * @brief Contains a rotation matrix that can be used to transform one coordinate system to another. | ||
| 523 | */ | ||
| 524 | typedef struct | ||
| 525 | { | ||
| 526 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 527 | double rot[3][3]; /**< A normalized 3x3 rotation matrix. */ | ||
| 528 | } | ||
| 529 | astro_rotation_t; | ||
| 530 | |||
| 531 | /** | ||
| 532 | * @brief Selects whether to correct for atmospheric refraction, and if so, how. | ||
| 533 | */ | ||
| 534 | typedef enum | ||
| 535 | { | ||
| 536 | REFRACTION_NONE, /**< No atmospheric refraction correction (airless). */ | ||
| 537 | REFRACTION_NORMAL, /**< Recommended correction for standard atmospheric refraction. */ | ||
| 538 | REFRACTION_JPLHOR /**< Used only for compatibility testing with JPL Horizons online tool. */ | ||
| 539 | } | ||
| 540 | astro_refraction_t; | ||
| 541 | |||
| 542 | /** | ||
| 543 | * @brief Information about idealized atmospheric variables at a given elevation. | ||
| 544 | */ | ||
| 545 | typedef struct | ||
| 546 | { | ||
| 547 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 548 | double pressure; /**< Atmospheric pressure in pascals */ | ||
| 549 | double temperature; /**< Atmospheric temperature in kelvins */ | ||
| 550 | double density; /**< Atmospheric density relative to sea level */ | ||
| 551 | } | ||
| 552 | astro_atmosphere_t; | ||
| 553 | |||
| 554 | /** | ||
| 555 | * @brief The result of a search for an astronomical event. | ||
| 556 | */ | ||
| 557 | typedef struct | ||
| 558 | { | ||
| 559 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 560 | astro_time_t time; /**< The time at which a searched-for event occurs. */ | ||
| 561 | } | ||
| 562 | astro_search_result_t; | ||
| 563 | |||
| 564 | /** | ||
| 565 | * @brief | ||
| 566 | * The dates and times of changes of season for a given calendar year. | ||
| 567 | * Call #Astronomy_Seasons to calculate this data structure for a given year. | ||
| 568 | */ | ||
| 569 | typedef struct | ||
| 570 | { | ||
| 571 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 572 | astro_time_t mar_equinox; /**< The date and time of the March equinox for the specified year. */ | ||
| 573 | astro_time_t jun_solstice; /**< The date and time of the June soltice for the specified year. */ | ||
| 574 | astro_time_t sep_equinox; /**< The date and time of the September equinox for the specified year. */ | ||
| 575 | astro_time_t dec_solstice; /**< The date and time of the December solstice for the specified year. */ | ||
| 576 | } | ||
| 577 | astro_seasons_t; | ||
| 578 | |||
| 579 | /** | ||
| 580 | * @brief A lunar quarter event (new moon, first quarter, full moon, or third quarter) along with its date and time. | ||
| 581 | */ | ||
| 582 | typedef struct | ||
| 583 | { | ||
| 584 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 585 | int quarter; /**< 0=new moon, 1=first quarter, 2=full moon, 3=third quarter. */ | ||
| 586 | astro_time_t time; /**< The date and time of the lunar quarter. */ | ||
| 587 | } | ||
| 588 | astro_moon_quarter_t; | ||
| 589 | |||
| 590 | /** | ||
| 591 | * @brief A real value returned by a function whose ascending root is to be found. | ||
| 592 | * | ||
| 593 | * When calling #Astronomy_Search, the caller must pass in a callback function | ||
| 594 | * compatible with the function-pointer type #astro_search_func_t | ||
| 595 | * whose ascending root is to be found. That callback function must return astro_func_result_t. | ||
| 596 | * If the function call is successful, it will set `status` to `ASTRO_SUCCESS` and `value` | ||
| 597 | * to the numeric value appropriate for the given date and time. | ||
| 598 | * If the call fails for some reason, it should set `status` to an appropriate error value | ||
| 599 | * other than `ASTRO_SUCCESS`; in the error case, to guard against any possible misuse of `value`, | ||
| 600 | * it is recommended to set `value` to `NAN`, though this is not strictly necessary. | ||
| 601 | */ | ||
| 602 | typedef struct | ||
| 603 | { | ||
| 604 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 605 | double value; /**< The value returned by a function whose ascending root is to be found. */ | ||
| 606 | } | ||
| 607 | astro_func_result_t; | ||
| 608 | |||
| 609 | /** | ||
| 610 | * @brief A pointer to a function that is to be passed as a callback to #Astronomy_Search. | ||
| 611 | * | ||
| 612 | * The function #Astronomy_Search numerically solves for the time that a given event occurs. | ||
| 613 | * An event is defined as the time when an arbitrary function transitions between having | ||
| 614 | * a negative value and a non-negative value. This transition is called an *ascending root*. | ||
| 615 | * | ||
| 616 | * The type astro_search_func_t represents such a callback function that accepts a | ||
| 617 | * custom `context` pointer and an astro_time_t representing the time to probe. | ||
| 618 | * The function returns an astro_func_result_t that contains either a real | ||
| 619 | * number in `value` or an error code in `status` that aborts the search. | ||
| 620 | * | ||
| 621 | * The `context` points to some data whose type varies depending on the callback function. | ||
| 622 | * It can contain any auxiliary parameters (other than time) needed to evaluate the function. | ||
| 623 | * For example, a function may pertain to a specific celestial body, in which case `context` | ||
| 624 | * may point to a value of type astro_body_t. The `context` parameter is supplied by | ||
| 625 | * the caller of #Astronomy_Search, which passes it along to every call to the callback function. | ||
| 626 | * If the caller of `Astronomy_Search` knows that the callback function does not need a context, | ||
| 627 | * it is safe to pass `NULL` as the context pointer. | ||
| 628 | */ | ||
| 629 | typedef astro_func_result_t (* astro_search_func_t) (void *context, astro_time_t time); | ||
| 630 | |||
| 631 | /** | ||
| 632 | * @brief A pointer to a function that calculates Delta T. | ||
| 633 | * | ||
| 634 | * Delta T is the discrepancy between times measured using an atomic clock | ||
| 635 | * and times based on observations of the Earth's rotation, which is gradually | ||
| 636 | * slowing down over time. Delta T = TT - UT, where | ||
| 637 | * TT = Terrestrial Time, based on atomic time, and | ||
| 638 | * UT = Universal Time, civil time based on the Earth's rotation. | ||
| 639 | * Astronomy Engine defaults to using a Delta T function defined by | ||
| 640 | * Espenak and Meeus in their "Five Millennium Canon of Solar Eclipses". | ||
| 641 | * See: https://eclipse.gsfc.nasa.gov/SEhelp/deltatpoly2004.html | ||
| 642 | */ | ||
| 643 | typedef double (* astro_deltat_func) (double ut); | ||
| 644 | |||
| 645 | double Astronomy_DeltaT_EspenakMeeus(double ut); | ||
| 646 | double Astronomy_DeltaT_JplHorizons(double ut); | ||
| 647 | |||
| 648 | void Astronomy_SetDeltaTFunction(astro_deltat_func func); | ||
| 649 | |||
| 650 | /** | ||
| 651 | * @brief Indicates whether a body (especially Mercury or Venus) is best seen in the morning or evening. | ||
| 652 | */ | ||
| 653 | typedef enum | ||
| 654 | { | ||
| 655 | VISIBLE_MORNING, /**< The body is best visible in the morning, before sunrise. */ | ||
| 656 | VISIBLE_EVENING /**< The body is best visible in the evening, after sunset. */ | ||
| 657 | } | ||
| 658 | astro_visibility_t; | ||
| 659 | |||
| 660 | /** | ||
| 661 | * @brief | ||
| 662 | * Contains information about the visibility of a celestial body at a given date and time. | ||
| 663 | * See #Astronomy_Elongation for more detailed information about the members of this structure. | ||
| 664 | * See also #Astronomy_SearchMaxElongation for how to search for maximum elongation events. | ||
| 665 | */ | ||
| 666 | typedef struct | ||
| 667 | { | ||
| 668 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 669 | astro_time_t time; /**< The date and time of the observation. */ | ||
| 670 | astro_visibility_t visibility; /**< Whether the body is best seen in the morning or the evening. */ | ||
| 671 | double elongation; /**< The angle in degrees between the body and the Sun, as seen from the Earth. */ | ||
| 672 | double ecliptic_separation; /**< The difference between the ecliptic longitudes of the body and the Sun, as seen from the Earth. */ | ||
| 673 | } | ||
| 674 | astro_elongation_t; | ||
| 675 | |||
| 676 | /** | ||
| 677 | * @brief Information about a celestial body crossing a specific hour angle. | ||
| 678 | * | ||
| 679 | * Returned by the function #Astronomy_SearchHourAngleEx to report information about | ||
| 680 | * a celestial body crossing a certain hour angle as seen by a specified topocentric observer. | ||
| 681 | */ | ||
| 682 | typedef struct | ||
| 683 | { | ||
| 684 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 685 | astro_time_t time; /**< The date and time when the body crosses the specified hour angle. */ | ||
| 686 | astro_horizon_t hor; /**< Apparent coordinates of the body at the time it crosses the specified hour angle. */ | ||
| 687 | } | ||
| 688 | astro_hour_angle_t; | ||
| 689 | |||
| 690 | /** | ||
| 691 | * @brief Information about the brightness and illuminated shape of a celestial body. | ||
| 692 | * | ||
| 693 | * Returned by the functions #Astronomy_Illumination and #Astronomy_SearchPeakMagnitude | ||
| 694 | * to report the visual magnitude and illuminated fraction of a celestial body at a given date and time. | ||
| 695 | */ | ||
| 696 | typedef struct | ||
| 697 | { | ||
| 698 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 699 | astro_time_t time; /**< The date and time of the observation. */ | ||
| 700 | double mag; /**< The visual magnitude of the body. Smaller values are brighter. */ | ||
| 701 | double phase_angle; /**< The angle in degrees between the Sun and the Earth, as seen from the body. Indicates the body's phase as seen from the Earth. */ | ||
| 702 | double phase_fraction; /**< A value in the range [0.0, 1.0] indicating what fraction of the body's apparent disc is illuminated, as seen from the Earth. */ | ||
| 703 | double helio_dist; /**< The distance between the Sun and the body at the observation time. */ | ||
| 704 | double ring_tilt; /**< For Saturn, the tilt angle in degrees of its rings as seen from Earth. For all other bodies, 0. */ | ||
| 705 | } | ||
| 706 | astro_illum_t; | ||
| 707 | |||
| 708 | /** | ||
| 709 | * @brief The type of apsis: pericenter (closest approach) or apocenter (farthest distance). | ||
| 710 | */ | ||
| 711 | typedef enum | ||
| 712 | { | ||
| 713 | APSIS_PERICENTER, /**< The body is at its closest approach to the object it orbits. */ | ||
| 714 | APSIS_APOCENTER, /**< The body is at its farthest distance from the object it orbits. */ | ||
| 715 | APSIS_INVALID /**< Undefined or invalid apsis. */ | ||
| 716 | } | ||
| 717 | astro_apsis_kind_t; | ||
| 718 | |||
| 719 | /** | ||
| 720 | * @brief An apsis event: pericenter (closest approach) or apocenter (farthest distance). | ||
| 721 | * | ||
| 722 | * For the Moon orbiting the Earth, or a planet orbiting the Sun, an *apsis* is an | ||
| 723 | * event where the orbiting body reaches its closest or farthest point from the primary body. | ||
| 724 | * The closest approach is called *pericenter* and the farthest point is *apocenter*. | ||
| 725 | * | ||
| 726 | * More specific terminology is common for particular orbiting bodies. | ||
| 727 | * The Moon's closest approach to the Earth is called *perigee* and its farthest | ||
| 728 | * point is called *apogee*. The closest approach of a planet to the Sun is called | ||
| 729 | * *perihelion* and the furthest point is called *aphelion*. | ||
| 730 | * | ||
| 731 | * This data structure is returned by #Astronomy_SearchLunarApsis and #Astronomy_NextLunarApsis | ||
| 732 | * to iterate through consecutive alternating perigees and apogees. | ||
| 733 | */ | ||
| 734 | typedef struct | ||
| 735 | { | ||
| 736 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 737 | astro_time_t time; /**< The date and time of the apsis. */ | ||
| 738 | astro_apsis_kind_t kind; /**< Whether this is a pericenter or apocenter event. */ | ||
| 739 | double dist_au; /**< The distance between the centers of the bodies in astronomical units. */ | ||
| 740 | double dist_km; /**< The distance between the centers of the bodies in kilometers. */ | ||
| 741 | } | ||
| 742 | astro_apsis_t; | ||
| 743 | |||
| 744 | /** | ||
| 745 | * @brief The different kinds of lunar/solar eclipses. | ||
| 746 | */ | ||
| 747 | typedef enum | ||
| 748 | { | ||
| 749 | ECLIPSE_NONE, /**< No eclipse found. */ | ||
| 750 | ECLIPSE_PENUMBRAL, /**< A penumbral lunar eclipse. (Never used for a solar eclipse.) */ | ||
| 751 | ECLIPSE_PARTIAL, /**< A partial lunar/solar eclipse. */ | ||
| 752 | ECLIPSE_ANNULAR, /**< An annular solar eclipse. (Never used for a lunar eclipse.) */ | ||
| 753 | ECLIPSE_TOTAL /**< A total lunar/solar eclipse. */ | ||
| 754 | } | ||
| 755 | astro_eclipse_kind_t; | ||
| 756 | |||
| 757 | /** | ||
| 758 | * @brief Information about a lunar eclipse. | ||
| 759 | * | ||
| 760 | * Returned by #Astronomy_SearchLunarEclipse or #Astronomy_NextLunarEclipse | ||
| 761 | * to report information about a lunar eclipse event. | ||
| 762 | * If a lunar eclipse is found, `status` holds `ASTRO_SUCCESS` and the other fields are set. | ||
| 763 | * If `status` holds any other value, it is an error code and the other fields are undefined. | ||
| 764 | * | ||
| 765 | * When a lunar eclipse is found, it is classified as penumbral, partial, or total. | ||
| 766 | * Penumbral eclipses are difficult to observe, because the Moon is only slightly dimmed | ||
| 767 | * by the Earth's penumbra; no part of the Moon touches the Earth's umbra. | ||
| 768 | * Partial eclipses occur when part, but not all, of the Moon touches the Earth's umbra. | ||
| 769 | * Total eclipses occur when the entire Moon passes into the Earth's umbra. | ||
| 770 | * | ||
| 771 | * The `kind` field thus holds `ECLIPSE_PENUMBRAL`, `ECLIPSE_PARTIAL`, or `ECLIPSE_TOTAL`, | ||
| 772 | * depending on the kind of lunar eclipse found. | ||
| 773 | * | ||
| 774 | * The `obscuration` field holds a value in the range [0, 1] that indicates what fraction | ||
| 775 | * of the Moon's apparent disc area is covered by the Earth's umbra at the eclipse's peak. | ||
| 776 | * This indicates how dark the peak eclipse appears. For penumbral eclipses, the obscuration | ||
| 777 | * is 0, because the Moon does not pass through the Earth's umbra. For partial eclipses, | ||
| 778 | * the obscuration is somewhere between 0 and 1. For total lunar eclipses, the obscuration is 1. | ||
| 779 | * | ||
| 780 | * Field `peak` holds the date and time of the center of the eclipse, when it is at its peak. | ||
| 781 | * | ||
| 782 | * Fields `sd_penum`, `sd_partial`, and `sd_total` hold the semi-duration of each phase | ||
| 783 | * of the eclipse, which is half of the amount of time the eclipse spends in each | ||
| 784 | * phase (expressed in minutes), or 0 if the eclipse never reaches that phase. | ||
| 785 | * By converting from minutes to days, and subtracting/adding with `center`, the caller | ||
| 786 | * may determine the date and time of the beginning/end of each eclipse phase. | ||
| 787 | */ | ||
| 788 | typedef struct | ||
| 789 | { | ||
| 790 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 791 | astro_eclipse_kind_t kind; /**< The type of lunar eclipse found. */ | ||
| 792 | double obscuration; /**< The peak fraction of the Moon's apparent disc that is covered by the Earth's umbra. */ | ||
| 793 | astro_time_t peak; /**< The time of the eclipse at its peak. */ | ||
| 794 | double sd_penum; /**< The semi-duration of the penumbral phase in minutes. */ | ||
| 795 | double sd_partial; /**< The semi-duration of the partial phase in minutes, or 0.0 if none. */ | ||
| 796 | double sd_total; /**< The semi-duration of the total phase in minutes, or 0.0 if none. */ | ||
| 797 | } | ||
| 798 | astro_lunar_eclipse_t; | ||
| 799 | |||
| 800 | |||
| 801 | /** | ||
| 802 | * @brief Reports the time and geographic location of the peak of a solar eclipse. | ||
| 803 | * | ||
| 804 | * Returned by #Astronomy_SearchGlobalSolarEclipse or #Astronomy_NextGlobalSolarEclipse | ||
| 805 | * to report information about a solar eclipse event. | ||
| 806 | * If a solar eclipse is found, `status` holds `ASTRO_SUCCESS` and `kind`, `peak`, and `distance` | ||
| 807 | * have valid values. The `latitude` and `longitude` are set only for total and annular eclipses | ||
| 808 | * (see more below). | ||
| 809 | * If `status` holds any value other than `ASTRO_SUCCESS`, it is an error code; | ||
| 810 | * in that case, `kind` holds `ECLIPSE_NONE` and all the other fields are undefined. | ||
| 811 | * | ||
| 812 | * The eclipse is classified as partial, annular, or total, depending on the | ||
| 813 | * maximum amount of the Sun's disc obscured, as seen at the peak location | ||
| 814 | * on the surface of the Earth. | ||
| 815 | * | ||
| 816 | * The `kind` field thus holds `ECLIPSE_PARTIAL`, `ECLIPSE_ANNULAR`, or `ECLIPSE_TOTAL`. | ||
| 817 | * A total eclipse is when the peak observer sees the Sun completely blocked by the Moon. | ||
| 818 | * An annular eclipse is like a total eclipse, but the Moon is too far from the Earth's surface | ||
| 819 | * to completely block the Sun; instead, the Sun takes on a ring-shaped appearance. | ||
| 820 | * A partial eclipse is when the Moon blocks part of the Sun's disc, but nobody on the Earth | ||
| 821 | * observes either a total or annular eclipse. | ||
| 822 | * | ||
| 823 | * If `kind` is `ECLIPSE_TOTAL` or `ECLIPSE_ANNULAR`, the `latitude` and `longitude` | ||
| 824 | * fields give the geographic coordinates of the center of the Moon's shadow projected | ||
| 825 | * onto the daytime side of the Earth at the instant of the eclipse's peak. | ||
| 826 | * If `kind` has any other value, `latitude` and `longitude` are undefined and should | ||
| 827 | * not be used. | ||
| 828 | * | ||
| 829 | * For total or annular eclipses, the `obscuration` field holds the fraction (0, 1] | ||
| 830 | * of the Sun's apparent disc area that is blocked from view by the Moon's silhouette, | ||
| 831 | * as seen by an observer located at the geographic coordinates `latitude`, `longitude` | ||
| 832 | * at the darkest time `peak`. The value will always be 1 for total eclipses, and less than | ||
| 833 | * 1 for annular eclipses. | ||
| 834 | * For partial eclipses, `obscuration` is undefined and should not be used. | ||
| 835 | * This is because there is little practical use for an obscuration value of | ||
| 836 | * a partial eclipse without supplying a particular observation location. | ||
| 837 | * Developers who wish to find an obscuration value for partial solar eclipses should therefore use | ||
| 838 | * #Astronomy_SearchLocalSolarEclipse and provide the geographic coordinates of an observer. | ||
| 839 | */ | ||
| 840 | typedef struct | ||
| 841 | { | ||
| 842 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 843 | astro_eclipse_kind_t kind; /**< The type of solar eclipse found. */ | ||
| 844 | double obscuration; /**< The peak fraction of the Sun's apparent disc area obscured by the Moon (total and annular eclipses only). */ | ||
| 845 | astro_time_t peak; /**< The date and time when the solar eclipse is darkest. This is the instant when the axis of the Moon's shadow cone passes closest to the Earth's center. */ | ||
| 846 | double distance; /**< The distance between the Sun/Moon shadow axis and the center of the Earth, in kilometers. */ | ||
| 847 | double latitude; /**< The geographic latitude at the center of the peak eclipse shadow. */ | ||
| 848 | double longitude; /**< The geographic longitude at the center of the peak eclipse shadow. */ | ||
| 849 | } | ||
| 850 | astro_global_solar_eclipse_t; | ||
| 851 | |||
| 852 | |||
| 853 | /** | ||
| 854 | * @brief Holds a time and the observed altitude of the Sun at that time. | ||
| 855 | * | ||
| 856 | * When reporting a solar eclipse observed at a specific location on the Earth | ||
| 857 | * (a "local" solar eclipse), a series of events occur. In addition | ||
| 858 | * to the time of each event, it is important to know the altitude of the Sun, | ||
| 859 | * because each event may be invisible to the observer if the Sun is below | ||
| 860 | * the horizon. | ||
| 861 | * | ||
| 862 | * If `altitude` is negative, the event is theoretical only; it would be | ||
| 863 | * visible if the Earth were transparent, but the observer cannot actually see it. | ||
| 864 | * If `altitude` is positive but less than a few degrees, visibility will be impaired by | ||
| 865 | * atmospheric interference (sunrise or sunset conditions). | ||
| 866 | */ | ||
| 867 | typedef struct | ||
| 868 | { | ||
| 869 | astro_time_t time; /**< The date and time of the event. */ | ||
| 870 | double altitude; /**< The angular altitude of the center of the Sun above/below the horizon, at `time`, corrected for atmospheric refraction and expressed in degrees. */ | ||
| 871 | } | ||
| 872 | astro_eclipse_event_t; | ||
| 873 | |||
| 874 | |||
| 875 | /** | ||
| 876 | * @brief Information about a solar eclipse as seen by an observer at a given time and geographic location. | ||
| 877 | * | ||
| 878 | * Returned by #Astronomy_SearchLocalSolarEclipse or #Astronomy_NextLocalSolarEclipse | ||
| 879 | * to report information about a solar eclipse as seen at a given geographic location. | ||
| 880 | * If a solar eclipse is found, `status` holds `ASTRO_SUCCESS` and the other fields are set. | ||
| 881 | * If `status` holds any other value, it is an error code and the other fields are undefined. | ||
| 882 | * | ||
| 883 | * When a solar eclipse is found, it is classified as partial, annular, or total. | ||
| 884 | * The `kind` field thus holds `ECLIPSE_PARTIAL`, `ECLIPSE_ANNULAR`, or `ECLIPSE_TOTAL`. | ||
| 885 | * A partial solar eclipse is when the Moon does not line up directly enough with the Sun | ||
| 886 | * to completely block the Sun's light from reaching the observer. | ||
| 887 | * An annular eclipse occurs when the Moon's disc is completely visible against the Sun | ||
| 888 | * but the Moon is too far away to completely block the Sun's light; this leaves the | ||
| 889 | * Sun with a ring-like appearance. | ||
| 890 | * A total eclipse occurs when the Moon is close enough to the Earth and aligned with the | ||
| 891 | * Sun just right to completely block all sunlight from reaching the observer. | ||
| 892 | * | ||
| 893 | * The `obscuration` field reports what fraction of the Sun's disc appears blocked | ||
| 894 | * by the Moon when viewed by the observer at the peak eclipse time. | ||
| 895 | * This is a value that ranges from 0 (no blockage) to 1 (total eclipse). | ||
| 896 | * The obscuration value will be between 0 and 1 for partial eclipses and annular eclipses. | ||
| 897 | * The value will be exactly 1 for total eclipses. Obscuration gives an indication | ||
| 898 | * of how dark the eclipse appears. | ||
| 899 | * | ||
| 900 | * There are 5 "event" fields, each of which contains a time and a solar altitude. | ||
| 901 | * Field `peak` holds the date and time of the center of the eclipse, when it is at its peak. | ||
| 902 | * The fields `partial_begin` and `partial_end` are always set, and indicate when | ||
| 903 | * the eclipse begins/ends. If the eclipse reaches totality or becomes annular, | ||
| 904 | * `total_begin` and `total_end` indicate when the total/annular phase begins/ends. | ||
| 905 | * When an event field is valid, the caller must also check its `altitude` field to | ||
| 906 | * see whether the Sun is above the horizon at that time. See #astro_eclipse_kind_t | ||
| 907 | * for more information. | ||
| 908 | */ | ||
| 909 | typedef struct | ||
| 910 | { | ||
| 911 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 912 | astro_eclipse_kind_t kind; /**< The type of solar eclipse found: `ECLIPSE_PARTIAL`, `ECLIPSE_ANNULAR`, or `ECLIPSE_TOTAL`. */ | ||
| 913 | double obscuration; /**< The fraction of the Sun's apparent disc area obscured by the Moon at the eclipse peak. */ | ||
| 914 | astro_eclipse_event_t partial_begin; /**< The time and Sun altitude at the beginning of the eclipse. */ | ||
| 915 | astro_eclipse_event_t total_begin; /**< If this is an annular or a total eclipse, the time and Sun altitude when annular/total phase begins; otherwise invalid. */ | ||
| 916 | astro_eclipse_event_t peak; /**< The time and Sun altitude when the eclipse reaches its peak. */ | ||
| 917 | astro_eclipse_event_t total_end; /**< If this is an annular or a total eclipse, the time and Sun altitude when annular/total phase ends; otherwise invalid. */ | ||
| 918 | astro_eclipse_event_t partial_end; /**< The time and Sun altitude at the end of the eclipse. */ | ||
| 919 | } | ||
| 920 | astro_local_solar_eclipse_t; | ||
| 921 | |||
| 922 | |||
| 923 | /** | ||
| 924 | * @brief Information about a transit of Mercury or Venus, as seen from the Earth. | ||
| 925 | * | ||
| 926 | * Returned by #Astronomy_SearchTransit or #Astronomy_NextTransit to report | ||
| 927 | * information about a transit of Mercury or Venus. | ||
| 928 | * A transit is when Mercury or Venus passes between the Sun and Earth so that | ||
| 929 | * the other planet is seen in silhouette against the Sun. | ||
| 930 | * | ||
| 931 | * The `start` field reports the moment in time when the planet first becomes | ||
| 932 | * visible against the Sun in its background. | ||
| 933 | * The `peak` field reports when the planet is most aligned with the Sun, | ||
| 934 | * as seen from the Earth. | ||
| 935 | * The `finish` field reports the last moment when the planet is visible | ||
| 936 | * against the Sun in its background. | ||
| 937 | * | ||
| 938 | * The calculations are performed from the point of view of a geocentric observer. | ||
| 939 | */ | ||
| 940 | typedef struct | ||
| 941 | { | ||
| 942 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 943 | astro_time_t start; /**< Date and time at the beginning of the transit. */ | ||
| 944 | astro_time_t peak; /**< Date and time of the peak of the transit. */ | ||
| 945 | astro_time_t finish; /**< Date and time at the end of the transit. */ | ||
| 946 | double separation; /**< Angular separation in arcminutes between the centers of the Sun and the planet at time `peak`. */ | ||
| 947 | } | ||
| 948 | astro_transit_t; | ||
| 949 | |||
| 950 | |||
| 951 | /** | ||
| 952 | * @brief Aberration calculation options. | ||
| 953 | * | ||
| 954 | * [Aberration](https://en.wikipedia.org/wiki/Aberration_of_light) is an effect | ||
| 955 | * causing the apparent direction of an observed body to be shifted due to transverse | ||
| 956 | * movement of the Earth with respect to the rays of light coming from that body. | ||
| 957 | * This angular correction can be anywhere from 0 to about 20 arcseconds, | ||
| 958 | * depending on the position of the observed body relative to the instantaneous | ||
| 959 | * velocity vector of the Earth. | ||
| 960 | * | ||
| 961 | * Some Astronomy Engine functions allow optional correction for aberration by | ||
| 962 | * passing in a value of this enumerated type. | ||
| 963 | * | ||
| 964 | * Aberration correction is useful to improve accuracy of coordinates of | ||
| 965 | * apparent locations of bodies seen from the Earth. | ||
| 966 | * However, because aberration affects not only the observed body (such as a planet) | ||
| 967 | * but the surrounding stars, aberration may be unhelpful (for example) | ||
| 968 | * for determining exactly when a planet crosses from one constellation to another. | ||
| 969 | */ | ||
| 970 | typedef enum | ||
| 971 | { | ||
| 972 | ABERRATION, /**< Request correction for aberration. */ | ||
| 973 | NO_ABERRATION /**< Do not correct for aberration. */ | ||
| 974 | } | ||
| 975 | astro_aberration_t; | ||
| 976 | |||
| 977 | /** | ||
| 978 | * @brief Selects the date for which the Earth's equator is to be used for representing equatorial coordinates. | ||
| 979 | * | ||
| 980 | * The Earth's equator is not always in the same plane due to precession and nutation. | ||
| 981 | * | ||
| 982 | * Sometimes it is useful to have a fixed plane of reference for equatorial coordinates | ||
| 983 | * across different calendar dates. In these cases, a fixed *epoch*, or reference time, | ||
| 984 | * is helpful. Astronomy Engine provides the J2000 epoch for such cases. This refers | ||
| 985 | * to the plane of the Earth's orbit as it was on noon UTC on 1 January 2000. | ||
| 986 | * | ||
| 987 | * For some other purposes, it is more helpful to represent coordinates using the Earth's | ||
| 988 | * equator exactly as it is on that date. For example, when calculating rise/set times | ||
| 989 | * or horizontal coordinates, it is most accurate to use the orientation of the Earth's | ||
| 990 | * equator at that same date and time. For these uses, Astronomy Engine allows *of-date* | ||
| 991 | * calculations. | ||
| 992 | */ | ||
| 993 | typedef enum | ||
| 994 | { | ||
| 995 | EQUATOR_J2000, /**< Represent equatorial coordinates in the J2000 epoch. */ | ||
| 996 | EQUATOR_OF_DATE /**< Represent equatorial coordinates using the Earth's equator at the given date and time. */ | ||
| 997 | } | ||
| 998 | astro_equator_date_t; | ||
| 999 | |||
| 1000 | /** | ||
| 1001 | * @brief Selects whether to search for a rise time or a set time. | ||
| 1002 | * | ||
| 1003 | * The #Astronomy_SearchRiseSetEx function finds the rise or set time of a body | ||
| 1004 | * depending on the value of its `direction` parameter. | ||
| 1005 | */ | ||
| 1006 | typedef enum | ||
| 1007 | { | ||
| 1008 | DIRECTION_RISE = +1, /**< Search for the time a body begins to rise above the horizon. */ | ||
| 1009 | DIRECTION_SET = -1, /**< Search for the time a body finishes sinking below the horizon. */ | ||
| 1010 | } | ||
| 1011 | astro_direction_t; | ||
| 1012 | |||
| 1013 | |||
| 1014 | /** | ||
| 1015 | * @brief Reports the constellation that a given celestial point lies within. | ||
| 1016 | * | ||
| 1017 | * The #Astronomy_Constellation function returns this struct | ||
| 1018 | * to report which constellation corresponds with a given point in the sky. | ||
| 1019 | * Constellations are defined with respect to the B1875 equatorial system | ||
| 1020 | * per IAU standard. Although `Astronomy.Constellation` requires J2000 equatorial | ||
| 1021 | * coordinates, the struct contains converted B1875 coordinates for reference. | ||
| 1022 | */ | ||
| 1023 | typedef struct | ||
| 1024 | { | ||
| 1025 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 1026 | const char *symbol; /**< 3-character mnemonic symbol for the constellation, e.g. "Ori". */ | ||
| 1027 | const char *name; /**< Full name of constellation, e.g. "Orion". */ | ||
| 1028 | double ra_1875; /**< Right ascension expressed in B1875 coordinates. */ | ||
| 1029 | double dec_1875; /**< Declination expressed in B1875 coordinates. */ | ||
| 1030 | } | ||
| 1031 | astro_constellation_t; | ||
| 1032 | |||
| 1033 | |||
| 1034 | /** | ||
| 1035 | * @brief Selects the output format of the function #Astronomy_FormatTime. | ||
| 1036 | */ | ||
| 1037 | typedef enum | ||
| 1038 | { | ||
| 1039 | TIME_FORMAT_DAY, /**< Truncate to UTC calendar date only, e.g. `2020-12-31`. Buffer size must be at least 11 characters. */ | ||
| 1040 | TIME_FORMAT_MINUTE, /**< Round to nearest UTC minute, e.g. `2020-12-31T15:47Z`. Buffer size must be at least 18 characters. */ | ||
| 1041 | TIME_FORMAT_SECOND, /**< Round to nearest UTC second, e.g. `2020-12-31T15:47:32Z`. Buffer size must be at least 21 characters. */ | ||
| 1042 | TIME_FORMAT_MILLI /**< Round to nearest UTC millisecond, e.g. `2020-12-31T15:47:32.397Z`. Buffer size must be at least 25 characters. */ | ||
| 1043 | } | ||
| 1044 | astro_time_format_t; | ||
| 1045 | |||
| 1046 | /** | ||
| 1047 | * @brief Lunar libration angles, returned by #Astronomy_Libration. | ||
| 1048 | */ | ||
| 1049 | typedef struct | ||
| 1050 | { | ||
| 1051 | double elat; /**< Sub-Earth libration ecliptic latitude angle, in degrees. */ | ||
| 1052 | double elon; /**< Sub-Earth libration ecliptic longitude angle, in degrees. */ | ||
| 1053 | double mlat; /**< Moon's geocentric ecliptic latitude, in degrees. */ | ||
| 1054 | double mlon; /**< Moon's geocentric ecliptic longitude, in degrees. */ | ||
| 1055 | double dist_km; /**< Distance between the centers of the Earth and Moon in kilometers. */ | ||
| 1056 | double diam_deg; /**< The apparent angular diameter of the Moon, in degrees, as seen from the center of the Earth. */ | ||
| 1057 | } | ||
| 1058 | astro_libration_t; | ||
| 1059 | |||
| 1060 | /** | ||
| 1061 | * @brief Information about a body's rotation axis at a given time. | ||
| 1062 | * | ||
| 1063 | * This structure is returned by #Astronomy_RotationAxis to report | ||
| 1064 | * the orientation of a body's rotation axis at a given moment in time. | ||
| 1065 | * The axis is specified by the direction in space that the body's north pole | ||
| 1066 | * points, using angular equatorial coordinates in the J2000 system (EQJ). | ||
| 1067 | * | ||
| 1068 | * Thus `ra` is the right ascension, and `dec` is the declination, of the | ||
| 1069 | * body's north pole vector at the given moment in time. The north pole | ||
| 1070 | * of a body is defined as the pole that lies on the north side of the | ||
| 1071 | * [Solar System's invariable plane](https://en.wikipedia.org/wiki/Invariable_plane), | ||
| 1072 | * regardless of the body's direction of rotation. | ||
| 1073 | * | ||
| 1074 | * The `spin` field indicates the angular position of a prime meridian | ||
| 1075 | * arbitrarily recommended for the body by the International Astronomical | ||
| 1076 | * Union (IAU). | ||
| 1077 | * | ||
| 1078 | * The fields `ra`, `dec`, and `spin` correspond to the variables | ||
| 1079 | * α0, δ0, and W, respectively, from | ||
| 1080 | * [Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2015](https://astropedia.astrogeology.usgs.gov/download/Docs/WGCCRE/WGCCRE2015reprint.pdf). | ||
| 1081 | * | ||
| 1082 | * The field `north` is a unit vector pointing in the direction of the body's north pole. | ||
| 1083 | * It is expressed in the equatorial J2000 system (EQJ). | ||
| 1084 | */ | ||
| 1085 | typedef struct | ||
| 1086 | { | ||
| 1087 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 1088 | double ra; /**< The J2000 right ascension of the body's north pole direction, in sidereal hours. */ | ||
| 1089 | double dec; /**< The J2000 declination of the body's north pole direction, in degrees. */ | ||
| 1090 | double spin; /**< Rotation angle of the body's prime meridian, in degrees. */ | ||
| 1091 | astro_vector_t north; /**< A J2000 dimensionless unit vector pointing in the direction of the body's north pole. */ | ||
| 1092 | } | ||
| 1093 | astro_axis_t; | ||
| 1094 | |||
| 1095 | |||
| 1096 | /** | ||
| 1097 | * \def TIME_TEXT_BYTES | ||
| 1098 | * @brief The smallest number of characters that is always large enough for #Astronomy_FormatTime. | ||
| 1099 | */ | ||
| 1100 | #define TIME_TEXT_BYTES 28 | ||
| 1101 | |||
| 1102 | /** | ||
| 1103 | * @brief Holds the positions and velocities of Jupiter's major 4 moons. | ||
| 1104 | * | ||
| 1105 | * The #Astronomy_JupiterMoons function returns this struct | ||
| 1106 | * to report position and velocity vectors for Jupiter's largest 4 moons | ||
| 1107 | * Io, Europa, Ganymede, and Callisto. Each position vector is relative | ||
| 1108 | * to the center of Jupiter. Both position and velocity are oriented in | ||
| 1109 | * the EQJ system (that is, using Earth's equator at the J2000 epoch.) | ||
| 1110 | * The positions are expressed in astronomical units (AU), | ||
| 1111 | * and the velocities in AU/day. | ||
| 1112 | */ | ||
| 1113 | typedef struct | ||
| 1114 | { | ||
| 1115 | astro_state_vector_t io; /**< Jovicentric position and velocity of Io. */ | ||
| 1116 | astro_state_vector_t europa; /**< Jovicentric position and velocity of Europa. */ | ||
| 1117 | astro_state_vector_t ganymede; /**< Jovicentric position and velocity of Ganymede. */ | ||
| 1118 | astro_state_vector_t callisto; /**< Jovicentric position and velocity of Callisto. */ | ||
| 1119 | } | ||
| 1120 | astro_jupiter_moons_t; | ||
| 1121 | |||
| 1122 | |||
| 1123 | /** | ||
| 1124 | * @brief Indicates whether a crossing through the ecliptic plane is ascending or descending. | ||
| 1125 | */ | ||
| 1126 | typedef enum | ||
| 1127 | { | ||
| 1128 | INVALID_NODE = 0, /**< Placeholder value for a missing or invalid node. */ | ||
| 1129 | ASCENDING_NODE = +1, /**< The body passes through the ecliptic plane from south to north. */ | ||
| 1130 | DESCENDING_NODE = -1 /**< The body passes through the ecliptic plane from north to south. */ | ||
| 1131 | } | ||
| 1132 | astro_node_kind_t; | ||
| 1133 | |||
| 1134 | /** | ||
| 1135 | * @brief Information about an ascending or descending node of a body. | ||
| 1136 | * | ||
| 1137 | * This structure is returned by #Astronomy_SearchMoonNode and #Astronomy_NextMoonNode | ||
| 1138 | * to report information about the center of the Moon passing through the ecliptic plane. | ||
| 1139 | */ | ||
| 1140 | typedef struct | ||
| 1141 | { | ||
| 1142 | astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */ | ||
| 1143 | astro_time_t time; /**< The time when the body passes through the ecliptic plane. */ | ||
| 1144 | astro_node_kind_t kind; /**< Either `ASCENDING_NODE` or `DESCENDING_NODE`, depending on the direction of the ecliptic plane crossing. */ | ||
| 1145 | } | ||
| 1146 | astro_node_event_t; | ||
| 1147 | |||
| 1148 | |||
| 1149 | /** | ||
| 1150 | * @brief A data type used for managing simulation of the gravitational forces on a small body. | ||
| 1151 | * | ||
| 1152 | * This is an opaque data type used to hold the internal state of | ||
| 1153 | * a numeric integrator used to calculate the trajectory of a small | ||
| 1154 | * body moving through the Solar System. | ||
| 1155 | */ | ||
| 1156 | typedef struct astro_grav_sim_s astro_grav_sim_t; | ||
| 1157 | |||
| 1158 | |||
| 1159 | /*---------- functions ----------*/ | ||
| 1160 | |||
| 1161 | void Astronomy_Reset(void); | ||
| 1162 | double Astronomy_VectorLength(astro_vector_t vector); | ||
| 1163 | astro_angle_result_t Astronomy_AngleBetween(astro_vector_t a, astro_vector_t b); | ||
| 1164 | const char *Astronomy_BodyName(astro_body_t body); | ||
| 1165 | astro_body_t Astronomy_BodyCode(const char *name); | ||
| 1166 | astro_observer_t Astronomy_MakeObserver(double latitude, double longitude, double height); | ||
| 1167 | #if !defined(ASTRONOMY_ENGINE_NO_CURRENT_TIME) | ||
| 1168 | astro_time_t Astronomy_CurrentTime(void); | ||
| 1169 | #endif | ||
| 1170 | astro_time_t Astronomy_MakeTime(int year, int month, int day, int hour, int minute, double second); | ||
| 1171 | astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc); | ||
| 1172 | astro_utc_t Astronomy_UtcFromTime(astro_time_t time); | ||
| 1173 | astro_status_t Astronomy_FormatTime(astro_time_t time, astro_time_format_t format, char *text, size_t size); | ||
| 1174 | astro_time_t Astronomy_TimeFromDays(double ut); | ||
| 1175 | astro_time_t Astronomy_TerrestrialTime(double tt); | ||
| 1176 | astro_time_t Astronomy_AddDays(astro_time_t time, double days); | ||
| 1177 | double Astronomy_SiderealTime(astro_time_t *time); | ||
| 1178 | astro_func_result_t Astronomy_HelioDistance(astro_body_t body, astro_time_t time); | ||
| 1179 | astro_vector_t Astronomy_HelioVector(astro_body_t body, astro_time_t time); | ||
| 1180 | astro_vector_t Astronomy_GeoVector(astro_body_t body, astro_time_t time, astro_aberration_t aberration); | ||
| 1181 | astro_vector_t Astronomy_GeoMoon(astro_time_t time); | ||
| 1182 | astro_spherical_t Astronomy_EclipticGeoMoon(astro_time_t time); | ||
| 1183 | astro_state_vector_t Astronomy_GeoMoonState(astro_time_t time); | ||
| 1184 | astro_state_vector_t Astronomy_GeoEmbState(astro_time_t time); | ||
| 1185 | astro_libration_t Astronomy_Libration(astro_time_t time); | ||
| 1186 | astro_state_vector_t Astronomy_BaryState(astro_body_t body, astro_time_t time); | ||
| 1187 | astro_state_vector_t Astronomy_HelioState(astro_body_t body, astro_time_t time); | ||
| 1188 | |||
| 1189 | double Astronomy_MassProduct(astro_body_t body); | ||
| 1190 | double Astronomy_PlanetOrbitalPeriod(astro_body_t body); | ||
| 1191 | |||
| 1192 | astro_state_vector_t Astronomy_LagrangePoint( | ||
| 1193 | int point, | ||
| 1194 | astro_time_t time, | ||
| 1195 | astro_body_t major_body, | ||
| 1196 | astro_body_t minor_body | ||
| 1197 | ); | ||
| 1198 | |||
| 1199 | astro_state_vector_t Astronomy_LagrangePointFast( | ||
| 1200 | int point, | ||
| 1201 | astro_state_vector_t major_state, | ||
| 1202 | double major_mass, | ||
| 1203 | astro_state_vector_t minor_state, | ||
| 1204 | double minor_mass | ||
| 1205 | ); | ||
| 1206 | |||
| 1207 | astro_jupiter_moons_t Astronomy_JupiterMoons(astro_time_t time); | ||
| 1208 | |||
| 1209 | astro_equatorial_t Astronomy_Equator( | ||
| 1210 | astro_body_t body, | ||
| 1211 | astro_time_t *time, | ||
| 1212 | astro_observer_t observer, | ||
| 1213 | astro_equator_date_t equdate, | ||
| 1214 | astro_aberration_t aberration | ||
| 1215 | ); | ||
| 1216 | |||
| 1217 | astro_vector_t Astronomy_ObserverVector( | ||
| 1218 | astro_time_t *time, | ||
| 1219 | astro_observer_t observer, | ||
| 1220 | astro_equator_date_t equdate | ||
| 1221 | ); | ||
| 1222 | |||
| 1223 | astro_state_vector_t Astronomy_ObserverState( | ||
| 1224 | astro_time_t *time, | ||
| 1225 | astro_observer_t observer, | ||
| 1226 | astro_equator_date_t equdate | ||
| 1227 | ); | ||
| 1228 | |||
| 1229 | astro_observer_t Astronomy_VectorObserver(astro_vector_t *vector, astro_equator_date_t equdate); | ||
| 1230 | |||
| 1231 | double Astronomy_ObserverGravity(double latitude, double height); | ||
| 1232 | |||
| 1233 | astro_ecliptic_t Astronomy_SunPosition(astro_time_t time); | ||
| 1234 | astro_ecliptic_t Astronomy_Ecliptic(astro_vector_t eqj); | ||
| 1235 | astro_angle_result_t Astronomy_EclipticLongitude(astro_body_t body, astro_time_t time); | ||
| 1236 | |||
| 1237 | astro_horizon_t Astronomy_Horizon( | ||
| 1238 | astro_time_t *time, | ||
| 1239 | astro_observer_t observer, | ||
| 1240 | double ra, | ||
| 1241 | double dec, | ||
| 1242 | astro_refraction_t refraction); | ||
| 1243 | |||
| 1244 | astro_angle_result_t Astronomy_AngleFromSun(astro_body_t body, astro_time_t time); | ||
| 1245 | astro_elongation_t Astronomy_Elongation(astro_body_t body, astro_time_t time); | ||
| 1246 | astro_elongation_t Astronomy_SearchMaxElongation(astro_body_t body, astro_time_t startTime); | ||
| 1247 | astro_angle_result_t Astronomy_PairLongitude(astro_body_t body1, astro_body_t body2, astro_time_t time); | ||
| 1248 | |||
| 1249 | /** @cond DOXYGEN_SKIP */ | ||
| 1250 | /* Provided for backward compatibility. Newer code can use Astronomy_PairLongitude. */ | ||
| 1251 | #define Astronomy_LongitudeFromSun(body,time) (Astronomy_PairLongitude((body), BODY_SUN, (time))) | ||
| 1252 | /** @endcond */ | ||
| 1253 | |||
| 1254 | astro_search_result_t Astronomy_SearchRelativeLongitude(astro_body_t body, double targetRelLon, astro_time_t startTime); | ||
| 1255 | astro_angle_result_t Astronomy_MoonPhase(astro_time_t time); | ||
| 1256 | astro_search_result_t Astronomy_SearchMoonPhase(double targetLon, astro_time_t startTime, double limitDays); | ||
| 1257 | astro_moon_quarter_t Astronomy_SearchMoonQuarter(astro_time_t startTime); | ||
| 1258 | astro_moon_quarter_t Astronomy_NextMoonQuarter(astro_moon_quarter_t mq); | ||
| 1259 | astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime); | ||
| 1260 | astro_lunar_eclipse_t Astronomy_NextLunarEclipse(astro_time_t prevEclipseTime); | ||
| 1261 | astro_global_solar_eclipse_t Astronomy_SearchGlobalSolarEclipse(astro_time_t startTime); | ||
| 1262 | astro_global_solar_eclipse_t Astronomy_NextGlobalSolarEclipse(astro_time_t prevEclipseTime); | ||
| 1263 | astro_local_solar_eclipse_t Astronomy_SearchLocalSolarEclipse(astro_time_t startTime, astro_observer_t observer); | ||
| 1264 | astro_local_solar_eclipse_t Astronomy_NextLocalSolarEclipse(astro_time_t prevEclipseTime, astro_observer_t observer); | ||
| 1265 | astro_transit_t Astronomy_SearchTransit(astro_body_t body, astro_time_t startTime); | ||
| 1266 | astro_transit_t Astronomy_NextTransit(astro_body_t body, astro_time_t prevTransitTime); | ||
| 1267 | astro_node_event_t Astronomy_SearchMoonNode(astro_time_t startTime); | ||
| 1268 | astro_node_event_t Astronomy_NextMoonNode(astro_node_event_t prevNode); | ||
| 1269 | |||
| 1270 | astro_search_result_t Astronomy_Search( | ||
| 1271 | astro_search_func_t func, | ||
| 1272 | void *context, | ||
| 1273 | astro_time_t t1, | ||
| 1274 | astro_time_t t2, | ||
| 1275 | double dt_tolerance_seconds); | ||
| 1276 | |||
| 1277 | astro_search_result_t Astronomy_SearchSunLongitude( | ||
| 1278 | double targetLon, | ||
| 1279 | astro_time_t startTime, | ||
| 1280 | double limitDays); | ||
| 1281 | |||
| 1282 | astro_hour_angle_t Astronomy_SearchHourAngleEx( | ||
| 1283 | astro_body_t body, | ||
| 1284 | astro_observer_t observer, | ||
| 1285 | double hourAngle, | ||
| 1286 | astro_time_t startTime, | ||
| 1287 | int direction); | ||
| 1288 | |||
| 1289 | astro_func_result_t Astronomy_HourAngle( | ||
| 1290 | astro_body_t body, | ||
| 1291 | astro_time_t *time, | ||
| 1292 | astro_observer_t observer); | ||
| 1293 | |||
| 1294 | /** @cond DOXYGEN_SKIP */ | ||
| 1295 | |||
| 1296 | /* The following are provided for backward compatibility. */ | ||
| 1297 | #define Astronomy_SearchHourAngle(body,observer,hourAngle,startTime) \ | ||
| 1298 | Astronomy_SearchHourAngleEx((body), (observer), (hourAngle), (startTime), +1) | ||
| 1299 | |||
| 1300 | #define Astronomy_SearchRiseSet(body,observer,direction,startTime,limitDays) \ | ||
| 1301 | Astronomy_SearchRiseSetEx((body), (observer), (direction), (startTime), (limitDays), 0.0) | ||
| 1302 | |||
| 1303 | /** @endcond */ | ||
| 1304 | |||
| 1305 | astro_search_result_t Astronomy_SearchRiseSetEx( | ||
| 1306 | astro_body_t body, | ||
| 1307 | astro_observer_t observer, | ||
| 1308 | astro_direction_t direction, | ||
| 1309 | astro_time_t startTime, | ||
| 1310 | double limitDays, | ||
| 1311 | double metersAboveGround); | ||
| 1312 | |||
| 1313 | astro_search_result_t Astronomy_SearchAltitude( | ||
| 1314 | astro_body_t body, | ||
| 1315 | astro_observer_t observer, | ||
| 1316 | astro_direction_t direction, | ||
| 1317 | astro_time_t startTime, | ||
| 1318 | double limitDays, | ||
| 1319 | double altitude); | ||
| 1320 | |||
| 1321 | astro_atmosphere_t Astronomy_Atmosphere(double elevationMeters); | ||
| 1322 | |||
| 1323 | astro_axis_t Astronomy_RotationAxis(astro_body_t body, astro_time_t *time); | ||
| 1324 | |||
| 1325 | astro_seasons_t Astronomy_Seasons(int year); | ||
| 1326 | astro_illum_t Astronomy_Illumination(astro_body_t body, astro_time_t time); | ||
| 1327 | astro_illum_t Astronomy_SearchPeakMagnitude(astro_body_t body, astro_time_t startTime); | ||
| 1328 | astro_apsis_t Astronomy_SearchLunarApsis(astro_time_t startTime); | ||
| 1329 | astro_apsis_t Astronomy_NextLunarApsis(astro_apsis_t apsis); | ||
| 1330 | astro_apsis_t Astronomy_SearchPlanetApsis(astro_body_t body, astro_time_t startTime); | ||
| 1331 | astro_apsis_t Astronomy_NextPlanetApsis(astro_body_t body, astro_apsis_t apsis); | ||
| 1332 | |||
| 1333 | astro_rotation_t Astronomy_IdentityMatrix(void); | ||
| 1334 | astro_rotation_t Astronomy_InverseRotation(astro_rotation_t rotation); | ||
| 1335 | astro_rotation_t Astronomy_CombineRotation(astro_rotation_t a, astro_rotation_t b); | ||
| 1336 | astro_rotation_t Astronomy_Pivot(astro_rotation_t rotation, int axis, double angle); | ||
| 1337 | astro_vector_t Astronomy_VectorFromSphere(astro_spherical_t sphere, astro_time_t time); | ||
| 1338 | astro_spherical_t Astronomy_SphereFromVector(astro_vector_t vector); | ||
| 1339 | astro_equatorial_t Astronomy_EquatorFromVector(astro_vector_t vector); | ||
| 1340 | astro_vector_t Astronomy_VectorFromHorizon(astro_spherical_t sphere, astro_time_t time, astro_refraction_t refraction); | ||
| 1341 | astro_spherical_t Astronomy_HorizonFromVector(astro_vector_t vector, astro_refraction_t refraction); | ||
| 1342 | astro_vector_t Astronomy_RotateVector(astro_rotation_t rotation, astro_vector_t vector); | ||
| 1343 | astro_state_vector_t Astronomy_RotateState(astro_rotation_t rotation, astro_state_vector_t state); | ||
| 1344 | |||
| 1345 | astro_rotation_t Astronomy_Rotation_EQD_EQJ(astro_time_t *time); | ||
| 1346 | astro_rotation_t Astronomy_Rotation_EQD_ECL(astro_time_t *time); | ||
| 1347 | astro_rotation_t Astronomy_Rotation_EQD_ECT(astro_time_t *time); | ||
| 1348 | astro_rotation_t Astronomy_Rotation_EQD_HOR(astro_time_t *time, astro_observer_t observer); | ||
| 1349 | astro_rotation_t Astronomy_Rotation_EQJ_EQD(astro_time_t *time); | ||
| 1350 | astro_rotation_t Astronomy_Rotation_EQJ_ECT(astro_time_t *time); | ||
| 1351 | astro_rotation_t Astronomy_Rotation_EQJ_ECL(void); | ||
| 1352 | astro_rotation_t Astronomy_Rotation_EQJ_HOR(astro_time_t *time, astro_observer_t observer); | ||
| 1353 | astro_rotation_t Astronomy_Rotation_ECL_EQD(astro_time_t *time); | ||
| 1354 | astro_rotation_t Astronomy_Rotation_ECL_EQJ(void); | ||
| 1355 | astro_rotation_t Astronomy_Rotation_ECL_HOR(astro_time_t *time, astro_observer_t observer); | ||
| 1356 | astro_rotation_t Astronomy_Rotation_ECT_EQJ(astro_time_t *time); | ||
| 1357 | astro_rotation_t Astronomy_Rotation_ECT_EQD(astro_time_t *time); | ||
| 1358 | astro_rotation_t Astronomy_Rotation_HOR_EQD(astro_time_t *time, astro_observer_t observer); | ||
| 1359 | astro_rotation_t Astronomy_Rotation_HOR_EQJ(astro_time_t *time, astro_observer_t observer); | ||
| 1360 | astro_rotation_t Astronomy_Rotation_HOR_ECL(astro_time_t *time, astro_observer_t observer); | ||
| 1361 | astro_rotation_t Astronomy_Rotation_EQJ_GAL(void); | ||
| 1362 | astro_rotation_t Astronomy_Rotation_GAL_EQJ(void); | ||
| 1363 | |||
| 1364 | double Astronomy_Refraction(astro_refraction_t refraction, double altitude); | ||
| 1365 | double Astronomy_InverseRefraction(astro_refraction_t refraction, double bent_altitude); | ||
| 1366 | |||
| 1367 | astro_constellation_t Astronomy_Constellation(double ra, double dec); | ||
| 1368 | |||
| 1369 | astro_status_t Astronomy_GravSimInit( | ||
| 1370 | astro_grav_sim_t **simOut, | ||
| 1371 | astro_body_t originBody, | ||
| 1372 | astro_time_t time, | ||
| 1373 | int numBodies, | ||
| 1374 | const astro_state_vector_t *bodyStateArray | ||
| 1375 | ); | ||
| 1376 | |||
| 1377 | astro_status_t Astronomy_GravSimUpdate( | ||
| 1378 | astro_grav_sim_t *sim, | ||
| 1379 | astro_time_t time, | ||
| 1380 | int numBodies, | ||
| 1381 | astro_state_vector_t *bodyStateArray | ||
| 1382 | ); | ||
| 1383 | |||
| 1384 | astro_state_vector_t Astronomy_GravSimBodyState( | ||
| 1385 | astro_grav_sim_t *sim, | ||
| 1386 | astro_body_t body | ||
| 1387 | ); | ||
| 1388 | |||
| 1389 | astro_time_t Astronomy_GravSimTime(const astro_grav_sim_t *sim); | ||
| 1390 | int Astronomy_GravSimNumBodies(const astro_grav_sim_t *sim); | ||
| 1391 | astro_body_t Astronomy_GravSimOrigin(const astro_grav_sim_t *sim); | ||
| 1392 | void Astronomy_GravSimSwap(astro_grav_sim_t *sim); | ||
| 1393 | void Astronomy_GravSimFree(astro_grav_sim_t *sim); | ||
| 1394 | |||
| 1395 | /** | ||
| 1396 | * @brief A function for which to solve a light-travel time problem. | ||
| 1397 | * | ||
| 1398 | * The function #Astronomy_CorrectLightTravel solves a generalized | ||
| 1399 | * problem of deducing how far in the past light must have left | ||
| 1400 | * a target object to be seen by an observer at a specified time. | ||
| 1401 | * This function pointer type expresses an arbitrary position vector | ||
| 1402 | * as function of time. Such a function must be passed to | ||
| 1403 | * `Astronomy_CorrectLightTravel`. | ||
| 1404 | */ | ||
| 1405 | typedef astro_vector_t (* astro_position_func_t) (void *context, astro_time_t time); | ||
| 1406 | |||
| 1407 | astro_vector_t Astronomy_CorrectLightTravel( | ||
| 1408 | void *context, | ||
| 1409 | astro_position_func_t func, | ||
| 1410 | astro_time_t time | ||
| 1411 | ); | ||
| 1412 | |||
| 1413 | astro_vector_t Astronomy_BackdatePosition( | ||
| 1414 | astro_time_t time, | ||
| 1415 | astro_body_t observerBody, | ||
| 1416 | astro_body_t targetBody, | ||
| 1417 | astro_aberration_t aberration | ||
| 1418 | ); | ||
| 1419 | |||
| 1420 | astro_status_t Astronomy_DefineStar( | ||
| 1421 | astro_body_t body, | ||
| 1422 | double ra, | ||
| 1423 | double dec, | ||
| 1424 | double distanceLightYears | ||
| 1425 | ); | ||
| 1426 | |||
| 1427 | #ifdef __cplusplus | ||
| 1428 | } | ||
| 1429 | #endif | ||
| 1430 | |||
| 1431 | #endif /* ifndef __ASTRONOMY_H */ | ||
Packages/MoonriseKit/Sources/SkyEngine/SkyEngine.swift added +131
| @@ -0,0 +1,131 @@ | |||
| 1 | import CAstronomy | ||
| 2 | import Foundation | ||
| 3 | |||
| 4 | /// A place on Earth. Latitude and longitude in degrees (east positive), height in metres. | ||
| 5 | public struct Observer: Sendable, Equatable { | ||
| 6 | public var latitude: Double | ||
| 7 | public var longitude: Double | ||
| 8 | public var height: Double | ||
| 9 | |||
| 10 | public init(latitude: Double, longitude: Double, height: Double = 0) { | ||
| 11 | self.latitude = latitude | ||
| 12 | self.longitude = longitude | ||
| 13 | self.height = height | ||
| 14 | } | ||
| 15 | |||
| 16 | var astro: astro_observer_t { Astronomy_MakeObserver(latitude, longitude, height) } | ||
| 17 | } | ||
| 18 | |||
| 19 | public enum Body: String, CaseIterable, Sendable { | ||
| 20 | case sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto | ||
| 21 | |||
| 22 | public static let planets: [Body] = [.mercury, .venus, .mars, .jupiter, .saturn, .uranus, .neptune] | ||
| 23 | |||
| 24 | var astro: astro_body_t { | ||
| 25 | switch self { | ||
| 26 | case .sun: BODY_SUN | ||
| 27 | case .moon: BODY_MOON | ||
| 28 | case .mercury: BODY_MERCURY | ||
| 29 | case .venus: BODY_VENUS | ||
| 30 | case .mars: BODY_MARS | ||
| 31 | case .jupiter: BODY_JUPITER | ||
| 32 | case .saturn: BODY_SATURN | ||
| 33 | case .uranus: BODY_URANUS | ||
| 34 | case .neptune: BODY_NEPTUNE | ||
| 35 | case .pluto: BODY_PLUTO | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Apparent right ascension (hours) and declination (degrees) of date, distance in AU. | ||
| 41 | public struct Equatorial: Sendable, Equatable { | ||
| 42 | public var rightAscension: Double | ||
| 43 | public var declination: Double | ||
| 44 | public var distance: Double | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Altitude above the horizon and azimuth from north through east, both in degrees. | ||
| 48 | public struct Horizontal: Sendable, Equatable { | ||
| 49 | public var altitude: Double | ||
| 50 | public var azimuth: Double | ||
| 51 | |||
| 52 | public init(altitude: Double, azimuth: Double) { | ||
| 53 | self.altitude = altitude | ||
| 54 | self.azimuth = azimuth | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | public struct Illumination: Sendable, Equatable { | ||
| 59 | public var magnitude: Double | ||
| 60 | /// Fraction of the disc that is lit, 0...1. | ||
| 61 | public var phaseFraction: Double | ||
| 62 | } | ||
| 63 | |||
| 64 | public enum SkyEngineError: Error, Equatable { | ||
| 65 | case astronomy(status: Int) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Positions and phases from Astronomy Engine. Its C code keeps internal caches, | ||
| 69 | /// so call it from one thread at a time. | ||
| 70 | public enum SkyEngine { | ||
| 71 | static func time(_ date: Date) -> astro_time_t { | ||
| 72 | // Days since J2000 (2000-01-01 12:00 UTC). | ||
| 73 | Astronomy_TimeFromDays((date.timeIntervalSince1970 - 946_728_000) / 86_400) | ||
| 74 | } | ||
| 75 | |||
| 76 | static func check(_ status: astro_status_t) throws { | ||
| 77 | guard status == ASTRO_SUCCESS else { throw SkyEngineError.astronomy(status: Int(status.rawValue)) } | ||
| 78 | } | ||
| 79 | |||
| 80 | public static func equatorial(_ body: Body, at date: Date, from observer: Observer) throws -> Equatorial { | ||
| 81 | var t = time(date) | ||
| 82 | let eq = Astronomy_Equator(body.astro, &t, observer.astro, EQUATOR_OF_DATE, ABERRATION) | ||
| 83 | try check(eq.status) | ||
| 84 | return Equatorial(rightAscension: eq.ra, declination: eq.dec, distance: eq.dist) | ||
| 85 | } | ||
| 86 | |||
| 87 | public static func horizontal(_ body: Body, at date: Date, from observer: Observer, refraction: Bool = false) throws -> Horizontal { | ||
| 88 | let eq = try equatorial(body, at: date, from: observer) | ||
| 89 | return horizontal(rightAscension: eq.rightAscension, declination: eq.declination, at: date, from: observer, refraction: refraction) | ||
| 90 | } | ||
| 91 | |||
| 92 | /// For fixed objects such as stars: RA in hours, declination in degrees, both of date. | ||
| 93 | public static func horizontal(rightAscension: Double, declination: Double, at date: Date, from observer: Observer, refraction: Bool = false) -> Horizontal { | ||
| 94 | var t = time(date) | ||
| 95 | let hor = Astronomy_Horizon(&t, observer.astro, rightAscension, declination, refraction ? REFRACTION_NORMAL : REFRACTION_NONE) | ||
| 96 | return Horizontal(altitude: hor.altitude, azimuth: hor.azimuth) | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Horizontal positions for many fixed objects at once, from J2000 RA and declination in degrees. | ||
| 100 | /// Applies precession and nutation; no aberration or refraction. | ||
| 101 | public static func horizontal(j2000 coordinates: [(ra: Double, dec: Double)], at date: Date, from observer: Observer) -> [Horizontal] { | ||
| 102 | var t = time(date) | ||
| 103 | let rotation = Astronomy_Rotation_EQJ_HOR(&t, observer.astro) | ||
| 104 | return coordinates.map { c in | ||
| 105 | let ra = c.ra * .pi / 180, dec = c.dec * .pi / 180 | ||
| 106 | let v = astro_vector_t(status: ASTRO_SUCCESS, x: cos(dec) * cos(ra), y: cos(dec) * sin(ra), z: sin(dec), t: t) | ||
| 107 | let s = Astronomy_HorizonFromVector(Astronomy_RotateVector(rotation, v), REFRACTION_NONE) | ||
| 108 | return Horizontal(altitude: s.lat, azimuth: s.lon) | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Heliocentric position in AU, J2000 equatorial axes. | ||
| 113 | public static func heliocentric(_ body: Body, at date: Date) throws -> SIMD3<Double> { | ||
| 114 | let v = Astronomy_HelioVector(body.astro, time(date)) | ||
| 115 | try check(v.status) | ||
| 116 | return SIMD3(v.x, v.y, v.z) | ||
| 117 | } | ||
| 118 | |||
| 119 | public static func illumination(_ body: Body, at date: Date) throws -> Illumination { | ||
| 120 | let i = Astronomy_Illumination(body.astro, time(date)) | ||
| 121 | try check(i.status) | ||
| 122 | return Illumination(magnitude: i.mag, phaseFraction: i.phase_fraction) | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Moon's ecliptic longitude east of the sun in degrees: 0 new, 90 first quarter, 180 full, 270 last quarter. | ||
| 126 | public static func moonPhase(at date: Date) throws -> Double { | ||
| 127 | let a = Astronomy_MoonPhase(time(date)) | ||
| 128 | try check(a.status) | ||
| 129 | return a.angle | ||
| 130 | } | ||
| 131 | } | ||
Packages/MoonriseKit/Sources/SkyRenderer/Resources/bsc5.json added +1
| @@ -0,0 +1 @@ | |||
| 1 | [[1,1.29125,45.22917,6.7,0.07],[2,1.26583,-0.50306,6.29,1.1],[3,1.33375,-5.7075,4.61,1.04],[4,1.425,13.39611,5.51,0.9],[5,1.56667,58.43667,5.96,0.67],[6,1.57917,-49.075,5.7,0.52],[7,1.61042,64.19611,5.59,-0.03],[8,1.65333,29.02139,6.13,0.75],[9,1.70875,-23.1075,6.18,0.38],[10,1.82583,-17.38639,6.19,0.14],[11,1.93375,-2.54889,6.43,-0.14],[12,1.945,-22.50889,5.94,0.14],[13,2.01458,-33.52944,5.68,1.12],[14,2.05042,-2.44778,6.07,1.38],[15,2.09708,29.09056,2.06,-0.11],[16,2.0725,-8.82389,5.99,1.04],[17,2.17083,36.62667,6.19,0.48],[18,2.13917,-17.5775,6.06,1.67],[19,2.2175,25.46278,6.23,0.97],[20,2.33417,79.71472,6.01,0.19],[21,2.29458,59.14972,2.27,0.34],[22,2.26,18.21194,5.53,1.04],[23,2.26,-54.00194,6.33,0.74],[24,2.3375,-27.98778,5.42,0.42],[25,2.35292,-45.7475,3.88,1.03],[26,2.50958,11.14556,5.51,-0.07],[27,2.58042,46.07222,5.03,0.4],[28,2.62375,57.16556,6.74,-0.08],[29,2.57833,-5.24861,5.84,0.98],[30,2.50875,-82.22389,5.28,1.05],[31,2.67833,-12.58,5.85,1.01],[32,2.66083,-73.22444,6.64,0.37],[33,2.81625,-15.46806,4.89,0.49],[34,2.89333,-27.79972,5.41,1.34],[35,2.93333,-35.13306,5.25,0.44],[36,2.99625,48.1525,6.16,1.45],[37,3.04167,-17.93833,5.25,1.48],[38,3.21,37.69333,6.73,-0.13],[39,3.30917,15.18361,2.83,-0.23],[40,3.35,26.98722,6.3,0.65],[41,3.37833,41.03528,5.72,0.31],[42,3.42542,-26.02194,5.94,1.55],[43,3.43417,-26.28472,6.31,1.48],[44,3.50958,33.20611,6.25,-0.01],[45,3.65083,20.20667,4.8,1.57],[46,3.615,-7.78056,5.12,1.62],[47,3.33083,-84.99417,5.77,1.72],[48,3.66,-18.93278,4.44,1.66],[49,3.73375,22.28417,6.24,-0.01],[50,3.745,8.82083,5.79,0.31],[51,3.72708,-9.56972,5.75,-0.08],[52,3.77917,31.53583,6.45,0.6],[53,3.79417,27.28306,6.35,-0.02],[54,3.7425,-34.90444,6.17,1.34],[55,4.05833,76.95083,6.35,-0.07],[56,4.09,43.59472,6.15,0.05],[57,4.03708,-31.44639,5.67,1.35],[58,3.98,-75.91139,6.49,1.0],[59,4.14208,8.24,6.11,0.92],[60,4.23792,61.53333,5.74,0.88],[61,4.17708,-20.21056,6.47,-0.09],[62,4.28792,47.9475,5.89,-0.09],[63,4.27292,38.68167,4.61,0.06],[64,4.20417,-78.78056,6.77,0.46],[65,4.42917,51.43306,6.14,-0.13],[66,4.38583,-19.05111,6.45,0.37],[67,4.44875,1.68889,6.17,0.94],[68,4.58208,36.78528,4.52,0.05],[69,4.57167,11.20583,6.05,1.03],[70,4.67542,43.79111,6.11,-0.08],[71,4.65958,31.51722,5.87,-0.01],[72,4.67417,-8.05278,6.46,0.68],[73,4.6775,-43.23528,6.33,1.21],[74,4.85708,-8.82389,3.56,1.22],[75,4.92333,40.72972,6.33,1.18],[76,5.02167,48.86528,6.52,0.6],[77,5.01792,-64.87472,4.23,0.58],[78,5.10167,30.93583,5.9,-0.1],[79,5.18958,32.91139,5.79,1.59],[80,5.14958,8.19028,5.37,1.34],[81,5.22708,10.97694,6.56,0.6],[82,5.28042,37.96861,5.18,0.42],[83,5.1625,-69.625,5.51,-0.05],[84,5.38,-28.98167,5.18,1.0],[85,5.44292,-20.05778,5.12,1.82],[86,5.60625,13.4825,6.23,1.22],[87,5.36917,-77.42694,5.97,1.4],[88,5.71583,-12.20944,6.39,0.66],[89,5.8025,-31.03611,6.55,0.0],[90,6.00833,38.57722,7.39,1.97],[91,6.065,52.02,5.57,-0.12],[93,6.19792,61.83111,5.4,0.0],[94,6.12375,-2.21917,6.07,1.22],[96,6.27667,53.04694,5.74,-0.06],[97,6.35083,1.93972,5.77,0.86],[98,6.43792,-77.25417,2.8,0.62],[99,6.57083,-42.30611,2.39,1.09],[100,6.55083,-43.68,3.94,0.17],[101,6.65583,-0.04972,6.19,0.9],[102,6.81125,-25.54722,5.98,1.03],[103,7.01208,17.89306,5.06,1.65],[104,7.05708,44.39444,5.17,0.03],[105,6.98208,-33.00722,4.81,1.64],[106,7.05292,16.445,6.06,1.58],[107,7.08375,10.18972,6.04,0.43],[108,7.08792,-20.335,6.43,0.59],[109,7.11,-39.915,5.43,1.56],[110,7.23583,36.9,6.26,0.92],[111,7.17958,-50.53278,6.26,1.09],[112,7.72917,77.01944,6.21,0.84],[113,7.58292,59.97722,5.94,0.01],[114,7.53042,29.75167,5.23,0.24],[115,7.46625,-14.86417,6.14,0.37],[116,7.45375,-32.11667,6.57,1.34],[117,7.51,-3.95722,5.72,1.56],[118,7.59458,-23.78778,5.19,0.12],[119,7.61583,-40.93944,6.19,0.33],[120,7.60875,-48.215,5.69,0.35],[121,7.85542,66.51944,6.18,-0.1],[122,7.85667,33.58167,5.87,1.14],[123,7.94333,54.52222,4.73,-0.1],[124,7.92167,52.83944,5.6,1.15],[125,7.85417,-48.80361,4.77,0.02],[126,7.88625,-62.95806,4.37,-0.07],[127,7.89,-62.96583,4.54,0.15],[128,8.11167,43.49472,6.7,-0.01],[129,8.33042,70.98167,6.42,-0.01],[130,8.25,62.93167,4.16,0.14],[131,8.14792,20.29444,5.38,1.08],[132,8.09917,6.95556,5.67,0.0],[133,8.14375,27.58056,6.67,0.6],[134,8.20458,28.28028,6.3,1.0],[135,8.29333,54.895,5.93,1.04],[136,8.1825,-63.03111,5.09,0.04],[137,8.60375,66.75028,6.48,-0.08],[138,8.42125,-29.55833,5.55,1.27],[139,8.34708,-71.26611,6.13,0.23],[140,8.61583,-52.37306,5.57,0.47],[141,8.73042,13.37111,6.4,0.6],[142,8.81208,-3.59278,5.2,0.56],[143,8.88667,-0.50556,5.93,0.44],[144,9.03458,54.16861,5.08,-0.11],[145,8.97792,13.20667,6.41,0.52],[146,9.11375,60.32611,5.79,0.29],[147,8.92125,-48.00083,5.51,0.44],[148,8.88917,-54.82194,6.06,1.01],[149,9.08333,27.25472,6.5,-0.09],[150,9.0125,-14.97361,6.45,1.06],[151,9.02875,-22.8425,6.06,0.29],[152,9.19417,44.48861,5.13,1.6],[153,9.24292,53.89694,3.66,-0.2],[154,9.22042,33.71944,4.36,-0.14],[155,9.19708,15.23167,5.89,-0.15],[156,9.28,24.01417,6.47,0.6],[157,9.33792,35.39944,5.48,0.88],[158,9.94708,82.49389,6.4,0.55],[159,9.33625,-24.76722,5.57,0.72],[160,9.15583,-65.12472,6.42,1.26],[161,9.37708,3.13528,6.39,1.33],[162,9.32542,-54.39417,6.41,1.0],[163,9.63875,29.31167,4.37,0.87],[164,9.79125,49.35444,5.43,1.64],[165,9.83208,30.86083,3.27,1.28],[166,9.84083,21.25056,5.87,0.85],[167,9.98167,21.43833,5.36,1.16],[168,10.12708,56.53722,2.23,1.17],[169,9.67,-73.13722,6.85,0.11],[170,9.99125,-33.96167,6.69,0.51],[171,9.96667,-44.79667,6.01,1.14],[172,10.11917,-16.51694,6.49,0.92],[173,10.13708,-23.80444,6.14,0.7],[174,10.17667,-4.35194,5.91,1.1],[175,10.28,39.45861,5.33,0.89],[176,10.11,-59.45444,5.89,0.55],[177,10.51417,66.1475,5.83,1.04],[178,10.4,24.62917,6.04,0.26],[179,10.51625,50.5125,4.8,-0.11],[180,10.33167,-46.085,4.59,0.97],[181,10.62958,58.75333,6.17,-0.01],[183,10.44333,-56.50167,5.7,0.2],[184,10.86708,47.02472,4.94,0.18],[185,10.67875,-38.46333,6.06,-0.03],[186,10.67417,-60.2625,5.98,1.32],[187,10.61833,-65.46806,5.39,0.5],[188,10.8975,-17.98667,2.04,1.02],[189,11.11,47.86417,5.67,-0.12],[190,10.95917,-12.01167,6.02,1.1],[191,10.83833,-57.46306,4.36,0.0],[192,11.4125,74.98806,5.66,0.05],[193,11.18125,48.28444,4.54,-0.07],[194,11.0475,-10.60944,4.76,1.01],[195,11.05042,-38.42167,5.9,1.16],[196,11.32167,55.22167,5.42,0.04],[197,11.185,-22.00611,5.24,0.33],[198,11.23792,-42.67667,5.94,0.28],[199,11.13458,-62.49778,6.07,0.44],[200,11.6625,69.325,6.33,0.6],[201,11.35042,-4.62917,6.15,1.62],[202,11.25,-53.715,6.15,0.53],[203,11.36958,-12.88083,6.15,0.61],[204,11.56292,55.30528,6.52,0.6],[205,11.54542,44.86139,6.05,-0.07],[206,11.42375,-16.42417,6.47,0.31],[207,11.67667,59.57444,6.39,1.09],[208,11.94208,74.8475,5.41,-0.08],[209,11.44,-47.55167,5.8,0.64],[210,11.54917,-22.52194,5.5,0.98],[211,11.6375,15.47556,5.38,1.65],[212,12.03792,72.675,5.87,1.01],[213,11.75625,11.97389,5.5,0.97],[214,11.80667,19.57889,6.13,0.27],[215,11.83458,24.26722,4.06,1.12],[216,11.84833,6.74083,5.99,0.94],[217,11.97833,20.92528,6.54,0.5],[218,11.93042,-18.06139,5.7,1.3],[219,12.275,57.81583,3.44,0.57],[220,12.00458,-21.7225,5.57,-0.06],[221,12.0725,7.3,5.93,1.1],[222,12.09583,5.28056,5.75,0.88],[223,12.20875,50.96833,4.89,-0.11],[224,12.17083,7.585,4.43,1.5],[225,12.24458,16.94056,5.07,0.51],[226,12.45333,41.07889,4.53,-0.15],[227,12.35667,-13.56139,5.59,1.3],[228,12.30792,-24.13639,5.9,0.95],[229,12.23625,-46.69778,6.27,0.9],[230,12.47,27.71083,7.0,0.36],[231,12.47167,27.71028,7.1,0.6],[232,12.38917,-23.36167,6.28,0.13],[233,12.68167,64.2475,5.39,0.49],[234,12.57625,45.00222,6.15,0.01],[235,12.53167,-10.64444,5.19,0.5],[236,12.1475,-74.92333,5.07,1.37],[237,12.81833,61.80583,6.07,1.88],[238,12.73917,51.50806,6.39,0.28],[239,12.51542,-43.39472,6.48,0.29],[240,13.72125,83.70722,5.62,0.09],[241,12.89042,51.57111,6.21,0.6],[242,12.67167,-50.98694,5.22,0.36],[243,12.82625,3.385,6.37,1.07],[244,13.26708,61.12417,4.82,0.53],[245,12.96708,-43.70917,6.9,0.35],[246,13.2225,38.54861,6.69,0.02],[247,13.16917,-24.00583,5.46,1.24],[248,13.25208,-1.14417,4.77,1.57],[249,13.3675,37.41806,6.06,1.14],[250,13.44833,52.68917,6.27,0.19],[251,13.30167,-24.77694,6.46,0.44],[252,13.10125,-69.50444,6.22,0.56],[253,13.75042,58.97278,4.83,1.21],[254,13.64667,19.18833,5.74,-0.01],[255,13.57333,-8.74083,6.16,0.92],[256,13.77167,48.67861,6.27,1.68],[257,13.40792,-62.87139,5.7,1.56],[258,13.74167,23.62833,5.47,1.0],[259,13.81125,24.55694,6.2,1.62],[260,14.05375,57.99667,6.21,1.37],[261,14.23167,68.77611,6.37,0.38],[262,13.99375,27.20944,6.09,0.14],[263,13.92667,-7.34722,5.85,1.52],[264,14.17708,60.71667,2.47,-0.15],[265,14.16583,59.18111,4.63,0.96],[266,14.19625,60.36278,5.55,-0.07],[267,14.00625,-11.26667,5.31,1.52],[268,13.98125,-27.77556,6.1,1.67],[269,14.18833,38.49944,3.87,0.13],[270,13.75125,-69.52694,5.45,1.09],[271,14.30167,23.4175,4.42,0.94],[272,14.41542,45.83972,6.12,1.02],[273,14.62958,66.35222,5.97,-0.02],[274,14.45917,28.99222,5.42,1.08],[275,14.55917,33.95083,5.98,1.0],[276,14.47708,13.69583,6.32,0.89],[277,14.57875,21.40444,6.37,0.09],[278,15.12917,70.98306,6.39,0.13],[279,14.68292,-11.38,5.61,0.97],[280,14.65167,-29.3575,4.31,-0.16],[281,14.59333,-60.69639,6.23,0.1],[282,15.01417,44.71111,6.84,0.6],[283,15.015,44.71333,6.04,-0.01],[284,14.95708,6.48306,6.11,1.67],[285,17.18625,86.25694,4.25,1.21],[286,23.46,89.01556,6.46,0.11],[287,15.57667,51.035,6.47,0.6],[288,15.32625,-38.91667,5.59,1.18],[289,15.75625,47.37611,6.45,0.25],[290,15.72625,41.345,5.98,0.16],[291,15.70458,31.80444,5.5,-0.05],[292,15.90417,61.075,5.92,0.49],[293,15.61,-31.55194,5.5,0.08],[294,15.73583,7.89,4.28,0.96],[295,15.5075,-57.0025,6.11,0.94],[296,15.76083,-4.83667,5.43,1.11],[297,16.08167,61.58028,5.84,0.54],[298,16.01,52.50222,5.99,1.47],[299,15.705,-46.3975,5.36,0.9],[300,15.82333,-29.52583,6.29,0.93],[301,15.95417,1.36667,6.04,0.27],[302,16.195,51.01,6.54,-0.08],[303,16.115,29.65861,6.19,0.43],[304,15.67875,-65.45611,6.21,1.64],[305,16.15167,39.99111,6.72,0.31],[306,19.05625,87.14528,6.25,1.12],[307,16.21917,5.65639,6.0,1.51],[308,16.2725,14.94611,5.68,0.41],[309,16.595,62.76167,6.54,0.19],[310,16.42042,21.47333,5.34,-0.03],[311,16.42375,21.46528,5.56,-0.07],[312,17.30125,80.01167,6.29,0.6],[313,16.455,4.90833,6.35,0.38],[314,16.46417,4.90944,7.25,0.49],[315,16.40333,-9.97917,6.12,1.01],[316,16.75083,56.935,6.43,1.18],[317,16.52125,-9.83944,5.58,0.01],[318,16.78958,53.49833,6.38,0.6],[319,16.64,12.95611,6.12,0.96],[320,16.53208,-23.9925,6.14,1.09],[321,17.06833,54.92028,5.17,0.69],[322,16.52083,-46.71861,3.31,0.89],[323,16.61042,-35.66083,6.61,0.14],[324,17.00375,43.94194,5.03,0.11],[325,16.80458,-23.99639,6.37,0.23],[326,17.13875,58.26361,5.79,-0.01],[327,17.00542,32.01222,6.25,0.4],[328,16.98833,20.73917,5.55,0.13],[329,16.9425,-9.78556,5.82,0.43],[330,17.0925,5.64972,5.52,0.34],[331,16.94958,-41.48694,5.21,0.16],[332,16.82792,-61.77528,5.37,0.88],[333,18.06958,79.67389,5.64,0.01],[334,17.1475,-10.18222,3.45,1.16],[335,17.37583,47.24194,4.25,-0.07],[336,17.66375,68.77861,5.29,-0.02],[337,17.43292,35.62056,2.06,1.58],[338,17.09625,-55.24583,3.92,-0.08],[339,17.455,19.65861,5.55,0.69],[340,17.57833,42.08139,5.65,0.6],[341,17.58083,25.45778,5.8,1.47],[342,17.85667,64.20278,5.55,-0.07],[343,17.77583,55.14972,4.33,0.17],[344,17.54792,15.67417,6.06,1.49],[345,17.9225,65.01889,5.57,-0.09],[346,17.55,-8.90611,6.4,1.04],[347,17.64,2.44556,5.95,1.51],[348,17.79292,37.72417,5.81,-0.1],[349,17.77833,31.42472,5.16,0.23],[350,17.53083,-57.69444,6.41,0.89],[351,17.86333,21.03472,4.66,1.03],[352,17.915,30.08972,4.51,1.09],[353,17.93125,-2.25111,5.94,1.4],[354,18.29125,61.70583,6.41,0.01],[355,18.14167,45.33778,6.11,1.64],[356,18.24792,30.06417,6.19,1.0],[357,19.12792,79.91,6.26,0.42],[358,18.0975,-30.80222,6.52,0.48],[359,18.18917,-37.85639,5.92,0.28],[360,18.43708,24.58361,4.65,1.04],[361,18.43292,7.57528,5.24,0.32],[362,18.43875,7.57833,6.3,0.49],[363,18.52083,28.52972,6.43,1.7],[364,18.53167,16.13361,5.98,-0.08],[365,19.05042,71.74389,7.83,0.78],[366,18.6,-7.92306,5.13,0.46],[367,18.67667,6.99528,6.03,1.08],[368,18.705,-0.97389,5.7,0.42],[369,19.10208,48.08222,6.61,-0.05],[370,18.79625,-45.53139,4.96,0.58],[371,19.07833,33.11472,6.02,1.15],[372,19.27125,44.90194,6.34,0.6],[373,19.15125,-2.50028,5.41,0.9],[374,19.35042,31.74472,6.73,0.6],[375,20.08125,77.57056,6.31,0.92],[376,19.5425,47.41972,6.25,1.35],[377,18.94208,-68.87611,4.86,0.47],[378,19.45,3.61444,5.16,0.07],[379,19.69583,37.38611,6.46,0.23],[380,19.265,-66.39806,6.24,0.05],[381,20.49625,76.23889,6.38,0.28],[382,20.02042,58.23167,4.98,0.68],[383,19.86667,27.26417,4.76,0.03],[384,20.27167,64.65833,6.34,0.09],[385,19.95125,-0.50889,5.87,0.64],[386,20.945,78.72583,6.07,0.39],[387,20.14375,-3.24694,6.23,1.02],[388,20.11583,-11.23889,6.15,1.11],[389,20.28083,28.73806,5.23,1.39],[390,20.585,45.52889,4.88,1.08],[391,20.84,58.14306,6.45,0.6],[392,20.65417,1.72639,6.2,1.52],[393,20.645,-0.44972,6.49,1.08],[394,20.62708,-19.08139,6.35,0.48],[395,20.91917,37.715,5.58,0.26],[396,20.90625,34.24583,6.29,0.83],[397,20.85375,20.46889,5.97,1.71],[398,21.44375,70.98,6.49,0.6],[399,21.48333,68.13,4.74,1.05],[400,20.87917,-30.94556,5.84,1.61],[401,21.01042,-8.0075,6.21,0.23],[402,21.00583,-8.18333,3.6,1.06],[403,21.45417,60.23528,2.68,0.13],[404,21.08583,-6.91472,5.91,0.41],[405,21.16583,-15.66028,6.14,0.91],[406,21.20292,-2.84861,6.15,0.96],[407,21.39875,23.51167,6.18,0.43],[408,21.17,-41.4925,5.42,1.04],[409,21.5775,43.45778,5.96,0.49],[410,21.53625,34.57972,6.31,0.47],[411,21.17458,-44.52833,6.26,1.14],[412,21.405,-14.59889,4.9,1.23],[413,21.56375,19.17222,5.38,0.39],[414,21.67375,19.24028,5.5,1.11],[415,21.77583,34.3775,6.27,0.46],[416,21.61375,-0.39861,6.41,1.24],[417,21.91417,45.40667,4.83,0.42],[418,21.86083,41.10056,6.46,0.27],[419,21.72292,3.53528,6.58,-0.04],[420,21.27208,-64.36944,5.93,1.56],[421,21.715,-13.05667,5.66,0.35],[422,21.94583,40.33556,6.6,-0.04],[423,21.74208,-32.54306,5.79,3.86],[424,37.95292,89.26417,2.02,0.6],[425,21.94417,-10.90167,6.13,1.32],[426,22.09542,7.96139,6.2,1.12],[427,22.8075,70.26472,5.81,0.47],[428,22.71792,66.09806,6.14,0.08],[429,22.09125,-43.31833,3.41,1.57],[430,22.52583,47.00722,5.27,1.0],[431,22.18042,-33.76361,6.58,0.3],[432,22.47042,18.35556,6.02,0.14],[433,22.40042,-21.62944,5.12,0.02],[434,22.54625,6.14389,4.84,1.37],[435,22.37667,-46.75639,6.31,1.66],[436,22.59542,-26.20778,5.93,1.34],[437,22.87083,15.34583,3.62,0.97],[438,23.03167,34.8,6.39,-0.12],[439,23.3575,58.3275,5.7,1.52],[440,22.81292,-49.07278,3.95,0.99],[441,22.93042,-30.28333,5.82,1.08],[442,23.48292,59.23194,4.71,1.0],[443,22.91292,-45.57556,6.17,0.06],[444,23.26458,-9.01472,6.59,-0.04],[445,23.23333,-36.86528,5.51,1.02],[446,23.56917,37.23722,5.88,-0.07],[447,23.15125,-49.72778,6.28,0.46],[448,23.42875,-7.02528,5.76,0.64],[449,24.34375,74.30083,6.58,0.03],[450,23.70458,18.46056,5.89,1.52],[451,23.6575,-15.67611,5.63,0.07],[452,23.96875,41.07639,6.38,1.11],[453,23.71125,-31.89222,6.12,1.11],[454,24.11333,48.72278,5.92,1.21],[455,23.94333,14.66139,6.22,-0.04],[456,24.62875,73.04,5.28,0.96],[457,23.97833,17.43361,5.8,0.6],[458,24.19917,41.40556,4.09,0.54],[459,23.99583,-15.40028,5.42,1.21],[460,23.81333,-58.13944,6.01,0.38],[461,24.53167,57.9775,5.56,1.38],[462,24.035,-29.9075,5.69,0.33],[463,24.27458,12.14167,5.57,0.35],[464,24.49833,48.62833,3.57,1.28],[465,24.63208,45.4,6.36,0.04],[466,24.40708,-9.40389,6.24,0.53],[467,23.41333,-78.50472,6.11,0.97],[468,24.18667,-58.27083,6.18,1.61],[469,24.8375,44.38611,4.98,0.89],[470,25.05458,53.86833,6.39,1.61],[471,24.61417,-36.52833,5.94,1.05],[472,24.42875,-57.23667,0.46,-0.16],[473,24.71583,-21.27528,5.58,0.34],[474,24.70833,-25.02194,6.7,-0.08],[475,24.92,16.40583,5.97,1.12],[476,25.16583,43.29778,5.61,0.21],[477,25.145,40.57694,4.94,-0.09],[478,25.58542,68.04306,5.59,-0.07],[479,24.70083,-53.43889,6.84,0.44],[480,25.73292,70.6225,5.18,-0.04],[481,25.5125,61.03833,6.71,-0.03],[482,25.57417,58.62778,6.37,-0.02],[483,25.44667,42.61361,4.95,0.62],[484,25.32667,25.74583,6.17,0.44],[485,25.41333,30.04722,5.99,1.01],[486,24.9475,-56.19806,5.87,0.9],[487,24.94917,-56.19472,5.76,0.87],[488,25.74333,61.42167,6.34,0.01],[489,25.35792,5.4875,4.44,1.36],[490,25.51458,35.24556,5.64,-0.07],[491,25.83208,60.55111,5.78,-0.02],[492,25.43625,-11.32472,5.75,0.44],[493,25.62417,20.26861,5.24,0.84],[494,25.36375,-38.13306,6.17,0.42],[495,25.81875,45.32222,6.34,1.01],[496,25.915,50.68861,4.07,-0.04],[497,25.53583,-32.32694,5.25,1.05],[498,25.5125,-36.8325,5.72,-0.01],[499,26.07458,57.53639,6.21,0.1],[500,25.68125,-3.69028,4.99,1.38],[501,25.42125,-50.03889,6.64,0.12],[502,26.19208,57.08917,6.25,0.04],[503,25.95833,32.19167,6.34,0.6],[504,26.11042,46.13972,6.35,0.6],[505,25.45,-60.78944,5.71,1.27],[506,25.62208,-53.74056,5.52,0.53],[507,25.97833,-4.76556,6.19,1.54],[508,26.2325,20.08306,6.27,0.75],[509,26.01708,-15.9375,3.5,0.72],[510,26.34833,9.15778,4.26,0.96],[511,26.93667,63.85222,5.63,0.81],[512,24.48167,-82.975,5.87,0.61],[513,26.49708,-5.73333,5.34,1.52],[514,26.41167,-25.0525,5.31,0.39],[515,26.64708,17.41306,6.55,0.25],[516,25.33875,-79.14833,6.33,0.94],[517,26.50417,-27.34917,6.39,0.36],[518,26.95,46.22972,6.32,0.43],[519,26.52417,-50.81639,5.49,1.6],[520,26.52625,-53.52194,5.04,0.04],[521,27.16208,37.95278,5.94,0.97],[522,27.04542,16.95556,5.84,-0.03],[523,27.17333,32.69028,5.79,0.55],[524,26.82,-41.76,6.18,1.54],[525,24.36667,-84.76972,5.69,0.94],[526,27.31542,47.89694,5.82,0.29],[527,27.10833,3.68556,5.91,0.97],[528,26.94917,-37.15972,6.32,1.02],[529,27.73792,51.93333,5.9,0.43],[530,27.53542,22.27528,5.86,0.74],[531,27.39625,-10.68639,4.67,0.33],[532,27.33125,-31.07278,6.34,1.22],[533,27.99708,55.1475,5.52,-0.18],[534,27.71667,11.04333,5.94,0.3],[535,27.45333,-38.40389,6.37,0.52],[536,28.03917,50.79278,5.79,-0.07],[537,27.58417,-47.81639,6.14,1.01],[538,28.21167,51.47472,6.26,0.6],[539,27.865,-10.335,3.73,1.14],[540,28.45208,55.59806,6.45,0.18],[541,27.72708,-50.20611,5.94,0.14],[542,28.59875,63.67,3.38,-0.15],[543,28.32208,40.72972,5.4,1.32],[544,28.27042,29.57889,3.41,0.49],[545,28.3825,19.29583,4.83,0.6],[546,28.3825,19.29361,4.75,-0.04],[547,28.21708,-16.92917,5.8,0.27],[548,29.0,68.68528,4.99,-0.1],[549,28.38875,3.1875,4.62,0.94],[550,26.94375,-80.17667,6.06,0.34],[551,28.72417,40.70194,6.24,1.25],[552,28.73958,37.12833,6.26,1.17],[553,28.66,20.80806,2.64,0.13],[554,28.34667,-38.59472,6.1,1.12],[555,28.41167,-46.3025,4.41,1.59],[556,28.97667,37.27778,5.89,1.63],[557,29.03875,37.25167,5.67,1.06],[558,28.59167,-42.49694,5.11,-0.06],[559,28.9625,23.57722,5.74,1.19],[560,28.97417,1.84972,6.01,0.56],[561,29.63875,61.69806,6.02,-0.04],[562,29.485,41.69444,6.78,-0.06],[563,29.33792,17.8175,5.1,0.92],[564,29.4325,27.80444,5.82,1.6],[565,29.1675,-22.52694,4.85,1.42],[566,28.98958,-51.60889,3.7,0.85],[567,29.90833,64.62139,5.26,0.01],[568,29.64,49.20417,5.69,1.01],[569,29.48208,23.59611,4.79,0.28],[570,28.73375,-67.64722,4.69,0.95],[571,28.94333,-60.86139,6.06,0.37],[572,30.73833,77.91639,6.04,1.14],[573,29.25042,-51.76611,6.1,0.48],[574,29.29167,-47.385,4.83,0.88],[575,30.48917,70.90694,4.54,0.16],[576,29.61125,-33.06667,6.35,1.02],[577,29.89875,21.05833,5.87,1.03],[578,29.85792,12.29472,6.09,0.19],[579,30.79375,73.85056,6.23,0.6],[580,30.85875,72.42139,3.98,-0.01],[581,31.28083,77.28139,5.38,0.31],[582,30.03833,3.09722,5.88,0.62],[583,29.94208,-20.82444,5.41,1.65],[584,29.47333,-65.42472,6.37,0.9],[585,30.00125,-21.07778,4.0,1.57],[586,30.71958,64.90139,6.0,0.03],[587,30.11208,-8.52361,5.51,1.52],[588,29.91167,-42.03056,5.57,1.06],[589,30.75125,64.39,5.58,0.38],[590,30.57542,54.4875,5.04,-0.08],[591,29.6925,-61.56972,2.86,0.28],[592,31.38,76.115,5.22,0.95],[593,28.96042,-78.34833,6.16,0.44],[594,30.31125,-30.00167,5.35,0.88],[595,30.51167,2.76361,5.23,0.6],[596,30.51167,2.76361,4.33,0.03],[597,32.35542,81.29583,6.05,0.11],[598,31.16708,65.10333,6.52,0.0],[599,30.74167,33.28389,5.5,0.03],[600,29.92125,-66.06639,6.1,1.17],[601,30.64667,13.47667,5.94,1.59],[602,30.42667,-44.71361,5.14,1.49],[603,30.975,42.32972,2.26,1.37],[654,34.21542,57.05528,6.48,0.28],[604,30.97792,42.33083,4.84,0.03],[605,30.91375,25.93556,5.63,0.54],[606,30.61708,-29.665,6.42,0.14],[607,30.79875,0.12833,5.43,0.15],[608,30.74417,-15.30583,5.86,0.98],[609,30.9275,18.25333,6.21,1.42],[610,30.95083,-0.34028,5.93,0.88],[611,30.91875,-4.10361,5.62,1.59],[612,31.1225,-29.29694,4.69,-0.17],[613,31.64125,22.64833,5.03,0.11],[614,31.55125,8.2475,6.31,1.65],[615,31.705,25.70472,6.15,-0.03],[616,31.62208,0.035,6.28,0.9],[617,31.79333,23.4625,2.0,1.15],[618,32.16875,58.42361,5.67,0.61],[619,32.14,44.45944,6.42,0.6],[620,32.12208,37.85917,4.82,0.12],[621,32.5325,53.84306,6.31,0.95],[622,32.38583,34.98722,3.0,0.14],[623,32.35542,25.93972,4.98,0.33],[624,32.34625,17.22444,6.43,0.6],[625,32.19042,-17.77944,6.1,1.61],[626,33.33833,74.02778,6.29,0.91],[627,32.87083,57.64583,6.36,0.33],[628,32.72,39.03944,5.63,-0.02],[629,32.72375,39.04306,6.1,-0.07],[630,32.395,-24.34583,6.48,0.29],[631,32.65667,19.50028,5.7,1.65],[632,32.28875,-43.51667,5.85,1.19],[633,32.8,25.93694,6.02,1.36],[634,32.85417,31.52639,6.23,0.11],[635,32.83792,8.56972,5.63,0.56],[636,32.52042,-43.81556,6.32,0.9],[637,32.60667,-50.82444,6.12,0.82],[638,32.8425,-10.05222,6.01,0.39],[639,32.89917,-1.82528,5.93,0.97],[640,33.62125,66.52444,6.07,0.63],[641,33.42292,58.56111,6.44,0.6],[642,33.09292,30.30306,4.94,0.78],[643,33.30542,44.23167,4.83,1.48],[644,33.15625,24.16778,5.96,1.37],[645,33.40125,51.06583,5.31,0.93],[646,33.20042,21.21083,5.27,0.43],[647,33.51083,47.48417,6.06,0.4],[648,33.26375,15.27972,5.71,1.55],[649,33.25,8.84667,4.37,0.89],[650,33.19792,-2.39361,5.54,0.57],[651,33.25375,-21.00028,5.86,1.01],[652,33.22708,-30.72389,5.28,-0.02],[653,33.99125,47.81139,6.33,0.6],[655,33.98417,33.35889,5.28,-0.01],[656,33.94167,25.78306,5.79,0.44],[657,33.92833,25.04306,5.58,0.49],[658,33.86792,-9.46556,6.55,-0.02],[659,33.63333,-41.16667,5.91,0.97],[660,34.26333,34.22417,4.87,0.61],[661,34.49958,57.89972,5.75,1.17],[662,34.51875,57.51667,5.98,1.05],[663,34.38917,44.30694,6.7,0.6],[664,34.32875,33.84722,4.01,0.02],[665,34.29333,23.76778,6.55,1.02],[666,34.24583,-6.42222,5.51,0.96],[667,33.56125,-67.84167,5.55,1.55],[668,35.05375,64.33722,6.6,-0.03],[669,34.53125,19.90111,5.62,0.01],[670,34.82,47.38,5.3,-0.01],[671,34.79542,46.4725,6.21,0.6],[672,34.50583,1.75778,5.58,0.6],[673,34.84458,48.95528,6.37,0.6],[674,34.1275,-51.51222,3.56,-0.12],[675,34.7375,28.6425,5.03,0.04],[676,34.74167,23.16778,6.46,0.34],[677,34.90542,39.835,6.63,-0.09],[678,33.86875,-67.74639,5.69,1.3],[679,35.1725,47.31083,6.11,-0.08],[680,35.01833,30.18833,6.47,0.6],[681,34.83625,-2.9775,3.04,1.42],[682,35.2425,50.15139,5.59,-0.13],[683,34.74375,-25.94556,6.34,0.73],[684,34.92,-4.34556,6.5,0.08],[685,35.58917,55.84556,5.17,0.37],[686,34.85292,-41.84833,6.37,1.16],[687,35.70958,41.39639,5.82,0.27],[688,34.97625,-55.94472,5.81,1.55],[689,35.48583,0.39583,5.28,1.65],[690,35.96583,55.36444,6.28,0.86],[691,35.55167,-0.885,5.42,0.31],[692,35.50583,-10.77778,5.46,0.35],[693,35.52083,-17.66222,5.87,1.23],[694,36.10375,50.00667,5.19,0.98],[695,35.63583,-23.81639,5.2,0.6],[696,36.31667,56.61,6.25,0.31],[697,35.74083,-18.35444,6.22,0.94],[698,35.54917,-43.2,6.31,1.0],[699,36.40583,50.27861,4.71,1.53],[700,35.77708,-37.57639,6.53,1.61],[701,35.7275,-51.09222,5.92,0.22],[702,36.20417,10.61056,5.47,-0.1],[703,36.08375,-25.8475,6.44,1.32],[704,36.24333,-2.78,6.33,0.0],[705,35.43708,-68.65944,4.09,0.03],[706,36.14083,-40.84056,6.18,0.66],[707,37.26667,67.4025,4.52,0.12],[708,36.4875,-12.29056,4.89,-0.03],[709,36.96583,50.56972,6.12,0.41],[710,36.50125,-15.34111,5.83,0.15],[711,36.77917,27.01333,6.18,1.43],[712,36.86542,31.80139,5.54,1.12],[713,36.64667,-20.04278,5.88,1.26],[714,36.22458,-60.31194,5.35,0.39],[715,35.71792,-73.64583,5.01,1.09],[716,37.35417,55.53639,6.51,0.09],[717,37.04167,29.66944,5.29,0.6],[718,37.03958,8.46,4.28,-0.06],[719,37.0,1.96083,6.45,0.97],[720,37.20208,29.93194,5.89,0.58],[721,36.74625,-47.70389,4.25,-0.14],[722,36.35958,-66.49472,6.41,1.54],[723,37.30667,23.46889,6.19,0.15],[724,37.00708,-33.81111,5.14,0.1],[725,37.39708,9.56583,6.07,1.02],[726,37.56917,33.83389,6.25,1.07],[727,37.1475,-31.1025,6.11,1.11],[728,37.635,25.235,5.92,0.41],[729,37.66,19.85528,6.15,0.25],[730,37.48083,-22.68278,6.77,0.19],[731,37.72667,17.70389,6.23,0.9],[732,37.68833,0.25528,6.0,0.17],[733,37.5575,-25.18639,6.51,0.29],[734,37.01833,-64.29972,6.37,-0.04],[735,37.63667,-22.54556,6.1,1.61],[736,38.02583,36.14722,5.15,1.47],[737,37.87542,2.26722,5.25,1.27],[738,38.21875,34.5425,5.83,1.07],[739,38.03917,-1.035,5.35,1.02],[740,38.02167,-15.24472,4.75,0.45],[741,38.22542,15.03472,6.04,0.54],[742,38.06167,-36.4275,6.3,1.02],[743,39.50833,72.81833,5.16,0.88],[744,38.27917,-34.65,5.9,1.06],[745,38.4175,-20.00194,6.21,1.1],[746,38.86625,39.66444,6.36,-0.1],[747,39.40042,65.74556,5.78,1.56],[748,38.91167,37.31222,5.71,1.39],[749,38.46125,-28.2325,4.9,-0.05],[750,38.945,34.6875,5.35,1.66],[751,38.76708,7.47139,6.18,1.06],[752,38.67792,-7.85944,5.75,1.4],[753,39.02042,6.88694,5.82,0.98],[754,38.96875,5.59333,4.86,0.87],[755,38.4775,-51.09361,6.24,0.52],[756,39.23833,38.73278,5.9,0.48],[757,39.17875,31.6075,6.1,1.05],[758,39.26042,34.26389,5.3,1.55],[759,39.0,-7.83167,5.53,1.59],[760,39.33667,39.89583,6.54,-0.12],[761,39.27667,32.89194,6.25,0.48],[762,38.39,-62.58694,6.77,-0.06],[763,39.15792,12.4475,5.68,0.49],[764,39.24042,24.64833,7.09,0.5],[765,39.25208,24.6475,6.5,0.41],[766,39.14625,7.72972,5.81,1.04],[767,39.03875,-30.04472,5.75,1.02],[768,39.57417,37.72667,6.18,0.47],[769,39.61583,38.08944,6.3,-0.03],[770,39.50333,7.69528,6.39,0.44],[771,39.42417,-3.39611,5.65,1.02],[772,39.24417,-34.57833,5.79,0.66],[773,39.70417,21.96139,5.43,0.16],[774,41.94833,81.44833,5.78,1.3],[775,39.65375,3.44306,6.21,1.0],[776,37.91875,-79.10944,5.28,0.98],[777,39.57792,-30.19417,5.83,0.48],[778,39.35167,-52.54306,5.31,0.27],[779,39.87083,0.32861,4.07,-0.22],[780,39.60333,-37.99056,6.49,0.52],[781,39.89083,-11.87222,4.84,0.45],[782,40.17125,27.06083,5.3,0.09],[783,40.06542,6.11194,6.25,0.4],[784,40.05125,-9.45306,5.78,0.52],[785,40.76167,55.10583,5.77,-0.13],[786,40.01042,-30.63389,6.52,1.04],[787,40.74875,53.52611,5.84,1.12],[788,40.56208,40.19389,4.91,0.59],[789,39.95,-42.89167,4.75,0.06],[790,40.30792,-0.69583,5.71,0.52],[791,41.20708,67.82472,5.95,0.1],[792,40.75792,48.26556,6.48,0.6],[793,40.59167,20.01167,5.69,-0.02],[794,40.16667,-39.85556,4.11,1.02],[795,40.45125,-3.21333,6.05,1.17],[796,40.39167,-14.54944,5.98,0.43],[797,40.62042,10.74167,6.3,0.06],[798,39.88208,-64.28194,6.55,-0.08],[799,41.05,49.22833,4.12,0.49],[800,41.02167,44.29694,5.43,0.9],[801,40.86292,27.70722,4.66,-0.13],[802,40.165,-54.55,5.21,0.4],[803,40.96333,25.63806,6.35,0.08],[804,40.825,3.23583,3.47,0.09],[805,40.5275,-38.38389,6.01,0.92],[806,39.8975,-68.26694,4.11,-0.06],[807,40.53542,-46.52444,6.1,0.88],[808,41.07958,17.76389,6.46,1.07],[809,41.13708,15.31167,5.77,-0.01],[810,40.63958,-50.80028,5.41,0.56],[811,41.03083,-13.85861,4.25,-0.14],[812,41.24,12.44583,5.18,0.24],[813,41.23542,10.11417,4.27,0.31],[814,40.83458,-40.5275,6.36,-0.02],[815,42.23125,69.63417,6.18,0.18],[816,41.33708,4.71167,6.03,0.31],[817,41.08542,-32.525,6.22,0.04],[818,41.27583,-18.5725,4.47,0.48],[819,41.74292,35.98361,6.25,0.93],[820,41.76458,35.555,6.3,0.6],[821,41.04458,-52.57056,6.15,0.09],[822,41.31875,-46.28722,6.85,1.36],[823,40.86083,-66.71444,6.26,0.53],[824,41.97708,29.24722,4.51,1.11],[825,42.37833,57.08417,6.25,0.89],[826,41.68833,-21.63972,6.49,0.38],[827,41.79667,-22.48556,6.47,0.39],[828,42.13375,18.28361,5.82,1.2],[829,42.99458,68.88861,5.8,0.64],[830,42.19125,25.18806,5.86,-0.07],[831,42.36292,37.32611,6.45,0.43],[832,41.98333,-12.46056,6.9,1.56],[833,41.36458,-63.70444,5.74,0.93],[834,42.67417,55.89556,3.76,1.68],[835,41.89042,-35.55083,6.51,0.96],[836,42.32292,17.46417,5.22,-0.06],[837,41.38583,-67.61667,4.84,0.06],[838,42.49583,27.26056,3.63,-0.1],[839,42.93958,58.31472,6.45,0.1],[840,42.64625,38.31861,4.23,0.34],[841,42.2725,-32.40583,4.46,0.99],[842,42.92375,46.84194,5.88,0.89],[843,42.87833,35.05972,4.53,1.56],[844,42.4625,-24.56028,6.14,1.07],[845,42.47583,-27.94167,5.39,0.02],[846,43.21667,52.99778,6.36,0.07],[847,42.87333,15.08194,5.49,-0.09],[848,42.56167,-35.84361,5.92,0.9],[849,43.33833,48.56944,6.26,1.18],[850,42.75958,-21.00417,4.75,0.91],[851,42.66833,-35.67611,5.47,1.25],[852,42.25625,-62.80667,5.26,0.1],[853,42.69958,-39.93167,6.36,0.05],[854,43.56458,52.7625,3.95,0.74],[855,43.4275,38.3375,5.33,0.41],[856,43.29875,16.48333,6.31,0.6],[857,43.13375,-12.76944,6.04,0.87],[858,42.98042,-30.81444,6.4,0.48],[859,43.21042,-9.44111,6.32,0.19],[860,43.98708,61.52111,5.59,0.45],[861,44.10333,64.3325,6.24,2.03],[862,43.39708,-22.37639,5.95,1.04],[863,43.39333,-38.43694,5.92,0.44],[864,44.21083,51.26083,6.22,1.57],[865,44.13917,47.16389,6.02,1.34],[866,43.08,-62.90972,6.03,1.25],[867,43.95208,18.33167,5.91,1.47],[868,43.47042,-49.89028,4.0,2.11],[869,44.10875,18.02306,5.63,0.43],[870,44.0575,8.38167,5.97,0.48],[871,43.52708,-50.87139,6.21,1.58],[872,42.61875,-75.06694,4.75,1.33],[873,44.32208,31.93417,5.11,-0.01],[874,44.10708,-8.89806,3.89,1.11],[875,44.15583,-3.71222,5.17,0.08],[876,44.50958,38.615,6.04,1.41],[877,44.26917,4.50111,6.11,1.69],[878,44.52167,20.66861,5.8,0.41],[879,44.69042,39.66278,4.7,0.06],[880,43.58708,-64.43556,6.56,1.39],[881,46.5325,79.41861,5.49,1.57],[882,44.76542,35.18306,4.93,1.23],[883,44.34875,-23.86194,5.45,0.23],[884,44.30458,-29.85528,6.29,0.47],[885,44.95792,47.22083,5.47,0.89],[886,44.91625,41.03306,5.89,1.44],[887,44.80292,21.34028,4.63,0.04],[888,44.80292,21.34028,4.63,0.04],[889,44.52375,-23.60611,5.84,1.33],[890,45.2175,52.35167,5.28,-0.05],[891,45.2225,52.35222,6.74,0.0],[892,44.675,-2.7825,5.23,0.0],[893,44.38625,-38.19111,6.41,-0.03],[894,45.04917,38.13167,6.11,-0.06],[895,44.6975,-9.77639,6.14,0.22],[896,44.92875,8.9075,4.7,-0.12],[897,44.56542,-40.30472,3.24,0.14],[898,44.56792,-40.30444,4.35,0.08],[899,44.92167,-2.465,5.56,-0.08],[900,44.7775,-28.90694,6.14,1.04],[901,44.90042,-25.27417,5.71,0.4],[902,45.18375,10.87028,5.95,1.59],[903,44.90958,-32.50722,6.31,0.0],[904,45.2125,-2.87861,6.11,1.77],[905,45.47542,26.46222,5.9,0.14],[906,47.92833,81.47056,5.95,0.15],[907,45.29167,-7.66278,5.75,1.05],[908,45.46792,5.33611,6.25,1.05],[909,44.69917,-64.07139,4.99,0.13],[910,45.59375,4.35278,5.61,-0.1],[911,45.57,4.08972,2.53,1.64],[912,45.48375,-9.96139,5.83,1.11],[913,45.53875,-6.49472,6.19,0.6],[914,45.40708,-28.09167,5.89,0.79],[915,46.19917,53.50639,2.93,0.7],[916,45.87625,28.26972,6.36,0.6],[917,45.67625,-7.68528,5.32,0.94],[918,46.385,56.70583,4.76,1.02],[919,45.59792,-23.62444,4.09,0.16],[920,46.41625,56.06861,6.11,1.02],[921,46.29417,38.84028,3.39,1.65],[922,46.82917,64.05778,5.89,-0.03],[923,46.33667,40.5825,6.05,1.01],[924,46.16958,15.85611,6.49,0.6],[925,46.06833,-7.60083,5.26,0.2],[926,46.15875,1.86361,6.05,1.04],[927,46.36125,25.25528,6.8,-0.02],[928,46.36125,25.25528,7.0,0.6],[929,45.73292,-46.975,5.82,1.3],[930,47.01625,52.21333,6.31,-0.01],[931,46.59875,13.18722,5.62,1.08],[932,47.98458,74.39361,4.87,0.02],[933,46.9475,47.30833,6.41,0.12],[934,45.90333,-59.73778,5.11,0.34],[935,46.63958,-6.08861,5.27,1.6],[936,47.04208,40.95556,2.12,-0.05],[937,47.26667,49.61333,4.05,0.59],[938,46.85708,17.88,6.11,-0.12],[939,45.56417,-71.9025,5.53,-0.14],[940,47.08833,18.795,6.27,1.58],[941,47.37417,44.85722,3.8,0.98],[942,47.16125,8.47083,6.28,1.06],[943,46.96208,-27.83111,6.19,0.16],[944,47.40292,29.07694,5.72,0.12],[945,47.53667,27.82,6.42,0.01],[946,47.6125,26.89639,6.02,1.28],[947,47.8225,39.61167,4.63,1.11],[948,47.66208,11.8725,5.98,-0.06],[949,48.11,47.72583,6.33,1.43],[950,48.04,42.37611,6.15,-0.09],[951,47.9075,19.72667,4.35,1.03],[952,47.84125,13.04778,6.12,1.02],[953,47.6475,-23.73833,6.38,0.93],[954,48.05917,27.25694,5.79,-0.12],[955,47.82833,-3.81167,6.05,1.66],[956,48.35,48.17694,5.9,0.97],[957,47.82,-16.02528,6.26,1.2],[958,48.11,6.66083,5.56,1.08],[959,46.955,-69.26556,6.15,1.02],[960,47.61417,-48.73417,6.12,1.12],[961,50.08208,77.73472,5.45,0.19],[962,48.19333,-1.19611,5.06,0.57],[963,48.01792,-28.98694,3.87,0.52],[964,48.95,57.14083,5.79,0.73],[965,53.08375,84.91111,5.61,0.92],[966,48.73625,42.50389,6.07,1.07],[967,49.38167,65.65861,6.36,0.09],[968,48.1075,-44.41972,5.93,0.44],[969,49.05083,50.93778,5.03,1.15],[970,48.25625,-35.94389,6.27,-0.08],[971,48.83542,30.55667,5.52,0.01],[972,48.72542,21.04444,4.89,-0.01],[973,49.01958,45.34583,6.16,1.67],[974,48.40833,-29.80417,6.16,1.05],[975,48.94583,32.85639,6.31,0.37],[976,49.00792,34.68861,6.25,0.28],[977,48.13833,-57.32167,5.74,2.28],[978,49.14625,32.18361,6.06,0.99],[979,49.2975,40.48333,6.45,0.6],[980,48.75083,-26.10028,6.25,0.04],[981,46.88333,-78.98944,5.57,0.3],[982,49.4475,44.025,5.47,-0.06],[983,49.00375,-5.91861,6.17,-0.02],[984,48.95833,-8.81972,4.8,0.23],[985,49.9975,65.65222,4.84,-0.15],[986,49.44083,39.28333,5.96,0.07],[987,49.6575,50.22222,5.15,-0.06],[988,49.14875,-9.15444,6.14,0.4],[989,49.78167,50.095,5.03,-0.06],[990,49.04708,-30.8275,6.65,-0.07],[991,49.6825,34.22278,4.82,1.49],[992,49.59333,-0.93028,5.38,1.04],[993,49.51167,-28.79694,5.91,0.33],[994,49.59208,-22.51139,4.88,0.9],[995,49.9825,27.07111,5.9,0.86],[996,49.84042,3.37028,4.83,0.68],[997,49.67167,-18.55972,5.71,0.37],[998,49.36083,-47.75167,5.85,1.24],[999,50.085,29.04833,4.47,1.55],[1000,50.10667,25.66278,6.12,1.24],[1001,50.46917,49.07083,5.93,0.43],[1002,50.36042,43.32944,4.95,0.04],[1003,49.87917,-21.75778,3.69,1.62],[1004,49.895,-24.12306,5.61,1.66],[1005,50.30667,21.14694,5.28,-0.07],[1006,49.4425,-62.57528,5.54,0.64],[1007,50.27833,3.67556,5.69,0.97],[1008,49.98208,-43.06972,4.27,0.71],[1009,51.16875,64.58611,5.23,2.08],[1010,49.55375,-62.50639,5.24,0.6],[1011,50.805,49.21333,5.29,-0.07],[1012,50.54958,27.6075,5.52,1.1],[1013,50.18833,-26.60639,6.39,0.54],[1014,49.49583,-66.92694,6.05,0.13],[1015,50.68833,20.74194,5.09,1.24],[1016,50.35,-23.63528,5.52,0.88],[1017,51.08083,49.86111,1.79,0.48],[1018,50.56792,-25.58778,6.35,0.01],[1019,51.12375,33.53583,5.61,-0.03],[1020,51.45167,53.92167,6.51,0.29],[1021,50.38875,-47.77694,6.39,1.0],[1022,51.07708,24.72417,5.5,1.19],[1023,50.91292,4.88194,6.38,0.86],[1024,50.82417,-7.79417,6.2,0.7],[1025,48.99,-77.38833,5.52,0.44],[1026,51.28917,41.25722,6.51,0.6],[1027,51.10875,20.80361,6.08,-0.04],[1028,51.04208,12.62944,6.04,1.23],[1029,51.48917,49.12083,6.09,-0.07],[1030,51.20333,9.02889,3.6,0.89],[1031,50.93583,-32.70722,6.5,1.37],[1032,52.58125,71.86389,6.32,1.8],[1033,52.09833,60.25556,6.49,0.02],[1034,52.01292,49.06278,4.98,-0.09],[1035,52.26708,59.94028,4.21,0.41],[1036,51.76333,18.75639,6.57,0.15],[1037,52.21833,49.84833,5.58,-0.04],[1038,51.7925,9.73278,3.74,-0.09],[1039,51.82792,12.735,6.28,-0.02],[1040,52.47875,58.87861,4.54,0.56],[1041,52.08667,33.8075,5.61,0.02],[1042,51.4825,-35.92083,6.39,0.08],[1043,52.54708,59.36611,6.13,0.08],[1044,52.34208,49.50889,4.67,-0.09],[1045,51.59375,-27.3175,5.93,0.94],[1046,52.50083,55.45194,5.09,0.05],[1047,52.35917,46.93778,6.24,0.13],[1048,52.11083,22.80417,6.03,0.95],[1049,51.54875,-41.63694,6.32,0.06],[1050,52.00417,-11.28667,5.73,1.1],[1051,52.65417,48.10361,5.82,-0.04],[1052,52.64375,47.99528,4.36,1.35],[1053,51.01042,-69.62472,6.15,0.48],[1054,51.88917,-35.68139,5.71,1.29],[1055,53.80167,73.34694,6.57,0.03],[1056,52.8725,49.20972,6.29,0.09],[1058,52.04792,-35.85333,6.5,0.13],[1059,52.95458,49.40083,6.39,0.09],[1060,52.41292,-6.805,5.99,1.02],[1061,52.60208,11.33639,5.14,-0.03],[1062,52.4,-12.67472,5.59,0.17],[1063,53.03583,48.02361,5.47,-0.1],[1064,51.40083,-69.33639,5.96,0.42],[1065,52.83667,27.57194,5.96,0.6],[1066,52.71833,12.93667,4.11,1.12],[1067,52.68917,6.18861,5.94,0.96],[1068,53.38375,58.765,6.4,0.14],[1069,53.10958,46.05694,5.31,0.4],[1070,52.65458,-5.07528,4.73,-0.09],[1071,53.42167,57.86889,6.37,0.51],[1072,53.16292,44.85556,6.41,0.03],[1073,53.4125,54.97472,5.98,0.11],[1074,53.16667,35.46167,5.9,-0.06],[1075,52.47917,-42.63417,5.78,0.22],[1076,52.55667,-41.37,6.12,0.48],[1077,53.75333,60.04111,6.46,0.39],[1078,53.39625,39.89944,5.81,0.11],[1079,53.15,9.37361,5.77,-0.08],[1080,54.85333,75.73972,6.27,0.97],[1081,52.65417,-47.37528,5.99,0.11],[1082,52.97458,-25.61417,6.38,0.12],[1083,52.34458,-62.9375,4.72,0.4],[1084,53.2325,-9.45833,3.73,0.88],[1085,53.53458,17.83278,6.17,0.89],[1086,53.61083,24.46444,5.92,0.13],[1087,54.1225,48.19278,4.23,-0.06],[1088,53.44708,-21.63278,4.27,-0.11],[1089,53.705,6.41778,6.49,0.63],[1090,53.145,-50.37861,5.68,1.1],[1091,53.65583,-9.86861,6.25,0.02],[1092,52.715,-66.48972,5.83,-0.06],[1093,53.48667,-31.08028,6.2,0.48],[1094,54.58208,56.93278,6.3,-0.13],[1095,53.63958,-31.87472,6.4,1.4],[1096,53.215,-61.01694,6.41,1.04],[1097,54.50083,42.58306,6.42,-0.06],[1098,53.99042,-11.19361,5.57,0.91],[1099,54.19708,0.58778,5.71,0.92],[1100,54.0725,-17.46694,5.23,-0.13],[1101,54.21833,0.40167,4.28,0.58],[1102,54.44917,15.43083,6.39,0.17],[1103,54.75042,20.91583,6.5,0.15],[1104,53.60292,-65.76444,6.75,0.17],[1105,55.53875,63.21667,5.1,1.63],[1106,54.27375,-40.27472,4.58,1.04],[1107,62.50625,86.62611,5.86,0.37],[1108,54.62167,-7.39167,5.85,0.98],[1109,52.495,-78.35194,5.7,0.93],[1110,54.85708,16.53667,6.16,1.0],[1111,54.75458,-5.62611,5.96,0.92],[1112,55.67792,59.96944,5.76,1.71],[1113,55.28292,37.58,5.57,-0.07],[1114,54.69875,-27.94306,6.01,-0.02],[1115,54.96292,3.05694,5.57,0.94],[1116,54.90958,-3.39306,6.23,1.04],[1117,54.85583,-10.43722,6.19,1.03],[1118,55.19292,25.32944,6.11,0.06],[1119,54.99792,-1.12056,6.12,1.0],[1120,55.0475,-15.22667,6.33,0.88],[1121,55.15958,-5.21056,5.53,-0.15],[1122,55.73125,47.7875,3.01,-0.13],[1123,55.59375,33.965,4.97,-0.01],[1124,56.50375,67.20167,5.8,0.35],[1125,55.3075,-11.80306,6.49,0.36],[1126,55.57875,19.70028,5.69,-0.01],[1127,56.02667,48.52361,6.06,1.55],[1128,55.34333,-19.58472,6.59,0.09],[1129,56.50958,63.345,4.8,0.8],[1130,56.17042,46.09972,6.11,0.28],[1131,56.07958,32.28833,3.83,0.05],[1132,55.94667,19.665,6.14,1.01],[1133,56.13083,36.46,5.59,0.06],[1134,55.56208,-31.93833,5.0,-0.16],[1135,56.29833,42.57861,3.77,0.42],[1136,55.81208,-9.76333,3.54,0.92],[1137,56.11708,20.92861,6.1,0.01],[1138,57.3075,70.87111,5.44,0.09],[1139,55.89083,-10.48556,5.6,0.22],[1140,56.20083,24.28944,5.46,-0.04],[1141,56.49708,45.68194,5.66,-0.07],[1142,56.21875,24.11333,3.7,-0.11],[1143,55.70875,-37.31361,4.59,1.2],[1144,56.29042,24.83917,5.64,-0.07],[1145,56.30208,24.46722,4.3,-0.11],[1146,56.12708,-1.16306,5.25,-0.1],[1147,56.88417,55.9225,6.1,-0.03],[1148,57.58958,71.33222,4.63,0.03],[1149,56.45667,24.36778,3.87,-0.07],[1150,56.23542,-0.29667,5.55,1.42],[1151,56.47667,24.55472,5.76,-0.04],[1152,56.51208,24.52806,6.43,-0.02],[1153,56.41833,6.05,5.35,-0.12],[1154,54.125,-78.32306,6.29,1.15],[1155,57.38,65.52611,4.47,1.88],[1156,56.58167,23.94833,4.18,-0.06],[1157,56.02583,-40.66028,6.45,1.06],[1158,57.40292,63.29722,5.85,0.18],[1159,56.53958,6.80306,5.91,0.99],[1160,57.07625,50.73667,6.14,0.06],[1161,57.33167,57.11806,6.46,0.06],[1162,56.53542,-12.10167,4.42,1.63],[1163,56.96917,33.6,6.57,0.08],[1164,56.95375,32.195,6.25,0.47],[1165,56.87125,24.105,2.87,-0.09],[1166,57.92417,68.5075,6.32,-0.08],[1167,56.21083,-48.06139,6.49,1.02],[1168,56.14083,-54.27389,6.3,1.04],[1169,56.31583,-47.35972,5.73,0.96],[1170,57.28417,43.96306,6.02,0.29],[1171,56.61417,-29.33806,5.9,0.12],[1172,57.08708,23.42111,5.45,-0.07],[1173,56.71208,-23.24972,4.23,0.42],[1174,57.06792,11.14333,5.07,-0.13],[1175,56.05,-64.80694,3.85,1.13],[1176,57.51875,44.96778,5.66,0.76],[1177,57.38583,33.09139,5.11,0.07],[1178,57.29042,24.05333,3.63,-0.09],[1179,56.83375,-29.90194,6.55,0.49],[1180,57.29667,24.13667,5.09,-0.08],[1181,56.915,-23.87472,5.24,0.07],[1182,57.16208,0.22778,5.91,1.23],[1183,57.43125,23.71167,6.17,-0.05],[1184,56.98333,-30.16778,5.54,0.98],[1185,57.47917,22.24444,6.07,-0.01],[1186,56.95667,-36.10583,6.21,-0.1],[1187,57.14917,-20.90306,5.81,1.64],[1188,57.57875,25.57944,5.26,0.21],[1189,57.1475,-37.62222,5.4,0.01],[1190,57.14958,-37.62056,4.73,-0.03],[1191,57.97375,34.35917,5.77,0.0],[1192,58.43042,57.975,5.8,0.18],[1193,57.9025,22.03167,6.83,0.6],[1194,57.81583,13.04583,6.3,-0.07],[1195,57.36375,-36.20028,4.17,0.95],[1196,59.12583,71.82167,6.34,0.3],[1197,58.01792,31.16833,6.25,0.2],[1198,58.41125,48.65056,5.76,1.05],[1199,58.00083,6.53472,5.67,0.06],[1200,57.65667,-36.42528,6.86,-0.04],[1201,58.29167,17.32694,5.97,0.34],[1202,58.17333,-5.36139,5.48,-0.1],[1203,58.53292,31.88361,2.85,0.12],[1204,59.35625,63.07222,5.03,-0.09],[1205,59.28458,61.10889,5.0,1.45],[1206,58.30417,-18.43444,6.22,0.88],[1207,58.9925,47.87139,5.37,-0.08],[1208,56.80958,-74.23889,3.24,1.62],[1209,58.84583,31.04583,6.1,0.29],[1210,59.15208,50.69528,5.28,0.41],[1211,58.5725,-2.95278,6.14,0.09],[1212,58.57292,-2.95472,4.79,0.94],[1213,58.4275,-24.6125,4.65,-0.13],[1214,58.41208,-34.73222,5.11,-0.13],[1215,59.11958,35.08111,5.49,-0.03],[1216,58.38875,-46.89361,5.93,1.24],[1217,58.81792,-12.09917,6.0,0.32],[1218,59.21708,22.47806,5.63,0.3],[1219,58.59667,-40.35722,5.71,0.6],[1220,59.46333,40.01028,2.89,-0.18],[1221,59.26583,23.17556,6.06,0.02],[1222,59.36,24.46194,6.16,1.37],[1223,59.51292,34.81444,6.53,0.23],[1224,59.25708,6.04,6.09,0.06],[1225,59.15792,-9.75083,6.19,0.28],[1226,59.62125,38.84028,6.3,1.08],[1227,58.64167,-52.69056,6.46,0.16],[1228,59.74125,35.79111,4.04,0.01],[1229,59.91667,38.82056,6.38,0.1],[1230,62.51167,80.69861,5.1,0.56],[1231,59.5075,-13.50861,2.95,1.59],[1232,59.71792,-5.47,5.83,1.0],[1233,59.91958,10.33083,6.37,0.42],[1234,60.31125,36.99,6.41,-0.01],[1235,59.87542,-12.57417,5.6,1.48],[1236,59.01667,-63.46361,6.14,1.1],[1237,60.15375,17.29667,6.32,0.06],[1238,60.20333,18.19389,5.89,0.32],[1239,60.17,12.49028,3.47,-0.12],[1240,59.98125,-24.01639,4.66,-0.13],[1241,61.51292,68.68,5.87,1.54],[1242,61.11333,59.15556,5.06,0.5],[1243,60.44208,9.99778,5.67,0.02],[1244,60.38333,-1.54972,5.28,-0.15],[1245,59.67875,-57.1025,6.05,0.44],[1246,60.16958,-30.49083,5.93,0.04],[1247,59.68625,-61.40028,4.56,1.62],[1248,61.6625,65.52083,6.17,0.14],[1249,60.65292,-0.26889,5.38,0.5],[1250,60.06542,-51.56472,6.51,1.64],[1251,60.78917,5.98917,3.91,0.03],[1252,61.09042,24.10583,5.47,0.86],[1253,60.93583,5.43556,5.33,-0.08],[1254,60.98583,8.19722,5.46,0.37],[1255,61.6525,54.00861,6.31,0.99],[1256,61.17375,22.08194,4.36,1.07],[1257,61.04125,2.82694,5.36,0.5],[1258,60.85292,-20.14417,6.46,-0.18],[1259,60.90333,-20.15833,7.01,1.22],[1260,61.96292,62.33,6.99,0.42],[1261,61.64583,50.35139,4.29,0.02],[1262,61.33417,22.00889,5.9,0.62],[1263,61.03625,-16.58889,6.39,1.26],[1264,60.22417,-62.15944,4.51,1.65],[1265,61.09458,-12.7925,5.61,1.08],[1266,60.32583,-61.07889,4.97,1.42],[1267,61.17083,-20.38167,6.13,1.16],[1268,61.65167,27.6,5.2,-0.13],[1269,61.75208,29.00139,5.23,0.34],[1270,62.365,59.90806,6.28,1.14],[1271,55.63375,-85.26222,6.41,-0.01],[1272,61.48542,-8.85611,6.26,0.06],[1273,62.16542,47.7125,4.04,-0.03],[1274,61.44458,-20.51222,6.34,0.92],[1275,61.40583,-27.65167,5.59,0.31],[1276,62.34333,54.82889,6.18,0.6],[1277,62.06375,37.72778,6.09,0.95],[1278,62.1525,38.03972,5.51,0.46],[1279,61.925,15.16278,6.01,0.4],[1280,61.9975,17.33972,5.89,1.5],[1281,63.43708,72.12639,6.03,1.01],[1282,63.215,68.50167,6.32,1.18],[1283,62.29167,19.60917,5.5,1.07],[1284,62.25667,13.39833,5.95,0.05],[1285,61.85458,-42.91694,6.59,0.93],[1286,62.74583,33.58667,5.72,1.44],[1287,62.70792,26.48083,5.41,0.34],[1288,62.32417,-16.38583,5.37,-0.15],[1289,67.05417,83.80778,5.57,-0.13],[1290,62.59375,-6.92389,5.44,0.94],[1291,62.14125,-45.86472,6.59,0.38],[1292,62.83458,5.52306,5.72,0.36],[1293,62.69875,-8.81972,5.7,1.06],[1294,61.84,-64.2225,6.38,0.64],[1295,63.13083,17.2775,6.09,1.08],[1296,63.7575,57.46028,6.08,0.61],[1297,63.21333,22.41361,6.12,0.19],[1298,62.96625,-6.8375,4.04,0.33],[1299,62.69083,-35.27389,6.44,1.07],[1300,62.90083,-20.35611,5.79,0.18],[1301,63.49833,37.96694,6.45,0.6],[1302,62.71083,-41.99361,4.93,0.33],[1303,63.72458,48.40944,4.14,0.95],[1304,67.50042,83.34056,5.46,0.87],[1305,64.22333,61.85028,5.7,-0.14],[1306,63.72208,40.48361,4.71,1.01],[1307,63.395,10.21222,6.23,0.05],[1308,63.38042,8.89056,6.51,0.16],[1309,63.38792,7.71611,5.29,0.36],[1310,63.45792,12.75333,6.25,1.16],[1311,63.485,9.26361,4.84,0.8],[1312,63.40917,-1.14972,6.44,-0.13],[1313,64.28417,57.86056,5.71,1.09],[1314,64.17958,53.61194,5.19,0.05],[1315,63.65125,10.01111,5.22,-0.1],[1316,63.13167,-44.36833,6.71,1.48],[1317,66.76167,80.82417,5.43,1.17],[1318,63.59875,-10.25639,4.87,1.17],[1319,63.94292,15.40056,6.32,0.4],[1320,63.88375,8.89222,4.29,-0.06],[1321,63.8575,6.19972,6.93,0.7],[1322,63.87167,6.18667,6.31,0.59],[1323,63.39875,-40.35778,6.37,1.46],[1324,64.56083,50.29556,4.61,0.04],[1325,63.81792,-7.65278,4.43,0.82],[1326,63.50042,-42.29444,3.86,1.1],[1327,65.16792,65.14056,5.27,0.81],[1328,64.53417,42.14111,6.22,-0.08],[1329,64.315,20.57861,4.94,0.26],[1330,64.80542,50.04889,5.45,0.22],[1331,64.59667,21.57917,5.65,0.28],[1332,64.33,-6.47194,5.94,1.08],[1333,65.04792,50.92083,5.55,-0.01],[1334,64.60208,9.48667,6.54,0.16],[1335,65.44833,60.73556,5.39,1.49],[1336,63.60625,-62.47389,3.35,0.91],[1337,65.06,41.80806,5.92,0.94],[1338,64.00667,-51.48667,4.25,0.3],[1339,64.85875,21.14222,5.35,-0.08],[1340,63.70292,-62.19194,5.45,1.1],[1341,64.90292,21.77361,5.38,-0.14],[1342,65.46583,56.50667,5.88,0.6],[1343,65.1025,34.56667,4.93,0.94],[1344,65.04167,31.95333,6.16,1.71],[1345,64.56625,-20.71556,6.0,1.67],[1346,64.94833,15.6275,3.65,0.99],[1347,64.47375,-33.79833,3.56,-0.12],[1348,65.08875,27.35083,4.95,1.15],[1349,64.90625,10.12139,6.31,1.43],[1350,65.38833,46.49889,4.85,-0.03],[1351,64.99042,14.03528,5.59,0.28],[1352,65.74125,59.61639,6.19,0.6],[1353,64.65583,-22.97028,6.07,0.3],[1354,65.10458,18.7425,6.12,0.37],[1355,64.12042,-59.30194,4.44,1.08],[1356,65.15125,15.09528,5.26,0.22],[1357,64.08792,-60.94861,6.37,0.07],[1358,65.22,13.86417,6.17,0.46],[1359,64.7625,-33.905,6.37,0.13],[1360,65.17167,6.13083,5.77,0.92],[1361,65.20417,9.22556,6.53,0.15],[1362,65.16125,-6.24556,6.27,0.91],[1363,65.17833,-7.5925,5.85,-0.13],[1364,64.81917,-44.26806,5.34,1.08],[1365,64.66667,-52.86,6.09,0.49],[1366,65.36292,-0.09806,5.86,1.32],[1367,65.1625,-20.63972,5.38,-0.02],[1368,65.51458,14.07722,5.72,0.32],[1369,65.64542,25.62917,5.37,-0.05],[1370,65.595,20.82139,5.91,1.66],[1371,65.89958,42.42806,6.23,0.0],[1372,64.4175,-63.25556,5.87,-0.07],[1373,65.73375,17.5425,3.76,0.98],[1374,65.38042,-25.72833,6.01,0.35],[1375,65.885,20.98222,5.99,0.03],[1376,65.85417,16.77722,5.64,0.3],[1377,66.12167,34.13056,5.73,-0.06],[1378,65.99917,24.30111,6.36,0.17],[1379,66.15583,33.95972,5.76,0.4],[1380,66.02417,17.44389,4.8,0.15],[1381,65.96625,9.46083,5.12,0.07],[1382,66.75375,57.58528,6.32,0.04],[1383,65.92,-3.74556,5.17,0.08],[1384,65.77375,-24.89222,5.83,1.51],[1385,66.23792,19.04167,5.98,0.37],[1386,65.78208,-35.545,6.39,1.24],[1387,66.34208,22.29389,4.22,0.13],[1388,66.35417,22.19972,5.28,0.25],[1389,66.3725,17.92806,4.29,0.05],[1390,66.52625,31.43889,5.28,0.97],[1391,66.40542,15.94083,6.46,0.49],[1392,66.57708,22.81361,4.28,0.26],[1393,66.00917,-34.01694,3.96,1.49],[1394,66.58667,15.61833,4.49,0.25],[1395,65.47208,-63.38639,5.24,0.96],[1396,66.65167,14.71361,4.69,0.98],[1397,66.58792,8.59028,6.06,0.02],[1398,66.235,-34.75778,6.55,0.44],[1399,66.82292,22.99639,5.53,-0.1],[1400,66.75292,2.07944,6.23,1.09],[1401,68.37792,72.52861,5.94,0.3],[1402,66.87,11.2125,5.88,0.05],[1403,67.00333,21.62,5.72,0.27],[1404,66.32958,-44.16083,6.39,0.44],[1405,66.05083,-57.07139,6.29,0.66],[1406,67.21625,30.36139,6.4,0.52],[1407,67.11,16.35972,4.97,1.13],[1408,67.0975,14.74083,5.9,0.32],[1459,69.12167,23.34083,6.02,0.38],[1409,67.15417,19.18028,3.53,1.01],[1410,66.7375,-24.08139,6.11,0.14],[1411,67.14375,15.96222,3.84,0.95],[1412,67.16542,15.87083,3.4,0.18],[1413,67.015,1.85861,6.15,1.02],[1414,67.20917,13.0475,5.03,0.23],[1415,67.13375,1.38083,5.55,-0.1],[1416,66.27208,-61.23833,5.94,1.54],[1417,68.0075,53.91083,5.77,0.18],[1418,66.775,-46.9475,6.1,0.46],[1419,67.65958,32.45806,6.21,-0.04],[1420,67.42875,10.52167,6.79,0.09],[1421,67.1625,-19.45833,5.96,1.22],[1422,67.53583,15.63806,5.58,0.32],[1423,67.27875,-13.04833,5.6,-0.23],[1424,67.85,40.01028,6.26,0.04],[1425,67.51,10.2625,6.48,1.03],[1426,64.49625,-80.21389,5.69,0.84],[1427,67.64042,16.19389,4.78,0.17],[1428,67.66208,15.69194,5.48,0.26],[1429,67.03917,-41.96,6.44,1.64],[1430,67.65542,13.72444,5.4,0.26],[1431,67.54042,-13.59222,6.24,1.0],[1432,67.96583,15.85167,6.02,0.34],[1433,67.33333,-46.51528,6.16,1.06],[1434,68.35375,43.06389,6.09,0.38],[1435,66.94125,-62.52083,5.75,1.01],[1436,68.02,5.41,6.39,0.42],[1437,67.96958,-0.04389,4.91,1.32],[1438,67.85792,-13.645,6.21,0.12],[1439,67.66792,-35.65361,5.96,1.0],[1440,69.10083,64.26167,5.94,-0.03],[1441,68.15583,-3.20917,5.81,-0.14],[1442,68.38708,18.01667,6.25,0.07],[1443,67.70875,-44.95389,5.07,-0.19],[1444,68.46208,14.84444,4.65,0.25],[1445,68.65833,28.96111,5.88,-0.06],[1446,68.45042,9.41306,6.01,1.06],[1447,68.34167,-10.78583,6.06,1.38],[1448,68.53458,5.56861,5.68,0.05],[1449,68.47833,-6.73889,5.72,-0.13],[1450,68.55917,-6.83778,6.09,1.38],[1451,68.54833,-8.23139,5.11,1.7],[1452,68.54875,-8.97028,5.26,1.47],[1453,68.3775,-29.76667,4.51,0.98],[1454,69.1725,41.26472,4.25,1.22],[1455,68.92792,19.88167,6.36,0.6],[1456,65.24125,-81.58,5.79,0.35],[1457,68.98,16.50917,0.85,1.54],[1458,68.91375,10.16083,4.25,0.18],[1460,68.80833,-9.73667,6.37,0.11],[1461,68.75208,-19.92056,6.13,1.17],[1462,69.00667,-3.61194,6.33,-0.1],[1463,69.07958,-3.3525,3.93,-0.21],[1464,68.8875,-30.56222,3.82,0.98],[1465,68.49917,-55.045,3.27,-0.1],[1466,69.99208,53.47306,5.35,0.32],[1467,69.97792,53.07972,5.05,1.07],[1468,71.50125,76.61111,6.49,0.51],[1469,69.30708,0.99833,5.31,-0.12],[1470,69.62333,26.94,6.47,0.6],[1471,69.56583,20.68472,5.92,-0.05],[1472,69.53917,16.03333,5.79,0.31],[1473,69.53958,12.51083,4.27,0.12],[1474,69.40042,-2.47333,5.23,0.28],[1475,68.39167,-62.82361,5.79,1.04],[1476,69.21208,-30.71667,6.3,-0.1],[1477,69.84625,25.21833,6.22,0.16],[1478,69.78833,15.79972,5.07,0.15],[1479,69.81875,15.91806,4.69,0.15],[1480,69.77583,7.87083,5.39,0.26],[1481,69.545,-14.30389,3.87,1.09],[1482,70.35042,48.30083,5.67,-0.02],[1483,69.72333,-12.12306,5.01,0.07],[1484,70.01417,12.19778,5.46,-0.12],[1485,65.71208,-82.89917,6.76,0.2],[1486,70.82542,59.52083,6.5,0.6],[1487,69.83208,-14.35917,5.45,1.06],[1488,69.94667,-1.05278,6.1,0.94],[1489,70.45958,38.28028,5.99,0.57],[1490,70.3325,28.615,5.78,-0.03],[1491,72.20958,75.94111,6.06,0.28],[1492,69.19,-62.0775,5.4,1.58],[1493,70.84,49.97389,5.87,0.02],[1494,70.72625,43.365,5.29,0.0],[1495,70.02833,-24.4825,5.58,0.92],[1496,70.11042,-19.67167,4.32,1.61],[1497,70.56125,22.95694,4.28,-0.13],[1498,69.76792,-51.67278,6.44,1.32],[1499,70.8075,24.08889,6.13,0.54],[1500,71.05375,40.78694,6.08,0.06],[1501,70.95125,32.86528,6.45,0.6],[1502,70.14042,-41.86389,4.45,0.34],[1503,70.51458,-37.14444,5.05,0.37],[1504,70.07625,-58.94361,6.53,0.68],[1505,70.89417,-8.79361,6.82,0.39],[1506,70.89625,-8.79611,6.7,0.92],[1507,71.1075,11.14611,5.4,0.25],[1508,71.02208,-8.50361,5.9,-0.11],[1509,70.78875,-30.76556,5.68,1.41],[1510,72.65167,70.94167,6.37,-0.08],[1511,72.00125,56.75722,5.3,0.25],[1512,71.42708,23.62806,6.35,0.06],[1513,71.03333,-18.66667,5.53,0.02],[1514,71.685,40.31278,5.97,0.93],[1515,72.02917,55.6025,6.26,0.6],[1516,70.69333,-50.48139,5.31,0.98],[1517,71.57,18.735,6.01,1.21],[1518,70.93417,-41.06472,6.25,1.46],[1519,71.50708,11.70556,5.37,0.19],[1520,71.37542,-3.25472,4.02,-0.15],[1521,71.2675,-21.28361,5.72,1.47],[1522,71.60042,-2.95444,6.33,0.04],[1523,75.08625,81.19389,5.07,1.28],[1524,71.45667,-34.005,6.86,0.0],[1525,71.6075,-28.0875,6.19,0.19],[1526,71.48083,-39.35667,6.05,1.07],[1527,73.02167,63.50528,5.44,1.57],[1528,72.32917,32.58833,5.86,0.24],[1529,72.30333,31.43722,5.58,1.12],[1530,71.08792,-59.73278,5.27,0.2],[1531,69.59042,-77.65611,6.05,1.1],[1532,71.90125,-16.93444,5.51,0.63],[1533,72.4775,37.48833,4.88,1.44],[1534,72.18583,3.58833,6.03,1.19],[1535,72.78875,48.74083,5.66,0.99],[1536,72.15125,-5.67389,5.78,0.62],[1537,72.43375,15.90417,6.08,1.6],[1538,72.13542,-16.32944,5.77,0.55],[1539,71.95667,-30.02028,6.37,1.07],[1540,71.24125,-63.22972,6.46,1.08],[1541,70.76625,-70.93111,5.54,-0.12],[1542,73.5125,66.34278,4.29,0.03],[1543,72.46,6.96139,3.19,0.45],[1544,72.65292,8.90028,4.36,0.01],[1545,72.42583,-13.76972,6.26,0.44],[1546,73.29083,52.84083,6.41,0.6],[1547,72.84375,18.83972,5.1,0.21],[1548,72.14083,-43.98,6.72,0.95],[1549,72.54833,-16.21722,5.03,0.98],[1550,73.19917,42.58667,5.71,0.11],[1551,73.15833,36.70306,4.78,1.41],[1552,72.80167,5.605,3.69,-0.17],[1553,72.93083,9.975,6.11,0.08],[1554,73.19625,27.8975,5.97,0.37],[1555,73.76292,55.25917,5.52,0.04],[1556,73.13333,14.25056,4.74,1.84],[1557,72.5675,-41.32083,6.07,0.37],[1558,73.71375,44.06083,6.08,-0.02],[1559,72.8675,-34.90639,5.86,0.08],[1560,73.22375,-5.45278,4.39,0.25],[1561,74.02958,52.86944,5.75,0.11],[1562,73.345,2.50806,5.33,1.64],[1563,72.73,-53.46139,5.61,0.33],[1564,72.73458,-53.45972,6.42,0.6],[1565,73.4825,1.56944,6.61,0.04],[1566,73.74292,19.48528,6.37,0.29],[1567,73.56292,2.44056,3.72,-0.18],[1568,74.32167,53.75222,4.47,-0.02],[1569,73.69542,11.42611,5.19,0.12],[1570,73.72417,10.15083,4.65,0.09],[1571,73.69917,7.77917,5.33,1.22],[1572,75.58375,74.26917,6.06,1.57],[1573,74.08292,36.16889,6.07,0.41],[1574,73.71125,0.4675,5.99,-0.12],[1575,74.065,24.59222,6.37,0.33],[1576,73.95958,15.04,5.81,-0.08],[1577,74.24833,33.16611,2.69,1.53],[1578,73.99333,5.39917,6.5,0.02],[1579,73.77833,-16.74056,5.7,0.96],[1580,74.09292,13.51444,4.07,1.15],[1581,73.8275,-16.41778,5.72,0.88],[1582,74.10083,-5.17139,5.51,-0.13],[1583,73.87583,-25.72778,6.72,0.27],[1584,73.72833,-39.62861,6.1,1.42],[1585,74.34292,17.15361,5.48,1.31],[1586,74.45292,23.94861,5.79,1.11],[1587,76.05417,73.76389,6.66,0.6],[1588,74.94292,53.15556,6.08,1.46],[1589,76.16583,74.06694,5.96,0.6],[1590,74.53917,25.05028,5.81,0.0],[1591,74.32167,-1.06722,6.23,0.42],[1592,74.81417,37.89028,4.94,0.04],[1593,75.39958,61.07806,6.03,0.41],[1594,75.71,66.82278,6.19,0.48],[1595,74.43667,-14.23139,6.15,-0.21],[1596,74.545,-2.2125,6.35,0.1],[1597,73.72083,-58.54722,6.12,0.44],[1598,73.37708,-66.67556,6.41,1.63],[1599,75.07625,39.39472,5.95,0.41],[1600,74.7475,14.54278,6.09,0.04],[1601,74.63708,1.71417,4.47,1.4],[1602,75.09667,39.655,6.58,0.6],[1603,75.85458,60.44222,4.03,0.92],[1604,74.75542,-16.37583,5.66,0.43],[1605,75.49208,43.82333,2.99,0.54],[1606,73.27292,-72.4075,6.28,0.52],[1607,74.90208,-14.80583,7.71,5.74],[1608,74.96,-10.26333,5.38,0.8],[1609,75.13583,3.61528,7.03,0.6],[1610,75.14125,3.61611,6.66,-0.07],[1611,74.9825,-12.5375,4.79,0.26],[1612,75.61958,41.07583,3.75,1.22],[1613,75.16583,-2.06556,6.32,0.28],[1614,75.20417,-5.75333,6.22,1.06],[1615,75.8275,41.44167,6.14,0.16],[1616,82.95,85.93861,6.51,0.32],[1617,75.35958,-7.17389,4.81,-0.19],[1618,75.45958,0.72222,5.92,1.27],[1619,75.5,1.60889,6.24,-0.04],[1620,75.77375,21.59,4.64,0.16],[1621,75.35667,-20.05194,4.91,-0.05],[1622,76.53542,58.9725,5.08,-0.08],[1623,76.55083,59.02111,6.08,1.12],[1624,76.62375,61.17,6.04,1.37],[1625,75.68958,-4.20972,5.85,1.21],[1626,76.06042,30.49472,6.14,1.21],[1627,76.15375,32.32028,6.62,0.27],[1628,75.54083,-26.275,5.02,1.07],[1629,73.79667,-74.93694,5.47,1.52],[1630,76.59167,54.40583,7.24,0.6],[1631,75.39375,-39.71806,6.03,0.88],[1632,76.15792,27.69611,6.6,0.24],[1633,76.09,21.27806,6.19,1.32],[1634,75.68708,-22.795,5.75,1.2],[1635,75.595,-31.77139,5.94,1.17],[1636,77.40292,69.63944,6.41,1.11],[1637,76.66917,51.59778,5.0,0.33],[1638,76.14208,15.40417,4.68,-0.06],[1639,76.50375,35.93639,6.52,0.16],[1640,75.96667,-14.36944,6.41,-0.18],[1641,76.62875,41.23444,3.17,-0.18],[1642,76.38375,19.80639,6.44,0.21],[1643,78.09333,73.94667,5.43,-0.12],[1644,76.70667,43.17472,6.2,0.43],[1645,75.97208,-24.38778,5.61,0.1],[1646,76.22708,-3.03972,6.05,-0.11],[1647,77.43542,64.91944,6.41,0.4],[1648,76.34875,1.1775,6.17,3.45],[1649,75.7025,-49.15139,5.38,0.42],[1650,78.64833,76.47278,6.37,-0.02],[1651,75.975,-41.745,6.31,0.53],[1652,76.10167,-35.48333,4.55,1.2],[1653,76.10875,-35.70528,6.34,0.3],[1654,76.36542,-22.37111,3.19,1.46],[1655,76.3175,-26.1525,5.73,1.17],[1656,76.8625,18.645,5.0,0.65],[1657,76.69042,-4.655,5.12,-0.06],[1658,76.95167,20.41833,5.3,0.09],[1659,77.0275,24.26528,5.5,0.06],[1660,76.98125,21.70472,5.89,0.19],[1661,76.65292,-13.12194,6.05,-0.06],[1662,76.90958,9.47194,6.17,0.62],[1663,76.24167,-49.57778,5.03,1.49],[1664,76.97042,8.49833,5.34,0.33],[1665,76.85417,-12.49056,5.97,0.6],[1666,76.9625,-5.08639,2.79,0.13],[1667,76.2525,-54.4075,6.27,1.54],[1668,77.67875,46.96222,5.68,0.42],[1669,77.57875,37.30194,6.02,0.72],[1670,77.43792,28.03056,6.01,0.27],[1671,77.08417,-8.665,5.78,-0.06],[1672,77.33167,9.82944,5.43,0.24],[1673,77.18167,-4.45611,5.12,0.44],[1674,76.3775,-57.47278,4.72,0.52],[1675,78.26333,61.85,6.17,0.02],[1676,77.425,15.59722,4.82,0.32],[1677,75.67917,-71.31444,5.31,1.0],[1678,78.38042,62.69139,6.5,0.21],[1679,77.28667,-8.75417,4.27,-0.19],[1680,77.06167,-35.71833,6.52,1.08],[1681,77.51333,-0.56528,6.1,1.1],[1682,75.055,-78.30028,6.29,1.51],[1683,79.55542,73.26806,5.74,0.6],[1684,77.92333,16.04556,5.18,1.49],[1685,77.74167,-2.25389,6.25,0.98],[1686,80.63958,79.23111,5.05,0.47],[1687,77.83,-2.49083,5.9,0.46],[1688,78.79708,59.40556,6.15,1.18],[1689,78.35708,38.48444,4.86,0.18],[1690,77.9225,0.51472,6.67,-0.07],[1691,77.93875,1.03694,5.89,0.66],[1692,78.68458,53.21389,6.2,0.07],[1693,77.845,-11.84917,5.68,1.46],[1694,77.68542,-25.90944,6.41,1.25],[1695,76.89167,-63.39972,5.2,1.65],[1696,78.07458,-11.86917,4.45,-0.1],[1697,78.20083,-6.05722,5.91,0.96],[1698,78.32292,2.86111,4.46,1.19],[1699,77.89958,-37.39528,6.57,1.62],[1700,76.53875,-73.03778,6.27,-0.01],[1701,78.38167,1.96806,6.09,0.42],[1702,78.23292,-16.20556,3.31,-0.11],[1703,78.44667,0.56028,6.32,1.46],[1704,78.38875,-8.14778,6.37,-0.13],[1705,78.30792,-12.94139,4.36,-0.1],[1706,78.85167,32.68778,5.02,0.23],[1707,79.32417,53.58611,6.5,1.66],[1708,79.1725,45.99806,0.08,0.8],[1709,78.68375,5.15611,5.5,1.37],[1710,78.49958,-14.60667,6.21,0.37],[1711,78.86542,22.28472,6.27,0.08],[1712,79.07583,34.31194,5.96,0.22],[1713,78.63458,-8.20167,0.12,-0.03],[1714,85.95292,85.66806,6.6,0.6],[1715,78.44375,-35.82556,6.98,1.47],[1716,74.71208,-82.47056,5.85,0.93],[1717,78.82667,-1.40917,6.15,0.39],[1718,79.01708,11.34139,5.56,-0.04],[1719,79.86583,58.11722,6.13,-0.03],[1720,80.09417,62.65361,5.61,1.75],[1721,78.62,-35.97722,5.76,1.01],[1722,79.56625,42.79222,5.48,1.65],[1723,78.85125,-26.94333,5.07,-0.1],[1724,79.17125,1.94722,6.42,-0.02],[1725,79.66833,40.465,6.18,1.37],[1726,79.54458,33.37167,4.54,1.27],[1727,78.47208,-52.03111,6.05,1.39],[1728,79.57875,33.76722,6.14,-0.06],[1729,79.78542,40.09917,4.71,0.63],[1730,78.94583,-34.92667,6.66,0.15],[1731,79.20042,-17.14167,6.56,-0.16],[1732,79.75,33.74833,5.41,-0.19],[1733,80.00958,44.42556,6.62,0.6],[1734,79.84833,33.98556,6.49,0.24],[1735,79.40167,-6.84444,3.6,-0.11],[1736,80.16375,46.96389,6.54,0.6],[1737,79.4175,-13.51972,5.5,0.93],[1738,80.06167,41.08611,5.52,0.11],[1739,79.81917,22.09639,4.94,0.93],[1740,80.00375,33.95806,5.03,0.27],[1741,79.81125,20.13472,6.08,1.01],[1742,78.91208,-52.18194,6.49,1.21],[1743,79.37125,-34.89528,4.83,1.0],[1744,78.43917,-67.18528,4.83,1.28],[1745,82.35708,77.9775,6.56,0.14],[1746,79.79667,2.59583,5.34,0.41],[1747,79.71,-18.13,5.96,0.58],[1748,79.89667,-1.41222,6.34,-0.11],[1749,80.45167,41.80444,5.23,-0.15],[1750,80.24708,27.95722,6.33,0.04],[1751,80.86583,57.54444,5.28,-0.03],[1752,80.30292,29.57,5.76,0.06],[1753,79.82292,-18.52,6.36,-0.16],[1754,79.82625,-18.50972,6.54,-0.11],[1755,80.23583,19.81417,6.18,1.23],[1756,79.89375,-13.17667,4.29,-0.26],[1757,79.99625,-12.31556,5.3,-0.12],[1758,79.84875,-27.36889,5.99,-0.04],[1759,80.11,-5.36667,6.39,-0.03],[1760,80.70958,41.02944,5.54,0.12],[1761,80.33042,4.01194,6.57,-0.09],[1762,80.11208,-21.23944,4.71,-0.05],[1763,80.43167,8.42861,5.8,-0.13],[1764,80.3825,-0.41639,5.68,-0.12],[1765,80.44042,-0.3825,4.73,-0.17],[1766,80.08583,-34.69889,6.34,0.33],[1767,79.84208,-50.60611,5.45,0.51],[1768,80.84542,28.93667,6.46,-0.04],[1769,80.4625,-13.75611,6.56,-0.09],[1770,80.70833,3.54444,5.0,-0.15],[1771,80.4425,-24.77306,5.06,0.67],[1772,80.32042,-34.34528,6.09,-0.19],[1773,81.16333,37.38556,4.99,1.42],[1774,80.90708,16.69944,6.08,0.14],[1775,81.15958,31.22389,6.28,1.24],[1776,81.16042,31.14306,5.94,0.04],[1777,80.87958,5.3225,6.35,0.12],[1778,80.82708,-8.41583,5.9,-0.03],[1779,81.30417,34.85528,6.55,1.11],[1780,81.10583,17.38333,4.99,0.53],[1781,80.92625,-0.15972,5.7,-0.21],[1782,80.96417,-0.86722,6.11,0.5],[1783,80.87583,-13.92722,5.25,-0.21],[1784,80.98667,-7.80806,4.14,0.96],[1785,80.8,-26.70583,6.49,0.5],[1786,81.15083,2.35278,6.32,-0.15],[1787,81.12042,-0.89139,5.08,0.96],[1788,81.11917,-2.39694,3.36,-0.17],[1789,81.18667,1.84639,4.95,-0.2],[1790,81.28292,6.34972,1.64,-0.22],[1791,81.57292,28.6075,1.65,-0.13],[1792,81.11833,-16.97611,5.65,0.0],[1793,80.85,-39.67861,5.71,1.62],[1794,81.72625,35.45722,6.15,1.45],[1795,81.70375,34.39167,5.94,0.14],[1796,81.71375,33.26278,6.15,1.15],[1797,80.9125,-37.33667,6.82,1.04],[1798,81.52375,16.70028,6.25,-0.08],[1799,81.25667,-10.32917,5.61,1.56],[1800,81.38,-0.54417,6.57,-0.05],[1801,80.59208,-56.13444,6.11,-0.1],[1802,82.5425,63.06722,5.42,1.71],[1803,81.44583,0.52083,6.16,-0.18],[1804,81.78458,30.20861,5.74,0.16],[1805,81.91208,34.47583,5.07,1.4],[1806,81.51,-5.51833,6.23,-0.06],[1807,81.66167,6.86917,6.42,-0.02],[1808,81.79208,17.96222,5.42,-0.1],[1809,81.8075,15.25778,6.16,0.08],[1810,81.90875,21.93694,4.88,-0.15],[1811,81.70917,3.09556,4.59,-0.21],[1812,81.49917,-19.69556,5.89,0.4],[1813,81.23167,-44.22583,6.08,1.2],[1814,81.94,15.87417,5.5,0.01],[1815,78.10708,-81.54167,6.51,1.11],[1816,82.00667,17.23889,5.77,1.63],[1817,81.77,-11.90083,6.35,0.51],[1818,81.1925,-52.31639,6.27,0.07],[1819,82.145,13.67889,6.35,0.15],[1820,82.00667,1.29833,6.41,-0.18],[1821,82.31875,25.15056,5.47,-0.04],[1822,82.41917,29.18639,6.24,0.45],[1823,81.90208,-21.37556,6.07,1.04],[1824,82.7025,41.46194,6.0,1.11],[1825,82.68792,39.82583,6.37,0.97],[1826,82.23625,-3.3075,6.39,-0.01],[1827,81.77208,-40.94361,5.87,0.23],[1828,83.14083,57.22111,6.48,0.57],[1829,82.06125,-20.75944,2.84,0.82],[1830,82.34833,-3.44639,5.79,1.15],[1831,82.68083,22.4625,6.29,1.18],[1832,82.60875,15.36028,5.94,0.14],[1833,82.47833,1.78917,5.78,-0.2],[1834,82.43333,-1.09222,4.71,1.57],[1835,82.06375,-37.23056,5.57,0.02],[1836,81.58042,-58.9125,5.14,1.0],[1837,82.5825,4.20417,6.21,1.27],[1838,82.27792,-30.11667,6.75,1.06],[1839,82.69625,5.94806,4.2,-0.14],[1840,82.58625,-7.43472,6.33,-0.19],[1842,82.81042,3.29222,5.46,-0.18],[1843,83.18208,32.19194,4.76,0.34],[1844,84.93208,75.04389,6.17,1.57],[1845,83.05333,18.59444,4.38,2.07],[1846,83.36958,42.10889,6.55,-0.02],[1847,83.05917,17.05806,5.46,-0.04],[1848,82.83708,-6.70833,6.22,-0.17],[1849,82.78167,-20.86361,5.55,0.0],[1850,83.36458,32.80111,6.48,0.09],[1851,83.00208,-0.28444,6.85,-0.16],[1852,83.00167,-0.29917,2.23,-0.22],[1853,84.3175,66.69611,6.26,0.6],[1854,83.40833,34.72583,6.27,0.15],[1855,82.9825,-7.30139,4.62,-0.26],[1856,82.53958,-47.07778,5.46,0.62],[1857,84.31292,64.15472,6.15,0.01],[1858,83.38167,18.54028,5.69,0.01],[1859,81.75,-68.62278,6.03,0.34],[1860,83.41167,20.47417,6.18,-0.07],[1861,83.17208,-1.59194,5.35,-0.19],[1862,82.80292,-35.47056,3.87,1.14],[1863,83.28,-1.71833,6.46,-0.09],[1864,83.47625,14.30556,5.64,-0.14],[1865,83.1825,-17.82222,2.58,0.21],[1866,84.14667,54.42889,5.73,1.63],[1867,82.3225,-62.31444,6.59,1.53],[1868,83.38083,-1.15611,5.34,-0.18],[1869,84.06625,47.71528,6.11,0.26],[1870,82.90042,-45.92528,5.86,1.35],[1871,83.49,1.40778,6.59,-0.17],[1872,83.56958,3.76694,5.36,0.05],[1873,83.51625,-1.03556,6.22,-0.17],[1874,83.51667,-1.47056,5.93,1.55],[1875,83.86292,24.03944,5.38,-0.09],[1876,83.705,9.48944,4.41,-0.16],[1877,83.21375,-38.51306,5.48,1.22],[1878,83.98125,27.66222,6.27,1.54],[1879,83.78458,9.93417,3.54,-0.18],[1880,83.78542,9.935,5.61,0.04],[1881,83.28083,-35.13972,5.78,1.07],[1882,82.56625,-63.92778,6.19,0.22],[1883,83.80583,10.24,5.6,0.12],[1884,84.21833,40.18222,6.09,1.03],[1885,90.33417,85.18222,6.11,1.57],[1886,83.75375,-6.00917,5.67,-0.23],[1887,83.76125,-6.00194,4.78,-0.25],[1888,83.46708,-29.84889,6.53,-0.02],[1889,84.12583,25.93944,6.49,0.43],[1890,83.84083,-4.49333,6.56,-0.13],[1891,83.84375,-4.42528,6.24,-0.15],[1892,83.84667,-4.83833,4.59,-0.19],[1893,83.81625,-5.38722,6.73,0.02],[1894,83.81708,-5.38528,7.96,0.24],[1895,83.81875,-5.38972,5.13,0.02],[1896,83.82208,-5.38778,6.7,0.09],[1897,83.84542,-5.41611,5.08,-0.09],[1898,83.87958,-4.36472,6.38,-0.14],[1899,83.85833,-5.91,2.77,-0.24],[1900,83.89917,-3.25278,6.4,-0.12],[1901,83.91458,-4.85583,5.26,0.24],[1902,84.28667,26.92417,5.83,-0.07],[1903,84.05333,-1.20194,1.7,-0.19],[1904,84.44042,33.55917,6.33,1.31],[1905,84.26583,17.04028,5.54,0.22],[1906,84.0625,-5.64806,6.54,-0.18],[1907,84.22625,9.29056,4.09,0.95],[1908,84.26833,11.035,5.94,1.58],[1909,83.81417,-33.08,5.78,1.12],[1910,84.41125,21.1425,3.0,-0.19],[1911,84.14875,-6.065,5.72,-0.22],[1912,83.435,-54.90222,6.43,0.55],[1913,84.33042,8.95194,6.12,-0.17],[1914,84.65875,30.4925,5.4,0.45],[1915,84.04292,-28.70778,6.26,0.13],[1916,85.61,65.69778,5.6,1.24],[1917,83.24833,-64.2275,5.34,1.04],[1918,84.36417,-5.93833,6.05,-0.22],[1919,84.28667,-11.77556,6.11,0.05],[1920,84.50458,7.54139,5.88,-0.07],[1921,84.73917,26.61778,6.37,0.6],[1922,83.40625,-62.48972,3.76,0.82],[1923,84.4725,-4.81361,6.19,-0.04],[1924,84.82667,29.21528,5.96,0.16],[1925,85.33458,53.48111,6.23,0.84],[1926,84.31875,-27.87139,6.16,0.34],[1927,84.01125,-47.31389,6.11,1.16],[1928,84.93417,25.89694,5.18,-0.15],[1929,84.8625,21.76278,6.34,0.06],[1930,83.76,-58.87111,6.75,1.45],[1931,84.68667,-2.6,3.81,-0.24],[1932,84.69625,-2.59417,6.65,-0.19],[1933,84.6575,-6.57389,5.96,-0.23],[1934,84.79625,4.12139,4.57,-0.11],[1935,84.43583,-28.68944,5.31,0.46],[1936,83.74,-61.17583,6.32,0.85],[1937,84.72125,-7.21306,4.8,0.13],[1938,85.14958,31.35806,6.04,0.04],[1939,85.17542,31.92083,6.11,2.12],[1940,84.88,-3.56472,6.0,0.27],[1941,85.75667,56.58167,6.05,0.95],[1942,84.87833,-9.70667,6.5,-0.1],[1943,86.03583,61.47667,6.15,0.9],[1944,84.81792,-17.84944,6.38,-0.12],[1945,85.3375,29.4875,6.43,-0.11],[1946,85.32375,16.53389,4.86,-0.13],[1947,84.68167,-40.7075,5.82,-0.09],[1948,85.18958,-1.94278,2.05,-0.21],[1949,85.19,-1.94278,4.21,0.6],[1950,85.15542,-2.825,6.22,-0.2],[1951,85.4775,23.32639,6.59,-0.06],[1952,85.21083,-1.12889,4.95,-0.21],[1953,82.97042,-76.34111,5.19,1.13],[1954,85.51625,22.66028,6.36,0.6],[1955,85.27333,0.33778,5.93,0.3],[1956,84.91208,-34.07417,2.64,-0.12],[1957,85.19167,-10.40944,6.52,-0.16],[1958,84.9575,-32.62917,5.45,0.92],[1959,85.41792,-2.89611,6.42,0.3],[1960,84.22792,-66.56028,6.31,-0.07],[1961,85.83125,23.20417,6.21,-0.06],[1962,85.42292,-16.72556,6.21,-0.13],[1963,85.61917,1.47472,4.91,1.17],[1964,83.68625,-73.74139,5.78,1.71],[1965,85.55958,-17.53028,6.15,1.38],[1966,85.3625,-33.40056,6.34,-0.04],[1967,85.72417,-6.79611,6.02,0.4],[1968,85.55792,-22.37361,5.87,0.11],[1969,86.62667,56.11556,5.94,0.17],[1970,85.78875,-1.61306,6.31,1.47],[1971,86.475,49.82639,5.47,0.03],[1972,85.54833,-30.53556,6.19,0.01],[1973,85.56292,-34.66778,5.29,-0.05],[1974,86.45625,40.50722,6.58,0.25],[1975,85.84,-18.5575,5.73,-0.01],[1976,87.27042,62.80806,6.13,0.6],[1977,86.41417,20.695,6.95,3.03],[1978,86.25792,4.00806,6.09,0.31],[1979,86.81125,42.52667,6.29,1.35],[1980,86.11833,-20.12639,6.34,0.58],[1981,85.87583,-39.40694,6.25,0.37],[1982,86.11042,-22.42167,6.15,0.94],[1983,86.11583,-22.44833,3.6,0.47],[1984,85.92125,-45.83306,6.39,0.29],[1985,86.68958,15.8225,6.0,-0.06],[1986,86.51167,-4.26833,6.34,1.05],[1987,86.71708,9.52222,5.79,0.88],[1988,86.64583,1.16806,5.95,0.78],[1989,86.805,14.48833,5.72,0.04],[1990,86.85917,17.72917,5.49,0.3],[1991,83.90125,-78.82083,6.05,-0.02],[1992,87.64167,56.91889,6.54,0.6],[1993,86.92875,13.89972,5.29,-0.17],[1994,88.23125,68.47139,6.2,0.95],[1995,87.29375,39.18111,4.52,0.94],[1996,86.49958,-32.30639,5.17,-0.28],[1997,87.09333,20.86944,6.07,-0.08],[1998,86.73875,-14.82194,3.55,0.1],[1999,87.00083,6.45417,5.27,0.23],[2000,86.78167,-16.23778,6.17,0.89],[2001,86.86125,-10.53306,6.03,0.16],[2002,87.25417,24.5675,4.86,1.01],[2003,87.735,51.51472,6.29,1.05],[2004,86.93917,-9.66972,2.06,-0.17],[2005,86.76958,-28.63917,6.22,-0.15],[2006,88.0725,58.96417,6.14,-0.04],[2007,87.14542,-4.09472,5.97,0.64],[2008,86.61417,-46.59722,5.31,1.04],[2009,86.8275,-35.67472,6.32,1.18],[2010,87.38708,12.65111,4.91,-0.07],[2011,87.76,37.30556,4.74,1.62],[2012,87.8725,39.14861,3.97,1.13],[2013,87.74208,27.96778,5.56,0.97],[2014,87.51083,9.87111,5.8,0.87],[2015,86.19333,-65.73556,4.35,0.21],[2016,87.62042,14.30556,5.52,1.01],[2017,86.99208,-40.6525,6.61,1.12],[2018,87.85708,32.125,6.25,1.75],[2019,87.55458,4.42333,5.97,1.36],[2020,86.82125,-51.06639,3.85,0.17],[2021,87.40208,-14.48361,5.49,0.88],[2022,84.29083,-80.46917,5.65,0.6],[2023,86.80417,-54.36083,6.18,1.4],[2024,87.625,2.02444,5.98,0.91],[2025,88.16458,39.57444,6.45,0.09],[2026,87.47292,-22.97167,5.87,0.06],[2027,88.74083,59.88833,5.2,0.02],[2028,88.16708,33.9175,5.98,1.6],[2029,88.71167,55.70694,4.99,0.05],[2030,88.0975,19.86806,6.06,0.54],[2031,87.84167,-7.51806,5.35,-0.2],[2032,87.39208,-44.87528,6.38,1.27],[2033,88.09292,14.17167,5.59,-0.05],[2034,88.33167,27.61222,4.58,-0.02],[2035,87.83042,-20.87917,3.81,0.99],[2036,87.86917,-22.92611,6.17,1.02],[2037,88.11,1.855,4.78,1.38],[2038,88.32958,20.29917,6.71,-0.07],[2039,88.03167,-9.04139,5.97,0.1],[2040,87.74,-35.76833,3.12,1.16],[2041,89.39583,66.09611,6.25,1.36],[2042,87.45708,-56.16667,4.51,1.1],[2043,87.99792,-29.44861,6.45,1.48],[2044,87.61917,-52.76778,6.35,0.76],[2045,89.06,51.80389,6.49,0.6],[2046,88.74583,31.70167,5.9,0.14],[2047,88.59542,20.27611,4.41,0.59],[2048,88.55583,10.58694,6.12,1.47],[2049,87.72167,-52.10889,5.17,0.99],[2050,88.63417,11.7625,6.59,0.03],[2051,88.56542,3.22528,6.31,1.29],[2052,88.73625,19.74972,5.92,-0.17],[2053,88.13833,-37.63111,5.63,1.04],[2054,89.27042,49.02944,6.47,0.99],[2055,88.19875,-38.52583,6.7,1.08],[2056,88.27875,-33.80139,4.87,-0.15],[2057,88.68375,0.96833,6.0,1.34],[2058,88.64458,-4.06389,6.57,-0.18],[2059,82.55792,-84.785,6.2,-0.02],[2060,88.48917,-19.63833,6.69,0.1],[2061,88.79292,7.40694,0.5,1.85],[2062,86.95042,-72.70222,6.53,1.08],[2063,88.95542,20.175,5.4,0.99],[2064,87.47333,-66.90111,5.11,-0.14],[2065,88.68167,-11.77389,5.66,1.53],[2066,89.14083,28.94222,6.32,0.3],[2067,89.01458,13.92528,6.6,0.65],[2068,88.55875,-29.14778,6.36,0.37],[2069,88.34542,-42.92139,6.55,1.37],[2070,88.87625,-4.61639,5.87,1.18],[2071,88.8975,-4.78833,6.28,0.06],[2072,88.08458,-57.15611,5.94,0.66],[2073,87.84583,-64.03361,6.36,0.86],[2074,89.23375,24.24972,6.02,0.39],[2075,89.11667,9.50972,5.99,-0.07],[2076,89.20625,11.52111,5.87,1.1],[2077,89.88167,54.28472,3.72,1.0],[2078,91.28875,75.58583,6.4,0.6],[2079,89.94042,55.32083,6.44,0.31],[2080,89.95042,54.54722,6.14,1.2],[2081,89.84083,49.92444,5.89,1.23],[2082,88.71875,-39.95806,5.57,1.51],[2083,88.54458,-50.36194,6.52,0.9],[2084,89.49875,25.95389,4.82,-0.06],[2085,89.10125,-14.16778,3.71,0.33],[2086,89.05958,-22.84028,5.96,1.11],[2087,88.87458,-37.12083,4.97,1.11],[2088,89.88208,44.9475,1.9,0.03],[2089,88.67125,-49.62694,6.1,-0.13],[2090,89.14375,-23.21556,6.36,1.07],[2091,89.98375,45.93694,4.26,1.72],[2092,89.08708,-31.3825,5.5,0.6],[2093,89.47583,1.22444,6.22,1.48],[2094,88.70917,-52.63528,5.29,0.31],[2095,89.93042,37.2125,2.62,-0.08],[2096,90.07917,44.59194,6.22,0.6],[2097,89.54875,-0.99417,6.22,1.14],[2098,89.20417,-31.97611,6.44,1.07],[2099,89.72167,12.80861,5.7,0.89],[2100,89.60167,1.83694,5.9,0.21],[2101,90.24417,47.90194,5.73,-0.01],[2102,88.52542,-63.09,4.65,1.05],[2103,89.70667,0.55306,5.22,0.01],[2104,88.54958,-64.48222,6.63,0.38],[2105,90.42958,48.95944,5.96,1.45],[2106,89.38417,-35.28333,4.36,-0.18],[2107,89.75417,-9.38222,6.12,0.29],[2108,89.76792,-9.55833,5.03,0.19],[2109,89.90708,-1.44444,6.63,-0.07],[2110,90.2925,31.03472,5.98,0.08],[2111,90.25167,27.57222,6.05,0.25],[2112,90.70292,49.90556,6.05,-0.06],[2113,90.01417,-3.07417,4.53,1.22],[2114,89.31,-53.42611,6.45,1.48],[2115,90.72333,43.37861,6.42,0.6],[2116,90.42333,22.40083,6.37,-0.07],[2117,89.65667,-44.03444,5.81,1.07],[2118,90.07375,-12.89972,6.22,0.36],[2119,90.825,42.91167,6.1,0.97],[2120,89.78667,-42.81528,3.96,1.14],[2121,91.28458,59.39306,6.34,1.1],[2122,90.72958,32.63583,6.24,0.42],[2123,91.12125,51.57333,6.45,0.18],[2124,90.59583,9.6475,4.12,0.16],[2125,87.56958,-79.36139,5.47,-0.08],[2126,91.66333,63.45361,6.39,0.6],[2127,90.57125,1.69444,6.59,-0.05],[2128,90.46,-10.59806,4.95,-0.12],[2129,90.30458,-25.41778,6.05,0.02],[2130,90.86375,19.69056,5.14,-0.11],[2131,90.31833,-33.91167,5.55,1.58],[2132,91.26417,42.98167,5.87,0.3],[2133,90.85292,11.68083,6.08,-0.04],[2134,91.03,23.26333,4.16,0.82],[2135,90.98,20.13833,4.63,0.28],[2136,90.64083,-14.49722,6.2,0.96],[2137,91.26083,37.96417,6.34,0.6],[2138,90.205,-51.21639,5.67,0.2],[2139,91.39167,33.59917,6.23,-0.08],[2140,90.81458,-26.28444,5.04,1.34],[2141,91.53542,35.3875,6.12,0.6],[2142,91.05625,-6.70917,5.21,-0.06],[2143,91.64625,38.48278,5.36,0.25],[2144,91.2425,5.42,5.67,1.04],[2145,91.24333,4.15861,5.63,1.04],[2146,91.59375,29.5125,6.08,1.73],[2147,91.86208,41.85417,6.12,1.22],[2148,91.24625,-16.48444,4.93,0.24],[2149,91.08458,-32.1725,5.65,-0.19],[2150,91.3625,-10.24278,5.87,0.37],[2151,90.53833,-60.09694,6.45,1.58],[2152,92.49625,58.93583,5.36,1.09],[2153,92.09583,41.05556,6.36,1.05],[2154,91.66125,-4.19389,5.38,-0.13],[2155,91.53875,-14.93528,4.67,0.05],[2156,91.44042,-24.19556,6.95,1.63],[2157,91.11833,-45.03667,6.35,0.52],[2158,91.1675,-45.07889,5.93,0.49],[2159,91.89292,14.76833,4.42,-0.17],[2160,91.36292,-35.51361,5.8,0.02],[2161,91.71625,-11.17361,6.66,-0.15],[2162,91.19542,-48.45861,6.58,0.72],[2163,91.63333,-23.11056,5.47,0.08],[2164,91.52292,-29.75861,5.81,0.05],[2165,93.21292,65.71833,5.32,1.34],[2166,91.74,-21.81278,5.78,1.64],[2167,92.19667,8.67,6.55,-0.07],[2168,91.92333,-19.16583,5.31,1.68],[2169,92.385,22.19,5.93,1.63],[2170,91.76542,-34.31194,5.83,-0.13],[2171,91.67083,-42.29861,6.12,0.25],[2172,92.94167,52.64722,6.3,0.14],[2173,92.43333,23.11333,5.75,0.21],[2174,92.24125,2.49944,5.73,0.07],[2175,92.90208,48.71306,6.82,0.6],[2176,92.9025,48.71111,6.09,0.1],[2177,91.88167,-37.25306,5.02,-0.11],[2178,91.75792,-45.09139,6.51,1.16],[2179,92.40083,-5.71111,6.17,0.34],[2180,92.24125,-22.4275,5.5,-0.01],[2181,91.97042,-42.15389,5.5,0.0],[2182,92.33458,-18.12611,6.35,-0.03],[2183,92.39292,-14.58417,5.56,1.16],[2184,92.7575,18.12944,6.33,1.35],[2185,92.88458,24.42028,5.8,1.11],[2186,92.44958,-22.77417,5.71,0.44],[2187,92.14417,-44.35611,6.27,-0.15],[2188,93.43875,51.1725,6.04,1.06],[2189,93.08375,32.69333,5.78,1.66],[2190,92.96417,21.86861,6.56,2.25],[2191,92.86625,13.63861,6.04,0.0],[2192,92.44667,-26.70083,6.27,1.01],[2193,93.00542,19.79056,5.75,-0.07],[2194,91.53917,-66.03972,5.71,-0.03],[2195,92.75542,-6.75417,6.15,0.01],[2196,91.76417,-62.15472,5.05,1.25],[2197,93.07958,22.90833,6.39,2.24],[2198,93.01375,16.13056,4.95,-0.14],[2199,92.985,14.20889,4.48,-0.18],[2200,92.64417,-27.15417,5.72,1.07],[2201,93.91917,59.99917,5.35,1.34],[2202,92.93208,-4.66556,6.18,-0.08],[2203,92.54292,-40.35361,5.58,1.68],[2204,92.34708,-49.56278,6.49,0.52],[2205,92.96583,-6.55028,5.05,-0.2],[2206,92.80625,-26.48222,6.09,-0.04],[2207,93.38917,18.68028,6.58,-0.08],[2208,93.3025,10.6275,6.45,0.67],[2209,94.71167,69.31972,4.8,0.03],[2210,93.185,-2.50444,6.62,0.05],[2211,92.66625,-45.28194,6.31,-0.03],[2212,92.57458,-54.96861,4.81,-0.23],[2213,93.19292,-17.76306,6.52,-0.17],[2214,93.61917,17.90639,5.88,0.22],[2215,94.47833,61.51528,4.98,1.83],[2216,93.71917,22.50667,3.28,1.6],[2217,93.9125,36.14861,6.92,0.45],[2218,93.47625,-3.74139,5.83,0.94],[2219,93.84458,29.49806,4.35,1.02],[2220,93.71208,19.15639,5.2,0.44],[2221,92.18417,-68.84333,5.06,-0.08],[2222,93.78542,13.85111,5.91,-0.23],[2223,93.85458,16.14306,5.3,-0.14],[2224,93.65292,-4.56833,5.83,-0.17],[2225,93.43792,-23.86194,6.39,0.72],[2226,93.38875,-29.39611,6.54,-0.1],[2227,93.71375,-6.27472,3.98,1.32],[2228,94.39458,46.42417,6.52,0.27],[2229,93.9375,12.55111,5.33,-0.02],[2230,94.07917,23.97,6.08,0.9],[2231,93.91708,6.06611,6.07,-0.13],[2232,93.94583,4.28361,6.64,-0.17],[2233,93.89292,-0.51222,5.65,0.5],[2234,93.87375,-4.91528,5.99,0.09],[2235,94.09917,17.18139,6.39,1.58],[2236,93.975,1.16917,6.37,0.46],[2237,93.85833,-9.03528,6.1,-0.08],[2238,94.90583,59.01083,4.48,0.01],[2239,94.57042,46.36056,6.38,1.11],[2240,94.24458,23.74083,6.25,0.45],[2241,94.11083,12.27222,5.04,0.42],[2242,93.785,-20.27222,5.91,1.32],[2243,93.82292,-18.47667,5.99,1.06],[2244,93.93708,-13.71833,5.01,-0.08],[2245,92.8125,-65.58944,5.01,1.62],[2246,94.08833,1.08028,6.63,-0.05],[2247,94.2775,9.9425,5.39,0.1],[2248,94.24333,7.05306,6.57,-0.12],[2249,94.03208,-16.61778,5.92,-0.17],[2250,94.38875,14.05833,6.59,-0.03],[2251,94.31708,5.10028,5.71,0.61],[2252,93.98833,-29.78833,6.67,1.55],[2253,94.52333,14.38278,6.16,0.01],[2254,94.26458,-22.715,6.07,0.56],[2255,94.39667,-10.72528,6.75,0.35],[2256,94.13792,-35.14056,4.37,1.0],[2257,95.515,59.37222,5.94,0.6],[2258,94.7575,17.325,6.32,-0.07],[2259,94.66875,9.04722,6.24,0.87],[2260,94.42375,-16.81583,5.14,1.3],[2261,92.56,-74.75306,5.09,0.72],[2262,94.14833,-39.26444,6.0,0.16],[2263,94.255,-37.7375,5.53,1.14],[2264,95.44208,53.45222,5.36,0.43],[2265,94.28958,-37.25306,5.87,0.14],[2266,94.55708,-19.96694,5.52,-0.18],[2267,94.71083,-9.39028,5.36,1.24],[2268,94.70333,-15.02472,6.06,1.66],[2269,95.0175,14.65111,5.69,1.61],[2270,94.78292,-8.58639,6.22,-0.04],[2271,94.74583,-20.92611,5.81,-0.15],[2272,95.30042,29.54111,6.43,0.06],[2273,94.92833,-7.82306,5.27,-0.19],[2274,94.07708,-59.21361,6.43,0.59],[2275,94.99833,-2.94444,4.9,1.6],[2276,95.21792,11.75639,6.54,-0.1],[2277,95.35792,17.76361,6.35,0.6],[2278,94.46542,-52.73306,6.41,1.46],[2279,94.92083,-34.39667,5.78,-0.08],[2280,95.3575,2.26861,6.31,0.24],[2281,94.695,-50.35917,7.04,0.83],[2282,95.07833,-30.06333,3.02,-0.19],[2283,93.77458,-71.70278,6.64,0.56],[2284,95.35292,-11.77333,5.64,-0.02],[2285,97.06083,70.53556,5.97,0.6],[2286,95.74,22.51361,2.88,1.64],[2287,95.65208,12.57,6.0,0.6],[2288,95.15125,-34.14389,5.53,-0.19],[2289,96.22458,49.28806,4.91,1.97],[2290,95.02542,-48.74111,6.6,0.66],[2291,96.6075,56.285,5.64,0.24],[2292,95.82708,3.76444,6.4,-0.15],[2293,96.70375,58.41722,5.21,1.53],[2294,95.675,-17.95583,1.98,-0.23],[2295,95.84458,-4.68722,6.67,0.06],[2296,95.52833,-33.43639,3.85,0.88],[2297,96.21917,29.70722,6.71,-0.06],[2298,95.94208,4.59278,4.44,0.18],[2299,95.94375,4.59556,6.72,0.45],[2300,96.00958,8.88472,6.26,-0.08],[2301,95.9,-9.87444,6.19,1.84],[2302,96.22042,16.05694,6.33,1.08],[2303,95.94167,-15.07167,6.24,1.53],[2304,96.38708,23.32694,6.06,-0.03],[2305,96.04292,-11.53028,5.22,1.24],[2306,95.94833,-19.78528,6.6,-0.15],[2307,95.81,-31.79,6.34,0.89],[2308,96.36708,14.72194,6.24,2.34],[2309,96.08542,-12.9625,6.12,-0.08],[2310,96.30458,7.08583,5.98,1.22],[2311,95.98292,-25.57778,5.63,1.56],[2312,96.32667,1.50111,6.66,-0.04],[2313,96.31833,-0.94611,5.87,0.56],[2314,96.96292,47.40528,6.56,-0.04],[2315,96.44417,2.27222,6.51,-0.03],[2316,96.00417,-36.70778,5.62,1.04],[2317,96.44625,-3.88917,6.35,1.02],[2318,96.18292,-28.78,6.39,0.62],[2319,96.89792,32.56306,6.43,1.26],[2320,95.7325,-56.37,5.61,0.24],[2321,96.495,-7.89417,6.4,0.14],[2322,95.90708,-52.18111,5.98,1.04],[2323,96.18542,-40.28417,6.31,-0.05],[2324,96.665,-1.50722,5.87,0.08],[2325,96.64375,-4.59722,6.15,-0.14],[2326,95.98792,-52.69583,-0.72,0.15],[2327,96.74458,0.84083,6.71,0.04],[2328,96.68708,-7.51139,6.27,-0.04],[2329,96.37458,-35.06389,6.25,1.36],[2330,96.98583,20.49611,6.22,0.01],[2331,97.69667,58.16278,5.88,0.94],[2332,97.14208,30.49306,5.55,0.68],[2333,96.835,2.90806,5.55,1.04],[2334,96.8075,0.29917,5.2,1.18],[2335,96.815,-0.27611,5.55,1.38],[2336,95.94542,-58.54389,6.48,0.12],[2337,95.75542,-63.68306,6.27,1.6],[2338,97.5125,46.68556,5.9,1.44],[2339,97.23583,26.96778,6.47,0.6],[2340,97.11667,16.23833,6.23,0.9],[2341,96.20083,-52.80639,6.51,1.7],[2342,97.07833,10.30389,6.15,1.15],[2343,97.24083,20.21222,4.15,-0.13],[2344,96.99,-4.76222,5.06,-0.17],[2345,96.05708,-60.28111,5.8,0.0],[2346,100.07,79.59944,6.54,0.6],[2347,97.07,1.91222,6.48,-0.07],[2348,96.43167,-48.17722,5.76,-0.06],[2349,96.79667,-25.85667,6.07,0.53],[2350,101.12583,82.11528,6.65,0.17],[2351,97.25,11.01944,6.59,0.6],[2352,95.65917,-69.98417,5.56,1.51],[2353,96.78125,-37.89556,6.48,0.39],[2354,96.10958,-63.42889,6.46,0.66],[2355,97.31208,2.64611,6.16,1.54],[2356,97.20417,-7.03278,4.6,-0.1],[2357,97.20625,-7.03444,5.4,-0.07],[2358,97.20625,-7.03444,5.6,0.6],[2359,97.15542,-17.46611,5.77,1.12],[2360,96.23167,-63.82778,6.27,-0.13],[2361,97.04208,-32.58,4.48,-0.17],[2362,97.52292,9.02917,6.57,0.14],[2363,100.12,77.99583,5.73,1.47],[2364,97.16333,-32.37111,5.74,-0.17],[2365,99.47875,73.69556,6.24,0.38],[2366,97.79208,16.93861,6.2,1.16],[2367,97.54708,-10.08139,5.93,1.38],[2368,97.17625,-41.07444,6.32,0.4],[2369,96.76667,-58.00222,5.82,1.28],[2370,97.78958,11.25083,6.14,-0.08],[2371,97.90583,15.90333,6.4,0.6],[2372,98.11333,32.45472,5.87,0.14],[2373,97.64458,-13.14806,6.16,-0.15],[2374,97.91333,11.79222,6.65,-0.12],[2375,97.95125,11.54444,5.23,0.15],[2376,98.63667,55.35306,6.45,0.6],[2377,96.36917,-69.69028,5.38,0.97],[2378,98.09708,11.67361,6.03,1.07],[2379,97.84583,-12.39167,5.15,1.27],[2380,97.69292,-27.76944,5.93,-0.15],[2381,97.95875,-8.15806,5.43,1.38],[2382,98.08,4.85583,5.84,0.99],[2383,98.42792,33.02417,6.42,0.03],[2384,97.45458,-50.23917,5.27,0.41],[2385,98.22583,7.33306,4.5,0.0],[2386,98.09625,-5.86889,5.6,0.25],[2387,97.96375,-23.41833,4.33,-0.24],[2388,97.80417,-35.25917,5.84,0.81],[2389,97.36875,-56.85278,5.22,1.09],[2390,97.74917,-40.91639,6.2,1.4],[2391,98.40042,14.15528,5.53,1.11],[2392,98.19542,-11.16639,6.24,1.11],[2393,97.89583,-36.94,6.34,1.63],[2394,99.4225,61.48111,5.94,0.89],[2395,98.40792,-1.22028,5.1,-0.14],[2396,100.13417,71.74889,5.92,1.19],[2397,98.1625,-32.03056,5.69,-0.17],[2398,98.80042,28.02222,5.27,-0.03],[2399,98.08875,-37.69667,5.24,1.0],[2400,97.82625,-51.82611,5.6,0.54],[2401,101.55875,79.56472,5.45,0.5],[2402,99.41042,56.8575,5.85,0.6],[2403,98.36083,-20.92389,6.4,0.83],[2404,98.69333,7.5725,6.45,-0.02],[2405,99.13708,38.44528,5.29,2.61],[2406,98.82333,9.98833,5.88,1.51],[2407,98.29292,-38.62528,6.44,1.0],[2408,97.5125,-65.56833,6.29,0.33],[2409,98.81583,0.89,5.8,0.0],[2410,97.79417,-61.87972,6.15,-0.15],[2411,98.45625,-36.23222,5.42,1.44],[2412,97.99333,-58.75417,5.7,-0.06],[2413,99.0,4.4975,6.55,-0.06],[2414,98.76417,-22.96472,4.54,-0.05],[2415,98.64708,-32.71639,5.62,-0.08],[2416,98.35875,-52.32889,6.19,1.09],[2417,99.36333,24.59083,6.44,0.1],[2418,99.14708,-5.21111,5.52,-0.08],[2419,99.66458,39.39083,5.69,1.35],[2420,99.705,39.9025,5.2,-0.07],[2421,99.42792,16.39917,1.93,0.0],[2422,99.35042,6.13528,6.06,0.05],[2423,99.095,-18.66,5.7,0.85],[2424,98.85042,-36.78,5.59,-0.14],[2425,99.59583,28.98417,5.79,0.0],[2426,99.40375,10.85306,6.38,0.6],[2427,99.83292,42.48889,4.79,1.23],[2428,99.19458,-13.32083,5.97,1.56],[2429,99.17083,-19.25583,3.95,1.06],[2430,99.41792,2.70417,6.17,1.08],[2431,98.975,-36.08889,6.35,0.48],[2432,99.46958,4.95722,6.15,0.14],[2433,99.17042,-22.615,6.35,-0.11],[2434,99.99167,44.01389,6.41,0.6],[2435,98.74417,-52.97556,4.39,-0.02],[2436,99.77208,22.03083,6.04,1.03],[2437,99.42042,-12.985,6.12,1.0],[2438,99.88792,28.26306,6.03,-0.08],[2439,99.88083,24.6,6.38,0.53],[2440,99.58542,-2.54361,6.14,1.48],[2441,99.70625,4.70056,6.57,-0.07],[2442,99.65875,1.61361,6.21,0.15],[2443,99.4725,-18.2375,4.43,1.15],[2444,99.25792,-38.14667,6.04,1.03],[2445,99.21375,-41.55694,6.34,1.15],[2446,99.3075,-36.99056,5.71,-0.12],[2447,99.44833,-32.33972,5.27,1.18],[2448,99.6475,-16.87361,6.03,0.03],[2449,99.94833,12.98306,5.97,0.06],[2450,99.81958,-14.14583,4.82,1.5],[2451,99.44042,-43.19611,3.17,-0.11],[2452,100.40708,35.93194,6.46,0.49],[2453,100.3375,28.19639,6.42,1.09],[2454,100.1325,6.37167,6.51,-0.14],[2455,99.90125,-23.69556,6.05,-0.03],[2456,100.24458,9.89556,4.66,-0.25],[2457,100.34083,16.3975,6.28,-0.01],[2458,100.32167,11.00333,6.11,1.68],[2459,100.77083,44.52444,5.02,1.48],[2460,99.92792,-30.47028,5.71,1.14],[2461,100.2725,0.49528,5.79,-0.1],[2462,99.65667,-48.22028,4.93,0.87],[2463,101.04833,53.29639,6.27,1.08],[2464,100.8075,37.14694,6.19,1.03],[2465,99.98708,-38.15889,6.58,1.18],[2466,100.60125,17.64528,5.21,0.06],[2467,100.49708,6.345,6.37,-0.05],[2468,99.5025,-61.53306,6.18,0.62],[2469,100.485,-9.16722,5.19,1.53],[2470,101.55875,59.44167,4.87,0.08],[2471,101.0525,36.10972,6.31,0.06],[2473,100.98292,25.13111,2.98,1.4],[2474,100.77708,3.03333,6.19,1.37],[2475,100.30875,-40.34972,6.12,-0.14],[2476,100.20542,-47.67472,6.65,1.58],[2477,101.70625,57.16917,5.35,0.96],[2478,100.99708,13.22778,4.49,1.16],[2479,100.91125,3.93222,5.9,-0.02],[2480,101.18958,28.97083,5.44,1.45],[2481,100.69083,-22.44917,6.13,0.34],[2482,100.56833,-38.39861,6.29,0.34],[2483,101.68458,43.5775,5.25,0.56],[2484,101.3225,12.89556,3.36,0.43],[2485,102.05375,55.70444,6.33,0.6],[2486,102.05125,55.70444,6.28,0.47],[2487,101.915,48.78944,5.22,1.12],[2488,100.84708,-39.19333,6.3,0.26],[2489,101.47583,12.69361,6.46,0.6],[2490,102.73792,67.57194,5.14,-0.17],[2491,101.28708,-16.71611,-1.46,0.0],[2492,101.11833,-31.07056,5.2,-0.12],[2493,101.21625,-27.34139,6.45,0.54],[2494,101.635,8.58722,5.93,-0.17],[2495,101.34708,-23.46194,6.05,1.2],[2497,101.26,-30.58611,6.54,-0.11],[2498,101.49708,-14.79611,5.32,0.07],[2499,101.84792,18.19333,6.2,0.07],[2500,101.345,-31.79361,5.92,0.48],[2501,101.38042,-30.94889,5.8,-0.19],[2502,101.6625,-10.10722,5.66,-0.05],[2503,101.8325,8.03722,4.77,1.4],[2504,101.71292,-14.42583,5.29,-0.04],[2505,100.24083,-71.77556,6.51,1.11],[2506,101.965,2.41222,4.47,1.11],[2507,101.51375,-39.54,6.62,-0.13],[2508,101.90458,-8.99833,5.07,1.8],[2509,101.75625,-21.01556,6.08,-0.19],[2510,101.55042,-37.77556,6.21,-0.13],[2511,103.42583,68.88833,5.12,-0.13],[2512,102.42208,32.60667,5.71,1.29],[2513,101.35875,-52.20111,6.57,1.08],[2514,102.07917,-1.31917,5.75,0.28],[2515,101.47375,-52.41,5.8,1.53],[2516,102.69125,41.78139,5.02,1.27],[2517,102.26542,1.00194,6.15,-0.13],[2518,101.83917,-37.92972,5.26,-0.08],[2519,102.4575,16.20278,5.85,-0.13],[2520,103.27125,59.44861,5.33,0.65],[2521,102.31833,-2.27194,5.74,-0.1],[2522,102.24083,-15.14472,5.39,-0.1],[2523,101.72,-51.26583,5.4,1.34],[2524,101.67292,-54.69472,6.46,0.86],[2525,102.60625,13.41333,5.65,1.34],[2526,101.82792,-55.54,5.61,1.52],[2527,105.01667,76.9775,4.55,1.36],[2528,102.43333,-24.07583,6.33,0.04],[2529,102.8875,21.76111,5.27,-0.02],[2530,102.70792,-0.54083,5.77,0.39],[2531,100.90333,-73.11806,6.37,0.96],[2532,103.28167,44.83944,6.26,0.21],[2533,103.0,23.60167,5.65,1.45],[2534,102.67625,-8.04111,6.29,0.0],[2535,102.59125,-17.08389,5.79,1.44],[2536,101.23375,-70.43389,6.11,1.33],[2537,102.525,-27.33389,7.04,-0.18],[2538,102.46042,-32.50861,3.96,-0.23],[2539,103.25625,38.86917,6.12,0.37],[2540,103.19708,33.96111,3.6,0.1],[2541,103.30583,38.43806,6.3,0.49],[2542,103.26417,35.78861,6.01,1.45],[2543,102.91375,3.04167,6.38,0.04],[2544,102.65375,-25.77806,6.33,-0.18],[2545,102.59708,-31.70611,5.7,0.14],[2546,102.49083,-45.45,6.55,1.51],[2547,103.4875,38.505,6.48,-0.05],[2548,102.4775,-46.61472,5.14,0.45],[2549,102.71833,-34.36722,4.99,1.38],[2550,102.0475,-61.94139,3.27,0.21],[2551,103.20583,8.38028,5.77,0.26],[2552,103.09542,-5.31611,6.3,0.99],[2553,102.48417,-50.61472,2.93,1.2],[2554,102.46375,-53.62222,4.4,0.92],[2555,103.34375,10.99639,6.24,0.97],[2556,103.81333,45.82639,6.34,0.94],[2557,103.81125,43.91,6.13,0.32],[2558,102.92625,-36.23028,5.96,0.18],[2559,100.01125,-80.81361,5.64,0.2],[2560,104.31875,58.4225,4.35,0.85],[2561,104.305,57.56333,6.05,1.49],[2562,102.50458,-60.24917,6.11,0.46],[2563,102.88667,-48.2925,6.42,1.21],[2564,103.66125,13.17778,4.65,0.3],[2565,103.32833,-19.03278,5.64,0.28],[2566,103.34042,-18.93333,6.14,0.15],[2567,103.25042,-26.9575,6.4,1.53],[2568,104.13458,46.27417,5.87,-0.06],[2569,103.8275,25.37556,5.73,0.57],[2570,103.53542,-5.8525,6.41,0.17],[2571,103.38708,-20.22417,4.83,-0.21],[2572,103.6025,-1.12694,5.45,0.18],[2573,104.23333,46.705,5.86,1.08],[2574,103.5475,-12.03861,4.07,1.43],[2575,103.16542,-42.50444,6.52,0.42],[2576,103.39125,-28.53972,6.04,0.71],[2577,103.67542,-1.75639,6.21,0.56],[2578,103.48042,-24.53917,6.21,0.01],[2579,103.19625,-43.97583,6.46,-0.1],[2580,103.53292,-24.18389,3.87,1.73],[2581,105.33958,70.80806,5.68,1.33],[2582,103.745,-2.80361,6.04,1.1],[2583,103.55417,-23.92833,6.91,-0.28],[2584,103.89417,8.32472,6.29,0.04],[2585,104.40458,45.09417,4.9,0.03],[2586,104.25208,33.68111,5.89,0.88],[2587,103.19542,-54.09,6.57,1.08],[2588,103.76125,-20.40472,5.74,0.08],[2589,104.1075,9.95639,5.92,-0.08],[2590,103.90583,-20.13639,4.68,0.37],[2591,103.61125,-42.36556,6.32,2.24],[2592,103.18917,-59.34111,6.41,0.18],[2593,104.0275,-14.04361,5.0,1.18],[2594,103.50958,-50.61167,6.26,0.99],[2595,103.945,-22.94139,5.3,-0.18],[2596,104.03417,-17.05417,4.37,-0.07],[2597,104.35708,11.9075,6.27,0.34],[2598,103.97833,-31.79,6.36,0.6],[2599,104.25042,-8.17889,6.34,0.64],[2600,104.76167,38.05222,6.0,1.23],[2601,104.6975,26.08111,6.1,0.46],[2602,102.8625,-70.96333,5.4,-0.11],[2603,104.31167,-22.20333,6.61,-0.19],[2604,104.19042,-35.34167,6.29,1.29],[2605,104.86625,25.91417,6.4,-0.11],[2606,104.6625,7.62194,6.27,0.11],[2607,104.39125,-24.63056,5.46,0.36],[2608,104.06667,-48.72111,4.95,1.69],[2609,115.12708,87.02,5.07,1.63],[2610,104.7375,3.60222,5.97,1.06],[2611,104.42667,-27.5375,6.23,-0.14],[2612,104.32333,-35.5075,6.23,0.46],[2613,104.83375,7.31694,6.35,-0.11],[2614,104.53167,-27.16472,6.37,-0.19],[2615,105.06583,16.07889,5.68,1.66],[2616,104.64958,-25.41389,5.59,-0.16],[2617,106.46583,70.73194,6.5,0.6],[2618,104.65625,-28.97222,1.5,-0.21],[2619,104.60458,-34.11167,5.06,-0.16],[2620,105.32167,32.41444,6.59,0.27],[2621,104.6825,-30.99778,6.42,-0.14],[2622,105.075,-5.36694,6.3,0.57],[2623,104.91375,-21.60333,6.26,-0.15],[2624,105.09875,-8.40694,5.96,-0.08],[2625,105.03375,-20.15889,6.31,-0.15],[2626,104.67417,-45.76806,6.22,0.0],[2627,105.16375,-9.20306,6.49,0.21],[2628,105.08042,-22.11944,6.53,-0.17],[2629,105.4225,4.81806,6.63,0.06],[2630,105.60333,24.21528,5.18,0.94],[2631,105.60625,17.75556,5.94,1.63],[2632,105.5725,15.33611,5.74,0.6],[2633,105.47917,5.5575,6.59,-0.02],[2634,104.665,-55.72944,6.27,1.16],[2635,105.63958,16.67417,5.82,1.68],[2636,105.47042,-1.34556,6.17,1.28],[2637,105.1775,-28.48944,6.27,0.45],[2638,104.65083,-56.39472,6.45,0.39],[2639,105.485,-5.72222,5.2,1.68],[2640,105.27458,-25.21528,5.63,-0.17],[2641,105.2075,-33.46528,6.4,1.05],[2642,106.50542,59.80194,6.44,0.6],[2643,105.87667,29.33722,5.93,0.6],[2644,106.41583,52.75806,6.12,0.11],[2645,106.2875,47.775,6.38,-0.04],[2646,105.42958,-27.93472,3.47,1.73],[2647,105.82458,9.13833,5.97,0.12],[2648,105.72833,-4.23917,4.99,-0.2],[2649,105.90833,10.95167,5.13,1.39],[2650,106.02708,20.57028,3.79,0.79],[2651,105.965,12.59444,5.98,1.58],[2652,105.21458,-51.4025,5.14,1.61],[2653,105.75625,-23.83333,3.02,-0.08],[2654,106.08417,1.48833,6.57,0.01],[2655,106.02167,-5.32361,5.62,1.29],[2656,105.98875,-10.12417,6.45,-0.08],[2657,105.93958,-15.63333,4.12,-0.12],[2658,105.565,-43.40417,6.43,-0.04],[2659,106.32667,22.63722,6.02,-0.03],[2660,106.54833,34.47389,5.55,0.91],[2661,105.27125,-58.94,6.02,0.3],[2662,104.96042,-67.91611,5.17,1.4],[2663,106.41292,9.18583,5.78,1.52],[2664,106.19625,-22.03222,6.09,1.22],[2665,106.84292,34.00944,5.91,1.51],[2666,106.01167,-42.33722,5.2,0.2],[2667,105.98875,-43.60806,5.54,0.64],[2668,105.995,-43.61167,6.79,0.8],[2669,106.85417,28.17722,6.48,-0.09],[2670,106.45708,-10.66111,6.49,-0.05],[2671,106.83917,22.70361,7.68,2.21],[2672,105.97375,-49.58389,4.93,0.13],[2673,107.05542,33.83222,6.28,1.34],[2674,105.815,-59.17806,5.5,-0.11],[2675,107.15125,37.445,6.16,1.21],[2676,106.77667,4.91028,6.11,-0.13],[2677,106.38333,-34.77778,6.14,0.37],[2678,106.66958,-11.29417,5.39,0.05],[2679,106.64958,-12.39389,6.48,-0.1],[2680,106.5025,-30.65556,6.34,-0.16],[2681,108.49208,71.81667,6.35,1.12],[2682,106.95625,7.47111,5.75,1.18],[2683,106.07625,-56.74972,5.17,-0.04],[2684,107.09167,15.93083,5.44,1.03],[2685,106.50958,-38.38278,6.11,0.7],[2686,106.71792,-24.96056,6.08,1.34],[2687,106.31875,-50.36028,6.46,0.6],[2688,106.75042,-26.65778,6.62,-0.17],[2689,104.14333,-79.42028,5.45,0.05],[2690,106.84417,-23.84028,5.71,-0.1],[2691,106.77958,-40.89333,5.79,-0.17],[2692,107.52792,21.24694,6.43,0.89],[2693,107.09792,-26.39333,1.84,0.68],[2694,107.33458,-10.34722,6.21,0.03],[2695,107.20542,-24.04417,6.65,-0.19],[2696,107.91375,39.32056,4.9,1.45],[2697,107.785,30.24528,4.41,1.26],[2698,106.80542,-51.96778,5.96,1.0],[2699,107.38833,-16.23472,6.03,0.06],[2700,107.84625,26.85667,5.78,0.1],[2701,107.55708,-4.23722,4.92,1.03],[2702,107.21292,-39.65583,4.83,-0.18],[2703,108.3475,51.42889,5.47,1.67],[2704,107.42875,-25.23111,5.69,-0.16],[2705,107.53875,-18.68528,6.23,0.4],[2706,108.11,24.12833,5.85,0.36],[2707,107.84833,-0.30194,5.45,0.29],[2708,107.58083,-27.49139,5.46,1.0],[2709,111.34125,81.2575,6.31,-0.04],[2710,107.96375,5.65472,6.09,-0.02],[2711,108.20417,27.22472,6.43,0.49],[2712,106.55875,-68.83722,6.47,1.04],[2713,108.03083,5.47472,6.16,1.14],[2714,107.96625,-0.49278,4.15,-0.01],[2715,108.97875,59.6375,5.2,1.07],[2716,107.92333,-20.88306,5.84,-0.04],[2717,108.34292,16.15889,5.0,1.66],[2718,108.05083,-25.9425,5.92,-0.17],[2719,107.69792,-48.93222,5.14,1.24],[2720,108.01708,-30.82167,6.1,0.27],[2721,108.95875,47.24,5.58,0.58],[2722,108.61083,24.71083,6.89,-0.03],[2723,108.28,-11.25139,5.78,1.51],[2724,108.10042,-27.47417,6.59,0.19],[2725,108.67458,24.885,5.82,1.56],[2726,108.10708,-36.54444,5.96,-0.14],[2727,108.06583,-40.49889,5.31,0.06],[2728,108.63583,12.11583,5.62,1.01],[2729,108.58333,3.11139,5.35,1.19],[2730,108.34958,-22.67333,6.01,1.48],[2731,108.54583,-3.90139,5.75,1.58],[2732,108.56458,-9.9475,5.9,1.52],[2733,108.45125,-22.90639,6.36,-0.21],[2734,108.40125,-27.35639,6.12,-0.23],[2735,107.17583,-70.49722,5.69,0.4],[2736,107.18708,-70.49889,3.78,1.04],[2737,109.39042,52.13083,5.92,1.26],[2738,108.98792,27.8975,5.71,1.63],[2739,108.6175,-10.31667,6.03,-0.18],[2740,108.14,-46.75944,4.49,0.32],[2741,108.44625,-31.08389,6.6,-0.17],[2742,112.76833,82.41139,4.96,1.66],[2743,108.48833,-30.34,6.33,-0.2],[2744,108.83042,-0.16139,6.41,0.9],[2745,108.56333,-26.3525,4.66,-0.19],[2746,108.30583,-45.18306,4.89,-0.02],[2747,108.91417,7.97778,5.82,1.6],[2748,108.385,-44.63972,5.1,1.56],[2749,108.70292,-26.77278,3.85,-0.17],[2750,108.71292,-27.03806,5.58,1.22],[2751,109.63292,49.465,5.05,0.08],[2752,108.92958,-10.58389,5.95,1.18],[2753,109.50917,40.88333,5.78,0.17],[2754,108.00833,-63.19,6.02,-0.02],[2755,108.94792,-23.74056,6.32,0.6],[2756,108.8375,-30.68639,5.36,-0.17],[2757,109.51708,30.95583,6.24,-0.03],[2758,109.06042,-15.58583,5.46,0.08],[2759,108.73792,-41.42583,5.94,-0.16],[2760,109.32417,6.68056,6.65,-0.11],[2761,108.69125,-46.84972,5.72,-0.11],[2762,108.65917,-48.27167,4.76,-0.1],[2763,109.52333,16.54028,3.58,0.11],[2764,109.15333,-23.31556,4.79,1.71],[2765,109.38208,-6.68,6.29,1.62],[2766,109.14583,-27.88111,4.64,1.6],[2767,108.8375,-52.49972,5.97,1.1],[2768,109.23833,-30.89667,6.32,0.2],[2769,109.1325,-38.31889,5.8,-0.13],[2770,109.20625,-36.59278,5.03,-0.17],[2771,109.06458,-46.77444,5.66,1.44],[2772,110.57167,59.90194,6.35,0.6],[2773,109.28583,-37.0975,2.7,1.62],[2774,109.44958,-26.7975,6.46,-0.15],[2775,110.26292,42.65556,6.35,1.46],[2776,110.32292,45.22806,5.77,0.32],[2777,110.03083,21.98222,3.53,0.34],[2778,109.84333,2.74056,5.89,1.07],[2779,109.94833,7.14278,5.91,0.53],[2780,110.02875,15.14278,6.45,-0.02],[2781,109.66792,-24.55889,4.98,-0.15],[2782,109.67667,-24.95417,4.4,-0.15],[2783,110.71208,55.28444,6.53,0.6],[2784,110.71708,55.28139,5.45,0.6],[2785,109.75833,-19.28028,6.09,0.62],[2786,109.71333,-26.58583,5.28,0.96],[2787,109.57667,-36.73417,4.66,-0.1],[2788,109.86708,-16.395,5.7,0.35],[2789,109.5175,-43.98667,5.85,-0.12],[2790,109.65917,-36.74278,5.11,-0.16],[2791,109.64,-39.21028,5.25,0.01],[2792,110.55583,38.99611,6.4,1.22],[2793,110.51083,36.76056,5.13,1.08],[2794,109.80708,-33.72722,6.3,1.31],[2795,110.48667,20.44361,5.1,1.52],[2796,110.2425,-14.36,5.45,0.98],[2797,113.66542,80.89667,6.41,0.6],[2798,110.32042,-8.87833,6.55,0.54],[2799,110.22167,-22.85167,6.61,-0.15],[2800,110.22917,-26.96361,6.01,-0.17],[2801,110.51458,0.17722,5.99,-0.07],[2802,110.26792,-25.89139,5.87,1.64],[2803,109.2075,-67.95722,3.98,0.79],[2804,111.23833,51.88722,5.8,1.61],[2805,111.03542,40.67222,5.19,1.23],[2806,110.50833,-8.97917,6.43,-0.19],[2807,110.57708,-2.97889,6.23,0.68],[2808,110.86875,25.05056,5.03,0.9],[2809,111.8575,66.33167,6.47,-0.09],[2810,110.8675,22.94528,6.02,-0.01],[2811,110.60583,-5.98278,5.82,0.35],[2812,110.55625,-19.01667,4.96,-0.04],[2813,110.08917,-52.31167,6.05,0.42],[2814,110.09042,-52.30972,6.6,0.59],[2815,110.16125,-52.08611,5.39,-0.07],[2816,111.13917,27.63806,5.76,0.34],[2817,111.11542,15.51722,6.41,-0.18],[2818,111.67833,49.21139,4.64,-0.02],[2819,110.7525,-31.92389,5.43,-0.15],[2820,111.2425,11.66972,5.3,0.1],[2821,111.43167,27.79806,3.79,1.03],[2822,110.87125,-27.83417,5.38,1.53],[2823,110.88292,-32.20222,5.39,-0.18],[2824,110.97625,-30.21694,6.6,-0.2],[2825,111.16708,-16.20111,5.33,-0.05],[2826,111.07167,-22.91278,6.19,-0.09],[2827,111.02375,-29.30306,2.45,-0.08],[2828,111.41208,9.27611,4.99,1.01],[2829,110.99292,-35.83778,6.31,-0.15],[2830,112.71958,68.46556,5.64,1.11],[2831,111.21125,-19.01222,6.24,0.29],[2832,111.28458,-13.75194,5.78,0.42],[2833,111.4625,-5.775,5.97,0.92],[2834,111.1825,-31.80889,5.35,1.07],[2835,111.70917,21.53583,6.54,0.46],[2836,111.61625,10.60833,6.37,0.09],[2837,111.73458,20.25722,5.93,0.3],[2838,111.51458,-4.5375,6.76,-0.01],[2839,111.33292,-21.98278,6.05,0.23],[2840,111.67208,11.00917,6.41,-0.13],[2841,111.35542,-25.21778,5.78,-0.1],[2842,111.19667,-37.29,6.97,0.22],[2843,111.19708,-37.29083,6.84,0.27],[2844,112.215,48.18389,5.72,-0.1],[2845,111.7875,8.28944,2.9,-0.09],[2846,111.935,21.445,5.22,0.39],[2847,111.42917,-31.73861,6.31,-0.17],[2848,101.74458,-87.025,6.47,0.42],[2849,112.48333,49.6725,5.36,0.45],[2850,111.66958,-23.71222,6.56,0.01],[2851,112.00875,6.94194,5.25,0.22],[2852,112.27792,31.78444,4.18,0.32],[2853,111.78333,-17.86444,5.63,0.32],[2854,112.04083,8.92556,4.32,1.43],[2855,111.7475,-23.08611,5.61,-0.13],[2856,111.67625,-34.14083,5.9,-0.17],[2857,112.335,28.11806,5.05,0.11],[2858,112.19708,15.10944,6.22,-0.05],[2859,111.96542,-11.55694,5.79,0.58],[2860,111.92875,-22.85972,5.95,-0.09],[2861,112.45292,27.91611,5.01,1.11],[2862,111.59125,-51.01833,5.1,1.06],[2863,111.99667,-29.15583,5.54,-0.08],[2864,112.44917,12.00667,4.54,1.28],[2865,112.32792,-1.90528,5.59,1.5],[2866,112.35708,-7.55111,5.86,0.48],[2867,112.34208,-10.32667,5.75,1.62],[2868,112.34125,-14.99917,6.05,0.47],[2869,112.095,-37.81028,6.58,0.06],[2870,112.21375,-31.84833,6.38,-0.17],[2871,112.21458,-31.84694,7.13,-0.15],[2872,112.98208,38.89639,6.54,0.07],[2873,112.27042,-31.45639,5.77,-0.19],[2874,112.46417,-23.02444,4.85,0.23],[2875,112.27375,-38.81222,5.43,-0.16],[2876,112.71292,-5.22639,6.24,1.18],[2877,112.95167,17.08611,5.42,1.13],[2878,112.3075,-43.30139,3.25,1.51],[2879,113.21083,22.88778,6.54,1.01],[2880,113.02458,1.91444,5.25,0.22],[2881,112.67708,-30.96222,4.65,0.93],[2882,112.67667,-37.33972,6.65,0.63],[2883,113.02417,-8.88083,5.9,0.54],[2884,112.49833,-52.65111,5.87,1.01],[2885,112.8575,-36.15278,6.68,-0.12],[2886,113.40208,15.82667,5.25,0.05],[2887,113.29833,3.29028,5.59,0.31],[2888,112.21375,-64.51,6.39,1.56],[2889,112.92833,-35.88778,6.61,-0.08],[2890,113.65,31.88861,2.88,0.04],[2891,113.65,31.88833,1.98,0.03],[2892,112.62875,-54.39944,5.96,1.56],[2893,113.52125,10.56833,6.28,-0.01],[2894,114.19583,55.75528,5.92,1.12],[2895,113.09292,-35.96139,6.3,-0.1],[2896,113.78625,30.96111,5.33,1.01],[2897,113.34208,-14.33833,6.21,-0.04],[2898,113.98333,43.03111,6.3,0.6],[2899,113.33125,-19.4125,5.66,1.12],[2900,113.29042,-24.71083,5.85,0.17],[2901,113.56583,3.37139,5.81,-0.02],[2902,113.44958,-14.52389,4.97,1.41],[2903,114.13167,46.18028,5.65,1.56],[2904,113.69167,2.725,6.55,0.22],[2905,113.98042,26.89583,4.06,1.54],[2906,113.51333,-22.29611,4.45,0.51],[2907,113.30625,-40.05889,6.26,-0.12],[2908,113.30542,-43.08639,6.52,0.92],[2909,113.5775,-23.47361,5.83,0.44],[2910,113.57958,-23.47472,5.87,0.6],[2911,113.4625,-36.33833,5.54,-0.06],[2912,113.62,-26.11667,6.65,-0.01],[2913,113.55333,-33.46333,6.11,0.29],[2914,114.47458,48.77361,5.92,0.22],[2915,114.32417,40.02528,6.38,0.6],[2916,113.64542,-27.01194,5.77,1.06],[2917,113.49375,-39.90583,6.76,1.13],[2918,114.14458,5.86167,5.91,0.6],[2919,111.40833,-79.09417,5.53,1.28],[2920,114.06917,-8.31139,6.27,1.54],[2921,114.01625,-14.49278,5.7,-0.12],[2922,113.845,-28.36944,4.64,-0.11],[2923,114.0325,-22.16056,6.34,0.99],[2924,114.63667,35.04861,5.56,0.93],[2925,113.66458,-51.47472,6.28,0.08],[2926,114.56042,24.36028,6.27,0.6],[2927,114.31958,-4.11111,5.13,0.44],[2928,114.17083,-19.70222,5.74,-0.19],[2929,115.20625,57.08278,6.06,1.46],[2930,114.79125,34.58417,4.9,0.4],[2931,114.79958,24.2225,6.17,0.01],[2932,114.41208,-14.44111,6.53,-0.01],[2933,114.32,-23.775,6.37,0.54],[2934,113.91542,-52.53389,4.94,1.4],[2935,115.06083,38.34444,5.73,1.63],[2936,114.97542,32.00972,6.17,0.35],[2937,114.34208,-34.96861,4.53,-0.09],[2938,114.86917,17.67472,5.05,1.56],[2939,115.30167,48.13167,5.56,1.01],[2940,114.18292,-48.83028,5.72,-0.06],[2941,114.00708,-55.8875,6.39,1.18],[2942,114.43667,-35.27722,6.6,1.14],[2943,114.82542,5.225,0.38,0.42],[2944,114.575,-25.36472,4.7,-0.11],[2945,114.43833,-38.01056,6.38,1.48],[2946,115.75167,58.71028,4.99,0.08],[2947,114.77958,-18.67917,6.72,-0.1],[2948,114.70542,-26.80167,4.5,-0.17],[2949,114.7075,-26.80361,4.62,0.6],[2950,115.02917,5.23083,6.02,-0.04],[2951,115.24375,23.01861,5.89,1.58],[2952,114.60083,-39.99139,6.59,-0.05],[2953,115.19708,13.77083,6.24,0.6],[2954,114.68292,-36.49694,5.8,-0.16],[2955,114.63583,-38.78111,6.19,1.02],[2956,114.86208,-26.86306,6.5,-0.1],[2957,114.57583,-48.60111,5.68,0.65],[2958,115.14792,-8.18583,6.01,0.15],[2959,115.09667,-15.26361,4.94,1.56],[2960,115.05667,-19.66083,5.93,1.16],[2961,114.86417,-38.30833,4.84,-0.19],[2962,115.68125,34.00028,6.02,0.6],[2963,114.9325,-38.13944,5.73,-0.12],[2964,114.94917,-38.26083,5.76,-0.07],[2965,115.46583,13.48056,5.77,1.67],[2966,115.39667,3.62472,5.94,-0.04],[2967,115.51333,14.20833,5.56,1.64],[2968,114.99167,-37.57944,6.0,-0.04],[2969,116.0175,50.43389,5.27,0.0],[2970,115.31167,-9.55111,3.93,1.02],[2971,114.75167,-53.27333,6.06,-0.11],[2972,115.18083,-27.94583,6.76,-0.16],[2973,115.82792,28.88361,4.28,1.12],[2974,115.21958,-31.66083,6.56,1.18],[2975,116.66708,65.45583,5.92,1.18],[2976,115.34833,-22.33722,6.18,1.62],[2977,116.61417,62.83056,6.49,0.26],[2978,115.8425,22.39944,6.21,0.93],[2979,113.84042,-74.27556,7.16,-0.02],[2980,113.84083,-74.27556,7.26,0.6],[2981,115.31583,-38.53361,5.42,-0.15],[2982,115.7725,0.18944,6.19,1.02],[2983,116.02875,25.78417,5.31,1.54],[2984,115.34083,-44.63222,6.41,0.0],[2985,116.11167,24.39806,3.57,0.93],[2986,115.49167,-38.52889,6.54,-0.09],[2987,116.05833,12.85944,6.43,0.6],[2988,115.70042,-26.35111,5.64,0.99],[2989,116.03083,2.405,6.47,0.2],[2990,116.32875,28.02611,1.14,1.0],[2991,116.28875,20.31639,6.33,0.0],[2992,115.91292,-25.50389,6.55,0.06],[2993,115.885,-28.41111,4.59,1.63],[2994,115.8,-36.05028,5.6,-0.13],[2995,115.77917,-38.86417,6.89,1.02],[2996,115.95208,-28.95472,3.96,0.18],[2997,119.07208,80.26556,6.56,0.73],[2998,115.73833,-45.17333,5.06,0.78],[2999,116.66375,37.5175,5.18,1.58],[3000,114.0175,-77.63417,6.18,1.73],[3001,115.92875,-38.20194,6.4,-0.16],[3002,115.92458,-40.93389,5.17,1.1],[3003,116.53083,18.51,4.88,1.45],[3004,116.14167,-24.67389,5.62,-0.19],[3005,115.77875,-49.99278,6.57,0.07],[3006,115.5425,-58.63083,6.43,-0.1],[3007,116.04,-36.06278,5.8,0.31],[3008,116.5675,10.76833,5.3,0.01],[3009,116.36958,-14.68611,6.89,0.6],[3010,116.37125,-14.69083,6.07,0.11],[3011,116.1425,-37.94306,5.88,-0.11],[3012,115.72208,-58.23,6.21,1.05],[3013,116.87625,33.41556,5.14,1.6],[3014,116.50917,-6.7725,5.49,1.38],[3015,116.48708,-14.56389,5.04,0.33],[3016,116.26917,-37.88778,6.54,-0.1],[3017,116.31375,-37.96861,3.61,1.73],[3018,116.39583,-34.17306,5.37,0.6],[3019,116.68708,-12.67528,6.39,-0.02],[3020,116.32542,-43.75222,6.03,-0.07],[3021,117.14,23.14111,6.18,0.6],[3022,116.54333,-37.93389,5.88,-0.11],[3023,116.80208,-22.51972,5.9,-0.19],[3024,115.455,-72.60611,3.95,1.04],[3025,116.63917,-40.05972,6.57,-0.13],[3026,116.91042,-15.99083,6.34,1.78],[3027,116.93833,-16.01444,6.43,1.7],[3028,117.77375,54.12917,6.02,0.46],[3029,116.98625,-12.19306,5.48,0.48],[3030,117.25833,13.37083,6.04,1.38],[3031,116.39833,-56.7225,6.12,0.39],[3032,116.77417,-39.33139,6.31,-0.17],[3033,117.24542,4.33278,6.53,0.78],[3034,117.02167,-25.93722,4.5,-0.05],[3035,116.85417,-38.51111,5.08,-0.1],[3036,116.18292,-66.07194,6.38,0.95],[3037,116.88125,-46.60861,5.23,-0.14],[3038,116.05417,-69.82139,6.18,-0.06],[3039,118.1525,55.20944,6.38,0.6],[3040,117.75958,33.23361,6.03,0.15],[3041,117.03542,-40.65222,6.14,1.58],[3042,117.36917,-13.35333,6.23,-0.09],[3043,117.25708,-24.91222,5.33,0.76],[3044,117.42167,-17.22833,5.18,1.28],[3045,117.32375,-24.85972,3.34,1.24],[3046,117.08458,-47.07778,4.71,1.06],[3047,117.54417,-9.18333,5.61,1.44],[3048,117.43833,-20.20694,6.56,0.58],[3049,117.31125,-35.24333,5.93,-0.05],[3050,117.6975,3.27722,6.18,1.12],[3051,117.52375,-19.52361,6.12,1.26],[3052,117.3975,-33.28889,5.6,1.61],[3053,117.98625,19.32528,5.99,1.13],[3054,117.73,-11.12861,6.16,1.13],[3055,117.30958,-46.37333,4.11,-0.18],[3056,117.08,-56.47111,6.33,1.01],[3057,117.3675,-44.75194,6.32,0.96],[3058,117.30375,-46.85778,5.84,-0.14],[3059,117.925,1.76694,5.14,-0.12],[3060,117.75,-24.52833,6.45,-0.01],[3061,118.03,3.27722,6.31,1.59],[3062,117.27792,-56.41056,5.59,1.13],[3063,117.92042,-12.81944,6.36,0.39],[3064,117.94292,-13.89806,5.17,0.6],[3065,118.62208,47.38611,6.25,1.15],[3066,118.67792,47.56472,5.45,1.46],[3067,118.37417,26.76583,4.97,0.09],[3068,117.92875,-21.17389,5.63,0.96],[3069,117.67667,-44.57972,6.45,0.9],[3070,117.30375,-60.28361,5.78,0.42],[3071,117.59958,-50.50972,5.91,1.09],[3072,118.19958,-5.42806,5.76,0.41],[3073,118.07875,-14.84639,5.69,0.37],[3074,117.83542,-43.09556,6.32,-0.17],[3075,120.04875,73.91806,5.41,1.42],[3076,117.47833,-60.05111,6.72,1.24],[3077,119.11167,56.50444,6.72,0.6],[3078,117.91792,-42.88833,6.04,-0.18],[3079,118.06542,-34.70528,5.01,0.44],[3080,118.05417,-40.57583,3.73,1.04],[3081,117.42083,-66.19583,5.79,-0.04],[3082,121.19625,79.47972,5.42,-0.06],[3083,118.92,35.41278,6.23,0.28],[3084,118.16125,-38.86306,4.49,-0.19],[3085,118.26458,-36.36389,5.43,1.16],[3086,118.91625,19.88389,5.35,-0.04],[3087,118.88083,8.86278,5.86,0.35],[3088,118.12375,-54.36722,5.7,-0.15],[3089,118.26542,-49.61306,4.63,-0.23],[3090,118.32583,-48.10306,4.24,-0.14],[3091,118.54583,-35.8775,5.49,-0.19],[3092,118.66625,-34.84694,6.15,1.53],[3093,119.09958,4.48583,6.17,0.98],[3094,119.56917,43.9775,6.34,1.06],[3095,119.2475,15.79028,5.78,1.28],[3096,118.80708,-30.9175,6.44,0.6],[3097,119.31625,8.64139,6.05,1.0],[3098,119.3175,1.12694,6.35,0.5],[3099,119.095,-30.28528,6.33,1.66],[3100,118.75208,-52.58306,6.38,-0.01],[3101,118.94375,-43.845,6.02,-0.11],[3102,119.21458,-22.88,4.2,0.72],[3103,119.52417,7.21361,6.41,-0.04],[3104,119.63125,16.51861,5.99,1.47],[3105,118.72167,-57.30306,5.63,1.3],[3106,120.33625,59.0475,5.77,0.39],[3107,119.10125,-40.73639,6.78,-0.18],[3108,124.22417,84.05778,6.49,0.03],[3109,120.42667,60.32444,6.01,0.14],[3110,119.58583,2.22472,5.29,0.92],[3111,119.24125,-42.40611,6.09,1.36],[3112,120.62833,63.09028,6.4,0.59],[3113,119.41708,-30.33472,4.79,0.15],[3114,119.24125,-43.50028,5.35,-0.18],[3115,119.89625,13.24222,6.02,1.32],[3116,119.32667,-44.10972,5.09,-0.17],[3117,119.19458,-52.98222,3.47,-0.18],[3118,119.33333,-47.89028,6.22,-0.1],[3119,120.64917,57.27361,6.49,0.62],[3120,119.0775,-60.52639,5.74,1.55],[3121,119.46583,-45.57778,5.17,1.27],[3122,119.93375,-3.67972,4.93,1.21],[3123,119.77375,-23.31056,5.11,1.12],[3124,120.23292,25.39278,5.83,1.02],[3125,120.2,19.81611,6.25,1.11],[3126,119.21125,-59.12639,6.25,2.08],[3127,120.25292,23.58306,6.34,0.6],[3128,120.19708,17.30861,5.55,1.32],[3129,119.56,-49.245,4.41,-0.17],[3130,120.47958,35.41306,6.34,1.56],[3131,119.96667,-18.39917,4.61,0.08],[3132,120.4325,25.08972,6.31,0.01],[3133,119.58917,-51.44861,6.44,0.25],[3134,120.37625,16.45528,5.99,-0.02],[3135,120.18375,-2.88167,6.51,-0.07],[3136,120.3075,4.87972,5.65,0.0],[3137,119.7575,-45.21611,5.99,-0.14],[3138,119.44542,-60.30333,5.6,0.57],[3139,119.30208,-63.29694,6.14,-0.1],[3140,119.86833,-39.29722,5.24,0.39],[3141,120.30542,-1.3925,4.68,1.49],[3142,119.80125,-49.97667,6.32,0.6],[3143,119.80583,-49.97361,6.34,0.6],[3144,120.46125,8.91389,6.22,0.57],[3145,120.56625,2.33444,4.39,1.25],[3146,120.08167,-45.45694,6.61,1.27],[3147,119.71042,-60.82444,5.81,-0.09],[3148,120.06208,-48.98139,6.02,0.04],[3149,120.87958,27.79417,4.94,1.12],[3150,120.60833,-6.33722,6.33,0.62],[3151,120.12042,-48.87139,6.12,-0.11],[3152,119.9175,-60.2075,6.33,-0.06],[3153,119.90667,-60.58694,5.17,1.74],[3154,120.40583,-37.28361,5.95,0.14],[3155,120.52583,-37.05056,6.34,1.62],[3156,120.20792,-54.15139,5.87,-0.14],[3157,120.34542,-54.515,6.1,-0.04],[3158,121.18833,18.84222,6.15,-0.05],[3159,120.08333,-63.5675,4.82,-0.17],[3160,120.76708,-32.46389,5.82,1.22],[3161,120.38125,-55.455,6.28,-0.15],[3162,120.68667,-41.31,5.52,-0.15],[3163,121.26875,13.11806,5.12,0.01],[3164,121.40417,27.52972,6.21,0.01],[3165,120.89625,-40.00333,2.25,-0.26],[3166,120.87292,-42.94861,6.29,1.01],[3167,121.79125,43.26028,6.26,-0.02],[3168,121.17292,-19.72806,6.13,-0.16],[3169,121.57667,22.63556,5.99,1.66],[3170,121.0675,-32.675,5.31,1.91],[3171,119.81708,-73.24472,6.34,0.14],[3172,121.45667,-0.57361,6.41,1.05],[3173,122.11417,51.50667,4.84,0.05],[3174,121.61458,-9.245,6.23,-0.07],[3175,122.51583,58.24806,5.93,1.39],[3176,121.94083,21.58167,5.3,0.63],[3177,121.43708,-33.56917,6.14,1.11],[3178,121.17667,-50.59056,5.95,1.21],[3179,121.335,-46.97889,6.19,-0.15],[3180,121.26667,-53.10806,5.53,1.34],[3181,122.34625,42.43056,6.27,1.27],[3182,123.20333,68.47417,5.32,1.04],[3183,121.825,-20.55472,5.38,0.1],[3184,122.17667,13.64083,6.27,0.43],[3185,121.88583,-24.30417,2.81,0.43],[3186,121.17875,-62.83611,6.3,-0.1],[3187,121.66833,-45.26639,5.05,1.5],[3188,122.14833,-2.98389,4.34,0.97],[3189,122.23708,-11.33972,6.32,0.01],[3190,122.18125,-20.36306,6.36,0.16],[3191,122.61333,25.50722,5.73,0.81],[3192,122.25667,-19.245,4.4,-0.15],[3193,122.84,38.73139,6.58,0.59],[3194,122.36875,-16.24889,5.68,-0.17],[3195,122.15667,-37.68139,6.37,-0.04],[3196,122.27792,-30.3225,6.65,1.4],[3197,126.13708,82.43083,6.32,0.01],[3198,122.745,14.62944,6.23,0.02],[3199,122.2925,-35.455,6.2,0.89],[3200,123.45917,56.45222,5.85,1.01],[3201,122.81917,9.82111,6.07,-0.1],[3202,122.66583,-13.79917,5.54,0.49],[3203,122.29,-48.68444,5.7,-0.12],[3204,122.39958,-44.12278,5.21,-0.19],[3205,122.44875,-42.64056,6.26,-0.04],[3206,122.37208,-47.34583,4.27,-0.23],[3207,122.38333,-47.33667,1.78,-0.22],[3208,123.05292,17.64778,5.63,0.54],[3209,123.05292,17.64778,6.02,0.54],[3210,123.05542,17.64778,6.2,0.6],[3211,122.81792,-12.92694,4.72,0.95],[3212,122.8875,-7.7725,5.36,0.89],[3213,122.43,-47.9375,5.23,-0.21],[3214,123.09208,14.00389,6.54,0.6],[3215,123.28708,29.65667,5.64,-0.07],[3216,124.88417,75.75694,5.54,0.9],[3217,122.10208,-63.80111,6.28,-0.06],[3218,122.39,-56.08556,5.66,0.21],[3219,122.75667,-37.29222,6.44,-0.01],[3220,122.25292,-61.3025,4.76,0.43],[3221,123.96042,60.38056,6.45,0.2],[3222,123.24917,16.51417,6.01,0.89],[3223,121.9825,-68.61722,4.35,-0.11],[3224,123.42375,23.13778,6.56,0.11],[3225,122.83958,-39.61861,4.45,1.62],[3226,122.85792,-42.98722,4.75,0.18],[3227,122.79625,-48.46194,5.82,-0.15],[3228,123.54625,17.67583,6.47,0.3],[3229,123.33333,-15.78833,4.99,1.07],[3230,123.19167,-29.91083,6.52,0.06],[3231,123.5875,13.04833,6.38,0.6],[3232,123.0,-46.64417,5.76,0.59],[3233,123.21458,-37.92444,6.43,0.1],[3234,123.12875,-46.26417,6.03,-0.11],[3235,124.46,59.57111,5.64,0.16],[3236,125.16792,72.40722,5.98,1.54],[3237,123.37333,-35.89972,4.78,-0.11],[3238,123.42125,-33.56917,6.37,1.14],[3239,123.54583,-32.14083,6.06,-0.16],[3240,123.49333,-36.3225,5.08,-0.19],[3241,123.495,-36.34111,6.11,-0.18],[3242,123.555,-35.49056,5.78,1.02],[3243,123.51208,-40.34806,4.44,1.17],[3244,123.40083,-46.99194,5.13,-0.14],[3245,124.82208,62.50722,5.71,0.89],[3246,124.56583,54.14361,6.27,1.54],[3247,123.39208,-50.19611,5.51,1.61],[3248,124.14125,11.72639,7.13,1.56],[3249,124.12875,9.18556,3.52,1.48],[3250,123.59958,-45.83444,5.83,-0.2],[3251,123.96875,-30.92583,6.21,0.78],[3252,124.38208,8.86611,6.29,0.98],[3253,123.99542,-35.90278,6.16,1.55],[3254,125.10875,57.74333,5.89,0.39],[3255,124.22583,-21.32028,6.6,0.02],[3256,123.84708,-50.44944,6.44,1.49],[3257,124.34625,-16.285,6.16,0.01],[3258,125.12125,53.57444,6.49,0.28],[3259,124.59958,-12.63194,5.98,0.76],[3260,123.81625,-62.91583,5.16,0.09],[3261,124.49292,-30.00333,6.45,1.04],[3262,125.01625,27.21778,5.14,0.47],[3263,125.68375,60.63111,6.41,0.97],[3264,125.0875,20.74778,5.83,1.13],[3265,124.81292,-10.16583,6.32,0.33],[3266,124.5725,-35.45167,5.58,1.24],[3267,124.5525,-37.37417,6.7,0.23],[3268,125.13375,24.02222,5.98,-0.03],[3269,124.95792,3.94778,6.05,0.97],[3270,124.63875,-36.65944,4.45,0.22],[3271,125.05458,-0.90944,6.18,0.6],[3272,125.07125,-5.32917,6.13,1.33],[3273,124.8725,-34.59028,6.43,-0.08],[3274,124.4825,-59.16694,6.42,0.39],[3275,125.70875,43.18806,4.25,1.55],[3276,125.11417,-22.92472,6.13,1.04],[3277,125.95208,53.21972,5.51,0.11],[3278,125.33417,-1.60222,6.5,0.02],[3279,125.33833,-20.07917,5.58,0.77],[3280,124.57875,-65.61333,5.07,1.15],[3281,125.4775,-17.58639,5.75,1.05],[3282,125.34583,-33.05444,4.83,1.45],[3283,125.3375,-36.48444,5.2,-0.19],[3284,125.84083,18.33222,5.95,0.17],[3285,125.62583,-6.17917,6.15,0.22],[3286,125.35083,-39.62083,6.16,0.17],[3287,126.17833,42.005,6.02,1.59],[3288,125.725,-7.54333,5.96,1.67],[3289,125.695,-13.05472,6.11,1.0],[3290,125.98,10.63194,6.08,1.49],[3291,125.70792,-26.34806,5.9,0.37],[3292,126.27042,35.01139,6.06,1.27],[3293,125.3,-57.97306,5.97,-0.09],[3294,125.63167,-48.49028,4.82,-0.15],[3295,126.15167,-4.71694,6.01,0.46],[3296,125.82167,-38.28583,6.32,1.63],[3297,126.14583,-3.75111,5.61,0.46],[3298,125.28208,-64.10611,6.12,0.93],[3299,126.45792,17.04611,6.14,0.41],[3300,125.73,-52.12389,5.85,0.01],[3301,124.95417,-71.515,5.37,-0.06],[3302,125.00292,-71.50528,5.65,-0.1],[3303,127.4425,67.2975,5.88,0.97],[3304,126.61542,27.89361,5.57,1.4],[3305,126.39792,2.10222,5.73,1.53],[3306,126.47833,7.56444,5.13,0.94],[3307,125.62833,-59.50972,1.86,1.28],[3308,126.22958,-23.15361,5.68,0.06],[3309,126.90333,45.65306,6.32,0.62],[3310,126.695,26.93444,6.32,0.18],[3311,126.69583,26.93528,6.3,0.18],[3312,126.66583,24.53417,7.02,0.32],[3313,126.66708,24.53528,7.81,0.52],[3314,126.415,-3.90639,3.9,-0.02],[3315,126.26542,-24.04611,5.28,1.48],[3316,126.32958,-21.04583,6.01,0.4],[3317,126.41417,-17.43944,6.44,0.6],[3318,124.63167,-76.91972,4.07,0.39],[3319,126.68292,12.65444,5.5,1.6],[3320,126.48167,-14.92972,5.98,0.17],[3321,126.61333,-3.9875,5.59,0.22],[3322,126.23833,-42.76944,5.98,-0.17],[3323,127.56625,60.71806,3.36,0.84],[3324,126.67458,-12.53444,5.54,1.19],[3325,126.82167,-6.40972,6.59,0.51],[3326,126.46625,-42.15333,5.47,-0.15],[3327,126.57333,-39.05944,6.53,-0.09],[3328,126.57583,-39.06028,7.25,0.08],[3329,127.15333,24.14472,6.1,0.24],[3330,126.37917,-51.72806,5.17,-0.16],[3331,126.71167,-29.21528,6.73,-0.05],[3332,128.2225,69.32,6.31,1.35],[3333,127.15542,14.21083,5.95,0.19],[3334,125.51833,-73.4,5.29,0.01],[3335,126.88875,-20.84389,6.56,0.04],[3336,126.81833,-31.67306,6.33,0.9],[3337,127.12167,-2.51722,6.39,0.33],[3338,127.08208,-8.81611,6.43,1.2],[3339,126.97292,-26.1325,6.62,0.11],[3340,125.16042,-77.48444,4.35,1.16],[3341,126.605,-52.8075,6.05,0.06],[3342,127.21208,-9.74833,6.0,0.42],[3343,126.99708,-35.11389,5.75,-0.15],[3344,127.14958,-23.07167,6.51,0.05],[3345,127.22208,-20.95028,6.67,-0.06],[3346,126.465,-64.60083,5.97,0.96],[3347,126.43417,-66.13694,3.77,1.13],[3348,127.83292,37.26583,6.18,-0.03],[3349,126.86417,-55.01167,6.53,-0.02],[3350,126.90208,-53.08861,5.09,0.25],[3351,128.13958,53.11472,6.24,1.01],[3352,129.20292,74.72361,6.31,0.6],[3353,127.365,-27.3325,6.7,-0.13],[3354,128.65083,65.145,5.47,0.18],[3355,127.87708,24.08111,5.75,0.28],[3356,127.28167,-44.16056,5.79,-0.16],[3357,127.89875,18.09444,5.35,1.56],[3358,127.26958,-47.92917,5.33,-0.14],[3359,127.365,-44.725,4.99,-0.16],[3360,128.22917,38.01639,5.9,1.11],[3361,127.9775,9.81444,6.83,-0.07],[3362,127.61917,-32.15944,5.65,1.49],[3363,127.44,-46.33194,5.99,-0.15],[3364,127.62333,-36.72111,6.69,1.95],[3365,128.34083,36.43639,6.24,0.36],[3366,128.17708,20.44111,5.33,1.25],[3367,127.87875,-19.5775,5.42,-0.06],[3368,127.40125,-55.19111,6.36,0.8],[3369,128.25042,24.08472,6.36,1.02],[3370,126.82042,-70.09333,5.53,-0.03],[3371,127.66333,-44.73722,6.3,-0.02],[3372,128.16625,10.06611,6.46,-0.02],[3373,127.8525,-39.06417,6.31,-0.15],[3374,128.13875,-15.02944,6.38,0.27],[3375,127.795,-47.86667,6.39,-0.15],[3376,128.43792,13.25722,6.28,1.17],[3377,128.6825,36.41944,5.78,0.04],[3378,128.43125,4.75667,5.87,1.07],[3379,129.9275,73.62972,6.15,1.02],[3380,128.55542,8.45194,6.03,0.33],[3381,128.27,-24.60639,6.19,0.27],[3382,127.87333,-54.39417,6.34,1.31],[3383,128.50667,-2.15167,5.81,0.0],[3384,128.21458,-31.50083,6.38,0.79],[3385,128.24417,-34.63389,6.36,0.95],[3386,128.02,-53.21222,5.69,0.58],[3387,128.83083,19.59,6.58,0.68],[3388,128.33292,-38.37111,6.49,-0.18],[3389,128.40958,-38.84889,5.96,-0.14],[3390,128.37667,-46.97111,6.24,1.56],[3391,129.79875,65.02083,5.64,0.62],[3392,128.85375,2.74361,6.33,1.02],[3393,126.0825,-80.91417,5.69,1.02],[3394,129.03208,15.31361,6.32,0.6],[3395,128.9625,6.62,5.99,0.52],[3396,128.96375,6.6225,7.25,0.71],[3397,128.63208,-32.59861,6.43,0.75],[3398,128.8675,-7.98222,5.72,-0.03],[3399,128.62208,-37.61139,6.3,1.56],[3400,129.5925,53.40139,5.66,0.96],[3401,129.7925,59.93944,6.48,0.6],[3402,128.87,-26.84361,5.96,0.37],[3403,130.05333,64.32778,4.6,1.17],[3404,128.8025,-39.97,6.47,0.58],[3405,129.75,52.925,6.42,0.6],[3406,129.27417,9.65556,5.88,0.09],[3407,128.68167,-49.94417,5.01,1.33],[3408,129.82333,52.71167,5.91,1.17],[3409,129.57917,32.80194,5.94,1.12],[3410,129.41417,5.70361,4.16,0.0],[3411,129.36292,-4.93361,6.19,1.05],[3412,129.52167,9.57472,6.53,-0.02],[3413,128.96708,-50.97,5.8,-0.13],[3414,128.83167,-58.00917,4.86,1.0],[3415,128.81417,-58.225,5.26,-0.14],[3416,129.58458,-6.6625,6.51,0.44],[3417,128.17583,-73.35667,6.12,0.95],[3418,129.68917,3.34139,4.44,1.21],[3419,129.37375,-33.74583,6.48,0.33],[3420,129.4675,-26.255,5.27,-0.04],[3421,129.33292,-40.1475,6.55,0.6],[3422,130.25458,45.83389,5.37,0.99],[3423,130.07625,31.94194,6.1,0.6],[3424,129.8525,8.01722,6.45,1.08],[3425,129.66792,-19.73694,6.33,1.59],[3426,129.41125,-42.98917,4.14,0.11],[3427,130.02667,20.00778,6.39,0.98],[3428,130.09208,19.67,6.44,1.02],[3429,130.1125,19.545,6.3,0.17],[3430,129.78333,-22.66194,5.05,0.73],[3431,130.00625,-12.47528,4.98,1.42],[3432,129.32833,-62.85361,5.47,1.02],[3433,129.92708,-29.56111,4.89,0.9],[3434,129.84208,-36.60694,6.13,0.42],[3435,129.68708,-53.09056,6.47,-0.1],[3436,130.75083,46.90111,6.22,0.96],[3437,130.25667,-9.05194,6.63,-0.02],[3438,130.02583,-35.30833,3.97,0.94],[3439,130.08042,-40.26417,5.2,-0.01],[3440,129.84917,-53.43972,5.48,-0.16],[3441,130.43042,-15.94333,4.88,1.06],[3442,129.99,-53.055,5.19,-0.14],[3443,129.805,-60.31722,6.36,0.0],[3444,130.14708,-45.19139,5.71,1.65],[3445,130.15667,-46.64889,3.84,0.71],[3446,130.54083,-11.96611,6.45,0.16],[3447,130.07333,-52.92194,3.62,-0.18],[3448,130.0725,-53.01528,5.61,-0.14],[3449,130.82125,21.46861,4.66,0.02],[3450,130.80125,12.68083,5.64,0.39],[3451,131.04208,36.91806,6.33,0.6],[3452,130.30458,-47.31694,4.77,0.12],[3453,130.27208,-48.9225,5.9,-0.21],[3454,130.80625,3.39861,4.3,-0.2],[3455,130.18167,-57.54528,6.34,0.2],[3456,130.48708,-45.41083,5.23,0.21],[3457,130.15417,-59.76111,4.33,-0.11],[3458,130.99875,4.33472,6.37,-0.06],[3459,130.91833,-7.23361,4.62,0.84],[3460,129.77167,-70.38694,5.2,0.01],[3461,131.17125,18.15417,3.94,1.08],[3462,130.56667,-48.09917,5.51,-0.18],[3463,130.7375,-35.94333,6.42,0.02],[3464,131.33917,30.69778,6.13,0.94],[3465,131.1875,10.08167,5.66,-0.11],[3466,130.57875,-53.1,5.52,-0.15],[3467,130.60625,-53.11389,4.86,-0.17],[3468,130.89792,-33.18639,3.68,-0.18],[3469,131.25542,5.68056,6.13,0.2],[3470,132.20583,66.70806,6.2,-0.11],[3471,130.58708,-55.77417,6.29,1.18],[3472,131.33667,-2.60083,6.41,0.52],[3473,131.23,-21.16778,6.11,0.22],[3474,131.66667,28.76528,6.57,0.04],[3475,131.67417,28.76,4.02,1.01],[3476,130.91792,-49.82278,5.16,-0.2],[3477,131.1,-42.64917,4.07,0.87],[3478,131.51042,-2.04889,5.7,1.1],[3479,131.21625,-37.14722,5.76,-0.15],[3480,131.52875,-11.00639,6.25,1.61],[3481,131.73333,12.11,5.87,0.11],[3482,131.69417,6.41889,3.38,0.68],[3483,131.45542,-25.3875,6.1,0.08],[3484,131.59375,-13.54778,4.32,0.9],[3485,131.17583,-54.70833,1.96,0.04],[3486,131.8125,-1.89722,5.29,0.04],[3487,131.50708,-46.04167,3.91,0.0],[3488,131.59917,-41.12556,6.21,-0.05],[3489,131.27208,-58.725,6.21,-0.09],[3490,131.705,-34.62278,6.37,-0.13],[3491,130.97625,-68.21167,6.32,1.5],[3492,132.10833,5.83778,4.36,-0.04],[3493,132.02042,-6.55861,6.09,1.28],[3494,131.6275,-45.91278,5.46,0.27],[3495,131.125,-65.82556,6.05,0.2],[3496,131.82875,-46.15556,5.75,0.56],[3497,131.91875,-41.73722,6.36,0.58],[3498,131.67708,-56.76972,4.49,-0.17],[3499,132.63417,33.28528,6.25,0.49],[3500,132.34042,-3.44306,5.31,-0.09],[3501,132.03667,-42.46389,6.43,-0.2],[3502,130.33125,-78.96333,5.47,-0.1],[3503,132.00083,-52.85056,6.3,-0.1],[3504,132.68792,18.83222,6.16,-0.01],[3505,133.34417,61.96222,5.73,0.28],[3506,133.27458,59.05611,6.25,0.6],[3507,132.43708,-21.04861,6.47,0.23],[3508,132.98667,43.72667,5.15,0.98],[3509,133.04833,45.3125,5.99,1.26],[3510,132.75625,15.35056,6.38,0.64],[3511,133.04167,42.0025,5.99,1.25],[3512,132.46458,-32.78056,5.21,0.87],[3513,132.50917,-29.46306,5.87,0.95],[3514,132.41333,-40.32056,5.48,0.06],[3516,132.59,-28.61806,6.17,-0.08],[3517,132.46833,-39.14167,6.39,0.23],[3518,132.63292,-27.71,4.01,1.27],[3519,133.14417,32.47417,5.66,0.22],[3520,132.44875,-45.30806,4.93,0.05],[3521,133.11917,28.25917,6.23,1.62],[3522,133.14917,28.33083,5.95,0.87],[3523,132.89375,-7.17722,5.54,0.15],[3524,130.80167,-79.06972,6.05,0.23],[3525,132.5875,-42.09,6.0,-0.1],[3526,133.10083,5.34,6.33,0.12],[3527,132.63958,-46.52917,5.1,-0.21],[3528,133.48208,35.53833,6.14,0.04],[3529,133.12792,-13.23361,6.13,1.14],[3530,132.86625,-42.50444,6.55,0.04],[3531,134.15625,64.60389,5.58,0.86],[3532,133.56125,30.57944,5.39,1.05],[3533,133.10875,-32.50917,6.5,1.46],[3534,133.16083,-36.54556,6.42,0.56],[3535,133.2,-38.72417,5.82,1.5],[3536,132.9025,-57.63361,5.59,-0.11],[3537,132.645,-66.79306,5.35,0.42],[3538,133.57458,-5.43444,6.0,0.67],[3539,133.16083,-48.35917,5.91,-0.15],[3540,133.91542,27.9275,5.22,1.0],[3541,133.84542,17.23139,6.64,3.36],[3542,133.17,-52.12917,6.39,0.0],[3543,131.48,-79.50444,5.79,1.6],[3544,132.45958,-72.55083,6.11,0.2],[3545,134.20833,45.63194,5.74,1.09],[3546,134.12708,40.20167,5.89,0.35],[3547,133.84833,5.94556,3.11,1.0],[3548,133.46125,-40.4475,6.47,0.96],[3549,133.26583,-56.64944,6.03,-0.02],[3550,133.98167,11.62611,5.41,1.46],[3551,133.46125,-47.52083,5.33,0.26],[3552,133.87292,-7.97028,6.91,0.6],[3553,133.87333,-7.97111,6.67,0.22],[3554,133.80167,-18.24139,5.75,1.32],[3555,134.23583,32.91028,5.45,0.12],[3556,133.88125,-27.68194,4.89,0.11],[3557,134.15375,4.23667,6.14,1.0],[3558,134.28417,17.14389,6.17,1.0],[3559,133.98292,-23.81833,6.39,0.16],[3560,133.45292,-60.35417,5.78,-0.08],[3561,134.31208,15.32278,5.2,0.15],[3562,133.83042,-45.04167,6.26,-0.16],[3563,134.49458,30.23361,6.29,0.43],[3564,134.14208,-16.70944,5.96,1.54],[3565,134.39667,15.58139,5.67,0.2],[3566,134.61458,35.8025,6.51,-0.01],[3567,134.425,9.38778,6.19,0.98],[3568,133.725,-58.24,6.38,-0.11],[3569,134.80167,48.04167,3.14,0.19],[3570,133.79958,-54.96556,5.71,0.48],[3571,133.76167,-60.64472,3.84,-0.1],[3572,134.62167,11.85778,4.25,0.14],[3573,134.53417,1.54167,6.59,0.06],[3574,134.08083,-52.72361,4.69,-0.12],[3575,134.88583,32.41861,5.2,0.93],[3576,135.63625,67.62972,4.76,1.53],[3577,134.795,18.13472,6.38,1.55],[3578,134.68292,-16.13278,5.86,0.53],[3579,135.16,41.78278,3.97,0.44],[3580,135.12833,37.60444,6.44,0.6],[3581,138.83833,84.18111,6.33,0.3],[3582,134.24333,-59.22944,4.92,-0.19],[3583,134.4825,-48.57333,5.87,1.07],[3584,134.91625,-19.20806,6.18,0.46],[3585,134.81542,-28.80611,6.25,1.0],[3586,135.41917,39.71333,6.36,0.3],[3587,135.35042,32.2525,5.82,0.0],[3588,134.71792,-47.23472,5.18,0.25],[3589,135.45375,27.90278,6.07,0.6],[3590,135.38083,5.64083,6.07,1.11],[3591,135.0225,-41.25389,4.45,0.65],[3592,136.00167,54.28389,5.75,0.02],[3593,135.0925,-43.17333,6.07,-0.16],[3594,135.90625,47.15667,3.6,0.0],[3595,135.68458,24.45278,5.45,-0.04],[3596,135.49167,-0.48278,5.67,1.15],[3597,135.2975,-26.66389,6.2,1.13],[3598,134.85042,-59.08361,5.16,0.42],[3599,135.68667,7.29806,5.85,1.11],[3600,135.33667,-41.86444,5.55,-0.14],[3601,136.04125,27.89833,6.38,0.0],[3602,135.52667,-39.4025,6.27,1.0],[3603,136.35042,48.53028,5.95,0.45],[3604,135.19042,-60.96389,5.79,1.21],[3605,135.43583,-52.18833,5.23,-0.12],[3606,136.23,32.37694,6.46,0.24],[3607,135.78667,-25.50444,6.74,-0.03],[3608,136.67958,59.34444,6.45,0.04],[3609,137.09833,66.87333,5.14,1.51],[3610,135.28542,-68.68389,5.88,1.63],[3611,135.77167,-53.54972,6.4,-0.09],[3612,136.6325,38.45222,4.56,1.04],[3613,136.49333,5.09222,4.97,1.22],[3614,136.03875,-47.09778,3.75,1.2],[3615,135.61167,-66.39611,4.0,0.14],[3616,137.59667,67.13472,4.8,0.49],[3617,136.86208,22.98111,6.4,0.6],[3618,136.74958,1.46278,6.17,1.65],[3619,137.21792,51.60472,4.48,0.27],[3620,137.01708,32.54056,6.5,0.36],[3621,137.00042,29.65417,5.43,0.89],[3622,136.2,-57.8525,6.44,0.25],[3623,136.93667,10.66806,5.24,-0.11],[3624,137.72958,63.51361,4.67,0.35],[3625,137.21292,33.88222,5.93,0.6],[3626,137.19708,26.62917,5.98,0.66],[3627,137.33958,22.04556,5.14,0.97],[3628,137.01208,-25.85833,4.58,1.59],[3629,136.64167,-55.80333,6.11,-0.15],[3630,137.17583,-8.58944,5.6,-0.06],[3631,136.81125,-51.21194,6.73,1.62],[3632,136.53167,-64.49972,6.37,1.35],[3633,138.51333,71.65583,6.55,0.96],[3634,136.99917,-43.4325,2.21,1.66],[3635,137.44333,11.56444,6.48,0.34],[3636,137.29792,-12.35778,5.77,0.94],[3637,137.18125,-26.76778,6.15,0.17],[3638,137.26792,-18.32861,5.73,0.0],[3639,137.66125,30.96306,5.95,1.67],[3640,137.58708,21.99639,6.01,0.9],[3641,137.39792,-8.78778,5.46,1.01],[3642,136.40958,-70.53889,4.71,-0.15],[3643,136.28667,-72.60278,4.48,0.61],[3644,137.485,-30.36528,5.59,0.19],[3645,138.96917,72.94611,5.96,0.18],[3646,137.59583,-23.17667,6.53,0.0],[3647,137.43792,-49.42472,6.48,0.17],[3648,138.58583,61.42333,5.13,0.58],[3649,137.98167,5.46833,6.35,0.32],[3650,138.07333,14.99611,6.51,0.73],[3651,138.05375,3.86722,6.14,-0.01],[3652,138.45083,43.21778,5.32,-0.14],[3653,137.99458,-19.74778,5.73,0.98],[3654,137.76833,-44.86806,5.0,0.23],[3655,138.10833,-7.10972,6.11,0.22],[3656,137.92083,-39.25889,6.0,-0.11],[3657,138.40542,21.28333,6.48,0.02],[3658,137.88833,-46.58389,5.79,-0.21],[3659,137.74167,-58.96694,3.44,-0.19],[3660,138.95792,56.74139,5.27,1.56],[3661,138.1275,-43.61361,5.57,-0.11],[3662,139.04708,54.02194,4.83,0.19],[3663,137.81917,-62.31722,3.97,-0.18],[3664,138.80958,34.63361,5.97,0.86],[3665,138.59125,2.31417,3.88,-0.06],[3666,139.9825,74.01639,6.5,0.6],[3667,138.35792,-38.61639,6.31,0.0],[3668,138.3275,-42.27361,6.29,1.25],[3669,138.8075,14.94139,5.34,1.32],[3670,138.39375,-47.33861,5.92,-0.05],[3672,138.53417,-44.14583,5.85,-0.12],[3673,138.23167,-59.41444,5.54,0.85],[3674,138.60208,-43.2275,5.25,-0.14],[3675,138.85375,-15.02472,6.35,0.02],[3676,139.38,46.81722,5.97,0.04],[3677,138.73833,-37.6025,5.86,0.83],[3678,134.17125,-85.66306,5.42,0.31],[3679,138.575,-55.56972,5.27,0.99],[3680,138.81083,-45.55556,6.25,-0.08],[3681,139.17375,-6.35306,5.24,1.17],[3682,138.90292,-38.57,4.94,1.11],[3683,139.17208,-8.74472,5.47,-0.09],[3684,138.93792,-37.41333,4.62,0.45],[3685,138.3,-69.71722,1.68,0.0],[3686,139.60833,35.36417,5.75,0.6],[3687,139.28125,-14.57361,5.84,1.06],[3688,139.0175,-44.89861,6.04,0.28],[3689,139.46417,11.50111,6.41,0.07],[3690,139.71125,36.8025,3.82,0.06],[3691,138.82292,-58.38861,6.02,-0.1],[3692,139.09625,-44.26583,5.12,1.67],[3693,138.89583,-57.57806,6.32,1.04],[3694,139.23833,-39.40139,5.33,1.17],[3695,138.05125,-76.66306,6.14,1.09],[3696,139.05083,-57.54139,4.34,1.63],[3697,140.1825,51.26611,6.13,0.42],[3698,140.43042,56.69917,5.47,1.61],[3699,139.2725,-59.27528,2.25,0.18],[3700,139.42625,-54.49528,6.33,1.41],[3701,140.24708,38.18833,6.12,0.38],[3702,139.89042,-11.31417,6.62,0.08],[3703,139.52417,-51.05111,5.26,-0.07],[3704,139.88833,-15.83444,5.78,1.29],[3705,140.26375,34.3925,3.13,1.55],[3706,139.94333,-11.975,4.79,0.93],[3707,140.36333,32.90194,6.16,1.09],[3708,139.67583,-51.56056,5.87,0.54],[3709,140.12083,-9.55583,4.8,0.93],[3710,139.94958,-34.10333,6.39,-0.1],[3711,140.31417,15.37111,6.53,-0.01],[3712,139.32167,-68.68944,5.39,0.42],[3713,139.46542,-67.05083,6.11,1.26],[3714,140.23125,-15.61778,6.33,0.46],[3715,140.18417,-31.76056,6.82,0.0],[3716,140.12333,-37.58139,6.05,1.39],[3717,139.88583,-55.18667,6.28,-0.1],[3718,140.37333,-25.96556,4.72,1.63],[3719,141.965,75.09833,6.29,0.07],[3720,139.35583,-74.89444,5.29,0.02],[3721,139.36417,-74.73472,5.86,-0.02],[3722,141.43417,63.94083,6.28,1.46],[3723,140.8825,25.18306,6.41,0.82],[3724,140.71208,-9.83889,6.53,0.22],[3725,141.23208,51.57389,6.31,0.75],[3726,140.46208,-42.195,5.58,1.63],[3727,141.09375,36.58694,6.67,0.21],[3728,140.23667,-62.40472,4.81,0.94],[3729,140.65292,-39.77472,6.54,1.13],[3730,140.6,-46.0475,5.75,0.92],[3731,141.16375,26.18222,4.46,1.23],[3732,140.45833,-55.515,5.63,0.19],[3733,140.80125,-28.83389,4.69,0.92],[3734,140.52833,-55.01083,2.5,-0.18],[3735,140.93667,-37.75722,6.48,0.18],[3736,141.38542,16.58556,6.29,0.97],[3737,141.06792,-39.42583,6.06,0.2],[3738,141.35,-5.1175,5.59,1.52],[3739,140.99792,-51.73722,6.08,0.57],[3740,140.86417,-60.3025,6.3,1.48],[3741,141.59292,-1.46389,6.01,1.32],[3742,141.02375,-61.64889,5.99,1.06],[3743,142.16667,45.60139,5.41,0.98],[3744,141.81083,-9.22361,6.54,0.05],[3745,141.68667,-28.7875,6.1,-0.1],[3746,141.61875,-40.50194,6.2,0.25],[3747,142.44833,55.74556,6.45,0.6],[3748,141.89667,-8.65861,1.98,1.44],[3749,141.82667,-22.34389,4.69,1.14],[3750,141.945,-6.07111,5.38,0.64],[3751,144.27167,81.32639,4.29,1.48],[3752,141.36292,-61.95028,5.77,0.15],[3753,141.57542,-53.37917,5.11,-0.11],[3754,142.11458,9.05667,5.41,0.6],[3755,142.12167,8.18833,5.71,1.04],[3756,141.91,-35.00778,6.65,0.2],[3757,142.88208,63.06194,3.67,0.33],[3758,142.25917,-1.25694,6.27,0.27],[3759,142.28708,-2.76889,4.6,0.46],[3760,142.35208,-2.20528,6.14,0.22],[3761,141.68417,-64.92972,6.05,0.15],[3762,142.385,-4.24722,6.26,0.84],[3763,142.3025,-20.74861,5.66,1.6],[3764,142.68,33.65556,5.85,1.05],[3765,142.31125,-35.95139,4.51,1.44],[3766,142.31792,-38.40389,6.19,0.22],[3767,142.45792,-23.34528,6.24,1.57],[3768,143.72333,72.20556,5.72,0.48],[3769,142.885,35.10306,5.37,1.53],[3770,142.47708,-26.58972,5.48,1.36],[3771,143.62042,69.83028,4.56,0.77],[3772,142.59417,-15.57722,5.85,1.2],[3773,142.93,22.96806,4.31,1.54],[3774,144.02833,74.31778,6.46,-0.1],[3775,143.21417,51.67722,3.17,0.46],[3776,142.19583,-62.27306,5.92,1.1],[3777,141.77667,-71.60222,5.47,1.08],[3778,143.28,49.43861,6.76,0.6],[3779,142.99,9.71583,5.07,1.37],[3780,142.68917,-31.89139,7.0,0.09],[3781,142.69208,-31.88944,6.18,0.03],[3782,142.98625,11.29972,4.97,1.05],[3783,142.1275,-66.70194,5.91,0.01],[3784,142.52167,-51.51722,5.45,-0.1],[3785,142.91208,-10.55222,6.14,0.24],[3786,142.675,-40.46667,3.6,0.36],[3787,142.99542,-1.185,4.57,0.1],[3788,142.9825,-10.37056,6.13,1.19],[3789,142.88417,-31.87194,5.93,0.26],[3790,142.88708,-35.715,5.87,1.29],[3791,143.37625,36.48694,6.18,1.27],[3792,143.32625,28.36806,6.53,0.12],[3793,142.5975,-58.36167,5.88,1.7],[3794,143.1725,1.86417,6.11,0.62],[3795,141.03792,-80.78694,5.36,0.45],[3796,143.085,-19.40028,5.74,0.14],[3797,143.58167,46.90222,6.52,0.22],[3798,143.07708,-28.62806,6.46,0.33],[3799,143.70625,52.05139,4.5,0.01],[3800,143.55583,36.3975,4.55,0.92],[3801,143.25875,-8.50528,6.12,0.98],[3802,143.2325,-13.51694,5.94,1.5],[3803,142.80542,-57.03444,3.13,1.55],[3804,143.49625,23.45389,6.25,1.46],[3805,143.33375,-7.19,6.24,1.17],[3806,144.48417,73.08056,6.42,0.6],[3807,143.08042,-40.64944,5.35,0.9],[3808,143.30208,-21.11583,5.01,1.02],[3809,143.76583,39.62139,4.81,0.99],[3810,143.35917,-22.86389,5.91,0.02],[3811,143.84333,39.96333,6.76,0.35],[3812,143.2825,-39.12889,6.43,0.33],[3813,142.88708,-66.71944,6.27,1.35],[3814,143.63625,-5.915,5.56,1.16],[3815,143.915,35.81028,5.41,0.77],[3816,143.06083,-62.78889,6.1,1.43],[3817,143.43542,-49.005,5.12,-0.12],[3818,143.97042,14.37972,6.36,0.01],[3819,143.53667,-51.25528,5.01,-0.18],[3820,144.17875,31.16167,5.56,1.59],[3821,142.90125,-73.08083,5.47,1.56],[3822,143.89042,-19.58361,6.31,0.06],[3823,143.79917,-35.82389,6.49,0.42],[3824,144.86625,67.27222,5.94,1.52],[3825,143.61125,-59.22944,4.08,0.01],[3826,144.26083,16.43778,5.69,1.25],[3827,144.30292,6.83583,5.0,1.05],[3828,144.14042,-24.70278,6.53,0.38],[3829,144.59042,40.23972,5.25,0.22],[3830,144.25083,-25.29667,5.7,1.12],[3831,144.10542,-48.75139,6.17,0.27],[3832,144.46458,-9.42444,6.4,-0.04],[3833,144.29125,-32.17861,5.63,1.02],[3834,144.61375,4.64917,4.68,1.32],[3835,144.36792,-36.09583,5.98,1.12],[3836,144.20667,-49.35528,4.35,0.17],[3837,144.19292,-52.94417,6.19,1.05],[3838,145.56167,69.2375,5.69,1.14],[3839,145.73833,72.2525,5.17,1.04],[3840,144.30125,-53.66861,5.45,0.15],[3841,144.02125,-64.95056,6.56,0.1],[3842,144.50625,-43.19111,5.5,1.0],[3843,146.37833,78.13472,6.23,1.35],[3844,144.66958,-39.61417,6.7,0.49],[3845,144.96417,-1.14278,3.91,1.32],[3846,144.9475,-10.57028,6.31,-0.03],[3847,146.825,79.13667,6.17,0.25],[3848,145.08375,-10.76917,6.37,0.08],[3849,145.07667,-14.33222,5.06,-0.15],[3850,145.39667,31.27806,5.89,1.6],[3851,145.50125,39.75778,5.62,0.95],[3852,145.2875,9.89222,3.52,0.49],[3853,145.41042,25.91278,6.24,1.25],[3854,145.67958,48.43111,6.39,-0.01],[3855,145.77917,54.36361,6.47,0.13],[3856,144.8375,-61.32806,4.52,-0.07],[3857,145.67792,35.09333,6.14,0.36],[3858,145.32083,-23.59167,4.77,-0.12],[3859,146.15292,64.98389,6.17,0.6],[3860,143.47167,-80.94139,5.11,-0.14],[3861,145.88875,29.97444,5.64,0.12],[3862,145.56,-23.91556,4.94,0.53],[3863,145.17708,-57.98361,5.32,0.2],[3864,145.25917,-57.25972,5.8,1.09],[3865,146.48083,63.65333,6.34,0.31],[3866,145.93292,14.02167,5.35,1.63],[3867,145.6725,-35.50167,6.41,-0.06],[3868,145.44917,-55.21417,6.0,-0.13],[3869,146.125,18.86361,6.5,0.6],[3870,146.63208,57.12806,5.2,1.62],[3871,146.05042,-27.76944,4.79,0.51],[3872,145.86458,-51.22833,6.15,-0.1],[3873,146.46292,23.77417,2.98,0.8],[3874,146.06583,-39.57111,6.82,0.3],[3875,145.92583,-53.89167,5.56,-0.04],[3876,146.54167,6.70861,5.79,1.64],[3877,146.59708,11.81,5.63,1.49],[3878,146.34083,-30.20278,6.45,-0.13],[3879,146.59833,1.78556,5.65,0.34],[3880,146.85792,11.56833,6.45,0.43],[3881,147.1475,46.02111,5.09,0.62],[3882,146.88958,11.42889,6.02,1.3],[3883,146.41875,-57.18556,6.46,-0.11],[3884,146.31167,-62.50778,3.69,1.22],[3885,147.59875,65.59333,6.31,0.28],[3886,146.62667,-44.755,5.55,-0.18],[3887,146.48083,-58.79417,6.22,0.46],[3888,147.7475,59.03861,3.8,0.29],[3889,147.45875,21.17944,6.09,0.25],[3890,146.77542,-65.07194,3.01,0.28],[3891,146.77792,-65.0725,6.26,0.08],[3892,147.36708,-37.18639,5.97,1.24],[3893,147.62542,4.34361,6.24,0.48],[3894,148.02667,54.06444,4.59,0.03],[3895,147.16667,-56.41194,6.06,0.94],[3896,147.75833,13.06611,6.46,1.58],[3897,147.46375,-36.26861,6.37,1.01],[3898,147.48792,-45.73278,5.08,-0.1],[3899,147.80833,-4.24333,6.01,0.17],[3900,147.97083,24.39528,5.32,0.23],[3901,147.84,-6.18167,6.42,0.58],[3902,146.58583,-76.77611,5.45,0.89],[3903,147.86958,-14.84667,4.12,0.92],[3904,147.675,-46.93444,5.73,1.08],[3905,148.19083,26.00694,3.88,1.22],[3906,148.05083,2.45417,6.02,-0.04],[3907,148.05,0.07556,6.35,0.94],[3908,147.99833,-16.53472,6.08,1.04],[3909,148.12667,-8.105,5.05,0.04],[3910,147.8325,-46.19389,5.62,1.17],[3911,148.76417,61.11611,6.27,1.05],[3912,147.92,-46.54778,4.58,1.2],[3913,147.80042,-59.42583,5.79,1.36],[3914,147.73208,-62.74528,5.57,1.32],[3915,148.42875,5.95833,5.95,1.66],[3916,148.24167,-27.33222,6.3,0.62],[3917,148.92917,49.82,5.27,0.07],[3918,149.595,72.87944,5.83,1.14],[3919,148.55125,-25.9325,4.88,1.23],[3920,148.25,-55.37333,6.48,-0.14],[3921,148.63208,-22.48833,6.24,0.04],[3922,149.30667,57.41833,5.93,0.89],[3923,148.7175,-19.00944,4.94,1.57],[3924,148.45917,-51.14694,5.93,-0.15],[3925,148.57333,-45.28389,5.71,-0.11],[3926,149.10833,8.93306,5.85,1.13],[3927,148.71375,-50.24389,5.72,-0.01],[3928,149.42125,41.05556,5.14,0.46],[3929,149.48708,45.41444,6.3,1.11],[3930,149.0225,-40.82472,6.41,1.61],[3931,149.19375,-26.55028,6.28,0.22],[3932,149.14792,-33.41861,5.84,1.2],[3933,149.225,-27.475,6.32,0.17],[3934,152.14292,83.91833,6.37,1.52],[3935,149.09125,-51.33611,6.37,-0.17],[3936,149.60875,27.75889,6.3,0.6],[3937,149.55583,12.44472,5.26,-0.04],[3938,149.53167,8.31417,6.04,1.36],[3939,149.96542,56.81194,5.48,1.46],[3940,149.21583,-54.56778,3.54,-0.08],[3941,149.29542,-52.63889,6.12,-0.13],[3942,149.90083,29.64528,5.73,1.06],[3943,149.42708,-48.41444,6.05,-0.14],[3944,149.04042,-71.38944,6.35,-0.08],[3945,149.92958,3.38472,6.7,0.27],[3946,149.77542,-23.95028,6.21,-0.1],[3947,149.71792,-35.89111,5.23,0.31],[3948,149.31333,-64.48944,6.58,1.13],[3949,149.24875,-69.10194,6.2,-0.1],[3950,150.05333,8.04417,4.7,1.6],[3951,150.25292,31.92361,5.36,0.66],[3952,150.70375,21.94917,5.66,-0.19],[3953,150.14333,-56.94667,6.52,0.98],[3954,151.15125,53.89167,5.74,0.48],[3955,150.41875,-53.36444,6.2,-0.13],[3956,150.70542,-30.5775,6.54,1.19],[3957,150.49167,-57.34972,6.2,1.12],[3958,151.29375,52.37083,6.14,0.6],[3959,150.92083,-9.57389,6.12,1.66],[3960,150.5,-60.42083,5.94,0.26],[3961,151.035,3.20111,6.45,0.4],[3962,150.9225,-25.31667,6.7,0.0],[3963,151.01208,-18.10167,5.86,-0.06],[3964,150.83542,-46.63611,6.12,0.02],[3965,151.0875,-24.28556,5.7,0.3],[3966,150.74958,-60.17861,6.19,0.17],[3967,150.70583,-62.15639,6.42,1.72],[3968,151.0975,-39.97583,6.43,1.3],[3969,151.42042,15.7575,6.37,0.37],[3970,151.28125,-13.06472,4.6,-0.09],[3971,150.89292,-61.88389,6.14,-0.04],[3972,151.31333,-36.38389,6.27,1.11],[3973,151.6975,5.61139,6.21,0.94],[3974,151.8575,35.24472,4.48,0.18],[3975,151.83333,16.76278,3.52,-0.03],[3976,151.54708,-47.37,5.08,0.88],[3977,151.78958,-17.14167,5.6,1.5],[3978,151.52958,-52.18806,6.52,0.98],[3979,152.06625,31.60417,6.24,0.45],[3980,151.97625,9.9975,4.37,1.45],[3981,151.98458,-0.37167,4.49,-0.04],[3982,152.09292,11.96722,1.35,-0.11],[3983,150.18208,-82.21472,5.52,0.03],[3984,152.00708,-37.33361,6.36,0.98],[3985,152.19042,-10.88472,6.53,0.02],[3986,152.14792,-15.61167,6.27,-0.01],[3987,152.74542,40.66139,6.32,1.25],[3988,152.48542,-12.09583,6.24,0.18],[3989,152.53125,-8.40833,5.91,0.02],[3990,152.23458,-51.81111,4.86,-0.12],[3991,152.52458,-12.81611,5.31,0.36],[3992,152.3825,-35.85667,6.13,0.6],[3993,152.80333,37.40194,5.85,1.29],[3994,152.64708,-12.35417,3.61,1.01],[3995,152.1775,-65.81528,5.28,0.98],[3996,152.7325,-8.41833,5.65,1.3],[3997,151.03167,-81.56583,6.6,0.92],[3998,152.90917,13.355,6.44,0.46],[3999,152.34125,-61.54917,5.6,1.6],[4000,152.82417,-7.31667,6.25,0.01],[4001,152.65792,-41.715,5.98,1.24],[4002,152.37708,-68.68333,5.81,0.02],[4003,153.01208,-28.60639,6.28,0.01],[4004,153.20125,4.61472,5.77,1.18],[4005,153.1575,-19.15361,6.44,0.5],[4006,153.4575,27.13583,6.04,0.85],[4007,152.89667,-58.82806,6.4,1.66],[4008,153.78208,59.98556,6.25,1.6],[4009,152.94375,-58.06056,5.72,-0.08],[4010,153.09583,-52.16333,6.16,1.17],[4011,153.33083,-27.02889,6.25,0.31],[4012,153.62375,21.16778,6.02,0.56],[4013,153.35333,-33.03194,6.38,0.59],[4014,153.77625,31.46806,6.46,0.6],[4015,153.44125,-40.34583,5.9,1.21],[4016,154.50458,73.07333,6.4,0.23],[4017,153.345,-51.23333,5.28,0.25],[4018,153.25542,-59.91806,6.1,-0.08],[4019,153.48583,-40.31056,6.35,0.94],[4020,153.36667,-51.75611,5.78,0.14],[4021,154.46083,71.06056,6.66,0.32],[4022,153.33833,-61.65889,6.41,-0.11],[4023,153.68417,-42.12194,3.85,0.05],[4024,154.06,29.31056,5.35,0.01],[4025,153.3775,-66.37306,5.16,0.21],[4026,154.50833,65.10833,5.82,0.12],[4027,154.11708,28.6825,6.49,0.6],[4028,154.06708,17.74028,6.55,0.6],[4029,153.83708,-36.51806,6.19,1.06],[4030,154.13458,23.50306,5.97,0.67],[4031,154.1725,23.41722,3.44,0.31],[4032,154.17458,25.37139,5.84,1.2],[4033,154.27417,42.91444,3.45,0.03],[4034,154.03792,-11.20333,6.08,1.1],[4035,154.16958,13.72833,5.41,1.61],[4036,153.88125,-43.1125,5.6,1.52],[4037,153.43417,-70.03806,3.32,-0.08],[4038,153.81917,-54.97417,6.16,-0.17],[4039,154.31083,23.10611,5.82,0.5],[4040,154.19,-20.67056,6.57,0.48],[4041,154.54292,27.41528,6.52,-0.02],[4042,154.4075,-8.06889,5.24,0.31],[4043,154.01292,-59.90333,6.22,0.2],[4044,154.74583,46.76083,6.43,0.6],[4045,154.1675,-51.205,6.3,1.54],[4046,154.86167,48.39694,6.0,1.02],[4047,155.26417,68.7475,5.96,0.2],[4048,154.75292,24.71167,6.4,0.6],[4049,154.53167,-28.99194,5.34,0.24],[4050,154.27083,-61.33222,3.4,1.54],[4051,155.06167,53.77917,6.45,0.54],[4052,155.13,54.21694,6.0,1.13],[4053,154.6575,-36.80472,6.3,1.28],[4054,154.93375,19.47083,4.79,0.45],[4055,154.82,-12.52806,6.0,0.26],[4056,154.6175,-41.66833,5.96,-0.06],[4057,154.99292,19.84167,2.61,1.15],[4058,154.99417,19.84056,3.8,0.6],[4059,154.88417,-5.10583,6.37,0.9],[4060,154.9975,-9.05889,6.32,0.33],[4061,154.65667,-56.11,5.81,0.48],[4062,157.42292,84.25222,5.5,0.6],[4063,154.90333,-55.02944,4.57,1.62],[4064,155.25833,2.28972,6.66,-0.08],[4065,154.77,-64.67639,5.67,0.05],[4066,155.06958,-47.69917,5.65,1.67],[4067,155.54375,41.22944,5.76,0.54],[4068,155.2825,-17.985,6.51,0.4],[4069,155.58208,41.49944,3.05,1.59],[4070,155.45958,14.97556,6.12,0.02],[4071,155.36958,-23.71083,6.5,0.2],[4072,156.03292,65.56639,4.97,-0.06],[4073,155.4,-22.52833,6.51,0.07],[4074,155.22833,-56.04306,4.5,-0.12],[4075,155.77625,33.90806,5.9,0.14],[4076,155.55375,-19.86694,6.13,0.03],[4077,155.75167,6.5425,6.07,1.12],[4078,155.92417,29.61583,6.39,1.09],[4079,155.81083,5.69417,6.54,0.46],[4080,155.58167,-41.65,4.83,1.12],[4081,156.03583,33.71861,5.5,1.18],[4082,155.86042,-4.07417,5.97,-0.1],[4083,155.80458,-30.16222,6.27,0.31],[4084,157.76917,82.55861,5.26,0.37],[4085,156.05458,2.36806,6.32,1.0],[4086,155.87208,-38.01,5.33,0.25],[4087,155.91833,-41.95333,6.27,0.88],[4088,156.31333,8.78472,5.61,1.62],[4089,155.74208,-66.90167,4.99,-0.13],[4090,156.47875,33.79611,4.74,0.25],[4091,155.96208,-57.95389,6.35,1.52],[4092,156.43458,-7.05972,5.57,1.52],[4093,156.32167,-42.46806,6.18,1.0],[4094,156.5225,-16.83639,3.81,1.48],[4095,156.2475,-58.57639,5.95,0.32],[4096,156.86667,41.60083,6.02,0.17],[4097,156.75208,19.36444,6.15,1.14],[4098,157.01583,48.78472,6.44,0.6],[4099,156.53958,-42.73889,6.13,1.13],[4100,156.97083,36.70722,4.21,0.9],[4101,156.9125,9.7625,6.04,-0.06],[4102,156.09875,-74.03167,4.0,0.35],[4103,157.15208,45.21222,6.35,1.32],[4104,156.78792,-31.06778,4.25,1.45],[4105,156.18458,-73.97167,6.19,0.07],[4106,157.47625,65.62611,6.32,1.14],[4107,156.70417,-54.8775,5.58,1.56],[4108,157.61083,64.2575,6.12,0.16],[4109,157.18333,-3.7425,6.05,0.05],[4110,156.85167,-57.63889,4.66,0.51],[4111,157.00792,-49.40556,6.1,1.51],[4112,157.65667,55.98056,4.84,0.52],[4113,157.52667,38.92528,5.77,0.08],[4114,156.96958,-58.73944,3.82,0.31],[4115,156.85542,-65.70472,6.01,0.09],[4116,157.36958,-2.73917,5.21,-0.06],[4117,157.37083,-29.66361,5.58,1.42],[4118,157.3975,-30.60722,5.56,-0.04],[4119,157.57292,-0.63694,5.09,-0.14],[4120,157.21917,-64.17222,5.29,1.86],[4121,159.00708,80.49444,6.52,0.95],[4122,157.74458,-7.6375,6.2,1.38],[4123,157.74917,-13.58833,5.58,-0.04],[4124,157.96417,32.37944,5.9,0.11],[4125,157.71417,-26.48389,6.51,0.54],[4126,158.77292,75.71306,4.84,0.96],[4127,158.04917,14.13722,5.46,1.68],[4128,157.66333,-61.35611,6.43,1.76],[4129,157.53625,-66.985,6.19,0.01],[4130,157.9525,-28.2375,6.05,0.51],[4131,158.43167,53.4975,6.45,0.6],[4132,158.30792,40.42556,4.75,0.23],[4133,158.20292,9.30667,3.85,-0.14],[4134,157.84083,-53.71556,4.89,0.5],[4135,157.98958,-45.06667,5.74,-0.15],[4136,157.98583,-45.06944,6.09,-0.11],[4137,158.37875,34.98861,5.58,0.02],[4138,157.58375,-71.99306,4.74,0.04],[4139,158.14,-44.61889,5.91,0.92],[4140,158.00583,-61.68528,3.32,-0.09],[4141,158.79042,57.08278,5.16,0.34],[4142,157.75833,-73.22167,4.93,1.68],[4143,158.23708,-47.00333,5.02,1.04],[4144,158.19833,-58.66694,6.0,0.32],[4145,158.50375,-23.74528,5.08,1.6],[4146,158.7,6.95361,5.08,0.94],[4147,158.35542,-58.19028,6.14,0.35],[4148,158.75917,8.65028,5.67,0.05],[4149,158.74042,-23.17611,6.1,0.5],[4150,159.08917,36.32694,6.28,0.39],[4151,158.55375,-60.98778,6.23,1.4],[4152,158.91208,-18.56917,6.49,0.2],[4153,158.80375,-39.56278,5.38,2.88],[4154,158.79375,-43.66472,6.08,0.94],[4155,159.0725,-10.58333,6.57,0.29],[4156,159.06958,-16.34444,6.03,1.65],[4157,159.01917,-26.675,6.29,0.48],[4158,159.135,-12.23028,5.7,0.52],[4159,158.89708,-57.55778,4.45,1.62],[4160,159.29833,-11.74861,6.52,0.29],[4161,157.9625,-81.92111,7.07,-0.08],[4162,159.30708,-27.4125,4.89,1.62],[4163,159.38833,-13.38444,4.82,2.68],[4164,159.08458,-59.56472,5.08,1.18],[4165,159.77375,53.66833,5.52,1.27],[4166,159.68,31.97611,4.71,0.81],[4167,159.32542,-48.22583,3.84,0.3],[4168,159.78167,37.91,5.85,0.57],[4169,159.36167,-58.73333,5.45,0.5],[4170,158.85333,-76.30917,6.3,1.2],[4171,159.64583,-16.87667,4.91,0.92],[4172,159.71,-12.44361,6.04,0.0],[4173,159.51042,-57.25639,5.91,-0.14],[4174,158.86708,-78.60778,4.11,1.58],[4175,159.70958,-42.75361,6.11,0.66],[4176,160.45125,68.44333,5.75,1.3],[4177,159.68792,-59.18306,4.66,1.48],[4178,160.48583,65.71639,5.12,1.2],[4179,159.7475,-58.81694,5.92,1.65],[4180,159.82667,-55.60333,4.28,1.04],[4181,160.76708,69.07611,5.0,1.38],[4182,160.35083,-1.74167,6.26,0.88],[4183,160.215,-35.74167,6.37,0.92],[4184,160.54708,31.69694,6.02,1.62],[4185,160.04708,-65.10056,5.52,-0.16],[4186,159.81917,-74.49361,6.07,1.71],[4187,160.93042,57.19917,5.8,-0.02],[4188,160.32333,-59.67694,6.42,0.08],[4189,160.7575,26.32556,5.51,0.17],[4190,160.63042,-13.975,6.24,1.54],[4191,160.88708,46.20389,5.18,0.33],[4192,160.85417,23.18833,5.08,0.04],[4193,160.83708,4.74778,5.79,1.17],[4194,160.68,-32.71583,5.64,0.0],[4195,161.26667,67.41139,6.0,2.39],[4196,160.55833,-64.46639,4.82,-0.14],[4197,161.06042,19.75861,6.27,0.17],[4198,160.66917,-59.21583,5.38,0.26],[4199,160.73917,-64.39444,2.76,-0.22],[4200,160.88375,-60.56667,4.57,1.71],[4201,161.28917,2.48806,6.28,1.21],[4202,161.59375,57.36583,6.34,1.56],[4203,161.46625,30.68222,5.24,-0.06],[4204,160.96333,-64.24889,5.77,0.0],[4205,161.02875,-63.96111,4.82,-0.13],[4206,160.46375,-79.78333,5.97,-0.07],[4207,161.52375,6.37306,6.37,1.12],[4208,161.60208,18.89139,5.49,1.12],[4209,161.60542,14.19472,5.48,0.91],[4210,161.265,-59.68417,6.21,0.61],[4211,161.08083,-70.86,6.26,0.2],[4212,161.13375,-70.855,6.46,0.23],[4213,161.11042,-72.44389,6.27,0.49],[4214,161.71667,-17.29667,5.42,0.11],[4215,162.20833,65.13222,6.39,-0.02],[4216,161.6925,-49.42,2.69,0.9],[4217,161.57,-60.60333,6.25,0.04],[4218,161.90833,-15.26194,6.67,-0.01],[4219,161.56875,-64.515,5.34,-0.1],[4220,161.62375,-64.26333,5.23,-0.07],[4221,161.73958,-56.75722,5.23,-0.08],[4222,161.71333,-64.38333,4.85,-0.15],[4223,162.23833,29.41583,6.15,1.15],[4224,162.16917,-1.95889,5.93,1.6],[4225,162.05917,-31.68833,5.88,0.03],[4226,161.91125,-57.46778,6.36,1.62],[4227,162.31417,10.54528,5.34,0.03],[4228,162.0225,-59.91917,6.0,0.27],[4229,162.32208,-4.02417,6.61,0.22],[4230,162.47375,27.97389,6.04,0.37],[4231,161.31542,-80.46972,5.47,0.95],[4232,162.40625,-16.19361,3.11,1.25],[4233,162.43125,-9.85278,5.86,1.07],[4234,161.445,-80.54028,4.45,-0.19],[4235,162.79625,56.58222,5.67,1.12],[4236,162.84875,59.32,5.58,1.14],[4237,162.57542,-8.89778,5.79,0.16],[4238,162.4875,-34.05806,5.61,0.04],[4239,162.35042,-59.32361,5.91,0.0],[4240,162.7725,-3.0925,5.95,1.48],[4241,163.12833,52.56528,6.65,0.6],[4242,163.13292,52.50361,6.44,1.11],[4243,163.37792,69.85389,5.93,0.99],[4244,163.05708,1.02528,6.38,0.08],[4245,163.15042,-0.20139,6.31,1.5],[4246,163.39375,54.585,5.1,1.36],[4247,163.32792,34.215,3.83,1.04],[4248,163.49458,43.19,4.71,-0.05],[4249,163.35375,-2.25528,6.12,0.9],[4250,163.12875,-57.24056,5.25,0.16],[4251,163.37292,-20.13889,5.24,0.47],[4252,163.38708,-15.44556,6.38,1.16],[4253,163.43208,-2.12917,5.45,0.96],[4254,163.67583,25.49083,6.2,0.28],[4255,163.57417,-13.75806,5.66,0.83],[4256,163.7425,34.03472,5.72,1.01],[4257,163.37333,-58.85333,3.78,0.95],[4258,163.935,33.50694,5.03,1.1],[4259,163.90333,24.74972,4.5,0.01],[4260,163.90542,24.74889,6.3,0.6],[4261,163.79833,-20.665,6.44,1.1],[4262,163.425,-70.72028,5.99,-0.02],[4263,163.75417,-42.25111,6.11,-0.08],[4264,164.06042,42.00833,6.03,1.13],[4265,163.92667,0.73694,5.91,0.42],[4266,163.62333,-61.82667,5.93,1.75],[4267,164.00625,6.18528,5.81,1.45],[4268,163.11458,-79.55944,6.33,1.46],[4269,164.07042,22.35167,6.14,1.55],[4270,164.14333,25.5,6.35,1.03],[4271,163.82167,-60.51694,5.92,1.08],[4272,164.98667,77.77,6.2,0.97],[4273,164.17958,-37.13778,4.6,1.03],[4274,164.28292,-50.765,5.91,0.18],[4275,164.82458,51.88222,6.17,1.38],[4276,164.45167,-59.73194,6.11,1.1],[4277,164.86667,40.43028,5.05,0.61],[4278,164.88667,36.09306,6.0,1.59],[4279,164.31542,-75.09972,6.13,1.53],[4280,165.06125,45.52611,5.47,1.47],[4281,164.92125,11.70583,6.55,0.43],[4282,164.80792,-33.73722,5.71,0.37],[4283,165.10667,51.50194,6.43,0.6],[4284,164.87875,-16.35389,5.89,1.63],[4285,165.08583,42.91139,6.02,0.57],[4286,165.27417,63.42111,6.39,0.16],[4287,164.94375,-18.29889,4.08,1.09],[4288,165.21,39.21222,5.08,0.24],[4289,165.04833,-14.08333,5.88,1.5],[4290,164.80833,-61.32028,6.16,-0.06],[4291,165.14,3.6175,4.84,1.16],[4292,164.9975,-43.80722,5.81,-0.08],[4293,165.03875,-42.22583,4.39,0.11],[4294,165.18667,6.10139,4.99,0.16],[4295,165.46042,56.3825,2.37,-0.02],[4296,165.03583,-51.81778,6.15,0.18],[4297,165.23833,-15.79278,6.34,1.18],[4298,165.17,-31.83944,6.07,0.52],[4299,165.45708,-2.48472,4.74,1.62],[4300,165.5825,20.17972,4.42,0.05],[4301,165.93208,61.75083,1.79,1.07],[4302,165.60167,-26.83139,6.23,0.31],[4303,165.81083,-0.7525,6.14,0.26],[4304,164.80375,-81.55611,6.71,0.55],[4305,165.81208,-11.30361,5.5,0.94],[4306,165.9025,-0.00083,5.95,1.22],[4307,165.81708,-31.96083,6.46,1.62],[4308,165.90208,-13.43444,6.34,0.88],[4309,166.13,38.24139,6.0,0.16],[4310,166.25417,7.33611,4.63,0.33],[4311,166.13,-47.67917,5.67,0.24],[4312,164.8075,-84.59389,6.19,0.11],[4313,166.22583,-35.80472,5.43,0.03],[4314,166.33292,-27.29361,4.94,0.36],[4315,166.39167,-11.08889,6.09,0.3],[4316,166.2675,-49.3925,6.13,-0.02],[4317,166.49,-27.28778,5.71,-0.06],[4318,166.52417,-51.2125,6.3,0.95],[4319,166.72583,1.95556,5.52,0.97],[4320,166.66125,-28.72778,6.77,0.04],[4321,166.61417,-50.95667,6.32,1.16],[4322,166.91542,23.32361,6.46,0.16],[4323,166.62208,-58.67528,6.02,1.24],[4324,166.785,-32.58722,6.59,0.34],[4325,166.635,-62.42417,4.61,1.03],[4326,166.60083,-64.83972,6.41,0.12],[4327,166.81917,-42.63861,5.15,0.03],[4328,166.97667,-30.17472,6.54,0.6],[4329,166.70792,-70.87806,5.57,-0.05],[4330,167.41625,67.21028,6.06,0.22],[4331,167.06583,-29.97278,6.49,0.03],[4332,167.20458,24.65833,5.68,0.06],[4333,167.32958,36.30944,5.74,1.51],[4334,167.18292,-28.08056,5.44,0.07],[4335,167.41583,44.49861,3.01,1.14],[4336,167.41042,43.2075,5.89,1.57],[4337,167.1475,-58.975,3.91,1.23],[4338,167.14167,-61.94722,5.13,0.22],[4339,167.4725,-32.3675,5.81,0.04],[4340,168.04583,68.27194,6.4,0.6],[4341,167.93208,14.40028,6.3,0.21],[4342,167.72792,-58.45528,6.88,-0.08],[4343,167.91458,-22.82583,4.48,0.03],[4344,168.18542,54.89417,6.63,0.6],[4345,168.13417,35.81361,6.41,0.61],[4346,168.06167,-32.43389,6.38,1.7],[4347,168.12667,-18.5,6.13,-0.01],[4348,168.14417,-21.74917,6.4,1.38],[4349,167.87292,-71.43639,6.35,1.37],[4350,168.13792,-49.10111,5.36,0.18],[4351,168.4175,41.08861,6.33,1.15],[4352,168.15,-60.3175,4.6,0.55],[4353,168.23708,-49.73639,6.11,1.06],[4354,168.31125,-44.37222,5.8,1.66],[4355,168.18833,-64.16972,5.23,-0.06],[4356,168.44,-0.06972,5.42,-0.03],[4357,168.52708,20.52361,2.56,0.12],[4358,168.5075,8.06056,5.79,1.12],[4359,168.56,15.42944,3.34,-0.01],[4360,168.41375,-53.23167,5.76,1.32],[4361,168.37833,-59.61944,5.74,-0.1],[4362,168.80083,23.09556,4.63,1.66],[4363,169.01667,52.77306,6.5,0.43],[4364,168.725,-43.73417,6.21,1.61],[4365,168.96625,13.3075,5.32,1.2],[4366,168.99083,12.84472,6.67,0.27],[4367,169.17458,49.47639,5.88,1.08],[4368,169.16542,-3.65167,4.47,0.21],[4369,169.2425,-7.13472,6.14,0.2],[4370,169.11542,-45.88,6.31,0.4],[4371,169.3225,2.01056,5.18,1.52],[4372,169.29917,-38.01444,6.27,0.1],[4373,169.41333,-34.73722,6.45,0.4],[4374,169.54542,31.52917,4.87,0.6],[4375,169.54583,31.52917,4.41,0.59],[4376,169.42917,-36.53444,6.68,0.98],[4377,169.61958,33.09417,3.48,1.4],[4378,169.5875,11.98472,6.66,0.07],[4379,169.32917,-67.82361,6.06,1.76],[4380,169.78292,38.18556,4.78,0.12],[4381,169.72917,1.65056,5.91,1.04],[4382,169.83542,-14.77861,3.56,1.12],[4383,170.22417,67.10056,6.21,1.01],[4384,169.81875,-64.5825,5.99,0.46],[4385,169.64292,-79.66861,6.35,0.26],[4386,170.28417,6.02944,4.05,-0.06],[4387,169.90125,-75.1425,6.27,-0.03],[4388,170.45542,57.075,6.43,0.15],[4389,170.01625,-71.99444,6.41,0.05],[4390,170.25167,-54.49111,3.89,-0.15],[4391,170.71375,64.33056,6.02,0.09],[4392,170.70667,43.48278,4.99,0.99],[4393,170.59625,-44.64583,6.12,0.92],[4394,170.825,0.13167,6.05,1.46],[4395,170.84125,-18.78,5.09,0.42],[4396,170.80292,-36.16472,5.0,1.46],[4397,170.48792,-77.60833,6.43,0.2],[4398,170.78375,-56.77944,5.79,0.01],[4399,170.98125,10.52917,3.94,0.41],[4400,171.00958,1.40778,5.39,0.94],[4401,170.84083,-64.955,5.11,-0.08],[4402,171.1525,-10.85944,4.83,1.56],[4403,171.09208,-42.66917,6.12,-0.18],[4404,171.24542,11.43028,5.8,1.38],[4405,171.22042,-17.68389,4.08,0.21],[4406,171.04625,-72.25667,5.59,0.06],[4407,171.48792,55.85056,5.75,0.6],[4408,171.40167,16.45639,5.57,0.36],[4409,171.3725,-36.06306,5.22,0.99],[4410,171.45833,3.86,6.37,0.33],[4411,171.38792,-37.74778,5.89,1.54],[4412,171.60625,33.45056,6.32,0.43],[4413,171.43,-63.97278,5.17,0.5],[4414,171.68875,3.01306,6.5,0.79],[4415,171.64708,-61.11528,5.3,-0.08],[4416,171.78958,-12.35667,5.94,0.49],[4417,171.69708,-53.16,5.81,0.52],[4418,171.98417,2.85611,4.95,1.0],[4419,171.97375,-1.7,6.25,1.04],[4420,171.99375,-35.32861,6.45,0.6],[4421,172.26917,61.77833,5.83,0.36],[4422,172.2675,39.33694,5.31,0.01],[4423,172.14625,-42.67417,5.08,-0.03],[4424,172.43125,56.7375,6.28,0.15],[4425,172.07583,-72.47444,6.09,0.16],[4426,172.42458,15.41333,5.74,1.32],[4427,172.55375,54.36167,6.41,0.6],[4428,172.41083,-24.46333,5.76,0.07],[4429,172.96,81.12722,6.15,0.24],[4430,172.60417,46.6575,6.35,1.27],[4431,172.62958,43.17333,5.94,0.49],[4432,172.57875,-3.00361,4.77,1.54],[4433,172.62083,18.40972,5.52,1.05],[4434,172.85083,69.33111,3.84,1.62],[4435,172.72042,47.92917,6.42,0.88],[4436,172.7925,48.78917,6.56,0.93],[4437,172.93708,14.36444,6.2,0.57],[4438,172.8125,-61.27833,6.38,0.52],[4439,173.08667,61.0825,5.48,0.5],[4440,172.94833,-20.77667,6.24,0.54],[4441,172.94208,-59.44222,5.13,1.08],[4442,172.95333,-59.51583,5.15,0.49],[4443,173.06708,-29.26333,5.81,0.53],[4444,173.06792,-29.26111,5.64,0.53],[4445,173.09708,-26.74667,6.16,1.66],[4446,173.19792,-7.8275,5.95,1.38],[4447,173.20042,-40.43639,5.64,1.58],[4448,173.08292,-66.96222,5.9,1.13],[4449,173.22542,-31.08722,5.04,1.58],[4450,173.25042,-31.85778,3.54,0.94],[4451,173.31083,-16.28056,6.05,0.6],[4452,173.48458,36.81556,6.4,1.05],[4453,173.40542,-40.58694,5.39,0.12],[4454,173.54167,11.02361,6.55,0.18],[4455,173.59167,3.06,5.77,0.46],[4456,173.67708,16.79694,5.95,-0.16],[4457,173.77042,54.78528,5.63,1.02],[4458,173.62292,-32.83139,5.98,0.81],[4459,173.76583,20.44139,6.45,1.01],[4460,173.69042,-54.26417,4.62,-0.08],[4461,174.01167,69.32278,5.2,1.01],[4462,173.73667,-49.13667,5.5,1.04],[4463,173.805,-47.3725,5.71,1.66],[4464,173.93083,10.91111,6.56,0.13],[4465,174.07458,27.78111,5.8,0.23],[4466,173.98167,-47.64167,5.25,0.25],[4467,173.945,-63.01972,3.13,-0.04],[4468,174.17042,-9.80222,4.7,-0.08],[4469,174.14583,-33.57,5.74,1.02],[4470,174.17,-37.23778,6.31,0.06],[4471,174.23708,-0.82389,4.3,1.0],[4472,174.09292,-61.05222,5.83,-0.09],[4473,174.255,-32.98806,6.29,0.46],[4474,174.47083,50.61833,6.14,1.07],[4475,174.2525,-61.28333,5.15,1.12],[4476,174.39125,-47.74722,5.44,1.24],[4477,174.58583,43.62556,5.59,0.33],[4478,174.54083,8.88417,6.17,1.08],[4479,174.315,-75.89667,5.65,0.35],[4480,174.63958,46.83417,6.1,0.38],[4481,174.705,64.34694,6.46,0.6],[4482,174.63417,33.62556,6.27,1.32],[4483,174.615,8.13417,5.36,1.57],[4484,174.60042,-2.43611,6.22,1.12],[4485,174.45167,-67.62028,5.96,1.02],[4486,174.68708,45.10861,6.44,0.56],[4487,174.53042,-61.82639,5.15,-0.02],[4488,174.66708,-13.20194,5.48,0.52],[4489,174.75167,-24.72111,6.42,0.82],[4490,174.96292,-14.46861,6.21,0.0],[4491,174.96,-16.62028,6.19,1.62],[4492,174.8725,-65.39778,5.17,0.8],[4493,175.11417,57.97056,6.37,-0.12],[4494,175.05333,-34.74472,4.7,-0.07],[4495,175.19625,21.35278,5.26,0.98],[4496,175.2625,34.20167,5.33,0.72],[4497,175.1775,-53.96861,5.96,1.66],[4498,175.285,-29.19639,6.44,0.66],[4499,175.22333,-62.09,4.94,1.15],[4500,175.43167,55.1725,6.27,1.49],[4501,175.39292,31.74611,5.73,0.43],[4502,175.3325,-43.09583,5.55,0.6],[4503,175.43333,-32.49972,5.22,1.48],[4504,175.61833,66.745,5.3,1.28],[4505,175.52167,22.21083,6.59,0.6],[4506,175.51458,-20.29389,6.22,0.95],[4507,175.255,-83.1,6.33,1.08],[4508,175.86333,-37.19028,5.98,1.46],[4509,175.73083,-79.30639,6.39,0.9],[4510,175.97958,-6.67722,6.07,0.96],[4511,175.88,-62.48944,5.03,0.8],[4512,176.055,25.21833,6.02,1.53],[4513,175.97042,-62.87833,6.1,0.06],[4514,176.19083,-18.35083,4.73,0.97],[4515,176.32125,8.25833,4.85,0.18],[4516,176.3025,-49.06972,6.26,1.17],[4517,176.465,6.52944,4.03,1.51],[4518,176.5125,47.77944,3.71,1.18],[4519,176.43292,-45.69,5.29,-0.12],[4520,176.40167,-66.72861,3.64,0.16],[4521,176.73167,55.62833,5.27,1.27],[4522,176.62833,-61.17833,4.11,0.9],[4523,176.62958,-40.50056,4.91,0.66],[4524,176.77917,-35.90694,6.17,0.96],[4525,176.81542,-30.28694,6.48,0.68],[4526,176.82958,-57.69639,5.41,1.67],[4527,176.99625,20.21889,4.53,0.55],[4528,176.97875,8.24583,5.32,0.02],[4529,177.09792,-10.31333,6.26,0.58],[4530,177.05958,-66.81472,4.72,1.54],[4531,177.16125,14.28417,5.88,0.29],[4532,177.18792,-26.74972,5.11,1.6],[4533,177.255,-0.31861,6.15,0.52],[4534,177.265,14.57194,2.14,0.09],[4535,177.31208,16.24278,6.04,0.27],[4536,177.42375,34.93167,5.7,0.46],[4537,177.42125,-63.78833,4.32,-0.15],[4538,177.48583,-70.22583,4.97,1.4],[4539,177.58125,-15.86389,6.13,0.95],[4540,177.67375,1.76472,3.61,0.55],[4541,177.61333,-62.64944,5.7,0.26],[4542,177.65458,-27.27778,6.48,0.98],[4543,177.73042,12.27889,6.35,0.27],[4544,177.75917,-5.33333,5.64,1.06],[4545,177.78917,33.375,6.27,0.32],[4546,177.78625,-45.17361,4.46,1.3],[4547,177.84125,-12.18778,6.35,0.4],[4548,177.92333,-30.835,5.85,0.56],[4549,177.96333,-65.20611,4.9,-0.11],[4550,178.245,37.71861,6.45,0.75],[4551,178.04208,-56.98778,5.57,0.07],[4552,178.2275,-33.90806,4.28,-0.1],[4553,178.36167,-35.06667,6.17,0.08],[4554,178.4575,53.69472,2.44,0.0],[4555,178.45958,0.55194,6.3,0.2],[4556,178.54792,-57.41,6.06,0.05],[4557,178.6075,-37.74889,6.46,0.52],[4558,178.67708,-25.71389,5.3,0.88],[4559,178.76292,8.44389,5.58,0.94],[4560,178.77375,46.47694,6.54,0.1],[4561,178.79667,46.46972,7.03,0.01],[4562,178.80875,36.75639,6.49,1.58],[4563,178.74917,-63.27889,5.91,0.19],[4564,178.91875,15.64667,5.53,0.11],[4565,178.91708,-28.47694,5.93,1.5],[4566,178.99333,56.59861,5.84,1.1],[4567,179.00375,-17.15083,5.18,-0.02],[4568,178.97792,-39.68917,6.13,1.02],[4569,179.22167,61.54917,6.22,0.96],[4570,179.18292,-47.0725,6.26,0.4],[4571,179.26542,-33.31528,6.21,-0.04],[4572,179.31083,40.34361,6.62,0.46],[4573,179.41708,-62.44889,5.57,-0.15],[4574,179.53,32.27389,6.42,0.29],[4575,179.58583,61.46472,6.76,0.6],[4576,179.56333,-56.31722,5.44,-0.08],[4577,179.58458,-40.94722,6.79,0.97],[4578,179.69875,-64.33917,5.61,0.18],[4579,179.72667,-25.90889,6.43,0.03],[4580,179.76417,0.53056,6.17,1.26],[4581,179.82333,33.1675,5.96,1.15],[4582,179.79542,-51.69667,6.05,1.28],[4583,179.90542,-78.22194,4.91,-0.06],[4584,179.98833,34.035,6.5,0.22],[4585,179.98708,3.65528,5.37,0.0],[4586,180.0775,80.85306,6.17,1.61],[4587,180.18542,-10.44611,5.55,0.77],[4588,180.17708,-21.83722,6.28,1.22],[4589,180.21833,6.61417,4.66,0.13],[4590,180.21333,-19.65889,5.26,-0.2],[4591,180.2575,-1.76806,6.31,1.21],[4592,180.37083,-57.50361,6.16,0.0],[4593,180.41458,36.04194,5.59,1.01],[4594,180.52833,43.04556,5.21,0.26],[4595,180.58375,-85.63167,6.05,1.29],[4596,180.61917,-71.48889,6.42,1.17],[4597,180.65708,-69.19222,5.89,-0.08],[4598,180.715,-7.68361,6.22,1.49],[4599,180.75625,-63.31278,4.33,0.27],[4600,180.915,-42.43417,5.15,0.41],[4601,180.93458,-74.21389,6.44,1.23],[4602,181.06917,21.45917,5.87,0.22],[4603,181.08,-63.16556,4.72,-0.08],[4604,181.16125,-68.32917,5.35,-0.01],[4605,181.19375,-76.51917,5.04,1.49],[4606,181.11708,85.58722,6.27,0.59],[4607,181.23792,-60.96917,5.96,1.67],[4608,181.30208,8.73306,4.12,0.98],[4609,181.31292,76.90583,5.8,1.01],[4610,181.41542,62.93306,6.13,1.17],[4611,181.47167,-65.54722,6.33,0.22],[4612,181.48625,-35.69389,6.23,-0.08],[4613,181.49917,-3.13167,6.37,1.0],[4614,181.5825,-68.65083,6.23,1.24],[4615,181.59625,-65.70917,6.06,0.65],[4616,181.72042,-64.61361,4.15,0.34],[4617,181.9575,-75.36694,5.18,1.3],[4618,182.02167,-50.66139,4.47,-0.15],[4619,182.02,-50.76333,6.37,-0.05],[4620,182.06125,-48.69278,5.34,-0.01],[4621,182.08958,-50.7225,2.6,-0.12],[4622,182.1025,-60.84722,6.22,1.74],[4623,182.10333,-24.72889,4.02,0.32],[4624,182.22417,-44.32611,5.75,0.24],[4625,182.22708,-41.23139,5.48,-0.07],[4626,182.42208,1.89778,5.95,1.12],[4627,182.44708,74.66139,6.35,0.5],[4628,182.51042,-34.705,6.17,0.03],[4629,182.51417,5.80694,5.72,0.35],[4630,182.53125,-22.61972,3.0,1.33],[4631,182.64083,-37.87028,6.06,0.2],[4632,182.63167,16.80917,6.39,0.06],[4633,182.69208,27.28139,6.01,0.11],[4634,182.77042,-61.2775,6.08,0.39],[4635,182.76625,-23.6025,5.46,0.06],[4636,182.76208,-45.42278,6.61,1.08],[4637,182.88083,-51.35944,6.23,0.82],[4638,182.91292,-52.36861,3.96,-0.15],[4639,182.75,81.71,6.0,1.62],[4640,182.96333,25.87028,5.66,1.41],[4641,182.93708,57.05444,6.43,0.6],[4642,183.005,28.53611,6.49,0.36],[4643,183.03875,20.54194,5.57,0.95],[4644,183.09167,-62.95083,5.92,0.29],[4645,183.195,-70.15194,6.17,0.84],[4646,183.04958,77.61639,5.14,0.33],[4647,183.30417,-34.12556,6.5,1.66],[4648,183.35542,-38.92917,5.76,-0.13],[4649,183.48208,-78.57361,6.35,1.24],[4650,183.35792,10.26222,5.85,0.27],[4651,183.40292,-33.79278,6.33,0.08],[4652,183.51083,-45.72389,5.31,1.43],[4653,183.57,-64.40861,6.22,0.12],[4654,183.68083,53.43472,6.16,1.04],[4655,183.74833,-20.84417,5.83,1.05],[4656,183.78625,-58.74889,2.8,-0.23],[4657,183.79417,-10.3125,6.11,0.46],[4658,183.87708,-41.91306,6.26,1.01],[4659,183.78542,70.2,5.71,1.19],[4660,183.85667,57.0325,3.31,0.08],[4661,183.94583,-23.35361,6.54,0.45],[4662,183.95167,-17.54194,2.59,-0.11],[4663,184.00083,14.89889,5.1,0.06],[4664,184.09792,-72.61472,6.22,-0.01],[4665,183.9225,72.55083,6.29,1.14],[4666,184.03167,40.66028,5.66,1.55],[4667,184.08542,23.94528,4.95,0.97],[4668,184.12542,33.06139,5.0,1.14],[4669,184.275,-65.69278,6.06,0.03],[4670,184.26375,-16.69361,6.05,0.1],[4671,184.39208,-67.96083,4.11,1.58],[4672,184.37292,53.19111,5.81,1.3],[4673,184.37708,28.93722,5.7,0.16],[4674,184.58625,-79.31222,4.26,-0.12],[4675,184.44708,-36.09389,6.15,0.01],[4676,184.43458,15.14417,6.34,1.37],[4677,184.53792,-3.95444,6.99,0.36],[4678,184.54,-3.94861,6.54,0.33],[4679,184.60875,-64.00306,4.04,-0.17],[4680,184.63167,30.24917,6.23,0.3],[4681,184.66792,-0.78722,5.9,0.17],[4682,184.74875,-55.14306,5.0,1.59],[4683,184.21417,86.43611,6.33,0.43],[4684,184.75875,26.00778,6.48,0.18],[4685,184.82958,23.03472,6.27,0.17],[4686,183.83458,87.7,6.28,0.35],[4687,184.70792,75.16056,5.38,-0.02],[4688,184.87333,28.15694,6.33,0.5],[4689,184.97667,-0.66694,3.89,0.02],[4690,184.95292,48.98417,5.29,1.66],[4691,185.04458,-22.17556,5.97,0.82],[4692,185.11667,-65.84278,6.21,-0.04],[4693,185.08208,26.61944,5.54,1.09],[4694,185.07375,26.00194,6.15,0.3],[4695,185.0875,3.3125,4.96,1.16],[4696,185.14042,-22.21583,5.21,-0.1],[4697,185.17917,17.79278,4.74,1.01],[4698,185.17208,27.05472,7.13,0.36],[4699,185.23208,-13.56556,5.14,1.05],[4700,185.34,-60.40111,3.59,1.42],[4701,185.21167,57.86389,5.55,1.43],[4702,185.48958,-56.37472,5.92,1.57],[4703,185.53042,-67.52194,5.15,0.19],[4704,185.54958,-68.3075,5.74,1.04],[4705,185.545,24.77389,6.19,-0.01],[4706,185.70542,-57.67611,5.39,-0.1],[4707,185.62625,25.84611,4.81,0.49],[4708,185.63333,5.30556,6.4,0.6],[4709,186.40625,-86.15056,6.33,1.08],[4710,185.8075,-67.63167,6.36,0.89],[4711,185.84,-24.84056,5.68,1.16],[4712,185.8975,-35.41278,5.32,-0.08],[4713,185.90375,-39.30306,6.4,0.29],[4714,185.93708,-38.91111,5.79,-0.08],[4715,185.94583,42.54278,6.06,0.33],[4716,186.00625,51.56222,4.8,0.87],[4717,186.07708,26.09861,5.18,0.08],[4718,186.18625,-41.38417,6.25,1.18],[4719,186.11125,25.58278,6.42,0.27],[4720,186.32208,-65.77056,6.3,0.96],[4721,186.28542,-42.51444,6.11,0.65],[4722,186.29875,-11.61028,5.95,0.03],[4723,186.32667,-27.74917,6.09,1.27],[4724,186.34083,-35.18639,5.73,-0.06],[4725,186.31292,23.92611,6.03,1.1],[4726,186.26333,56.7775,5.81,1.62],[4727,186.27667,63.80278,6.32,0.91],[4728,186.46208,39.01861,5.02,0.96],[4729,186.62875,-63.1225,4.86,-0.12],[4730,186.64958,-63.09917,1.33,-0.24],[4731,186.65208,-63.09944,1.73,-0.26],[4732,186.63167,-51.45083,4.82,-0.14],[4733,186.60042,27.26833,4.95,0.27],[4734,186.70083,-48.91333,6.26,0.68],[4735,186.71542,-32.83,5.55,0.01],[4736,186.8525,-63.78917,6.0,0.07],[4737,186.73458,28.26833,4.36,1.13],[4738,186.74708,26.82556,5.0,0.08],[4739,186.86958,-58.99194,5.5,1.45],[4740,186.60083,71.92972,6.24,1.06],[4741,186.92542,8.61028,6.37,0.93],[4742,186.95583,-16.63194,6.35,0.84],[4743,187.01,-50.23056,3.91,-0.19],[4744,187.07875,-64.34139,6.04,0.02],[4745,186.89625,55.71278,5.7,1.55],[4746,186.965,-4.61528,6.22,0.44],[4747,187.10625,-61.79528,6.22,1.26],[4748,187.09375,-39.04139,5.44,-0.08],[4749,187.13917,-56.40778,6.15,0.92],[4750,187.15875,26.22667,6.54,0.17],[4751,187.18583,25.89917,6.65,0.22],[4752,187.22792,25.91278,5.29,-0.05],[4753,187.36208,24.10889,5.48,0.43],[4754,187.47458,-56.52472,5.8,1.56],[4755,187.49125,-41.73611,6.02,1.52],[4756,187.43,20.89611,5.69,0.07],[4757,187.46625,-16.51556,2.95,-0.05],[4758,187.52,-13.39306,6.35,0.56],[4759,187.57292,-23.69667,5.63,1.67],[4760,187.48875,58.40583,5.35,0.2],[4761,187.51208,51.53556,6.21,0.51],[4762,187.51792,58.7675,6.08,0.98],[4763,187.79125,-57.11333,1.63,1.59],[4764,187.81958,-57.08111,6.42,0.16],[4765,187.52792,69.20111,4.95,1.62],[4766,187.7525,24.56722,5.46,0.05],[4767,187.70875,53.07667,6.21,0.54],[4768,187.91792,-59.42389,5.48,0.63],[4769,188.04167,-73.00167,5.88,1.11],[4770,187.83917,7.60417,6.05,1.52],[4771,187.9825,-63.50611,5.95,0.27],[4772,187.91125,-5.0525,6.19,1.04],[4773,188.11667,-72.13306,3.87,-0.15],[4774,188.01875,-32.53361,6.46,0.2],[4775,188.0175,-16.19611,4.31,0.38],[4776,188.15,-13.85917,5.74,0.4],[4777,188.26208,10.29556,6.26,0.95],[4778,188.34333,-19.79194,6.26,0.29],[4779,188.39292,-12.83028,5.58,0.86],[4780,188.3925,24.28306,6.29,0.11],[4781,188.445,-9.45194,5.48,-0.03],[4782,188.49667,-49.90944,6.38,0.46],[4783,188.41208,33.2475,5.42,1.0],[4784,188.4475,33.38472,6.24,1.05],[4785,188.43542,41.3575,4.26,0.59],[4786,188.59667,-23.39667,2.65,0.89],[4787,188.37083,69.78833,3.87,-0.13],[4788,188.67625,-44.67333,5.77,0.7],[4789,188.71292,22.62917,4.81,0.0],[4790,188.87083,-61.84194,6.22,0.73],[4791,188.77625,18.37722,6.56,0.25],[4792,188.7825,18.37694,5.02,1.15],[4793,188.78375,21.88139,5.85,1.22],[4794,188.93958,-41.02194,5.13,0.22],[4795,188.68333,70.02194,4.94,1.31],[4796,189.005,-39.87,5.8,0.01],[4797,188.99417,-20.52722,6.2,0.33],[4798,189.29583,-69.13556,2.69,-0.2],[4799,189.1975,-5.83194,5.87,0.07],[4800,189.09708,59.48694,5.5,0.6],[4801,189.24292,17.08944,5.68,1.41],[4802,189.42583,-48.54111,3.86,0.05],[4803,189.42583,-27.13889,5.45,0.32],[4804,189.81042,-75.36944,6.49,0.08],[4805,189.51833,3.2825,6.33,0.01],[4806,189.71875,-67.19306,6.25,0.06],[4807,189.59333,1.85472,5.71,1.6],[4808,189.625,6.98833,7.08,1.44],[4809,189.68583,-18.25028,6.0,0.29],[4810,189.76458,-30.42222,5.89,1.21],[4811,189.69292,40.87444,6.37,0.18],[4812,189.75875,22.65944,6.38,1.1],[4813,189.81167,-7.99556,4.66,1.23],[4814,189.98167,-66.51167,6.26,-0.05],[4815,189.78042,21.0625,5.46,0.96],[4816,189.82042,35.95194,6.45,0.06],[4817,189.96875,-39.9875,4.64,-0.08],[4818,190.34583,-46.14556,5.84,1.52],[4819,190.37917,-48.95972,2.17,-0.01],[4820,190.52125,-69.4075,6.33,0.78],[4821,190.31667,-13.01361,6.08,0.6],[4822,190.3175,-13.015,5.98,0.42],[4823,190.48583,-59.68583,4.93,-0.04],[4824,190.39333,10.42639,6.19,0.19],[4825,190.415,-1.44944,3.65,0.36],[4826,190.415,-1.44944,3.68,0.6],[4827,190.455,-19.75861,6.03,0.39],[4828,190.47125,10.23556,4.88,0.09],[4829,190.48792,6.80667,5.59,0.0],[4830,190.70958,-63.05861,5.31,0.27],[4831,190.6475,-48.81306,4.66,1.09],[4832,190.70667,-55.94722,6.08,-0.03],[4833,190.39125,62.71306,6.07,0.01],[4834,190.78792,-56.17611,6.0,-0.08],[4835,190.8675,-58.90306,6.4,1.09],[4836,190.85958,-40.17778,6.44,0.25],[4837,190.90875,-1.57694,5.93,0.86],[4838,190.99458,-36.34917,6.39,-0.06],[4839,191.00208,-28.32389,5.48,1.34],[4840,190.7675,61.15556,6.38,1.26],[4841,191.25708,-68.83111,6.16,0.69],[4842,191.40792,-60.98111,4.69,1.05],[4843,191.11292,44.10306,6.33,0.43],[4844,191.57042,-68.10806,3.05,-0.18],[4845,191.24792,39.27889,5.95,0.55],[4846,191.2825,45.44028,4.99,2.54],[4847,191.40458,7.67333,5.22,0.33],[4848,191.59458,-56.48889,4.65,-0.16],[4849,191.59375,9.54,5.67,0.99],[4850,191.69167,-33.31556,5.86,1.34],[4851,191.66125,16.5775,5.12,1.35],[4852,191.10833,80.62111,6.4,0.12],[4853,191.93,-59.68861,1.25,-0.23],[4854,191.75958,5.95083,6.34,-0.05],[4855,191.80667,11.95806,6.07,0.12],[4856,191.88917,-6.30194,6.26,0.55],[4857,191.97375,-24.85167,6.44,-0.06],[4858,191.96417,3.57278,6.41,1.6],[4859,191.82875,62.78083,5.89,0.6],[4860,192.11,-27.5975,5.66,0.95],[4861,192.05958,13.55306,6.56,0.01],[4862,192.43708,-71.98639,5.55,1.17],[4863,191.89333,66.79028,5.43,1.56],[4864,192.19583,24.84028,6.31,0.7],[4865,192.22583,14.1225,5.7,0.02],[4866,192.17417,48.46694,6.27,0.18],[4867,192.16417,60.32,5.85,0.46],[4868,192.55,-60.40083,6.75,0.35],[4869,192.3225,27.55222,5.78,0.03],[4870,193.74417,-85.12333,5.46,1.02],[4871,192.58167,-48.45972,6.24,0.05],[4872,192.74125,-52.7875,5.73,0.13],[4873,192.5725,22.86333,6.43,1.0],[4874,192.67167,-33.99944,4.91,-0.04],[4875,192.54458,37.51694,5.89,0.15],[4876,192.82417,-60.32972,5.72,0.38],[4877,192.84542,-10.33833,6.41,1.02],[4878,192.90375,3.05667,6.02,1.29],[4879,192.98667,-39.68083,5.98,-0.1],[4880,193.02208,-48.09417,6.33,0.03],[4881,192.99125,-26.73806,6.15,0.23],[4882,193.1025,-53.82944,6.24,1.13],[4883,192.92458,27.54056,4.94,0.67],[4884,193.05125,17.07389,6.32,1.59],[4885,193.26625,-54.9525,5.93,1.31],[4886,193.115,16.1225,6.3,0.16],[4887,193.34083,-60.32861,5.76,0.33],[4888,193.27875,-48.94333,4.33,1.37],[4889,193.35917,-40.17889,4.27,0.21],[4890,193.45458,-60.37694,5.9,0.2],[4891,193.29667,-3.55306,6.11,0.5],[4892,192.2775,83.41806,5.85,-0.06],[4893,192.30667,83.41278,5.28,-0.03],[4894,193.32417,21.245,4.9,0.9],[4895,193.59167,-58.43056,6.58,0.76],[4896,193.40875,-4.22389,6.44,1.1],[4897,193.66333,-59.14667,4.62,-0.15],[4898,193.64833,-57.17778,4.03,-0.17],[4899,193.65333,-57.16833,5.17,-0.12],[4900,193.45708,12.41861,6.25,0.27],[4901,193.57792,-11.64861,6.0,0.08],[4902,193.58833,-9.53889,4.79,1.6],[4903,193.74375,-44.15194,5.89,0.64],[4904,193.55458,33.53444,6.26,0.2],[4905,193.50708,55.95972,1.77,-0.02],[4906,193.83083,-42.91583,5.47,1.68],[4907,194.13125,-72.18528,5.93,1.13],[4908,193.9875,-56.83611,5.32,0.01],[4909,193.73542,47.19667,5.84,1.55],[4910,193.90083,3.3975,3.38,1.58],[4911,193.97208,-15.32694,6.17,0.0],[4912,194.12542,-26.46028,6.62,0.68],[4913,194.26833,-51.19861,5.16,-0.06],[4914,194.00167,38.31472,5.6,0.34],[4915,194.00708,38.31833,2.9,-0.12],[4916,193.86875,65.43861,5.24,0.28],[4917,194.07333,54.09944,5.82,0.19],[4918,194.38833,-22.75389,6.31,1.07],[4919,194.28208,46.17694,6.12,1.01],[4920,194.73083,17.40944,4.78,1.56],[4921,194.91458,-3.81194,5.79,0.18],[4922,195.13583,-33.50528,6.02,0.38],[4923,195.5675,-71.54889,3.62,1.18],[4924,195.06875,30.785,4.9,1.17],[4925,195.14958,-3.36861,5.99,1.12],[4926,195.16167,18.37306,6.2,0.42],[4927,194.69708,75.4725,6.01,0.99],[4928,194.97958,66.59722,5.32,1.29],[4929,195.29,17.12306,5.96,0.96],[4930,195.77167,-71.47611,6.03,0.05],[4931,195.1825,56.36639,4.93,0.36],[4932,195.54417,10.95917,2.83,0.94],[4933,195.88833,-49.52722,4.85,0.02],[4934,195.445,63.61028,6.0,0.41],[4935,195.94208,-20.58306,5.58,0.56],[4936,195.66833,59.71611,6.53,0.05],[4937,195.97667,-3.66333,6.59,0.29],[4938,196.20042,-41.19667,6.26,1.68],[4939,196.37833,-52.115,6.43,1.7],[4940,196.56958,-48.46361,4.71,-0.14],[4941,196.64625,-41.58861,5.59,1.05],[4942,196.7275,-49.90611,4.27,-0.19],[4943,196.43542,35.79889,5.25,-0.08],[4944,196.85083,-59.86056,5.99,0.48],[4945,196.46792,45.26861,5.63,1.13],[4946,196.58833,21.15333,5.99,0.39],[4947,196.72625,-35.86194,6.54,-0.02],[4948,196.5425,29.02944,6.54,0.05],[4949,196.59417,22.61611,5.6,1.59],[4950,196.20708,73.02528,6.31,0.6],[4951,196.90958,-53.45972,5.71,-0.07],[4952,197.02917,-65.30639,5.51,-0.02],[4953,196.59458,62.04194,6.14,0.99],[4954,196.79458,27.62472,4.8,1.48],[4955,196.97417,-10.74028,5.19,1.14],[4956,196.97333,27.55583,6.19,1.36],[4957,197.13542,-8.98444,5.55,1.18],[4958,197.26375,-23.11806,4.95,1.05],[4959,197.31,-9.53833,6.32,1.02],[4960,197.30167,10.02222,5.78,1.0],[4961,197.43875,-10.32944,5.94,1.49],[4962,197.44917,16.84861,5.91,1.45],[4963,197.4875,-5.53889,4.38,-0.01],[4964,197.41125,37.42306,6.02,1.15],[4965,197.74333,-52.56694,6.06,-0.09],[4966,197.965,-69.94194,5.91,0.42],[4967,197.425,38.53389,6.28,-0.12],[4968,197.49708,17.52944,5.22,0.45],[4969,197.49708,17.52944,5.22,0.6],[4970,197.78667,-42.23306,5.79,0.52],[4971,197.51333,38.49889,5.91,0.29],[4972,197.97125,-63.30278,6.33,0.44],[4973,197.84667,-43.36889,5.25,1.05],[4974,197.45917,62.22917,6.54,-0.02],[4975,198.0725,-59.92083,4.6,-0.08],[4976,198.57167,-78.44722,5.85,1.07],[4977,198.20375,-66.22694,5.9,0.06],[4978,197.91333,-26.55167,6.5,0.19],[4979,198.01333,-37.80306,4.85,0.7],[4980,198.23333,-59.81667,6.16,0.6],[4981,198.01458,-16.19861,5.04,0.46],[4982,198.21208,-42.69972,6.22,1.06],[4983,197.96833,27.87806,4.26,0.57],[4984,198.035,24.25806,6.33,0.98],[4985,198.34792,-50.7,5.89,-0.02],[4986,198.13708,11.55611,5.77,1.51],[4987,198.14958,18.75167,6.53,0.88],[4988,198.55042,-58.68389,5.89,1.08],[4989,198.56167,-59.10333,4.92,0.48],[4990,198.36167,-18.82667,6.28,0.09],[4991,198.48958,-43.13889,6.16,1.38],[4992,198.30167,18.72694,6.11,1.2],[4993,198.81208,-67.89444,4.8,-0.08],[4994,198.85708,-69.67972,6.37,1.21],[4995,198.54542,-19.93083,5.33,0.87],[4996,198.67958,-48.95667,5.89,1.06],[4997,198.42917,40.15278,4.92,1.06],[4998,198.63042,11.33167,5.67,1.5],[4999,198.79042,-36.37111,6.19,0.97],[5000,199.18667,-65.13833,6.07,0.44],[5001,198.995,-19.94306,5.22,1.03],[5002,199.30417,-66.78361,4.87,1.5],[5003,198.38333,72.79889,6.59,0.08],[5004,198.88333,40.85528,5.79,0.19],[5005,199.10625,-1.39056,6.68,0.31],[5006,199.22125,-31.50611,5.1,0.96],[5007,199.05958,19.05167,6.45,0.98],[5008,199.30792,-43.97944,5.84,0.2],[5009,198.10583,80.47139,6.25,0.94],[5010,199.13458,19.78528,6.45,0.25],[5011,199.19375,9.42417,5.22,0.59],[5012,199.82875,-72.03556,6.04,1.35],[5013,199.315,13.67556,5.33,1.31],[5014,199.37458,-0.67667,6.37,0.26],[5015,199.40125,5.46972,4.8,1.67],[5016,199.64417,-51.28611,6.19,0.01],[5017,199.38542,40.5725,4.73,0.3],[5018,199.11917,68.40806,6.2,-0.06],[5019,199.60125,-18.31139,4.74,0.71],[5020,199.73042,-23.17167,3.0,0.92],[5021,199.71292,3.68778,6.62,0.06],[5022,199.61583,34.09806,5.82,1.35],[5023,199.56042,49.68194,5.15,-0.07],[5024,200.14542,-59.77333,6.18,0.43],[5025,199.7675,35.12806,6.02,0.24],[5026,200.1575,-52.74806,5.48,-0.13],[5027,200.20125,-55.80056,6.02,0.29],[5028,200.14917,-36.71222,2.75,0.04],[5029,200.24042,-46.88056,5.77,1.12],[5030,200.71917,-72.14667,6.05,0.09],[5031,200.17333,2.94167,6.26,0.1],[5032,200.07917,40.15056,5.6,1.2],[5033,200.37458,-19.48889,6.21,0.09],[5034,200.64875,-60.97222,6.18,0.01],[5035,200.65792,-60.98833,4.53,-0.13],[5036,200.5675,-52.18306,5.83,0.12],[5037,200.42333,2.08722,5.69,0.06],[5038,200.71958,-47.94306,6.16,0.18],[5039,200.76083,-48.56278,6.38,-0.07],[5040,200.54042,5.15472,5.87,0.12],[5041,201.00208,-64.53583,4.53,0.85],[5042,201.27958,-74.88778,5.05,1.11],[5043,200.78625,-33.19,6.22,1.65],[5044,200.75458,-17.73528,5.37,0.99],[5045,200.51583,43.90306,6.35,0.24],[5046,200.96792,-49.82306,6.48,0.96],[5047,200.82875,-4.92444,5.89,1.43],[5048,201.30792,-64.48528,5.31,0.4],[5049,201.45875,-70.6275,5.67,-0.03],[5050,201.13833,-5.16389,5.75,0.42],[5051,201.82625,-74.69194,6.63,-0.06],[5052,200.975,37.03389,6.07,1.68],[5053,201.12708,12.43194,6.44,1.06],[5054,200.98125,54.92528,2.27,0.02],[5055,200.985,54.92167,3.95,0.13],[5056,201.29833,-11.16139,0.98,-0.23],[5057,201.27792,23.85444,5.78,0.06],[5058,201.5325,-39.75528,5.09,1.2],[5059,201.5475,-1.1925,5.97,0.19],[5060,201.73375,-41.49806,5.69,1.47],[5061,201.77625,-49.14389,6.31,0.6],[5062,201.30625,54.98806,4.01,0.16],[5063,201.83667,-49.38083,6.28,-0.12],[5064,201.68,-12.70778,5.25,1.52],[5065,201.81125,-40.16306,6.4,0.98],[5066,202.19333,-69.62806,6.2,0.02],[5067,201.56917,46.02806,5.88,0.97],[5068,201.86333,-15.97361,4.76,1.09],[5069,202.28167,-64.67583,6.11,0.11],[5070,201.49958,63.26111,6.5,0.74],[5071,202.355,-51.16528,5.06,0.07],[5072,202.1075,13.77889,4.98,0.71],[5073,201.53375,72.39139,5.79,1.63],[5074,201.76917,64.73556,6.66,0.37],[5075,201.79458,64.71944,7.04,0.39],[5076,201.99792,52.74583,6.34,0.23],[5077,202.10917,40.72972,6.47,0.92],[5078,202.31208,-1.36444,6.43,1.11],[5079,202.04875,50.58722,6.8,0.6],[5080,202.42833,-23.28139,4.97,1.6],[5081,202.30417,10.81833,5.65,1.05],[5082,203.31167,-77.56833,6.48,0.48],[5083,202.19042,50.71833,6.43,0.37],[5084,205.23125,-85.78611,5.58,0.18],[5085,202.11292,59.94583,5.4,-0.01],[5086,202.50042,7.17889,6.17,1.47],[5087,202.49,6.01333,6.51,0.96],[5088,202.60708,-6.47028,6.09,0.33],[5089,202.76125,-39.4075,3.88,1.17],[5090,202.88875,-28.11278,6.47,0.1],[5091,201.73625,78.64389,5.77,0.77],[5092,203.02208,-38.39917,6.16,1.04],[5093,203.39875,-65.6325,6.37,-0.02],[5094,203.01167,-18.72889,6.01,0.18],[5095,202.99125,-6.25583,4.69,1.62],[5096,202.81583,42.10611,6.08,1.05],[5097,203.14958,-28.69278,5.69,0.04],[5098,203.14375,-29.56528,6.45,0.44],[5099,203.21542,-15.36306,5.55,1.23],[5100,203.24208,-10.165,5.21,0.96],[5101,203.25292,-7.195,6.68,1.28],[5102,203.20042,24.34639,6.11,0.96],[5103,203.62042,-48.27222,6.33,-0.05],[5104,203.68167,-33.31083,6.44,1.26],[5105,203.53292,3.65889,4.94,0.03],[5106,203.66875,-13.21444,5.91,0.02],[5107,203.67333,-0.59583,3.37,0.11],[5108,203.59083,38.78917,6.37,0.21],[5109,203.53042,55.34861,5.6,-0.03],[5110,203.69917,37.1825,4.98,0.4],[5111,203.88042,-5.39611,5.73,0.95],[5112,203.61375,49.01611,4.7,0.12],[5113,204.30083,-61.69194,5.63,0.5],[5114,203.88875,10.20472,6.49,1.03],[5115,204.79958,-75.68389,6.34,0.01],[5116,203.80875,44.19694,6.84,0.2],[5117,204.21042,-34.46778,6.5,1.03],[5118,204.275,-44.14333,5.98,0.94],[5119,204.69042,-70.445,6.1,1.42],[5120,204.20167,-26.495,5.78,0.22],[5121,204.34875,-46.42833,5.9,-0.12],[5122,204.53167,-58.415,6.42,1.12],[5123,204.24625,24.61333,5.74,1.55],[5124,204.70458,-57.62306,6.01,1.14],[5125,205.0025,-70.78833,6.59,1.3],[5126,204.16583,49.48667,6.49,0.6],[5127,204.365,36.295,4.82,0.23],[5128,204.675,-29.56083,5.83,0.4],[5129,204.53292,14.30167,6.52,0.25],[5130,205.04542,-64.57694,5.79,0.39],[5131,203.67833,76.54667,6.57,0.6],[5132,204.97167,-53.46639,2.3,-0.22],[5133,204.42917,50.71472,6.48,1.55],[5134,204.99875,-49.95028,6.0,1.5],[5135,204.92,-39.74806,6.27,1.66],[5136,204.9525,-40.05194,5.6,1.3],[5137,204.75958,18.26528,6.48,1.2],[5138,204.89417,10.74611,5.57,0.33],[5139,204.29583,71.24222,5.5,1.2],[5140,205.50458,-58.78722,5.38,-0.03],[5141,205.43625,-54.56,5.01,-0.05],[5142,204.87667,52.92139,5.46,0.1],[5143,205.065,31.01194,6.21,0.96],[5144,205.16875,19.95556,5.75,0.01],[5145,205.16292,28.06528,6.23,1.28],[5146,205.37875,-23.44972,6.59,0.06],[5147,205.44042,-33.59694,6.05,1.44],[5148,205.09667,50.51944,6.32,0.54],[5149,205.25958,22.49583,5.62,1.01],[5150,205.40333,-8.70306,5.01,1.63],[5151,205.73375,-56.76806,6.0,-0.08],[5152,205.7275,-50.79056,6.41,1.67],[5153,205.08875,57.2075,6.29,0.1],[5154,205.18458,54.68167,4.66,1.64],[5155,205.72917,-41.40111,5.98,1.02],[5156,205.55292,8.38833,6.16,0.42],[5157,205.91667,-42.0675,5.98,-0.08],[5158,206.06625,-51.01306,6.47,-0.13],[5159,205.76542,3.53806,5.36,1.11],[5160,205.62,41.67417,6.3,0.86],[5161,205.68083,34.98889,5.98,0.85],[5162,205.37458,64.8225,5.85,0.07],[5163,205.97625,-5.49889,6.51,0.05],[5164,205.93833,22.70028,6.13,1.42],[5165,206.12417,-16.17917,5.6,0.81],[5166,206.19042,-25.50083,6.21,1.38],[5167,206.40375,-26.11611,5.81,0.02],[5168,206.42167,-33.04389,4.23,0.38],[5169,205.97792,52.06444,6.02,0.0],[5170,206.39625,-15.7675,6.19,0.05],[5171,206.79458,-62.59,6.51,1.98],[5172,206.66375,-51.43278,4.65,0.96],[5173,206.48458,-12.42667,5.51,0.9],[5174,206.735,-36.25194,5.15,-0.02],[5175,206.865,-50.24944,5.91,0.29],[5176,206.91,-50.32111,5.45,1.36],[5177,206.305,55.87944,6.5,0.47],[5178,206.80625,-9.70917,6.05,1.42],[5179,206.55625,41.08861,5.87,0.21],[5180,206.57917,38.50389,5.94,0.94],[5181,206.85583,-17.86,5.43,1.62],[5182,206.68042,25.70222,5.95,0.49],[5183,206.73792,6.35056,6.33,0.63],[5184,205.66375,78.06444,5.91,1.01],[5185,206.81542,17.45667,4.5,0.48],[5186,206.74917,38.54278,5.5,1.03],[5187,206.64875,54.43278,5.7,-0.08],[5188,208.91125,-82.66611,5.95,1.46],[5189,207.22958,-35.70389,6.53,0.57],[5190,207.37625,-41.68778,3.41,-0.22],[5191,206.885,49.31333,1.86,-0.19],[5192,207.36125,-34.45083,4.19,1.5],[5193,207.40417,-42.47389,3.04,-0.17],[5194,207.9475,-69.40139,5.75,1.73],[5195,207.16125,31.19028,5.62,1.01],[5196,207.46792,-18.13417,4.97,1.06],[5197,207.52708,-29.08139,6.18,-0.03],[5198,207.58083,-39.90111,6.44,0.99],[5199,207.23833,39.54278,7.4,0.6],[5200,207.36917,15.79778,4.07,1.52],[5201,207.42833,21.26417,4.91,1.43],[5202,207.64375,-19.89722,6.53,0.51],[5203,205.59625,82.7525,5.98,1.01],[5204,207.4375,36.63278,6.38,0.6],[5205,207.60292,5.49722,6.01,0.9],[5206,207.94667,-46.89917,5.77,-0.16],[5207,208.02,-52.81167,5.25,-0.09],[5208,207.9025,-36.43333,6.35,0.48],[5209,207.835,-24.39083,6.45,0.69],[5210,207.95667,-32.99444,4.56,-0.13],[5211,207.95875,-32.99472,6.06,-0.04],[5212,208.00375,-31.61944,6.12,0.48],[5213,207.43958,61.48917,5.96,0.96],[5214,207.76875,34.7725,6.65,0.12],[5215,207.78833,34.66444,5.87,1.62],[5216,207.61542,58.53944,6.46,0.09],[5217,208.42958,-53.37361,5.89,0.01],[5218,208.70375,-67.6525,5.71,1.49],[5219,207.94792,34.44417,4.74,1.66],[5220,208.07667,12.16528,6.04,0.04],[5221,208.30208,-31.92778,4.73,-0.14],[5222,208.38667,-35.66417,5.54,0.44],[5223,208.48708,-47.12806,6.1,-0.07],[5224,208.4675,-35.31444,6.19,1.02],[5225,208.30375,17.93278,5.7,0.84],[5226,207.85792,64.72333,4.65,1.58],[5227,207.74667,68.31528,6.4,1.17],[5228,208.56917,-28.56972,6.04,1.13],[5229,208.29292,28.64806,5.9,0.2],[5230,208.80083,-52.16111,5.71,-0.08],[5231,208.885,-47.28833,2.55,-0.22],[5232,208.67542,-1.50306,5.15,1.08],[5233,208.7425,-8.05889,6.19,0.53],[5234,209.0825,-54.13194,6.14,0.07],[5235,208.67125,18.39778,2.68,0.58],[5236,209.1375,-54.70444,6.0,0.78],[5237,208.93542,-31.285,6.51,0.9],[5238,208.4625,53.72861,5.7,-0.05],[5239,209.08125,-46.5925,5.83,1.14],[5240,210.13667,-78.59,6.09,0.03],[5241,209.41208,-63.68667,4.71,1.11],[5242,209.63,-65.80056,6.2,1.05],[5243,208.95833,14.05639,6.16,0.5],[5244,209.11625,1.05056,5.91,0.2],[5245,209.04375,32.0325,6.32,0.37],[5246,209.36542,-23.02278,6.14,1.43],[5247,209.1425,27.49194,5.01,1.42],[5248,209.56792,-42.10083,3.83,-0.21],[5249,209.67,-44.80361,3.87,-0.2],[5250,209.62958,-24.97222,5.15,-0.1],[5251,209.82208,-50.37,5.91,0.96],[5252,210.07208,-61.48139,6.49,0.33],[5253,210.2175,-66.26861,5.97,0.35],[5254,209.66625,14.64944,6.0,1.44],[5255,209.66208,21.69611,5.76,-0.03],[5256,209.38375,61.49278,6.37,0.6],[5257,210.00042,-25.01028,5.77,0.48],[5258,209.95542,-3.54972,6.4,0.49],[5259,210.32917,-40.22222,6.13,1.25],[5260,210.43083,-45.60361,4.34,0.6],[5261,211.3325,-76.79667,5.5,1.55],[5262,210.335,8.89472,5.99,0.09],[5263,210.29375,27.38667,6.23,0.17],[5264,210.41167,1.54444,4.26,0.1],[5265,210.595,-27.43,5.48,1.34],[5266,210.85917,-56.21361,5.92,1.22],[5267,210.95583,-60.37306,0.61,-0.23],[5268,210.75708,-31.68389,6.18,0.48],[5269,210.86458,-41.42333,6.11,-0.11],[5270,210.6325,9.68639,6.2,0.9],[5271,210.55083,45.75361,6.27,1.32],[5272,210.97125,-22.42167,6.3,0.45],[5273,210.88458,10.78667,6.3,0.74],[5274,210.90333,7.54639,6.26,0.94],[5275,210.9825,4.90083,6.24,0.39],[5276,211.06083,-5.38139,6.39,1.32],[5277,211.1125,-14.97167,6.28,1.08],[5278,211.44375,-54.66944,6.17,0.23],[5279,212.11292,-74.85028,6.02,0.58],[5280,210.74875,50.97194,6.15,0.01],[5281,211.60458,-59.71556,6.42,0.12],[5282,210.46083,68.67861,6.34,1.4],[5283,211.15625,2.2975,6.28,1.02],[5284,211.30833,-16.33583,6.56,0.1],[5285,211.51167,-41.17972,4.36,-0.19],[5286,211.54542,-43.09194,6.2,0.98],[5287,211.59292,-26.6825,3.27,1.12],[5288,211.67083,-36.37,2.06,1.01],[5289,212.05958,-63.20806,6.4,1.02],[5290,211.67833,-9.31333,5.46,0.34],[5291,211.09708,64.37583,3.65,-0.05],[5292,212.23458,-59.27667,6.34,0.05],[5293,212.62875,-70.30556,6.05,1.74],[5294,212.21625,-43.47111,6.17,-0.06],[5295,212.75792,-69.71972,6.06,0.18],[5296,212.39583,-51.50472,6.0,-0.06],[5297,212.47833,-53.43917,4.75,0.94],[5298,212.2525,-10.33444,6.47,1.0],[5299,211.9825,43.85444,5.27,1.59],[5300,212.07208,49.45806,5.25,1.65],[5301,212.71042,-16.30194,4.91,1.72],[5302,212.19167,59.33778,6.46,1.02],[5303,214.5575,-81.00778,4.91,0.25],[5304,212.59958,25.09167,4.83,0.54],[5305,211.735,74.59361,6.45,0.14],[5306,214.22875,-77.66389,6.47,1.42],[5307,212.88,1.36222,6.43,0.48],[5308,213.31833,-53.66583,5.56,1.44],[5309,213.10208,-24.36417,6.34,1.35],[5310,212.81292,32.29556,6.11,1.26],[5311,213.41625,-54.62583,6.11,0.05],[5312,213.19167,-27.26111,5.08,1.16],[5313,213.06583,2.40944,5.01,-0.12],[5314,213.305,-26.61222,6.24,1.08],[5315,213.22417,-10.27361,4.19,1.33],[5316,213.7375,-57.08583,5.07,-0.08],[5317,213.42,-0.84556,5.91,0.47],[5318,213.6775,-41.8375,5.61,0.93],[5319,213.83833,-53.50972,6.39,1.57],[5320,214.16083,-66.58778,5.75,-0.06],[5321,212.21208,77.5475,4.82,1.36],[5322,213.58875,-5.9475,6.36,0.6],[5323,213.52167,12.95944,5.54,0.54],[5324,213.75542,-29.28194,6.08,0.6],[5325,213.91167,-45.00083,6.31,0.6],[5326,214.1425,-59.91389,6.39,2.04],[5327,216.09708,-82.84861,6.42,0.02],[5328,213.36542,51.78778,6.69,0.39],[5329,213.37083,51.79028,4.54,0.2],[5330,213.71167,10.10056,5.29,1.0],[5331,213.72083,3.33611,6.45,1.6],[5332,213.85042,-18.20083,5.43,-0.03],[5333,213.67083,21.87333,6.39,0.18],[5334,213.01667,69.4325,5.24,1.58],[5335,213.59792,41.51889,6.24,1.04],[5336,215.59458,-80.10889,5.06,-0.1],[5337,214.07625,-33.24139,6.55,0.3],[5338,214.00375,-6.00056,4.08,0.52],[5339,216.72875,-83.66778,4.32,1.31],[5340,213.91542,19.1825,-0.04,1.23],[5341,214.08958,-6.62194,6.44,0.28],[5342,214.12542,-3.19639,6.15,-0.01],[5343,214.0175,18.91194,5.98,0.26],[5344,214.26583,-18.58528,6.22,0.98],[5345,213.82042,52.53583,6.58,0.1],[5346,214.13667,20.12139,6.25,0.49],[5347,214.10083,39.74472,6.38,0.36],[5348,214.59917,-33.22056,6.54,0.27],[5349,214.96458,-61.27306,5.23,0.29],[5350,214.04125,51.36722,4.75,0.2],[5351,214.09583,46.08833,4.18,0.08],[5352,214.36833,15.26333,5.8,1.71],[5353,214.5025,-7.5425,6.47,0.73],[5354,214.85083,-46.05778,3.55,-0.18],[5355,214.65958,-18.71611,5.9,0.0],[5356,214.75333,-25.81556,5.87,0.5],[5357,214.84958,-37.00361,5.94,0.08],[5358,215.08125,-56.38667,4.33,0.12],[5359,214.7775,-13.37111,4.52,0.13],[5360,214.33792,51.30722,6.2,0.04],[5361,214.49917,35.50944,4.81,1.06],[5362,215.04042,-43.05889,5.56,0.92],[5363,214.455,48.00167,6.32,0.44],[5364,215.17708,-45.18722,4.77,0.31],[5365,214.81792,13.00417,5.41,0.38],[5366,214.88542,-2.26556,5.14,1.02],[5367,215.13917,-37.88528,4.05,-0.03],[5368,214.92042,0.38417,6.19,0.2],[5369,214.73208,38.7675,6.86,1.06],[5370,214.93833,16.30694,4.86,1.23],[5371,215.65417,-58.45944,4.92,0.86],[5372,214.7325,54.86417,6.53,0.6],[5373,214.94875,38.79389,6.33,0.05],[5374,215.03625,30.42917,6.44,0.15],[5375,215.66125,-48.32028,6.09,-0.13],[5376,215.58208,-34.78694,5.56,-0.09],[5377,215.83458,-50.77222,6.02,1.36],[5378,215.75917,-39.51222,4.42,-0.18],[5379,216.27625,-68.19528,5.61,0.49],[5380,215.95167,-53.17667,6.0,1.1],[5381,215.77417,-27.75389,4.77,1.31],[5382,216.41458,-66.17333,6.36,0.13],[5383,215.85667,-11.71417,6.21,0.99],[5384,215.81375,1.24167,6.27,0.63],[5385,215.84417,8.445,6.86,0.43],[5386,215.84458,8.44667,5.12,-0.02],[5387,215.77833,25.33806,6.22,0.37],[5388,216.00375,8.24361,5.95,0.06],[5389,217.40333,-76.72917,6.07,1.18],[5390,216.2025,-24.80639,5.32,0.96],[5391,216.77958,-65.82167,5.85,1.5],[5392,216.04708,5.82,5.1,0.12],[5393,216.17042,-11.66972,6.49,0.42],[5394,216.07625,8.085,6.19,1.2],[5395,216.53417,-45.22139,4.56,-0.15],[5396,216.545,-45.37944,4.35,0.43],[5397,216.37417,-19.96972,6.61,0.14],[5398,216.55583,-42.31917,6.32,1.19],[5399,216.44875,-26.8525,6.48,0.94],[5400,216.70792,-39.87389,6.35,-0.08],[5401,216.80083,-46.13444,5.83,0.31],[5402,216.37167,38.39306,6.27,1.23],[5403,217.18125,-59.19778,6.45,0.14],[5404,216.29917,51.85083,4.05,0.5],[5405,216.61417,19.22694,5.39,0.23],[5406,216.85167,-6.12028,6.17,0.09],[5407,217.04333,-29.49167,4.97,-0.07],[5408,217.81875,-67.71722,5.83,1.0],[5409,217.05042,-2.22806,4.81,0.7],[5410,217.17375,-6.90056,5.42,1.49],[5411,216.86375,41.025,6.63,0.37],[5412,217.53583,-45.32167,5.5,-0.08],[5413,217.58708,-49.51917,5.37,0.05],[5414,217.13125,28.28917,7.62,-0.01],[5415,217.13875,28.29083,7.12,-0.03],[5416,217.06833,36.19694,6.1,0.6],[5417,217.73542,-40.845,6.39,1.44],[5418,217.46042,0.82889,5.94,0.16],[5419,217.795,-38.86972,5.97,1.06],[5420,217.1575,49.84472,5.59,0.85],[5421,218.13708,-56.88778,6.93,0.91],[5422,217.45708,31.79111,6.06,-0.03],[5423,217.40333,41.79583,6.35,0.7],[5424,217.68917,4.77222,6.02,1.42],[5425,218.15375,-50.45694,4.42,-0.19],[5426,218.385,-54.99833,5.87,0.48],[5427,218.37458,-52.68,5.87,1.09],[5428,218.29,-30.71417,6.09,1.03],[5429,217.9575,30.37139,3.58,1.3],[5430,216.88125,75.69611,4.25,1.44],[5431,218.53333,-42.09972,6.6,0.06],[5432,218.82167,-60.01583,6.4,1.26],[5433,218.08417,26.67722,6.01,0.22],[5434,218.13542,22.26,5.92,0.35],[5435,218.01958,38.30833,3.03,0.19],[5436,217.69208,63.18556,6.09,0.41],[5437,217.92833,60.22556,6.27,0.24],[5438,218.71125,-20.43917,6.5,0.14],[5439,218.88125,-41.51722,5.87,-0.08],[5440,218.87667,-42.15778,2.31,-0.19],[5441,218.33458,36.95944,6.43,0.6],[5442,218.12875,55.39778,5.76,1.5],[5443,219.44292,-67.93222,6.04,0.5],[5444,219.07917,-46.24528,5.55,1.48],[5445,218.54875,32.53444,6.33,0.4],[5446,219.10083,-39.59722,6.13,1.05],[5447,218.67,29.745,4.46,0.36],[5448,218.66042,36.62583,6.03,1.38],[5449,219.18417,-40.21167,5.74,-0.12],[5450,219.33375,-46.13389,5.41,1.01],[5451,218.56625,57.06528,6.48,0.49],[5452,218.665,49.36833,5.74,1.56],[5453,219.47167,-49.42583,4.05,-0.15],[5454,219.02875,23.25028,6.38,1.06],[5455,219.24917,-12.30528,6.2,0.46],[5456,219.58167,-38.79417,6.02,1.43],[5457,219.79583,-46.58417,6.07,0.51],[5458,219.85292,-49.05556,6.39,0.44],[5459,219.89958,-60.83528,-0.01,0.71],[5460,219.90042,-60.83556,1.33,0.88],[5461,220.13667,-56.44083,6.3,1.18],[5462,219.55833,18.29833,5.91,1.1],[5463,220.62667,-64.97528,3.19,0.24],[5464,219.55208,43.64194,5.7,1.48],[5465,220.48208,-58.61611,6.22,0.45],[5466,220.25583,-36.135,5.67,-0.08],[5467,219.56333,54.02333,5.85,-0.01],[5468,219.70917,44.40444,5.39,0.0],[5469,220.4825,-47.38833,2.3,-0.2],[5470,221.965,-79.04472,3.83,1.43],[5471,220.49,-37.79361,4.0,-0.17],[5472,220.09125,21.97556,6.1,0.4],[5473,220.17667,13.53417,5.91,0.21],[5474,220.46292,-30.93333,6.37,-0.09],[5475,220.18167,16.41833,4.94,-0.03],[5476,220.18292,16.41778,5.88,0.24],[5477,220.28708,13.72833,4.83,0.6],[5478,220.28708,13.72833,4.43,0.05],[5479,218.40958,79.66028,6.26,1.3],[5480,220.41167,8.16167,4.86,1.0],[5481,220.43125,11.66056,5.56,0.94],[5482,221.32208,-62.87583,5.36,0.29],[5483,220.47583,21.12361,6.38,0.6],[5484,220.80667,-24.9975,5.73,-0.01],[5485,220.91417,-35.17361,4.05,1.35],[5486,221.23125,-58.47778,6.11,1.02],[5487,220.765,-5.65833,3.88,0.38],[5488,221.29542,-55.60194,6.1,-0.06],[5489,221.24667,-35.19194,4.92,0.01],[5490,220.85583,26.52778,4.81,1.66],[5491,232.07958,-88.13306,6.48,0.3],[5492,220.51333,61.26194,6.25,0.41],[5493,220.935,40.45917,5.73,1.39],[5494,221.62125,-47.44111,5.74,0.07],[5495,221.75542,-52.38361,5.21,0.98],[5496,221.29875,-1.41778,6.07,1.61],[5497,221.50042,-25.44306,4.94,0.35],[5498,221.80125,-52.20556,6.07,0.09],[5499,221.52833,-23.15306,5.81,0.98],[5500,222.185,-66.59389,5.91,-0.07],[5501,221.37583,0.71722,5.69,-0.03],[5502,221.31042,16.96444,4.6,0.98],[5503,221.49083,-15.45972,6.33,1.19],[5504,221.54542,-21.17611,6.4,0.58],[5505,221.24667,27.075,5.12,0.6],[5506,221.24667,27.07417,2.7,0.97],[5507,221.33625,18.88472,6.13,0.83],[5508,221.77125,-38.29083,5.94,1.33],[5509,221.88375,-43.5575,6.3,1.08],[5510,221.30708,32.78833,6.28,1.58],[5511,221.56208,1.89278,3.72,-0.01],[5512,221.525,15.13194,5.63,1.57],[5513,221.80708,-21.32472,6.06,1.65],[5514,221.84375,-25.62444,5.63,0.6],[5515,222.27875,-56.66778,6.23,1.13],[5516,221.93667,-26.0875,5.24,0.94],[5517,221.98958,-26.64639,5.77,-0.02],[5518,221.97875,-12.83972,6.35,1.1],[5519,222.15875,-36.63472,6.04,1.54],[5520,223.30708,-73.19,5.6,0.82],[5521,222.32792,-24.25167,5.68,1.3],[5522,222.22542,-0.84778,6.14,-0.04],[5523,222.32958,-14.14889,5.31,0.07],[5524,222.09708,24.36667,6.14,0.48],[5525,225.46167,-83.22778,5.65,0.95],[5526,222.57208,-27.96028,4.41,1.4],[5527,223.14583,-63.81,5.87,0.72],[5528,222.91,-43.57556,4.32,-0.15],[5529,222.27792,37.81111,6.16,0.36],[5530,222.67167,-15.99722,5.15,0.41],[5531,222.71958,-16.04167,2.75,0.15],[5532,222.49333,28.61583,5.8,0.05],[5533,222.32792,46.11611,5.74,0.48],[5534,222.56583,23.91194,5.85,0.56],[5535,222.75417,-2.29917,4.94,0.98],[5536,222.75042,-0.25722,6.18,1.4],[5537,222.38458,51.37472,6.51,0.4],[5538,222.42208,48.72056,5.69,0.47],[5539,223.67667,-65.99139,6.09,-0.06],[5540,224.47042,-76.66278,5.34,1.45],[5541,222.62333,37.27194,5.48,1.02],[5542,223.13792,-30.57694,6.29,0.6],[5543,223.21292,-37.80333,5.03,-0.16],[5544,222.84708,19.10111,4.55,0.76],[5545,226.19458,-83.03833,5.65,1.3],[5546,223.89417,-60.11417,5.2,1.16],[5547,225.04917,-77.16028,5.93,1.05],[5548,223.58375,-24.64222,5.3,1.32],[5549,223.65833,-33.30056,5.82,1.42],[5550,223.34708,15.70444,6.4,0.57],[5551,224.18333,-62.78083,5.11,0.0],[5552,222.86,59.29389,5.46,1.36],[5553,223.34875,19.15278,6.01,0.83],[5554,223.59542,-11.89833,5.8,0.97],[5555,224.98333,-75.0325,6.2,-0.04],[5556,224.07208,-52.80972,5.38,0.13],[5557,227.78458,-84.7875,5.91,-0.06],[5558,223.93625,-33.85583,5.32,0.04],[5559,224.13333,-47.87917,5.64,-0.04],[5560,224.25583,-51.44694,6.64,1.7],[5561,224.14917,-39.41611,6.36,0.06],[5562,224.12875,-32.63667,6.06,1.41],[5563,222.67625,74.15556,2.08,1.47],[5564,224.19208,-11.40972,5.46,1.49],[5565,224.30667,-29.15778,6.29,-0.04],[5566,224.53667,-48.86306,6.35,0.71],[5567,224.055,14.44639,5.77,-0.06],[5568,224.36667,-21.41556,5.74,1.11],[5569,223.99458,32.30028,6.12,0.1],[5570,224.29583,-4.34639,4.49,0.32],[5571,224.63292,-43.13389,2.68,-0.22],[5572,224.65333,-39.90667,6.15,1.23],[5573,224.38875,-0.1675,5.53,1.13],[5574,224.265,21.55528,6.49,-0.01],[5575,224.29875,16.38833,5.71,0.94],[5576,224.79042,-42.10417,3.13,-0.2],[5577,224.66333,-27.65722,5.65,0.24],[5578,224.55583,-11.155,6.6,0.01],[5579,224.80792,-37.88139,6.47,-0.08],[5580,224.86292,-43.16,6.1,0.6],[5581,224.09583,49.62861,5.63,0.5],[5582,224.72333,-11.14417,5.87,1.26],[5583,224.72,-4.98917,6.09,0.5],[5584,224.84625,4.56778,5.93,1.6],[5585,225.30417,-38.05833,5.89,1.24],[5586,225.24333,-8.51889,4.92,0.0],[5587,225.49208,-34.35889,6.22,0.24],[5588,224.90375,39.26528,5.64,0.31],[5589,224.39583,65.9325,4.6,1.59],[5590,225.3325,-2.755,5.52,1.68],[5591,225.52667,-28.06056,5.85,0.16],[5592,225.21833,22.04556,6.38,0.6],[5593,226.20083,-64.03167,5.17,0.93],[5594,225.45375,-0.14056,5.71,1.52],[5595,225.74708,-32.64333,5.44,-0.12],[5596,222.585,82.51194,5.64,0.68],[5597,225.16125,47.27778,6.37,-0.14],[5598,226.78625,-71.90528,6.52,1.59],[5599,225.68708,-3.03139,6.61,0.2],[5600,225.52708,25.00806,4.81,1.5],[5601,225.725,2.09139,4.4,1.04],[5602,225.48667,40.39056,3.5,0.97],[5603,226.0175,-25.28194,3.29,1.7],[5604,226.17875,-40.86139,6.41,1.46],[5605,226.28,-47.05111,4.72,-0.14],[5606,226.28,-47.05111,4.82,0.6],[5607,226.32958,-41.06722,5.15,1.01],[5608,225.36292,60.20444,5.93,0.09],[5609,225.77542,35.20583,5.51,1.02],[5610,226.02667,5.4925,6.5,0.31],[5611,226.98667,-65.27556,6.17,1.47],[5612,225.7775,44.64444,6.65,0.46],[5613,225.90208,34.56611,6.59,1.02],[5614,226.44875,-25.78972,6.67,-0.01],[5615,226.55792,-36.26417,6.27,1.65],[5616,226.11125,26.9475,4.54,1.24],[5617,226.85792,-49.08833,5.77,0.92],[5618,225.9475,47.65444,4.76,0.65],[5619,226.63875,-30.91889,5.96,-0.08],[5620,226.61292,-22.03194,6.17,1.05],[5621,227.37458,-67.08417,5.76,0.69],[5622,226.65667,-16.25694,5.2,1.58],[5623,227.35625,-63.64278,6.28,0.06],[5624,227.05042,-40.58389,5.79,-0.14],[5625,227.16292,-42.86778,5.85,-0.11],[5626,227.21083,-45.27972,4.05,-0.18],[5627,226.3575,48.15111,5.57,0.0],[5628,228.14083,-72.77028,6.01,0.0],[5629,225.99083,65.91972,6.13,0.6],[5630,226.64625,36.45556,6.35,0.52],[5631,226.91792,5.49806,6.16,0.94],[5632,227.68583,-61.4225,6.3,0.63],[5633,226.835,18.44167,6.02,0.06],[5634,226.82542,24.86917,4.93,0.43],[5635,226.56958,54.55639,5.25,0.96],[5636,227.53125,-38.7925,5.98,0.6],[5637,227.81667,-55.34611,5.54,1.14],[5638,227.09917,26.30111,5.67,1.24],[5639,227.2225,13.235,6.1,0.96],[5640,227.14792,25.10861,5.81,1.24],[5641,227.5775,-26.33278,5.76,1.05],[5642,227.89542,-45.2775,6.44,1.04],[5643,227.88292,-45.27944,7.39,1.08],[5644,228.57958,-70.07944,5.81,3.38],[5645,228.25417,-61.74389,6.32,1.9],[5646,227.98375,-48.73778,3.87,-0.05],[5647,227.99,-48.74361,5.69,0.14],[5648,227.08125,50.055,6.39,1.03],[5649,228.07125,-52.09917,3.41,0.92],[5650,228.13042,-48.21861,6.33,1.12],[5651,228.20625,-44.50056,4.82,-0.17],[5652,228.05542,-19.79167,4.54,-0.08],[5653,228.28083,-36.09139,6.1,-0.08],[5654,228.01792,18.97583,5.89,1.53],[5655,228.32292,-24.00833,6.47,-0.04],[5656,228.33,-19.6475,6.08,0.12],[5657,228.36958,-25.30917,6.45,0.7],[5658,228.47208,-26.19361,5.84,1.14],[5659,228.18125,19.28583,6.68,0.68],[5660,228.65542,-31.51917,4.91,0.37],[5661,229.15167,-60.90389,5.73,-0.08],[5662,228.64042,-17.76861,6.17,-0.05],[5663,228.97417,-48.07417,5.95,0.21],[5664,229.23625,-60.9575,5.09,-0.06],[5665,228.38292,22.98333,6.3,0.06],[5666,229.41208,-63.61056,4.86,1.25],[5667,229.01667,-41.49111,5.16,0.58],[5668,229.04375,-43.48472,6.04,-0.14],[5669,228.71125,-5.50278,6.28,1.47],[5670,229.37833,-58.80111,4.07,0.09],[5671,229.7275,-68.67944,2.89,0.0],[5672,227.68375,67.78139,6.17,0.6],[5673,228.39833,38.26472,6.2,1.2],[5674,228.52542,31.78806,5.99,1.52],[5675,228.7975,4.93944,5.33,1.09],[5676,228.62167,29.16417,5.26,0.03],[5677,228.54292,42.17139,6.13,1.66],[5678,229.09583,-22.39944,5.5,1.38],[5679,228.95458,0.37222,5.63,0.18],[5680,229.70375,-60.49639,5.46,-0.1],[5681,228.87583,33.31472,3.47,0.95],[5682,229.53917,-41.06083,6.28,0.18],[5683,229.63333,-47.875,4.27,-0.08],[5684,230.16958,-67.48139,6.28,-0.09],[5685,229.25167,-9.38306,2.61,-0.11],[5686,229.45792,-30.14889,4.34,1.1],[5687,229.735,-40.78833,5.59,-0.1],[5688,229.67208,-31.20944,6.18,1.23],[5689,229.88167,-37.09667,6.2,0.96],[5690,229.60917,-0.46139,5.89,1.51],[5691,228.65958,67.34667,5.13,0.53],[5692,229.60208,20.57278,5.7,0.97],[5693,228.71625,68.94528,6.51,0.6],[5694,229.82833,1.76528,5.06,0.54],[5695,230.34292,-40.6475,3.22,-0.22],[5696,230.39708,-40.74972,6.2,0.07],[5697,230.37542,-38.21917,6.48,-0.06],[5698,230.53458,-47.92778,5.0,0.5],[5699,230.45083,-48.31778,5.65,0.65],[5700,230.79333,-60.65694,5.67,0.48],[5701,230.22375,-18.15861,6.17,1.02],[5702,229.87583,32.51528,6.32,0.24],[5703,230.25583,-15.54833,6.3,0.41],[5704,230.84458,-59.32083,4.51,0.19],[5705,230.45167,-36.26139,3.56,1.54],[5706,230.19625,-2.41333,6.35,1.06],[5707,230.28167,-5.82472,5.54,1.04],[5708,230.67042,-44.68944,3.37,-0.18],[5709,230.03542,29.61611,5.51,1.02],[5710,230.25833,0.71528,5.35,1.19],[5711,230.27875,24.95778,6.39,1.23],[5712,230.78917,-36.85861,4.54,-0.15],[5713,231.56125,-68.30917,5.89,1.02],[5714,229.27458,71.82389,5.02,1.37],[5715,230.02167,51.95861,5.66,0.11],[5716,230.17333,44.43417,6.19,0.39],[5717,230.59667,12.5675,6.28,-0.02],[5718,230.4525,32.93389,5.37,-0.07],[5719,231.18708,-39.71028,5.37,-0.11],[5720,230.9675,-12.36944,5.72,1.04],[5721,230.93208,-1.0225,6.12,0.25],[5722,231.2775,-38.16944,7.03,0.09],[5723,231.04958,-10.32222,4.94,0.44],[5724,231.33417,-38.73361,4.6,0.0],[5725,231.88792,-64.53139,5.71,1.64],[5726,230.65583,39.58139,5.5,1.59],[5727,230.80125,30.28778,5.58,0.58],[5728,230.80125,30.28778,6.08,0.6],[5729,235.82,-84.46528,5.57,0.11],[5730,232.87833,-73.38944,5.49,-0.12],[5731,230.65542,62.04722,5.98,-0.03],[5732,231.02125,45.27111,6.01,1.2],[5733,231.1225,37.37722,4.31,0.31],[5734,231.12875,37.34778,6.5,0.59],[5735,230.18208,71.83389,3.05,0.05],[5736,231.82583,-36.76778,5.45,-0.15],[5737,230.66,63.34139,5.79,1.27],[5738,232.11333,-51.5975,6.1,1.09],[5739,231.4475,15.42806,5.17,1.66],[5740,231.47208,19.48083,6.27,0.6],[5741,231.5725,34.33583,5.46,1.4],[5742,232.35125,-46.73278,5.24,1.74],[5743,232.06417,-16.71639,5.64,1.55],[5744,231.2325,58.96611,3.29,1.16],[5745,231.91208,25.10167,6.02,1.62],[5746,232.15917,1.84222,5.17,0.23],[5747,231.95708,29.10583,3.68,0.28],[5748,231.63542,54.02,6.45,0.05],[5749,232.65125,-20.72833,6.22,0.17],[5750,232.66833,-16.60944,5.82,1.06],[5751,233.01833,-38.62306,6.25,0.2],[5752,232.18542,47.20139,6.15,0.1],[5753,232.95958,-32.88111,6.46,0.09],[5754,231.92,62.27556,6.5,0.14],[5755,231.96417,60.67028,5.9,1.44],[5756,232.93083,-20.16472,6.22,0.22],[5757,234.82625,-77.91806,6.18,1.2],[5758,232.73083,8.57917,6.57,0.37],[5759,232.23667,55.195,6.43,0.08],[5760,232.59458,31.28611,6.46,0.19],[5761,232.61625,36.80417,6.37,0.6],[5762,233.15292,-19.67056,5.52,0.18],[5763,232.7325,40.83306,5.02,1.59],[5764,233.23,-16.85278,5.5,-0.14],[5765,233.28792,-24.48944,7.0,0.27],[5766,234.0725,-65.61333,6.51,0.35],[5767,233.50667,-40.06611,5.82,1.68],[5768,232.33833,62.09972,6.38,0.6],[5769,232.84333,36.61639,6.38,0.5],[5770,233.04042,16.05611,6.22,-0.05],[5771,234.18,-66.31694,4.11,1.17],[5772,233.24125,-1.18639,5.51,1.09],[5773,233.58708,-39.34944,6.36,-0.03],[5774,232.94583,40.89944,5.02,0.07],[5775,233.65542,-28.04694,5.15,1.3],[5776,233.78542,-41.16694,2.78,-0.2],[5777,233.54458,-10.06444,4.62,1.01],[5778,233.2325,31.35917,4.14,-0.13],[5779,233.58667,-5.695,6.51,0.58],[5780,233.61083,-9.18333,5.17,-0.09],[5781,233.97167,-44.95861,4.54,-0.18],[5782,235.08833,-73.44667,5.65,-0.04],[5783,233.47,17.13778,6.45,0.6],[5784,234.05042,-44.39694,5.43,1.5],[5785,232.73208,64.20861,5.79,0.96],[5786,235.4775,-76.08194,5.95,-0.04],[5787,233.88167,-14.78944,3.91,1.01],[5788,233.70042,10.5375,3.8,0.26],[5789,233.70042,10.53917,3.8,0.26],[5790,234.0475,-33.09278,6.24,0.6],[5791,233.76917,1.66889,6.56,0.28],[5792,235.04792,-70.22778,6.44,0.08],[5793,233.67208,26.71472,2.23,-0.02],[5794,234.25625,-28.135,3.58,1.38],[5795,233.88833,17.65556,6.12,0.94],[5796,233.9725,11.26556,6.07,1.09],[5797,234.51333,-42.5675,4.33,1.42],[5798,234.70625,-52.37278,5.44,0.0],[5799,234.14042,-0.56167,6.51,0.72],[5800,233.8125,39.01,5.11,1.64],[5801,234.36875,-26.28,6.19,0.01],[5802,234.12333,10.01,5.26,0.95],[5803,234.98542,-59.90833,5.95,0.48],[5804,234.12208,16.11889,5.93,0.29],[5805,234.63625,-39.16083,6.57,-0.07],[5806,234.45042,-23.14167,5.78,1.1],[5807,234.67625,-39.12806,6.04,0.21],[5808,233.95542,38.37389,6.42,0.6],[5809,234.56542,-28.20667,6.32,1.17],[5810,234.56792,-21.01611,5.84,1.08],[5811,233.81792,53.92194,5.97,1.18],[5812,234.66417,-29.77778,3.66,-0.17],[5813,234.2225,29.99111,6.52,0.6],[5814,234.72708,-19.30194,5.38,0.86],[5815,234.66667,-8.79444,6.5,0.53],[5816,234.66708,-8.79111,6.48,0.52],[5817,234.01708,52.06972,6.74,0.32],[5818,233.98792,54.63056,5.74,0.6],[5819,234.83875,-23.15028,6.34,0.02],[5820,234.94167,-34.41194,4.67,0.99],[5821,235.24292,-47.73556,6.23,1.63],[5822,235.06417,-31.21361,6.34,1.43],[5823,234.45667,40.35333,5.24,0.88],[5824,235.07042,-23.81806,4.96,1.33],[5825,235.29708,-44.66111,4.64,0.4],[5826,232.85375,77.34944,4.96,1.58],[5827,234.70375,34.675,6.11,1.02],[5828,234.38333,54.50889,5.87,1.07],[5829,232.29583,80.44861,6.58,0.67],[5830,234.5675,46.79778,5.75,0.36],[5831,235.04333,12.05306,6.25,0.94],[5832,235.655,-49.48944,6.04,1.31],[5833,234.8425,36.63667,6.0,0.6],[5834,234.84458,36.63583,5.07,-0.12],[5835,234.64292,50.42333,5.84,0.83],[5836,235.97958,-60.28722,6.48,1.05],[5837,235.65958,-37.425,5.24,0.98],[5838,235.48667,-19.67889,4.74,1.57],[5839,235.67083,-34.71056,4.75,-0.14],[5840,235.24667,16.02472,6.01,0.9],[5841,234.78958,57.92444,6.45,1.09],[5842,235.38792,19.67028,4.52,0.04],[5843,235.4475,12.8475,5.33,0.04],[5844,234.41292,69.28333,5.62,1.35],[5845,235.47792,18.46389,5.81,0.2],[5846,236.09417,-41.81944,5.94,0.0],[5847,235.85375,-15.04333,6.31,1.16],[5848,236.01833,-15.67278,5.41,0.23],[5849,235.68583,26.29556,3.84,0.0],[5850,235.79417,13.66778,6.48,0.86],[5851,236.97333,-65.4425,6.18,0.22],[5852,236.97375,-65.4425,6.39,0.24],[5853,236.0075,2.515,5.88,0.68],[5854,236.06708,6.42556,2.65,1.17],[5855,235.99708,32.51583,5.56,1.06],[5856,236.55333,-28.06167,6.51,0.32],[5857,235.71125,52.36083,5.51,-0.07],[5858,236.17542,17.26417,6.14,0.0],[5859,236.34792,5.44694,5.58,0.04],[5860,236.68417,-34.6825,5.61,-0.11],[5861,236.41542,0.89139,6.33,1.19],[5862,236.85583,-40.19417,6.42,0.88],[5863,236.52333,-1.80444,5.4,-0.03],[5864,236.87083,-37.91639,6.01,0.72],[5865,237.20958,-52.43806,6.07,1.38],[5866,236.68917,-6.12028,6.24,1.17],[5867,236.54708,15.42194,3.67,0.06],[5868,236.61083,7.35306,4.43,0.6],[5869,237.52875,-53.20944,5.77,-0.08],[5870,236.82208,14.11528,5.71,0.09],[5871,237.48958,-48.91194,5.84,0.07],[5872,237.56792,-45.40167,6.12,0.29],[5873,237.77833,-55.05583,5.73,0.06],[5874,237.05542,13.78861,6.0,1.25],[5875,237.23667,-3.81861,5.53,0.12],[5876,238.23625,-65.1525,6.54,0.19],[5877,237.00833,31.73583,6.44,0.6],[5878,236.645,55.47472,5.92,1.39],[5879,237.185,18.14167,4.09,1.62],[5880,237.14333,28.15667,5.85,0.77],[5881,237.405,-3.43028,3.53,-0.04],[5882,237.88125,-47.06056,6.01,1.16],[5883,237.73958,-33.62722,3.95,-0.04],[5884,238.345,-62.60694,6.19,1.47],[5885,237.74458,-25.75139,4.64,-0.05],[5886,236.66667,62.59944,5.19,0.04],[5887,236.90833,55.37667,5.86,0.25],[5888,237.57292,2.19639,5.23,1.02],[5889,237.39875,26.06833,4.63,0.8],[5890,238.21458,-50.61528,6.6,1.53],[5891,238.87333,-68.60306,5.09,1.13],[5892,237.70417,4.47778,3.71,0.15],[5893,238.05333,-29.88667,6.4,0.98],[5894,237.67375,15.13361,5.2,0.6],[5895,237.815,-3.09056,5.11,0.12],[5896,237.91,-14.13361,6.19,1.0],[5897,238.78542,-63.43056,2.85,0.29],[5898,238.71917,-60.74333,6.15,0.1],[5899,237.81625,20.97778,4.76,1.54],[5900,238.88458,-60.17778,5.77,0.35],[5901,237.80792,35.6575,4.82,1.0],[5902,238.33375,-20.16722,5.03,-0.01],[5903,236.01458,77.79444,4.32,0.04],[5904,238.40292,-25.32722,4.59,-0.07],[5905,239.025,-60.48278,5.76,0.06],[5906,238.47417,-24.53306,5.39,-0.02],[5907,238.4825,-23.97806,5.42,-0.04],[5908,238.45625,-16.72944,4.15,1.02],[5909,238.23417,17.40361,6.36,0.6],[5910,238.625,-27.33861,6.14,-0.07],[5911,238.30042,13.19667,6.1,0.6],[5912,238.66458,-25.24361,5.87,-0.06],[5913,238.39542,16.075,6.09,0.6],[5914,238.16875,42.45167,4.62,0.56],[5915,238.75125,-19.38306,5.94,-0.01],[5916,238.87667,-31.08361,6.21,1.37],[5917,238.87542,-26.26583,5.62,0.15],[5918,239.02708,-39.86417,6.03,0.15],[5919,238.66792,8.58028,6.29,0.18],[5920,239.74208,-65.03778,5.75,-0.06],[5921,239.26583,-48.16222,6.31,0.38],[5922,238.06917,55.82667,5.81,0.96],[5923,239.05792,-31.78583,6.29,0.43],[5924,238.64417,20.31083,5.44,1.59],[5925,239.22292,-33.96639,5.12,0.12],[5926,239.22583,-33.96417,5.62,0.05],[5927,239.06,-14.39944,6.37,0.48],[5928,239.22125,-29.21417,3.88,-0.2],[5929,239.33875,-36.185,5.8,1.01],[5930,239.13917,-14.82917,6.13,0.23],[5931,238.91583,18.62056,6.26,-0.11],[5932,238.65792,43.13861,5.37,1.65],[5933,239.11333,15.66167,3.85,0.48],[5934,239.41833,-20.98306,5.85,0.02],[5935,239.62792,-37.50306,6.31,1.01],[5936,238.94833,37.94694,5.45,0.33],[5937,239.975,-54.02111,6.1,0.0],[5938,238.8775,42.56611,5.75,-0.11],[5939,240.29458,-63.77667,6.41,0.78],[5940,239.31083,14.41444,5.54,1.14],[5941,239.5475,-14.27944,4.88,-0.1],[5942,239.645,-24.83139,5.43,-0.09],[5943,239.87625,-41.74444,4.99,1.0],[5944,239.71292,-26.11417,2.89,-0.19],[5945,239.99167,-40.65278,6.49,1.52],[5946,240.27667,-54.57778,6.13,0.25],[5947,239.39708,26.87778,4.15,1.23],[5948,240.03042,-38.39694,3.41,-0.22],[5949,238.95708,58.91167,6.31,-0.02],[5950,239.37458,39.69528,6.31,1.07],[5951,240.71833,-62.54167,6.25,-0.04],[5952,240.22375,-40.43528,6.21,0.01],[5953,240.08333,-22.62167,2.32,-0.12],[5954,240.08167,-16.53333,5.47,0.52],[5955,241.4825,-72.40083,5.7,1.17],[5956,240.33125,-31.88944,6.33,1.45],[5957,239.74042,36.64389,5.62,1.49],[5958,239.87583,25.92028,2.0,0.1],[5959,240.19833,-8.41139,5.55,0.05],[5960,239.4475,54.74972,4.95,0.26],[5961,240.88292,-57.77528,4.63,0.24],[5962,240.80375,-49.22972,4.65,0.92],[5963,240.21292,4.4275,5.83,1.0],[5964,239.76833,49.88111,6.05,0.29],[5965,240.66417,-29.13583,6.03,1.31],[5966,240.30958,17.81833,5.12,0.99],[5967,240.85083,-38.6025,4.89,-0.14],[5968,240.26125,33.30361,5.41,0.6],[5969,240.83583,-25.86528,5.0,1.22],[5970,240.89292,-32.00056,6.01,0.47],[5971,240.36083,29.85111,4.99,-0.07],[5972,240.57375,22.80444,4.83,0.07],[5973,240.97792,-24.72639,6.21,1.38],[5974,241.07417,-33.21444,6.1,0.34],[5975,241.15292,-37.86306,5.9,0.4],[5976,240.94042,4.98667,6.08,0.96],[5977,241.09208,-11.37306,5.07,0.6],[5978,241.09208,-11.37306,4.77,0.47],[5979,241.85,-56.19139,6.16,0.58],[5980,241.6225,-45.17333,4.72,0.23],[5981,240.52292,52.91583,5.93,1.48],[5982,240.69958,46.03667,4.76,-0.11],[5983,240.83083,36.63167,5.83,0.56],[5984,241.35917,-19.80556,2.62,-0.07],[5985,241.36042,-19.80194,4.92,-0.02],[5986,240.47208,58.56528,4.01,0.52],[5987,241.64792,-36.80222,4.23,-0.17],[5988,241.52625,-23.60639,5.92,-0.08],[5989,241.43542,-6.29167,6.53,0.48],[5990,241.49917,-6.13944,6.41,1.03],[5991,241.81833,-36.75556,5.73,0.29],[5992,241.4075,8.09611,6.29,0.08],[5993,241.70167,-20.66917,3.96,-0.04],[5994,242.3275,-57.93444,5.57,-0.04],[5995,240.78875,59.41083,6.19,1.58],[5996,241.76417,-14.07083,6.32,0.66],[5997,241.85125,-20.86861,4.32,0.84],[5998,241.96625,-24.46194,6.33,-0.06],[5999,242.1425,-39.10528,7.05,0.36],[6000,242.14333,-39.09306,6.65,-0.07],[6001,242.03167,-26.32667,5.38,1.65],[6002,241.90167,-12.74556,5.78,0.01],[6003,242.18208,-23.68556,5.88,0.02],[6004,241.90625,9.89167,5.63,0.2],[6005,241.8425,21.8225,6.14,1.37],[6006,242.38208,-32.64972,6.19,0.79],[6007,242.46917,-33.54583,5.54,-0.1],[6008,242.01875,17.04694,5.0,0.95],[6009,242.02042,17.05444,6.25,1.14],[6010,242.11667,8.53417,5.73,1.61],[6011,242.24542,3.45444,5.91,1.47],[6012,242.48,-18.34083,6.47,0.44],[6013,242.19417,17.20583,6.14,0.0],[6014,242.29667,6.37889,5.97,1.0],[6015,242.82375,-41.11972,5.86,0.27],[6016,242.46042,-3.46694,5.37,1.45],[6017,242.75833,-29.41639,5.13,1.12],[6018,242.24292,36.49083,4.76,1.01],[6019,243.34417,-55.54083,5.81,0.34],[6020,245.08667,-78.69583,4.68,1.69],[6021,245.11167,-78.66722,5.27,1.41],[6022,243.32,-53.67167,5.83,1.96],[6023,242.1925,44.935,4.26,-0.07],[6024,243.36958,-54.63056,4.94,1.04],[6025,241.58208,67.81028,5.44,-0.02],[6026,242.99417,-19.44972,6.3,0.13],[6027,242.99875,-19.46056,4.01,0.04],[6028,243.07583,-27.92639,4.59,-0.16],[6029,243.06625,-28.4175,5.67,0.01],[6030,243.85958,-63.68556,3.85,1.11],[6031,243.0,-10.06417,4.94,0.09],[6032,242.87375,9.7125,6.53,0.24],[6033,243.03042,-8.5475,5.43,0.12],[6034,240.88042,76.79361,5.56,0.6],[6035,242.86958,16.66556,6.08,0.02],[6036,242.26208,57.93778,6.33,0.6],[6037,244.27292,-67.94139,5.75,0.15],[6038,242.35833,55.82889,6.49,0.6],[6039,242.90833,23.49472,5.7,1.56],[6040,243.95708,-57.91222,5.63,0.14],[6041,243.23542,-4.22083,6.25,0.13],[6042,243.44042,-24.42194,6.41,0.04],[6043,242.91542,33.3425,6.29,1.2],[6044,243.59292,-33.01139,5.92,1.02],[6045,243.81375,-47.37222,5.14,-0.13],[6046,242.95,36.425,5.63,1.34],[6047,243.31417,5.02111,5.48,1.47],[6048,243.46208,-11.8375,5.22,1.42],[6049,243.85,-42.89972,6.14,1.11],[6050,242.94833,42.37444,5.87,1.45],[6051,243.62,-21.1075,6.41,-0.01],[6052,243.18875,26.67083,6.5,0.39],[6053,243.66292,-18.53528,6.32,1.09],[6054,243.72333,-25.47694,6.05,0.04],[6055,244.17958,-53.81111,5.44,1.73],[6056,243.58625,-3.69444,2.74,1.58],[6057,243.55625,5.90194,6.31,1.15],[6058,244.25375,-50.06833,4.99,0.8],[6059,244.33708,-53.08667,6.33,0.27],[6060,243.90542,-8.36944,5.5,0.65],[6061,243.96458,-14.84917,6.09,0.06],[6062,244.71625,-57.89972,6.49,1.0],[6063,243.67,33.85861,5.64,0.51],[6064,243.67,33.85833,6.66,0.6],[6065,243.86958,18.80833,5.69,1.12],[6066,244.245,-21.30389,6.61,0.02],[6067,244.23042,-3.95333,6.18,0.31],[6068,243.9475,27.42222,6.14,1.31],[6069,243.10458,67.14417,6.21,0.99],[6070,244.57458,-28.61389,4.78,0.02],[6071,244.82375,-42.67389,5.45,0.1],[6072,244.96,-50.15556,4.02,1.08],[6073,245.105,-55.14,5.77,0.97],[6124,246.70875,2.3475,6.07,0.92],[6074,244.18667,29.15028,5.78,0.07],[6075,244.58042,-4.6925,3.24,0.96],[6076,244.78208,-20.21778,6.29,1.08],[6077,244.88625,-30.90667,5.49,0.47],[6078,244.75167,-14.8725,5.94,1.52],[6079,242.70625,75.8775,5.48,-0.11],[6080,245.13583,-39.43083,6.12,-0.07],[6081,245.15917,-24.16944,4.55,0.84],[6082,243.13417,75.21056,6.39,0.6],[6083,245.61667,-49.57222,5.33,-0.04],[6084,245.29708,-25.59278,2.89,0.13],[6085,245.62042,-43.91222,5.88,1.16],[6086,244.31375,59.755,5.4,1.63],[6087,245.01792,21.1325,6.05,0.94],[6088,243.63958,73.395,5.98,0.08],[6089,246.34167,-63.12472,6.15,0.03],[6090,244.79667,49.03806,5.91,1.37],[6091,244.97958,39.70861,5.46,0.4],[6092,244.935,46.31333,3.89,-0.15],[6093,245.51833,1.02917,4.82,0.34],[6094,246.00542,-39.19306,5.4,0.62],[6095,245.48,19.15306,3.75,0.27],[6096,245.66208,-2.07972,6.23,0.07],[6097,245.98625,-33.19944,6.47,0.01],[6098,247.11708,-70.08444,4.91,0.55],[6099,246.22583,-45.34917,6.33,0.19],[6100,246.13208,-37.56583,5.42,-0.11],[6101,244.53917,68.55444,6.41,0.6],[6102,248.3625,-78.89722,3.89,0.91],[6103,245.52417,30.89194,4.85,0.97],[6104,246.02583,-20.0375,4.5,1.01],[6105,246.165,-29.70333,6.63,0.59],[6106,246.16542,-29.70472,5.84,0.59],[6107,245.58917,33.79917,5.2,1.6],[6108,245.62167,33.70361,5.39,1.53],[6109,246.98875,-64.05806,5.27,0.36],[6110,245.73542,32.33306,6.4,0.08],[6111,246.045,6.94806,5.85,-0.01],[6112,246.39667,-23.44722,5.02,0.24],[6113,246.39625,-23.44611,5.92,0.6],[6114,247.06333,-58.59972,5.69,0.0],[6115,246.79625,-47.555,4.47,-0.07],[6116,244.37625,75.75528,4.95,0.37],[6117,246.35417,14.03333,4.57,0.0],[6118,246.75583,-18.45639,4.42,0.28],[6119,246.44875,18.8925,6.7,0.6],[6120,247.4375,-57.75583,6.06,1.48],[6121,246.54792,11.4075,6.11,1.03],[6122,247.06125,-37.17944,5.79,1.1],[6123,246.35083,37.39389,5.54,0.17],[6125,247.70583,-61.63361,5.2,1.23],[6126,245.45292,69.10944,5.25,1.12],[6127,246.10542,55.205,5.74,0.0],[6128,246.93125,-7.59806,5.23,1.72],[6129,246.95042,-8.37167,4.63,0.17],[6130,245.94583,61.69667,5.67,0.96],[6131,247.42625,-46.24333,5.35,0.56],[6132,245.99792,61.51417,2.74,0.91],[6133,258.99708,-87.56639,6.57,0.91],[6134,247.35167,-26.43194,0.96,1.83],[6135,248.58042,-70.98806,5.5,1.22],[6136,247.14167,0.665,5.39,1.46],[6137,247.20375,-8.12889,6.48,0.4],[6138,251.47333,-83.23889,6.57,1.57],[6139,255.24375,-86.36444,6.04,0.05],[6140,247.44542,-14.55083,5.68,0.82],[6141,247.55167,-25.115,4.79,-0.11],[6142,247.92375,-41.81694,5.33,0.33],[6143,247.84542,-34.70444,4.23,-0.16],[6144,247.62458,-7.515,6.5,0.37],[6145,247.845,-26.53778,6.1,1.08],[6146,247.16042,41.88167,5.04,1.52],[6147,247.78458,-16.61278,4.28,0.92],[6148,247.555,21.48972,2.77,0.94],[6149,247.72833,1.98389,3.82,0.01],[6150,247.18083,51.40778,6.29,1.05],[6151,248.93667,-65.49528,5.52,0.93],[6152,247.64,20.47917,5.25,1.29],[6153,248.03417,-21.46639,4.45,0.13],[6154,247.80583,22.19528,5.76,1.61],[6155,248.52083,-44.04528,4.94,0.05],[6156,247.525,48.96083,6.45,0.03],[6157,247.76167,35.225,6.25,1.64],[6158,248.14875,5.52111,5.63,-0.06],[6159,248.15125,11.48806,4.84,1.49],[6160,248.78292,-45.24472,6.46,1.34],[6161,246.99583,68.76806,5.0,-0.06],[6162,247.94708,45.59833,5.65,0.12],[6163,250.76917,-77.5175,4.24,1.06],[6164,249.09375,-42.85889,5.47,0.4],[6165,248.97083,-28.21611,2.82,-0.25],[6166,249.09375,-35.25556,4.16,1.57],[6167,249.71958,-60.99028,6.18,-0.08],[6168,248.52583,42.43694,4.2,-0.01],[6169,248.85958,17.05722,6.41,0.05],[6170,248.10708,60.82333,5.94,0.02],[6171,249.08958,-2.32472,5.75,0.82],[6172,250.34708,-68.29611,5.91,-0.08],[6173,246.42958,78.96389,5.56,0.26],[6174,249.60917,-43.39861,5.83,-0.02],[6175,249.28958,-10.56722,2.56,0.02],[6176,249.17917,15.49806,6.3,-0.11],[6177,250.21042,-60.44611,6.18,0.48],[6178,249.77167,-37.2175,5.91,0.0],[6179,249.50667,-6.53806,6.09,0.16],[6180,247.86708,72.61194,6.3,1.32],[6181,249.45,13.68694,6.31,0.41],[6182,250.84208,-67.4325,6.03,0.02],[6183,249.04667,46.61333,5.79,1.04],[6184,249.04792,52.90028,5.53,-0.07],[6185,249.05708,52.92444,5.08,-0.04],[6186,249.05875,52.92417,6.53,-0.06],[6187,250.33458,-48.76306,5.65,0.13],[6188,250.4175,-49.65167,5.65,-0.03],[6189,249.91292,-9.55444,6.35,0.48],[6190,250.14375,-20.40861,6.26,1.08],[6191,247.66167,77.44667,6.34,1.0],[6192,250.43958,-33.14639,5.87,0.65],[6193,250.40083,-24.46806,6.09,0.2],[6194,250.14625,4.20722,6.93,0.13],[6195,250.16125,4.21972,5.77,-0.02],[6196,250.39333,-17.74222,4.96,1.11],[6197,250.76417,-46.07056,6.23,0.85],[6198,249.22917,63.07278,6.16,1.53],[6199,249.50167,56.01556,5.29,1.08],[6200,249.68708,48.92833,4.9,1.55],[6201,250.29792,-1.00056,6.24,0.3],[6202,250.47375,-19.92444,5.57,0.43],[6203,250.21417,12.395,6.08,0.05],[6204,251.66667,-67.10972,5.13,-0.08],[6205,250.42708,1.18111,5.74,0.32],[6206,250.93958,-41.11889,6.2,0.14],[6207,251.16583,-53.1525,5.96,1.26],[6208,250.2525,24.85861,6.06,1.31],[6209,250.97458,-41.11306,6.12,-0.07],[6210,250.94833,-38.15667,6.05,-0.06],[6211,250.91125,-32.10611,6.46,-0.08],[6212,250.32167,31.60306,2.81,0.65],[6213,250.40292,26.91694,5.92,0.4],[6214,251.1775,-40.83972,5.71,-0.12],[6215,251.58833,-58.50361,5.74,-0.09],[6216,251.07208,-27.45611,6.58,0.1],[6217,252.16625,-69.02778,1.92,1.44],[6218,251.25083,-28.50972,6.02,0.09],[6219,251.83125,-58.34139,5.58,-0.08],[6220,250.72417,38.92222,3.53,0.92],[6221,251.69917,-39.37722,5.48,0.98],[6222,250.96542,34.03889,5.99,0.29],[6223,250.22958,64.58917,4.83,1.22],[6224,251.37375,1.02028,6.03,-0.02],[6225,251.71375,-25.52861,6.71,1.18],[6226,250.74333,55.69028,6.16,0.07],[6227,251.34375,15.74528,5.56,1.66],[6228,251.45792,8.5825,5.15,1.53],[6229,252.44625,-59.04139,3.76,1.57],[6230,251.29875,43.21722,6.05,1.4],[6231,253.0725,-67.68194,6.32,1.28],[6232,251.79083,2.06444,6.1,0.14],[6233,252.97458,-65.37556,6.13,-0.02],[6234,251.94333,5.24667,5.24,-0.02],[6235,252.1125,-14.90944,6.03,0.2],[6236,252.64958,-50.04556,6.47,0.31],[6237,251.32417,56.78194,4.85,0.38],[6238,249.47042,78.91833,6.32,1.14],[6239,252.03708,13.59056,6.35,0.87],[6240,252.36583,-15.6675,6.1,0.6],[6241,252.54083,-34.29333,2.29,1.15],[6242,251.8325,42.23889,5.87,1.61],[6243,252.45833,-10.78306,4.65,0.47],[6244,252.74917,-37.51444,6.11,0.12],[6245,252.89042,-41.23056,5.22,0.07],[6246,252.39417,13.26139,5.91,0.03],[6247,252.9675,-38.0475,3.08,-0.2],[6248,252.59292,-2.65389,6.32,0.42],[6249,253.07958,-41.85444,6.49,0.23],[6250,252.58083,7.24778,5.49,0.1],[6251,253.50208,-57.90944,5.94,1.59],[6252,253.08375,-38.0175,3.57,-0.21],[6253,253.85292,-63.26972,6.02,0.05],[6254,252.30917,45.98333,4.82,0.09],[6255,252.85375,1.21611,5.51,0.05],[6256,252.41875,43.43056,6.13,1.26],[6257,253.42708,-43.05083,5.96,1.73],[6258,252.6625,29.80667,5.72,1.6],[6259,252.67958,32.55361,6.13,1.01],[6260,253.5075,-41.80639,5.45,0.19],[6261,253.495,-41.99472,6.32,0.5],[6262,253.49875,-42.36222,4.73,0.49],[6263,253.54917,-41.85028,6.45,0.2],[6264,252.65042,41.89667,6.29,1.08],[6265,253.58167,-41.82,6.59,0.21],[6266,253.61208,-42.47889,5.88,0.64],[6267,250.775,77.51417,5.98,0.42],[6268,253.02042,14.97417,6.52,-0.05],[6269,253.355,-20.41556,5.88,0.68],[6270,252.93875,24.65639,5.04,1.25],[6271,253.64583,-42.36139,3.62,1.37],[6272,253.74333,-41.15111,5.77,0.15],[6273,253.65,-30.58722,6.35,0.21],[6274,254.03708,-50.675,6.33,-0.02],[6275,254.11958,-52.28389,5.94,-0.08],[6276,254.89083,-69.26833,5.79,-0.1],[6277,253.54417,-1.61222,6.25,0.28],[6278,253.66792,-11.7925,6.57,0.14],[6279,253.24208,31.70167,5.32,0.29],[6280,253.64875,-6.15389,5.25,1.08],[6281,253.50208,10.16528,4.38,-0.08],[6282,253.99083,-33.50722,6.37,1.71],[6283,254.15,-40.82361,6.15,0.26],[6284,254.0075,-16.80611,6.37,0.92],[6285,254.655,-55.99028,3.13,1.6],[6286,253.32333,47.41694,6.0,1.32],[6287,253.73,20.95861,5.41,0.97],[6288,254.29625,-33.25944,5.48,1.59],[6289,254.575,-50.64111,5.55,0.02],[6290,253.81667,13.61972,6.34,0.34],[6291,254.2,-23.15,5.58,-0.02],[6292,253.75875,25.73056,6.08,0.92],[6293,253.8425,18.43333,5.35,1.41],[6294,254.26667,-19.54,6.27,0.07],[6295,254.89625,-53.16056,4.06,1.45],[6296,254.35875,-10.96333,6.19,1.0],[6297,255.02583,-54.59694,5.65,0.19],[6298,254.71833,-37.62111,6.09,0.19],[6299,254.41708,9.375,3.2,1.15],[6300,255.11292,-48.64778,6.0,0.88],[6301,254.38333,13.88417,6.37,0.94],[6302,254.67333,-14.86972,6.59,0.4],[6303,255.13458,-45.45167,6.65,1.8],[6304,255.4475,-58.95833,6.11,-0.03],[6305,254.37917,25.35278,6.28,0.9],[6306,254.02667,50.03889,6.56,1.62],[6307,254.42625,24.38139,6.32,1.1],[6308,254.99,-25.09194,5.86,1.62],[6310,255.03958,-24.98917,5.75,0.41],[6311,255.15375,-35.93417,5.97,1.16],[6312,255.44208,-51.13083,6.45,0.27],[6313,254.45917,42.5125,6.34,1.28],[6314,255.78667,-53.23694,5.29,0.48],[6315,254.00708,65.13472,4.89,0.48],[6316,255.46958,-32.14361,5.03,-0.1],[6317,255.1225,6.58361,6.59,0.23],[6318,255.265,-4.2225,4.82,1.48],[6319,254.105,65.03917,6.41,0.38],[6320,256.10292,-57.71222,5.73,-0.1],[6321,255.46333,-18.88556,6.26,1.38],[6322,251.49208,82.03722,4.23,0.89],[6323,255.92375,-47.16,6.06,0.6],[6324,255.0725,30.92639,3.92,-0.01],[6325,255.24208,22.63222,5.65,1.33],[6326,255.3875,14.94944,6.31,0.04],[6327,255.96167,-38.15222,5.91,0.38],[6328,255.29,27.19639,6.55,0.41],[6329,255.49625,8.45056,6.33,0.09],[6330,254.83958,56.68861,6.03,1.16],[6331,256.27208,-45.50167,6.28,0.07],[6332,255.40167,33.56833,5.25,0.02],[6333,255.57792,25.50556,5.75,1.02],[6334,256.20583,-34.12278,4.87,0.26],[6335,254.07,73.12778,6.3,0.24],[6336,255.57167,31.88472,6.36,1.13],[6337,255.7825,14.09194,4.98,1.6],[6338,256.4525,-44.105,6.19,0.28],[6339,255.79333,14.51111,6.52,0.76],[6340,256.18875,-20.49472,6.3,-0.02],[6341,255.91375,13.60528,5.93,0.0],[6342,255.99167,13.5675,6.08,1.03],[6343,255.96958,19.69056,6.35,1.52],[6344,256.58458,-37.2275,5.98,0.07],[6345,254.76083,69.18639,6.4,0.6],[6346,255.87583,35.41417,6.69,0.6],[6347,256.61833,-35.45111,6.13,0.5],[6348,255.31958,60.64917,6.13,1.0],[6349,256.32042,0.7025,6.01,0.58],[6350,256.54917,-21.56472,6.3,0.13],[6351,255.97292,34.79028,6.04,0.22],[6352,256.17208,19.59917,6.17,-0.01],[6353,256.38458,-0.89194,5.64,0.16],[6354,256.72167,-26.51306,6.29,-0.04],[6355,256.34458,12.74083,4.91,0.12],[6356,257.52625,-61.67556,6.39,-0.04],[6357,258.0825,-70.72111,6.22,1.06],[6358,256.54042,9.73333,6.37,1.45],[6359,256.55458,10.45417,6.37,0.87],[6360,255.56375,64.60056,6.1,0.96],[6361,256.72042,-1.65639,6.38,0.2],[6362,256.27083,43.81222,6.43,0.09],[6363,256.2075,48.80417,6.09,1.09],[6364,256.57542,22.08417,5.56,1.3],[6365,257.06208,-17.60917,5.99,1.01],[6366,257.19792,-30.40361,5.97,0.25],[6367,257.05667,-1.07944,6.06,0.08],[6368,258.32292,-67.19667,5.89,1.06],[6369,256.3325,54.47028,5.83,0.6],[6370,256.33208,54.47028,5.8,0.48],[6371,257.67625,-44.5575,5.08,0.86],[6372,257.22708,-3.88278,6.36,0.69],[6373,259.14833,-74.53306,6.25,-0.01],[6374,257.91208,-48.87417,5.84,1.84],[6375,257.45,-10.52333,5.56,0.52],[6376,256.94458,40.51611,6.34,0.03],[6377,257.00833,35.93528,5.39,0.31],[6378,257.59458,-15.72472,2.43,0.06],[6379,255.4175,75.29722,6.21,0.6],[6380,258.03833,-43.23917,3.33,0.41],[6381,258.0675,-39.50694,5.67,0.04],[6382,258.06875,-38.82222,6.3,1.05],[6383,257.07125,50.84222,6.46,0.0],[6384,258.555,-56.88833,6.09,1.78],[6385,257.69083,12.46722,6.57,0.08],[6386,258.05667,-25.25472,6.54,-0.04],[6387,258.10417,-27.76194,6.14,-0.04],[6388,257.38875,40.77722,5.08,1.28],[6389,258.24458,-32.43861,6.01,0.07],[6390,257.93833,7.89472,6.33,1.04],[6391,257.76333,24.23778,6.19,0.23],[6392,258.61542,-39.76694,6.6,2.21],[6393,258.11583,10.58528,5.33,1.59],[6394,258.22667,0.35194,6.65,0.5],[6395,257.6275,52.40889,6.29,0.0],[6396,257.19667,65.71472,3.17,-0.12],[6397,258.83042,-33.54833,5.53,-0.01],[6398,258.89958,-38.59389,5.96,0.58],[6399,257.9175,49.74667,6.04,0.6],[6400,260.05333,-70.04556,6.53,0.6],[6401,258.8375,-26.60278,5.11,0.86],[6402,258.83667,-26.60139,5.07,0.85],[6403,258.96458,-30.21056,6.21,-0.03],[6404,258.83458,-14.58389,5.99,1.1],[6405,259.08958,-35.74944,6.12,0.48],[6406,258.66208,14.39028,3.48,1.44],[6407,258.66333,14.39,5.39,0.6],[6408,259.80167,-59.69444,5.91,1.37],[6409,259.26542,-32.66278,5.55,0.5],[6410,258.75792,24.83917,3.14,0.08],[6411,260.52458,-70.12333,5.41,-0.04],[6412,259.05917,2.18611,6.17,0.22],[6413,259.17833,-6.245,6.09,1.1],[6414,259.13208,1.21056,5.88,0.06],[6415,259.15292,-0.44528,4.73,1.14],[6416,259.76333,-46.63389,5.48,0.8],[6417,260.4975,-67.77056,4.78,1.21],[6418,258.76167,36.80917,3.16,1.44],[6419,258.92333,23.74278,5.96,1.31],[6420,259.7,-44.12972,5.76,-0.05],[6421,258.13583,62.87444,5.56,0.21],[6422,259.585,-32.55333,6.36,0.15],[6423,259.87625,-50.06333,6.27,0.41],[6424,259.50292,-24.28694,5.2,1.1],[6425,259.50208,-24.28417,6.8,0.51],[6426,259.73833,-34.98972,5.91,1.04],[6427,259.85208,-44.22306,6.65,0.2],[6428,259.58,-16.31194,6.43,1.66],[6429,262.86375,-80.85917,5.88,1.67],[6430,259.39958,23.09083,6.45,0.6],[6431,259.33125,33.1,4.82,-0.17],[6432,259.52083,17.31806,6.0,0.01],[6433,259.65417,10.86444,5.03,1.55],[6434,259.72,6.08528,6.51,0.39],[6435,259.97208,-17.75639,6.02,0.04],[6436,259.41792,37.29167,4.65,0.05],[6437,259.2025,49.69111,7.48,0.6],[6438,260.72917,-58.01028,5.88,1.07],[6439,259.99792,-5.91722,6.32,0.85],[6440,261.00458,-62.86417,5.7,-0.14],[6441,260.1425,-19.33278,6.52,0.58],[6442,260.77875,-56.52528,5.8,1.0],[6443,259.70208,28.82306,5.65,0.98],[6444,259.59708,38.81139,5.94,1.0],[6445,260.25083,-21.11278,4.39,0.39],[6446,260.20708,-12.84694,4.33,0.03],[6447,261.07792,-60.67361,5.77,-0.08],[6448,259.1225,60.67056,6.32,1.09],[6449,260.21958,-10.69611,6.46,0.33],[6450,260.66417,-37.805,6.41,0.64],[6451,260.81708,-47.46833,5.25,-0.11],[6452,260.07875,18.05722,5.0,1.62],[6453,260.5025,-24.99944,3.27,-0.22],[6454,260.65833,-35.91,6.47,0.54],[6455,260.04083,25.5375,5.38,0.03],[6456,260.72875,-37.22056,5.93,1.08],[6457,260.22583,24.49944,5.12,-0.03],[6458,260.165,32.46778,5.39,0.62],[6459,260.84,-28.14306,5.35,1.55],[6460,261.05417,-44.1625,5.12,-0.06],[6461,261.325,-55.53,2.85,1.46],[6462,261.34833,-56.3775,3.34,-0.13],[6463,260.38917,16.73083,6.35,1.61],[6464,260.08792,46.24083,5.59,1.57],[6465,260.71375,-2.38833,6.29,0.68],[6466,260.38,28.75806,6.35,0.7],[6467,260.14,48.18833,6.43,0.43],[6468,261.5,-50.63361,5.23,1.06],[6469,260.43167,39.97444,5.51,0.68],[6470,261.26125,-34.69639,6.16,0.02],[6471,262.03208,-63.03639,6.24,-0.09],[6472,261.175,-21.44167,5.85,0.93],[6473,261.15458,-18.44583,6.21,0.03],[6474,261.27583,-24.24361,6.19,1.1],[6475,261.73458,-51.94917,6.19,-0.03],[6476,260.99,8.85278,5.77,1.25],[6528,262.73083,31.15833,5.61,0.95],[6477,261.71458,-45.84361,5.29,-0.07],[6478,261.80167,-50.63028,5.92,0.08],[6479,260.43917,53.42056,5.67,1.47],[6480,261.0275,22.96028,5.74,0.21],[6481,261.13125,16.30111,5.71,0.07],[6482,261.14083,15.60611,6.35,-0.02],[6483,261.99,-52.29722,5.75,1.17],[6484,260.91958,37.14667,5.47,0.6],[6485,260.92083,37.14583,4.52,-0.03],[6486,261.5925,-24.17528,4.17,0.28],[6487,262.16125,-55.16972,5.94,1.11],[6488,261.00917,38.58278,6.49,0.73],[6489,261.49125,-1.65167,6.44,0.46],[6490,261.73,-25.94333,6.44,-0.06],[6491,261.11292,36.95194,6.28,0.88],[6492,261.83875,-29.86694,4.29,0.4],[6493,261.65792,-5.08667,4.54,0.39],[6494,261.90625,-29.72444,6.0,0.0],[6495,261.47667,16.9175,5.98,1.62],[6496,261.75875,-12.5125,6.21,0.51],[6497,261.57917,7.59556,6.06,0.58],[6498,261.62875,4.14028,4.34,1.5],[6499,261.50375,26.87889,6.41,0.1],[6500,262.77458,-60.68389,3.62,-0.1],[6501,262.23375,-36.77833,6.02,1.11],[6502,261.70458,20.08083,5.54,-0.13],[6503,262.35708,-38.51667,6.39,0.09],[6504,262.00958,-8.20833,6.37,0.58],[6505,262.84583,-56.92056,5.95,-0.08],[6506,261.6925,34.69583,5.94,-0.01],[6507,262.20708,0.33056,5.44,0.22],[6508,262.69083,-37.29583,2.69,-0.22],[6509,261.68417,48.26,5.85,0.12],[6510,262.96042,-49.87611,2.95,-0.17],[6511,261.42208,60.04833,5.65,0.03],[6512,262.4475,-5.91972,6.37,0.93],[6513,262.95458,-46.03639,6.03,0.83],[6514,261.52042,58.65194,6.51,0.14],[6516,262.59917,-1.0625,5.31,0.72],[6517,262.9475,-33.70278,6.44,1.19],[6518,261.25083,67.30639,6.43,0.76],[6519,262.85417,-23.96278,4.81,0.0],[6520,262.935,-26.26972,6.05,-0.06],[6521,262.59333,11.925,6.39,0.06],[6522,263.10208,-34.27972,6.17,0.35],[6523,263.28083,-41.17361,5.84,0.06],[6524,262.83875,2.72444,5.59,0.84],[6525,263.89542,-59.84611,6.28,-0.08],[6526,262.68458,26.11056,4.41,1.44],[6527,263.40208,-37.10389,1.63,-0.22],[6529,259.90417,80.13639,5.72,1.5],[6530,263.83333,-53.35278,6.1,0.02],[6531,262.66792,38.88222,6.43,0.49],[6532,263.0625,11.93028,6.42,-0.01],[6533,262.95667,28.4075,5.62,0.0],[6534,263.37458,-5.74472,5.62,0.18],[6535,263.67708,-32.58167,5.7,0.04],[6536,262.60833,52.30139,2.79,0.98],[6537,263.915,-46.50556,4.59,-0.03],[6538,263.00458,34.27083,6.56,0.65],[6539,263.92917,-37.44,6.48,0.01],[6540,262.6825,57.87639,6.4,0.6],[6541,263.345,19.25667,5.64,0.48],[6542,263.41417,16.3175,5.69,1.01],[6543,263.42833,14.84167,6.48,1.6],[6544,263.69333,-11.24194,5.55,0.02],[6545,263.82708,-22.04389,6.57,0.01],[6546,264.13667,-38.63528,4.29,1.09],[6547,264.36333,-50.06,5.93,1.1],[6548,263.65292,9.58667,5.81,0.02],[6549,264.52333,-54.50028,5.25,0.2],[6550,263.28042,41.24361,5.74,1.09],[6551,263.61333,16.50389,6.4,0.6],[6552,270.39208,-85.21472,6.45,0.44],[6553,264.33,-42.99778,1.87,0.4],[6554,263.04417,55.18417,4.88,0.26],[6555,263.06667,55.17306,4.87,0.28],[6556,263.73375,12.56,2.08,0.15],[6557,264.36167,-38.06556,6.26,1.25],[6558,264.53542,-42.88056,6.1,-0.06],[6559,263.99833,20.99611,6.1,0.19],[6560,263.38208,57.55889,6.17,0.59],[6561,264.39667,-15.39861,3.54,0.26],[6562,264.40083,-15.57111,5.94,0.37],[6563,263.92667,37.30167,6.1,0.98],[6564,264.03292,28.18472,6.38,0.6],[6565,266.08208,-72.22083,6.49,0.48],[6566,262.99125,68.135,5.05,1.08],[6567,264.46125,-8.11889,4.62,0.11],[6568,264.53958,-10.92639,5.75,1.23],[6569,265.09833,-49.41556,4.77,0.4],[6570,264.15292,30.78528,6.02,0.16],[6571,264.37958,24.31,5.77,0.11],[6572,265.31792,-46.92194,5.79,-0.01],[6573,263.74792,61.875,5.23,0.61],[6574,264.15667,48.58556,5.37,1.15],[6575,264.78542,2.02806,6.26,1.03],[6576,265.51625,-50.51056,6.24,1.76],[6577,264.74083,13.32917,6.12,0.56],[6578,265.04917,-2.1525,6.19,1.67],[6579,264.70708,32.73944,6.37,0.6],[6580,265.62208,-39.03,2.41,-0.22],[6581,265.35375,-12.87528,4.26,0.08],[6582,266.43333,-64.72389,3.62,1.19],[6583,265.7125,-36.94583,5.54,1.55],[6584,264.98958,31.2025,6.03,1.58],[6585,266.03625,-51.83417,5.15,0.7],[6586,266.2325,-57.54528,6.01,0.9],[6587,265.77875,-33.05111,6.4,1.81],[6588,264.86625,46.00639,3.8,-0.18],[6589,265.29583,15.17806,6.34,0.03],[6590,265.38458,6.31278,5.95,1.26],[6591,265.17167,31.2875,6.28,1.05],[6592,265.27292,24.51333,6.36,1.2],[6593,265.82417,-27.88417,6.36,0.47],[6594,265.49458,15.95194,5.52,0.38],[6595,265.8575,-21.68333,4.87,0.47],[6596,264.23792,68.75806,4.8,0.43],[6597,266.175,-42.72889,5.87,0.16],[6598,264.16542,69.57083,6.42,0.58],[6599,265.15667,43.47083,6.59,0.6],[6600,265.9525,-13.50861,6.39,0.37],[6601,265.94583,-7.07944,6.3,0.38],[6602,265.61833,24.56417,5.52,1.46],[6603,265.86833,4.56722,2.77,1.16],[6604,265.84167,14.295,6.24,0.42],[6605,265.15083,57.31028,6.77,0.92],[6606,264.28667,72.45583,5.86,1.01],[6607,265.34083,51.81806,5.99,1.05],[6608,265.84,24.32778,5.71,0.65],[6609,266.14167,2.57944,6.17,0.07],[6610,266.1475,2.57889,6.56,0.6],[6611,266.07208,14.41028,6.19,0.21],[6612,265.77333,44.08444,6.34,1.54],[6613,266.78042,-38.11167,6.43,-0.02],[6614,267.15875,-55.40167,6.11,0.28],[6615,266.89625,-40.12694,3.03,0.51],[6616,266.89,-27.83083,4.54,0.8],[6617,266.94,-22.47806,6.18,1.49],[6618,265.99708,53.80167,5.75,0.01],[6619,266.41792,31.50472,6.23,0.0],[6620,266.90333,-14.72583,5.94,0.05],[6621,267.11583,-26.975,6.35,0.12],[6622,267.6175,-53.61222,5.92,-0.08],[6623,266.61458,27.72056,3.42,0.75],[6624,267.89792,-60.16444,5.78,1.0],[6625,266.47375,38.88139,6.52,1.0],[6626,266.49375,39.3225,6.68,1.39],[6627,266.78333,17.69722,5.72,0.01],[6628,267.29375,-31.70333,4.83,-0.04],[6629,266.97333,2.70722,3.75,0.04],[6630,267.46458,-37.04333,3.21,1.17],[6631,267.54667,-40.09056,4.81,0.26],[6632,267.79583,-53.13056,6.09,0.0],[6633,267.08417,3.80417,6.22,0.16],[6634,268.32667,-65.48917,6.49,1.08],[6635,269.42417,-76.1775,6.07,1.2],[6636,265.48458,72.14889,4.58,0.42],[6637,265.49167,72.15694,5.79,0.53],[6638,267.10333,20.56556,5.69,0.94],[6639,267.32875,1.96111,6.47,1.24],[6640,267.93542,-45.60056,6.11,0.95],[6641,266.78375,47.61222,6.43,0.11],[6642,267.19958,19.25528,6.12,0.02],[6643,267.88667,-40.7725,5.96,1.59],[6644,267.205,25.62278,5.12,1.16],[6645,267.80208,-30.55722,6.66,0.04],[6646,271.36125,-81.48639,6.35,1.5],[6647,268.05667,-34.79917,5.9,-0.1],[6648,268.08208,-34.41667,5.84,1.13],[6649,268.21958,-41.99667,6.2,0.54],[6650,267.68125,11.94667,6.17,1.25],[6651,268.205,-34.11417,6.06,1.23],[6652,268.23292,-35.01861,6.45,0.02],[6653,268.24125,-35.62417,6.03,0.34],[6654,267.59542,29.32222,5.5,1.05],[6655,267.70167,22.31639,5.98,0.6],[6656,267.26792,50.78111,5.02,0.02],[6657,268.33167,-34.73056,6.17,-0.03],[6658,268.34708,-34.89528,5.6,1.14],[6659,267.99792,-1.23667,6.35,1.12],[6660,268.43958,-34.78583,6.38,0.0],[6661,268.16167,-6.14361,6.21,1.4],[6662,268.47875,-34.7525,5.96,-0.01],[6663,268.49208,-34.83167,6.42,0.01],[6664,267.51375,48.39417,6.68,-0.11],[6665,267.99375,15.32583,6.46,0.6],[6666,268.265,-10.89972,6.18,1.11],[6667,268.1475,1.305,5.95,1.58],[6668,268.61333,-34.46639,5.96,-0.01],[6669,267.80833,40.0725,6.46,0.6],[6670,268.30917,6.10139,5.77,0.42],[6671,268.78292,-36.47583,6.06,0.08],[6672,268.725,-24.88722,6.2,0.04],[6673,268.01958,39.98222,6.04,1.33],[6674,268.00417,46.64333,6.38,1.09],[6675,269.1975,-44.34222,4.86,1.21],[6676,268.55917,11.13056,6.38,0.45],[6677,268.32542,40.00806,5.16,1.18],[6678,269.2325,-40.30556,6.43,1.43],[6679,268.97917,-18.80222,6.52,0.05],[6680,269.17458,-28.06528,5.8,0.21],[6681,269.07917,-15.8125,5.89,0.05],[6682,269.44875,-41.71611,4.88,1.65],[6683,269.49083,-39.13694,6.29,0.01],[6684,269.07667,0.67028,5.82,0.09],[6685,268.855,26.05,5.46,0.34],[6686,269.19875,-4.08194,5.47,1.16],[6687,268.96167,22.46417,5.58,1.24],[6688,268.38208,56.87278,3.75,1.18],[6689,269.26792,0.06639,5.97,0.1],[6690,269.23333,6.48778,6.29,0.0],[6691,269.73167,-36.85833,5.74,0.9],[6692,269.66292,-28.75917,6.01,-0.08],[6693,269.77167,-30.25306,5.16,1.77],[6694,269.77292,-30.25333,7.04,1.06],[6695,269.06333,37.25056,3.86,1.35],[6696,269.36208,11.04417,6.36,0.11],[6697,269.30958,23.99583,6.3,0.63],[6698,269.75667,-9.77361,3.34,0.99],[6699,268.84875,55.97139,6.1,0.32],[6700,269.94833,-23.81611,4.76,-0.04],[6701,267.3625,76.96278,5.04,0.49],[6702,269.20167,45.35083,6.02,1.64],[6703,269.44125,29.24778,3.7,0.94],[6704,270.00042,-20.33917,6.21,1.4],[6705,269.15167,51.48889,2.23,1.52],[6706,269.90292,-4.82139,5.87,1.56],[6707,269.62583,30.18944,4.41,0.39],[6708,270.45125,-36.37778,6.3,-0.03],[6709,270.06458,0.62944,6.37,0.15],[6710,270.12083,-3.69028,4.62,0.38],[6711,269.67625,36.28778,6.0,0.94],[6712,270.06583,4.36861,4.64,-0.03],[6713,270.01417,16.75083,4.67,1.26],[6714,270.16125,2.93167,3.97,0.02],[6715,270.34625,-17.15694,6.28,1.8],[6716,270.47667,-22.78056,5.77,0.0],[6717,267.54375,78.30667,6.24,1.44],[6718,269.71792,45.47611,6.48,-0.08],[6719,270.22042,6.26833,6.34,-0.08],[6720,270.11542,19.50583,6.5,-0.06],[6721,283.69542,-87.60583,5.28,1.28],[6722,270.23833,15.09333,6.26,0.69],[6723,270.43833,1.30528,4.45,0.02],[6724,270.71292,-24.28222,5.34,0.52],[6725,268.79667,72.005,5.45,0.3],[6726,270.15167,33.21389,5.99,1.51],[6727,270.75708,-22.71833,6.74,-0.05],[6728,269.98417,45.50139,5.67,1.57],[6729,270.37458,21.59528,5.18,0.95],[6730,270.37667,21.59556,4.96,0.12],[6731,272.81542,-75.89139,5.86,1.24],[6732,270.69292,-5.35861,6.76,0.16],[6733,270.77083,-8.18028,5.94,0.6],[6734,270.77042,-8.18056,5.24,0.38],[6735,268.61083,75.17083,6.36,0.98],[6736,270.96833,-24.36056,5.97,0.0],[6737,270.39958,33.31139,6.15,1.55],[6738,270.59625,20.83361,5.28,-0.09],[6739,271.21,-35.90139,6.0,1.16],[6740,271.95125,-64.55,6.41,1.26],[6741,270.62542,22.92306,6.21,-0.1],[6742,271.25542,-29.58,4.69,0.78],[6743,271.65792,-50.09167,3.66,-0.08],[6744,270.81125,19.61306,6.5,0.01],[6745,272.145,-63.66833,4.35,0.22],[6746,271.45208,-30.42417,2.99,1.0],[6747,271.15542,1.91917,6.14,0.0],[6748,271.59875,-36.01972,5.95,0.62],[6749,271.7075,-43.42472,5.77,0.22],[6750,271.7075,-43.42472,5.77,0.6],[6751,273.14375,-73.67167,5.85,0.46],[6752,271.36375,2.49944,4.03,0.86],[6753,270.7875,48.46444,6.21,0.03],[6754,271.1675,23.9425,6.34,0.3],[6755,271.53083,-8.32389,5.85,0.21],[6756,271.56333,-4.75139,5.77,0.96],[6757,271.53083,-0.44667,6.34,1.06],[6758,271.43042,12.00389,7.04,0.3],[6759,272.12542,-45.76722,6.15,-0.08],[6760,272.49,-59.04,6.38,1.55],[6761,272.60875,-62.00222,5.49,0.58],[6762,271.7975,-21.44389,6.28,0.12],[6763,271.37583,21.64694,6.15,1.23],[6764,271.18,40.08417,6.52,0.46],[6765,271.50792,22.21889,5.06,1.58],[6766,272.02083,-28.45722,4.57,0.94],[6767,271.25333,41.94667,6.34,0.26],[6768,271.45667,32.23056,5.71,1.16],[6769,271.95167,-17.15417,5.52,1.11],[6770,271.82667,8.73389,4.64,0.96],[6771,271.8375,9.56389,3.73,0.12],[6772,272.34333,-36.6725,6.58,-0.03],[6773,272.22542,-25.4725,6.61,0.02],[6774,273.60042,-70.75139,6.73,-0.03],[6775,271.75625,30.56194,5.04,0.52],[6776,271.95167,13.07111,6.63,0.07],[6777,272.49958,-32.71972,6.43,1.02],[6778,272.76875,-47.51306,6.07,1.2],[6779,271.88583,28.7625,3.83,-0.03],[6780,272.52375,-30.72861,5.53,0.97],[6781,271.95625,26.10139,5.86,0.12],[6782,271.95625,26.0975,5.9,0.14],[6783,272.8075,-45.95444,4.53,1.01],[6784,272.14042,14.28472,6.37,0.17],[6785,272.43083,-13.93444,6.39,1.42],[6786,272.77333,-41.35917,5.86,0.29],[6787,272.18958,20.81444,4.36,-0.16],[6788,272.73,-33.79972,6.16,-0.14],[6789,263.05375,86.58639,4.36,0.02],[6790,271.72292,50.82278,6.29,1.04],[6791,271.87,43.46167,5.0,0.91],[6792,271.77625,49.71056,6.32,0.6],[6793,272.00917,36.40139,5.48,1.17],[6794,272.22042,20.04528,5.1,0.15],[6795,272.39083,3.99333,5.73,0.37],[6796,273.5675,-63.68944,6.47,1.41],[6797,272.475,3.11972,5.69,0.47],[6798,272.81167,-19.84222,6.36,0.16],[6799,272.2925,30.46944,6.38,1.19],[6800,272.66792,3.32417,5.51,1.19],[6801,272.93083,-23.70111,4.98,1.05],[6802,272.9925,-28.90139,6.51,-0.01],[6803,272.53625,16.47667,6.09,0.6],[6804,273.3025,-41.33611,5.47,-0.17],[6805,273.92042,-63.05556,5.6,0.92],[6806,272.40625,38.4575,6.4,0.87],[6807,272.49583,36.46639,5.58,0.91],[6808,274.50375,-68.22917,6.33,-0.04],[6809,270.01417,80.00083,6.04,0.51],[6810,270.03833,80.00417,5.68,0.5],[6811,262.7,86.96806,5.79,0.25],[6812,273.44083,-21.05889,3.86,0.23],[6813,273.29167,-4.01167,6.59,0.27],[6814,272.93792,33.44694,5.88,0.01],[6815,272.97583,31.40528,4.97,1.65],[6816,273.56625,-21.71306,5.44,1.52],[6817,272.63167,54.28667,5.95,0.94],[6818,273.96958,-44.20667,5.46,0.96],[6819,274.28125,-56.02333,5.33,-0.05],[6820,273.31875,21.88028,6.12,1.47],[6821,274.25375,-51.06833,6.06,-0.06],[6822,273.80375,-20.72833,5.38,0.07],[6823,273.80375,-20.38806,5.95,0.02],[6824,273.1775,41.14694,6.36,1.03],[6825,273.87833,-18.66139,6.07,0.2],[6826,273.27,38.77361,6.04,-0.08],[6827,272.77958,60.40944,6.49,0.0],[6828,274.9175,-63.88694,6.18,0.58],[6829,275.90042,-75.04417,5.47,0.02],[6830,273.99167,-3.6175,6.36,0.2],[6831,273.68333,29.20722,6.56,0.54],[6832,274.40667,-36.76167,3.11,1.56],[6833,274.40042,-34.10722,6.16,-0.11],[6834,274.02333,2.37778,6.01,1.59],[6835,274.35,-28.6525,6.19,0.18],[6836,274.34875,-28.28917,6.4,0.54],[6837,277.33125,-80.23278,5.95,1.16],[6838,274.29833,-17.37389,5.75,1.58],[6839,274.66667,-42.28833,6.3,-0.11],[6840,274.22125,-3.00722,6.0,0.89],[6841,274.36875,-18.46333,6.54,0.11],[6842,274.51333,-27.0425,4.65,1.66],[6843,274.35083,-9.75861,6.31,0.38],[6844,274.27,1.00583,6.63,0.31],[6845,273.91167,42.15944,5.59,-0.1],[6846,274.67375,-25.60472,6.51,1.34],[6847,273.88583,45.20944,6.29,0.62],[6848,274.68042,-18.61944,6.84,0.31],[6849,273.67125,56.58833,6.37,0.34],[6850,273.47417,64.39722,5.03,0.38],[6851,274.51208,13.77694,6.3,-0.03],[6852,274.53208,18.13139,5.99,0.03],[6853,274.27833,40.93667,6.11,0.99],[6854,274.53208,23.29667,6.63,0.6],[6855,275.80667,-61.49389,4.36,1.48],[6856,275.23042,-37.4875,6.45,1.31],[6857,274.78958,7.25972,5.39,1.07],[6858,275.03667,-15.83167,5.39,1.47],[6859,275.24875,-29.82806,2.7,1.38],[6860,274.79458,24.44611,5.27,1.53],[6861,275.38083,-24.915,6.25,1.84],[6862,275.5775,-38.65694,5.1,1.49],[6863,275.34625,-18.86,5.75,0.94],[6864,275.50083,-28.43,6.16,0.26],[6865,273.82083,68.75583,5.95,1.06],[6866,275.21708,3.37722,4.86,0.91],[6867,274.96708,29.66611,5.99,1.29],[6868,275.07458,21.96139,4.95,1.59],[6869,275.3275,-2.89889,3.26,0.94],[6870,275.72125,-36.66944,5.34,-0.14],[6871,276.38125,-63.02139,6.14,0.2],[6872,274.96542,36.06444,4.33,1.17],[6873,275.36875,5.43583,6.13,-0.04],[6874,275.87042,-36.23833,5.55,1.02],[6875,276.07583,-44.11028,5.25,-0.19],[6876,275.23792,29.85889,5.63,0.21],[6877,275.25417,28.87,5.12,0.2],[6878,275.75917,-10.21861,6.33,0.14],[6879,276.04292,-34.38472,1.85,-0.03],[6880,274.98375,51.34778,6.3,1.1],[6881,275.80083,-12.01472,5.73,0.01],[6882,275.53625,23.28528,5.41,1.6],[6883,275.64708,12.02889,5.89,0.04],[6884,275.91458,-8.93417,4.68,0.95],[6885,275.70417,17.82667,5.25,1.27],[6886,275.27958,49.72556,6.4,1.07],[6887,275.76208,16.68806,6.22,1.2],[6888,276.25625,-30.75667,5.6,1.14],[6889,276.34042,-35.99167,6.15,1.0],[6890,276.01458,-3.58333,6.38,0.34],[6891,275.38625,49.12167,5.05,1.66],[6892,276.17542,-7.07528,6.31,1.16],[6893,276.4775,-33.94528,6.3,-0.08],[6894,276.72458,-48.11694,5.46,0.84],[6895,275.92458,21.76972,3.84,1.18],[6896,276.3375,-20.54167,4.81,1.31],[6897,276.74333,-45.96833,3.51,-0.17],[6898,276.2375,-1.57944,6.15,0.37],[6899,278.23042,-73.96556,5.89,0.98],[6900,276.28667,5.08472,6.74,0.02],[6901,275.98917,38.73917,6.36,0.6],[6902,276.41167,8.03194,5.65,0.92],[6903,276.0575,39.50722,5.12,0.03],[6904,276.24375,27.39528,6.27,0.05],[6905,277.20792,-49.07083,4.13,1.02],[6906,276.48083,14.96667,6.37,0.01],[6907,276.95625,-29.81639,5.92,0.52],[6908,277.48625,-57.52306,5.76,0.98],[6909,276.9325,-26.63472,6.31,0.26],[6910,277.11292,-38.99556,5.64,0.13],[6911,275.94917,53.30083,6.32,0.16],[6912,280.55875,-81.80778,6.27,-0.13],[6913,276.9925,-25.42167,2.81,1.04],[6914,277.02583,-26.75722,6.27,0.16],[6915,277.30375,-43.84583,6.36,0.9],[6916,277.84333,-62.27833,4.64,-0.11],[6917,276.495,29.82889,5.83,0.06],[6918,276.80208,0.19611,5.21,0.5],[6919,276.98542,-17.8,6.2,0.01],[6920,275.18958,71.33778,4.22,-0.1],[6921,277.32,-38.85111,6.63,-0.02],[6922,277.4825,-47.22028,5.7,1.26],[6923,275.97708,58.80056,4.98,0.08],[6924,276.67042,26.44944,6.53,-0.11],[6925,276.95958,3.74861,6.07,1.62],[6926,277.23917,-26.58167,6.5,0.11],[6927,275.26417,72.73278,3.57,0.49],[6928,276.995,6.19417,5.73,-0.03],[6929,277.34167,-25.25639,6.59,0.07],[6930,277.29958,-14.56583,4.7,0.06],[6931,277.7625,-41.91361,6.04,0.14],[6932,277.445,-14.58167,5.96,-0.04],[6933,277.54958,-18.72889,5.66,1.06],[6934,277.93917,-45.915,4.96,-0.11],[6935,277.42083,-1.98528,5.39,0.96],[6936,277.77,-32.98917,5.34,0.16],[6937,277.98417,-43.50778,5.72,1.34],[6938,278.00833,-45.75722,5.07,-0.14],[6939,278.37292,-58.70917,6.44,0.68],[6940,277.55958,-5.72417,6.28,0.95],[6941,277.52125,4.06528,6.69,0.11],[6942,278.08917,-39.70389,5.16,0.08],[6943,277.39875,23.86611,5.9,-0.1],[6944,277.85958,-18.40278,5.14,0.0],[6945,276.49625,65.56361,4.82,1.19],[6946,277.85708,-10.79583,5.72,0.24],[6947,277.97208,-19.125,6.68,1.13],[6948,278.25375,-39.89222,6.22,0.42],[6949,276.92625,59.54917,6.43,0.6],[6950,277.67333,20.81528,6.5,0.79],[6951,278.37583,-42.3125,4.64,1.01],[6952,278.34708,-38.72028,6.32,0.6],[6953,278.34625,-38.72611,5.65,-0.06],[6954,278.63,-52.89194,6.22,1.25],[6955,277.76833,16.92861,5.77,0.04],[6956,278.08667,-14.64417,6.37,0.22],[6957,277.9875,-1.00306,5.94,0.16],[6958,278.02917,3.65972,6.43,-0.04],[6959,278.18042,-14.86556,5.5,1.97],[6960,278.49083,-33.01667,5.28,-0.11],[6961,278.47292,-24.0325,5.49,1.79],[6962,278.4125,-14.85361,5.76,0.04],[6963,278.34542,-5.91139,6.36,0.02],[6964,282.99125,-83.31639,7.16,1.26],[6965,278.63667,-24.2225,6.51,0.54],[6966,278.1925,23.61694,5.84,1.46],[6967,278.34708,8.26833,6.42,-0.03],[6968,278.20792,30.55417,5.48,-0.1],[6969,278.83875,-20.84083,6.48,0.28],[6970,278.76,-10.97722,5.14,0.92],[6971,278.34625,30.8925,6.59,-0.12],[6972,278.99875,-29.69917,6.37,1.27],[6973,278.80167,-8.24417,3.85,1.33],[6974,278.0475,52.11556,6.56,-0.07],[6975,278.58167,20.46639,6.57,0.11],[6976,278.69792,10.89167,6.4,0.08],[6977,278.8025,18.20333,5.78,0.0],[6978,278.14375,57.04556,4.77,0.61],[6979,277.81167,65.43611,6.59,0.3],[6980,278.87667,23.60556,5.61,1.0],[6981,278.97167,16.97556,6.21,0.53],[6982,280.75875,-71.42806,4.01,1.14],[6983,278.48625,52.35361,5.36,1.09],[6984,278.80625,34.45778,6.1,-0.11],[6985,279.11583,9.1225,5.39,0.37],[6986,279.80958,-47.90972,5.86,0.23],[6987,279.16292,6.67194,5.45,0.37],[6988,279.47667,-21.39778,5.94,0.19],[6989,279.51917,-14.00472,6.47,0.21],[6990,279.62792,-23.505,5.81,0.02],[6991,279.89583,-43.18611,5.37,1.68],[6992,279.3025,11.42167,6.42,-0.02],[6993,279.4,-0.30944,5.75,0.07],[6994,281.95583,-77.86722,6.39,0.6],[6995,279.2875,16.19833,6.29,0.9],[6996,280.59375,-64.64306,6.37,0.15],[6997,279.155,33.46889,5.42,-0.1],[6998,279.7225,-21.05194,5.86,0.68],[6999,279.59875,-3.19361,6.49,0.55],[7000,279.57958,-1.11333,6.66,0.42],[7001,279.23458,38.78361,0.03,0.0],[7002,279.5875,8.83389,6.4,1.32],[7003,279.19,43.22194,6.2,0.24],[7004,280.905,-64.55111,5.78,0.96],[7005,280.3775,-48.095,6.49,1.22],[7006,277.43708,77.54694,5.64,1.18],[7007,280.00208,-7.79056,5.84,1.55],[7008,279.90375,5.26417,6.38,0.78],[7009,279.52708,39.66806,6.04,1.65],[7010,279.965,7.35833,6.28,0.96],[7011,280.465,-23.83333,6.23,0.23],[7012,281.36208,-64.87139,4.79,0.2],[7013,279.05542,65.48861,6.06,0.28],[7014,280.42708,-14.56417,6.42,0.81],[7015,281.29792,-61.095,6.04,1.46],[7016,280.00833,30.84944,6.36,0.6],[7017,279.88792,40.935,6.25,-0.06],[7018,279.38958,62.52667,5.74,-0.06],[7019,280.05083,38.36722,6.45,0.21],[7020,280.56833,-9.0525,4.72,0.35],[7021,280.94542,-38.32361,5.13,0.09],[7022,281.34875,-56.88167,6.22,1.38],[7023,280.73,-19.28389,6.35,1.75],[7024,280.65042,-7.07333,6.15,1.0],[7025,276.03833,83.17528,6.17,0.05],[7026,281.03208,-36.71861,6.32,0.98],[7027,282.43125,-72.99472,6.06,0.0],[7028,279.97,52.19611,6.0,-0.07],[7029,281.08083,-35.64194,4.87,-0.18],[7030,280.42208,31.61778,6.41,-0.04],[7031,281.23833,-39.68639,5.43,0.87],[7032,280.88042,-8.27528,4.9,1.12],[7033,280.53375,34.74667,6.47,-0.13],[7034,280.96417,-6.81861,6.31,0.48],[7035,281.20667,-25.01111,5.83,0.05],[7036,282.1575,-65.07778,5.73,0.24],[7037,281.74625,-50.09444,6.54,0.28],[7038,281.32792,-21.00139,6.36,0.45],[7039,281.41417,-26.99083,3.17,-0.11],[7040,281.20792,2.06,5.02,-0.06],[7041,280.81958,39.30028,6.45,1.59],[7042,280.23458,62.74972,6.09,0.98],[7043,280.90042,36.55667,6.01,1.04],[7044,280.965,31.92667,5.7,0.34],[7045,281.505,-19.60639,6.42,1.65],[7046,281.58583,-22.39222,5.37,1.64],[7047,281.1675,23.58972,6.31,0.4],[7048,281.36833,5.49972,5.83,0.04],[7049,280.65792,55.53944,5.04,-0.09],[7050,281.93583,-40.40611,5.24,0.78],[7051,281.085,39.67,5.06,0.16],[7052,281.08458,39.67111,6.02,0.6],[7053,281.09542,39.61306,5.14,0.19],[7054,281.09542,39.61278,5.37,0.6],[7055,281.68042,-10.125,5.71,0.59],[7056,281.19333,37.605,4.36,0.19],[7057,281.20083,37.59444,5.73,0.28],[7058,281.39875,21.985,6.51,0.02],[7059,281.61875,-0.96167,5.9,0.13],[7060,280.87083,53.87194,6.11,0.12],[7061,281.41542,20.54639,4.19,0.46],[7062,282.21042,-43.68,5.49,0.13],[7063,281.79375,-4.74778,4.22,1.1],[7064,281.51875,26.66222,4.83,1.2],[7065,282.36417,-45.81028,5.81,0.9],[7066,281.87083,-5.705,5.2,1.47],[7067,281.6725,18.70583,6.17,1.59],[7068,282.39583,-43.43389,5.61,-0.08],[7069,281.75542,18.18139,4.36,0.13],[7070,282.32167,-34.74861,6.62,1.08],[7071,281.23083,54.89667,6.23,0.82],[7072,282.18875,-18.60139,6.47,0.6],[7073,281.55417,41.44167,6.07,-0.13],[7074,283.05417,-62.1875,4.22,-0.14],[7075,281.07583,61.04806,5.99,0.96],[7076,282.01125,4.24139,6.21,1.51],[7077,282.39792,-19.14222,6.75,0.2],[7078,282.41708,-20.32472,5.24,1.41],[7079,282.06833,23.51417,6.15,0.49],[7080,281.74583,46.315,6.52,0.07],[7081,281.98958,31.75694,6.06,-0.13],[7082,280.7925,70.79278,6.44,0.6],[7083,282.42083,-5.91278,5.99,1.6],[7084,281.67958,52.98806,5.88,-0.09],[7085,282.40458,0.83583,6.25,0.04],[7086,282.2225,19.32861,5.88,0.03],[7087,283.165,-52.1075,5.17,0.94],[7088,282.71042,-22.16222,6.61,0.38],[7089,282.58333,-7.9075,6.8,3.09],[7090,281.91667,49.075,6.4,0.6],[7091,282.31,25.04639,6.59,0.6],[7092,283.11292,-46.59528,5.54,1.63],[7093,283.30042,-51.93111,6.31,-0.09],[7094,282.74375,-9.77417,5.83,0.61],[7095,283.26042,-48.36,6.19,0.14],[7096,282.06708,48.7675,6.12,0.21],[7097,283.24833,-46.58583,6.19,0.03],[7098,282.43333,31.62917,6.64,0.02],[7099,282.69,10.97639,6.55,0.6],[7100,282.44125,32.81278,5.91,-0.16],[7101,282.84208,-3.31778,6.1,0.3],[7102,282.47042,32.55083,5.25,0.08],[7103,283.11875,-26.65028,6.29,0.94],[7104,283.15417,-29.37944,6.13,1.35],[7105,283.17375,-30.73417,6.63,-0.05],[7106,282.52,33.36278,3.45,0.0],[7107,284.2375,-67.23361,4.44,0.71],[7108,283.63458,-49.87861,6.6,0.08],[7109,283.00792,13.96556,6.14,0.0],[7110,283.25792,-9.57611,6.34,0.2],[7111,284.22792,-62.80111,6.48,1.53],[7112,282.89958,28.78361,6.18,1.18],[7113,283.06833,21.42528,5.48,-0.07],[7114,283.50042,-21.35972,5.69,1.23],[7115,282.90208,36.53861,6.09,-0.11],[7116,283.5425,-22.745,4.83,1.41],[7117,281.44458,74.08556,5.27,0.92],[7118,283.02958,41.38333,6.28,-0.09],[7119,283.67958,-15.60306,5.1,0.17],[7120,283.77958,-22.67139,4.99,1.33],[7121,283.81625,-26.29667,2.02,-0.22],[7122,284.07042,-42.71056,5.36,1.0],[7123,282.89583,52.975,5.51,0.84],[7124,281.5925,75.43389,5.35,0.05],[7125,282.80042,59.38833,4.66,1.19],[7126,283.87917,-16.37667,5.79,0.36],[7127,284.65167,-60.20056,5.14,1.37],[7128,284.0025,-23.17361,5.93,-0.02],[7129,284.16875,-37.34333,5.38,-0.14],[7130,285.01458,-66.65333,6.01,0.97],[7131,283.43167,36.97167,5.58,-0.15],[7132,283.555,27.90944,5.62,1.35],[7133,283.68708,22.645,4.59,0.78],[7134,284.61542,-52.93861,4.87,-0.05],[7135,283.86458,6.61528,5.57,1.04],[7136,284.39458,-39.82333,6.31,0.2],[7137,283.30667,50.70833,4.92,0.9],[7138,283.55958,41.22556,7.3,0.6],[7139,283.62583,36.89889,4.3,1.68],[7140,283.71875,33.96861,6.02,0.91],[7141,284.055,4.20361,4.62,0.17],[7142,284.06083,4.20194,4.98,0.2],[7143,284.09458,-1.8,6.22,-0.05],[7144,284.10667,2.47111,6.15,0.97],[7145,284.33542,-20.65639,5.08,0.13],[7146,283.7175,41.60278,5.44,1.03],[7147,284.01625,17.995,6.63,-0.04],[7148,284.02583,18.10528,5.69,1.09],[7149,284.26542,-5.84611,4.83,1.08],[7150,284.4325,-21.10667,3.51,1.18],[7151,284.58875,-31.03611,6.12,1.35],[7152,284.68083,-37.1075,4.87,0.41],[7153,283.44292,57.48694,6.22,1.23],[7154,283.69625,48.85972,5.77,0.43],[7155,284.58542,-24.87667,6.62,0.08],[7156,284.79625,-39.53472,6.49,-0.04],[7157,283.83375,43.94611,4.04,1.59],[7158,284.31917,2.53528,5.57,0.0],[7159,284.6025,-22.52944,6.14,0.09],[7160,281.40875,79.9425,6.39,0.28],[7161,285.87375,-68.75528,5.88,0.56],[7162,284.25667,32.90139,5.22,0.59],[7163,284.59917,6.24028,6.21,0.46],[7164,284.86167,-18.56694,6.37,0.99],[7165,284.56125,17.36083,5.38,0.8],[7166,284.84917,-12.84056,5.53,-0.04],[7167,284.69542,13.90667,5.89,0.25],[7168,285.10333,-24.94222,6.36,1.25],[7169,285.26333,-37.06083,6.69,0.6],[7170,285.26792,-37.06194,6.4,-0.03],[7171,284.68792,19.79417,6.5,-0.04],[7172,284.77375,13.6225,5.23,0.53],[7173,284.82292,10.14083,6.75,0.25],[7174,284.50792,38.26611,5.89,-0.17],[7175,284.1875,57.815,5.66,1.15],[7176,284.90583,15.06833,4.02,1.08],[7177,285.53625,-41.91,6.23,0.0],[7178,284.73583,32.68944,3.24,-0.05],[7179,284.69417,40.67917,6.22,-0.16],[7180,283.59958,71.29722,4.82,1.15],[7181,284.93958,26.23056,5.27,1.24],[7182,285.4075,-22.69556,6.24,1.66],[7183,284.99208,22.81472,6.29,1.75],[7184,284.36833,58.22528,6.46,0.08],[7185,284.80125,39.21778,6.41,-0.17],[7186,285.38958,-15.2825,6.32,1.0],[7187,284.10667,65.25806,5.63,0.95],[7188,285.77875,-42.09528,4.75,-0.02],[7190,285.98917,-51.01861,5.93,1.24],[7191,284.3225,62.39667,6.45,0.93],[7192,285.00375,32.14556,4.93,1.47],[7193,285.42,-5.73889,4.02,1.09],[7194,285.65292,-29.88028,2.6,0.08],[7195,285.61542,-24.84694,5.65,1.23],[7196,284.74833,50.80944,6.3,0.98],[7197,285.82375,-38.25333,5.74,0.32],[7198,285.27292,19.30972,6.39,0.6],[7199,283.38833,75.7875,6.22,0.6],[7200,285.34417,20.83361,6.69,0.02],[7201,285.07917,40.68417,6.65,0.6],[7202,285.3225,26.29139,5.69,-0.08],[7203,285.76583,-19.24528,6.05,1.29],[7204,285.23,33.80222,6.01,0.97],[7205,285.77917,-19.10333,6.37,0.48],[7206,285.39542,25.02583,6.72,0.6],[7207,285.45625,22.26389,6.4,0.16],[7208,285.59,8.37417,6.3,1.67],[7209,285.72708,-3.69889,5.42,0.0],[7210,285.05708,50.53361,5.38,-0.18],[7211,286.10458,-31.04694,5.5,0.03],[7212,285.45125,33.62139,6.39,-0.12],[7213,286.58292,-52.34083,5.16,0.53],[7214,285.88417,1.81889,5.83,0.18],[7215,285.36,46.93472,5.01,0.19],[7216,285.71917,19.66111,6.09,1.34],[7217,286.17083,-21.74167,3.77,1.01],[7218,285.18083,55.65833,5.48,0.86],[7219,286.04458,3.33056,6.73,0.13],[7220,286.10083,-5.685,6.9,4.19],[7221,287.46958,-68.42472,5.33,0.91],[7222,285.92708,21.26778,6.52,0.32],[7223,286.73167,-48.29917,5.97,-0.02],[7224,284.71917,69.53111,6.52,-0.15],[7225,286.24,-4.03139,5.42,1.12],[7226,286.60458,-37.06333,4.93,0.52],[7227,286.60458,-37.06333,4.99,0.52],[7228,317.1925,-88.95639,5.47,0.27],[7229,285.52917,52.26111,6.31,1.0],[7230,286.42167,-15.66028,5.97,-0.02],[7231,286.3275,-1.51278,6.53,0.35],[7232,286.71875,-37.81028,6.16,0.72],[7233,287.21708,-55.72028,6.49,1.1],[7234,286.735,-27.67056,3.32,1.19],[7235,286.3525,13.86333,2.99,0.01],[7236,286.56208,-4.8825,3.44,-0.09],[7237,286.24125,31.74444,5.56,1.54],[7238,286.24292,30.73333,6.06,1.55],[7239,286.71792,-16.22889,6.03,-0.04],[7240,286.87875,-28.63694,6.04,1.61],[7241,286.78542,-18.73778,6.29,-0.04],[7242,287.08708,-40.49667,4.59,1.09],[7243,286.5925,8.23,6.09,1.6],[7244,286.44625,29.92167,6.31,1.65],[7245,286.78792,0.64167,6.56,0.05],[7246,287.06083,-24.65694,6.3,0.04],[7247,284.48833,77.05083,6.54,0.38],[7248,286.74417,11.07139,5.09,-0.07],[7249,287.06958,-19.29,5.54,-0.11],[7250,286.66,24.25083,5.77,0.09],[7251,286.23,53.39667,5.38,-0.01],[7252,286.29125,49.92333,6.43,0.6],[7253,286.65708,28.62861,5.55,0.29],[7254,287.36792,-37.90444,4.11,0.04],[7255,287.41542,-39.82806,6.46,1.06],[7256,287.40167,-36.16472,6.56,-0.02],[7257,287.49083,-41.8925,5.88,-0.08],[7258,286.57083,41.41389,6.49,-0.15],[7259,287.50708,-39.34083,4.11,1.2],[7260,286.98875,16.85333,6.07,0.7],[7261,286.85667,32.50167,5.23,0.34],[7262,286.82542,36.10028,5.28,-0.11],[7263,287.015,21.69889,6.23,0.4],[7264,287.44083,-21.02361,2.89,0.35],[7265,287.45042,-19.80361,6.13,1.16],[7266,287.24958,6.07333,5.22,0.35],[7267,287.1675,16.85139,6.48,0.52],[7268,287.7575,-39.005,6.36,0.01],[7269,287.465,-0.42806,6.34,-0.04],[7270,287.82875,-29.50222,6.3,-0.04],[7271,288.19208,-50.48639,6.13,0.95],[7272,287.26833,34.60056,6.74,0.63],[7273,288.04083,-37.58278,6.57,1.02],[7274,289.11875,-69.19056,6.27,0.17],[7275,287.1075,52.42556,5.81,1.09],[7276,288.11667,-21.65806,6.41,1.13],[7277,288.30708,-25.90667,5.8,1.38],[7278,289.3,-66.66139,5.53,0.18],[7279,288.16958,-7.93944,5.34,0.13],[7280,287.87875,26.73583,6.36,0.41],[7281,288.66542,-45.19333,5.92,0.9],[7282,288.31458,-12.2825,5.51,1.44],[7283,287.94167,31.28333,5.98,-0.07],[7284,287.84625,40.42917,6.18,0.09],[7285,288.14333,16.84639,6.73,-0.01],[7286,288.15292,21.55444,5.93,0.03],[7287,288.42792,2.29361,5.15,-0.07],[7288,288.43375,5.51583,6.49,0.09],[7289,289.09042,-45.46639,5.4,1.35],[7290,287.44083,65.97861,6.25,0.0],[7291,288.88833,-24.17917,6.25,0.54],[7292,288.885,-25.25667,4.85,0.56],[7293,288.01917,49.85417,6.75,0.65],[7294,288.02083,49.85611,6.57,0.65],[7295,287.91875,56.85917,5.12,1.01],[7296,289.13667,-33.52167,6.25,0.02],[7297,289.53917,-53.38667,6.38,0.49],[7298,288.43958,39.14611,4.39,-0.15],[7299,288.76083,20.20333,6.0,0.88],[7300,288.83375,15.08361,5.57,1.07],[7301,288.8225,21.23222,5.64,0.11],[7302,288.85333,30.52639,5.85,1.67],[7303,289.12917,4.83472,5.59,0.08],[7304,289.40875,-18.95306,4.96,1.02],[7305,288.9875,27.45556,6.54,-0.06],[7306,289.05417,21.39028,4.77,-0.05],[7307,289.11167,14.54472,5.63,-0.02],[7308,288.9975,27.92694,6.16,0.61],[7309,288.48,57.705,4.99,1.16],[7310,288.13875,67.66167,3.07,1.0],[7311,288.83042,50.07083,6.27,0.6],[7312,287.29083,76.56056,5.13,0.31],[7313,289.45083,2.03167,6.19,0.02],[7314,289.09208,38.13361,4.36,1.26],[7315,289.45417,11.59528,5.28,0.2],[7316,289.91667,-35.42139,5.59,-0.13],[7317,289.75042,-15.53639,6.06,1.43],[7318,289.43167,23.02556,5.43,0.02],[7319,289.63542,1.08528,5.1,1.15],[7320,291.0225,-68.37111,6.34,1.23],[7321,289.71208,0.33917,6.41,1.05],[7322,289.21417,46.99917,6.0,0.44],[7323,290.10917,-31.81778,6.58,1.67],[7324,289.50333,31.02222,6.68,0.08],[7325,289.71958,9.61806,6.32,1.05],[7326,289.70208,19.61056,6.58,0.03],[7327,290.15875,-22.4025,5.58,0.27],[7328,289.27583,53.36861,3.77,0.96],[7329,290.71292,-54.42361,5.05,0.02],[7330,290.37458,-34.98389,6.48,0.63],[7331,289.91375,12.37472,5.53,0.26],[7332,289.97083,11.535,6.02,0.08],[7333,290.13708,-5.41583,5.01,0.92],[7334,290.54,-42.01611,6.34,1.14],[7335,289.76583,33.38889,6.6,-0.19],[7336,290.14875,-0.89222,5.49,-0.04],[7337,290.65958,-44.45889,4.01,-0.1],[7338,289.755,37.44528,6.22,-0.03],[7339,290.40458,-19.23417,6.26,-0.1],[7340,290.41833,-17.84722,3.93,0.22],[7341,289.65708,49.56972,6.31,1.12],[7342,290.43167,-15.955,4.61,0.1],[7343,290.805,-44.79972,4.29,0.34],[7344,290.46208,-18.30833,5.87,1.06],[7345,289.91208,37.33056,6.31,0.68],[7346,290.13792,35.18611,6.31,-0.12],[7347,290.58625,-8.20111,6.31,-0.1],[7348,290.97167,-40.61611,3.97,-0.1],[7349,290.58958,-0.2525,5.83,1.09],[7350,291.08917,-43.72306,6.17,1.6],[7351,289.90208,54.37611,6.26,0.03],[7352,288.8875,73.35556,4.45,1.25],[7353,290.76917,-7.40028,6.32,1.45],[7354,290.70167,9.91306,6.35,0.44],[7355,291.12542,-27.86556,6.04,-0.13],[7356,290.06708,57.64528,5.91,1.58],[7357,290.78417,14.92111,6.64,0.27],[7358,290.71208,26.2625,5.18,-0.12],[7359,290.63917,33.51806,6.06,1.03],[7360,291.26708,-29.30917,5.93,1.26],[7361,289.94208,64.39083,6.52,-0.04],[7362,291.31875,-24.50861,5.03,0.23],[7363,291.37375,-23.96222,5.43,1.43],[7364,290.94542,20.26444,6.4,0.01],[7365,290.35583,57.76694,6.43,0.6],[7366,291.25667,-4.88417,6.52,0.33],[7367,291.34,-13.89694,5.69,1.43],[7368,290.89167,33.22222,6.37,0.81],[7369,291.09208,16.93778,6.25,0.08],[7370,291.95042,-54.32528,5.69,1.4],[7371,290.16708,65.71472,4.59,0.02],[7372,291.03167,29.62139,4.97,-0.1],[7373,291.2425,11.94444,5.16,0.77],[7374,291.09333,28.08778,6.53,-0.08],[7375,291.58,-21.77667,5.59,1.22],[7376,291.02542,36.45194,6.36,0.6],[7377,291.37458,3.11472,3.36,0.32],[7378,291.54625,-15.05306,5.72,0.02],[7379,291.6025,-14.55111,6.7,0.05],[7380,291.73542,-29.74333,5.67,0.0],[7381,290.84917,50.27139,6.51,-0.08],[7382,290.98542,43.38806,5.84,0.92],[7383,292.79542,-68.43389,5.96,1.64],[7384,291.34333,20.27139,6.31,-0.02],[7385,291.36917,19.79861,5.16,0.98],[7386,291.3575,24.91278,6.19,0.51],[7387,291.62958,0.33861,4.66,0.6],[7388,292.46917,-55.44139,6.13,0.98],[7389,291.60042,13.02389,5.74,0.47],[7390,291.555,20.09778,5.63,-0.01],[7391,291.61958,19.89139,5.81,1.55],[7392,292.34958,-43.44583,5.71,0.22],[7393,292.64375,-55.11,6.3,0.45],[7394,259.23667,89.03778,6.38,1.57],[7395,291.53792,36.31778,5.15,-0.12],[7396,291.89125,14.2825,6.32,-0.06],[7397,292.08667,2.93028,5.85,0.01],[7398,292.4675,-26.98556,5.52,1.12],[7399,292.56125,-32.09222,6.6,0.39],[7400,292.25417,1.95028,5.8,0.08],[7401,291.44458,58.02722,6.6,-0.15],[7402,292.33958,-7.04389,6.61,1.1],[7403,291.90208,37.94111,6.34,-0.14],[7404,292.325,0.24611,6.25,1.32],[7405,292.17625,24.665,4.44,1.5],[7406,292.2375,24.76861,5.81,1.03],[7407,292.34208,14.59583,5.56,1.05],[7408,291.85792,52.32056,5.75,0.0],[7409,292.33708,20.27972,6.33,-0.1],[7410,292.725,-21.31222,6.13,0.12],[7411,293.22417,-53.18583,5.75,0.3],[7412,292.54375,2.90417,6.09,1.82],[7413,291.61042,62.55722,6.38,1.39],[7414,292.66583,-2.78889,5.03,1.75],[7415,292.63792,3.44444,6.05,0.01],[7416,293.34,-45.27194,5.61,-0.04],[7417,292.68042,27.95972,3.08,1.13],[7418,292.68875,27.96528,5.11,-0.1],[7419,292.69542,36.22861,6.25,-0.04],[7420,292.42625,51.72972,3.79,0.14],[7421,292.84083,26.61722,5.87,0.93],[7422,293.53542,-40.03472,5.89,0.09],[7423,290.4175,79.60278,6.05,0.07],[7424,293.80417,-48.09917,4.9,1.09],[7425,288.7825,83.46278,6.53,0.1],[7426,292.94292,34.45306,4.74,-0.14],[7427,292.83042,50.30667,5.53,1.25],[7428,292.80667,55.73194,6.37,1.16],[7429,293.5225,7.37889,4.45,1.17],[7430,293.78042,-10.56028,5.12,1.13],[7431,294.00708,-24.71917,5.65,0.19],[7432,293.87417,-7.46028,6.34,1.12],[7433,293.89,-12.25278,6.27,1.09],[7434,294.60792,-57.98333,6.18,0.97],[7435,294.96708,-66.68556,6.39,0.02],[7436,293.40167,38.76222,6.61,0.03],[7437,293.64542,19.77333,5.0,-0.09],[7438,293.855,2.91333,6.38,0.41],[7439,294.10875,-18.85278,6.11,0.27],[7440,294.17667,-24.88361,4.6,-0.07],[7441,293.71208,29.46306,5.38,0.55],[7442,293.42333,49.2625,5.96,1.64],[7443,294.26375,-18.23111,5.64,1.26],[7444,293.67167,42.41278,5.35,0.05],[7445,294.03333,11.15,6.68,0.6],[7446,294.22292,-7.0275,4.95,0.0],[7447,294.18042,-1.28639,4.36,-0.08],[7448,293.29208,60.15861,6.29,1.57],[7449,294.06583,14.39167,6.38,1.04],[7450,292.75083,70.98944,6.07,1.41],[7451,293.5825,51.23667,5.73,0.48],[7452,294.03458,22.58583,6.32,-0.07],[7453,293.66625,48.16472,6.67,0.24],[7454,294.39333,-14.30167,5.47,0.5],[7455,295.40625,-65.85417,6.09,1.55],[7456,294.21875,11.27333,5.98,0.88],[7457,293.95125,36.94444,6.05,-0.11],[7458,294.15708,20.33278,7.14,1.32],[7459,295.07625,-54.4175,6.26,1.0],[7460,294.44708,-4.6475,5.46,0.43],[7461,294.925,-45.27806,6.25,0.28],[7462,293.09,69.66111,4.68,0.79],[7463,294.3225,16.46278,5.66,1.02],[7464,294.98208,-39.43333,6.61,0.23],[7465,293.98292,50.23861,6.52,1.06],[7466,294.28917,29.33361,6.43,-0.09],[7467,294.23583,38.38389,6.5,-0.15],[7468,294.15792,44.695,5.17,0.93],[7469,294.11042,50.22111,4.48,0.38],[7470,294.95583,-23.42778,6.34,0.03],[7471,294.70417,3.38167,6.35,0.04],[7472,294.57292,20.78278,6.48,1.08],[7473,295.02958,-23.42861,5.97,1.04],[7474,294.79833,5.39778,5.17,0.03],[7475,294.85583,16.57139,6.38,2.07],[7476,295.18083,-16.29333,6.2,1.13],[7477,294.48625,49.28444,6.47,0.99],[7478,294.84417,30.15333,4.69,0.97],[7479,295.02417,18.01389,4.37,0.78],[7480,295.18042,-0.62111,5.67,0.11],[7481,294.9375,33.97917,6.1,0.08],[7482,295.11792,20.47667,6.5,0.39],[7483,294.86042,42.81833,5.4,-0.08],[7484,294.67167,54.97389,5.82,0.44],[7485,295.16542,23.7175,6.64,0.01],[7486,295.27292,13.81556,6.01,-0.08],[7487,294.89333,45.95806,6.2,0.9],[7488,295.26208,17.47611,4.37,1.05],[7489,295.62958,-16.12389,5.06,0.33],[7490,295.31208,22.45278,6.36,1.53],[7491,295.90667,-37.53889,6.16,0.01],[7492,295.17167,43.07778,6.16,1.56],[7493,295.55333,12.19333,6.34,-0.03],[7494,299.00625,-81.34972,6.39,1.4],[7495,295.20917,45.525,5.06,0.4],[7496,295.88958,-15.47,5.49,0.46],[7497,295.64167,11.82667,5.27,0.57],[7498,297.35542,-72.50333,5.41,0.22],[7499,295.48958,40.25389,6.23,0.18],[7500,295.05417,60.50722,6.51,0.22],[7501,295.70458,29.33167,6.49,0.33],[7502,295.68583,32.42667,5.94,0.11],[7503,295.45375,50.52528,5.96,0.64],[7504,295.46667,50.5175,6.2,0.66],[7505,295.78958,30.67861,6.05,0.0],[7506,295.92875,25.77194,5.49,0.93],[7507,296.505,-31.90861,5.52,0.02],[7508,295.98292,27.13556,6.28,1.1],[7509,295.51708,55.46333,6.48,1.61],[7510,297.005,-56.3625,5.35,0.2],[7511,296.14208,13.30278,6.26,-0.03],[7512,295.96417,34.1625,6.05,-0.06],[7513,297.47208,-66.81278,6.45,1.48],[7514,295.9375,41.77306,5.84,1.57],[7515,296.59042,-19.76111,4.86,0.93],[7516,296.4675,-2.88333,6.48,0.05],[7517,296.06917,37.35444,4.89,0.95],[7518,296.20292,29.26472,6.82,0.64],[7519,296.41625,7.61333,5.91,0.18],[7520,296.15917,34.41389,6.57,0.6],[7521,297.22958,-52.88806,6.25,1.13],[7522,295.81083,58.01639,6.22,0.56],[7523,296.20417,40.71667,6.34,1.64],[7524,297.75542,-65.605,6.05,0.3],[7525,296.565,10.61333,2.72,1.52],[7526,295.915,57.0425,6.27,0.88],[7527,297.59042,-61.06139,6.21,-0.14],[7528,296.24375,45.13083,2.87,-0.03],[7529,296.415,36.09111,6.43,-0.03],[7530,296.46417,35.01278,6.09,1.06],[7531,297.68667,-59.19306,5.42,0.08],[7532,297.0125,-13.70333,6.11,0.2],[7533,296.66458,25.13389,6.62,0.27],[7534,296.60667,33.72778,4.99,0.47],[7535,296.64583,32.88861,6.18,1.13],[7536,296.84708,18.53417,3.82,1.41],[7537,297.5575,-47.55722,5.94,1.7],[7538,297.29833,-28.78889,6.05,0.4],[7540,296.9525,25.38389,5.95,0.99],[7541,297.25917,-10.87083,6.04,1.24],[7542,297.12667,10.69417,6.44,0.96],[7543,296.86583,38.4075,5.77,-0.06],[7544,297.17542,11.81583,5.72,0.6],[7545,296.07708,69.33694,5.92,0.05],[7546,297.24458,19.14222,5.0,0.1],[7547,296.86167,47.90778,6.12,1.64],[7548,298.15667,-54.97111,5.74,0.92],[7549,298.1625,-54.97667,6.5,0.1],[7550,297.18292,35.31139,6.53,0.44],[7551,297.21083,33.43722,6.44,0.2],[7552,297.96083,-39.87444,5.33,-0.06],[7553,297.695,-10.76361,5.39,0.38],[7554,297.57292,7.9025,6.51,-0.1],[7555,297.36458,38.71,6.11,0.9],[7556,297.4775,28.44056,6.38,-0.06],[7557,297.69583,8.86833,0.77,0.22],[7558,298.66833,-61.17083,6.24,0.16],[7559,297.79625,-2.46083,6.13,1.57],[7560,297.75667,10.41556,5.11,0.55],[7561,298.05,-19.045,5.92,0.98],[7562,297.82375,9.63028,6.25,0.1],[7563,296.68625,68.43833,6.34,0.28],[7564,297.64125,32.91417,4.23,1.82],[7565,297.76708,22.61,4.95,-0.14],[7566,297.64167,38.7225,5.12,1.69],[7567,297.65542,40.59972,5.69,-0.04],[7568,297.69583,37.82639,6.06,1.7],[7569,298.01458,11.62889,6.13,0.65],[7570,298.11833,1.00556,3.9,0.89],[7571,298.27667,-14.60306,6.48,0.14],[7572,298.065,10.35139,6.54,-0.01],[7573,298.00667,24.99222,5.57,0.71],[7574,298.09083,18.67194,6.23,0.01],[7575,298.32792,-3.11444,5.65,0.2],[7576,297.65708,52.98806,5.03,1.28],[7577,297.83083,47.37722,6.2,0.36],[7578,298.57375,-23.94111,6.18,1.02],[7579,299.67208,-69.16389,5.75,0.23],[7580,298.34417,4.40056,6.53,0.02],[7581,298.81542,-41.86833,4.13,1.08],[7582,297.04333,70.26778,3.83,0.89],[7583,298.06792,36.43222,6.1,1.43],[7584,298.53417,-8.57417,5.79,1.64],[7585,298.77125,-33.04639,6.46,1.48],[7586,299.24042,-57.92556,6.53,1.53],[7587,299.27583,-58.90139,5.26,-0.01],[7588,299.72083,-68.76222,6.39,0.16],[7589,297.99625,47.0275,5.62,-0.07],[7590,300.14792,-72.91056,3.96,-0.03],[7591,298.03,47.93194,5.91,-0.18],[7592,298.36542,24.07972,4.58,-0.06],[7593,298.65667,-8.22722,5.71,-0.08],[7594,298.65875,-8.23722,6.49,-0.04],[7595,298.56208,8.46139,4.71,1.05],[7596,298.68667,0.27361,5.61,0.1],[7597,298.96,-26.29944,4.7,0.75],[7598,298.66792,7.14028,6.15,0.03],[7599,298.83167,-6.73417,6.51,0.39],[7600,298.255,47.8075,6.29,-0.11],[7601,298.62958,24.31944,5.52,-0.02],[7602,298.82833,6.40667,3.71,0.86],[7603,300.09583,-66.94944,5.76,1.04],[7604,299.23667,-27.17,4.52,1.46],[7605,299.42208,-38.05833,6.55,0.39],[7606,298.70125,36.99611,5.76,0.75],[7607,298.77708,30.19528,6.57,-0.08],[7608,298.3225,57.52361,5.14,-0.13],[7609,299.00542,16.63472,5.36,0.67],[7610,299.05958,11.42389,5.28,-0.01],[7611,298.3975,59.70889,6.06,0.02],[7612,300.46833,-66.94417,5.31,1.22],[7613,298.96542,38.48667,4.94,-0.08],[7614,299.4875,-15.49139,5.02,0.05],[7615,299.07667,35.08333,3.89,1.02],[7616,299.25083,20.99806,6.48,0.07],[7617,299.735,-30.53806,6.28,1.05],[7618,299.73833,-26.19556,4.83,0.9],[7619,298.9075,52.43889,4.92,0.12],[7620,299.18417,36.25083,6.02,-0.15],[7621,300.10542,-49.35111,6.17,1.06],[7622,299.43917,16.78917,5.53,-0.05],[7623,299.93417,-35.27639,4.37,-0.15],[7624,299.96375,-34.69778,5.3,0.17],[7625,300.43625,-59.37611,5.13,1.53],[7626,298.84208,58.25028,6.09,1.02],[7627,300.11042,-43.04333,6.14,1.64],[7628,299.30792,40.36806,5.45,-0.1],[7629,300.06625,-37.70222,5.95,1.0],[7630,300.20125,-45.11306,5.81,0.28],[7631,300.08458,-33.70389,5.66,0.49],[7632,299.18792,50.9025,6.43,-0.01],[7633,298.98083,58.84611,4.96,1.59],[7634,299.07917,56.68694,6.12,0.6],[7635,299.68917,19.49222,3.47,1.57],[7636,299.84458,1.37778,6.17,1.13],[7637,299.9475,-9.95833,5.88,0.58],[7638,299.48417,42.26083,6.43,0.6],[7639,300.36,-40.81417,6.29,0.11],[7640,299.65833,30.98361,5.49,-0.06],[7641,299.79375,23.10139,5.67,0.6],[7642,299.64333,38.10556,6.32,-0.09],[7643,300.34958,-22.73722,6.01,0.97],[7644,301.38667,-67.32083,6.07,0.64],[7645,300.01375,17.51667,5.37,1.67],[7646,299.83542,45.77222,5.92,0.16],[7647,299.98,37.04278,5.19,-0.17],[7648,300.24542,8.55806,5.91,1.52],[7649,300.49417,-13.63694,5.71,0.08],[7650,300.66458,-27.70972,4.58,1.65],[7651,299.81417,52.05583,6.15,-0.19],[7652,300.88958,-37.94083,4.77,1.41],[7653,300.27542,27.75361,4.64,0.18],[7654,299.61958,63.53417,5.96,0.09],[7655,300.31375,37.09889,6.2,1.31],[7656,300.43625,24.80028,5.88,-0.12],[7657,300.50583,24.93806,5.22,0.36],[7658,300.93458,-22.59556,6.45,0.5],[7659,301.08167,-32.05639,4.99,1.21],[7660,300.34,50.10472,5.05,1.11],[7661,301.005,-7.46972,6.72,0.35],[7662,300.81833,18.50056,5.96,1.42],[7663,302.08542,-66.35472,6.45,1.58],[7664,300.875,16.03139,5.67,-0.1],[7665,302.18167,-66.18194,3.56,0.76],[7666,299.67458,70.36694,6.33,0.88],[7667,301.09667,-0.70944,5.68,1.3],[7668,301.38375,-33.0,6.53,-0.08],[7669,301.03458,7.27806,5.52,1.06],[7670,300.90583,29.89667,5.71,0.73],[7671,301.2725,-11.59944,6.34,0.52],[7672,301.02583,17.07,5.8,0.61],[7673,301.84667,-52.88083,4.94,1.62],[7674,301.89625,-55.01639,6.26,0.53],[7675,301.36,-12.66528,6.55,0.04],[7676,300.36875,64.82111,5.27,1.56],[7677,301.24417,23.21028,6.45,0.6],[7678,301.15042,32.21861,5.64,0.54],[7679,301.28958,19.99111,5.1,1.06],[7680,301.36042,15.50028,6.34,1.64],[7681,301.55083,-4.07833,6.47,1.16],[7682,300.58458,64.63444,6.57,0.6],[7683,301.29083,38.47833,6.19,0.64],[7684,301.12,48.22972,6.16,0.04],[7685,300.70458,67.87361,4.51,1.32],[7686,299.9025,76.48139,6.2,1.61],[7687,301.27792,51.83944,6.14,1.6],[7688,301.7225,23.61444,5.07,-0.18],[7689,301.59083,35.9725,5.36,0.85],[7690,302.0075,-0.67833,5.99,1.02],[7691,302.78,-57.52389,6.37,0.06],[7692,301.33958,56.34139,6.21,0.43],[7693,301.95958,9.39972,6.43,0.46],[7694,302.13042,-10.06278,6.18,0.06],[7695,301.18583,63.89056,6.26,0.1],[7696,302.02708,16.66417,6.42,1.91],[7697,301.5575,53.16583,5.85,0.39],[7698,306.22667,-83.31056,6.17,0.2],[7699,301.9225,34.42306,6.11,0.16],[7700,302.15958,10.72583,6.31,-0.12],[7701,301.38667,61.99556,5.39,1.18],[7702,301.79792,50.22917,6.54,0.13],[7703,302.79958,-36.10111,5.32,0.87],[7704,301.22208,68.02722,6.28,1.65],[7705,302.48583,20.91528,6.48,0.38],[7706,303.09958,-42.78056,6.22,1.23],[7707,303.61208,-63.41583,6.09,0.31],[7708,302.35667,36.83972,4.93,-0.13],[7709,302.79208,-8.84222,6.49,-0.15],[7710,302.82625,-0.82139,3.23,-0.07],[7711,302.63958,26.90417,5.52,0.08],[7712,302.99125,-12.3925,6.34,1.21],[7713,302.76458,21.13472,6.22,0.9],[7714,303.57917,-52.44556,5.65,1.5],[7715,303.10792,-12.6175,5.85,0.48],[7716,302.83792,21.87556,6.26,-0.02],[7717,303.14667,0.8675,6.27,0.02],[7718,302.95,26.80889,5.49,1.41],[7719,303.00292,26.47889,5.92,-0.11],[7720,303.30792,-1.00944,5.47,1.43],[7721,303.01583,47.73694,6.92,-0.12],[7722,303.8225,-27.03278,5.73,0.88],[7723,303.41917,24.23889,6.56,0.29],[7724,303.56917,15.1975,4.95,0.08],[7725,303.96083,-30.00528,6.3,1.51],[7726,303.1325,51.46361,6.01,1.14],[7727,302.89542,62.07861,5.75,0.47],[7728,304.09875,-36.45444,6.39,1.54],[7729,304.11042,-35.19778,6.53,0.37],[7730,303.325,46.81583,4.83,0.09],[7731,303.56042,28.69472,5.18,0.18],[7732,304.7625,-63.23111,6.27,1.04],[7733,303.42833,43.37917,6.14,1.5],[7734,303.52083,36.60528,6.45,0.0],[7735,303.40792,46.74139,3.79,1.28],[7736,303.63333,36.80639,4.97,0.14],[7737,303.59,42.10361,6.71,-0.04],[7738,304.095,-12.33694,6.32,0.01],[7739,303.81625,25.59194,4.78,-0.18],[7740,303.34958,56.56778,4.3,0.11],[7741,303.87583,23.50861,5.15,1.04],[7742,303.36542,60.64056,5.79,1.47],[7743,303.84875,33.72944,5.66,0.91],[7744,303.94208,27.81417,4.52,1.26],[7745,304.73333,-47.71083,6.31,1.67],[7746,304.08208,21.59861,6.13,1.04],[7747,304.41208,-12.50833,4.24,1.07],[7748,304.50583,-21.81,5.87,1.0],[7749,304.82458,-47.58028,6.13,0.46],[7750,302.22208,77.71139,4.39,-0.05],[7751,303.86792,47.71444,3.98,1.52],[7752,304.01417,38.89778,6.27,0.01],[7753,304.19625,24.67111,5.32,0.95],[7754,304.51375,-12.54472,3.57,0.94],[7755,303.93083,50.23278,6.31,0.6],[7756,304.0025,45.57944,5.91,0.38],[7757,304.1175,37.05639,6.48,-0.09],[7758,305.13458,-55.05083,6.27,1.59],[7759,304.23042,40.365,5.24,1.65],[7760,304.38125,29.14806,6.22,1.01],[7761,304.84833,-19.11861,5.28,1.4],[7762,304.37125,42.72194,6.29,1.64],[7763,304.44667,38.03306,4.81,0.42],[7764,305.11708,-29.19722,6.3,0.17],[7765,305.21625,-35.67361,6.46,1.31],[7766,305.42083,-49.99944,6.27,0.55],[7767,304.52917,40.73222,5.84,0.1],[7768,304.93042,-1.07861,6.06,1.09],[7769,304.61917,37.0,5.58,0.06],[7770,304.66292,34.98278,5.17,0.65],[7771,304.87167,13.21694,6.21,1.63],[7772,305.10875,-6.36167,6.63,1.55],[7773,305.16583,-12.75917,4.76,-0.05],[7774,305.00083,13.54806,5.95,0.31],[7775,305.19417,-14.785,6.1,-0.02],[7776,305.25292,-14.78139,3.08,0.79],[7777,304.70667,46.32278,6.45,-0.13],[7778,305.08542,14.56917,6.13,0.91],[7779,305.61458,-42.04972,5.59,0.0],[7780,305.08917,17.79306,5.8,1.49],[7781,304.60333,55.39722,5.76,0.11],[7782,304.95125,37.1325,6.57,0.0],[7783,304.38042,66.85389,5.93,0.58],[7784,305.06333,39.40333,6.23,0.06],[7785,308.32292,-80.965,5.77,1.14],[7786,304.98375,46.8375,6.5,-0.06],[7787,305.97167,-42.42278,5.64,0.2],[7788,305.75333,-9.65472,6.3,0.91],[7789,305.51417,24.44611,5.54,-0.06],[7790,306.41208,-56.735,1.94,-0.2],[7791,305.12667,53.59611,6.18,1.56],[7792,304.90292,62.2575,5.72,-0.05],[7793,305.71792,14.55139,6.17,0.51],[7794,305.79458,5.34306,5.31,0.97],[7795,305.5125,41.13139,6.39,1.07],[7796,305.55708,40.25667,2.2,0.68],[7797,305.65583,31.265,6.09,1.35],[7798,305.5225,45.795,5.58,1.08],[7799,306.44958,-40.79639,6.09,1.36],[7800,305.68875,41.02611,5.93,1.6],[7801,306.36167,-28.66333,5.85,1.1],[7802,305.73125,42.98333,6.2,0.95],[7803,306.15625,1.06861,6.15,-0.04],[7804,305.025,68.88028,5.55,1.59],[7805,305.2975,63.98028,5.69,1.56],[7806,305.96542,32.19,4.43,1.33],[7807,305.935,37.47639,5.9,-0.2],[7808,306.72083,-37.40306,6.25,0.97],[7809,306.42708,-2.80028,6.11,1.19],[7810,306.43333,10.05639,6.33,1.56],[7811,306.41875,21.40972,5.66,0.93],[7812,309.5775,-81.28889,5.91,1.71],[7813,306.505,19.865,6.41,1.02],[7814,306.83,-18.21167,5.25,-0.07],[7815,306.135,53.55194,6.51,0.01],[7816,306.59667,17.31556,6.22,1.01],[7817,307.19458,-35.59583,6.1,-0.11],[7818,306.27125,59.6,6.44,0.09],[7819,307.18167,-15.74167,6.41,0.99],[7820,307.03125,8.4375,6.25,1.08],[7821,307.10375,-3.35778,6.13,-0.06],[7822,307.215,-17.81361,4.78,0.38],[7823,306.78208,34.32889,6.39,0.48],[7824,307.07,2.93694,6.21,0.9],[7825,307.38042,-22.39167,6.16,1.55],[7826,306.89292,38.44028,5.62,0.06],[7827,306.59792,56.63889,6.36,0.0],[7828,306.75958,49.38333,5.69,0.26],[7829,307.46875,-18.58667,6.74,0.22],[7830,307.47458,-18.58333,5.94,0.08],[7831,307.4125,-2.88556,4.91,1.15],[7832,307.73667,-29.1125,6.39,0.21],[7833,307.33792,20.08778,6.55,0.23],[7834,307.34875,30.36861,4.01,0.4],[7835,307.335,36.45472,5.88,0.52],[7836,307.575,10.89583,6.08,-0.03],[7837,307.76792,-15.05639,6.12,0.79],[7838,308.96542,-69.61111,6.11,1.29],[7839,307.74208,20.60583,6.18,0.15],[7840,307.80417,11.26056,7.11,0.03],[7841,307.49958,45.92861,6.41,1.13],[7842,308.21833,-24.94389,6.36,-0.01],[7843,307.36292,56.06806,5.91,-0.04],[7844,307.51458,48.95167,4.95,-0.09],[7845,308.09875,-9.85333,5.65,0.69],[7846,308.47958,-44.51611,5.11,1.01],[7847,307.74667,36.93583,6.19,1.01],[7848,308.895,-60.58167,4.76,0.28],[7849,307.9925,25.80444,6.34,0.26],[7850,307.39542,62.99417,4.22,0.2],[7851,307.82833,49.22028,5.44,1.55],[7852,308.30333,11.30333,4.03,-0.13],[7853,308.73125,-38.08972,6.44,0.26],[7854,307.83792,52.30972,6.18,1.01],[7855,308.54875,-13.72111,6.13,0.54],[7856,308.6975,-30.47361,6.4,-0.08],[7857,308.47333,10.05972,6.56,0.08],[7858,308.4875,13.02722,5.38,0.07],[7859,309.39708,-61.53,4.88,0.43],[7860,307.94375,56.78,6.14,1.43],[7861,308.21792,43.19167,6.6,-0.11],[7862,308.54167,20.98528,6.48,-0.14],[7863,310.51208,-76.18056,6.0,0.44],[7864,310.43208,-75.35056,6.55,0.62],[7865,308.88417,-16.52583,6.19,0.2],[7866,308.47583,35.25083,4.61,1.6],[7867,308.45167,41.77222,6.49,1.0],[7868,307.50292,72.53167,6.27,1.35],[7869,309.39167,-47.29139,3.11,1.0],[7870,308.47875,46.69389,5.78,-0.16],[7871,308.82708,14.67417,4.68,0.11],[7872,309.96417,-62.90778,6.22,1.1],[7873,309.18167,-2.55,4.89,1.6],[7874,309.03458,25.8825,6.41,0.21],[7875,310.01083,-60.54889,5.12,0.53],[7876,308.71,51.85417,6.11,0.41],[7877,309.46708,-25.10889,6.36,0.33],[7878,309.32625,0.09694,6.22,-0.08],[7879,307.87667,74.95472,5.2,0.07],[7880,309.26958,26.46194,5.59,-0.07],[7881,310.48792,-66.76083,5.15,-0.06],[7882,309.3875,14.59528,3.63,0.44],[7883,309.45458,11.37778,5.43,0.06],[7884,309.58458,-1.10528,4.32,0.95],[7885,309.3825,31.5725,6.32,-0.09],[7886,309.4775,18.26917,6.25,1.48],[7887,309.38583,31.52194,6.49,0.34],[7888,309.34833,38.32861,6.2,0.99],[7889,309.81833,-14.95472,5.22,-0.12],[7890,309.805,-2.41278,6.22,-0.1],[7891,309.63042,21.20111,4.82,-0.02],[7892,309.68292,13.315,5.72,1.53],[7893,310.0825,-33.43194,5.47,1.12],[7894,309.63292,24.11611,5.04,-0.14],[7895,309.64625,23.68056,5.91,0.98],[7896,309.7825,10.08611,5.05,0.72],[7897,309.85375,0.48639,5.16,1.06],[7898,310.04875,-23.77389,6.37,0.72],[7899,309.77083,15.83806,5.97,-0.14],[7900,310.0125,-18.13861,5.1,1.66],[7901,307.06083,81.42278,5.46,1.02],[7902,310.15,-26.645,6.51,0.57],[7903,309.79417,21.81722,6.08,-0.06],[7904,309.74792,30.33444,5.68,1.09],[7905,310.13542,-16.12417,5.8,1.0],[7906,309.90958,15.91194,3.77,-0.06],[7907,309.96583,11.24972,6.42,0.55],[7908,307.36458,81.09139,5.96,0.92],[7909,310.34875,-31.59833,5.76,1.53],[7910,310.35042,-26.0,6.28,1.22],[7911,309.88875,40.57944,6.06,-0.16],[7912,309.84625,45.66694,6.58,-0.06],[7913,311.23958,-66.20306,3.42,0.16],[7914,310.18833,19.93528,6.45,0.63],[7915,310.72083,-39.55861,6.29,1.08],[7916,309.75083,56.005,6.48,0.6],[7917,310.15083,29.80528,6.08,0.14],[7918,310.3175,14.58306,5.99,1.24],[7919,310.01292,43.45861,5.95,1.19],[7920,311.00958,-51.92111,4.51,0.27],[7921,310.26083,32.30722,5.51,0.88],[7922,310.25167,39.08222,6.51,-0.12],[7923,310.4925,17.52139,6.22,0.94],[7924,310.35792,45.28028,1.25,0.09],[7925,310.07458,60.50528,6.01,0.46],[7926,310.48542,41.71694,5.67,-0.12],[7927,310.5925,35.45611,6.66,-0.16],[7928,310.86458,15.07444,4.43,0.32],[7929,310.5525,50.34,5.39,-0.1],[7930,307.26292,83.62556,6.19,0.13],[7931,311.305,-27.24722,6.5,0.94],[7932,310.85083,35.58778,6.47,1.23],[7933,311.58375,-39.19917,5.5,-0.1],[7934,312.32542,-68.77639,5.41,1.12],[7935,311.5775,-36.12028,6.49,0.39],[7936,311.52375,-25.27083,4.14,0.43],[7937,311.54167,-21.51417,5.93,0.04],[7938,310.66542,60.60139,6.15,0.01],[7939,311.21875,25.27056,4.91,1.18],[7940,310.80625,57.11417,6.32,0.33],[7941,311.3675,18.09028,6.38,1.68],[7942,311.41542,30.71972,4.22,1.05],[7943,312.12167,-43.98861,5.11,0.35],[7944,311.09167,56.48806,5.78,1.67],[7945,310.79583,66.6575,5.58,0.22],[7946,311.765,-2.48667,6.27,1.55],[7947,311.66125,16.12444,5.14,0.49],[7948,311.66458,16.12417,4.27,1.04],[7949,311.55292,33.97028,2.46,1.03],[7950,311.91917,-9.49583,3.77,0.0],[7951,311.93417,-5.02778,4.42,1.65],[7952,312.37083,-46.22694,4.89,1.52],[7953,311.95125,6.00833,5.58,-0.02],[7954,311.94917,3.30667,6.4,-0.02],[7955,311.33792,57.57972,4.51,0.54],[7956,311.795,34.37417,4.92,1.32],[7957,311.3225,61.83889,3.43,0.92],[7958,311.66042,46.53167,6.3,0.03],[7959,312.90958,-62.42917,6.28,0.15],[7960,312.91167,-62.42917,6.59,0.17],[7961,312.32333,-25.78139,5.86,-0.07],[7962,311.58833,52.99528,6.33,1.12],[7963,311.85208,36.49083,4.53,-0.11],[7964,312.33542,-18.03583,6.21,1.42],[7965,312.49208,-33.77972,4.9,1.0],[7966,311.83667,45.57972,6.4,1.61],[7967,311.13792,69.75194,6.41,0.6],[7968,312.87542,-51.60833,5.05,1.13],[7969,311.95542,47.83194,5.57,1.46],[7970,312.69625,-32.05444,6.36,1.54],[7971,312.75292,-37.91333,5.52,1.38],[7972,311.97,52.40722,6.27,0.89],[7973,312.4075,12.54528,5.98,0.43],[7974,312.45125,7.86417,6.33,0.02],[7975,312.49625,5.54472,6.21,0.98],[7976,312.67417,-12.545,5.88,1.07],[7977,312.23458,46.11417,4.84,0.41],[7978,312.17792,51.91056,6.29,-0.07],[7979,312.995,-33.17722,6.04,0.03],[7980,312.95542,-26.91917,4.11,1.64],[7981,312.65417,18.05139,6.52,0.04],[7982,312.85708,-5.62639,5.99,0.46],[7983,312.47792,46.66111,6.33,-0.07],[7984,312.52042,44.05944,5.04,0.2],[7985,313.03625,-5.50694,5.55,-0.08],[7986,313.7025,-58.45417,3.65,1.25],[7987,313.4175,-39.81,5.35,1.32],[7988,312.8675,28.25056,5.77,0.72],[7989,313.255,-23.78306,6.33,0.88],[7990,313.16333,-8.98333,4.73,0.32],[7991,313.35417,-30.71861,6.35,1.09],[7992,313.64583,-50.72778,6.24,-0.12],[7993,312.3225,64.04222,6.45,0.07],[7994,313.27333,-11.57361,6.38,0.67],[7995,313.03208,27.09694,4.59,0.83],[7996,313.00125,32.84917,6.44,-0.15],[7997,313.52833,-27.92556,6.41,1.6],[7998,313.49333,-6.88972,6.44,0.37],[7999,313.28083,29.64944,6.34,1.09],[8000,313.69958,-17.92306,5.78,1.12],[8001,313.31167,44.38722,4.78,-0.14],[8002,310.64667,82.53111,5.75,0.0],[8003,313.3275,45.18194,5.45,1.1],[8004,313.36,42.41028,6.66,-0.04],[8005,313.47458,33.43778,5.47,1.52],[8006,313.78375,-1.37333,6.55,0.29],[8007,313.59333,28.52194,6.56,-0.13],[8008,313.64,28.0575,5.01,1.48],[8009,313.59292,40.70306,6.7,-0.07],[8010,313.91958,4.53278,6.05,0.82],[8011,313.90292,13.72139,5.17,1.12],[8012,313.91083,12.56861,5.58,0.12],[8013,314.19708,-26.29639,5.7,0.5],[8014,314.07625,-3.56139,6.57,-0.1],[8015,314.225,-9.6975,5.51,1.47],[8016,311.88917,80.55222,5.39,1.12],[8017,314.29417,0.46361,6.05,1.22],[8018,314.41917,-16.03167,5.87,0.18],[8019,315.36708,-68.20972,6.37,0.1],[8020,313.9575,47.41778,5.67,0.47],[8021,316.17917,-77.02389,5.15,0.49],[8022,314.05375,51.075,6.63,-0.1],[8023,314.14458,44.925,5.96,0.05],[8024,314.67458,-14.48278,6.01,0.23],[8025,314.10625,50.72861,5.81,0.3],[8026,314.10792,49.19583,5.9,1.04],[8027,315.08958,-51.26528,5.76,0.47],[8028,314.29333,41.16722,3.94,0.02],[8029,314.07083,56.8875,6.23,-0.18],[8030,314.60792,10.83917,5.48,0.93],[8031,314.99875,-36.12972,6.11,0.39],[8032,314.56833,22.32583,5.31,1.4],[8033,314.90042,-19.03528,6.25,-0.13],[8034,314.76833,4.29361,5.23,0.46],[8035,314.58125,44.47167,5.55,0.97],[8036,314.62833,41.94028,6.16,-0.08],[8037,314.96167,16.82417,6.66,0.38],[8038,315.01625,7.51639,5.99,0.26],[8039,315.32292,-32.25778,4.67,0.89],[8040,314.62542,50.46222,5.61,-0.15],[8041,315.14083,-4.73028,6.21,0.63],[8042,315.5525,-43.00194,6.64,0.68],[8043,313.68458,75.92556,6.05,0.93],[8044,315.11542,19.32944,5.65,1.61],[8045,315.43875,-26.88111,6.05,0.06],[8046,315.61333,-38.53056,5.94,1.11],[8047,314.95667,47.52111,4.74,-0.05],[8048,315.74167,-38.63167,5.3,0.41],[8049,314.85583,59.43861,5.51,1.4],[8050,315.7925,-27.73194,6.25,0.95],[8051,315.30375,36.02611,5.97,0.98],[8052,317.19958,-76.2125,6.58,1.23],[8053,315.29542,46.15583,5.37,-0.21],[8054,315.74833,-0.92472,6.5,-0.1],[8055,316.30917,-54.72722,5.16,1.21],[8056,315.7625,1.53194,6.25,0.48],[8057,315.7575,14.73,6.31,1.67],[8058,316.01917,-5.82333,7.31,0.6],[8059,316.01958,-5.82306,5.89,0.68],[8060,316.10125,-19.855,4.84,0.17],[8061,317.34292,-73.17306,5.68,0.59],[8062,315.60042,44.79111,6.19,1.69],[8063,315.77,38.6575,6.07,1.01],[8064,315.70208,45.84889,6.48,-0.15],[8065,315.53792,56.66972,5.83,-0.07],[8066,316.14458,5.50278,5.61,1.65],[8067,316.17375,2.94194,6.42,1.05],[8068,316.18917,2.26972,6.33,0.97],[8069,316.60625,-41.38611,5.53,1.35],[8070,316.505,-30.125,5.68,1.03],[8071,315.96708,41.62806,6.33,0.38],[8072,315.85833,50.35194,6.37,0.98],[8073,317.13708,-63.92889,5.76,1.18],[8074,315.93042,46.86194,6.32,0.25],[8075,316.48667,-17.23278,4.07,-0.01],[8076,316.60292,-32.34167,5.18,1.1],[8077,316.36125,5.95833,5.94,0.54],[8078,315.94833,53.28611,5.9,0.99],[8079,316.23292,43.92778,3.72,1.65],[8080,316.78208,-25.00583,4.5,1.61],[8081,317.83625,-72.54417,6.2,1.08],[8082,316.59792,26.92444,6.12,1.05],[8083,316.93583,-17.45528,6.17,0.02],[8084,316.62625,31.18472,5.82,0.56],[8085,316.7275,38.74583,5.21,1.18],[8086,316.73042,38.74333,6.03,1.37],[8087,317.14,-21.19361,5.3,0.01],[8088,316.89,15.65861,6.34,1.01],[8089,316.65042,47.64833,4.55,1.57],[8090,317.1175,6.98944,6.15,1.66],[8091,317.3875,-20.55639,6.25,0.38],[8092,318.33542,-70.12639,5.02,1.58],[8093,317.39875,-11.37167,4.51,0.94],[8094,317.16208,30.20583,5.59,-0.1],[8095,317.49292,2.94333,6.45,0.37],[8096,317.69542,-9.35389,6.27,1.16],[8097,317.58542,10.13167,4.69,0.26],[8098,317.63,10.04889,6.07,0.02],[8099,316.59708,71.43194,5.87,0.4],[8100,318.05667,-40.26944,5.83,0.45],[8101,317.63333,22.45472,6.68,0.03],[8102,317.92208,-14.47222,6.48,0.28],[8103,317.49417,45.5025,6.63,-0.16],[8104,318.26292,-39.42528,5.26,0.44],[8105,317.76625,36.29917,6.54,-0.14],[8106,317.565,53.56333,5.73,-0.12],[8107,317.62917,47.69194,6.46,-0.01],[8108,318.32875,-36.42389,5.96,0.98],[8109,317.37042,63.29556,6.54,-0.13],[8110,318.32208,-27.61944,5.42,1.42],[8111,319.56708,-75.34667,6.63,0.03],[8112,316.37208,78.12639,5.91,-0.07],[8113,317.38333,68.49028,7.33,1.49],[8114,318.94125,-53.26306,5.75,0.19],[8115,318.23417,30.22694,3.2,0.99],[8116,318.37,15.9825,6.27,0.24],[8117,318.81125,-40.50639,6.21,1.14],[8118,318.56958,-10.60528,6.77,-0.08],[8119,317.95083,59.98639,5.64,0.11],[8120,318.35958,36.63361,6.05,0.21],[8121,318.65417,0.09222,6.38,1.61],[8122,318.7775,-17.345,6.04,0.96],[8123,318.62042,10.00694,4.49,0.5],[8124,318.945,-36.21083,6.12,1.37],[8125,319.50125,-64.68167,6.31,-0.07],[8126,318.54292,29.90111,6.17,1.09],[8127,318.90792,-20.65167,5.24,1.17],[8128,318.93708,-15.17139,5.28,1.64],[8129,323.0175,-84.81,6.45,1.4],[8130,318.69792,38.04556,3.72,0.39],[8131,318.95583,5.24778,3.92,0.53],[8132,319.165,-1.60778,6.48,0.98],[8133,318.4275,64.40389,6.39,0.6],[8134,319.30625,-13.27889,6.4,0.04],[8135,319.48458,-32.1725,4.71,0.06],[8136,318.90333,47.97361,6.46,-0.15],[8137,319.48875,-17.98528,5.43,-0.12],[8138,319.12333,42.25139,6.43,0.6],[8139,319.56542,-17.46222,7.05,0.34],[8140,319.96667,-53.44972,4.39,0.19],[8141,319.54625,-4.51944,5.82,-0.13],[8142,319.72667,-28.76556,6.4,0.97],[8143,319.35417,39.39472,4.23,0.12],[8144,319.34667,42.68333,6.19,-0.11],[8145,320.03958,-45.02222,6.0,2.33],[8146,319.47958,34.89694,4.43,-0.11],[8147,319.25833,53.9975,6.13,0.05],[8148,319.94083,-26.35306,6.56,0.73],[8149,319.71667,11.20333,5.96,1.65],[8150,319.30958,55.79806,5.98,1.45],[8151,320.19,-40.80972,4.82,0.02],[8152,320.31833,-49.93778,6.38,1.32],[8153,319.32833,58.61167,6.42,0.2],[8154,319.61333,43.94583,5.0,-0.01],[8155,319.73042,41.04083,6.15,0.3],[8156,321.06958,-69.73417,6.41,2.82],[8157,319.8425,38.2375,5.83,0.5],[8158,320.05833,22.02639,6.29,-0.08],[8159,321.32542,-71.79944,6.09,1.26],[8160,320.26792,-4.56,5.87,0.92],[8161,319.87,49.51028,5.76,-0.15],[8162,319.645,62.58556,2.44,0.22],[8163,320.27,7.35444,5.82,1.66],[8164,319.81583,58.62361,5.66,1.38],[8165,320.26833,23.85583,5.57,1.05],[8166,320.20875,32.45278,5.68,1.08],[8167,320.56167,-16.83444,4.28,0.9],[8168,318.92583,77.01222,5.95,1.5],[8169,320.34167,32.61278,6.04,0.03],[8170,320.25583,40.34556,6.4,0.53],[8171,319.8425,64.87194,5.18,-0.04],[8172,320.75208,-22.66889,5.6,1.63],[8173,320.52167,19.80444,4.08,1.11],[8174,318.33958,81.23083,6.15,0.09],[8175,320.73458,-9.31944,5.99,1.54],[8176,323.47667,-82.68306,6.38,0.13],[8177,321.08667,-46.615,6.31,0.2],[8178,320.72333,6.81111,5.16,0.05],[8179,320.13958,60.75694,6.11,1.0],[8180,321.10333,-41.00667,5.77,-0.05],[8181,321.61083,-65.36611,4.22,0.49],[8182,320.67458,30.30972,6.05,1.08],[8183,321.04,-20.85194,5.41,1.16],[8184,321.03292,-22.74694,6.38,1.02],[8185,320.50167,49.38889,5.69,1.1],[8186,320.69542,38.63417,6.63,0.01],[8187,321.04792,-12.87806,5.49,0.29],[8188,321.56417,-54.66056,6.12,0.34],[8189,320.84583,37.40667,6.58,0.6],[8190,320.995,24.27417,5.71,0.32],[8191,321.1025,10.17417,6.35,0.47],[8192,321.21542,-3.39833,6.36,0.33],[8193,320.95125,37.35139,6.47,0.6],[8194,321.03083,25.31222,6.15,0.6],[8195,321.30458,-9.74861,5.7,0.2],[8196,322.18708,-69.50528,5.34,1.55],[8197,321.09625,24.52889,6.32,1.04],[8198,321.14167,26.17444,5.68,0.31],[8199,321.32083,-3.55667,5.49,1.46],[8200,321.59542,-37.82944,5.63,1.19],[8201,323.33583,-80.03917,6.47,0.04],[8202,321.75667,-42.54778,5.51,0.39],[8203,321.46458,0.53444,6.46,0.16],[8204,321.66667,-22.41139,3.74,1.0],[8205,321.61708,1.10333,6.13,0.44],[8206,321.23125,49.32333,6.58,-0.03],[8207,321.81167,-21.19611,5.78,1.44],[8208,321.33167,46.71444,5.6,0.32],[8209,321.44583,36.6675,5.94,-0.08],[8210,321.61125,19.37556,6.07,0.22],[8211,322.25125,-53.70583,6.39,1.14],[8212,322.05792,-11.56833,6.61,0.49],[8213,322.18083,-21.80722,4.51,0.91],[8214,322.26583,-31.23861,6.5,0.03],[8215,321.83917,37.11667,5.31,-0.14],[8216,321.715,48.835,5.31,0.07],[8217,321.91708,27.60861,5.41,0.04],[8218,321.6875,52.89861,6.03,-0.12],[8219,322.10333,8.19556,6.4,1.64],[8220,322.03458,32.22528,5.8,0.32],[8221,322.24958,17.90583,6.44,1.39],[8222,322.49833,-19.14778,6.57,0.41],[8223,322.24958,22.17944,5.93,1.53],[8224,321.85542,59.75,6.1,1.75],[8225,322.48708,23.63889,4.57,1.62],[8226,322.21958,55.41861,6.12,0.14],[8227,321.94208,66.80917,5.44,-0.11],[8228,322.3625,46.54056,5.24,0.97],[8229,323.02458,-41.17917,5.29,1.1],[8230,323.06083,-33.94472,5.97,0.05],[8231,322.79,12.1375,6.08,-0.05],[8232,322.88958,-5.57111,2.91,0.83],[8233,323.32375,-52.7375,6.41,1.48],[8234,324.73417,-79.4425,6.18,0.46],[8235,323.13875,-24.59056,6.43,0.2],[8236,323.34792,-44.84861,5.57,1.04],[8237,322.585,52.95806,6.02,0.08],[8238,322.165,70.56083,3.23,-0.22],[8239,321.20708,80.52472,5.97,0.92],[8240,323.11292,23.39444,6.7,-0.09],[8241,323.57083,-42.925,6.32,1.09],[8242,322.86458,52.62,6.16,0.9],[8243,322.74708,60.45944,5.53,0.12],[8244,323.72083,-29.69611,6.41,-0.11],[8245,323.7125,-20.08444,5.69,0.4],[8246,323.23583,49.97778,5.75,-0.03],[8247,323.81625,-23.45417,6.4,1.1],[8248,323.32458,45.85417,6.25,1.81],[8249,324.51208,-64.82417,6.2,0.03],[8250,323.64167,22.75472,6.47,0.51],[8251,323.82333,-3.98306,5.77,1.11],[8252,323.49542,45.59194,4.02,0.89],[8253,324.04583,-26.17139,5.73,0.22],[8254,325.36875,-77.39,3.76,1.0],[8255,323.69417,38.53417,4.9,1.08],[8256,324.20375,-33.04806,6.11,0.22],[8257,323.82917,28.19722,6.31,0.35],[8258,323.8625,24.45222,6.11,0.6],[8259,323.61458,51.69833,6.15,0.02],[8260,324.27,-19.46611,4.68,-0.17],[8261,324.05792,30.05556,6.36,0.6],[8262,324.01,45.37472,5.53,1.58],[8263,324.39083,-0.39028,6.25,0.06],[8264,324.43792,-7.85417,4.69,0.17],[8265,324.43208,6.61833,6.18,0.02],[8266,324.2375,40.41361,5.01,0.18],[8267,324.43917,19.31861,5.45,0.3],[8268,324.77542,-33.67917,6.28,0.92],[8269,324.99875,-52.35917,6.21,0.6],[8270,324.63292,5.77167,5.67,0.25],[8271,325.14,-55.7375,6.33,1.06],[8272,324.36625,44.69667,6.2,0.19],[8273,324.86708,-10.57694,6.08,1.03],[8274,324.6875,25.49889,6.16,1.02],[8275,324.41125,54.04222,6.15,0.99],[8276,324.75458,20.26528,5.85,0.32],[8277,324.88875,2.24361,5.1,1.04],[8278,325.02292,-16.66222,3.68,0.32],[8279,324.48,62.08194,4.73,0.3],[8280,327.72625,-82.71917,5.29,0.75],[8281,324.74,57.48917,5.62,0.21],[8282,325.44208,-25.10194,6.49,1.19],[8283,325.38708,-14.0475,5.18,0.65],[8284,325.04625,43.27389,5.11,1.6],[8285,325.50333,-23.26278,5.24,0.95],[8286,326.37,-71.00889,6.01,-0.1],[8287,325.54208,1.28528,5.67,1.44],[8288,325.66458,-18.86639,4.73,0.88],[8289,325.56458,5.68,5.3,1.64],[8290,325.18042,54.87222,6.2,1.16],[8291,325.39292,40.80528,6.11,0.07],[8292,325.6375,10.82472,6.09,-0.11],[8293,325.80625,-19.62083,6.22,0.27],[8294,341.36917,-88.81833,6.57,0.28],[8295,325.76833,-14.39972,5.88,0.25],[8297,325.50458,35.51028,6.07,2.52],[8298,325.535,45.76583,6.17,1.55],[8299,326.12292,-38.5525,6.3,1.12],[8300,325.59542,41.07722,5.69,0.01],[8301,325.52375,51.18972,4.67,-0.12],[8302,326.00417,-14.74944,5.99,0.21],[8303,326.32917,-49.49861,6.45,1.15],[8304,325.66208,49.60028,6.09,1.0],[8305,326.23667,-33.02583,4.34,-0.05],[8306,325.77667,41.155,5.49,1.59],[8307,325.85708,38.28389,5.65,0.0],[8308,326.04667,9.875,2.39,1.53],[8309,326.03583,28.74278,4.73,0.48],[8310,326.03458,28.74306,6.08,0.36],[8311,326.25125,-9.0825,5.09,1.11],[8312,325.68917,59.27111,6.08,1.34],[8313,326.12792,17.35,4.34,1.17],[8314,326.13042,14.77194,5.94,0.59],[8315,326.16125,25.645,4.13,0.43],[8316,325.87667,58.78,4.08,2.35],[8317,325.48042,71.31139,4.56,1.1],[8318,326.56792,-9.27583,6.0,1.66],[8319,326.63375,-11.36583,5.58,-0.01],[8320,326.43542,35.85722,6.4,1.0],[8321,326.51833,22.94889,5.29,1.41],[8322,326.76,-16.12722,2.87,0.29],[8323,327.06583,-47.30361,5.58,0.6],[8324,325.76667,72.32028,5.17,1.05],[8325,326.6,25.56333,6.28,1.21],[8326,326.93417,-30.89833,5.01,0.04],[8327,326.22208,62.46056,5.95,0.31],[8328,326.80833,2.68611,5.64,0.0],[8329,326.56917,43.06083,6.54,0.28],[8330,326.76958,17.19417,6.21,0.34],[8331,327.50042,-64.7125,5.62,1.02],[8332,326.90875,-5.91722,6.17,0.22],[8333,327.69625,-69.62944,5.53,1.37],[8334,326.36208,61.12083,4.29,0.52],[8335,326.69833,49.30944,4.23,-0.12],[8336,327.03458,36.58056,6.47,0.6],[8337,327.42125,-12.72306,6.31,0.22],[8338,327.1225,38.64861,6.12,-0.08],[8339,326.85542,60.69278,5.52,1.52],[8340,327.55458,-16.84472,6.38,1.42],[8341,327.36208,20.4625,6.29,-0.1],[8342,326.75417,70.15083,6.29,0.6],[8343,327.46125,30.17417,5.04,-0.03],[8344,327.53625,17.28556,5.29,0.37],[8345,327.41708,41.14889,6.48,0.42],[8346,327.92417,-18.62306,6.16,0.36],[8347,327.32917,61.27278,6.17,1.67],[8348,327.8925,19.82667,5.77,-0.1],[8349,327.77042,39.53667,6.17,-0.08],[8350,328.07583,21.27306,6.89,0.6],[8351,328.32417,-13.55167,5.08,0.37],[8352,328.79792,-61.88639,5.9,0.39],[8353,328.48208,-37.365,3.01,-0.12],[8354,328.12458,28.79333,5.53,0.42],[8355,328.4,-10.31167,6.59,-0.12],[8356,328.26583,25.925,5.08,-0.17],[8357,328.00417,55.79694,5.71,-0.13],[8358,328.40583,19.66833,5.68,0.01],[8359,328.49083,6.86444,6.15,0.8],[8360,328.54333,-4.27611,5.71,1.18],[8361,327.90542,65.75278,6.37,0.6],[8362,329.05833,-57.89944,6.19,0.21],[8363,328.64958,-3.30111,6.2,0.48],[8364,328.5725,19.71833,6.39,0.97],[8365,328.98167,-30.60639,6.41,0.93],[8366,329.095,-37.25361,5.46,0.08],[8367,329.25917,-37.74694,6.18,0.33],[8368,329.47958,-54.9925,4.4,0.28],[8369,329.62542,-59.01222,6.12,0.46],[8370,330.46708,-77.6625,6.41,0.22],[8371,328.72167,56.61139,5.8,0.73],[8372,329.1,21.23972,6.4,1.7],[8373,329.235,12.07639,5.54,0.05],[8374,328.83625,61.54194,6.13,1.6],[8375,328.87958,65.32083,5.86,-0.06],[8376,329.55542,-5.42472,6.33,0.37],[8377,329.25917,48.66861,6.42,-0.08],[8378,329.6825,-21.18278,6.12,1.65],[8379,329.82458,-38.39528,5.5,1.0],[8380,330.76583,-76.11861,5.95,0.39],[8381,330.10042,-55.88278,6.01,-0.1],[8382,329.72917,-4.37306,6.22,1.0],[8383,329.16292,63.62556,4.91,1.77],[8384,329.29625,66.15611,6.43,-0.05],[8385,330.03292,6.7175,6.0,-0.12],[8386,330.20917,-28.45361,5.42,-0.09],[8387,330.84,-56.78611,4.69,1.06],[8388,329.7225,62.69833,5.93,1.67],[8389,329.84583,57.65833,6.59,0.03],[8390,330.27083,0.605,5.58,1.28],[8391,330.11167,33.00611,6.46,0.6],[8392,330.2725,13.11972,5.6,0.34],[8393,330.28833,8.25722,5.65,1.44],[8394,330.54958,-17.90361,6.28,1.01],[8395,329.4625,74.99667,6.35,1.56],[8396,330.61083,-16.96417,6.37,0.42],[8397,330.50583,10.97389,6.37,-0.1],[8398,330.82167,-29.90417,7.1,0.32],[8399,330.16375,62.48806,6.66,0.06],[8400,329.8125,73.18,5.03,0.44],[8401,330.81833,-6.5225,5.54,0.96],[8402,330.82875,-2.15528,4.69,-0.06],[8403,330.46083,52.88222,5.78,-0.11],[8404,330.82917,11.38639,5.8,-0.07],[8405,331.09958,-29.91667,6.47,1.63],[8406,330.51917,58.00056,5.56,0.06],[8407,330.73625,44.65,5.6,-0.03],[8408,331.15333,-26.8225,5.96,-0.14],[8409,331.46208,-59.63611,5.62,1.46],[8410,331.1975,-0.90667,5.3,0.23],[8411,331.52875,-39.54333,4.46,1.37],[8412,331.14333,32.94194,6.38,1.1],[8413,331.42,5.05861,4.84,1.44],[8414,331.44583,-0.31972,2.96,0.98],[8415,331.2975,26.67389,5.78,1.25],[8416,330.97042,63.11972,5.29,1.58],[8417,330.9475,64.62778,4.29,0.34],[8418,331.60917,-13.86972,4.27,-0.07],[8419,331.39458,28.96389,5.7,-0.06],[8420,332.67708,-75.88056,6.55,1.18],[8421,331.31833,46.74472,6.13,1.61],[8422,331.46083,45.11222,6.44,-0.03],[8423,329.55292,82.86972,6.98,0.52],[8424,331.50833,45.01444,5.14,1.57],[8425,332.05833,-46.96111,1.74,-0.13],[8426,331.25208,62.78556,5.27,1.41],[8427,331.46333,48.23167,6.27,-0.06],[8428,331.28708,62.28,5.11,0.08],[8429,331.55167,45.24861,6.19,0.09],[8430,331.75292,25.345,3.76,0.44],[8431,332.09583,-32.98861,4.5,0.05],[8432,332.98042,-76.11611,6.15,1.0],[8433,332.10833,-34.04389,4.99,1.48],[8434,331.55625,56.34306,6.39,-0.1],[8435,331.86917,19.47556,5.75,0.6],[8436,331.875,18.00056,6.35,1.64],[8437,332.17792,-33.12556,6.37,0.2],[8438,331.95958,21.70278,5.78,-0.1],[8439,332.24583,-18.51972,5.81,-0.17],[8440,332.49167,-48.10722,6.43,1.39],[8441,332.07167,25.54361,6.11,0.27],[8442,331.79,58.84083,6.32,0.88],[8443,331.85625,53.30722,6.14,0.43],[8444,332.48208,-34.01472,5.37,0.24],[8445,332.06875,49.79639,6.42,0.6],[8446,332.50042,-28.2925,6.44,0.15],[8447,332.53667,-32.54833,4.92,0.48],[8448,332.17083,45.74194,6.11,0.72],[8449,332.30667,33.17222,5.58,1.0],[8450,332.55,6.19778,3.53,0.08],[8451,332.58792,-3.89417,6.27,-0.01],[8452,332.65625,-11.565,5.46,-0.12],[8453,332.64083,-4.26722,6.01,0.98],[8454,332.49667,33.17833,4.29,0.46],[8455,332.57917,19.61694,6.18,0.69],[8456,332.5925,14.63,6.33,1.08],[8457,332.76,-21.2325,6.09,0.5],[8458,332.65583,11.62444,5.78,1.58],[8459,332.62583,20.97806,6.46,0.12],[8460,332.715,30.55306,6.32,0.18],[8461,332.96375,16.04056,5.95,0.95],[8462,333.1075,-14.19389,6.03,0.38],[8463,332.79125,50.82333,5.4,0.15],[8464,333.23958,-26.32778,6.17,0.17],[8465,332.71375,58.20111,3.35,1.57],[8466,333.03333,24.95,5.92,1.51],[8467,333.1825,-4.72083,6.39,0.5],[8468,332.45167,72.34111,4.79,0.92],[8469,332.87792,59.41444,5.04,0.25],[8470,333.435,-25.18083,5.58,0.49],[8471,334.46042,-77.51167,5.51,0.31],[8472,332.95333,56.83944,5.24,0.51],[8473,332.56375,72.11111,6.37,-0.06],[8474,332.66208,70.13278,5.5,0.38],[8475,333.19917,34.60472,5.33,1.13],[8476,332.98708,59.08472,6.3,1.13],[8477,333.66083,-41.38167,6.23,0.65],[8478,333.57833,-27.76694,5.43,-0.16],[8479,333.00792,60.75944,5.35,1.17],[8480,333.575,-21.07417,5.32,0.8],[8481,335.00625,-80.43972,5.1,1.47],[8482,333.41083,28.60833,5.89,1.15],[8483,333.09292,63.29139,5.79,1.67],[8484,333.89625,-44.45194,6.1,1.02],[8485,333.46958,39.715,4.49,1.39],[8486,333.90375,-41.34667,4.79,0.8],[8487,333.45542,45.44083,5.53,0.04],[8488,334.11083,-41.6275,5.1,0.92],[8489,333.685,42.95389,5.71,0.0],[8490,333.45625,63.1625,6.11,-0.08],[8491,333.99917,8.54944,6.21,0.02],[8492,334.15583,-25.89833,6.15,1.11],[8493,333.22042,73.30722,6.08,1.01],[8494,333.75833,57.04361,4.19,0.28],[8495,334.14,-1.59639,6.15,0.19],[8496,334.20042,-12.83139,5.34,1.14],[8497,334.24917,-23.14,6.17,1.05],[8498,333.9925,37.74889,4.13,1.46],[8499,334.20833,-7.78333,4.16,0.98],[8500,334.21917,-9.04,5.79,1.16],[8501,334.565,-53.62778,5.37,0.6],[8502,334.62542,-60.25972,2.86,1.39],[8503,334.12375,27.80417,6.37,0.99],[8504,334.27708,-5.38722,5.75,0.88],[8505,337.90583,-85.96722,5.77,1.02],[8506,334.11042,57.22028,5.88,0.93],[8507,334.51792,-0.23778,6.39,0.44],[8508,334.75333,-13.305,5.95,1.06],[8509,335.15083,-57.51,6.34,1.37],[8510,334.73417,37.76944,6.17,0.28],[8511,334.55292,62.80444,5.75,1.26],[8512,335.04958,-7.82111,5.37,-0.06],[8513,335.115,5.78944,5.37,-0.02],[8514,335.2325,8.18667,6.17,0.44],[8515,336.15333,-72.25556,5.29,0.65],[8516,335.39833,-21.59833,5.13,1.07],[8517,335.25042,26.93528,6.47,1.59],[8518,335.41417,-1.38722,3.84,-0.05],[8519,335.165,50.98083,6.42,0.6],[8520,335.37958,12.20528,5.01,-0.13],[8521,335.68292,-45.94778,6.62,2.01],[8522,335.33042,28.33056,4.81,0.0],[8523,335.25667,46.53667,4.57,-0.1],[8524,335.78333,-45.92861,5.62,0.36],[8525,334.585,76.48806,6.66,0.0],[8526,336.4625,-75.01556,6.04,0.64],[8527,336.29375,-70.43167,5.78,0.38],[8528,335.46208,42.07833,6.41,-0.08],[8529,335.87875,-24.7625,5.53,0.99],[8530,335.88375,-7.19444,5.93,1.0],[8531,336.235,-57.79722,5.32,0.67],[8532,335.915,20.84833,6.04,0.6],[8533,336.02875,-4.83694,5.78,-0.04],[8534,336.11292,-13.52944,5.76,0.97],[8535,335.75083,57.28444,6.16,-0.13],[8536,335.97583,38.57361,6.22,0.49],[8537,335.75083,62.42,6.04,0.05],[8538,335.89,52.22917,4.43,1.02],[8539,336.31917,1.3775,4.66,-0.03],[8540,336.83333,-64.96639,4.48,-0.03],[8541,336.12917,49.47639,4.57,0.09],[8542,336.54458,-23.6825,6.29,-0.03],[8543,336.41958,18.44444,6.26,1.22],[8544,336.6425,-16.74139,6.57,0.6],[8545,336.64333,-16.7425,6.35,0.61],[8546,333.29417,86.10806,5.27,-0.03],[8547,337.15708,-67.48917,5.55,0.2],[8548,336.65583,4.39361,5.75,0.52],[8549,336.69042,37.44389,6.46,-0.13],[8550,335.92208,78.24333,6.76,-0.06],[8551,336.96458,4.69556,4.79,1.05],[8552,337.16333,-39.13194,5.47,0.95],[8553,336.86042,39.80972,6.14,-0.14],[8554,336.74667,56.43333,6.57,-0.1],[8555,336.9425,31.84028,5.98,1.45],[8556,337.3175,-43.49556,3.97,1.03],[8557,336.50333,70.77083,5.47,1.2],[8558,337.20708,-0.02028,4.59,0.6],[8559,337.20875,-0.02,4.42,0.38],[8560,337.43958,-43.74944,4.11,1.57],[8561,336.77208,65.13222,5.46,0.37],[8562,337.28333,9.12889,5.58,1.55],[8563,337.44167,-27.10722,5.95,0.33],[8564,337.2925,26.76306,5.79,1.25],[8565,337.50625,-12.915,6.4,0.32],[8566,337.49167,4.43167,5.48,0.38],[8567,337.5725,-14.58583,6.37,-0.04],[8568,337.08208,64.08556,6.29,1.08],[8569,337.43292,35.72556,6.56,0.6],[8570,337.72375,-26.07361,6.43,1.08],[8571,337.29292,58.41528,3.75,0.6],[8572,337.3825,47.70694,4.36,1.68],[8573,337.66167,-10.67806,4.82,-0.06],[8574,337.5075,32.5725,5.65,-0.04],[8575,337.52708,49.35611,6.4,1.15],[8576,337.87625,-32.34611,4.29,0.01],[8577,338.86,-78.77167,6.15,1.38],[8578,336.67708,78.78583,5.83,0.15],[8579,337.62208,43.12333,4.51,-0.09],[8580,337.82667,-2.91111,6.16,1.08],[8581,337.82667,-6.555,6.14,0.56],[8582,338.25042,-61.98222,4.81,1.61],[8583,337.92208,-10.90556,6.38,0.28],[8584,337.8925,29.54278,6.35,0.19],[8585,337.82292,50.2825,3.77,0.01],[8586,338.14792,20.23,6.42,0.32],[8587,338.19542,15.86333,6.32,1.19],[8588,338.11,39.77972,5.88,0.16],[8589,338.07833,54.0375,6.35,0.6],[8590,338.51208,-1.57417,5.89,0.99],[8591,337.47042,78.82417,5.5,0.06],[8592,338.67333,-20.70833,5.2,0.44],[8593,338.97042,-57.88361,6.23,0.2],[8594,338.41917,56.625,5.71,0.96],[8595,338.26208,69.91361,6.6,0.32],[8596,338.90208,-23.99111,5.97,0.99],[8597,338.83917,-0.1175,4.02,-0.09],[8598,338.32083,70.37389,6.34,0.0],[8599,338.0675,76.22639,5.68,0.05],[8600,339.12208,-40.58278,6.28,0.12],[8601,339.1475,-31.66389,5.82,1.09],[8602,339.245,-40.59111,5.86,0.06],[8603,338.96792,39.63417,5.73,-0.15],[8604,339.03292,35.57722,6.1,1.0],[8605,339.15167,11.69694,6.4,0.0],[8606,338.9725,50.07111,6.29,-0.05],[8607,338.96583,56.07,6.38,0.12],[8608,339.26958,12.57722,6.3,0.6],[8609,339.20292,35.6525,6.3,1.37],[8610,339.43917,-4.22806,5.03,1.14],[8611,339.785,-52.69222,6.65,0.35],[8612,339.5925,-7.89778,6.23,0.78],[8613,339.34333,51.54528,4.63,0.24],[8614,339.68625,-28.74778,6.47,1.04],[8615,338.94208,73.64306,5.08,0.39],[8616,339.71458,-33.08139,5.66,0.05],[8617,339.57292,45.18306,6.4,0.6],[8618,339.71917,19.52222,5.82,0.92],[8619,339.93333,-28.32528,6.31,1.01],[8620,340.20375,-57.42222,5.97,1.46],[8621,339.65792,56.79583,5.21,1.58],[8622,339.81542,39.05028,4.88,-0.2],[8623,340.09292,-30.65889,5.87,1.3],[8624,339.94583,19.68111,6.21,0.0],[8625,339.30417,75.37167,5.79,1.57],[8626,339.89292,37.59278,6.03,0.86],[8627,339.6625,63.58444,5.19,0.06],[8628,340.16417,-27.04361,4.17,-0.11],[8629,340.2,-3.55417,6.31,0.52],[8630,341.51375,-81.38167,4.15,0.2],[8631,340.21958,14.54944,5.71,0.72],[8632,340.12875,44.27639,4.46,1.33],[8633,340.07667,53.84611,5.93,0.93],[8634,340.36542,10.83139,3.4,-0.09],[8635,340.65375,-47.21056,5.98,0.58],[8636,340.66708,-46.88472,2.1,1.6],[8637,340.59208,-29.36083,6.17,1.54],[8638,340.38083,30.96583,6.34,0.6],[8639,340.67958,-44.24778,6.07,0.98],[8640,340.36917,40.22556,5.25,-0.14],[8641,340.43917,29.3075,4.79,-0.01],[8642,340.48917,14.51639,5.9,1.11],[8643,340.40042,41.54944,5.94,0.6],[8644,340.875,-41.41444,4.85,1.03],[8645,340.76458,-8.31167,6.45,0.17],[8646,341.06875,-60.49944,6.3,0.56],[8647,340.80958,-6.96278,6.41,-0.04],[8648,340.58667,53.90889,6.12,1.62],[8649,340.89708,-18.83028,4.69,1.37],[8650,340.75042,30.22139,2.94,0.86],[8651,340.73125,37.80278,6.43,-0.09],[8652,340.76875,47.16861,6.39,0.47],[8653,340.92792,10.93917,6.51,0.46],[8654,341.02167,39.46556,5.95,1.48],[8655,341.40792,-53.50028,4.85,1.18],[8656,341.02292,41.81917,5.08,0.96],[8657,341.41958,-46.5475,5.51,1.32],[8658,341.53333,-48.97889,6.62,0.63],[8659,341.61792,-49.68583,6.48,1.14],[8660,341.3675,19.36667,6.25,1.05],[8661,341.205,52.51722,6.55,0.6],[8662,341.68208,-46.93944,6.56,0.3],[8663,342.59542,-80.12417,5.35,-0.15],[8664,342.42042,-77.05056,6.73,0.11],[8665,341.67333,12.17278,4.19,0.5],[8666,341.5425,44.54611,5.76,0.36],[8667,341.63292,23.56556,3.95,1.07],[8668,341.82958,-34.16111,6.28,1.16],[8669,342.08917,-61.68417,6.37,1.06],[8670,341.88792,-19.61333,5.26,0.94],[8671,341.94625,-38.22194,6.71,0.48],[8672,342.3225,-70.34778,6.34,0.07],[8673,341.92833,-14.05639,5.66,-0.05],[8674,341.98417,-25.91194,6.3,0.91],[8675,342.13875,-51.31694,3.49,0.08],[8676,342.12583,-10.55556,6.19,0.28],[8677,341.84667,58.48278,6.36,-0.04],[8678,342.04542,37.41667,5.9,1.03],[8679,342.39792,-13.5925,4.01,1.57],[8680,342.49625,-32.80528,6.33,0.31],[8681,342.38458,10.47889,6.54,0.29],[8682,342.19917,54.415,6.12,-0.07],[8683,342.18417,62.93833,6.06,1.2],[8684,342.50083,24.60167,3.48,0.93],[8685,342.75917,-39.15694,5.42,1.43],[8686,342.93708,-59.88139,6.46,1.13],[8687,342.25292,68.57028,6.19,0.6],[8688,342.4425,55.90278,5.43,1.17],[8689,343.04125,-63.18861,6.12,1.03],[8690,342.59083,41.95361,5.92,0.08],[8691,342.66292,19.14083,6.4,0.6],[8692,342.5425,50.67694,6.21,1.14],[8693,342.83708,-29.53611,5.97,0.91],[8694,342.42,66.20056,3.52,1.05],[8745,344.7875,59.81472,6.43,0.03],[8695,343.13167,-32.87556,4.46,-0.04],[8696,342.845,61.69694,5.6,0.78],[8697,343.10042,9.83556,5.16,0.48],[8698,343.15375,-7.57972,3.74,1.64],[8699,343.00833,43.3125,4.94,1.56],[8700,343.40792,-48.59806,6.04,0.62],[8701,343.66417,-70.07361,6.05,0.66],[8702,341.87083,83.15389,4.74,1.26],[8703,343.25958,16.84111,5.64,1.12],[8704,343.36958,-11.61667,5.8,-0.08],[8705,343.21792,50.41194,6.46,-0.04],[8706,343.29708,40.16722,6.34,-0.08],[8707,343.26583,60.10111,6.01,1.75],[8708,343.41708,44.74917,5.81,0.26],[8709,343.6625,-15.82083,3.27,0.05],[8710,343.64208,-7.20472,6.19,1.28],[8711,343.68958,-16.27194,5.56,1.14],[8712,343.52917,40.37694,5.81,1.13],[8713,343.81208,-36.38861,6.4,1.31],[8714,343.64875,16.94167,6.12,1.77],[8715,343.74792,1.06472,6.11,0.2],[8716,343.79583,-4.98778,5.72,0.88],[8717,343.80708,8.81583,4.9,0.0],[8718,343.76083,37.07694,5.91,0.42],[8719,343.96417,-31.63306,6.1,1.36],[8720,343.98708,-32.53972,4.21,0.97],[8721,344.1,-31.56556,6.48,1.1],[8722,344.19917,-47.96917,5.7,0.22],[8723,343.93542,36.35167,5.74,-0.05],[8724,344.21458,11.84833,6.51,0.18],[8725,344.09833,41.60389,5.59,-0.14],[8726,344.10833,49.73361,4.95,1.78],[8727,344.32167,-4.81,6.31,0.94],[8728,344.41292,-29.62222,1.16,0.09],[8729,344.36625,20.76889,5.49,0.67],[8730,344.38667,3.81028,6.28,1.12],[8731,344.26875,48.68417,5.43,-0.09],[8732,344.64583,-35.52306,6.13,0.58],[8733,344.41958,39.30889,6.18,-0.15],[8734,344.56458,-2.39528,6.16,0.74],[8735,344.59875,-1.41028,6.37,0.35],[8736,342.75917,85.37361,5.9,1.32],[8737,344.64625,9.35694,6.43,0.64],[8738,344.6775,7.33972,6.33,0.06],[8739,344.79917,11.72889,5.75,0.32],[8740,344.89917,-29.46222,5.51,0.25],[8741,344.89875,-13.07083,6.07,1.45],[8742,344.86417,0.96278,5.43,0.98],[8743,345.02417,-25.16417,5.65,1.25],[8744,344.79292,52.65444,6.29,1.42],[8746,345.1025,-25.62667,6.29,1.13],[8747,345.22,-52.75417,4.12,0.98],[8748,343.60333,84.34611,4.71,1.43],[8749,345.28125,-50.95,5.68,1.42],[8750,345.15792,0.18583,6.21,0.89],[8751,345.17875,3.01167,5.83,1.34],[8752,345.02125,56.94528,5.0,1.42],[8753,345.17708,31.08306,6.6,-0.04],[8754,345.33083,-28.85361,5.55,1.35],[8755,345.14333,45.375,6.5,0.6],[8756,345.34583,-22.79083,6.28,0.13],[8757,345.34833,-7.06111,6.21,1.41],[8758,345.22792,38.70806,6.54,-0.11],[8759,345.38208,-4.71139,5.94,1.0],[8760,345.64167,-36.42083,6.47,0.94],[8761,345.37792,57.10556,6.2,1.5],[8762,345.48042,42.32611,3.62,-0.09],[8763,345.63583,-6.57417,6.15,1.58],[8764,345.68458,-20.87056,5.97,0.94],[8765,345.63792,31.78056,6.57,0.35],[8766,345.65125,42.75778,5.1,0.09],[8767,345.87417,-34.74944,5.11,0.29],[8768,345.68833,44.05889,6.39,0.0],[8769,346.2175,-68.82028,5.52,0.36],[8770,345.6825,55.23639,6.5,-0.08],[8771,345.99792,-41.47833,5.79,1.08],[8772,345.98875,-4.79528,6.68,0.58],[8773,345.96917,3.82,4.53,-0.12],[8774,346.165,-53.965,5.37,1.45],[8775,345.94375,28.08278,2.42,1.67],[8776,346.00417,6.61667,6.41,0.39],[8777,345.84875,60.44528,6.74,-0.02],[8778,345.84,58.56472,6.43,0.9],[8779,345.88708,67.20917,5.24,1.26],[8780,346.04583,50.05222,4.65,1.06],[8781,346.19042,15.20528,2.49,-0.04],[8782,346.29083,-7.69361,5.43,0.3],[8783,346.30333,-17.07917,6.14,1.37],[8784,346.27625,16.56306,6.44,0.83],[8785,346.32333,1.30694,6.39,0.94],[8786,347.09875,-79.48083,6.12,0.14],[8787,346.72,-43.52056,4.28,0.42],[8788,346.57583,18.5175,6.13,0.44],[8789,346.67042,-23.74306,4.47,0.9],[8790,346.72333,-38.89222,5.61,0.01],[8791,346.78958,-49.60667,6.33,1.45],[8792,346.63292,19.91083,6.3,0.49],[8793,346.81125,-50.68639,5.83,0.48],[8794,347.14875,-73.58639,6.15,1.42],[8795,346.75125,9.40944,4.52,1.57],[8847,349.2075,-41.19444,6.47,1.08],[8796,346.77833,25.46833,4.76,1.34],[8797,346.65375,59.41972,4.85,-0.03],[8798,346.86542,32.82583,6.02,0.12],[8799,346.86958,21.13417,5.99,0.26],[8800,346.82542,46.06806,6.66,-0.05],[8801,346.79208,52.81639,6.11,1.05],[8802,347.0875,-28.82333,5.6,0.88],[8803,346.79333,59.7275,6.4,-0.01],[8804,346.91375,46.38722,5.33,1.41],[8805,346.93917,49.29583,5.7,0.44],[8806,347.05125,44.56167,6.56,0.17],[8807,347.17042,2.12778,5.4,0.91],[8808,346.94875,63.63333,6.26,-0.02],[8809,347.54875,-66.8575,6.47,0.95],[8810,348.05,-80.91278,6.41,1.5],[8811,346.98833,64.2225,6.21,1.1],[8812,347.36167,-21.1725,3.66,1.22],[8813,347.43583,-28.08861,5.87,1.31],[8814,347.48875,-42.86056,5.81,0.48],[8815,347.38125,8.67722,5.12,1.47],[8816,347.45667,-14.51056,6.42,0.01],[8817,347.47833,-22.4575,4.69,0.65],[8818,347.54083,-40.59167,5.83,1.62],[8819,346.97458,75.3875,4.41,0.8],[8820,347.59,-45.24667,3.9,1.02],[8821,347.50625,9.82194,5.39,-0.08],[8822,347.43375,59.33306,5.7,0.33],[8823,347.69417,-29.525,6.51,0.27],[8824,347.6775,17.59444,5.71,1.34],[8825,347.61333,43.54417,5.94,0.44],[8826,347.93417,8.72,5.16,0.13],[8827,347.955,26.84722,6.17,0.94],[8828,348.3125,-49.61889,6.8,0.92],[8829,348.5275,-62.7,6.12,0.79],[8830,348.1375,49.40639,4.52,0.29],[8831,348.26667,29.44167,6.35,0.93],[8832,348.32083,57.16833,5.56,1.01],[8833,348.36042,11.065,5.82,1.01],[8834,348.58083,-6.04889,4.22,1.56],[8835,348.74417,-41.10556,5.77,1.18],[8836,348.6675,-10.68861,6.12,1.49],[8837,348.55958,50.61778,6.31,0.6],[8838,348.59042,29.77167,6.41,0.45],[8839,348.65208,24.10306,6.36,0.6],[8840,348.89292,-3.49639,5.55,0.06],[8841,348.97292,-9.08778,4.21,1.11],[8842,348.94292,28.24778,6.49,0.85],[8843,349.24042,-62.00111,5.66,0.51],[8844,348.65542,74.23111,5.84,0.6],[8845,348.99125,24.77111,6.6,0.4],[8846,349.16542,-44.48917,5.92,1.06],[8848,349.3575,-58.23583,3.99,0.4],[8849,349.78375,-79.47278,6.33,0.91],[8850,349.21208,-7.72667,5.06,1.6],[8851,348.9075,70.88806,5.56,0.24],[8852,349.29125,3.28222,3.69,0.92],[8853,349.17625,53.21361,5.54,0.52],[8854,349.11167,61.96306,6.53,0.23],[8855,349.58333,-67.47111,6.13,1.35],[8856,349.41667,-11.71306,6.34,0.05],[8857,349.31917,45.16417,6.43,1.07],[8858,349.47583,-9.1825,4.39,-0.15],[8859,349.54125,-40.82444,5.53,0.44],[8860,349.43625,49.01528,4.85,1.67],[8861,349.48375,45.48889,6.48,-0.03],[8862,352.01542,-87.48222,5.49,1.27],[8863,349.70583,-32.53194,4.41,1.13],[8864,349.5975,41.77361,6.02,0.6],[8865,349.74042,-9.61083,4.98,-0.02],[8866,349.77792,-13.45889,5.08,0.8],[8867,349.32875,75.29917,6.38,0.03],[8868,349.85,-5.12444,5.55,0.39],[8869,349.85042,-18.07528,5.93,1.52],[8870,349.76,45.13722,6.5,0.6],[8871,349.93,-33.70806,6.37,1.3],[8872,349.65625,68.11167,4.75,0.84],[8873,349.86417,34.79333,6.32,-0.08],[8874,349.87417,48.62528,5.44,1.03],[8875,349.92333,48.38083,6.32,1.12],[8876,349.96833,42.07806,5.79,1.48],[8877,350.20833,-50.30667,6.05,0.42],[8878,350.08583,5.38139,5.05,1.2],[8879,350.17042,-5.90806,6.17,1.07],[8880,350.15917,23.74028,4.6,0.17],[8881,350.05958,61.97,6.45,0.6],[8882,350.20667,30.415,5.59,1.5],[8883,350.31458,-26.98667,5.64,0.82],[8884,350.18333,44.11639,6.13,0.17],[8885,350.22208,38.18222,5.77,0.46],[8886,350.14417,62.21306,6.39,1.61],[8887,350.47875,31.8125,5.32,-0.11],[8888,350.4925,26.60889,6.62,0.37],[8889,350.73708,-60.05583,6.09,1.6],[8890,350.66333,-15.03917,5.2,0.2],[8891,350.66875,20.82861,6.29,-0.04],[8892,350.7425,-20.10056,3.97,1.1],[8893,350.76917,12.31389,5.08,1.31],[8894,350.63542,60.13361,5.56,1.68],[8895,350.97625,-53.80833,6.15,0.26],[8896,350.93917,-43.12444,6.1,1.46],[8897,350.88292,0.29139,6.31,1.61],[8898,351.05458,-51.89139,5.75,1.59],[8899,350.94792,32.53139,6.69,0.45],[8900,351.0325,-18.6875,6.19,1.02],[8901,351.33083,-56.84917,5.59,1.07],[8902,351.14583,41.11278,6.72,0.06],[8903,351.21167,32.385,5.57,-0.11],[8904,351.20958,62.28278,4.98,1.68],[8905,351.345,23.40417,4.4,0.61],[8906,351.51167,-20.64194,4.39,1.47],[8907,351.6525,-52.72167,5.52,0.4],[8908,351.78,-66.58111,6.45,1.47],[8909,351.8125,-58.47611,5.63,0.98],[8910,351.78792,-50.15722,6.2,-0.08],[8911,351.73333,1.25556,4.94,0.03],[8912,351.81167,1.1225,6.25,1.02],[8913,351.78083,42.91194,5.75,0.0],[8914,352.00292,-35.54444,6.32,1.2],[8915,351.91833,25.16722,5.98,-0.06],[8916,351.99208,6.37889,4.28,1.07],[8917,352.02167,-11.44972,6.37,0.9],[8918,351.81917,70.35972,5.6,0.19],[8919,352.25417,-63.11083,5.68,-0.17],[8920,352.25333,-44.49778,6.43,1.18],[8921,352.2525,-9.26611,6.18,1.44],[8922,352.27333,23.04778,6.35,0.6],[8923,352.28875,12.76056,4.55,0.94],[8924,352.38375,-4.53278,6.25,1.09],[8925,352.53083,49.13306,6.17,1.46],[8926,352.50833,58.54889,4.91,-0.12],[8927,352.66542,38.66194,6.05,0.99],[8928,352.75458,-6.28833,6.39,1.26],[8929,352.8625,-44.84361,6.02,1.02],[8930,352.8225,39.23639,5.22,1.02],[8931,352.88125,-4.08722,6.49,0.54],[8932,352.92542,-21.36944,6.29,0.33],[8933,352.92958,28.40361,6.41,0.01],[8934,352.99,-1.08583,6.38,1.18],[8935,353.33125,-77.38528,5.81,0.68],[8936,353.1025,34.9525,6.65,-0.06],[8937,353.24292,-37.81833,4.37,-0.09],[8938,351.75333,87.3075,5.58,0.23],[8939,353.31917,-20.91444,4.71,0.02],[8940,353.36708,22.49889,5.32,1.6],[8941,353.42792,45.05806,6.24,0.98],[8942,353.48125,20.84083,6.06,1.73],[8943,353.48833,31.32528,4.98,1.38],[8944,353.5375,-1.2475,5.87,0.3],[8945,353.80292,-64.68944,7.4,0.6],[8946,353.70583,-15.24583,5.96,1.36],[8947,353.65625,40.23639,5.59,0.1],[8948,353.65917,33.49722,5.63,1.03],[8949,353.76917,-42.615,4.71,0.08],[8950,353.69458,38.02389,6.18,1.58],[8951,353.88375,-7.46444,6.39,0.88],[8952,353.74583,71.64222,5.84,1.8],[8953,353.98292,24.56111,6.45,1.73],[8954,354.09708,2.10222,5.68,0.44],[8955,354.12708,32.90417,6.35,0.46],[8956,354.2725,-31.87083,6.52,1.26],[8957,354.60042,-76.87,6.0,0.9],[8958,354.415,-13.06028,5.65,1.03],[8959,354.4625,-45.4925,4.74,0.08],[8960,354.41583,16.82556,6.26,0.0],[8961,354.39125,46.45806,3.82,1.01],[8962,354.38333,44.42917,5.8,-0.06],[8963,354.48667,18.40056,5.53,0.0],[8964,354.49417,46.19972,6.58,0.66],[8965,354.53417,43.26806,4.29,-0.1],[8966,354.86667,-46.63778,6.09,0.24],[8967,354.78458,50.47167,5.3,-0.06],[8968,354.94625,-14.22167,5.0,0.24],[8969,354.9875,5.62639,4.13,0.51],[8970,354.97958,9.67722,5.97,0.2],[8971,354.79333,75.29278,5.95,0.13],[8972,354.83792,74.00278,5.98,0.89],[8973,355.01167,37.6525,6.53,0.35],[8974,354.83667,77.6325,3.21,1.03],[8975,355.15917,-32.07306,5.31,0.97],[8976,355.10208,44.33389,4.14,-0.08],[8977,355.16917,36.72083,6.23,0.39],[8978,355.27917,-24.16028,6.6,1.57],[8979,355.28708,-11.68056,5.89,1.0],[8980,355.39375,-18.02722,5.34,1.57],[8981,355.36208,49.51222,6.26,0.19],[8982,355.44083,-17.81639,4.82,0.82],[8983,355.48625,7.25056,5.89,0.1],[8984,355.51167,1.78,4.5,0.2],[8985,355.47708,57.26,6.24,0.6],[8986,355.56167,44.99194,6.57,0.6],[8987,355.61625,-15.44778,5.28,1.37],[8988,355.68042,-14.545,4.49,-0.04],[8989,355.58667,64.51556,6.56,1.88],[8990,355.63125,61.67944,6.4,1.24],[8991,355.84333,10.33139,5.06,1.68],[8992,355.95625,-15.28444,6.36,1.58],[8993,356.00583,-45.08333,6.09,0.98],[8994,356.10583,-70.49028,6.07,0.91],[8995,356.16958,-78.79139,5.75,1.11],[8996,356.05,-64.40444,5.72,1.4],[8997,355.99792,29.36167,4.93,0.95],[8998,356.05042,-18.27694,5.24,-0.08],[8999,356.12042,-26.24639,6.17,0.5],[9000,356.20125,55.79972,6.51,0.6],[9001,356.505,-40.1825,6.31,0.2],[9002,356.50375,-18.67806,5.29,0.28],[9003,356.50875,46.42028,4.95,1.11],[9004,356.59792,3.48667,5.04,2.6],[9005,356.65292,66.78222,5.95,-0.04],[9006,356.81667,-50.22667,5.18,-0.19],[9007,356.84708,-68.39417,6.89,0.46],[9008,356.76458,58.65194,4.87,1.11],[9009,356.81625,-11.91083,5.73,1.08],[9010,356.75792,57.45139,5.51,1.65],[9011,356.88833,46.8325,6.07,-0.14],[9012,356.98542,-2.76167,5.49,0.94],[9013,356.97833,67.80694,5.04,-0.01],[9014,357.13542,-6.38056,6.07,1.45],[9015,357.20542,2.21417,6.46,0.44],[9016,357.23167,-28.13028,4.57,0.01],[9017,357.1625,64.87639,6.41,0.06],[9018,357.20917,62.21444,5.43,0.67],[9019,357.22458,59.97889,6.34,-0.01],[9020,357.3,58.96306,6.33,0.4],[9021,357.38167,-15.86111,6.24,1.22],[9022,357.36458,1.07611,5.77,0.16],[9023,357.43625,-62.83944,6.59,1.48],[9024,357.42083,36.42528,5.9,0.79],[9025,357.41417,28.8425,5.97,0.19],[9026,357.45667,-25.33139,6.42,0.12],[9027,357.56125,-9.97417,5.94,1.13],[9028,357.59292,51.62167,6.44,0.37],[9029,357.63875,-14.40194,5.72,1.51],[9030,357.83833,9.31333,5.79,1.66],[9031,357.83875,-18.90889,5.18,-0.14],[9032,358.02667,-82.01889,5.11,0.92],[9033,357.99125,2.93028,5.55,1.53],[9034,357.99,77.59944,6.55,0.44],[9035,358.0975,21.67083,6.11,1.59],[9036,358.12208,19.12028,5.08,1.6],[9037,358.125,-14.25111,5.87,1.28],[9038,358.10458,75.54472,6.39,0.98],[9039,358.15458,10.9475,5.3,0.18],[9040,358.21042,-8.99667,5.75,1.17],[9041,358.23167,-3.15556,5.93,1.07],[9042,358.27,2.09056,6.28,-0.01],[9043,358.33667,-24.22917,6.24,0.19],[9044,358.58917,-27.04222,6.35,0.2],[9045,358.59583,57.49944,4.54,1.22],[9046,358.66083,-40.3,6.03,0.57],[9047,358.69417,0.10917,5.61,1.59],[9048,358.7825,7.07111,6.21,-0.07],[9049,358.81917,-31.92167,6.1,-0.08],[9050,358.81917,-31.88417,6.83,-0.08],[9051,358.84583,25.955,6.54,0.6],[9052,358.89042,57.41222,6.0,0.21],[9053,358.89,47.35583,6.0,1.16],[9054,359.12458,-24.73722,6.31,0.92],[9055,359.17292,22.64806,6.15,1.6],[9056,359.11542,83.19111,6.59,0.05],[9057,359.265,42.65833,5.97,0.69],[9058,359.28458,-26.62361,6.26,1.45],[9059,359.28542,55.70583,5.55,0.49],[9060,359.33292,-62.95639,5.97,0.11],[9061,359.38625,-82.17,5.73,1.05],[9062,359.39667,-64.29833,5.0,0.06],[9063,359.38958,60.02361,6.47,0.01],[9064,359.43958,25.14139,4.66,1.59],[9065,359.58833,-15.8475,6.26,1.08],[9066,359.60333,51.38861,4.8,1.83],[9067,359.66833,-3.55611,4.86,0.93],[9068,359.705,32.38167,6.52,-0.11],[9069,359.7325,-52.74583,5.13,1.13],[9070,359.69375,46.41306,6.54,-0.09],[9071,359.75208,55.755,4.88,-0.07],[9072,359.82792,6.86333,4.01,0.42],[9073,359.86625,-29.485,5.62,1.6],[9074,359.87167,33.72444,6.58,0.55],[9075,359.87167,33.72444,6.58,0.6],[9076,359.97917,-65.57722,4.5,-0.08],[9077,0.08,-44.29056,6.29,0.76],[9078,0.09958,26.91833,6.46,0.5],[9079,0.12875,59.55972,6.19,1.01],[9080,0.18208,45.25333,6.38,-0.07],[9081,0.26875,-48.81,5.71,0.91],[9082,0.33333,-50.33722,5.53,1.6],[9083,0.33042,49.98167,6.22,0.97],[9084,0.39875,-77.06583,4.78,1.27],[9085,0.40375,61.22306,5.55,0.41],[9086,0.4325,42.36722,6.25,-0.03],[9087,0.45583,-3.0275,5.1,-0.12],[9088,0.5425,27.08194,5.75,0.67],[9089,0.49,-6.01417,4.41,1.63],[9090,0.53042,-14.67611,7.1,1.8],[9091,0.58292,-29.72028,5.01,-0.15],[9092,0.60083,8.95694,6.32,0.18],[9093,0.62375,8.48556,5.63,0.29],[9094,0.65042,66.09889,5.86,1.09],[9095,0.74,-20.04611,6.25,0.53],[9096,0.78208,-24.14528,6.44,1.18],[9097,0.85708,63.64194,6.24,0.33],[9098,0.935,-17.33611,4.55,-0.05],[9099,0.96625,66.71222,6.29,1.67],[9100,1.05667,62.28778,5.88,0.3],[9101,1.08208,-16.52889,5.78,1.1],[9102,1.08458,-29.26861,6.4,0.0],[9103,1.12542,-10.50944,4.94,1.63],[9104,1.175,67.16667,5.67,1.07],[9105,1.15292,42.09222,6.01,0.6],[9106,1.12792,-72.89778,7.31,0.44],[9107,1.22417,34.65972,6.12,0.62],[9108,1.17208,-71.43694,5.59,-0.12],[9109,1.23333,26.64889,6.25,1.4],[9110,1.27583,61.31417,5.8,-0.09]]\ No newline at end of file | ||
Packages/MoonriseKit/Sources/SkyRenderer/Resources/constellations.json added +1
| @@ -0,0 +1 @@ | |||
| 1 | [{"id":"And","lines":[[8961,8976,8965,8762,8965,68,165,15,165,163,215,163,165,337,603,337,464,337,269,226]]},{"id":"Ant","lines":[[4273,4104,3871,3765]]},{"id":"Aps","lines":[[5470,6020,6102,6163]]},{"id":"Aql","lines":[[7602,7557,7525,7377,7235,7377,7570,7710,7570,7377,7236,7193]]},{"id":"Aqr","lines":[[8812,8841,8834,8597,8518,8698,8709,8698,8518,8499,8418,8499,8518,8414,8232]]},{"id":"Ara","lines":[[6743,6510,6461,6462,6500,6462,6285,6229,6285,6295,6510]]},{"id":"Ari","lines":[[951,888,838,617,553,546]]},{"id":"Aur","lines":[[1708,2088,2095,1791,1577,1612,1641,1605,1708]]},{"id":"Boo","lines":[[5435,5429,5340,5506,5681,5602,5435,5351,5404,5329,5351]]},{"id":"CMa","lines":[[2596,2574,2657,2596,2491,2294,2429,2580,2618,2646,2693,2827,2693,2653,2491]]},{"id":"CMi","lines":[[2943,2845]]},{"id":"CVn","lines":[[4915,4785]]},{"id":"Cae","lines":[[1443,1502,1503]]},{"id":"Cam","lines":[[1040,1035,1155,1148,1542,1603,1568]]},{"id":"Cap","lines":[[7754,7776,7936,7980,8204,8278,8322,8278,8167,8075,7776]]},{"id":"Car","lines":[[2326,2554,3117,3307,3699,4050,4140,4199,4037,3685,3890]]},{"id":"Cas","lines":[[542,403,264,168,21]]},{"id":"Cen","lines":[[4467,4522,4390,4638,4621,4743,4802,4819,5132,5267,5132,5459,5132,5231,5193,5190,5028,5190,5288,5190,5248,5440,5576]]},{"id":"Cep","lines":[[8974,8238,8162,8465,8694,8974]]},{"id":"Cet","lines":[[804,718,813,896,911,804,779,681,539,402,334,188,433,585,539]]},{"id":"Cha","lines":[[3318,3340,4231,4674,4174,3318]]},{"id":"Cir","lines":[[5670,5463,5704]]},{"id":"Cnc","lines":[[3572,3461,3249,3461,3449,3475]]},{"id":"Col","lines":[[2296,2256,2106,2040,2120,2040,1956,1743]]},{"id":"Com","lines":[[4968,4983,4737]]},{"id":"CrA","lines":[[7152,7226,7254,7259,7242,7188]]},{"id":"CrB","lines":[[5971,5947,5889,5849,5793,5747,5778]]},{"id":"Crt","lines":[[4567,4514,4405,4343,4287,4382,4405,4382,4402,4468]]},{"id":"Cru","lines":[[4730,4763],[4853,4656]]},{"id":"Crv","lines":[[4623,4630,4662,4757,4786,4630]]},{"id":"Cyg","lines":[[7417,7615,7796,7949,8115,7949,7796,7924,7796,7528,7420]]},{"id":"Del","lines":[[7852,7882,7906,7948,7928,7882]]},{"id":"Dor","lines":[[1338,1465,1674,1922,2015,1922,2102]]},{"id":"Dra","lines":[[6688,6705,6536,6554,6688,7310,7582,6927,6636,6396,6132,5986,5744,5291,4787,4434,3751]]},{"id":"Equ","lines":[[8131,8178,8123,8097]]},{"id":"Eri","lines":[[1666,1560,1520,1463,1298,1231,1136,1084,874,811,818,919,1003,1088,1173,1213,1240,1464,1393,1347,1195,1190,1106,1008,898,794,789,721,674,566,472]]},{"id":"For","lines":[[963,841,612]]},{"id":"Gem","lines":[[2134,2216,2286,2473,2343,2473,2697,2852,2890,2852,2697,2540,2697,2821,2905,2990,2905,2985,2905,2777,2650,2421,2650,2777,2763,2484]]},{"id":"Gru","lines":[[8353,8411,8556,8636,8425,8636,8675,8747,8675,8820,8787]]},{"id":"Her","lines":[[6324,6418,6695,6588,6695,6418,6220,6168,6092,6168,6220,6212,6148,6212,6324,6410,6623,6703,6779]]},{"id":"Hor","lines":[[909,934,802,778,810,868,1326,1302]]},{"id":"Hya","lines":[[3492,3482,3410,3418,3454,3492,3547,3665,3845,3748,3903,3994,4094,4232,4450,4552,5020,5287]]},{"id":"Hyi","lines":[[591,806,1208,98,591]]},{"id":"Ind","lines":[[7869,8140,8368,8387,8368,8140,8055,7986]]},{"id":"LMi","lines":[[4247,4100,3974]]},{"id":"Lac","lines":[[8498,8485,8579,8632,8579,8523,8579,8572,8541,8538,8585,8572]]},{"id":"Leo","lines":[[3873,3905,4031,4058,3975,3982,3852,3982,4359,4399,4386,4399,4359,4534,4357,4058]]},{"id":"Lep","lines":[[1654,1829,1865,1702,1865,1998,2085]]},{"id":"Lib","lines":[[5908,5787,5685,5531,5603,5685,5603,5794,5812]]},{"id":"Lup","lines":[[5987,5948,5776,5708,5646,5649,5469,5571,5695,5776,5695,5705,5883]]},{"id":"Lyn","lines":[[3705,3690,3612,3579,3275,2818,2560,2238]]},{"id":"Lyr","lines":[[7001,7056,7106,7178,7139,7056]]},{"id":"Men","lines":[[2261,1953,1629,1677]]},{"id":"Mic","lines":[[8151,8135,8039,7965]]},{"id":"Mon","lines":[[2385,2298,2506,2714,2356,2227,2356,2714,2970,3188]]},{"id":"Mus","lines":[[4844,4798,4923,4798,4773,4798,4671,4520]]},{"id":"Nor","lines":[[6115,6072,5962]]},{"id":"Oct","lines":[[8630,5339,8254,8630]]},{"id":"Oph","lines":[[6556,6299,6149,6056,6075,6175,6378,6603,6556]]},{"id":"Ori","lines":[[1948,2004,1713,1852,1790,1879,2061,2124,2199,2159,2047,2135,2199,2124,2061,1948,1903,1852,1790,1543,1552,1567,1601,1567,1552,1543,1544,1580,1638,1676]]},{"id":"Pav","lines":[[7790,7913,8181,7913,7590,6982,6582,6745,6855,7074,7665,7913]]},{"id":"Peg","lines":[[8454,8650,8775,15,8775,8781,39,15,39,8781,8634,8450,8308]]},{"id":"Per","lines":[[936,941,1017,915,1017,1122,1220,1228,1203]]},{"id":"Phe","lines":[[99,322,429,440,429,322,338,191,25,99]]},{"id":"Pic","lines":[[2550,2042,2020]]},{"id":"PsA","lines":[[8728,8628,8478,8386,8326,8305,8431,8576,8695,8720,8728]]},{"id":"Psc","lines":[[360,383,352,360,437,510,596,489,294,224,9072,8969,8916,8878,8852,8911,8984,9004,8969]]},{"id":"Pup","lines":[[3185,3045,2948,2922,2773,2451,2553,2878,3165,3185]]},{"id":"Pyx","lines":[[3438,3468,3518]]},{"id":"Ret","lines":[[1336,1355,1247,1264,1175,1336]]},{"id":"Scl","lines":[[280,9016,8863,8937]]},{"id":"Sco","lines":[[5985,6084,5953,6084,5944,6084,6134,6165,6241,6247,6271,6380,6553,6615,6580,6527,6508]]},{"id":"Sct","lines":[[7063,6973,6930,6973,6884]]},{"id":"Ser","lines":[[6446,6561,6581,6869,7142],[5867,5879,5933,5867,5788,5854,5892,5881,6446]]},{"id":"Sex","lines":[[4119,3981,3909]]},{"id":"Sge","lines":[[7635,7536,7479,7536,7488]]},{"id":"Sgr","lines":[[7348,7581,7337,7581,7623,7650,7604,7440,7234,7121,7217,7264,7340,7342,7340,7264,7217,7150,7217,7121,7039,7194,7234,7194,7039,6913,6859,6746,6859,6879,6832]]},{"id":"Tau","lines":[[1910,1457,1346,1239,1030,1239,1346,1373,1409,1791]]},{"id":"Tel","lines":[[6783,6897,6905]]},{"id":"TrA","lines":[[6217,5671,5771,5897,6217]]},{"id":"Tri","lines":[[622,664,544,622]]},{"id":"Tuc","lines":[[8540,8502,8848,9076,77,126,8848]]},{"id":"UMa","lines":[[5191,5054,4905,4660,4554,4518,4377,4375,4377,4518,4335,4069,4033,4069,4335,4518,4554,4295,4301,4660,4301,3757,3323,3888,4295,3888,3775,3594,3569]]},{"id":"UMi","lines":[[424,6789,6322,5903,5563,5735,6116,5903]]},{"id":"Vel","lines":[[3206,3485,3734,3940,4216,4167,4023,3786,3634,3477,3426,3206]]},{"id":"Vir","lines":[[5056,4963,4826,4910,5107,5056]]},{"id":"Vol","lines":[[3615,3347,3223,2803,2736,3024,3223]]},{"id":"Vul","lines":[[7306,7405,7592]]}]\ No newline at end of file | ||
Packages/MoonriseKit/Sources/SkyRenderer/SkyScene.swift added +72
| @@ -0,0 +1,72 @@ | |||
| 1 | import Foundation | ||
| 2 | import SkyEngine | ||
| 3 | |||
| 4 | public struct Star: Sendable { | ||
| 5 | public var hr: Int | ||
| 6 | public var ra: Double | ||
| 7 | public var dec: Double | ||
| 8 | public var magnitude: Double | ||
| 9 | public var colorIndex: Double | ||
| 10 | } | ||
| 11 | |||
| 12 | public struct Constellation: Sendable, Decodable { | ||
| 13 | public var id: String | ||
| 14 | /// Polylines of HR numbers. | ||
| 15 | public var lines: [[Int]] | ||
| 16 | } | ||
| 17 | |||
| 18 | public enum Catalog { | ||
| 19 | /// Yale Bright Star Catalogue, 5th ed. (VizieR V/50): HR, RA and Dec (J2000, degrees), V, B−V. | ||
| 20 | public static let stars: [Star] = { | ||
| 21 | let url = Bundle.module.url(forResource: "bsc5", withExtension: "json")! | ||
| 22 | let rows = try! JSONDecoder().decode([[Double]].self, from: Data(contentsOf: url)) | ||
| 23 | return rows.map { Star(hr: Int($0[0]), ra: $0[1], dec: $0[2], magnitude: $0[3], colorIndex: $0[4]) } | ||
| 24 | }() | ||
| 25 | |||
| 26 | public static let constellations: [Constellation] = { | ||
| 27 | let url = Bundle.module.url(forResource: "constellations", withExtension: "json")! | ||
| 28 | return try! JSONDecoder().decode([Constellation].self, from: Data(contentsOf: url)) | ||
| 29 | }() | ||
| 30 | } | ||
| 31 | |||
| 32 | public struct PlacedStar: Sendable { | ||
| 33 | public var star: Star | ||
| 34 | public var position: Horizontal | ||
| 35 | } | ||
| 36 | |||
| 37 | public struct PlacedBody: Sendable { | ||
| 38 | public var body: Body | ||
| 39 | public var position: Horizontal | ||
| 40 | public var magnitude: Double | ||
| 41 | public var phaseFraction: Double | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Everything above (and just below) the horizon for one place and time. | ||
| 45 | public struct SkyScene: Sendable { | ||
| 46 | public var date: Date | ||
| 47 | public var observer: Observer | ||
| 48 | public var stars: [PlacedStar] | ||
| 49 | public var starsByHR: [Int: Horizontal] | ||
| 50 | public var bodies: [PlacedBody] | ||
| 51 | /// Moon's elongation east of the sun, degrees (0 new, 180 full). | ||
| 52 | public var moonPhase: Double | ||
| 53 | |||
| 54 | public init(date: Date, observer: Observer, magnitudeLimit: Double = 6.5) throws { | ||
| 55 | self.date = date | ||
| 56 | self.observer = observer | ||
| 57 | let visible = Catalog.stars.filter { $0.magnitude <= magnitudeLimit } | ||
| 58 | let positions = SkyEngine.horizontal(j2000: visible.map { ($0.ra, $0.dec) }, at: date, from: observer) | ||
| 59 | stars = zip(visible, positions).map { PlacedStar(star: $0, position: $1) } | ||
| 60 | starsByHR = Dictionary(uniqueKeysWithValues: stars.map { ($0.star.hr, $0.position) }) | ||
| 61 | bodies = try ([.moon] + Body.planets).map { body in | ||
| 62 | let i = try SkyEngine.illumination(body, at: date) | ||
| 63 | return PlacedBody( | ||
| 64 | body: body, | ||
| 65 | position: try SkyEngine.horizontal(body, at: date, from: observer), | ||
| 66 | magnitude: i.magnitude, | ||
| 67 | phaseFraction: i.phaseFraction | ||
| 68 | ) | ||
| 69 | } | ||
| 70 | moonPhase = try SkyEngine.moonPhase(at: date) | ||
| 71 | } | ||
| 72 | } | ||
Packages/MoonriseKit/Sources/SkyRenderer/SkyView.swift added +173
| @@ -0,0 +1,173 @@ | |||
| 1 | import SkyEngine | ||
| 2 | import SwiftUI | ||
| 3 | |||
| 4 | /// Stereographic projection of the sky centred on a viewing direction. | ||
| 5 | public struct Projection: Sendable { | ||
| 6 | public var centerAzimuth: Double | ||
| 7 | public var centerAltitude: Double | ||
| 8 | /// Horizontal field of view in degrees. | ||
| 9 | public var fieldOfView: Double | ||
| 10 | public var size: CGSize | ||
| 11 | |||
| 12 | public init(centerAzimuth: Double, centerAltitude: Double, fieldOfView: Double, size: CGSize) { | ||
| 13 | self.centerAzimuth = centerAzimuth | ||
| 14 | self.centerAltitude = centerAltitude | ||
| 15 | self.fieldOfView = fieldOfView | ||
| 16 | self.size = size | ||
| 17 | } | ||
| 18 | |||
| 19 | /// Screen point, or nil when the direction is behind the viewer. | ||
| 20 | public func point(_ h: Horizontal) -> CGPoint? { | ||
| 21 | let r = Double.pi / 180 | ||
| 22 | let alt = h.altitude * r, alt0 = centerAltitude * r | ||
| 23 | let dAz = (h.azimuth - centerAzimuth) * r | ||
| 24 | let cosC = sin(alt0) * sin(alt) + cos(alt0) * cos(alt) * cos(dAz) | ||
| 25 | guard cosC > -0.2 else { return nil } | ||
| 26 | let k = 2 / (1 + cosC) | ||
| 27 | let x = k * cos(alt) * sin(dAz) | ||
| 28 | let y = k * (cos(alt0) * sin(alt) - sin(alt0) * cos(alt) * cos(dAz)) | ||
| 29 | let scale = Double(size.width) / (4 * tan(fieldOfView * r / 4)) | ||
| 30 | return CGPoint(x: Double(size.width) / 2 + x * scale, y: Double(size.height) / 2 - y * scale) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | public struct SkyStyle: Sendable { | ||
| 35 | public var background = Color(red: 0.043, green: 0.078, blue: 0.141) | ||
| 36 | public var ground = Color(red: 0.02, green: 0.035, blue: 0.06) | ||
| 37 | public var line = Color(red: 0.30, green: 0.36, blue: 0.45) | ||
| 38 | public var label = Color(red: 0.95, green: 0.95, blue: 0.93) | ||
| 39 | public var muted = Color(red: 0.60, green: 0.64, blue: 0.70) | ||
| 40 | public var moonLight = Color(red: 0.93, green: 0.91, blue: 0.85) | ||
| 41 | public var moonDark = Color(red: 0.16, green: 0.20, blue: 0.27) | ||
| 42 | |||
| 43 | public init() {} | ||
| 44 | |||
| 45 | /// Star tint from B−V colour index, kept pale. | ||
| 46 | func starColor(_ bv: Double) -> Color { | ||
| 47 | let t = min(max((bv + 0.3) / 2.0, 0), 1) | ||
| 48 | return Color(red: 0.80 + 0.20 * t, green: 0.86 - 0.06 * t, blue: 1.0 - 0.32 * t) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public struct SkyView: View { | ||
| 53 | public var scene: SkyScene | ||
| 54 | public var centerAzimuth: Double | ||
| 55 | public var centerAltitude: Double | ||
| 56 | public var fieldOfView: Double | ||
| 57 | public var style = SkyStyle() | ||
| 58 | |||
| 59 | public init(scene: SkyScene, centerAzimuth: Double = 180, centerAltitude: Double = 55, fieldOfView: Double = 110) { | ||
| 60 | self.scene = scene | ||
| 61 | self.centerAzimuth = centerAzimuth | ||
| 62 | self.centerAltitude = centerAltitude | ||
| 63 | self.fieldOfView = fieldOfView | ||
| 64 | } | ||
| 65 | |||
| 66 | public var body: some View { | ||
| 67 | Canvas { context, size in | ||
| 68 | let p = Projection(centerAzimuth: centerAzimuth, centerAltitude: centerAltitude, fieldOfView: fieldOfView, size: size) | ||
| 69 | context.fill(Path(CGRect(origin: .zero, size: size)), with: .color(style.background)) | ||
| 70 | drawConstellations(&context, p) | ||
| 71 | drawStars(&context, p) | ||
| 72 | drawBodies(&context, p) | ||
| 73 | drawHorizon(&context, p, size: size) | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | func drawConstellations(_ context: inout GraphicsContext, _ p: Projection) { | ||
| 78 | var path = Path() | ||
| 79 | for c in Catalog.constellations { | ||
| 80 | for line in c.lines { | ||
| 81 | for (a, b) in zip(line, line.dropFirst()) { | ||
| 82 | guard let ha = scene.starsByHR[a], let hb = scene.starsByHR[b], | ||
| 83 | ha.altitude > -5 || hb.altitude > -5, | ||
| 84 | let pa = p.point(ha), let pb = p.point(hb) else { continue } | ||
| 85 | path.move(to: pa) | ||
| 86 | path.addLine(to: pb) | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | context.stroke(path, with: .color(style.line), lineWidth: 0.6) | ||
| 91 | } | ||
| 92 | |||
| 93 | func drawStars(_ context: inout GraphicsContext, _ p: Projection) { | ||
| 94 | for s in scene.stars where s.position.altitude > 0 { | ||
| 95 | guard let pt = p.point(s.position) else { continue } | ||
| 96 | let r = max(0.3, 2.6 - 0.42 * s.star.magnitude) | ||
| 97 | let opacity = min(1, max(0.25, 1.15 - 0.13 * s.star.magnitude)) | ||
| 98 | context.fill(Path(ellipseIn: CGRect(x: pt.x - r, y: pt.y - r, width: 2 * r, height: 2 * r)), with: .color(style.starColor(s.star.colorIndex).opacity(opacity))) | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | func drawBodies(_ context: inout GraphicsContext, _ p: Projection) { | ||
| 103 | // Naked-eye bodies only; Neptune (about mag 7.8) is left out. | ||
| 104 | for b in scene.bodies where b.position.altitude > 0 && b.magnitude <= 6 { | ||
| 105 | guard let pt = p.point(b.position) else { continue } | ||
| 106 | if b.body == .moon { | ||
| 107 | drawMoon(&context, at: pt, radius: 14, fraction: b.phaseFraction, waxing: scene.moonPhase < 180) | ||
| 108 | label(&context, "Moon \(Int((b.phaseFraction * 100).rounded()))%", at: CGPoint(x: pt.x - 20, y: pt.y), anchor: .trailing) | ||
| 109 | } else { | ||
| 110 | let r = max(2.0, 3.6 - 0.35 * b.magnitude) | ||
| 111 | context.fill(Path(ellipseIn: CGRect(x: pt.x - r, y: pt.y - r, width: 2 * r, height: 2 * r)), with: .color(Color(red: 0.95, green: 0.84, blue: 0.60))) | ||
| 112 | var tick = Path() | ||
| 113 | tick.move(to: CGPoint(x: pt.x + r + 3, y: pt.y)) | ||
| 114 | tick.addLine(to: CGPoint(x: pt.x + r + 15, y: pt.y)) | ||
| 115 | context.stroke(tick, with: .color(style.muted), lineWidth: 0.6) | ||
| 116 | label(&context, b.body.rawValue.capitalized, at: CGPoint(x: pt.x + r + 18, y: pt.y), anchor: .leading) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | func drawMoon(_ context: inout GraphicsContext, at c: CGPoint, radius r: Double, fraction: Double, waxing: Bool) { | ||
| 122 | let disc = CGRect(x: c.x - r, y: c.y - r, width: 2 * r, height: 2 * r) | ||
| 123 | context.fill(Path(ellipseIn: disc), with: .color(style.moonDark)) | ||
| 124 | // Lit limb: a half disc plus or minus the terminator ellipse. | ||
| 125 | let side: Double = waxing ? 1 : -1 | ||
| 126 | let e = r * abs(1 - 2 * fraction) | ||
| 127 | var lit = Path() | ||
| 128 | lit.move(to: CGPoint(x: c.x, y: c.y - r)) | ||
| 129 | lit.addArc(center: c, radius: r, startAngle: .degrees(-90), endAngle: .degrees(90), clockwise: side < 0) | ||
| 130 | lit.addEllipseArc(center: c, rx: e, ry: r, gibbous: fraction > 0.5, side: side) | ||
| 131 | lit.closeSubpath() | ||
| 132 | context.fill(lit, with: .color(style.moonLight)) | ||
| 133 | } | ||
| 134 | |||
| 135 | func drawHorizon(_ context: inout GraphicsContext, _ p: Projection, size: CGSize) { | ||
| 136 | var edge: [CGPoint] = [] | ||
| 137 | var az = centerAzimuth - fieldOfView | ||
| 138 | while az <= centerAzimuth + fieldOfView { | ||
| 139 | if let pt = p.point(Horizontal(altitude: 0, azimuth: az)) { edge.append(pt) } | ||
| 140 | az += 1 | ||
| 141 | } | ||
| 142 | guard let first = edge.first, let last = edge.last else { return } | ||
| 143 | var ground = Path() | ||
| 144 | ground.move(to: first) | ||
| 145 | for pt in edge.dropFirst() { ground.addLine(to: pt) } | ||
| 146 | ground.addLine(to: CGPoint(x: last.x, y: size.height + 10)) | ||
| 147 | ground.addLine(to: CGPoint(x: first.x, y: size.height + 10)) | ||
| 148 | ground.closeSubpath() | ||
| 149 | context.fill(ground, with: .color(style.ground)) | ||
| 150 | for (name, az) in [("SE", 135.0), ("S", 180.0), ("SW", 225.0), ("E", 90.0), ("W", 270.0)] { | ||
| 151 | guard let pt = p.point(Horizontal(altitude: 0, azimuth: az)), pt.x > 0, pt.x < size.width else { continue } | ||
| 152 | label(&context, name, at: CGPoint(x: pt.x, y: pt.y + 18), anchor: .center, color: style.muted) | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | func label(_ context: inout GraphicsContext, _ text: String, at point: CGPoint, anchor: UnitPoint, color: Color? = nil) { | ||
| 157 | let t = Text(text).font(.system(size: 11, weight: .regular)).foregroundStyle(color ?? style.label) | ||
| 158 | context.draw(t, at: point, anchor: anchor) | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | extension Path { | ||
| 163 | /// Half of the terminator ellipse, from the moon's bottom to its top. | ||
| 164 | mutating func addEllipseArc(center c: CGPoint, rx: Double, ry: Double, gibbous: Bool, side: Double) { | ||
| 165 | // Gibbous: terminator bulges into the dark half; crescent: into the lit half. | ||
| 166 | let direction = gibbous ? -side : side | ||
| 167 | let steps = 24 | ||
| 168 | for i in 0...steps { | ||
| 169 | let a = Double.pi / 2 - Double.pi * Double(i) / Double(steps) | ||
| 170 | addLine(to: CGPoint(x: c.x + direction * rx * cos(a), y: c.y + ry * sin(a))) | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
Packages/MoonriseKit/Tests/SkyEngineTests/HorizonsTests.swift added +67
| @@ -0,0 +1,67 @@ | |||
| 1 | import Foundation | ||
| 2 | import Testing | ||
| 3 | @testable import SkyEngine | ||
| 4 | |||
| 5 | // Reference values from JPL Horizons (ssd.jpl.nasa.gov/api/horizons.api), fetched 2026-10-17. | ||
| 6 | // Observer table: site -87.6298 E, 41.8781 N, 0 km; 2026-10-17 02:00 UT; quantities 2 (apparent | ||
| 7 | // RA/Dec, degrees) and 4 (airless apparent azimuth/elevation). | ||
| 8 | // Vector table: CENTER 500@10, REF_PLANE FRAME, 2026-10-17 00:00 UT, AU. | ||
| 9 | |||
| 10 | let chicago = Observer(latitude: 41.8781, longitude: -87.6298) | ||
| 11 | let observed = Date(timeIntervalSince1970: 1_792_202_400) // 2026-10-17T02:00:00Z | ||
| 12 | let vectorEpoch = Date(timeIntervalSince1970: 1_792_195_200) // 2026-10-17T00:00:00Z | ||
| 13 | |||
| 14 | struct HorizonsRow { | ||
| 15 | var body: Body | ||
| 16 | var raDegrees: Double | ||
| 17 | var dec: Double | ||
| 18 | var azimuth: Double | ||
| 19 | var altitude: Double | ||
| 20 | } | ||
| 21 | |||
| 22 | let observerRows: [HorizonsRow] = [ | ||
| 23 | HorizonsRow(body: .moon, raDegrees: 276.63979, dec: -28.01156, azimuth: 223.832359, altitude: 5.580273), | ||
| 24 | HorizonsRow(body: .mars, raDegrees: 133.60867, dec: 18.78232, azimuth: 15.406574, altitude: -27.902306), | ||
| 25 | HorizonsRow(body: .jupiter, raDegrees: 144.84484, dec: 14.69461, azimuth: 3.616329, altitude: -33.353920), | ||
| 26 | HorizonsRow(body: .saturn, raDegrees: 10.55474, dec: 1.59612, azimuth: 124.822854, altitude: 34.510943), | ||
| 27 | ] | ||
| 28 | |||
| 29 | /// One arcminute, Astronomy Engine's stated accuracy. | ||
| 30 | let arcminute = 1.0 / 60 | ||
| 31 | |||
| 32 | func angularDifference(_ a: Double, _ b: Double) -> Double { | ||
| 33 | abs(remainder(a - b, 360)) | ||
| 34 | } | ||
| 35 | |||
| 36 | @Test(arguments: observerRows) | ||
| 37 | func apparentEquatorialMatchesHorizons(_ row: HorizonsRow) throws { | ||
| 38 | let eq = try SkyEngine.equatorial(row.body, at: observed, from: chicago) | ||
| 39 | let raError = angularDifference(eq.rightAscension * 15, row.raDegrees) * cos(row.dec * .pi / 180) | ||
| 40 | #expect(raError < arcminute, "\(row.body) RA off by \(raError * 60)'") | ||
| 41 | #expect(abs(eq.declination - row.dec) < arcminute, "\(row.body) Dec off by \(abs(eq.declination - row.dec) * 60)'") | ||
| 42 | } | ||
| 43 | |||
| 44 | @Test(arguments: observerRows) | ||
| 45 | func horizontalMatchesHorizons(_ row: HorizonsRow) throws { | ||
| 46 | let hor = try SkyEngine.horizontal(row.body, at: observed, from: chicago) | ||
| 47 | let azError = angularDifference(hor.azimuth, row.azimuth) * cos(row.altitude * .pi / 180) | ||
| 48 | #expect(azError < arcminute, "\(row.body) azimuth off by \(azError * 60)'") | ||
| 49 | #expect(abs(hor.altitude - row.altitude) < arcminute, "\(row.body) altitude off by \(abs(hor.altitude - row.altitude) * 60)'") | ||
| 50 | } | ||
| 51 | |||
| 52 | @Test(arguments: [ | ||
| 53 | (Body.jupiter, SIMD3(-3.581981680856141, 3.573182031399209, 1.618755638647523)), | ||
| 54 | (Body.saturn, SIMD3(9.237054585455835, 1.860186986025053, 0.3705349659085789)), | ||
| 55 | ]) | ||
| 56 | func heliocentricMatchesHorizons(_ body: Body, _ expected: SIMD3<Double>) throws { | ||
| 57 | let v = try SkyEngine.heliocentric(body, at: vectorEpoch) | ||
| 58 | let d = v - expected | ||
| 59 | let error = (d * d).sum().squareRoot() | ||
| 60 | let angle = error / (expected * expected).sum().squareRoot() * 180 / .pi | ||
| 61 | #expect(angle < arcminute, "\(body) off by \(error) AU (\(angle * 3600)\")") | ||
| 62 | } | ||
| 63 | |||
| 64 | @Test func moonPhaseIsWaxing() throws { | ||
| 65 | let phase = try SkyEngine.moonPhase(at: observed) | ||
| 66 | #expect(phase > 0 && phase < 180) | ||
| 67 | } | ||
Packages/MoonriseKit/Tests/SkyRendererTests/SkyRenderTests.swift added +44
| @@ -0,0 +1,44 @@ | |||
| 1 | import Foundation | ||
| 2 | import SkyEngine | ||
| 3 | import SwiftUI | ||
| 4 | import Testing | ||
| 5 | @testable import SkyRenderer | ||
| 6 | |||
| 7 | let chicago = Observer(latitude: 41.8781, longitude: -87.6298) | ||
| 8 | let evening = Date(timeIntervalSince1970: 1_792_202_400) // 2026-10-17T02:00:00Z, 21:00 CDT | ||
| 9 | |||
| 10 | @Test func catalogLoads() { | ||
| 11 | #expect(Catalog.stars.count > 9_000) | ||
| 12 | #expect(Catalog.stars.contains { $0.hr == 2491 && $0.magnitude < -1 }) // Sirius | ||
| 13 | } | ||
| 14 | |||
| 15 | @Test func sceneHasSaturnUpAndMarsDown() throws { | ||
| 16 | let scene = try SkyScene(date: evening, observer: chicago) | ||
| 17 | let saturn = try #require(scene.bodies.first { $0.body == .saturn }) | ||
| 18 | let mars = try #require(scene.bodies.first { $0.body == .mars }) | ||
| 19 | #expect(saturn.position.altitude > 30) | ||
| 20 | #expect(mars.position.altitude < 0) | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Renders the sky to a PNG and reports timings. Set SKY_RENDER_OUT to keep the image. | ||
| 24 | @MainActor | ||
| 25 | @Test func renderStaticSky() throws { | ||
| 26 | let clock = ContinuousClock() | ||
| 27 | var scene: SkyScene! | ||
| 28 | let build = try clock.measure { scene = try SkyScene(date: evening, observer: chicago) } | ||
| 29 | |||
| 30 | let renderer = ImageRenderer(content: SkyView(scene: scene).frame(width: 393, height: 852)) | ||
| 31 | renderer.scale = 3 | ||
| 32 | var image: CGImage? | ||
| 33 | let render = clock.measure { image = renderer.cgImage } | ||
| 34 | let cg = try #require(image) | ||
| 35 | |||
| 36 | print("scene: \(scene.stars.count) stars, build \(build); render \(cg.width)x\(cg.height) in \(render)") | ||
| 37 | |||
| 38 | if let out = ProcessInfo.processInfo.environment["SKY_RENDER_OUT"] { | ||
| 39 | let url = URL(fileURLWithPath: out) | ||
| 40 | let dest = try #require(CGImageDestinationCreateWithURL(url as CFURL, "public.png" as CFString, 1, nil)) | ||
| 41 | CGImageDestinationAddImage(dest, cg, nil) | ||
| 42 | #expect(CGImageDestinationFinalize(dest)) | ||
| 43 | } | ||
| 44 | } | ||
THIRD_PARTY.org added +7
| @@ -0,0 +1,7 @@ | |||
| 1 | #+title: Third-party code and data | ||
| 2 | |||
| 3 | | Component | Path | Licence | Source | | ||
| 4 | |- | ||
| 5 | | Astronomy Engine v2.1.19, Copyright (c) 2019-2025 Don Cross | =Packages/MoonriseKit/Sources/CAstronomy= | MIT (=LICENSE= alongside) | https://github.com/cosinekitty/astronomy | | ||
| 6 | | Yale Bright Star Catalogue, 5th rev. ed. (Hoffleit & Warren 1991) | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/bsc5.json= | See VizieR policy; licence statement pending | https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 | | ||
| 7 | | ConstellationLines v1.4, Copyright (c) 2005-2026 Marc van der Sluys | =Packages/MoonriseKit/Sources/SkyRenderer/Resources/constellations.json= | CC BY 4.0 | https://github.com/MarcvdSluys/ConstellationLines, doi:10.5281/zenodo.10397192 | | ||
spikes/apns-worker/README.org added +34
| @@ -0,0 +1,34 @@ | |||
| 1 | #+title: apns-worker spike | ||
| 2 | |||
| 3 | Checks that a deployed Cloudflare Worker can send APNs pushes. APNs only | ||
| 4 | speaks HTTP/2; =wrangler dev= does not (workerd issue #4841), so test | ||
| 5 | deployed. | ||
| 6 | |||
| 7 | * probe (no Apple credentials) | ||
| 8 | #+begin_src sh | ||
| 9 | wrangler deploy | ||
| 10 | curl https://moonrise-apns-spike.<subdomain>.workers.dev/probe | ||
| 11 | #+end_src | ||
| 12 | |||
| 13 | Sends from a throwaway key to a dummy token on the sandbox host. Any APNs | ||
| 14 | JSON reason (expected: 403 =InvalidProviderToken=) means the HTTP/2 | ||
| 15 | connection worked. A network error means it did not. | ||
| 16 | |||
| 17 | * real push | ||
| 18 | Needs an APNs auth key (.p8), its key ID, the team ID, the app's bundle ID, | ||
| 19 | and a sandbox device token from a development build. | ||
| 20 | |||
| 21 | #+begin_src sh | ||
| 22 | # set APNS_KEY_ID, APNS_TEAM_ID, APNS_TOPIC in wrangler.jsonc | ||
| 23 | wrangler secret put APNS_KEY < AuthKey_XXXXXXXXXX.p8 | ||
| 24 | wrangler secret put SPIKE_SECRET | ||
| 25 | wrangler deploy | ||
| 26 | curl -X POST https://moonrise-apns-spike.<subdomain>.workers.dev/push \ | ||
| 27 | -H "authorization: Bearer $SPIKE_SECRET" \ | ||
| 28 | -d '{"token":"<device token hex>","sandbox":true}' | ||
| 29 | #+end_src | ||
| 30 | |||
| 31 | * test | ||
| 32 | #+begin_src sh | ||
| 33 | node --test test/ | ||
| 34 | #+end_src | ||
spikes/apns-worker/package.json added +9
| @@ -0,0 +1,9 @@ | |||
| 1 | { | ||
| 2 | "name": "moonrise-apns-spike", | ||
| 3 | "private": true, | ||
| 4 | "type": "module", | ||
| 5 | "scripts": { | ||
| 6 | "test": "node --test test/", | ||
| 7 | "deploy": "wrangler deploy" | ||
| 8 | } | ||
| 9 | } | ||
spikes/apns-worker/src/apns.ts added +78
| @@ -0,0 +1,78 @@ | |||
| 1 | // APNs provider-token auth and sending, using only Web APIs (Workers, Node). | ||
| 2 | |||
| 3 | export interface ApnsConfig { | ||
| 4 | keyId: string; | ||
| 5 | teamId: string; | ||
| 6 | /** Contents of the .p8 key file (PKCS#8 PEM). */ | ||
| 7 | privateKey: string; | ||
| 8 | /** App bundle ID. */ | ||
| 9 | topic: string; | ||
| 10 | } | ||
| 11 | |||
| 12 | export interface ApnsResult { | ||
| 13 | status: number; | ||
| 14 | apnsId: string | null; | ||
| 15 | reason: string | null; | ||
| 16 | } | ||
| 17 | |||
| 18 | const encoder = new TextEncoder(); | ||
| 19 | |||
| 20 | function base64url(bytes: Uint8Array): string { | ||
| 21 | let s = ""; | ||
| 22 | for (const b of bytes) s += String.fromCharCode(b); | ||
| 23 | return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); | ||
| 24 | } | ||
| 25 | |||
| 26 | function pemToDer(pem: string): Uint8Array { | ||
| 27 | const body = pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, ""); | ||
| 28 | return Uint8Array.from(atob(body), (c) => c.charCodeAt(0)); | ||
| 29 | } | ||
| 30 | |||
| 31 | export async function importKey(pem: string): Promise<CryptoKey> { | ||
| 32 | return crypto.subtle.importKey("pkcs8", pemToDer(pem), { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]); | ||
| 33 | } | ||
| 34 | |||
| 35 | /** ES256 provider token. WebCrypto returns the raw r||s signature JWS expects. */ | ||
| 36 | export async function providerToken(keyId: string, teamId: string, key: CryptoKey, issuedAt = Math.floor(Date.now() / 1000)): Promise<string> { | ||
| 37 | const header = base64url(encoder.encode(JSON.stringify({ alg: "ES256", kid: keyId }))); | ||
| 38 | const claims = base64url(encoder.encode(JSON.stringify({ iss: teamId, iat: issuedAt }))); | ||
| 39 | const input = `${header}.${claims}`; | ||
| 40 | const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, key, encoder.encode(input)); | ||
| 41 | return `${input}.${base64url(new Uint8Array(sig))}`; | ||
| 42 | } | ||
| 43 | |||
| 44 | /** Apple accepts a token for up to 60 minutes and rejects refreshes more often than every 20. */ | ||
| 45 | let cached: { token: string; issuedAt: number; keyId: string } | null = null; | ||
| 46 | |||
| 47 | async function token(config: ApnsConfig): Promise<string> { | ||
| 48 | const now = Math.floor(Date.now() / 1000); | ||
| 49 | if (cached && cached.keyId === config.keyId && now - cached.issuedAt < 50 * 60) return cached.token; | ||
| 50 | const t = await providerToken(config.keyId, config.teamId, await importKey(config.privateKey), now); | ||
| 51 | cached = { token: t, issuedAt: now, keyId: config.keyId }; | ||
| 52 | return t; | ||
| 53 | } | ||
| 54 | |||
| 55 | export async function send(config: ApnsConfig, deviceToken: string, payload: unknown, sandbox: boolean): Promise<ApnsResult> { | ||
| 56 | const host = sandbox ? "api.sandbox.push.apple.com" : "api.push.apple.com"; | ||
| 57 | const res = await fetch(`https://${host}/3/device/${deviceToken}`, { | ||
| 58 | method: "POST", | ||
| 59 | headers: { | ||
| 60 | authorization: `bearer ${await token(config)}`, | ||
| 61 | "apns-topic": config.topic, | ||
| 62 | "apns-push-type": "alert", | ||
| 63 | "apns-priority": "10", | ||
| 64 | "content-type": "application/json", | ||
| 65 | }, | ||
| 66 | body: JSON.stringify(payload), | ||
| 67 | }); | ||
| 68 | let reason: string | null = null; | ||
| 69 | if (!res.ok) { | ||
| 70 | const text = await res.text(); | ||
| 71 | try { | ||
| 72 | reason = (JSON.parse(text) as { reason?: string }).reason ?? text; | ||
| 73 | } catch { | ||
| 74 | reason = text; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | return { status: res.status, apnsId: res.headers.get("apns-id"), reason }; | ||
| 78 | } | ||
spikes/apns-worker/src/index.ts added +58
| @@ -0,0 +1,58 @@ | |||
| 1 | // Spike: does a deployed Worker reach APNs over HTTP/2? APNs rejects HTTP/1.1, | ||
| 2 | // so any APNs response with an apns-id answers yes. | ||
| 3 | |||
| 4 | import { send } from "./apns"; | ||
| 5 | |||
| 6 | interface Env { | ||
| 7 | APNS_KEY_ID: string; | ||
| 8 | APNS_TEAM_ID: string; | ||
| 9 | APNS_TOPIC: string; | ||
| 10 | APNS_KEY: string; | ||
| 11 | SPIKE_SECRET: string; | ||
| 12 | } | ||
| 13 | |||
| 14 | interface PushRequest { | ||
| 15 | token: string; | ||
| 16 | sandbox?: boolean; | ||
| 17 | title?: string; | ||
| 18 | body?: string; | ||
| 19 | } | ||
| 20 | |||
| 21 | export default { | ||
| 22 | async fetch(request: Request, env: Env): Promise<Response> { | ||
| 23 | const url = new URL(request.url); | ||
| 24 | if (request.method === "GET" && url.pathname === "/probe") return probe(); | ||
| 25 | if (request.method !== "POST" || url.pathname !== "/push") return new Response("not found", { status: 404 }); | ||
| 26 | if (request.headers.get("authorization") !== `Bearer ${env.SPIKE_SECRET}`) return new Response("unauthorized", { status: 401 }); | ||
| 27 | |||
| 28 | const req = (await request.json()) as PushRequest; | ||
| 29 | if (!/^[0-9a-f]{64,200}$/i.test(req.token ?? "")) return new Response("bad token", { status: 400 }); | ||
| 30 | |||
| 31 | const result = await send( | ||
| 32 | { keyId: env.APNS_KEY_ID, teamId: env.APNS_TEAM_ID, privateKey: env.APNS_KEY, topic: env.APNS_TOPIC }, | ||
| 33 | req.token, | ||
| 34 | { aps: { alert: { title: req.title ?? "Moonrise", body: req.body ?? "Push from a Cloudflare Worker" } } }, | ||
| 35 | req.sandbox ?? true, | ||
| 36 | ); | ||
| 37 | return Response.json(result, { status: result.status === 200 ? 200 : 502 }); | ||
| 38 | }, | ||
| 39 | } satisfies ExportedHandler<Env>; | ||
| 40 | |||
| 41 | /** Sends with a throwaway key and token. Needs no Apple credentials: an APNs JSON | ||
| 42 | * error (e.g. 403 InvalidProviderToken) still proves the HTTP/2 connection worked. */ | ||
| 43 | async function probe(): Promise<Response> { | ||
| 44 | const pair = (await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign"])) as CryptoKeyPair; | ||
| 45 | const der = new Uint8Array((await crypto.subtle.exportKey("pkcs8", pair.privateKey)) as ArrayBuffer); | ||
| 46 | const pem = `-----BEGIN PRIVATE KEY-----\n${btoa(String.fromCharCode(...der))}\n-----END PRIVATE KEY-----`; | ||
| 47 | try { | ||
| 48 | const result = await send( | ||
| 49 | { keyId: "PROBE00000", teamId: "PROBE00000", privateKey: pem, topic: "org.example.probe" }, | ||
| 50 | "0".repeat(64), | ||
| 51 | { aps: { alert: "probe" } }, | ||
| 52 | true, | ||
| 53 | ); | ||
| 54 | return Response.json({ http2: result.apnsId !== null || result.reason !== null, ...result }); | ||
| 55 | } catch (e) { | ||
| 56 | return Response.json({ http2: false, error: String(e) }, { status: 502 }); | ||
| 57 | } | ||
| 58 | } | ||
spikes/apns-worker/test/apns.test.ts added +25
| @@ -0,0 +1,25 @@ | |||
| 1 | import assert from "node:assert/strict"; | ||
| 2 | import { test } from "node:test"; | ||
| 3 | import { importKey, providerToken } from "../src/apns.ts"; | ||
| 4 | |||
| 5 | function b64urlDecode(s: string): Uint8Array { | ||
| 6 | const b64 = s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (s.length % 4)) % 4); | ||
| 7 | return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); | ||
| 8 | } | ||
| 9 | |||
| 10 | test("provider token is a valid ES256 JWT signed by the .p8 key", async () => { | ||
| 11 | const pair = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]); | ||
| 12 | const der = new Uint8Array(await crypto.subtle.exportKey("pkcs8", pair.privateKey)); | ||
| 13 | const pem = `-----BEGIN PRIVATE KEY-----\n${btoa(String.fromCharCode(...der)).replace(/(.{64})/g, "$1\n")}\n-----END PRIVATE KEY-----\n`; | ||
| 14 | |||
| 15 | const jwt = await providerToken("ABC123DEFG", "TEAM123456", await importKey(pem), 1_792_202_400); | ||
| 16 | const [h, c, s] = jwt.split("."); | ||
| 17 | |||
| 18 | assert.deepEqual(JSON.parse(new TextDecoder().decode(b64urlDecode(h))), { alg: "ES256", kid: "ABC123DEFG" }); | ||
| 19 | assert.deepEqual(JSON.parse(new TextDecoder().decode(b64urlDecode(c))), { iss: "TEAM123456", iat: 1_792_202_400 }); | ||
| 20 | |||
| 21 | const sig = b64urlDecode(s); | ||
| 22 | assert.equal(sig.length, 64); | ||
| 23 | const ok = await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, pair.publicKey, sig, new TextEncoder().encode(`${h}.${c}`)); | ||
| 24 | assert.ok(ok); | ||
| 25 | }); | ||
spikes/apns-worker/wrangler.jsonc added +11
| @@ -0,0 +1,11 @@ | |||
| 1 | { | ||
| 2 | "name": "moonrise-apns-spike", | ||
| 3 | "main": "src/index.ts", | ||
| 4 | "compatibility_date": "2026-10-01", | ||
| 5 | "vars": { | ||
| 6 | "APNS_KEY_ID": "", | ||
| 7 | "APNS_TEAM_ID": "", | ||
| 8 | "APNS_TOPIC": "" | ||
| 9 | } | ||
| 10 | // Secrets: APNS_KEY (.p8 contents), SPIKE_SECRET. Set with `wrangler secret put`. | ||
| 11 | } | ||