From 044f2f438186130af7251a99a4798212ed0656ab Mon Sep 17 00:00:00 2001 From: starainrt Date: Thu, 15 Oct 2026 23:54:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=E5=8F=AF=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E6=98=9F=E5=8E=86=E6=A8=A1=E5=9E=8B=E5=B9=B6=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E5=A4=A9=E8=B1=A1=E8=A7=82=E6=B5=8B=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则 --- NOTICE | 160 ++ README.en.md | 102 +- README.md | 76 +- astro.go | 12 +- baseline_regression_test.go | 5 + basic/api_default.go | 1794 +++++++++++++ basic/apparent_direction_test.go | 34 + basic/apparent_state_test.go | 46 + basic/apparent_vector.go | 77 + basic/apsis.go | 18 +- basic/apsis_test.go | 1 - basic/calendar_test.go | 3 +- basic/constellation.go | 67 +- basic/constellation_test.go | 20 +- basic/coordinate.go | 32 +- basic/diameter.go | 118 +- basic/diameter_test.go | 1 - basic/doc.go | 5 + basic/earth.go | 2 +- basic/earth_test.go | 2 +- basic/ephemeris.go | 35 + basic/ephemeris_instance.go | 120 + basic/ephemeris_instance_contract_test.go | 121 + basic/ephemeris_instance_test.go | 384 +++ basic/ephemeris_test.go | 41 + basic/event_baseline_golden_test.go | 85 + basic/event_refine.go | 4 +- basic/greatest_time_contour.go | 8 +- basic/heliacal.go | 272 ++ basic/heliacal_test.go | 138 + basic/iau2006.go | 32 + basic/iau2006_test.go | 25 + basic/jupiter.go | 71 +- basic/jupiter_events.go | 160 +- basic/jupiter_physical.go | 31 +- basic/jupiter_physical_test.go | 3 + basic/jupiter_satellite_contact_events.go | 84 +- basic/jupiter_satellite_events.go | 142 +- basic/jupiter_satellite_phenomena.go | 10 +- basic/jupiter_satellites.go | 48 +- basic/limb_limits.go | 198 ++ basic/limb_tolerance.go | 35 + basic/local_ephemeris.go | 74 +- basic/lunar_eclipse.go | 112 +- basic/lunar_eclipse_diagram.go | 12 +- basic/lunar_eclipse_ephemeris_instance.go | 61 + .../lunar_eclipse_ephemeris_instance_test.go | 162 ++ basic/lunar_eclipse_geometry.go | 23 +- basic/lunar_eclipse_limb.go | 147 ++ basic/lunar_eclipse_test.go | 12 + basic/lunar_limb.go | 424 +++ basic/lunar_limb_bench_test.go | 105 + basic/lunar_limb_contact_test.go | 277 ++ basic/lunar_limb_test.go | 489 ++++ basic/mars.go | 71 +- basic/mars_event_regression_test.go | 8 +- basic/mars_events.go | 160 +- basic/mercury.go | 71 +- basic/mercury_event_regression_test.go | 8 +- basic/mercury_events.go | 526 ++-- basic/moon_bright_limb.go | 38 +- basic/moon_ephemeris_instance.go | 107 + basic/moon_horizon.go | 14 +- basic/moon_max_declination.go | 86 +- basic/moon_max_declination_perf_test.go | 3 + basic/moon_max_declination_test.go | 1 - basic/moon_observation.go | 140 +- basic/moon_phase.go | 12 + basic/moon_phase_ephemeris_instance.go | 115 + basic/moon_physical.go | 29 +- basic/moon_planet_conjunction.go | 136 +- basic/moon_precision.go | 243 +- basic/moon_precision_test.go | 6 +- basic/moving_disk_engine.go | 7 +- basic/moving_disk_engine_test.go | 10 + basic/neptune.go | 71 +- basic/neptune_events.go | 160 +- basic/occultation.go | 33 +- basic/occultation_instant.go | 16 +- basic/occultation_isochrone.go | 6 +- basic/occultation_limb.go | 360 +++ basic/occultation_limb_range_test.go | 86 + basic/occultation_limit_separation_test.go | 15 +- basic/occultation_path.go | 122 +- basic/occultation_path_ephemeris.go | 12 +- basic/occultation_path_ephemeris_test.go | 2 + basic/occultation_planet.go | 195 +- basic/occultation_planet_diagram.go | 43 +- basic/occultation_planet_footprint.go | 20 +- basic/occultation_planet_path.go | 121 +- basic/occultation_planet_path_test.go | 6 +- basic/occultation_planet_test.go | 10 +- basic/occultation_rise_set.go | 12 +- basic/occultation_rise_set_refine.go | 10 +- basic/occultation_rise_set_topology.go | 30 +- basic/occultation_rise_set_tracking_test.go | 6 +- basic/occultation_star.go | 272 +- basic/occultation_star_diagram.go | 29 +- basic/occultation_star_limb.go | 461 ++++ basic/occultation_star_limb_test.go | 336 +++ basic/occultation_station_correction.go | 104 +- basic/occultation_station_correction_test.go | 69 +- basic/occultation_station_envelope.go | 13 +- basic/orbit_coordinates.go | 28 +- basic/orbit_kepler.go | 8 +- basic/orbit_test.go | 18 + basic/orbital_events.go | 191 ++ basic/orbital_events_test.go | 85 + basic/orbital_nodes.go | 123 +- basic/outer_planet_event_regression_test.go | 12 +- basic/planet_apparent.go | 54 +- basic/planet_apparent_vector_test.go | 51 + basic/planet_ephemeris_instance.go | 1070 ++++++++ basic/planet_magnitude_2018.go | 199 ++ basic/planet_magnitude_2018_test.go | 59 + basic/planet_phase.go | 155 +- basic/planet_physical.go | 55 +- basic/planet_transit.go | 201 +- basic/planet_transit_local.go | 1094 ++++++++ basic/planet_transit_local_test.go | 237 ++ basic/planet_transit_test.go | 27 + basic/planet_truncated.go | 533 ++-- basic/precession.go | 143 +- basic/refraction.go | 149 +- basic/refraction_test.go | 2 +- basic/rise_set_altitude.go | 298 +++ basic/rise_set_altitude_test.go | 86 + basic/rise_set_curve.go | 10 +- basic/rise_set_curve_test.go | 2 +- basic/rise_set_dynamic_test.go | 2 +- basic/rise_set_weather.go | 66 + basic/rise_set_weather_points.go | 143 + basic/rise_set_weather_test.go | 129 + basic/saturn.go | 71 +- basic/saturn_events.go | 160 +- basic/saturn_ring.go | 12 +- basic/saturn_ring_ephemeris_instance.go | 110 + basic/saturn_ring_test.go | 50 + basic/sky_events.go | 269 ++ basic/sky_events_test.go | 140 + basic/solar_eclipse.go | 144 +- basic/solar_eclipse_band.go | 29 +- basic/solar_eclipse_band_closure_scan.go | 4 +- basic/solar_eclipse_bessel.go | 16 +- basic/solar_eclipse_diagram.go | 39 +- basic/solar_eclipse_hybrid_envelope.go | 16 +- basic/solar_eclipse_isochrone.go | 10 +- basic/solar_eclipse_isochrone_test.go | 3 +- basic/solar_eclipse_limb.go | 155 ++ basic/solar_eclipse_local.go | 240 +- basic/solar_eclipse_magnitude.go | 45 +- basic/solar_eclipse_magnitude_interp_test.go | 84 + basic/solar_eclipse_noncentral_band.go | 12 +- basic/solar_eclipse_partial_limb.go | 165 ++ basic/solar_eclipse_path.go | 161 +- basic/solar_eclipse_path_geometry.go | 29 +- basic/solar_eclipse_path_test.go | 6 + .../solar_eclipse_path_width_contract_test.go | 19 +- basic/solar_eclipse_review_fixes_test.go | 4 +- basic/solar_eclipse_rise_set.go | 4 +- basic/solar_eclipse_rise_set_arc.go | 47 +- basic/solar_eclipse_rise_set_refine.go | 16 +- basic/solar_eclipse_rise_set_scan_test.go | 3 + basic/solar_eclipse_rise_set_topology.go | 12 +- basic/solar_eclipse_shadow.go | 34 +- basic/solar_terms.go | 164 +- basic/solar_terms_bounds_test.go | 14 + basic/solar_terms_ephemeris_instance.go | 159 ++ basic/star_catalog.dat | Bin 608381 -> 165083 bytes basic/star_catalog.go | 134 +- basic/star_catalog_cache_test.go | 24 +- basic/star_catalog_data.go | 231 +- basic/star_catalog_format_test.go | 49 + basic/star_catalog_test.go | 9 +- basic/sun.go | 21 +- basic/sun_ephemeris_instance.go | 143 + basic/sun_moon_alias_ephemeris_instance.go | 277 ++ basic/sun_observation.go | 259 +- basic/sun_observation_ephemeris_instance.go | 133 + basic/sun_physical.go | 6 +- basic/sun_physical_test.go | 1 - .../semianalytic/jupiter_event_baseline.json | 1732 +++++++++++++ .../semianalytic/lunar_eclipse_chauvenet.json | 50 + .../semianalytic/mars_event_baseline.json | 1732 +++++++++++++ .../semianalytic/mercury_event_baseline.json | 2308 +++++++++++++++++ .../semianalytic/neptune_event_baseline.json | 1732 +++++++++++++ .../occultation_limit_anchors.json | 10 + .../semianalytic/planet_apparent_de441.csv | 420 +++ .../semianalytic/saturn_event_baseline.json | 1732 +++++++++++++ .../semianalytic/uranus_event_baseline.json | 1732 +++++++++++++ .../semianalytic/venus_event_baseline.json | 2308 +++++++++++++++++ basic/time_labels.go | 33 + basic/timescale.go | 23 +- basic/timescale_table.go | 8 +- basic/timescale_test.go | 45 +- basic/topocentric_horizontal.go | 90 + basic/uranus.go | 71 +- basic/uranus_events.go | 160 +- basic/venus.go | 71 +- basic/venus_event_regression_test.go | 8 +- basic/venus_events.go | 492 ++-- calendar/chinese.go | 30 +- calendar/chinese_test.go | 2 +- calendar/parse_zone_test.go | 32 + calendar/reform_roundtrip_test.go | 5 +- calendar/time.go | 6 +- calendar/time_add_test.go | 19 +- context.go | 237 ++ context_example_test.go | 23 + context_internal_test.go | 30 + context_test.go | 350 +++ coord/coord.go | 10 + .../lunar-eclipse-2026-03-03-detailed-en.svg | 2 +- doc/img/lunar-eclipse-2026-03-03-detailed.svg | 2 +- doc/img/lunar-eclipse-2026-03-03-en.svg | 2 +- doc/img/lunar-eclipse-2026-03-03.svg | 2 +- .../lunar-eclipse-2029-01-01-detailed-en.svg | 2 +- doc/img/lunar-eclipse-2029-01-01-detailed.svg | 2 +- doc/img/lunar-eclipse-2029-01-01-en.svg | 2 +- .../lunar-eclipse-2029-01-01-global-en.svg | 2 +- doc/img/lunar-eclipse-2029-01-01-global.svg | 2 +- doc/img/lunar-eclipse-2029-01-01.svg | 2 +- ...ultation-hr4799-2025-06-05-detailed-en.svg | 2 +- ...occultation-hr4799-2025-06-05-detailed.svg | 2 +- ...ccultation-hr4799-2025-06-05-global-en.svg | 2 +- ...r-occultation-hr4799-2025-06-05-global.svg | 2 +- ...tion-hr4799-2025-06-05-limb-profile-en.svg | 2 + ...ltation-hr4799-2025-06-05-limb-profile.svg | 2 + ...occultation-hr4799-2025-06-05-local-en.svg | 2 +- ...ar-occultation-hr4799-2025-06-05-local.svg | 2 +- ...tation-hr4799-2025-06-05-southpolar-en.svg | 2 +- ...cultation-hr4799-2025-06-05-southpolar.svg | 2 +- .../solar-eclipse-arctic-2012-global-en.svg | 2 +- doc/img/solar-eclipse-arctic-2012-global.svg | 2 +- doc/img/solar-eclipse-beijing-2035-en.svg | 2 +- .../solar-eclipse-beijing-2035-global-en.svg | 2 +- doc/img/solar-eclipse-beijing-2035-global.svg | 2 +- doc/img/solar-eclipse-beijing-2035.svg | 2 +- ...solar-eclipse-southpolar-2021-12-04-en.svg | 2 +- .../solar-eclipse-southpolar-2021-12-04.svg | 2 +- doc/img/solar-eclipse-xiamen-2012-en.svg | 2 +- .../solar-eclipse-xiamen-2012-global-en.svg | 2 +- doc/img/solar-eclipse-xiamen-2012-global.svg | 2 +- doc/img/solar-eclipse-xiamen-2012.svg | 2 +- doc/img/solar-eclipse-yangshan-2009-en.svg | 2 +- .../solar-eclipse-yangshan-2009-global-en.svg | 2 +- ...ar-eclipse-yangshan-2009-global-ut1-en.svg | 2 +- ...solar-eclipse-yangshan-2009-global-ut1.svg | 2 +- .../solar-eclipse-yangshan-2009-global.svg | 2 +- .../solar-eclipse-yangshan-2009-globe-en.svg | 2 +- doc/img/solar-eclipse-yangshan-2009-globe.svg | 2 +- doc/img/solar-eclipse-yangshan-2009.svg | 2 +- doc/img/transit-mercury-beijing-2032-en.svg | 1 + ...transit-mercury-beijing-2032-global-en.svg | 1 + .../transit-mercury-beijing-2032-global.svg | 1 + doc/img/transit-mercury-beijing-2032.svg | 1 + doc/img/transit-venus-beijing-2012-en.svg | 1 + .../transit-venus-beijing-2012-global-en.svg | 1 + doc/img/transit-venus-beijing-2012-global.svg | 1 + doc/img/transit-venus-beijing-2012.svg | 1 + doc/manual/accuracy.md | 201 +- doc/manual/calendar.md | 38 +- doc/manual/coord.md | 19 +- doc/manual/eclipse.md | 185 +- doc/manual/en/accuracy.md | 209 +- doc/manual/en/calendar.md | 26 +- doc/manual/en/coord.md | 11 +- doc/manual/en/eclipse.md | 201 +- doc/manual/en/ephemeris.md | 219 ++ doc/manual/en/formula.md | 46 +- doc/manual/en/map-geojson.md | 59 +- doc/manual/en/occultation.md | 126 +- doc/manual/en/orbit.md | 31 +- doc/manual/en/phenomena.md | 243 ++ doc/manual/en/planets.md | 189 +- doc/manual/en/star.md | 68 +- doc/manual/en/sun-moon.md | 84 +- doc/manual/en/sundial.md | 22 +- doc/manual/en/timescale.md | 34 +- doc/manual/ephemeris.md | 219 ++ doc/manual/formula.md | 42 +- doc/manual/map-geojson.md | 47 +- doc/manual/occultation.md | 114 +- doc/manual/orbit.md | 27 +- doc/manual/phenomena.md | 243 ++ doc/manual/planets.md | 195 +- doc/manual/star.md | 66 +- doc/manual/sun-moon.md | 82 +- doc/manual/sundial.md | 10 +- doc/manual/timescale.md | 38 +- eclipse/api_default.go | 492 ++++ eclipse/context.go | 159 ++ eclipse/context_test.go | 186 ++ eclipse/label_ut1.go | 4 +- eclipse/lunar.go | 81 +- eclipse/lunar_ephemeris_instance.go | 142 + eclipse/lunar_ephemeris_instance_test.go | 153 ++ eclipse/lunar_limb_test.go | 272 ++ eclipse/lunar_local.go | 355 ++- eclipse/lunar_local_test.go | 7 +- eclipse/lunar_panel.go | 37 +- eclipse/lunar_test.go | 18 +- eclipse/observation_helpers.go | 36 +- eclipse/saros.go | 18 +- eclipse/saros_anchor_consistency_test.go | 4 +- eclipse/saros_benchmark_test.go | 4 +- eclipse/saros_extended.go | 73 +- eclipse/saros_extended_test.go | 98 +- eclipse/saros_generate_test.go | 54 +- ...nded.go => saros_table_extended_legacy.go} | 10 +- eclipse/saros_table_extended_semianalytic.go | 424 +++ eclipse/saros_table_lunar.go | 2 +- eclipse/saros_table_solar.go | 2 +- eclipse/saros_test.go | 2 +- eclipse/search_skip.go | 9 +- eclipse/solar.go | 185 +- eclipse/solar_bessel.go | 13 +- eclipse/solar_candidates.go | 12 +- eclipse/solar_local.go | 340 +-- eclipse/solar_local_search.go | 47 +- eclipse/solar_local_search_test.go | 4 + eclipse/solar_local_test.go | 11 +- eclipse/solar_panel.go | 71 +- eclipse/solar_path.go | 114 +- eclipse/solar_path_test.go | 6 + eclipse/solar_shadow.go | 22 +- eclipse/svg/data_source_test.go | 11 + eclipse/svg/doc.go | 41 +- eclipse/svg/eclipse_limb_svg_test.go | 70 + eclipse/svg/lunar.go | 9 +- eclipse/svg/lunar_detailed.go | 21 +- eclipse/svg/lunar_geocentric_maximum_test.go | 5 + eclipse/svg/lunar_map.go | 38 +- eclipse/svg/lunar_map_polar_test.go | 8 +- eclipse/svg/lunar_model.go | 1 + eclipse/svg/lunar_penumbral_phase_test.go | 8 +- eclipse/svg/solar.go | 8 +- eclipse/svg/solar_map.go | 58 +- eclipse/svg/solar_map_benchmark_test.go | 2 +- eclipse/svg/solar_map_detailed.go | 3 +- eclipse/svg/solar_map_legend_test.go | 2 +- eclipse/svg/solar_map_limb_panel_test.go | 203 ++ eclipse/svg/solar_model.go | 1 + .../semianalytic/lunar_geocentric_block.json | 6 + .../lunar_penumbral_phase_digest.json | 30 + eclipse/svg/transit_local.go | 438 ++++ eclipse/svg/transit_local_test.go | 82 + eclipse/svg/transit_map.go | 536 ++++ eclipse/svg/transit_map_test.go | 91 + .../local_lunar_chauvenet_available.json | 4 + .../local_solar_contact_points.json | 6 + .../lunar_chauvenet_available.json | 4 + .../semianalytic/lunar_contact_points.json | 6 + ephemeris.go | 100 + ephemeris_public_test.go | 75 + formula/earth_orientation.go | 82 + formula/earth_orientation_test.go | 44 + formula/extinction.go | 47 + formula/extinction_test.go | 38 + formula/refraction.go | 29 + formula/refraction_test.go | 21 + geojson/api_default.go | 84 + geojson/context.go | 50 + geojson/context_test.go | 74 + geojson/eclipse.go | 524 ++-- geojson/eclipse_limb_test.go | 49 + geojson/geojson.go | 22 +- geojson/geojson_test.go | 3 + geojson/lunar_penumbra_band_test.go | 3 + ...tract_test.go => marshal_contract_test.go} | 18 +- geojson/occultation.go | 26 +- .../occultation_envelope_regression_test.go | 3 + ...ltation_noncentral_band_regression_test.go | 9 +- geojson/regulus_20261007_regression_test.go | 70 + ...ar_central_band_coverage_internal_test.go} | 116 +- .../solar_eclipse_20120521_regression_test.go | 38 +- .../solar_eclipse_32881115_regression_test.go | 7 +- ...ar_eclipse_grazing_band_regression_test.go | 3 + ...st.go => solar_eclipse_limit_line_test.go} | 71 +- ...> solar_eclipse_marshal_benchmark_test.go} | 16 +- ...eclipse_polar_two_limit_regression_test.go | 3 + geojson/solar_shadow_instant.go | 6 +- geojson/solar_shadow_region.go | 44 +- geojson/spherical_sampling.go | 4 +- .../solar_eclipse_20120521_roots.json | 10 + geojson/transit.go | 451 ++++ geojson/transit_test.go | 147 ++ internal/ephem/apparent.go | 8 + internal/ephem/apparent_generic.go | 88 + internal/ephem/body.go | 54 + internal/ephem/default_legacy.go | 9 + internal/ephem/default_semianalytic.go | 7 + internal/ephem/ephem.go | 160 ++ internal/ephem/ephem_test.go | 63 + internal/ephem/global_test.go | 31 + internal/ephem/nil_engine_test.go | 44 + internal/ephem/stack.go | 243 ++ internal/ephem/stack_test.go | 127 + internal/ephemtest/ephemtest.go | 31 + internal/ephemtest/values.go | 98 + internal/geodata/earth.go | 60 + internal/geodata/linework.go | 40 +- internal/geodata/orthographic_test.go | 2 +- internal/geodata/polygon_union.go | 131 +- internal/geodata/seam_test.go | 2 +- internal/geodata/sphere.go | 14 +- internal/geodata/sphere_test.go | 4 +- internal/geodata/sweep.go | 34 +- internal/geodata/topology.go | 28 +- internal/iau2006/data.go | 1399 ++++++++++ internal/iau2006/iau2006.go | 155 ++ internal/iau2006/iau2006_test.go | 61 + internal/iau2006/reference_data_test.go | 13 + internal/limbnote/limbnote.go | 96 + internal/limbnote/limbnote_test.go | 23 + internal/ltp/basis.go | 77 + internal/ltp/basis_test.go | 43 + internal/ltp/ltp.go | 119 + internal/lunarhorizon/lunarhorizon.go | 28 +- internal/occultationgeo/band_cleanup.go | 31 +- internal/occultationgeo/band_linework.go | 66 +- internal/occultationgeo/continuity.go | 38 +- internal/occultationgeo/continuity_test.go | 30 +- internal/occultationgeo/curve_sampling.go | 15 +- internal/occultationgeo/differential_test.go | 2 +- internal/occultationgeo/direct_band.go | 10 +- internal/occultationgeo/footprint_sweep.go | 32 +- internal/occultationgeo/reference_test.go | 4 +- internal/occultationgeo/visible_band.go | 2 +- internal/refraction/refraction.go | 167 ++ internal/refraction/refraction_test.go | 21 + internal/solarclosure/solarclosure.go | 24 +- internal/timenote/timenote.go | 8 +- internal/timenote/timenote_test.go | 4 +- jupiter/api_default.go | 306 +++ jupiter/context.go | 13 + jupiter/context_test.go | 102 + jupiter/diameter.go | 20 +- jupiter/doc.go | 4 + jupiter/jupiter.go | 216 +- jupiter/nodes.go | 20 +- jupiter/parallactic.go | 10 +- jupiter/phase.go | 40 +- jupiter/phenomena.go | 7 +- jupiter/phenomena_contact_events.go | 18 +- jupiter/phenomena_events.go | 23 +- jupiter/physical.go | 24 +- jupiter/rise_set_atmosphere.go | 33 + jupiter/satellites.go | 7 +- jupiter/truncated.go | 97 +- kml/kml.go | 229 +- kml/kml_test.go | 29 + kml/transit_test.go | 43 + lite/internal/common.go | 6 +- lite/internal/horizon.go | 28 + lite/internal/horizon_test.go | 18 + lite/internal/refraction.go | 25 + lite/internal/time.go | 114 + lite/internal/time_test.go | 39 + lite/moon/moon.go | 29 +- lite/sun/sun.go | 37 +- low_precision_regression_test.go | 3 + mars/api_default.go | 258 ++ mars/context.go | 13 + mars/context_test.go | 92 + mars/diameter.go | 20 +- mars/doc.go | 4 + mars/mars.go | 216 +- mars/nodes.go | 20 +- mars/parallactic.go | 10 +- mars/phase.go | 40 +- mars/physical.go | 10 +- mars/rise_set_atmosphere.go | 33 + mars/rise_set_atmosphere_test.go | 33 + mars/truncated.go | 97 +- mercury/api_default.go | 302 +++ mercury/context.go | 13 + mercury/context_test.go | 102 + mercury/diameter.go | 20 +- mercury/doc.go | 4 + mercury/mercury.go | 258 +- mercury/mercury_test.go | 2 +- mercury/nodes.go | 20 +- mercury/parallactic.go | 10 +- mercury/phase.go | 40 +- mercury/physical.go | 10 +- mercury/rise_set_atmosphere.go | 33 + mercury/transit.go | 35 +- mercury/transit_test.go | 19 + mercury/truncated.go | 97 +- moon/api_default.go | 417 +++ moon/apsis.go | 8 +- moon/conjunction.go | 12 +- moon/context.go | 73 + moon/context_test.go | 144 + moon/diameter.go | 20 +- moon/doc.go | 5 + moon/limb/NOTICE | 28 + moon/limb/kaguya_lalt.bin | Bin 0 -> 1456965 bytes moon/limb/limb.go | 37 + moon/limb/limb_test.go | 110 + moon/limbtable/limbtable.go | 153 ++ moon/max_declination.go | 32 +- moon/moon.go | 441 ++-- moon/nodes.go | 20 +- moon/occultation.go | 21 +- moon/occultation_limb_graze_test.go | 46 + moon/occultation_limb_test.go | 117 + moon/occultation_path.go | 12 +- moon/occultation_planet.go | 18 +- moon/occultation_planet_diagram.go | 11 +- moon/occultation_planet_path.go | 12 +- moon/occultation_star.go | 19 +- moon/occultation_star_diagram.go | 39 +- moon/occultation_ut1.go | 23 +- moon/parallactic.go | 6 +- moon/phase.go | 23 +- moon/physical.go | 10 +- moon/physical_topocentric.go | 13 +- moon/refraction.go | 8 +- moon/rise_set_altitude.go | 42 + moon/rise_set_altitude_test.go | 75 + moon/svg/occultation.go | 16 +- moon/svg/occultation_detailed.go | 12 +- moon/svg/occultation_detailed_test.go | 16 +- moon/svg/occultation_limb_custom_test.go | 76 + moon/svg/occultation_limb_diagram_test.go | 116 + moon/svg/occultation_limb_profile.go | 629 +++++ moon/svg/occultation_limb_profile_test.go | 373 +++ moon/svg/occultation_local.go | 21 +- moon/svg/occultation_planet_local.go | 22 +- moon/svg/occultation_timescale_test.go | 2 +- .../occultation_detailed_panel.json | 6 + neptune/api_default.go | 258 ++ neptune/context.go | 13 + neptune/context_test.go | 92 + neptune/diameter.go | 20 +- neptune/doc.go | 4 + neptune/neptune.go | 216 +- neptune/neptune_test.go | 18 +- neptune/nodes.go | 20 +- neptune/parallactic.go | 10 +- neptune/phase.go | 40 +- neptune/physical.go | 10 +- neptune/rise_set_atmosphere.go | 33 + neptune/testdata/semianalytic/neptune.json | 5 + neptune/truncated.go | 97 +- orbit/doc.go | 7 +- orbit/orbit.go | 16 +- orbit/orbit_test.go | 188 +- orbit/propagation.go | 565 ++++ orbit/propagation_fix_test.go | 147 ++ phenomena/altitude.go | 43 + phenomena/api_default.go | 53 + phenomena/orbital_zone_test.go | 30 + phenomena/phenomena.go | 176 ++ phenomena/phenomena_test.go | 41 + phenomena/star_coordinate.go | 29 + phenomena/star_coordinate_test.go | 45 + planet/doc.go | 6 +- planet/internal/semianalytic/apparent.go | 247 ++ .../semianalytic/apparent_generic_test.go | 45 + .../semianalytic/apparent_moon_test.go | 76 + planet/internal/semianalytic/guard_test.go | 45 + planet/internal/semianalytic/lunar/default.go | 46 + planet/internal/semianalytic/lunar/fast.bin | Bin 0 -> 4653 bytes planet/internal/semianalytic/lunar/lunar.bin | Bin 0 -> 38071 bytes planet/internal/semianalytic/lunar/lunar.go | 513 ++++ .../internal/semianalytic/lunar/lunar_test.go | 115 + .../semianalytic/lunar/testdata/fast.csv | 51 + .../semianalytic/lunar/testdata/vectors.csv | 51 + planet/internal/semianalytic/ofdate.go | 154 ++ .../internal/semianalytic/planets/default.go | 67 + planet/internal/semianalytic/planets/fast.bin | Bin 0 -> 42150 bytes planet/internal/semianalytic/planets/long.bin | Bin 0 -> 115069 bytes .../internal/semianalytic/planets/modern.bin | Bin 0 -> 305370 bytes .../internal/semianalytic/planets/planets.go | 610 +++++ .../semianalytic/planets/planets_test.go | 166 ++ planet/internal/semianalytic/planets/pln3.go | 193 ++ .../semianalytic/planets/testdata/fast.csv | 108 + .../semianalytic/planets/testdata/long.csv | 976 +++++++ .../semianalytic/planets/testdata/modern.csv | 876 +++++++ planet/internal/semianalytic/state.go | 126 + planet/internal/semianalytic/state_test.go | 153 ++ .../semianalytic/testdata/de441-states.csv | 560 ++++ planet/model.go | 17 + planet/moon.go | 132 + planet/moon_model.go | 11 + planet/planet.go | 16 +- planet/planet_test.go | 3 + planet/semianalytic_test.go | 141 + planet/spk/apparent_test.go | 45 + planet/spk/guard_test.go | 84 + planet/spk/spk.go | 844 ++++++ planet/spk/spk_test.go | 441 ++++ planet/spk/testdata/ceres.bsp | Bin 0 -> 69632 bytes planet/spk/testdata/de441_ref.csv | 45 + saturn/api_default.go | 272 ++ saturn/context.go | 13 + saturn/context_test.go | 96 + saturn/diameter.go | 20 +- saturn/doc.go | 4 + saturn/nodes.go | 20 +- saturn/parallactic.go | 10 +- saturn/phase.go | 40 +- saturn/physical.go | 20 +- saturn/ring.go | 10 +- saturn/rise_set_atmosphere.go | 33 + saturn/saturn.go | 216 +- saturn/truncated.go | 97 +- semantics_regression_test.go | 25 +- star/catalog/badfile_test.go | 120 + star/catalog/catalog.go | 165 ++ star/catalog/catalog_test.go | 256 ++ star/catalog/cds.go | 244 ++ star/catalog/cross_test.go | 141 + star/catalog/csv.go | 314 +++ star/catalog/doc.go | 2 + star/catalog/names.go | 23 + star/catalog/near_test.go | 54 + star/catalog/parse.go | 164 ++ star/catalog/smoke_test.go | 41 + star/catalog/stellarium.go | 443 ++++ star/catalog/testdata/bsc5_sample.dat | 7 + star/catalog/testdata/cross-id.cat | Bin 0 -> 21 bytes star/catalog/testdata/extra_name.fab | 11 + star/catalog/testdata/hip2_sample.dat | 5 + star/catalog/testdata/name.fab | 6 + star/catalog/testdata/stars_0_0v0_18.cat | Bin 0 -> 352 bytes star/catalog/types.go | 242 ++ star/rise_set_atmosphere.go | 23 + star/rise_set_atmosphere_test.go | 29 + sun/api_default.go | 203 ++ sun/atmosphere_test.go | 26 + sun/context.go | 38 + sun/context_test.go | 79 + sun/diameter.go | 20 +- sun/doc.go | 4 + sun/parallactic.go | 14 +- sun/physical.go | 10 +- sun/refraction.go | 20 +- sun/rise_set_altitude.go | 42 + sun/rise_set_altitude_test.go | 74 + sun/sun.go | 313 +-- sun/sun_test.go | 2 +- sundial/planar.go | 4 + sundial/planar_test.go | 13 + timescale.go | 15 +- uranus/api_default.go | 266 ++ uranus/context.go | 13 + uranus/context_test.go | 94 + uranus/diameter.go | 20 +- uranus/doc.go | 4 + uranus/nodes.go | 20 +- uranus/parallactic.go | 10 +- uranus/phase.go | 40 +- uranus/physical.go | 20 +- uranus/rise_set_atmosphere.go | 33 + uranus/truncated.go | 97 +- uranus/uranus.go | 216 +- venus/api_default.go | 302 +++ venus/context.go | 13 + venus/context_test.go | 102 + venus/diameter.go | 20 +- venus/doc.go | 4 + venus/nodes.go | 20 +- venus/parallactic.go | 10 +- venus/phase.go | 40 +- venus/physical.go | 10 +- venus/rise_set_atmosphere.go | 33 + venus/transit.go | 35 +- venus/transit_test.go | 16 + venus/truncated.go | 97 +- venus/venus.go | 258 +- 674 files changed, 62983 insertions(+), 8396 deletions(-) create mode 100644 NOTICE create mode 100644 basic/api_default.go create mode 100644 basic/apparent_direction_test.go create mode 100644 basic/apparent_state_test.go create mode 100644 basic/apparent_vector.go create mode 100644 basic/doc.go create mode 100644 basic/ephemeris.go create mode 100644 basic/ephemeris_instance.go create mode 100644 basic/ephemeris_instance_contract_test.go create mode 100644 basic/ephemeris_instance_test.go create mode 100644 basic/ephemeris_test.go create mode 100644 basic/event_baseline_golden_test.go create mode 100644 basic/heliacal.go create mode 100644 basic/heliacal_test.go create mode 100644 basic/iau2006.go create mode 100644 basic/iau2006_test.go create mode 100644 basic/limb_limits.go create mode 100644 basic/limb_tolerance.go create mode 100644 basic/lunar_eclipse_ephemeris_instance.go create mode 100644 basic/lunar_eclipse_ephemeris_instance_test.go create mode 100644 basic/lunar_eclipse_limb.go create mode 100644 basic/lunar_limb.go create mode 100644 basic/lunar_limb_bench_test.go create mode 100644 basic/lunar_limb_contact_test.go create mode 100644 basic/lunar_limb_test.go create mode 100644 basic/moon_ephemeris_instance.go create mode 100644 basic/moon_phase_ephemeris_instance.go create mode 100644 basic/occultation_limb.go create mode 100644 basic/occultation_limb_range_test.go create mode 100644 basic/occultation_star_limb.go create mode 100644 basic/occultation_star_limb_test.go create mode 100644 basic/orbital_events.go create mode 100644 basic/orbital_events_test.go create mode 100644 basic/planet_apparent_vector_test.go create mode 100644 basic/planet_ephemeris_instance.go create mode 100644 basic/planet_magnitude_2018.go create mode 100644 basic/planet_magnitude_2018_test.go create mode 100644 basic/planet_transit_local.go create mode 100644 basic/planet_transit_local_test.go create mode 100644 basic/rise_set_altitude.go create mode 100644 basic/rise_set_altitude_test.go create mode 100644 basic/rise_set_weather.go create mode 100644 basic/rise_set_weather_points.go create mode 100644 basic/rise_set_weather_test.go create mode 100644 basic/saturn_ring_ephemeris_instance.go create mode 100644 basic/sky_events.go create mode 100644 basic/sky_events_test.go create mode 100644 basic/solar_eclipse_limb.go create mode 100644 basic/solar_eclipse_magnitude_interp_test.go create mode 100644 basic/solar_eclipse_partial_limb.go create mode 100644 basic/solar_terms_bounds_test.go create mode 100644 basic/solar_terms_ephemeris_instance.go create mode 100644 basic/star_catalog_format_test.go create mode 100644 basic/sun_ephemeris_instance.go create mode 100644 basic/sun_moon_alias_ephemeris_instance.go create mode 100644 basic/sun_observation_ephemeris_instance.go create mode 100644 basic/testdata/semianalytic/jupiter_event_baseline.json create mode 100644 basic/testdata/semianalytic/lunar_eclipse_chauvenet.json create mode 100644 basic/testdata/semianalytic/mars_event_baseline.json create mode 100644 basic/testdata/semianalytic/mercury_event_baseline.json create mode 100644 basic/testdata/semianalytic/neptune_event_baseline.json create mode 100644 basic/testdata/semianalytic/occultation_limit_anchors.json create mode 100644 basic/testdata/semianalytic/planet_apparent_de441.csv create mode 100644 basic/testdata/semianalytic/saturn_event_baseline.json create mode 100644 basic/testdata/semianalytic/uranus_event_baseline.json create mode 100644 basic/testdata/semianalytic/venus_event_baseline.json create mode 100644 basic/time_labels.go create mode 100644 basic/topocentric_horizontal.go create mode 100644 calendar/parse_zone_test.go create mode 100644 context.go create mode 100644 context_example_test.go create mode 100644 context_internal_test.go create mode 100644 context_test.go create mode 100644 doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile-en.svg create mode 100644 doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile.svg create mode 100644 doc/img/transit-mercury-beijing-2032-en.svg create mode 100644 doc/img/transit-mercury-beijing-2032-global-en.svg create mode 100644 doc/img/transit-mercury-beijing-2032-global.svg create mode 100644 doc/img/transit-mercury-beijing-2032.svg create mode 100644 doc/img/transit-venus-beijing-2012-en.svg create mode 100644 doc/img/transit-venus-beijing-2012-global-en.svg create mode 100644 doc/img/transit-venus-beijing-2012-global.svg create mode 100644 doc/img/transit-venus-beijing-2012.svg create mode 100644 doc/manual/en/ephemeris.md create mode 100644 doc/manual/en/phenomena.md create mode 100644 doc/manual/ephemeris.md create mode 100644 doc/manual/phenomena.md create mode 100644 eclipse/api_default.go create mode 100644 eclipse/context.go create mode 100644 eclipse/context_test.go create mode 100644 eclipse/lunar_ephemeris_instance.go create mode 100644 eclipse/lunar_ephemeris_instance_test.go create mode 100644 eclipse/lunar_limb_test.go rename eclipse/{saros_table_extended.go => saros_table_extended_legacy.go} (98%) create mode 100644 eclipse/saros_table_extended_semianalytic.go create mode 100644 eclipse/svg/eclipse_limb_svg_test.go create mode 100644 eclipse/svg/solar_map_limb_panel_test.go create mode 100644 eclipse/svg/testdata/semianalytic/lunar_geocentric_block.json create mode 100644 eclipse/svg/testdata/semianalytic/lunar_penumbral_phase_digest.json create mode 100644 eclipse/svg/transit_local.go create mode 100644 eclipse/svg/transit_local_test.go create mode 100644 eclipse/svg/transit_map.go create mode 100644 eclipse/svg/transit_map_test.go create mode 100644 eclipse/testdata/semianalytic/local_lunar_chauvenet_available.json create mode 100644 eclipse/testdata/semianalytic/local_solar_contact_points.json create mode 100644 eclipse/testdata/semianalytic/lunar_chauvenet_available.json create mode 100644 eclipse/testdata/semianalytic/lunar_contact_points.json create mode 100644 ephemeris.go create mode 100644 ephemeris_public_test.go create mode 100644 formula/earth_orientation.go create mode 100644 formula/earth_orientation_test.go create mode 100644 formula/extinction.go create mode 100644 formula/extinction_test.go create mode 100644 formula/refraction.go create mode 100644 formula/refraction_test.go create mode 100644 geojson/api_default.go create mode 100644 geojson/context.go create mode 100644 geojson/context_test.go create mode 100644 geojson/eclipse_limb_test.go rename geojson/{review15_contract_test.go => marshal_contract_test.go} (89%) create mode 100644 geojson/regulus_20261007_regression_test.go rename geojson/{review15_coverage_internal_test.go => solar_central_band_coverage_internal_test.go} (70%) rename geojson/{review15_limits_test.go => solar_eclipse_limit_line_test.go} (70%) rename geojson/{review15_benchmark_test.go => solar_eclipse_marshal_benchmark_test.go} (76%) create mode 100644 geojson/testdata/semianalytic/solar_eclipse_20120521_roots.json create mode 100644 geojson/transit.go create mode 100644 geojson/transit_test.go create mode 100644 internal/ephem/apparent.go create mode 100644 internal/ephem/apparent_generic.go create mode 100644 internal/ephem/body.go create mode 100644 internal/ephem/default_legacy.go create mode 100644 internal/ephem/default_semianalytic.go create mode 100644 internal/ephem/ephem.go create mode 100644 internal/ephem/ephem_test.go create mode 100644 internal/ephem/global_test.go create mode 100644 internal/ephem/nil_engine_test.go create mode 100644 internal/ephem/stack.go create mode 100644 internal/ephem/stack_test.go create mode 100644 internal/ephemtest/ephemtest.go create mode 100644 internal/ephemtest/values.go create mode 100644 internal/geodata/earth.go create mode 100644 internal/iau2006/data.go create mode 100644 internal/iau2006/iau2006.go create mode 100644 internal/iau2006/iau2006_test.go create mode 100644 internal/iau2006/reference_data_test.go create mode 100644 internal/limbnote/limbnote.go create mode 100644 internal/limbnote/limbnote_test.go create mode 100644 internal/ltp/basis.go create mode 100644 internal/ltp/basis_test.go create mode 100644 internal/ltp/ltp.go create mode 100644 internal/refraction/refraction.go create mode 100644 internal/refraction/refraction_test.go create mode 100644 jupiter/api_default.go create mode 100644 jupiter/context.go create mode 100644 jupiter/context_test.go create mode 100644 jupiter/rise_set_atmosphere.go create mode 100644 kml/transit_test.go create mode 100644 lite/internal/horizon.go create mode 100644 lite/internal/horizon_test.go create mode 100644 lite/internal/refraction.go create mode 100644 lite/internal/time.go create mode 100644 lite/internal/time_test.go create mode 100644 mars/api_default.go create mode 100644 mars/context.go create mode 100644 mars/context_test.go create mode 100644 mars/rise_set_atmosphere.go create mode 100644 mars/rise_set_atmosphere_test.go create mode 100644 mercury/api_default.go create mode 100644 mercury/context.go create mode 100644 mercury/context_test.go create mode 100644 mercury/rise_set_atmosphere.go create mode 100644 moon/api_default.go create mode 100644 moon/context.go create mode 100644 moon/context_test.go create mode 100644 moon/doc.go create mode 100644 moon/limb/NOTICE create mode 100644 moon/limb/kaguya_lalt.bin create mode 100644 moon/limb/limb.go create mode 100644 moon/limb/limb_test.go create mode 100644 moon/limbtable/limbtable.go create mode 100644 moon/occultation_limb_graze_test.go create mode 100644 moon/occultation_limb_test.go create mode 100644 moon/rise_set_altitude.go create mode 100644 moon/rise_set_altitude_test.go create mode 100644 moon/svg/occultation_limb_custom_test.go create mode 100644 moon/svg/occultation_limb_diagram_test.go create mode 100644 moon/svg/occultation_limb_profile.go create mode 100644 moon/svg/occultation_limb_profile_test.go create mode 100644 moon/svg/testdata/semianalytic/occultation_detailed_panel.json create mode 100644 neptune/api_default.go create mode 100644 neptune/context.go create mode 100644 neptune/context_test.go create mode 100644 neptune/rise_set_atmosphere.go create mode 100644 neptune/testdata/semianalytic/neptune.json create mode 100644 orbit/propagation.go create mode 100644 orbit/propagation_fix_test.go create mode 100644 phenomena/altitude.go create mode 100644 phenomena/api_default.go create mode 100644 phenomena/orbital_zone_test.go create mode 100644 phenomena/phenomena.go create mode 100644 phenomena/phenomena_test.go create mode 100644 phenomena/star_coordinate.go create mode 100644 phenomena/star_coordinate_test.go create mode 100644 planet/internal/semianalytic/apparent.go create mode 100644 planet/internal/semianalytic/apparent_generic_test.go create mode 100644 planet/internal/semianalytic/apparent_moon_test.go create mode 100644 planet/internal/semianalytic/guard_test.go create mode 100644 planet/internal/semianalytic/lunar/default.go create mode 100644 planet/internal/semianalytic/lunar/fast.bin create mode 100644 planet/internal/semianalytic/lunar/lunar.bin create mode 100644 planet/internal/semianalytic/lunar/lunar.go create mode 100644 planet/internal/semianalytic/lunar/lunar_test.go create mode 100644 planet/internal/semianalytic/lunar/testdata/fast.csv create mode 100644 planet/internal/semianalytic/lunar/testdata/vectors.csv create mode 100644 planet/internal/semianalytic/ofdate.go create mode 100644 planet/internal/semianalytic/planets/default.go create mode 100644 planet/internal/semianalytic/planets/fast.bin create mode 100644 planet/internal/semianalytic/planets/long.bin create mode 100644 planet/internal/semianalytic/planets/modern.bin create mode 100644 planet/internal/semianalytic/planets/planets.go create mode 100644 planet/internal/semianalytic/planets/planets_test.go create mode 100644 planet/internal/semianalytic/planets/pln3.go create mode 100644 planet/internal/semianalytic/planets/testdata/fast.csv create mode 100644 planet/internal/semianalytic/planets/testdata/long.csv create mode 100644 planet/internal/semianalytic/planets/testdata/modern.csv create mode 100644 planet/internal/semianalytic/state.go create mode 100644 planet/internal/semianalytic/state_test.go create mode 100644 planet/internal/semianalytic/testdata/de441-states.csv create mode 100644 planet/model.go create mode 100644 planet/moon.go create mode 100644 planet/moon_model.go create mode 100644 planet/semianalytic_test.go create mode 100644 planet/spk/apparent_test.go create mode 100644 planet/spk/guard_test.go create mode 100644 planet/spk/spk.go create mode 100644 planet/spk/spk_test.go create mode 100644 planet/spk/testdata/ceres.bsp create mode 100644 planet/spk/testdata/de441_ref.csv create mode 100644 saturn/api_default.go create mode 100644 saturn/context.go create mode 100644 saturn/context_test.go create mode 100644 saturn/rise_set_atmosphere.go create mode 100644 star/catalog/badfile_test.go create mode 100644 star/catalog/catalog.go create mode 100644 star/catalog/catalog_test.go create mode 100644 star/catalog/cds.go create mode 100644 star/catalog/cross_test.go create mode 100644 star/catalog/csv.go create mode 100644 star/catalog/doc.go create mode 100644 star/catalog/names.go create mode 100644 star/catalog/near_test.go create mode 100644 star/catalog/parse.go create mode 100644 star/catalog/smoke_test.go create mode 100644 star/catalog/stellarium.go create mode 100644 star/catalog/testdata/bsc5_sample.dat create mode 100644 star/catalog/testdata/cross-id.cat create mode 100644 star/catalog/testdata/extra_name.fab create mode 100644 star/catalog/testdata/hip2_sample.dat create mode 100644 star/catalog/testdata/name.fab create mode 100644 star/catalog/testdata/stars_0_0v0_18.cat create mode 100644 star/catalog/types.go create mode 100644 star/rise_set_atmosphere.go create mode 100644 star/rise_set_atmosphere_test.go create mode 100644 sun/api_default.go create mode 100644 sun/atmosphere_test.go create mode 100644 sun/context.go create mode 100644 sun/context_test.go create mode 100644 sun/rise_set_altitude.go create mode 100644 sun/rise_set_altitude_test.go create mode 100644 uranus/api_default.go create mode 100644 uranus/context.go create mode 100644 uranus/context_test.go create mode 100644 uranus/rise_set_atmosphere.go create mode 100644 venus/api_default.go create mode 100644 venus/context.go create mode 100644 venus/context_test.go create mode 100644 venus/rise_set_atmosphere.go diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..3e16fc3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,160 @@ +本文件是第三方数据与条款声明。代码与文档采用 Apache-2.0;下列数据文件**不适用** Apache-2.0,各自遵循其来源条款。分发内嵌了这些数据的二进制(含 c-shared DLL/SO)时,须一并附上对应署名。 +This file is the per-component licence and attribution notice. The code and documentation are Apache-2.0; the data files listed below are not, and each follows the terms of its own source. Distributing a binary that embeds them — including the c-shared DLL/SO build — requires carrying the corresponding attribution. + +================================================================================ +1. SELENE (KAGUYA) lunar limb profile — moon/limb/kaguya_lalt.bin +================================================================================ + +Source data + SELENE Global Grid Topographic Data of the Moon (LALT_GGT_NUM), dataset + sln-l-lalt-5-topo-ggt-num-v2.0, version 2.0, published 2017. + Origin of the data: ISAS/JAXA. + Provided by the SELENE (KAGUYA) Data Archive (DARTS), ISAS/JAXA: + https://darts.isas.jaxa.jp/datasets/darts:sln-l-lalt-5-topo-ggt-num-v2.0 + https://data.darts.isas.jaxa.jp/pub/pds3/sln-l-lalt-5-topo-ggt-num-v2.0/ + +Acknowledgement, as requested by the SELENE Data Archive + "We thank the SELENE(KAGUYA) LALT team and the SELENE Data Archive for + providing the SELENE(KAGUYA) data. SELENE is a Japanese mission developed + and operated by JAXA." + +Modifications + The original PDS3 fixed-width table (1/16 degree global grid, 2880 x 5760 + samples) was converted to a float32 elevation grid and then resampled into a + limb-radius profile table: the limb height is tabulated on a 1 degree grid of + selenographic (libration) longitude and latitude and on a 0.1 degree grid of + position angle, taking max (R + h) cos(alpha) over a +/-9 degree arc for each + direction. Values are quantized to 4 m, delta-encoded along longitude and + latitude, and then stored in this project's own zlib-compressed LLB1 + container. No other changes were made. + +Licence + The source data are provided under the ISAS Data Policy, + https://www.isas.jaxa.jp/en/researchers/data-policy/ + which states that its rules are compatible with Creative Commons Attribution + 4.0 International, https://creativecommons.org/licenses/by/4.0/ + This file is redistributed under the ISAS Data Policy. It is provided "as + is", without warranties of any kind. ISAS/JAXA is not responsible for the + actions of data users and does not endorse this project. + +Embedding + Compiled into the moon/limb package with //go:embed, and linked only when + that package is imported. A copy of this section ships as moon/limb/NOTICE + so that vendored copies of the package keep the attribution. + +================================================================================ +2. Planetary and lunar theories — planet/internal/semianalytic/**/*.bin +================================================================================ + +Upstream theories and authors + VSOP2013 — planetary theory, Simon, Francou, Fienga & Manche (2013), + distributed by IMCCE at https://ftp.imcce.fr/pub/ephem/planets/vsop2013/ + TOP2013 — planetary theory for the outer planets, IMCCE, distributed at + https://ftp.imcce.fr/pub/ephem/planets/top2013/ + ELP/MPP02 — lunar theory, J. Chapront, M. Chapront & G. Francou, 2003, + distributed by SYRTE, Observatoire de Paris (not through IMCCE's ftp server) + at ftp://cyrano-se.obspm.fr/pub/2_lunar_solutions/2_elpmpp02/ + Its distribution README.TXT (December 2002) is purely scientific — authors, + description, file list and references — and states no licence and no terms + of use. + Used by the legacy model as well: + ELP2000-82B — earlier lunar solution, Chapront-Touzé & Chapront, distributed + by IMCCE at https://ftp.imcce.fr/pub/ephem/moon/elp82b/ + VSOP87 — earlier planetary series, Bretagnon & Francou (1988). + +What this project distributes + Its own numerical refits of those theories' argument structure to the JPL + DE440/DE441 ephemerides. The amplitudes in the *.bin tables are produced by + this project's fitting pipeline; the upstream coefficient files are not + redistributed here. Relative to the upstream theories the tables are + modified in two ways: (1) the argument structure is truncated to the terms + the fit retains, and (2) every amplitude is re-fitted to DE440/DE441 instead + of being taken from the published series. + +Published constants + The argument phases and frequencies (the VSOP2013 "ll(1..17)" set) and the + GM constants used by the generator are published in the VSOP2013 README.pdf + distributed by IMCCE and in Simon, Francou, Fienga & Manche, A&A 557, A49 + (2013); for the Moon, in Chapront & Francou, A&A 404, 735 (2003). + +Rights in the underlying theories + This project can neither allow nor forbid the use of the VSOP2013, TOP2013, + ELP/MPP02, ELP2000-82B or VSOP87 theories. Apache-2.0 covers only this + project's own work — its code, and its compilation and rearrangement of the + published series — and not the theories themselves. Downstream users who + need to rely on the terms of the underlying theories should consult the IMCCE + legal notice, + https://www.imcce.fr/mentions-legales + which restricts reproduction and makes commercial or professional use + subject to IMCCE authorisation. + +ELP/MPP02 terms + The official ELP/MPP02 distribution ships README.TXT (December 2002) and the + explanatory note elpmpp02.pdf; neither states a licence or terms of use, and + no separate terms file is included. This project therefore treats ELP/MPP02 + as carrying no stated terms and relies on the disclaimer under "Rights in + the underlying theories" above. Note also that IMCCE's ftp server carries + only ELP2000-82B, not ELP/MPP02. Downstream users who need a formal position + should consult SYRTE, https://syrte.obspm.fr/, and the IMCCE legal notice. + +================================================================================ +3. JPL ephemerides — DE440 / DE441 and Horizons kernels +================================================================================ + +Files + basic/testdata/semianalytic/planet_apparent_de441.csv + planet/internal/semianalytic/testdata/de441-states.csv + planet/spk/testdata/de441_ref.csv + planet/spk/testdata/ceres.bsp + +Source + JPL DE440/DE441 planetary ephemerides via NAIF, + https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/ + and JPL Horizons, https://ssd.jpl.nasa.gov/horizons/ + The DE440/DE441 data are also the fitting target of the tables in section 2. + +Terms + Produced by the United States Government (NASA/JPL); not subject to + copyright protection in the United States. NAIF and PDS ask that the source + be credited, as done here. + +================================================================================ +4. Natural Earth land polygons — internal/svgmap/land_*.path +================================================================================ + +Source + Natural Earth 1:50m physical land, without administrative boundaries, + https://www.naturalearthdata.com/ + +Terms + Public domain. Natural Earth states that the data may be used for any + purpose without permission. Credit is given here as good practice, and the + rendered charts carry the "Natural Earth 1:50m physical land" label. + +================================================================================ +5. Star catalogue data — basic/star_catalog.dat +================================================================================ + +Astrometry + The built-in catalogue carries Bright Star Catalogue / Harvard (HR) numbers + 1..9110 with J2000 positions, proper motions and magnitudes. Its astrometric + content derives from the Yale Bright Star Catalogue (Hoffleit & Warren), + distributed by CDS as V/50, + https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 + Catalogue data from CDS is distributed free of charge subject to the CDS + general terms of use and to citation of the source. + +================================================================================ +References +================================================================================ + + ISAS Data Policy ...... https://www.isas.jaxa.jp/en/researchers/data-policy/ + CC BY 4.0 ............. https://creativecommons.org/licenses/by/4.0/ + SELENE Data Archive ... https://darts.isas.jaxa.jp/ + IMCCE legal notice .... https://www.imcce.fr/mentions-legales + Natural Earth ......... https://www.naturalearthdata.com/ + NAIF SPK kernels ...... https://naif.jpl.nasa.gov/pub/naif/generic_kernels/ + JPL Horizons .......... https://ssd.jpl.nasa.gov/horizons/ + CDS VizieR V/50 ....... https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50 + SYRTE (Observatoire de Paris) ... https://syrte.obspm.fr/ + ELP/MPP02 distribution .......... ftp://cyrano-se.obspm.fr/pub/2_lunar_solutions/2_elpmpp02/ diff --git a/README.en.md b/README.en.md index 10b3765..9e394cb 100644 --- a/README.en.md +++ b/README.en.md @@ -6,9 +6,20 @@ An astronomy library I have used for years for my own interest in astronomy and calendars. -Based on Jean Meeus's *Astronomical Algorithms*. Solar and planetary calculations use built-in VSOP87 terms; lunar calculations use ELP2000/82-style truncated series. No external ephemeris files are required. +> ### ⚠️ Time-scale change: UTC switches to leap hours on 2027-05-20 +> +> On 15 October 2026 the 28th General Conference on Weights and Measures (CGPM) adopted [Resolution 5, "On the technical actions needed to ensure the continuity of UTC"](https://www.bipm.org/en/cgpm-2026/resolution-5) (`Resolution-CGPM-28-5`, DOI `10.59161/CGPM2026RES5E`): **continuous UTC becomes effective on 20 May 2027** and the maximum value of `|UT1−UTC|` is widened from 0.9 seconds to **3600 seconds (1 hour)** — a leap hour in place of a leap second. +> +> [IERS Bulletin C 72 (2026-07-06)](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) confirms that **no leap second will be introduced at the end of December 2026**; the leap-second mechanism ends on 2027-05-20, so the possible insertion point at the end of June 2027 will not carry one either. +> +> From this release the library keeps **1972–2026 on the definitive leap-second table** (those historical values are unchanged) and changes the default policy beyond the built-in window (which ends 2026-10-01) from `TimeScaleLeapSecond` to **`TimeScaleLeapHour`**; past the window TT−UTC stays at 69.184 seconds. The numeric values of the `SetTimeScaleFuturePolicy` constants changed accordingly (`TimeScaleLeapHour` is now the zero value), so callers passing raw integers must update. See [Time scales](doc/manual/en/timescale.md). -Intended for calendar calculations, amateur observation and studying astronomical algorithms. +The library ships two ephemerides, selectable in one process or used side by side: + +1. **Semianalytic model** (`ModelSemianalytic`, default): the argument structure and initial amplitudes of VSOP2013/TOP2013 (planets) and ELP/MPP02 (Moon), refitted to the DE ephemerides — DE441 for the planetary long range, DE440 for the modern segment and DE441 for the Moon — and folded into a single compact series table. The planetary table is about 452 KiB and the lunar table about 42 KiB, with no external ephemeris files. Its geocentric apparent direction error (RMS) in the modern range is about 0.007″ (Sun) and 0.076″ (Moon). +2. **Legacy model** (`ModelLegacy`): the VSOP87/ELP2000-82 series with added long-term and per-body corrections. Switch to it to reproduce old values; its geocentric apparent direction error (RMS) in the modern range is about 0.04″ (Sun) and 0.75″ (Moon). See [Accuracy and Performance](doc/manual/en/accuracy.md). + +External ephemerides can also be supplied through SPK or a custom engine. ## Install @@ -19,15 +30,17 @@ go get b612.me/astro ## Highlights - Civil and Chinese calendar conversion from 721 BCE to 3000 CE, solar terms, sexagenary dates, era names and historical calendars. -- Positions, rise/set, transit, distances, angular diameters and physical ephemerides for the Sun, Moon and seven planets; lightweight Sun and Moon algorithms are also available. -- Solar and lunar eclipses, stellar and planetary lunar occultations, local contacts and global paths. -- SVG charts, GeoJSON data and KML for viewing and time playback in Google Earth. -- A built-in catalog of 9,100 stars, with constellation lookup, coordinate corrections and rise/set calculations. -- Coordinate transformations, sidereal time, precession, nutation, refraction, small-body orbits, sundials and astronomical formulas. +- Positions, rise/set, transit, distance, apparent diameter and physical ephemerides for the Sun, Moon and seven planets; lightweight Sun and Moon algorithms are also included. +- Solar and lunar eclipses, stellar and planetary lunar occultations, event searches, local contact times and global paths. +- Generic sky-event searches: ecliptic-longitude crossings, stationary points, phases, closest approach, rise/set at any altitude, perihelion/aphelion and heliacal visibility. +- Switchable ephemeris models, multiple instances used in parallel in one process, SPK multi-source stacking and optional perturbed propagation. +- SVG sky-event charts, GeoJSON geographic data and KML for viewing and time playback in Google Earth. +- A built-in catalog of 9100 stars with constellation lookup, coordinate corrections and rise/set calculations; CSV, Hipparcos, BSC5 and Stellarium external catalogs are also available. +- Celestial coordinate transformations, sidereal time, precession and nutation, refraction, astronomical formulas, sundial geometry and an optional lunar-limb terrain correction. ## Example -Calculate sunrise in Xi'an, the lunar phase and the Chinese calendar date: +Calculate the sunrise time, lunar phase and Chinese calendar date for a day in Xi'an: ```go package main @@ -61,49 +74,76 @@ func main() { } ``` -Longitude and latitude are in degrees, positive east and north. The example uses a height of 0 metres and enables refraction with `true`. Rise/set functions return errors for polar conditions or a missing event on that date. +Longitude and latitude are in degrees, positive east and north; the example uses a height of 0 metres and `true` makes the rise/set calculation account for atmospheric refraction. Rise/set functions return an error during polar day, polar night or when no event occurs on that date. -Calendar and phase descriptions in this example are returned in Chinese. +Package-level functions use the process-wide ephemeris model (semianalytic in default builds). To pin a model for a calculation, use `astro.NewContext`; multiple contexts can be used in parallel in one process: + +```go +func compareModels(date time.Time) { + legacy, err := astro.NewContext(astro.ModelLegacy) + if err != nil { + panic(err) + } + modern, err := astro.NewContext(astro.ModelSemianalytic) + if err != nil { + panic(err) + } + + fmt.Println(legacy.Moon().PhaseDesc(date), modern.Moon().PhaseDesc(date)) + + riseLegacy, _ := legacy.Sun().RiseTime(date, 108.93, 34.27, 0, true) + riseModern, _ := modern.Sun().RiseTime(date, 108.93, 34.27, 0, true) + fmt.Println(riseLegacy.Format("15:04:05"), riseModern.Format("15:04:05")) +} +``` + +A context exposes per-module entry points such as `Sun()`, `Moon()`, `Mars()`, `Eclipse()` and `Phenomena()`, with the same signatures as their package-level counterparts. Models isolate the ephemeris only; ΔT, TT−UTC and civil time-scale policy remain process-wide settings. See [Explicit ephemeris instances](doc/manual/en/ephemeris.md) for custom engines, SPK and multi-source stacking. ## Package Overview | Package | Purpose and documentation | | --- | --- | | `calendar` | [Calendar conversion, solar terms, sexagenary dates and historical calendars](doc/manual/en/calendar.md) | -| `sun` / `moon` / `lite/sun` / `lite/moon` | [Sun and Moon positions, rise/set, phases and physical quantities](doc/manual/en/sun-moon.md) | -| `mercury` / `venus` / `mars` / `jupiter` / `saturn` / `uranus` / `neptune` / `earth` | [Planetary positions, events, Jupiter's satellites and Saturn's rings](doc/manual/en/planets.md) | -| `eclipse` / `eclipse/svg` | [Eclipse searches, local visibility, paths and charts](doc/manual/en/eclipse.md) | -| `moon` / `moon/svg` | [Stellar and planetary lunar occultations, contacts and bands](doc/manual/en/occultation.md) | +| `sun` / `moon` / `lite/sun` / `lite/moon` | [Sun and Moon positions, rise/set, lunar phases and physical quantities](doc/manual/en/sun-moon.md) | +| `mercury` / `venus` / `mars` / `jupiter` / `saturn` / `uranus` / `neptune` / `earth` | [Planetary positions, special events, Jupiter's satellites and Saturn's rings](doc/manual/en/planets.md) | +| `eclipse` / `eclipse/svg` | [Solar and lunar eclipse and transit searches, local visibility, paths and charts](doc/manual/en/eclipse.md) | +| `moon` / `moon/svg` / `moon/limb` | [Stellar and planetary lunar occultations, contact times, bands and the optional lunar-limb terrain correction](doc/manual/en/occultation.md) | +| `phenomena` | [Ecliptic-longitude crossings, stationary points, phases, closest approach, heliacal visibility and rise/set at any altitude for any two bodies](doc/manual/en/phenomena.md) | | `geojson` / `kml` | [Event maps, GeoJSON and KML](doc/manual/en/map-geojson.md) | -| `star` | [Star catalog, constellations and observing quantities](doc/manual/en/star.md) | -| `coord` | [Coordinates, sidereal time, precession, nutation and refraction](doc/manual/en/coord.md) | -| `orbit` | [Asteroid and comet two-body orbits, visual binaries](doc/manual/en/orbit.md) | +| `star` / `star/catalog` | [Star database, constellations, observing quantities and external catalogs](doc/manual/en/star.md#external-catalogs) | +| `coord` | [Coordinate conversion, sidereal time, precession, nutation and refraction](doc/manual/en/coord.md) | +| `orbit` | [Asteroid and comet two-body or optional perturbed propagation, visual binaries](doc/manual/en/orbit.md) | | `sundial` | [Apparent solar time and sundial geometry](doc/manual/en/sundial.md) | -| `formula` | [Radiation, magnitudes, synodic periods and telescope formulas](doc/manual/en/formula.md) | -| `astro` (root) | [UTC, UT1, TT and ΔT models](doc/manual/en/timescale.md) | +| `formula` | [Radiation, magnitudes, telescopes, atmospheric refraction, extinction and Earth-orientation tools](doc/manual/en/formula.md) | +| `astro` / `basic` / `planet/spk` | [Ephemeris models and instances, multi-source ephemerides and custom engines](doc/manual/en/ephemeris.md) | +| `astro` (root) | [UTC, UT1, TT conversion and ΔT models](doc/manual/en/timescale.md) | -`basic` contains the underlying algorithms, `planet` holds analytical series, and `tools` provides numerical helpers. Most applications can use the packages above. - -Full signatures are also available in the [Go API documentation](https://pkg.go.dev/b612.me/astro). +`basic` contains the underlying algorithms, `planet` holds the built-in models and series, `planet/spk` reads external kernels, and `tools` provides numerical helpers. See [Accuracy and performance](doc/manual/en/accuracy.md) for model composition, measured errors and performance; full signatures are also available in the [Go API documentation](https://pkg.go.dev/b612.me/astro). ## Time Scale Conventions -Most observing APIs accept a civil instant as `time.Time` and handle UTC, UT1 and TT conversions internally. Before 1972 the library treats civil time as UT1, and as UTC thereafter. Orbital epochs and some low-level APIs have separate scale requirements. +Most observing APIs accept a civil instant as `time.Time` and handle UTC, UT1 and TT conversions internally. Before 1972 the library treats civil time as UT1, and as UTC thereafter; orbital epochs and some low-level APIs have separate time-scale requirements. -Angles default to degrees, angular diameters to arcseconds, sidereal time to hours, and distances to AU or km as specified by the API. SVG and GeoJSON time labels default to UTC, with explicit UT1 options. +Angles default to degrees, apparent diameters to arcseconds, sidereal time to hours, and distances to AU or km as specified by the API. SVG and GeoJSON time labels default to UTC, with an explicit UT1 option. See [Time scales](doc/manual/en/timescale.md) for the exact conventions. -See [Time scales](doc/manual/en/timescale.md). - -DUT1 and TT−UTC default to extrapolating the current leap-second rule beyond the observed window (through September 2026); `SetTimeScaleFuturePolicy` switches the TT−UTC convention to one of four alternatives: a frozen offset, continued UT1 tracking, the leap-hour rule, or a civil scale permanently identical to UT1. +Beyond the monthly ΔT table, DUT1 and TT−UTC default to **leap-hour** extrapolation (`TimeScaleLeapHour`, matching the continuous UTC effective 2027-05-20); `SetTimeScaleFuturePolicy` switches between `TimeScaleLeapHour` (default), `TimeScaleLeapSecond`, `TimeScaleAssumeUT1Tracking`, `TimeScaleFreezeUTCOffset` and `TimeScaleUT1Civil`. ## Observer Height Convention -Observer height is ellipsoidal height in metres. Convert orthometric height `H` using the local geoid undulation `N`: `height = H + N`. See [Observer height](doc/manual/en/coord.md#observer-height). +Observer height is ellipsoidal height in metres. If only orthometric height `H` is available, convert it with the local geoid undulation `N`: `height = H + N`. See [Observer height](doc/manual/en/coord.md#observer-height). ## Scope And Accuracy -The library uses analytical models and truncated series. Accuracy varies with date, body and calculation. Professional occultation predictions and spacecraft navigation require more precise ephemerides and physical models. +Accuracy varies with model, epoch, body and calculated quantity. The default model has changed from VSOP87/ELP2000-82 to the semianalytic tables: signatures are unchanged but values may differ. `lite/sun` and `lite/moon` use independent approximation chains and do not follow model switches. -`lite/sun` and `lite/moon` suit resource-constrained applications. Some main APIs also provide truncated variants with an `N` suffix. +See [Accuracy and performance](doc/manual/en/accuracy.md) for model composition, error conventions and measured results. Professional occultation prediction, spacecraft navigation and similar uses also need ephemerides, time scales and physical models of matching accuracy; loading an SPK alone does not make every high-level result as accurate as the kernel. -Model ranges, measured differences and benchmarks are documented under [Accuracy and performance](doc/manual/en/accuracy.md). +## License + +The code and documentation are licensed under [Apache-2.0](LICENSE). The third-party data embedded in this repository is **not** covered by Apache-2.0 and follows the terms of its own source; see [NOTICE](NOTICE) for the item-by-item statement: + +- **SELENE (KAGUYA) LALT lunar limb profile** (`moon/limb/kaguya_lalt.bin`): ISAS/JAXA, distributed under the ISAS Data Policy, which states that its rules are compatible with CC BY 4.0. +- **This project's own refits of VSOP2013/TOP2013 and ELP/MPP02** (`planet/internal/semianalytic/`): the underlying theories are IMCCE / SYRTE. This project can neither grant nor restrict rights in those theories; Apache-2.0 applies only to this project's own code and compilation. +- **JPL DE440/DE441 and Horizons data** (`*/testdata/`, `planet/spk/testdata/`): NASA/JPL. +- **Astrometry in the built-in star catalogue** (`basic/star_catalog.dat`): derived from the Yale Bright Star Catalogue (VizieR [V/50](https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50)). +- **Natural Earth 1:50m physical land** (`internal/svgmap/land_*.path`): public domain. diff --git a/README.md b/README.md index 4e4ce4f..8a36856 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,20 @@ 自用多年的天文算法库,用于个人天文历法爱好。 -基于 Jean Meeus《天文算法》(Astronomical Algorithms)实现。太阳和行星使用内置 VSOP87 解析项,月球使用 ELP2000/82 风格截断级数,不依赖外部星历文件。适合历法计算、业余观测和天文算法学习。 +> ### ⚠️ 时标变更:2027-05-20 起 UTC 改用闰时 +> +> 2026 年 10 月 15 日,第 28 届国际计量大会(CGPM)通过[第 5 号决议《关于确保 UTC 连续性所需的技术行动》](https://www.bipm.org/en/cgpm-2026/resolution-5)(`Resolution-CGPM-28-5`,DOI `10.59161/CGPM2026RES5E`):**连续 UTC 自 2027 年 5 月 20 日起生效**,`|UT1−UTC|` 的最大值由 0.9 秒放宽到 **3600 秒(1 小时)**,即以闰时取代闰秒。 +> +> [IERS Bulletin C 72(2026-07-06)](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) 已确认 **2026 年 12 月底不插入闰秒**;闰秒机制在 2027-05-20 终止,2027 年 6 月底这一可能的插入点同样不会有闰秒。 +> +> 本库从本版本起:**1972–2026 仍按确定的闰秒表计算**,历史值不变;内置窗口(截至 2026-10-01)之后的默认政策由 `TimeScaleLeapSecond` 改为 **`TimeScaleLeapHour`**,窗口之后 TT−UTC 将长期保持 69.184 秒。`SetTimeScaleFuturePolicy` 的常量数值随之重排(`TimeScaleLeapHour` 现为零值),传裸整数的调用方需要更新。详见[时标](doc/manual/timescale.md)。 + +本库内置两套星历,可在同一进程内切换或并行使用: + +1. **半解析模型**(`ModelSemianalytic`,默认):以 VSOP2013/TOP2013(行星)和 ELP/MPP02(月球)为基准,按 DE 星历重新拟合——行星长时段用 DE441、现代段用 DE440,月球用 DE441——折叠成一张紧凑级数表。行星表约 452 KiB、月球表约 42 KiB,无需外部星历文件。现代段地心视方向均方根误差约 0.007″(太阳)、0.076″(月球)。 +2. **旧模型**(`ModelLegacy`):VSOP87/ELP2000-82 级数,另加长期项与逐天体订正。需要复现旧版数值时切换;现代段地心视方向均方根误差约 0.04″(太阳)、0.75″(月球),详见[精度与性能](doc/manual/accuracy.md)。 + +除内置模型外,也可以通过 SPK 或自定义引擎接入外部星历。 ## 安装 @@ -19,9 +32,11 @@ go get b612.me/astro - 公历与农历互转,支持公元前 721 年至公元 3000 年;节气、干支、年号与古历。 - 太阳、月亮、七大行星的位置、升落、中天、距离、视直径与物理星历;另有轻量日月算法。 - 日月食、恒星与行星月掩的事件搜索、地方接触时刻和全球路径。 +- 通用天象搜索:黄经穿越、留、相位、最近角距、任意高度升落、近远日点与偕日可见。 +- 可切换星历模型、同一进程并行使用多个实例,支持 SPK 多源叠加与可选摄动传播。 - SVG 天象图、GeoJSON 地理数据和可供 Google Earth 查看及按时间播放的 KML。 -- 内置 9100 颗恒星,支持星座判定、坐标修正与升落计算。 -- 天球坐标转换、恒星时、岁差章动、折射、通用小天体轨道、日晷和常用天文公式。 +- 内置 9100 颗恒星,支持星座判定、坐标修正与升落计算;另有 CSV、Hipparcos、BSC5 与 Stellarium 外挂星表。 +- 天球坐标转换、恒星时、岁差章动、折射、天文公式、日晷几何与可选月缘地形修正。 ## 使用示例 @@ -61,6 +76,29 @@ func main() { 经纬度以东经、北纬为正,单位为度;示例高度为 0 米,`true` 表示升落计算考虑大气折射。极昼、极夜或当天没有升落事件时,升落接口会返回错误。 +包级函数使用进程当前的星历模型(默认构建为半解析)。要给某次计算固定模型,用 `astro.NewContext`,同一进程里可以有多个上下文并行: + +```go +func compareModels(date time.Time) { + legacy, err := astro.NewContext(astro.ModelLegacy) + if err != nil { + panic(err) + } + modern, err := astro.NewContext(astro.ModelSemianalytic) + if err != nil { + panic(err) + } + + fmt.Println(legacy.Moon().PhaseDesc(date), modern.Moon().PhaseDesc(date)) + + riseLegacy, _ := legacy.Sun().RiseTime(date, 108.93, 34.27, 0, true) + riseModern, _ := modern.Sun().RiseTime(date, 108.93, 34.27, 0, true) + fmt.Println(riseLegacy.Format("15:04:05"), riseModern.Format("15:04:05")) +} +``` + +上下文按模块提供 `Sun()`、`Moon()`、`Mars()`、`Eclipse()`、`Phenomena()` 等入口,方法与同名包级函数签名一致。模型只隔离星历,ΔT、TT−UTC 和民用时标政策仍是进程级设置。自定义引擎、SPK 与多源叠加见[显式星历实例](doc/manual/ephemeris.md)。 + ## 包概览 | 包 | 用途与文档 | @@ -68,25 +106,27 @@ func main() { | `calendar` | [历法转换、节气、干支与古历](doc/manual/calendar.md) | | `sun` / `moon` / `lite/sun` / `lite/moon` | [太阳与月亮的位置、升落、月相和物理量](doc/manual/sun-moon.md) | | `mercury` / `venus` / `mars` / `jupiter` / `saturn` / `uranus` / `neptune` / `earth` | [行星位置、特殊天象、木星卫星与土星环](doc/manual/planets.md) | -| `eclipse` / `eclipse/svg` | [日月食查询、地方可见性、路径与出图](doc/manual/eclipse.md) | -| `moon` / `moon/svg` | [恒星及行星月掩、接触时刻与掩带](doc/manual/occultation.md) | +| `eclipse` / `eclipse/svg` | [日月食与凌日的查询、地方可见性、路径与出图](doc/manual/eclipse.md) | +| `moon` / `moon/svg` / `moon/limb` | [恒星及行星月掩、接触时刻、掩带与可选月缘地形修正](doc/manual/occultation.md) | +| `phenomena` | [任意两天体黄经穿越、留、相位、最近角距、偕日可见与任意高度升落](doc/manual/phenomena.md) | | `geojson` / `kml` | [天象地图、GeoJSON 与 KML](doc/manual/map-geojson.md) | -| `star` | [恒星数据库、星座与观测量](doc/manual/star.md) | +| `star` / `star/catalog` | [恒星数据库、星座、观测量与外挂星表](doc/manual/star.md#外挂星表) | | `coord` | [坐标转换、恒星时、岁差、章动与折射](doc/manual/coord.md) | -| `orbit` | [小行星、彗星等二体轨道与视双星](doc/manual/orbit.md) | +| `orbit` | [小行星、彗星的二体或可选摄动传播与视双星](doc/manual/orbit.md) | | `sundial` | [真太阳时与日晷几何](doc/manual/sundial.md) | -| `formula` | [辐射、星等、会合周期与望远镜公式](doc/manual/formula.md) | +| `formula` | [辐射、星等、望远镜、大气折射、消光与地球姿态工具](doc/manual/formula.md) | +| `astro` / `basic` / `planet/spk` | [星历模型与实例、多源星历与自定义引擎](doc/manual/ephemeris.md) | | `astro`(根包) | [UTC、UT1、TT 换算与 ΔT 模型](doc/manual/timescale.md) | -`basic` 是底层算法包,`planet` 保存解析级数,`tools` 提供数值辅助函数。一般使用上表中的包即可;完整函数签名也可查 [Go API 文档](https://pkg.go.dev/b612.me/astro)。 +`basic` 是底层算法包,`planet` 保存内置模型与级数,`planet/spk` 读取外部核,`tools` 提供数值辅助函数。模型构成、实测误差和性能见[精度与性能](doc/manual/accuracy.md);完整函数签名也可查 [Go API 文档](https://pkg.go.dev/b612.me/astro)。 ## 时标约定 -一般观测接口接收表示民用时刻的 `time.Time`,内部处理 UTC、UT1 与 TT 换算。1972 年以前,库将民用时间按 UT1 处理,之后按UTC处理;轨道历元和部分底层接口另有时标要求。 +一般观测接口接收表示民用时刻的 `time.Time`,内部处理 UTC、UT1 与 TT 换算。1972 年以前,库将民用时间按 UT1 处理,之后按 UTC 处理;轨道历元和部分底层接口另有时标要求。 角度默认用度,视直径用角秒,恒星时用小时,距离按接口使用 AU 或 km。SVG 和 GeoJSON 默认输出 UTC 时间标签,也可显式选择 UT1。具体约定见[时标手册](doc/manual/timescale.md)。 -DUT1 与 TT−UTC 在实测窗口(截至 2026 年 9 月)之后默认沿用现行闰秒规则外推,可用 `SetTimeScaleFuturePolicy` 调整 TT−UTC 口径到"冻结偏移量"、"继续跟随 UT1"、"闰时规则"、"持续等价UT1"四个规则。 +DUT1 与 TT−UTC 在月度 ΔT 表覆盖范围之后默认按**闰时**外推(`TimeScaleLeapHour`,对应 2027-05-20 起的连续 UTC),可用 `SetTimeScaleFuturePolicy` 在 `TimeScaleLeapHour`(默认)、`TimeScaleLeapSecond`、`TimeScaleAssumeUT1Tracking`、`TimeScaleFreezeUTCOffset`、`TimeScaleUT1Civil` 之间切换。 ## 观测点高度约定 @@ -94,6 +134,16 @@ DUT1 与 TT−UTC 在实测窗口(截至 2026 年 9 月)之后默认沿用 ## 适用范围与精度 -本库使用解析模型和截断级数。精度随年代、天体与计算项目变化;专业掩星、航天导航等用途需要更精确的星历和物理模型。 +精度随模型、年代、天体和计算项目变化。默认模型已从 VSOP87/ELP2000-82 换成半解析表,签名不变但数值可能不同;`lite/sun`、`lite/moon` 使用独立近似链路,不随模型切换。 -`lite/sun`、`lite/moon` 适合资源受限环境;部分主接口也提供带 `N` 后缀的截断版本。模型范围、误差样本和性能数据见[精度与性能](doc/manual/accuracy.md)。 +模型构成、误差口径和实测结果见[精度与性能](doc/manual/accuracy.md)。专业掩星、航天导航等用途还需要相应精度的星历、时标与物理模型,接入 SPK 本身不保证全部高层结果达到核文件精度。 + +## 许可 + +代码与文档采用 [Apache-2.0](LICENSE)。仓库内嵌的第三方数据**不适用** Apache-2.0,各自遵循其来源条款,逐项声明见 [NOTICE](NOTICE): + +- **SELENE(KAGUYA)LALT 月缘剖面**(`moon/limb/kaguya_lalt.bin`):ISAS/JAXA,按 ISAS 数据政策分发(该政策声明其规则与 CC BY 4.0 兼容)。 +- **VSOP2013/TOP2013 与 ELP/MPP02 的自行再拟合表**(`planet/internal/semianalytic/`):上游理论属 IMCCE / SYRTE。 Apache-2.0 只适用本库自己的代码与汇编。 +- **JPL DE440/DE441 与 Horizons 数据**(`*/testdata/`、`planet/spk/testdata/`):NASA/JPL。 +- **内置星表的天测数据**(`basic/star_catalog.dat`):源自 Yale Bright Star Catalogue(VizieR [V/50](https://cdsarc.cds.unistra.fr/viz-bin/cat/V/50))。 +- **Natural Earth 1:50m 物理陆地**(`internal/svgmap/land_*.path`):公有领域。 diff --git a/astro.go b/astro.go index c310a9c..a2220f4 100644 --- a/astro.go +++ b/astro.go @@ -1,4 +1,4 @@ -// Package astro 提供进程级时标配置与时标换算 / process-wide time-scale configuration and conversions. +// Package astro 提供高层星历上下文、模型切换与进程级时标配置 / high-level ephemeris contexts, model switching and process-wide time-scale configuration. package astro import "b612.me/astro/basic" @@ -33,18 +33,18 @@ func DefaultTTMinusUTC() func(float64) float64 { return basic.TTMinusUTCSecondsDefault } -// TimeScaleFuturePolicy 民用时标换算政策,与输出用的 TimeScale 无关 / civil-time conversion policy, separate from output TimeScale. +// TimeScaleFuturePolicy 民用时标换算政策,零值为闰时,与输出用的 TimeScale 无关 / civil-time conversion policy, leap hour as the zero value; separate from output TimeScale. type TimeScaleFuturePolicy = basic.TimeScaleFuturePolicy const ( - // TimeScaleLeapSecond 默认按 ΔT 越限施加整数秒校正,不代表闰秒公告 / default integer-second corrections driven by extrapolated ΔT, not announcements. + // TimeScaleLeapHour 按 ΔT 越限施加整小时校正(2027 起 CGPM 决议) / applies hour-sized corrections as adopted by CGPM from 2027. + TimeScaleLeapHour = basic.TimeScaleLeapHour + // TimeScaleLeapSecond 按 ΔT 越限施加整数秒校正(1972–2026 确定值) / integer-second corrections (definitive 1972–2026). TimeScaleLeapSecond = basic.TimeScaleLeapSecond // TimeScaleAssumeUT1Tracking 窗口外固定末端 DUT1,平滑跟随 UT1 / holds the last observed DUT1 beyond the window. TimeScaleAssumeUT1Tracking = basic.TimeScaleAssumeUT1Tracking - // TimeScaleFreezeUTCOffset 假设 UTC 偏移冻结在精确窗口末端 / freezes the UTC offset at the window end. + // TimeScaleFreezeUTCOffset 窗口外固定末端 TT−UTC / holds the last TT−UTC offset beyond the window. TimeScaleFreezeUTCOffset = basic.TimeScaleFreezeUTCOffset - // TimeScaleLeapHour 按 ΔT 越限施加整小时校正,仅作情景演算 / applies hour-sized corrections as a scenario assumption. - TimeScaleLeapHour = basic.TimeScaleLeapHour // TimeScaleUT1Civil 全时轴民用时标等同 UT1,TT−UTC 覆盖仍优先 / uses UT1 as civil time everywhere unless TT−UTC is overridden. TimeScaleUT1Civil = basic.TimeScaleUT1Civil ) diff --git a/baseline_regression_test.go b/baseline_regression_test.go index c58c6da..bc13056 100644 --- a/baseline_regression_test.go +++ b/baseline_regression_test.go @@ -13,6 +13,8 @@ import ( "b612.me/astro/moon" "b612.me/astro/planet" "b612.me/astro/sun" + + "b612.me/astro/internal/ephemtest" ) type baselinePlanetSnapshot struct { @@ -171,6 +173,7 @@ func assertTruncationConverges(t *testing.T, cases []truncationCase, samples int } func TestPlanetMoonBaselineRegression(t *testing.T) { + ephemtest.PinLegacy(t) samples := loadBaselineSamples(t) for _, sample := range samples { for _, body := range sample.Planets { @@ -239,6 +242,7 @@ func planetMoonTruncationCases(samples []baselineSample) []truncationCase { } func TestPlanetMoonTruncationConvergesToFullSeries(t *testing.T) { + ephemtest.PinLegacy(t) samples := loadBaselineSamples(t) assertTruncationConverges(t, planetMoonTruncationCases(samples), len(samples)) } @@ -257,6 +261,7 @@ func publicSunMoonTruncationCases(t *testing.T, samples []baselineSample) []trun } func TestPublicSunMoonTruncationConvergesToFullSeries(t *testing.T) { + ephemtest.PinLegacy(t) samples := loadBaselineSamples(t) assertTruncationConverges(t, publicSunMoonTruncationCases(t, samples), len(samples)) } diff --git a/basic/api_default.go b/basic/api_default.go new file mode 100644 index 0000000..8048031 --- /dev/null +++ b/basic/api_default.go @@ -0,0 +1,1794 @@ +package basic + +import ( + "time" +) + +// ClosestJupiterGalileanPhenomenonContactEvent 最近一次 IMCCE 风格接触事件 / closest IMCCE-style contact event. +func ClosestJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { + return ClosestJupiterGalileanPhenomenonContactEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// ClosestJupiterGalileanPhenomenonEvent 最近一次伽利略卫星现象,jd 与返回时刻均为民用儒略日 / closest Galilean-satellite event in civil Julian days. +func ClosestJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { + return ClosestJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// ClosestMercuryTransit 最近一次地心水星凌日 / closest geocentric transit of Mercury. +func ClosestMercuryTransit(jde float64) PlanetTransitResult { + return ClosestMercuryTransitWithInstance(jde, nil) +} + +// ClosestMoonMaximumNorthDeclination 离指定时刻最近一次月球最大北赤纬 / closest maximum northern lunar declination to jd. +func ClosestMoonMaximumNorthDeclination(jd float64) DeclinationEvent { + return ClosestMoonMaximumNorthDeclinationWithInstance(jd, nil) +} + +// ClosestMoonMaximumSouthDeclination 离指定时刻最近一次月球最大南赤纬 / closest maximum southern lunar declination to jd. +func ClosestMoonMaximumSouthDeclination(jd float64) DeclinationEvent { + return ClosestMoonMaximumSouthDeclinationWithInstance(jd, nil) +} + +// ClosestMoonPlanetConjunction 离指定时刻最近一次行星合月(赤经合) / closest Moon-planet conjunction to jde. +func ClosestMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { + return ClosestMoonPlanetConjunctionWithInstance(jde, planet, nil) +} + +// ClosestVenusTransit 最近一次地心金星凌日 / closest geocentric transit of Venus. +func ClosestVenusTransit(jde float64) PlanetTransitResult { + return ClosestVenusTransitWithInstance(jde, nil) +} + +// EveningTwilight 昏朦影,localJD 为当地 0 时民用日锚点且只取整数日 / evening twilight on the local civil day anchored at localJD. +func EveningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, error) { + return EveningTwilightWithInstance(localJD, lon, lat, tz, targetAltitude, nil) +} + +// FindBestPlanetOccultations 窗口内每次行星月掩的全球海平面几何掩甚点,可见性不参与选点 / global sea-level geometric greatest point of each planetary occultation. +func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { + return FindBestPlanetOccultationsWithInstance(start, end, planet, options, nil) +} + +// FindBestStarOccultations 窗口内每次恒星月掩的全球海平面几何掩甚点,可见性不参与选点 / global sea-level geometric greatest point of each stellar occultation. +func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) { + return FindBestStarOccultationsWithInstance(start, end, star, options, nil) +} + +// FindPlanetOccultationPaths 行星月掩的全球外、内接触掩带,按全球掩甚点选取事件 / global outer- and inner-contact footprints of planetary occultations. +func FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, + options OccultationPathOptions) ([]PlanetOccultationPath, error) { + return FindPlanetOccultationPathsWithInstance(start, end, planet, options, nil) +} + +// FindPlanetOccultations 固定观测点的有限盘面行星月掩 / planetary occultations at a fixed site. +func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { + return FindPlanetOccultationsWithInstance(start, end, planet, longitude, latitude, height, options, nil) +} + +// FindStarOccultationPaths 点源恒星月掩的全球掩带,不加载内嵌星表 / global footprint of a stellar occultation; no embedded catalog is loaded. +func FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) { + return FindStarOccultationPathsWithInstance(start, end, star, options, nil) +} + +// FindStarOccultations 固定观测点的点源恒星月掩,不加载内嵌星表 / stellar occultations at a fixed site; no embedded catalog is loaded. +func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, + options OccultationSearchOptions) ([]StarOccultationInfo, error) { + return FindStarOccultationsWithInstance(start, end, star, longitude, latitude, height, options, nil) +} + +// GetSunRiseTime 日出时刻,julianDay 为当地 0 时民用日锚点 / sunrise on the local civil day anchored at julianDay. +func GetSunRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { + return GetSunRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, nil) +} + +// GetSunSetTime 日落时刻,julianDay 为当地 0 时民用日锚点 / sunset on the local civil day anchored at julianDay. +func GetSunSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { + return GetSunSetTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, nil) +} + +// HMoonGeocentricApparentDec 月亮地心视赤纬 / apparent geocentric declination of the Moon. +func HMoonGeocentricApparentDec(jde float64) float64 { + return HMoonGeocentricApparentDecWithInstance(jde, nil) +} + +// HMoonGeocentricApparentDecN 月亮地心视赤纬(n 变体) / n-parameterised apparent geocentric declination of the Moon. +func HMoonGeocentricApparentDecN(jde float64, n int) float64 { + return HMoonGeocentricApparentDecNWithInstance(jde, n, nil) +} + +// HMoonGeocentricApparentRa 月亮地心视赤经 / apparent geocentric right ascension of the Moon. +func HMoonGeocentricApparentRa(jde float64) float64 { + return HMoonGeocentricApparentRaWithInstance(jde, nil) +} + +// HMoonGeocentricApparentRaN 月亮地心视赤经(n 变体) / n-parameterised apparent geocentric right ascension of the Moon. +func HMoonGeocentricApparentRaN(jde float64, n int) float64 { + return HMoonGeocentricApparentRaNWithInstance(jde, n, nil) +} + +// HMoonGeocentricTrueDec 月亮地心真赤纬 / true geocentric declination of the Moon. +func HMoonGeocentricTrueDec(jde float64) float64 { return HMoonGeocentricTrueDecWithInstance(jde, nil) } + +// HMoonGeocentricTrueDecN 月亮地心真赤纬(n 变体) / n-parameterised true geocentric declination of the Moon. +func HMoonGeocentricTrueDecN(jde float64, n int) float64 { + return HMoonGeocentricTrueDecNWithInstance(jde, n, nil) +} + +// HMoonGeocentricTrueRa 月亮地心真赤经 / true geocentric right ascension of the Moon. +func HMoonGeocentricTrueRa(jde float64) float64 { return HMoonGeocentricTrueRaWithInstance(jde, nil) } + +// HMoonGeocentricTrueRaDec 月亮地心真赤经、真赤纬 / true geocentric right ascension and declination of the Moon. +func HMoonGeocentricTrueRaDec(jde float64) (float64, float64) { + return HMoonGeocentricTrueRaDecWithInstance(jde, nil) +} + +// HMoonGeocentricTrueRaDecN 月亮地心真赤经、真赤纬(n 变体) / n-parameterised true geocentric right ascension and declination of the Moon. +func HMoonGeocentricTrueRaDecN(jde float64, n int) (float64, float64) { + return HMoonGeocentricTrueRaDecNWithInstance(jde, n, nil) +} + +// HMoonGeocentricTrueRaN 月亮地心真赤经(n 变体) / n-parameterised true geocentric right ascension of the Moon. +func HMoonGeocentricTrueRaN(jde float64, n int) float64 { + return HMoonGeocentricTrueRaNWithInstance(jde, n, nil) +} + +// HMoonHeight 月心几何高度角(度,不含折射),jd 为 tz 时区的当地民用儒略日 / geometric Moon-centre altitude for a local civil Julian day in zone tz. +func HMoonHeight(jd, lon, lat, tz float64) float64 { + return HMoonHeightWithInstance(jd, lon, lat, tz, nil) +} + +// JupiterAscendingNodeN 木星升交点黄经(n 变体) / n-parameterised ascending node longitude of Jupiter. +func JupiterAscendingNodeN(jd float64, n int) float64 { + return JupiterAscendingNodeNWithInstance(jd, n, nil) +} + +// JupiterAzimuthN 木星方位角(n 变体) / n-parameterised azimuth of Jupiter. +func JupiterAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return JupiterAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// JupiterBrightLimbPositionAngleN 木星亮面中心位置角(n 变体) / n-parameterised position angle of Jupiter bright limb. +func JupiterBrightLimbPositionAngleN(jde float64, n int) float64 { + return JupiterBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// JupiterCentralMeridiansN 木星 System I/II/III 中央经线(n 变体) / n-parameterised Jupiter System I/II/III central meridians. +func JupiterCentralMeridiansN(jde float64, n int) JupiterCentralMeridianInfo { + return JupiterCentralMeridiansNWithInstance(jde, n, nil) +} + +// JupiterCulminationTimeN 木星中天时间(n 变体) / n-parameterised culmination time of Jupiter. +func JupiterCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return JupiterCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// JupiterDSDEN 木星 DS/DE 行星中心赤纬(n 变体) / n-parameterised Jupiter planetocentric declinations of Sun and Earth. +func JupiterDSDEN(jde float64, n int) (ds, de float64) { return JupiterDSDENWithInstance(jde, n, nil) } + +// JupiterDescendingNodeN 木星降交点黄经(n 变体) / n-parameterised descending node longitude of Jupiter. +func JupiterDescendingNodeN(jd float64, n int) float64 { + return JupiterDescendingNodeNWithInstance(jd, n, nil) +} + +// JupiterDiameterN 木星视直径(n 变体),单位角秒 / n-parameterised apparent Jupiter diameter in arcseconds. +func JupiterDiameterN(jde float64, n int) float64 { return JupiterDiameterNWithInstance(jde, n, nil) } + +// JupiterGalileanSatelliteObservations 四颗伽利略卫星视位置,次序为 Io、Europa、Ganymede、Callisto / apparent geometry of Io, Europa, Ganymede and Callisto in that order. +func JupiterGalileanSatelliteObservations(jde float64) [4]JupiterGalileanObservation { + return JupiterGalileanSatelliteObservationsWithInstance(jde, nil) +} + +// JupiterGalileanSatellitePhenomena 四颗伽利略卫星瞬时现象 / instantaneous phenomena of the four Galilean satellites. +func JupiterGalileanSatellitePhenomena(jde float64) [4]JupiterGalileanPhenomenon { + return JupiterGalileanSatellitePhenomenaWithInstance(jde, nil) +} + +// JupiterHeightN 木星高度角(n 变体) / n-parameterised altitude of Jupiter. +func JupiterHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return JupiterHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// JupiterHourAngleN 木星时角(n 变体) / n-parameterised hour angle of Jupiter. +func JupiterHourAngleN(jd, lon, timezone float64, n int) float64 { + return JupiterHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// JupiterIlluminatedFractionN 木星被照亮比例(n 变体) / n-parameterised illuminated fraction of Jupiter. +func JupiterIlluminatedFractionN(jd float64, n int) float64 { + return JupiterIlluminatedFractionNWithInstance(jd, n, nil) +} + +// JupiterMagN 木星视星等(n 变体) / n-parameterised apparent magnitude of Jupiter. +func JupiterMagN(jde float64, n int) float64 { return JupiterMagNWithInstance(jde, n, nil) } + +// JupiterPhaseAngleN 木星相位角(n 变体) / n-parameterised phase angle of Jupiter. +func JupiterPhaseAngleN(jd float64, n int) float64 { return JupiterPhaseAngleNWithInstance(jd, n, nil) } + +// JupiterPhysicalN 木星物理观测参数(n 变体) / n-parameterised physical observing parameters of Jupiter. +func JupiterPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return JupiterPhysicalNWithInstance(jd, n, nil) +} + +// JupiterRiseTimeN 木星升起时间(n 变体) / n-parameterised rise time of Jupiter. +func JupiterRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return JupiterRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// JupiterSemidiameterN 木星视半径(n 变体),单位角秒 / n-parameterised apparent Jupiter semidiameter in arcseconds. +func JupiterSemidiameterN(jde float64, n int) float64 { + return JupiterSemidiameterNWithInstance(jde, n, nil) +} + +// JupiterSetTimeN 木星落下时间(n 变体) / n-parameterised set time of Jupiter. +func JupiterSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return JupiterSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// LastJupiterGalileanPhenomenonContactEvent 上一次 IMCCE 风格接触事件 / previous IMCCE-style contact event. +func LastJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { + return LastJupiterGalileanPhenomenonContactEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// LastJupiterGalileanPhenomenonEvent 上一次伽利略卫星现象,jd 与返回时刻均为民用儒略日 / previous Galilean-satellite event in civil Julian days. +func LastJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { + return LastJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// LastMercuryTransit 上一次地心水星凌日 / previous geocentric transit of Mercury. +func LastMercuryTransit(jde float64) PlanetTransitResult { + return LastMercuryTransitWithInstance(jde, nil) +} + +// LastMoonMaximumNorthDeclination 指定时刻之前最近一次月球最大北赤纬 / last maximum northern lunar declination at or before jd. +func LastMoonMaximumNorthDeclination(jd float64) DeclinationEvent { + return LastMoonMaximumNorthDeclinationWithInstance(jd, nil) +} + +// LastMoonMaximumSouthDeclination 指定时刻之前最近一次月球最大南赤纬 / last maximum southern lunar declination at or before jd. +func LastMoonMaximumSouthDeclination(jd float64) DeclinationEvent { + return LastMoonMaximumSouthDeclinationWithInstance(jd, nil) +} + +// LastMoonPlanetConjunction 指定时刻之前最近一次行星合月(赤经合) / previous Moon-planet conjunction at or before jde. +func LastMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { + return LastMoonPlanetConjunctionWithInstance(jde, planet, nil) +} + +// LastVenusTransit 上一次地心金星凌日 / previous geocentric transit of Venus. +func LastVenusTransit(jde float64) PlanetTransitResult { return LastVenusTransitWithInstance(jde, nil) } + +// LocalSolarEclipseDiagramIAUSingleK 站心日食视圆图几何(IAU Single-K) / local solar eclipse disk diagram, IAU Single-K. +func LocalSolarEclipseDiagramIAUSingleK(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions) LocalSolarEclipseDiagramResult { + return LocalSolarEclipseDiagramIAUSingleKWithInstance(seedJDE, lon, lat, height, options, nil) +} + +// LocalSolarEclipseDiagramNASABulletinSplitK 站心日食视圆图几何(NASA bulletin Split-K) / local solar eclipse disk diagram, NASA bulletin Split-K. +func LocalSolarEclipseDiagramNASABulletinSplitK(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions) LocalSolarEclipseDiagramResult { + return LocalSolarEclipseDiagramNASABulletinSplitKWithInstance(seedJDE, lon, lat, height, options, nil) +} + +// LocalSolarEclipseWithOptions 计算给定近朔时刻附近的一次站心日食,半径口径由 options 指定 / computes one local solar eclipse with the given radius conventions. +func LocalSolarEclipseWithOptions(seedJDE, lon, lat, height float64, options SolarEclipseOptions) LocalSolarEclipseResult { + return LocalSolarEclipseWithOptionsWithInstance(seedJDE, lon, lat, height, options, nil) +} + +// MarsAscendingNodeN 火星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mars. +func MarsAscendingNodeN(jd float64, n int) float64 { return MarsAscendingNodeNWithInstance(jd, n, nil) } + +// MarsAzimuthN 火星方位角(n 变体) / n-parameterised azimuth of Mars. +func MarsAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return MarsAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// MarsBrightLimbPositionAngleN 火星亮面中心位置角(n 变体) / n-parameterised position angle of Mars bright limb. +func MarsBrightLimbPositionAngleN(jde float64, n int) float64 { + return MarsBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// MarsCulminationTimeN 火星中天时间(n 变体) / n-parameterised culmination time of Mars. +func MarsCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return MarsCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// MarsDescendingNodeN 火星降交点黄经(n 变体) / n-parameterised descending node longitude of Mars. +func MarsDescendingNodeN(jd float64, n int) float64 { + return MarsDescendingNodeNWithInstance(jd, n, nil) +} + +// MarsDiameterN 火星视直径(n 变体),单位角秒 / n-parameterised apparent Mars diameter in arcseconds. +func MarsDiameterN(jde float64, n int) float64 { return MarsDiameterNWithInstance(jde, n, nil) } + +// MarsHeightN 火星高度角(n 变体) / n-parameterised altitude of Mars. +func MarsHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return MarsHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// MarsHourAngleN 火星时角(n 变体) / n-parameterised hour angle of Mars. +func MarsHourAngleN(jd, lon, timezone float64, n int) float64 { + return MarsHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// MarsIlluminatedFractionN 火星被照亮比例(n 变体) / n-parameterised illuminated fraction of Mars. +func MarsIlluminatedFractionN(jd float64, n int) float64 { + return MarsIlluminatedFractionNWithInstance(jd, n, nil) +} + +// MarsMagN 火星视星等(n 变体) / n-parameterised apparent magnitude of Mars. +func MarsMagN(jde float64, n int) float64 { return MarsMagNWithInstance(jde, n, nil) } + +// MarsPhaseAngleN 火星相位角(n 变体) / n-parameterised phase angle of Mars. +func MarsPhaseAngleN(jd float64, n int) float64 { return MarsPhaseAngleNWithInstance(jd, n, nil) } + +// MarsPhysicalN 火星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mars. +func MarsPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return MarsPhysicalNWithInstance(jd, n, nil) +} + +// MarsRiseTimeN 火星升起时间(n 变体) / n-parameterised rise time of Mars. +func MarsRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return MarsRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// MarsSemidiameterN 火星视半径(n 变体),单位角秒 / n-parameterised apparent Mars semidiameter in arcseconds. +func MarsSemidiameterN(jde float64, n int) float64 { return MarsSemidiameterNWithInstance(jde, n, nil) } + +// MarsSetTimeN 火星落下时间(n 变体) / n-parameterised set time of Mars. +func MarsSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return MarsSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// MercuryAscendingNodeN 水星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mercury. +func MercuryAscendingNodeN(jd float64, n int) float64 { + return MercuryAscendingNodeNWithInstance(jd, n, nil) +} + +// MercuryAzimuthN 水星方位角(n 变体) / n-parameterised azimuth of Mercury. +func MercuryAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return MercuryAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// MercuryBrightLimbPositionAngleN 水星亮面中心位置角(n 变体) / n-parameterised position angle of Mercury bright limb. +func MercuryBrightLimbPositionAngleN(jde float64, n int) float64 { + return MercuryBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// MercuryCulminationTimeN 水星中天时间(n 变体) / n-parameterised culmination time of Mercury. +func MercuryCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return MercuryCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// MercuryDescendingNodeN 水星降交点黄经(n 变体) / n-parameterised descending node longitude of Mercury. +func MercuryDescendingNodeN(jd float64, n int) float64 { + return MercuryDescendingNodeNWithInstance(jd, n, nil) +} + +// MercuryDiameterN 水星视直径(n 变体),单位角秒 / n-parameterised apparent Mercury diameter in arcseconds. +func MercuryDiameterN(jde float64, n int) float64 { return MercuryDiameterNWithInstance(jde, n, nil) } + +// MercuryHeightN 水星高度角(n 变体) / n-parameterised altitude of Mercury. +func MercuryHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return MercuryHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// MercuryHourAngleN 水星时角(n 变体) / n-parameterised hour angle of Mercury. +func MercuryHourAngleN(jd, lon, timezone float64, n int) float64 { + return MercuryHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// MercuryIlluminatedFractionN 水星被照亮比例(n 变体) / n-parameterised illuminated fraction of Mercury. +func MercuryIlluminatedFractionN(jd float64, n int) float64 { + return MercuryIlluminatedFractionNWithInstance(jd, n, nil) +} + +// MercuryMagN 水星视星等(n 变体) / n-parameterised apparent magnitude of Mercury. +func MercuryMagN(jde float64, n int) float64 { return MercuryMagNWithInstance(jde, n, nil) } + +// MercuryPhaseAngleN 水星相位角(n 变体) / n-parameterised phase angle of Mercury. +func MercuryPhaseAngleN(jd float64, n int) float64 { return MercuryPhaseAngleNWithInstance(jd, n, nil) } + +// MercuryPhysicalN 水星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mercury. +func MercuryPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return MercuryPhysicalNWithInstance(jd, n, nil) +} + +// MercuryRiseTimeN 水星升起时间(n 变体) / n-parameterised rise time of Mercury. +func MercuryRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return MercuryRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// MercurySemidiameterN 水星视半径(n 变体),单位角秒 / n-parameterised apparent Mercury semidiameter in arcseconds. +func MercurySemidiameterN(jde float64, n int) float64 { + return MercurySemidiameterNWithInstance(jde, n, nil) +} + +// MercurySetTimeN 水星落下时间(n 变体) / n-parameterised set time of Mercury. +func MercurySetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return MercurySetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// MoonApogees 指定年月内的所有月球远地点 / all lunar apogees in the given Gregorian month. +func MoonApogees(year int, month time.Month) []ApsisEvent { + return MoonApogeesWithInstance(year, month, nil) +} + +// MoonAscendingNodeN 月球升交点黄经(n 变体) / n-parameterised ascending node longitude of the Moon. +func MoonAscendingNodeN(jd float64, n int) float64 { return MoonAscendingNodeNWithInstance(jd, n, nil) } + +// MoonBrightLimbPositionAngleN 月亮明亮边缘位置角(n 变体) / n-parameterised position angle of the Moon's bright limb. +func MoonBrightLimbPositionAngleN(jde float64, n int) float64 { + return MoonBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// MoonDescendingNodeN 月球降交点黄经(n 变体) / n-parameterised descending node longitude of the Moon. +func MoonDescendingNodeN(jd float64, n int) float64 { + return MoonDescendingNodeNWithInstance(jd, n, nil) +} + +// MoonDiameterN 月亮视直径(n 变体),单位角秒 / n-parameterised apparent lunar diameter in arcseconds. +func MoonDiameterN(jde float64, n int) float64 { return MoonDiameterNWithInstance(jde, n, nil) } + +// MoonHorizon UTC 儒略日下海平面月心几何地平圈的 [经度, 纬度] 顶点,不含折射;samples<=0 取 360,其余夹到 [12, 1440] / sea-level geometric Moon-centre horizon vertices. +func MoonHorizon(jdUTC float64, samples int) [][2]float64 { + return MoonHorizonWithInstance(jdUTC, samples, nil) +} + +// MoonMaximumNorthDeclinations 指定年月内的所有月球最大北赤纬事件 / all maximum northern lunar declination events in the given Gregorian month. +func MoonMaximumNorthDeclinations(year int, month time.Month) []DeclinationEvent { + return MoonMaximumNorthDeclinationsWithInstance(year, month, nil) +} + +// MoonMaximumSouthDeclinations 指定年月内的所有月球最大南赤纬事件 / all maximum southern lunar declination events in the given Gregorian month. +func MoonMaximumSouthDeclinations(year int, month time.Month) []DeclinationEvent { + return MoonMaximumSouthDeclinationsWithInstance(year, month, nil) +} + +// MoonPerigees 指定年月内的所有月球近地点 / all lunar perigees in the given Gregorian month. +func MoonPerigees(year int, month time.Month) []ApsisEvent { + return MoonPerigeesWithInstance(year, month, nil) +} + +// MoonPhysical 月球物理观测参数 / physical observing parameters of the Moon. +func MoonPhysical(jde float64) MoonPhysicalInfo { return MoonPhysicalWithInstance(jde, nil) } + +// MoonPhysicalN 月球物理观测参数(n 变体) / n-parameterised physical observing parameters of the Moon. +func MoonPhysicalN(jde float64, n int) MoonPhysicalInfo { + return MoonPhysicalNWithInstance(jde, n, nil) +} + +// MoonSemidiameter 月亮视半径,单位角秒 / apparent lunar semidiameter in arcseconds. +func MoonSemidiameter(jde float64) float64 { return MoonSemidiameterWithInstance(jde, nil) } + +// MoonSemidiameterN 月亮视半径(n 变体),单位角秒 / n-parameterised apparent lunar semidiameter in arcseconds. +func MoonSemidiameterN(jde float64, n int) float64 { return MoonSemidiameterNWithInstance(jde, n, nil) } + +// MoonStateAt 由 UTC 儒略日构造该瞬间的可复用月球状态 / builds the reusable state for one UTC Julian day. +func MoonStateAt(utcJD float64) MoonState { return MoonStateAtWithInstance(utcJD, nil) } + +// MoonTopocentricBrightLimbPositionAngleN 月亮站心明亮边缘位置角(n 变体) / n-parameterised topocentric position angle of the Moon's bright limb. +func MoonTopocentricBrightLimbPositionAngleN(jde, observerLon, observerLat, height float64, n int) float64 { + return MoonTopocentricBrightLimbPositionAngleNWithInstance(jde, observerLon, observerLat, height, n, nil) +} + +// MoonTopocentricPhysicalN 月球站心物理观测参数(n 变体) / n-parameterised topocentric physical observing parameters of the Moon. +func MoonTopocentricPhysicalN(jde, observerLon, observerLat, height float64, n int) MoonPhysicalInfo { + return MoonTopocentricPhysicalNWithInstance(jde, observerLon, observerLat, height, n, nil) +} + +// NeptuneAscendingNodeN 海王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Neptune. +func NeptuneAscendingNodeN(jd float64, n int) float64 { + return NeptuneAscendingNodeNWithInstance(jd, n, nil) +} + +// NeptuneAzimuthN 海王星方位角(n 变体) / n-parameterised azimuth of Neptune. +func NeptuneAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return NeptuneAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// NeptuneBrightLimbPositionAngleN 海王星亮面中心位置角(n 变体) / n-parameterised position angle of Neptune bright limb. +func NeptuneBrightLimbPositionAngleN(jde float64, n int) float64 { + return NeptuneBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// NeptuneCulminationTimeN 海王星中天时间(n 变体) / n-parameterised culmination time of Neptune. +func NeptuneCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return NeptuneCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// NeptuneDescendingNodeN 海王星降交点黄经(n 变体) / n-parameterised descending node longitude of Neptune. +func NeptuneDescendingNodeN(jd float64, n int) float64 { + return NeptuneDescendingNodeNWithInstance(jd, n, nil) +} + +// NeptuneDiameterN 海王星视直径(n 变体),单位角秒 / n-parameterised apparent Neptune diameter in arcseconds. +func NeptuneDiameterN(jde float64, n int) float64 { return NeptuneDiameterNWithInstance(jde, n, nil) } + +// NeptuneHeightN 海王星高度角(n 变体) / n-parameterised altitude of Neptune. +func NeptuneHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return NeptuneHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// NeptuneHourAngleN 海王星时角(n 变体) / n-parameterised hour angle of Neptune. +func NeptuneHourAngleN(jd, lon, timezone float64, n int) float64 { + return NeptuneHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// NeptuneIlluminatedFractionN 海王星被照亮比例(n 变体) / n-parameterised illuminated fraction of Neptune. +func NeptuneIlluminatedFractionN(jd float64, n int) float64 { + return NeptuneIlluminatedFractionNWithInstance(jd, n, nil) +} + +// NeptuneMagN 海王星视星等(n 变体) / n-parameterised apparent magnitude of Neptune. +func NeptuneMagN(jde float64, n int) float64 { return NeptuneMagNWithInstance(jde, n, nil) } + +// NeptunePhaseAngleN 海王星相位角(n 变体) / n-parameterised phase angle of Neptune. +func NeptunePhaseAngleN(jd float64, n int) float64 { return NeptunePhaseAngleNWithInstance(jd, n, nil) } + +// NeptunePhysicalN 海王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Neptune. +func NeptunePhysicalN(jd float64, n int) PlanetPhysicalInfo { + return NeptunePhysicalNWithInstance(jd, n, nil) +} + +// NeptuneRiseTimeN 海王星升起时间(n 变体) / n-parameterised rise time of Neptune. +func NeptuneRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return NeptuneRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// NeptuneSemidiameterN 海王星视半径(n 变体),单位角秒 / n-parameterised apparent Neptune semidiameter in arcseconds. +func NeptuneSemidiameterN(jde float64, n int) float64 { + return NeptuneSemidiameterNWithInstance(jde, n, nil) +} + +// NeptuneSetTimeN 海王星落下时间(n 变体) / n-parameterised set time of Neptune. +func NeptuneSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return NeptuneSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// NewSolarEclipseShadowSolver 构造单时刻求解器 / builds a single-instant solver. +func NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver { + return NewSolarEclipseShadowSolverWithInstance(options, nil) +} + +// NextJupiterGalileanPhenomenonContactEvent 下一次 IMCCE 风格接触事件 / next IMCCE-style contact event. +func NextJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { + return NextJupiterGalileanPhenomenonContactEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// NextJupiterGalileanPhenomenonEvent 下一次伽利略卫星现象,jd 与返回时刻均为民用儒略日 / next Galilean-satellite event in civil Julian days. +func NextJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { + return NextJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, nil) +} + +// NextMercuryTransit 下一次地心水星凌日 / next geocentric transit of Mercury. +func NextMercuryTransit(jde float64) PlanetTransitResult { + return NextMercuryTransitWithInstance(jde, nil) +} + +// NextMoonMaximumNorthDeclination 指定时刻之后最近一次月球最大北赤纬 / next maximum northern lunar declination after jd. +func NextMoonMaximumNorthDeclination(jd float64) DeclinationEvent { + return NextMoonMaximumNorthDeclinationWithInstance(jd, nil) +} + +// NextMoonMaximumSouthDeclination 指定时刻之后最近一次月球最大南赤纬 / next maximum southern lunar declination after jd. +func NextMoonMaximumSouthDeclination(jd float64) DeclinationEvent { + return NextMoonMaximumSouthDeclinationWithInstance(jd, nil) +} + +// NextMoonPlanetConjunction 指定时刻之后最近一次行星合月(赤经合) / next Moon-planet conjunction at or after jde. +func NextMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { + return NextMoonPlanetConjunctionWithInstance(jde, planet, nil) +} + +// NextVenusTransit 下一次地心金星凌日 / next geocentric transit of Venus. +func NextVenusTransit(jde float64) PlanetTransitResult { return NextVenusTransitWithInstance(jde, nil) } + +// PlanetOccultationDiagram 已求解固定地点行星月掩的月心切平面轨迹,事件无效时不含帧 / tangent-plane track of a solved fixed-site planetary occultation. +func PlanetOccultationDiagram( + info PlanetOccultationInfo, + options PlanetOccultationDiagramOptions, +) PlanetOccultationDiagramResult { + return PlanetOccultationDiagramWithInstance(info, options, nil) +} + +// PlanetOccultationFootprintsAt 指定时刻行星月掩外、内接触可见足迹 / visible outer- and inner-contact planetary footprints at one instant. +func PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) { + return PlanetOccultationFootprintsAtWithInstance(at, planet, nil) +} + +// SaturnAscendingNodeN 土星升交点黄经(n 变体) / n-parameterised ascending node longitude of Saturn. +func SaturnAscendingNodeN(jd float64, n int) float64 { + return SaturnAscendingNodeNWithInstance(jd, n, nil) +} + +// SaturnAzimuthN 土星方位角(n 变体) / n-parameterised azimuth of Saturn. +func SaturnAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return SaturnAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// SaturnBrightLimbPositionAngleN 土星亮面中心位置角(n 变体) / n-parameterised position angle of Saturn bright limb. +func SaturnBrightLimbPositionAngleN(jde float64, n int) float64 { + return SaturnBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// SaturnCulminationTimeN 土星中天时间(n 变体) / n-parameterised culmination time of Saturn. +func SaturnCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return SaturnCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// SaturnDescendingNodeN 土星降交点黄经(n 变体) / n-parameterised descending node longitude of Saturn. +func SaturnDescendingNodeN(jd float64, n int) float64 { + return SaturnDescendingNodeNWithInstance(jd, n, nil) +} + +// SaturnDiameterN 土星视直径(n 变体),单位角秒 / n-parameterised apparent Saturn diameter in arcseconds. +func SaturnDiameterN(jde float64, n int) float64 { return SaturnDiameterNWithInstance(jde, n, nil) } + +// SaturnHeightN 土星高度角(n 变体) / n-parameterised altitude of Saturn. +func SaturnHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return SaturnHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// SaturnHourAngleN 土星时角(n 变体) / n-parameterised hour angle of Saturn. +func SaturnHourAngleN(jd, lon, timezone float64, n int) float64 { + return SaturnHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// SaturnIlluminatedFractionN 土星被照亮比例(n 变体) / n-parameterised illuminated fraction of Saturn. +func SaturnIlluminatedFractionN(jd float64, n int) float64 { + return SaturnIlluminatedFractionNWithInstance(jd, n, nil) +} + +// SaturnMagN 土星视星等(n 变体) / n-parameterised apparent magnitude of Saturn. +func SaturnMagN(jde float64, n int) float64 { return SaturnMagNWithInstance(jde, n, nil) } + +// SaturnPhaseAngleN 土星相位角(n 变体) / n-parameterised phase angle of Saturn. +func SaturnPhaseAngleN(jd float64, n int) float64 { return SaturnPhaseAngleNWithInstance(jd, n, nil) } + +// SaturnPhysicalN 土星物理观测参数(n 变体) / n-parameterised physical observing parameters of Saturn. +func SaturnPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return SaturnPhysicalNWithInstance(jd, n, nil) +} + +// SaturnRiseTimeN 土星升起时间(n 变体) / n-parameterised rise time of Saturn. +func SaturnRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return SaturnRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// SaturnSemidiameterN 土星视半径(n 变体),单位角秒 / n-parameterised apparent Saturn semidiameter in arcseconds. +func SaturnSemidiameterN(jde float64, n int) float64 { + return SaturnSemidiameterNWithInstance(jde, n, nil) +} + +// SaturnSetTimeN 土星落下时间(n 变体) / n-parameterised set time of Saturn. +func SaturnSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return SaturnSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// SolarEclipseBesselianElements 近朔时刻附近日食的多项式贝塞尔根数,无日食时返回 false / polynomial Besselian elements of the solar eclipse near a new moon. +func SolarEclipseBesselianElements( + seedJDE float64, options SolarEclipseBesselianElementsOptions, +) (SolarEclipseBesselianElementsResult, bool) { + return SolarEclipseBesselianElementsWithInstance(seedJDE, options, nil) +} + +// SolarEclipseCentralPathIAUSingleK 日食中心路径(IAU Single-K) / solar eclipse central path, IAU Single-K. +func SolarEclipseCentralPathIAUSingleK(seedJDE float64, options SolarEclipsePathOptions) SolarEclipsePathResult { + return SolarEclipseCentralPathIAUSingleKWithInstance(seedJDE, options, nil) +} + +// SolarEclipseCentralPathNASABulletinSplitK 日食中心路径(NASA bulletin Split-K) / solar eclipse central path, NASA bulletin Split-K. +func SolarEclipseCentralPathNASABulletinSplitK(seedJDE float64, options SolarEclipsePathOptions) SolarEclipsePathResult { + return SolarEclipseCentralPathNASABulletinSplitKWithInstance(seedJDE, options, nil) +} + +// SolarEclipseIAUSingleK 近朔时刻附近的全局日食(IAU Single-K) / global solar eclipse near a new moon, IAU Single-K. +func SolarEclipseIAUSingleK(seedJDE float64) SolarEclipseResult { + return SolarEclipseIAUSingleKWithInstance(seedJDE, nil) +} + +// SolarEclipseNASABulletinSplitK 近朔时刻附近的全局日食(NASA bulletin Split-K) / global solar eclipse near a new moon, NASA bulletin Split-K. +func SolarEclipseNASABulletinSplitK(seedJDE float64) SolarEclipseResult { + return SolarEclipseNASABulletinSplitKWithInstance(seedJDE, nil) +} + +// SolarEclipsePartialFootprintsIAUSingleK 日食半影足迹序列(IAU Single-K) / penumbral footprint samples, IAU Single-K. +func SolarEclipsePartialFootprintsIAUSingleK(seedJDE float64, options SolarEclipsePartialFootprintOptions) SolarEclipsePartialFootprintsResult { + return SolarEclipsePartialFootprintsIAUSingleKWithInstance(seedJDE, options, nil) +} + +// SolarEclipsePartialFootprintsNASABulletinSplitK 日食半影足迹序列(NASA bulletin Split-K) / penumbral footprint samples, NASA bulletin Split-K. +func SolarEclipsePartialFootprintsNASABulletinSplitK(seedJDE float64, options SolarEclipsePartialFootprintOptions) SolarEclipsePartialFootprintsResult { + return SolarEclipsePartialFootprintsNASABulletinSplitKWithInstance(seedJDE, options, nil) +} + +// SolarEclipseSunSemidiameter 指定太阳半径口径下的视半径,单位角秒 / apparent solar semidiameter in arcseconds under a given eclipse sun radius convention. +func SolarEclipseSunSemidiameter(jde float64, model SolarEclipseSunRadiusModel) float64 { + return SolarEclipseSunSemidiameterWithInstance(jde, model, nil) +} + +// SolarEclipseWithOptions 计算给定近朔时刻附近的一次全局日食,半径口径由 options 指定 / computes one global solar eclipse with the given radius conventions. +func SolarEclipseWithOptions(seedJDE float64, options SolarEclipseOptions) SolarEclipseResult { + return SolarEclipseWithOptionsWithInstance(seedJDE, options, nil) +} + +// StarOccultationLimbProfile 沿星迹按月缘剖面精密求解一次恒星月掩 / precise stellar occultation along the track with a lunar limb profile. +func StarOccultationLimbProfile(info StarOccultationInfo, star StarCoordinate, limb LunarLimb, options StarOccultationLimbProfileOptions) (StarOccultationLimbProfileResult, error) { + return StarOccultationLimbProfileWithInstance(info, star, limb, options, nil) +} + +// StarOccultationDiagram 已求解固定地点恒星月掩的月心切平面轨迹,事件无效时不含帧 / tangent-plane track of a solved fixed-site stellar occultation. +func StarOccultationDiagram( + info StarOccultationInfo, + star StarCoordinate, + options StarOccultationDiagramOptions, +) StarOccultationDiagramResult { + return StarOccultationDiagramWithInstance(info, star, options, nil) +} + +// StarOccultationFootprintAt 指定时刻点源恒星月掩可见足迹 / visible stellar-occultation footprint at one instant. +func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) { + return StarOccultationFootprintAtWithInstance(at, star, nil) +} + +// SunDiameterN 太阳视直径(n 变体),单位角秒 / n-parameterised apparent solar diameter in arcseconds. +func SunDiameterN(jde float64, n int) float64 { return SunDiameterNWithInstance(jde, n, nil) } + +// SunPhysicalN 太阳物理观测参数(n 变体) / n-parameterised physical observing parameters of the Sun. +func SunPhysicalN(jde float64, n int) SunPhysicalInfo { return SunPhysicalNWithInstance(jde, n, nil) } + +// SunSemidiameterN 太阳视半径(n 变体),单位角秒 / n-parameterised apparent solar semidiameter in arcseconds. +func SunSemidiameterN(jde float64, n int) float64 { return SunSemidiameterNWithInstance(jde, n, nil) } + +// UranusAscendingNodeN 天王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Uranus. +func UranusAscendingNodeN(jd float64, n int) float64 { + return UranusAscendingNodeNWithInstance(jd, n, nil) +} + +// UranusAzimuthN 天王星方位角(n 变体) / n-parameterised azimuth of Uranus. +func UranusAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return UranusAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// UranusBrightLimbPositionAngleN 天王星亮面中心位置角(n 变体) / n-parameterised position angle of Uranus bright limb. +func UranusBrightLimbPositionAngleN(jde float64, n int) float64 { + return UranusBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// UranusCulminationTimeN 天王星中天时间(n 变体) / n-parameterised culmination time of Uranus. +func UranusCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return UranusCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// UranusDescendingNodeN 天王星降交点黄经(n 变体) / n-parameterised descending node longitude of Uranus. +func UranusDescendingNodeN(jd float64, n int) float64 { + return UranusDescendingNodeNWithInstance(jd, n, nil) +} + +// UranusDiameterN 天王星视直径(n 变体),单位角秒 / n-parameterised apparent Uranus diameter in arcseconds. +func UranusDiameterN(jde float64, n int) float64 { return UranusDiameterNWithInstance(jde, n, nil) } + +// UranusHeightN 天王星高度角(n 变体) / n-parameterised altitude of Uranus. +func UranusHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return UranusHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// UranusHourAngleN 天王星时角(n 变体) / n-parameterised hour angle of Uranus. +func UranusHourAngleN(jd, lon, timezone float64, n int) float64 { + return UranusHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// UranusIlluminatedFractionN 天王星被照亮比例(n 变体) / n-parameterised illuminated fraction of Uranus. +func UranusIlluminatedFractionN(jd float64, n int) float64 { + return UranusIlluminatedFractionNWithInstance(jd, n, nil) +} + +// UranusMagN 天王星视星等(n 变体) / n-parameterised apparent magnitude of Uranus. +func UranusMagN(jde float64, n int) float64 { return UranusMagNWithInstance(jde, n, nil) } + +// UranusPhaseAngleN 天王星相位角(n 变体) / n-parameterised phase angle of Uranus. +func UranusPhaseAngleN(jd float64, n int) float64 { return UranusPhaseAngleNWithInstance(jd, n, nil) } + +// UranusPhysicalN 天王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Uranus. +func UranusPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return UranusPhysicalNWithInstance(jd, n, nil) +} + +// UranusRiseTimeN 天王星升起时间(n 变体) / n-parameterised rise time of Uranus. +func UranusRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return UranusRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// UranusSemidiameterN 天王星视半径(n 变体),单位角秒 / n-parameterised apparent Uranus semidiameter in arcseconds. +func UranusSemidiameterN(jde float64, n int) float64 { + return UranusSemidiameterNWithInstance(jde, n, nil) +} + +// UranusSetTimeN 天王星落下时间(n 变体) / n-parameterised set time of Uranus. +func UranusSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return UranusSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// VenusAscendingNodeN 金星升交点黄经(n 变体) / n-parameterised ascending node longitude of Venus. +func VenusAscendingNodeN(jd float64, n int) float64 { + return VenusAscendingNodeNWithInstance(jd, n, nil) +} + +// VenusAzimuthN 金星方位角(n 变体) / n-parameterised azimuth of Venus. +func VenusAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { + return VenusAzimuthNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// VenusBrightLimbPositionAngleN 金星亮面中心位置角(n 变体) / n-parameterised position angle of Venus bright limb. +func VenusBrightLimbPositionAngleN(jde float64, n int) float64 { + return VenusBrightLimbPositionAngleNWithInstance(jde, n, nil) +} + +// VenusCulminationTimeN 金星中天时间(n 变体) / n-parameterised culmination time of Venus. +func VenusCulminationTimeN(localJD, lon, timezone float64, n int) float64 { + return VenusCulminationTimeNWithInstance(localJD, lon, timezone, n, nil) +} + +// VenusDescendingNodeN 金星降交点黄经(n 变体) / n-parameterised descending node longitude of Venus. +func VenusDescendingNodeN(jd float64, n int) float64 { + return VenusDescendingNodeNWithInstance(jd, n, nil) +} + +// VenusDiameterN 金星视直径(n 变体),单位角秒 / n-parameterised apparent Venus diameter in arcseconds. +func VenusDiameterN(jde float64, n int) float64 { return VenusDiameterNWithInstance(jde, n, nil) } + +// VenusHeightN 金星高度角(n 变体) / n-parameterised altitude of Venus. +func VenusHeightN(localJD, lon, lat, timezone float64, n int) float64 { + return VenusHeightNWithInstance(localJD, lon, lat, timezone, n, nil) +} + +// VenusHourAngleN 金星时角(n 变体) / n-parameterised hour angle of Venus. +func VenusHourAngleN(jd, lon, timezone float64, n int) float64 { + return VenusHourAngleNWithInstance(jd, lon, timezone, n, nil) +} + +// VenusIlluminatedFractionN 金星被照亮比例(n 变体) / n-parameterised illuminated fraction of Venus. +func VenusIlluminatedFractionN(jd float64, n int) float64 { + return VenusIlluminatedFractionNWithInstance(jd, n, nil) +} + +// VenusMagN 金星视星等(n 变体) / n-parameterised apparent magnitude of Venus. +func VenusMagN(jde float64, n int) float64 { return VenusMagNWithInstance(jde, n, nil) } + +// VenusPhaseAngleN 金星相位角(n 变体) / n-parameterised phase angle of Venus. +func VenusPhaseAngleN(jd float64, n int) float64 { return VenusPhaseAngleNWithInstance(jd, n, nil) } + +// VenusPhysicalN 金星物理观测参数(n 变体) / n-parameterised physical observing parameters of Venus. +func VenusPhysicalN(jd float64, n int) PlanetPhysicalInfo { + return VenusPhysicalNWithInstance(jd, n, nil) +} + +// VenusRiseTimeN 金星升起时间(n 变体) / n-parameterised rise time of Venus. +func VenusRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return VenusRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// VenusSemidiameterN 金星视半径(n 变体),单位角秒 / n-parameterised apparent Venus semidiameter in arcseconds. +func VenusSemidiameterN(jde float64, n int) float64 { + return VenusSemidiameterNWithInstance(jde, n, nil) +} + +// VenusSetTimeN 金星落下时间(n 变体) / n-parameterised set time of Venus. +func VenusSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { + return VenusSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, n, nil) +} + +// NextMercuryGreatestElongation 下一次水星大距(东西任一侧) / next greatest elongation of Mercury on either side. +func NextMercuryGreatestElongation(jde float64) float64 { + return NextMercuryGreatestElongationWithInstance(jde, nil) +} + +// NextVenusGreatestElongation 下一次金星大距(东西任一侧) / next greatest elongation of Venus on either side. +func NextVenusGreatestElongation(jde float64) float64 { + return NextVenusGreatestElongationWithInstance(jde, nil) +} + +// NextMercuryProgradeToRetrograde 下一次水星顺行转逆行的留 / next prograde-to-retrograde station of Mercury. +func NextMercuryProgradeToRetrograde(jde float64) float64 { + return NextMercuryProgradeToRetrogradeWithInstance(jde, nil) +} + +// EveningTwilightN 昏朦影(n 变体) / n-parameterised evening twilight. +func EveningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (float64, error) { + return EveningTwilightNWithInstance(localJD, lon, lat, tz, targetAltitude, n, nil) +} + +// GXCBo 黄纬光行差修正(角秒) / ecliptic-latitude aberration correction in arcseconds. +func GXCBo(lo, bo, jde float64) float64 { return GXCBoWithInstance(lo, bo, jde, nil) } + +// GXCLo 黄经光行差修正(角秒) / ecliptic-longitude aberration correction in arcseconds. +func GXCLo(lo, bo, jde float64) float64 { return GXCLoWithInstance(lo, bo, jde, nil) } + +// GetMoonRiseTime 月出时间 / moonrise time. +func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { + return GetMoonRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, nil) +} + +// GetMoonSetTime 月落时间 / moonset time. +func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { + return GetMoonSetTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, nil) +} + +// GetSunRiseTimeN 日出时间(n 变体) / n-parameterised sunrise time. +func GetSunRiseTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) { + return GetSunRiseTimeNWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, n, nil) +} + +// GetSunSetTimeN 日落时间(n 变体) / n-parameterised sunset time. +func GetSunSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) { + return GetSunSetTimeNWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height, n, nil) +} + +// HMoonApparentDec 当地民用时下的月球视赤纬 / apparent lunar declination for a local civil Julian day. +func HMoonApparentDec(jd, lon, lat, tz float64) float64 { + return HMoonApparentDecWithInstance(jd, lon, lat, tz, nil) +} + +// HMoonApparentDecN 当地民用时下的月球视赤纬(n 变体) / n-parameterised apparent lunar declination. +func HMoonApparentDecN(jd, lon, lat, tz float64, n int) float64 { + return HMoonApparentDecNWithInstance(jd, lon, lat, tz, n, nil) +} + +// HMoonApparentRa 当地民用时下的月球视赤经 / apparent lunar right ascension for a local civil Julian day. +func HMoonApparentRa(jd, lon, lat, tz float64) float64 { + return HMoonApparentRaWithInstance(jd, lon, lat, tz, nil) +} + +// HMoonApparentRaN 当地民用时下的月球视赤经(n 变体) / n-parameterised apparent lunar right ascension. +func HMoonApparentRaN(jd, lon, lat, tz float64, n int) float64 { + return HMoonApparentRaNWithInstance(jd, lon, lat, tz, n, nil) +} + +// HMoonAzimuth 当地民用时下的月球方位角 / lunar azimuth for a local civil Julian day. +func HMoonAzimuth(jd, lon, lat, tz float64) float64 { + return HMoonAzimuthWithInstance(jd, lon, lat, tz, nil) +} + +// HMoonAzimuthN 当地民用时下的月球方位角(n 变体) / n-parameterised lunar azimuth. +func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 { + return HMoonAzimuthNWithInstance(jd, lon, lat, tz, n, nil) +} + +// HMoonHeightN 月心几何高度角(n 变体) / n-parameterised geometric Moon-centre altitude. +func HMoonHeightN(jd, lon, lat, tz float64, n int) float64 { + return HMoonHeightNWithInstance(jd, lon, lat, tz, n, nil) +} + +// HMoonTrueDecN 月球真赤纬(n 变体),含章动光行差,与 HMoonGeocentricApparentDecN 同值 / n-parameterised lunar declination, identical to HMoonGeocentricApparentDecN. +func HMoonTrueDecN(jde float64, n int) float64 { return HMoonTrueDecNWithInstance(jde, n, nil) } + +// HMoonTrueRaDecN 月球真赤经、真赤纬(n 变体),含章动光行差,与 HMoonGeocentricApparentRaDecN 同值 / n-parameterised lunar RA/Dec, identical to HMoonGeocentricApparentRaDecN. +func HMoonTrueRaDecN(jde float64, n int) (float64, float64) { + return HMoonTrueRaDecNWithInstance(jde, n, nil) +} + +// HMoonTrueRaN 月球真赤经(n 变体),含章动光行差,与 HMoonGeocentricApparentRaN 同值 / n-parameterised lunar right ascension, identical to HMoonGeocentricApparentRaN. +func HMoonTrueRaN(jde float64, n int) float64 { return HMoonTrueRaNWithInstance(jde, n, nil) } + +// JupiterAzimuth 木星方位角 / azimuth of Jupiter. +func JupiterAzimuth(localJD, lon, lat, timezone float64) float64 { + return JupiterAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// JupiterCulminationTime 木星中天时间 / culmination time of Jupiter. +func JupiterCulminationTime(localJD, lon, timezone float64) float64 { + return JupiterCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// JupiterHeight 木星高度角 / altitude of Jupiter. +func JupiterHeight(localJD, lon, lat, timezone float64) float64 { + return JupiterHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// JupiterHourAngle 木星时角 / hour angle of Jupiter. +func JupiterHourAngle(jd, lon, timezone float64) float64 { + return JupiterHourAngleWithInstance(jd, lon, timezone, nil) +} + +// JupiterRiseTime 木星升起时间 / rise time of Jupiter. +func JupiterRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return JupiterRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// JupiterSetTime 木星落下时间 / set time of Jupiter. +func JupiterSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return JupiterSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// LastJupiterConjunction 上一次木星合 / previous conjunction of Jupiter. +func LastJupiterConjunction(jde float64) float64 { return LastJupiterConjunctionWithInstance(jde, nil) } + +// LastJupiterEasternQuadrature 上一次木星东方照 / previous eastern quadrature of Jupiter. +func LastJupiterEasternQuadrature(jde float64) float64 { + return LastJupiterEasternQuadratureWithInstance(jde, nil) +} + +// LastJupiterOpposition 上一次木星冲 / previous opposition of Jupiter. +func LastJupiterOpposition(jde float64) float64 { return LastJupiterOppositionWithInstance(jde, nil) } + +// LastJupiterProgradeToRetrograde 上一次木星顺行转逆行的留 / previous prograde-to-retrograde station of Jupiter. +func LastJupiterProgradeToRetrograde(jde float64) float64 { + return LastJupiterProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastJupiterRetrogradeToPrograde 上一次木星逆行转顺行的留 / previous retrograde-to-prograde station of Jupiter. +func LastJupiterRetrogradeToPrograde(jde float64) float64 { + return LastJupiterRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastJupiterWesternQuadrature 上一次木星西方照 / previous western quadrature of Jupiter. +func LastJupiterWesternQuadrature(jde float64) float64 { + return LastJupiterWesternQuadratureWithInstance(jde, nil) +} + +// LastMarsConjunction 上一次火星合 / previous conjunction of Mars. +func LastMarsConjunction(jde float64) float64 { return LastMarsConjunctionWithInstance(jde, nil) } + +// LastMarsEasternQuadrature 上一次火星东方照 / previous eastern quadrature of Mars. +func LastMarsEasternQuadrature(jde float64) float64 { + return LastMarsEasternQuadratureWithInstance(jde, nil) +} + +// LastMarsOpposition 上一次火星冲 / previous opposition of Mars. +func LastMarsOpposition(jde float64) float64 { return LastMarsOppositionWithInstance(jde, nil) } + +// LastMarsProgradeToRetrograde 上一次火星顺行转逆行的留 / previous prograde-to-retrograde station of Mars. +func LastMarsProgradeToRetrograde(jde float64) float64 { + return LastMarsProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastMarsRetrogradeToPrograde 上一次火星逆行转顺行的留 / previous retrograde-to-prograde station of Mars. +func LastMarsRetrogradeToPrograde(jde float64) float64 { + return LastMarsRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastMarsWesternQuadrature 上一次火星西方照 / previous western quadrature of Mars. +func LastMarsWesternQuadrature(jde float64) float64 { + return LastMarsWesternQuadratureWithInstance(jde, nil) +} + +// LastMercuryConjunction 上一次水星合 / previous conjunction of Mercury. +func LastMercuryConjunction(jde float64) float64 { return LastMercuryConjunctionWithInstance(jde, nil) } + +// LastMercuryConjunctionStrict 上一次水星合,不含查询时刻 / previous conjunction of Mercury, excluding jde. +func LastMercuryConjunctionStrict(jde float64) float64 { + return LastMercuryConjunctionStrictWithInstance(jde, nil) +} + +// LastMercuryGreatestElongation 上一次水星大距(东西任一侧) / previous greatest elongation on either side of Mercury. +func LastMercuryGreatestElongation(jde float64) float64 { + return LastMercuryGreatestElongationWithInstance(jde, nil) +} + +// LastMercuryGreatestElongationEast 上一次水星东大距 / previous greatest eastern elongation of Mercury. +func LastMercuryGreatestElongationEast(jde float64) float64 { + return LastMercuryGreatestElongationEastWithInstance(jde, nil) +} + +// LastMercuryGreatestElongationEastInclusive 上一次水星东大距,含查询时刻 / previous greatest eastern elongation of Mercury, including jde. +func LastMercuryGreatestElongationEastInclusive(jde float64) float64 { + return LastMercuryGreatestElongationEastInclusiveWithInstance(jde, nil) +} + +// LastMercuryGreatestElongationInclusive 上一次水星大距(东西任一侧),含查询时刻 / previous greatest elongation on either side of Mercury, including jde. +func LastMercuryGreatestElongationInclusive(jde float64) float64 { + return LastMercuryGreatestElongationInclusiveWithInstance(jde, nil) +} + +// LastMercuryGreatestElongationWest 上一次水星西大距 / previous greatest western elongation of Mercury. +func LastMercuryGreatestElongationWest(jde float64) float64 { + return LastMercuryGreatestElongationWestWithInstance(jde, nil) +} + +// LastMercuryGreatestElongationWestInclusive 上一次水星西大距,含查询时刻 / previous greatest western elongation of Mercury, including jde. +func LastMercuryGreatestElongationWestInclusive(jde float64) float64 { + return LastMercuryGreatestElongationWestInclusiveWithInstance(jde, nil) +} + +// LastMercuryInferiorConjunction 上一次水星下合 / previous inferior conjunction of Mercury. +func LastMercuryInferiorConjunction(jde float64) float64 { + return LastMercuryInferiorConjunctionWithInstance(jde, nil) +} + +// LastMercuryInferiorConjunctionInclusive 上一次水星下合,含查询时刻 / previous inferior conjunction of Mercury, including jde. +func LastMercuryInferiorConjunctionInclusive(jde float64) float64 { + return LastMercuryInferiorConjunctionInclusiveWithInstance(jde, nil) +} + +// LastMercuryProgradeToRetrograde 上一次水星顺行转逆行的留 / previous prograde-to-retrograde station of Mercury. +func LastMercuryProgradeToRetrograde(jde float64) float64 { + return LastMercuryProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastMercuryProgradeToRetrogradeInclusive 上一次水星顺行转逆行的留,含查询时刻 / previous prograde-to-retrograde station of Mercury, including jde. +func LastMercuryProgradeToRetrogradeInclusive(jde float64) float64 { + return LastMercuryProgradeToRetrogradeInclusiveWithInstance(jde, nil) +} + +// LastMercuryRetrograde 上一次水星留(任一方向) / previous station in either direction of Mercury. +func LastMercuryRetrograde(jde float64) float64 { return LastMercuryRetrogradeWithInstance(jde, nil) } + +// LastMercuryRetrogradeInclusive 上一次水星留(任一方向),含查询时刻 / previous station in either direction of Mercury, including jde. +func LastMercuryRetrogradeInclusive(jde float64) float64 { + return LastMercuryRetrogradeInclusiveWithInstance(jde, nil) +} + +// LastMercuryRetrogradeToPrograde 上一次水星逆行转顺行的留 / previous retrograde-to-prograde station of Mercury. +func LastMercuryRetrogradeToPrograde(jde float64) float64 { + return LastMercuryRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastMercuryRetrogradeToProgradeInclusive 上一次水星逆行转顺行的留,含查询时刻 / previous retrograde-to-prograde station of Mercury, including jde. +func LastMercuryRetrogradeToProgradeInclusive(jde float64) float64 { + return LastMercuryRetrogradeToProgradeInclusiveWithInstance(jde, nil) +} + +// LastMercurySuperiorConjunction 上一次水星上合 / previous superior conjunction of Mercury. +func LastMercurySuperiorConjunction(jde float64) float64 { + return LastMercurySuperiorConjunctionWithInstance(jde, nil) +} + +// LastMercurySuperiorConjunctionInclusive 上一次水星上合,含查询时刻 / previous superior conjunction of Mercury, including jde. +func LastMercurySuperiorConjunctionInclusive(jde float64) float64 { + return LastMercurySuperiorConjunctionInclusiveWithInstance(jde, nil) +} + +// LastNeptuneConjunction 上一次海王星合 / previous conjunction of Neptune. +func LastNeptuneConjunction(jde float64) float64 { return LastNeptuneConjunctionWithInstance(jde, nil) } + +// LastNeptuneEasternQuadrature 上一次海王星东方照 / previous eastern quadrature of Neptune. +func LastNeptuneEasternQuadrature(jde float64) float64 { + return LastNeptuneEasternQuadratureWithInstance(jde, nil) +} + +// LastNeptuneOpposition 上一次海王星冲 / previous opposition of Neptune. +func LastNeptuneOpposition(jde float64) float64 { return LastNeptuneOppositionWithInstance(jde, nil) } + +// LastNeptuneProgradeToRetrograde 上一次海王星顺行转逆行的留 / previous prograde-to-retrograde station of Neptune. +func LastNeptuneProgradeToRetrograde(jde float64) float64 { + return LastNeptuneProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastNeptuneRetrogradeToPrograde 上一次海王星逆行转顺行的留 / previous retrograde-to-prograde station of Neptune. +func LastNeptuneRetrogradeToPrograde(jde float64) float64 { + return LastNeptuneRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastNeptuneWesternQuadrature 上一次海王星西方照 / previous western quadrature of Neptune. +func LastNeptuneWesternQuadrature(jde float64) float64 { + return LastNeptuneWesternQuadratureWithInstance(jde, nil) +} + +// LastSaturnConjunction 上一次土星合 / previous conjunction of Saturn. +func LastSaturnConjunction(jde float64) float64 { return LastSaturnConjunctionWithInstance(jde, nil) } + +// LastSaturnEasternQuadrature 上一次土星东方照 / previous eastern quadrature of Saturn. +func LastSaturnEasternQuadrature(jde float64) float64 { + return LastSaturnEasternQuadratureWithInstance(jde, nil) +} + +// LastSaturnOpposition 上一次土星冲 / previous opposition of Saturn. +func LastSaturnOpposition(jde float64) float64 { return LastSaturnOppositionWithInstance(jde, nil) } + +// LastSaturnProgradeToRetrograde 上一次土星顺行转逆行的留 / previous prograde-to-retrograde station of Saturn. +func LastSaturnProgradeToRetrograde(jde float64) float64 { + return LastSaturnProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastSaturnRetrogradeToPrograde 上一次土星逆行转顺行的留 / previous retrograde-to-prograde station of Saturn. +func LastSaturnRetrogradeToPrograde(jde float64) float64 { + return LastSaturnRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastSaturnWesternQuadrature 上一次土星西方照 / previous western quadrature of Saturn. +func LastSaturnWesternQuadrature(jde float64) float64 { + return LastSaturnWesternQuadratureWithInstance(jde, nil) +} + +// LastUranusConjunction 上一次天王星合 / previous conjunction of Uranus. +func LastUranusConjunction(jde float64) float64 { return LastUranusConjunctionWithInstance(jde, nil) } + +// LastUranusEasternQuadrature 上一次天王星东方照 / previous eastern quadrature of Uranus. +func LastUranusEasternQuadrature(jde float64) float64 { + return LastUranusEasternQuadratureWithInstance(jde, nil) +} + +// LastUranusOpposition 上一次天王星冲 / previous opposition of Uranus. +func LastUranusOpposition(jde float64) float64 { return LastUranusOppositionWithInstance(jde, nil) } + +// LastUranusProgradeToRetrograde 上一次天王星顺行转逆行的留 / previous prograde-to-retrograde station of Uranus. +func LastUranusProgradeToRetrograde(jde float64) float64 { + return LastUranusProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastUranusRetrogradeToPrograde 上一次天王星逆行转顺行的留 / previous retrograde-to-prograde station of Uranus. +func LastUranusRetrogradeToPrograde(jde float64) float64 { + return LastUranusRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastUranusWesternQuadrature 上一次天王星西方照 / previous western quadrature of Uranus. +func LastUranusWesternQuadrature(jde float64) float64 { + return LastUranusWesternQuadratureWithInstance(jde, nil) +} + +// LastVenusConjunction 上一次金星合 / previous conjunction of Venus. +func LastVenusConjunction(jde float64) float64 { return LastVenusConjunctionWithInstance(jde, nil) } + +// LastVenusConjunctionStrict 上一次金星合,不含查询时刻 / previous conjunction of Venus, excluding jde. +func LastVenusConjunctionStrict(jde float64) float64 { + return LastVenusConjunctionStrictWithInstance(jde, nil) +} + +// LastVenusGreatestElongation 上一次金星大距(东西任一侧) / previous greatest elongation on either side of Venus. +func LastVenusGreatestElongation(jde float64) float64 { + return LastVenusGreatestElongationWithInstance(jde, nil) +} + +// LastVenusGreatestElongationEast 上一次金星东大距 / previous greatest eastern elongation of Venus. +func LastVenusGreatestElongationEast(jde float64) float64 { + return LastVenusGreatestElongationEastWithInstance(jde, nil) +} + +// LastVenusGreatestElongationEastInclusive 上一次金星东大距,含查询时刻 / previous greatest eastern elongation of Venus, including jde. +func LastVenusGreatestElongationEastInclusive(jde float64) float64 { + return LastVenusGreatestElongationEastInclusiveWithInstance(jde, nil) +} + +// LastVenusGreatestElongationInclusive 上一次金星大距(东西任一侧),含查询时刻 / previous greatest elongation on either side of Venus, including jde. +func LastVenusGreatestElongationInclusive(jde float64) float64 { + return LastVenusGreatestElongationInclusiveWithInstance(jde, nil) +} + +// LastVenusGreatestElongationWest 上一次金星西大距 / previous greatest western elongation of Venus. +func LastVenusGreatestElongationWest(jde float64) float64 { + return LastVenusGreatestElongationWestWithInstance(jde, nil) +} + +// LastVenusGreatestElongationWestInclusive 上一次金星西大距,含查询时刻 / previous greatest western elongation of Venus, including jde. +func LastVenusGreatestElongationWestInclusive(jde float64) float64 { + return LastVenusGreatestElongationWestInclusiveWithInstance(jde, nil) +} + +// LastVenusInferiorConjunction 上一次金星下合 / previous inferior conjunction of Venus. +func LastVenusInferiorConjunction(jde float64) float64 { + return LastVenusInferiorConjunctionWithInstance(jde, nil) +} + +// LastVenusInferiorConjunctionInclusive 上一次金星下合,含查询时刻 / previous inferior conjunction of Venus, including jde. +func LastVenusInferiorConjunctionInclusive(jde float64) float64 { + return LastVenusInferiorConjunctionInclusiveWithInstance(jde, nil) +} + +// LastVenusProgradeToRetrograde 上一次金星顺行转逆行的留 / previous prograde-to-retrograde station of Venus. +func LastVenusProgradeToRetrograde(jde float64) float64 { + return LastVenusProgradeToRetrogradeWithInstance(jde, nil) +} + +// LastVenusProgradeToRetrogradeInclusive 上一次金星顺行转逆行的留,含查询时刻 / previous prograde-to-retrograde station of Venus, including jde. +func LastVenusProgradeToRetrogradeInclusive(jde float64) float64 { + return LastVenusProgradeToRetrogradeInclusiveWithInstance(jde, nil) +} + +// LastVenusRetrograde 上一次金星留(任一方向) / previous station in either direction of Venus. +func LastVenusRetrograde(jde float64) float64 { return LastVenusRetrogradeWithInstance(jde, nil) } + +// LastVenusRetrogradeInclusive 上一次金星留(任一方向),含查询时刻 / previous station in either direction of Venus, including jde. +func LastVenusRetrogradeInclusive(jde float64) float64 { + return LastVenusRetrogradeInclusiveWithInstance(jde, nil) +} + +// LastVenusRetrogradeToPrograde 上一次金星逆行转顺行的留 / previous retrograde-to-prograde station of Venus. +func LastVenusRetrogradeToPrograde(jde float64) float64 { + return LastVenusRetrogradeToProgradeWithInstance(jde, nil) +} + +// LastVenusRetrogradeToProgradeInclusive 上一次金星逆行转顺行的留,含查询时刻 / previous retrograde-to-prograde station of Venus, including jde. +func LastVenusRetrogradeToProgradeInclusive(jde float64) float64 { + return LastVenusRetrogradeToProgradeInclusiveWithInstance(jde, nil) +} + +// LastVenusSuperiorConjunction 上一次金星上合 / previous superior conjunction of Venus. +func LastVenusSuperiorConjunction(jde float64) float64 { + return LastVenusSuperiorConjunctionWithInstance(jde, nil) +} + +// LastVenusSuperiorConjunctionInclusive 上一次金星上合,含查询时刻 / previous superior conjunction of Venus, including jde. +func LastVenusSuperiorConjunctionInclusive(jde float64) float64 { + return LastVenusSuperiorConjunctionInclusiveWithInstance(jde, nil) +} + +// MarsAzimuth 火星方位角 / azimuth of Mars. +func MarsAzimuth(localJD, lon, lat, timezone float64) float64 { + return MarsAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// MarsCulminationTime 火星中天时间 / culmination time of Mars. +func MarsCulminationTime(localJD, lon, timezone float64) float64 { + return MarsCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// MarsHeight 火星高度角 / altitude of Mars. +func MarsHeight(localJD, lon, lat, timezone float64) float64 { + return MarsHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// MarsHourAngle 火星时角 / hour angle of Mars. +func MarsHourAngle(jd, lon, timezone float64) float64 { + return MarsHourAngleWithInstance(jd, lon, timezone, nil) +} + +// MarsRiseTime 火星升起时间 / rise time of Mars. +func MarsRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return MarsRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// MarsSetTime 火星落下时间 / set time of Mars. +func MarsSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return MarsSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// MercuryAzimuth 水星方位角 / azimuth of Mercury. +func MercuryAzimuth(localJD, lon, lat, timezone float64) float64 { + return MercuryAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// MercuryCulminationTime 水星中天时间 / culmination time of Mercury. +func MercuryCulminationTime(localJD, lon, timezone float64) float64 { + return MercuryCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// MercuryHeight 水星高度角 / altitude of Mercury. +func MercuryHeight(localJD, lon, lat, timezone float64) float64 { + return MercuryHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// MercuryHourAngle 水星时角 / hour angle of Mercury. +func MercuryHourAngle(jd, lon, timezone float64) float64 { + return MercuryHourAngleWithInstance(jd, lon, timezone, nil) +} + +// MercuryRiseTime 水星升起时间 / rise time of Mercury. +func MercuryRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return MercuryRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// MercurySetTime 水星落下时间 / set time of Mercury. +func MercurySetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return MercurySetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// MercurySunElongation 水星与太阳的角距 / elongation from the Sun of Mercury. +func MercurySunElongation(jde float64) float64 { return MercurySunElongationWithInstance(jde, nil) } + +// MoonCulminationTime 月球中天时间 / lunar culmination time. +func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 { + return MoonCulminationTimeWithInstance(localJD, lon, lat, timezone, nil) +} + +// MoonTimeAngle 月球时角 / lunar hour angle. +func MoonTimeAngle(jd, lon, lat, tz float64) float64 { + return MoonTimeAngleWithInstance(jd, lon, lat, tz, nil) +} + +// MorningTwilight 晨朦影;localJD 是本地民用日锚点(当地 0 时),只取整数日 / morning twilight on the local civil day. +func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, error) { + return MorningTwilightWithInstance(localJD, lon, lat, tz, targetAltitude, nil) +} + +// MorningTwilightN 晨朦影(n 变体) / n-parameterised morning twilight. +func MorningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (float64, error) { + return MorningTwilightNWithInstance(localJD, lon, lat, tz, targetAltitude, n, nil) +} + +// NeptuneAzimuth 海王星方位角 / azimuth of Neptune. +func NeptuneAzimuth(localJD, lon, lat, timezone float64) float64 { + return NeptuneAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// NeptuneCulminationTime 海王星中天时间 / culmination time of Neptune. +func NeptuneCulminationTime(localJD, lon, timezone float64) float64 { + return NeptuneCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// NeptuneHeight 海王星高度角 / altitude of Neptune. +func NeptuneHeight(localJD, lon, lat, timezone float64) float64 { + return NeptuneHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// NeptuneHourAngle 海王星时角 / hour angle of Neptune. +func NeptuneHourAngle(jd, lon, timezone float64) float64 { + return NeptuneHourAngleWithInstance(jd, lon, timezone, nil) +} + +// NeptuneRiseTime 海王星升起时间 / rise time of Neptune. +func NeptuneRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return NeptuneRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// NeptuneSetTime 海王星落下时间 / set time of Neptune. +func NeptuneSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return NeptuneSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// NextJupiterConjunction 下一次木星合 / next conjunction of Jupiter. +func NextJupiterConjunction(jde float64) float64 { return NextJupiterConjunctionWithInstance(jde, nil) } + +// NextJupiterEasternQuadrature 下一次木星东方照 / next eastern quadrature of Jupiter. +func NextJupiterEasternQuadrature(jde float64) float64 { + return NextJupiterEasternQuadratureWithInstance(jde, nil) +} + +// NextJupiterOpposition 下一次木星冲 / next opposition of Jupiter. +func NextJupiterOpposition(jde float64) float64 { return NextJupiterOppositionWithInstance(jde, nil) } + +// NextJupiterProgradeToRetrograde 下一次木星顺行转逆行的留 / next prograde-to-retrograde station of Jupiter. +func NextJupiterProgradeToRetrograde(jde float64) float64 { + return NextJupiterProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextJupiterRetrogradeToPrograde 下一次木星逆行转顺行的留 / next retrograde-to-prograde station of Jupiter. +func NextJupiterRetrogradeToPrograde(jde float64) float64 { + return NextJupiterRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextJupiterWesternQuadrature 下一次木星西方照 / next western quadrature of Jupiter. +func NextJupiterWesternQuadrature(jde float64) float64 { + return NextJupiterWesternQuadratureWithInstance(jde, nil) +} + +// NextMarsConjunction 下一次火星合 / next conjunction of Mars. +func NextMarsConjunction(jde float64) float64 { return NextMarsConjunctionWithInstance(jde, nil) } + +// NextMarsEasternQuadrature 下一次火星东方照 / next eastern quadrature of Mars. +func NextMarsEasternQuadrature(jde float64) float64 { + return NextMarsEasternQuadratureWithInstance(jde, nil) +} + +// NextMarsOpposition 下一次火星冲 / next opposition of Mars. +func NextMarsOpposition(jde float64) float64 { return NextMarsOppositionWithInstance(jde, nil) } + +// NextMarsProgradeToRetrograde 下一次火星顺行转逆行的留 / next prograde-to-retrograde station of Mars. +func NextMarsProgradeToRetrograde(jde float64) float64 { + return NextMarsProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextMarsRetrogradeToPrograde 下一次火星逆行转顺行的留 / next retrograde-to-prograde station of Mars. +func NextMarsRetrogradeToPrograde(jde float64) float64 { + return NextMarsRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextMarsWesternQuadrature 下一次火星西方照 / next western quadrature of Mars. +func NextMarsWesternQuadrature(jde float64) float64 { + return NextMarsWesternQuadratureWithInstance(jde, nil) +} + +// NextMercuryConjunction 下一次水星合 / next conjunction of Mercury. +func NextMercuryConjunction(jde float64) float64 { return NextMercuryConjunctionWithInstance(jde, nil) } + +// NextMercuryConjunctionStrict 下一次水星合,不含查询时刻 / next conjunction of Mercury, excluding jde. +func NextMercuryConjunctionStrict(jde float64) float64 { + return NextMercuryConjunctionStrictWithInstance(jde, nil) +} + +// NextMercuryGreatestElongationEast 下一次水星东大距 / next greatest eastern elongation of Mercury. +func NextMercuryGreatestElongationEast(jde float64) float64 { + return NextMercuryGreatestElongationEastWithInstance(jde, nil) +} + +// NextMercuryGreatestElongationEastInclusive 下一次水星东大距,含查询时刻 / next greatest eastern elongation of Mercury, including jde. +func NextMercuryGreatestElongationEastInclusive(jde float64) float64 { + return NextMercuryGreatestElongationEastInclusiveWithInstance(jde, nil) +} + +// NextMercuryGreatestElongationInclusive 下一次水星大距(东西任一侧),含查询时刻 / next greatest elongation on either side of Mercury, including jde. +func NextMercuryGreatestElongationInclusive(jde float64) float64 { + return NextMercuryGreatestElongationInclusiveWithInstance(jde, nil) +} + +// NextMercuryGreatestElongationWest 下一次水星西大距 / next greatest western elongation of Mercury. +func NextMercuryGreatestElongationWest(jde float64) float64 { + return NextMercuryGreatestElongationWestWithInstance(jde, nil) +} + +// NextMercuryGreatestElongationWestInclusive 下一次水星西大距,含查询时刻 / next greatest western elongation of Mercury, including jde. +func NextMercuryGreatestElongationWestInclusive(jde float64) float64 { + return NextMercuryGreatestElongationWestInclusiveWithInstance(jde, nil) +} + +// NextMercuryInferiorConjunction 下一次水星下合 / next inferior conjunction of Mercury. +func NextMercuryInferiorConjunction(jde float64) float64 { + return NextMercuryInferiorConjunctionWithInstance(jde, nil) +} + +// NextMercuryInferiorConjunctionInclusive 下一次水星下合,含查询时刻 / next inferior conjunction of Mercury, including jde. +func NextMercuryInferiorConjunctionInclusive(jde float64) float64 { + return NextMercuryInferiorConjunctionInclusiveWithInstance(jde, nil) +} + +// NextMercuryProgradeToRetrogradeInclusive 下一次水星顺行转逆行的留,含查询时刻 / next prograde-to-retrograde station of Mercury, including jde. +func NextMercuryProgradeToRetrogradeInclusive(jde float64) float64 { + return NextMercuryProgradeToRetrogradeInclusiveWithInstance(jde, nil) +} + +// NextMercuryRetrograde 下一次水星留(任一方向) / next station in either direction of Mercury. +func NextMercuryRetrograde(jde float64) float64 { return NextMercuryRetrogradeWithInstance(jde, nil) } + +// NextMercuryRetrogradeInclusive 下一次水星留(任一方向),含查询时刻 / next station in either direction of Mercury, including jde. +func NextMercuryRetrogradeInclusive(jde float64) float64 { + return NextMercuryRetrogradeInclusiveWithInstance(jde, nil) +} + +// NextMercuryRetrogradeToPrograde 下一次水星逆行转顺行的留 / next retrograde-to-prograde station of Mercury. +func NextMercuryRetrogradeToPrograde(jde float64) float64 { + return NextMercuryRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextMercuryRetrogradeToProgradeInclusive 下一次水星逆行转顺行的留,含查询时刻 / next retrograde-to-prograde station of Mercury, including jde. +func NextMercuryRetrogradeToProgradeInclusive(jde float64) float64 { + return NextMercuryRetrogradeToProgradeInclusiveWithInstance(jde, nil) +} + +// NextMercurySuperiorConjunction 下一次水星上合 / next superior conjunction of Mercury. +func NextMercurySuperiorConjunction(jde float64) float64 { + return NextMercurySuperiorConjunctionWithInstance(jde, nil) +} + +// NextMercurySuperiorConjunctionInclusive 下一次水星上合,含查询时刻 / next superior conjunction of Mercury, including jde. +func NextMercurySuperiorConjunctionInclusive(jde float64) float64 { + return NextMercurySuperiorConjunctionInclusiveWithInstance(jde, nil) +} + +// NextNeptuneConjunction 下一次海王星合 / next conjunction of Neptune. +func NextNeptuneConjunction(jde float64) float64 { return NextNeptuneConjunctionWithInstance(jde, nil) } + +// NextNeptuneEasternQuadrature 下一次海王星东方照 / next eastern quadrature of Neptune. +func NextNeptuneEasternQuadrature(jde float64) float64 { + return NextNeptuneEasternQuadratureWithInstance(jde, nil) +} + +// NextNeptuneOpposition 下一次海王星冲 / next opposition of Neptune. +func NextNeptuneOpposition(jde float64) float64 { return NextNeptuneOppositionWithInstance(jde, nil) } + +// NextNeptuneProgradeToRetrograde 下一次海王星顺行转逆行的留 / next prograde-to-retrograde station of Neptune. +func NextNeptuneProgradeToRetrograde(jde float64) float64 { + return NextNeptuneProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextNeptuneRetrogradeToPrograde 下一次海王星逆行转顺行的留 / next retrograde-to-prograde station of Neptune. +func NextNeptuneRetrogradeToPrograde(jde float64) float64 { + return NextNeptuneRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextNeptuneWesternQuadrature 下一次海王星西方照 / next western quadrature of Neptune. +func NextNeptuneWesternQuadrature(jde float64) float64 { + return NextNeptuneWesternQuadratureWithInstance(jde, nil) +} + +// NextSaturnConjunction 下一次土星合 / next conjunction of Saturn. +func NextSaturnConjunction(jde float64) float64 { return NextSaturnConjunctionWithInstance(jde, nil) } + +// NextSaturnEasternQuadrature 下一次土星东方照 / next eastern quadrature of Saturn. +func NextSaturnEasternQuadrature(jde float64) float64 { + return NextSaturnEasternQuadratureWithInstance(jde, nil) +} + +// NextSaturnOpposition 下一次土星冲 / next opposition of Saturn. +func NextSaturnOpposition(jde float64) float64 { return NextSaturnOppositionWithInstance(jde, nil) } + +// NextSaturnProgradeToRetrograde 下一次土星顺行转逆行的留 / next prograde-to-retrograde station of Saturn. +func NextSaturnProgradeToRetrograde(jde float64) float64 { + return NextSaturnProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextSaturnRetrogradeToPrograde 下一次土星逆行转顺行的留 / next retrograde-to-prograde station of Saturn. +func NextSaturnRetrogradeToPrograde(jde float64) float64 { + return NextSaturnRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextSaturnWesternQuadrature 下一次土星西方照 / next western quadrature of Saturn. +func NextSaturnWesternQuadrature(jde float64) float64 { + return NextSaturnWesternQuadratureWithInstance(jde, nil) +} + +// NextUranusConjunction 下一次天王星合 / next conjunction of Uranus. +func NextUranusConjunction(jde float64) float64 { return NextUranusConjunctionWithInstance(jde, nil) } + +// NextUranusEasternQuadrature 下一次天王星东方照 / next eastern quadrature of Uranus. +func NextUranusEasternQuadrature(jde float64) float64 { + return NextUranusEasternQuadratureWithInstance(jde, nil) +} + +// NextUranusOpposition 下一次天王星冲 / next opposition of Uranus. +func NextUranusOpposition(jde float64) float64 { return NextUranusOppositionWithInstance(jde, nil) } + +// NextUranusProgradeToRetrograde 下一次天王星顺行转逆行的留 / next prograde-to-retrograde station of Uranus. +func NextUranusProgradeToRetrograde(jde float64) float64 { + return NextUranusProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextUranusRetrogradeToPrograde 下一次天王星逆行转顺行的留 / next retrograde-to-prograde station of Uranus. +func NextUranusRetrogradeToPrograde(jde float64) float64 { + return NextUranusRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextUranusWesternQuadrature 下一次天王星西方照 / next western quadrature of Uranus. +func NextUranusWesternQuadrature(jde float64) float64 { + return NextUranusWesternQuadratureWithInstance(jde, nil) +} + +// NextVenusConjunction 下一次金星合 / next conjunction of Venus. +func NextVenusConjunction(jde float64) float64 { return NextVenusConjunctionWithInstance(jde, nil) } + +// NextVenusConjunctionStrict 下一次金星合,不含查询时刻 / next conjunction of Venus, excluding jde. +func NextVenusConjunctionStrict(jde float64) float64 { + return NextVenusConjunctionStrictWithInstance(jde, nil) +} + +// NextVenusGreatestElongationEast 下一次金星东大距 / next greatest eastern elongation of Venus. +func NextVenusGreatestElongationEast(jde float64) float64 { + return NextVenusGreatestElongationEastWithInstance(jde, nil) +} + +// NextVenusGreatestElongationEastInclusive 下一次金星东大距,含查询时刻 / next greatest eastern elongation of Venus, including jde. +func NextVenusGreatestElongationEastInclusive(jde float64) float64 { + return NextVenusGreatestElongationEastInclusiveWithInstance(jde, nil) +} + +// NextVenusGreatestElongationInclusive 下一次金星大距(东西任一侧),含查询时刻 / next greatest elongation on either side of Venus, including jde. +func NextVenusGreatestElongationInclusive(jde float64) float64 { + return NextVenusGreatestElongationInclusiveWithInstance(jde, nil) +} + +// NextVenusGreatestElongationWest 下一次金星西大距 / next greatest western elongation of Venus. +func NextVenusGreatestElongationWest(jde float64) float64 { + return NextVenusGreatestElongationWestWithInstance(jde, nil) +} + +// NextVenusGreatestElongationWestInclusive 下一次金星西大距,含查询时刻 / next greatest western elongation of Venus, including jde. +func NextVenusGreatestElongationWestInclusive(jde float64) float64 { + return NextVenusGreatestElongationWestInclusiveWithInstance(jde, nil) +} + +// NextVenusInferiorConjunction 下一次金星下合 / next inferior conjunction of Venus. +func NextVenusInferiorConjunction(jde float64) float64 { + return NextVenusInferiorConjunctionWithInstance(jde, nil) +} + +// NextVenusInferiorConjunctionInclusive 下一次金星下合,含查询时刻 / next inferior conjunction of Venus, including jde. +func NextVenusInferiorConjunctionInclusive(jde float64) float64 { + return NextVenusInferiorConjunctionInclusiveWithInstance(jde, nil) +} + +// NextVenusProgradeToRetrograde 下一次金星顺行转逆行的留 / next prograde-to-retrograde station of Venus. +func NextVenusProgradeToRetrograde(jde float64) float64 { + return NextVenusProgradeToRetrogradeWithInstance(jde, nil) +} + +// NextVenusProgradeToRetrogradeInclusive 下一次金星顺行转逆行的留,含查询时刻 / next prograde-to-retrograde station of Venus, including jde. +func NextVenusProgradeToRetrogradeInclusive(jde float64) float64 { + return NextVenusProgradeToRetrogradeInclusiveWithInstance(jde, nil) +} + +// NextVenusRetrograde 下一次金星留(任一方向) / next station in either direction of Venus. +func NextVenusRetrograde(jde float64) float64 { return NextVenusRetrogradeWithInstance(jde, nil) } + +// NextVenusRetrogradeInclusive 下一次金星留(任一方向),含查询时刻 / next station in either direction of Venus, including jde. +func NextVenusRetrogradeInclusive(jde float64) float64 { + return NextVenusRetrogradeInclusiveWithInstance(jde, nil) +} + +// NextVenusRetrogradeToPrograde 下一次金星逆行转顺行的留 / next retrograde-to-prograde station of Venus. +func NextVenusRetrogradeToPrograde(jde float64) float64 { + return NextVenusRetrogradeToProgradeWithInstance(jde, nil) +} + +// NextVenusRetrogradeToProgradeInclusive 下一次金星逆行转顺行的留,含查询时刻 / next retrograde-to-prograde station of Venus, including jde. +func NextVenusRetrogradeToProgradeInclusive(jde float64) float64 { + return NextVenusRetrogradeToProgradeInclusiveWithInstance(jde, nil) +} + +// NextVenusSuperiorConjunction 下一次金星上合 / next superior conjunction of Venus. +func NextVenusSuperiorConjunction(jde float64) float64 { + return NextVenusSuperiorConjunctionWithInstance(jde, nil) +} + +// NextVenusSuperiorConjunctionInclusive 下一次金星上合,含查询时刻 / next superior conjunction of Venus, including jde. +func NextVenusSuperiorConjunctionInclusive(jde float64) float64 { + return NextVenusSuperiorConjunctionInclusiveWithInstance(jde, nil) +} + +// SaturnAzimuth 土星方位角 / azimuth of Saturn. +func SaturnAzimuth(localJD, lon, lat, timezone float64) float64 { + return SaturnAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// SaturnCulminationTime 土星中天时间 / culmination time of Saturn. +func SaturnCulminationTime(localJD, lon, timezone float64) float64 { + return SaturnCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// SaturnHeight 土星高度角 / altitude of Saturn. +func SaturnHeight(localJD, lon, lat, timezone float64) float64 { + return SaturnHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// SaturnHourAngle 土星时角 / hour angle of Saturn. +func SaturnHourAngle(jd, lon, timezone float64) float64 { + return SaturnHourAngleWithInstance(jd, lon, timezone, nil) +} + +// SaturnRiseTime 土星升起时间 / rise time of Saturn. +func SaturnRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return SaturnRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// SaturnSetTime 土星落下时间 / set time of Saturn. +func SaturnSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return SaturnSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// UranusAzimuth 天王星方位角 / azimuth of Uranus. +func UranusAzimuth(localJD, lon, lat, timezone float64) float64 { + return UranusAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// UranusCulminationTime 天王星中天时间 / culmination time of Uranus. +func UranusCulminationTime(localJD, lon, timezone float64) float64 { + return UranusCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// UranusHeight 天王星高度角 / altitude of Uranus. +func UranusHeight(localJD, lon, lat, timezone float64) float64 { + return UranusHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// UranusHourAngle 天王星时角 / hour angle of Uranus. +func UranusHourAngle(jd, lon, timezone float64) float64 { + return UranusHourAngleWithInstance(jd, lon, timezone, nil) +} + +// UranusRiseTime 天王星升起时间 / rise time of Uranus. +func UranusRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return UranusRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// UranusSetTime 天王星落下时间 / set time of Uranus. +func UranusSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { + return UranusSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight, nil) +} + +// VenusAzimuth 金星方位角 / azimuth of Venus. +func VenusAzimuth(localJD, lon, lat, timezone float64) float64 { + return VenusAzimuthWithInstance(localJD, lon, lat, timezone, nil) +} + +// VenusCulminationTime 金星中天时间 / culmination time of Venus. +func VenusCulminationTime(localJD, lon, timezone float64) float64 { + return VenusCulminationTimeWithInstance(localJD, lon, timezone, nil) +} + +// VenusHeight 金星高度角 / altitude of Venus. +func VenusHeight(localJD, lon, lat, timezone float64) float64 { + return VenusHeightWithInstance(localJD, lon, lat, timezone, nil) +} + +// VenusHourAngle 金星时角 / hour angle of Venus. +func VenusHourAngle(jd, lon, tz float64) float64 { return VenusHourAngleWithInstance(jd, lon, tz, nil) } + +// VenusRiseTime 金星升起时间 / rise time of Venus. +func VenusRiseTime(jd, lon, lat, tz, aeroCorrection, observerHeight float64) (float64, error) { + return VenusRiseTimeWithInstance(jd, lon, lat, tz, aeroCorrection, observerHeight, nil) +} + +// VenusSetTime 金星落下时间 / set time of Venus. +func VenusSetTime(jd, lon, lat, tz, aeroCorrection, observerHeight float64) (float64, error) { + return VenusSetTimeWithInstance(jd, lon, lat, tz, aeroCorrection, observerHeight, nil) +} + +// VenusSunElongation 金星与太阳的角距 / elongation from the Sun of Venus. +func VenusSunElongation(jde float64) float64 { return VenusSunElongationWithInstance(jde, nil) } diff --git a/basic/apparent_direction_test.go b/basic/apparent_direction_test.go new file mode 100644 index 0000000..040dcdb --- /dev/null +++ b/basic/apparent_direction_test.go @@ -0,0 +1,34 @@ +// 被测契约:日月视赤经纬单值与双值接口一致,且黄经、黄纬取自同一视方向。 +package basic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/ephemtest" +) + +func TestSunMoonApparentRaDecUseFullDirection(t *testing.T) { + ephemtest.Pin(t, ephem.Semianalytic) + const jde = 2460409.25 + for _, body := range []ephem.Body{ephem.Sun, ephem.Moon} { + lo, bo, _, _, ok := vectorApparent(body, jde, -1) + if !ok { + t.Fatalf("body %d: no vector apparent position", body) + } + wantRA, wantDec := LoBoToRaDec(jde, lo, bo) + var ra, dec float64 + if body == ephem.Sun { + ra, dec = HSunApparentRaDec(jde) + if r, d := HSunApparentRa(jde), HSunApparentDec(jde); r != ra || d != dec { + t.Errorf("Sun single (%v, %v) != pair (%v, %v)", r, d, ra, dec) + } + } else { + ra, dec = HMoonGeocentricApparentRaDecN(jde, -1) + } + if math.Abs(ra-wantRA)*3600 > 1e-6 || math.Abs(dec-wantDec)*3600 > 1e-6 { + t.Errorf("body %d: (%v, %v), want (%v, %v)", body, ra, dec, wantRA, wantDec) + } + } +} diff --git a/basic/apparent_state_test.go b/basic/apparent_state_test.go new file mode 100644 index 0000000..5ad6e3b --- /dev/null +++ b/basic/apparent_state_test.go @@ -0,0 +1,46 @@ +// 被测契约:包级与实例视位置共用同一判据,零方向、非正距离、负光行时与非有限值一律报错。 +package basic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +type fixedApparentEngine struct { + dir [3]float64 + dist, tau float64 +} + +func (e fixedApparentEngine) State(target, center ephem.Body, jdTDB float64) (pos, vel [3]float64, err error) { + return pos, vel, ephem.ErrUnsupportedBody +} + +func (e fixedApparentEngine) Coverage(ephem.Body) (start, end float64, ok bool) { return 0, 0, false } + +func (e fixedApparentEngine) Apparent(ephem.Body, float64) ([3]float64, float64, float64, error) { + return e.dir, e.dist, e.tau, nil +} + +func TestApparentStateRejectsInvalid(t *testing.T) { + nan := math.NaN() + for _, c := range []struct { + name string + e fixedApparentEngine + ok bool + }{ + {"valid", fixedApparentEngine{[3]float64{1, 0, 0}, 1.5e8, 0.0058}, true}, + {"nan direction", fixedApparentEngine{[3]float64{nan, 0, 0}, 1.5e8, 0.0058}, false}, + {"zero direction", fixedApparentEngine{[3]float64{}, 1.5e8, 0.0058}, false}, + {"zero distance", fixedApparentEngine{[3]float64{1, 0, 0}, 0, 0.0058}, false}, + {"inf distance", fixedApparentEngine{[3]float64{1, 0, 0}, math.Inf(1), 0.0058}, false}, + {"negative tau", fixedApparentEngine{[3]float64{1, 0, 0}, 1.5e8, -1}, false}, + {"nan tau", fixedApparentEngine{[3]float64{1, 0, 0}, 1.5e8, nan}, false}, + } { + _, _, _, _, ok, err := apparentState(ephem.Sun, 2451545, ephem.NewInstance(c.e)) + if c.ok && (err != nil || !ok) || !c.ok && err == nil { + t.Errorf("%s: ok=%v err=%v", c.name, ok, err) + } + } +} diff --git a/basic/apparent_vector.go b/basic/apparent_vector.go new file mode 100644 index 0000000..d5e3edf --- /dev/null +++ b/basic/apparent_vector.go @@ -0,0 +1,77 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/ephem" +) + +// planetBody 旧模型行星序号 1..7(水星..海王星,不含地球)对应的 NAIF 编号。 +var planetBody = [8]ephem.Body{0, ephem.Mercury, ephem.Venus, ephem.Mars, ephem.Jupiter, ephem.Saturn, ephem.Uranus, ephem.Neptune} + +// vectorApparent 新模型全精度时的矢量视位置:真黄道经纬(度)、光行时(日)、几何地心距(AU);不适用时 ok=false。 +// 状态非法时与显式实例路径一致:ok=true 且各值为 NaN,不得退回低精度公式掩盖错误。 +func vectorApparent(target ephem.Body, jde float64, n int) (lo, bo, tau, distAU float64, ok bool) { + if n >= 0 || ephem.Active() != ephem.Semianalytic { + return 0, 0, 0, 0, false + } + lo, bo, tau, distAU, ok, err := apparentState(target, jde, ephem.Global()) + if err != nil { + return math.NaN(), math.NaN(), math.NaN(), math.NaN(), true + } + return lo, bo, tau, distAU, ok +} + +// icrfToTrueEcliptic ICRF 单位矢量 → 真黄道经纬(度);黄赤交角与下游 LoBoToRaDec 同口径,往返得到真赤道。 +func icrfToTrueEcliptic(d [3]float64, jde float64) (lo, bo float64) { + m := ltpPBMAT(2000 + (jde-2451545)/365.25) + var q [3]float64 + for i := range q { + q[i] = m[i][0]*d[0] + m[i][1]*d[1] + m[i][2]*d[2] + } + s, c := math.Sincos(Obliquity1980(jde) * math.Pi / 180) + x, y, z := q[0], q[1]*c+q[2]*s, -q[1]*s+q[2]*c + lo = math.Atan2(y, x)*180/math.Pi + Nutation2000Bi(jde) + bo = math.Atan2(z, math.Hypot(x, y)) * 180 / math.Pi + return math.Mod(lo+720, 360), bo +} + +// sunApparentLoBoN 太阳视黄经与黄纬;有矢量视位置时两者取自同一方向,否则退回视黄经配几何黄纬。 +func sunApparentLoBoN(jde float64, n int, inst *Instance) (float64, float64) { + if lo, bo, _, _, ok, err := vectorApparentWithInstance(ephem.Sun, jde, n, inst); err != nil { + return math.NaN(), math.NaN() + } else if ok { + return lo, bo + } + return HSunApparentLoNWithInstance(jde, n, inst), HSunTrueBoNWithInstance(jde, n, inst) +} + +// moonApparentLoBoN 月球视黄经与黄纬;有矢量视位置时两者取自同一方向,否则退回视黄经配几何黄纬。 +func moonApparentLoBoN(jde float64, n int, inst *Instance) (float64, float64) { + if lo, bo, _, _, ok, err := vectorApparentWithInstance(ephem.Moon, jde, n, inst); err != nil { + return math.NaN(), math.NaN() + } else if ok { + return lo, bo + } + return HMoonApparentLoNWithInstance(jde, n, inst), HMoonTrueBoNWithInstance(jde, n, inst) +} + +func sunApparentRaDecN(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := sunApparentLoBoN(jde, n, inst) + return LoBoToRaDec(jde, lo, bo) +} + +func sunApparentRaN(jde float64, n int, inst *Instance) float64 { + ra, _ := sunApparentRaDecN(jde, n, inst) + return ra +} + +func moonApparentRaDecN(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := moonApparentLoBoN(jde, n, inst) + return LoBoToRaDec(jde, lo, bo) +} + +func moonApparentRaN(jde float64, n int, inst *Instance) float64 { + ra, _ := moonApparentRaDecN(jde, n, inst) + return ra +} diff --git a/basic/apsis.go b/basic/apsis.go index 398f2e1..2220139 100644 --- a/basic/apsis.go +++ b/basic/apsis.go @@ -57,14 +57,14 @@ func EarthAphelion(year int) ApsisEvent { return earthApsis(year, true) } -// MoonPerigees 指定年月内的所有月球近地点 / all lunar perigees in the given Gregorian month. -func MoonPerigees(year int, month time.Month) []ApsisEvent { - return moonApsisInMonth(year, month, false) +// MoonPerigeesWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonPerigeesWithInstance(year int, month time.Month, inst *Instance) []ApsisEvent { + return moonApsisInMonth(year, month, false, inst) } -// MoonApogees 指定年月内的所有月球远地点 / all lunar apogees in the given Gregorian month. -func MoonApogees(year int, month time.Month) []ApsisEvent { - return moonApsisInMonth(year, month, true) +// MoonApogeesWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonApogeesWithInstance(year int, month time.Month, inst *Instance) []ApsisEvent { + return moonApsisInMonth(year, month, true, inst) } func earthApsis(year int, aphelion bool) ApsisEvent { @@ -84,7 +84,9 @@ func earthApsis(year int, aphelion bool) ApsisEvent { } } -func moonApsisInMonth(year int, month time.Month, apogee bool) []ApsisEvent { +func moonApsisInMonth(year int, month time.Month, apogee bool, instances ...*Instance) []ApsisEvent { + inst := instanceValue(instances) + startUTC := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) endUTC := startUTC.AddDate(0, 1, 0) startTT := UTC2TT(Date2JD(startUTC)) @@ -109,7 +111,7 @@ func moonApsisInMonth(year int, month time.Month, apogee bool) []ApsisEvent { events := make([]ApsisEvent, 0, 2) for k := kStart; k <= kEnd; k++ { seedTT := moonApsisSeedTT(float64(k) + phase) - eventTT, distanceKM := refineDistanceExtremum(seedTT, cfg, HMoonAway) + eventTT, distanceKM := refineDistanceExtremum(seedTT, cfg, func(jde float64) float64 { return HMoonAwayWithInstance(jde, inst) }) eventUT := TT2UTC(eventTT) eventTimeUTC := JD2DateByZone(eventUT, time.UTC, false) if eventTimeUTC.Before(startUTC) || !eventTimeUTC.Before(endUTC) { diff --git a/basic/apsis_test.go b/basic/apsis_test.go index 3a16a7b..a2126f0 100644 --- a/basic/apsis_test.go +++ b/basic/apsis_test.go @@ -87,7 +87,6 @@ func TestEarthApsisMatchesHorizonsBaseline(t *testing.T) { } func TestMoonApsisMatchesHorizonsBaseline(t *testing.T) { - // Baseline is generated from JPL Horizons by scripts/generate_moon_apsis_baseline.sh. data, err := os.ReadFile("testdata/moon_apsis_baseline.json") if err != nil { t.Fatalf("read baseline: %v", err) diff --git a/basic/calendar_test.go b/basic/calendar_test.go index 05fd9db..006d9dc 100644 --- a/basic/calendar_test.go +++ b/basic/calendar_test.go @@ -61,8 +61,9 @@ func TestGetJQTime(t *testing.T) { for _, tc := range testCases { originalResult := originalFunc(tc.year, tc.angle) optimizedResult := GetJQTime(tc.year, tc.angle) + // 两种解法各自收敛到 1e-5 日,末步残差可达数十微秒;1e-8 日足以区分选错相邻年份的根。 diff := math.Abs(originalResult - optimizedResult) - if diff > 1e-10 { + if diff > 1e-8 { t.Fatalf("GetJQTime mismatch: year=%d angle=%d original=%.15f optimized=%.15f diff=%.15f", tc.year, tc.angle, originalResult, optimizedResult, diff) } diff --git a/basic/constellation.go b/basic/constellation.go index 35f4d98..0268b5b 100644 --- a/basic/constellation.go +++ b/basic/constellation.go @@ -1,6 +1,8 @@ package basic import ( + "math" + . "b612.me/astro/tools" ) @@ -148,29 +150,66 @@ func isCross(a, b, c, d constellationPoint) bool { } func resolveConstellationCode(ra, dec, jde float64) string { - var nra, ndec float64 initConstellationData() - nra = ra - if ra >= 360 { - nra -= 360 - } - nra, ndec = Precess(nra, dec, jde, 2451545.0) - if ra >= 360 && nra < 270 { - nra += 360 - } - if code := matchConstellationCode(nra, ndec); code != "" { + code, nra, ndec := lookupConstellationCode(ra, dec, jde) + if code != "" { return code } - if nra <= 270 { - ra = ra + 360 - return resolveConstellationCode(ra, dec, jde) + // 只从 [0,360) 补一圈重试一次,匹配 RA 已加 360 的跨零点星座。 + if nra <= 270 && ra < 360 { + if code, _, _ = lookupConstellationCode(ra+360, dec, jde); code != "" { + return code + } } if ndec > 50 { return "UMI" } else if ndec < -50 { return "OCT" } - return "" + if ra >= 360 { + return "" + } + // 边界数据相邻多边形之间有细缝,落在缝里的点归最近的边。 + return nearestConstellationCode(nra, ndec) +} + +func lookupConstellationCode(ra, dec, jde float64) (string, float64, float64) { + nra := ra + if ra >= 360 { + nra -= 360 + } + nra, ndec := Precess(nra, dec, jde, 2451545.0) + if ra >= 360 && nra < 270 { + nra += 360 + } + return matchConstellationCode(nra, ndec), nra, ndec +} + +func nearestConstellationCode(ra, dec float64) string { + cosDec := math.Cos(dec * math.Pi / 180) + best, bestDistance := "", math.Inf(1) + for _, boundary := range constellationBoundaries { + pointCount := len(boundary.points) + for index := 0; index < pointCount; index++ { + p, q := boundary.points[index], boundary.points[(index+1)%pointCount] + px, py := math.Remainder(p.RA-ra, 360)*cosDec, p.DEC-dec + qx, qy := math.Remainder(q.RA-ra, 360)*cosDec, q.DEC-dec + if distance := originSegmentDistance(px, py, qx, qy); distance < bestDistance { + best, bestDistance = boundary.code, distance + } + } + } + return best +} + +func originSegmentDistance(px, py, qx, qy float64) float64 { + dx, dy := qx-px, qy-py + length := dx*dx + dy*dy + t := 0.0 + if length > 0 { + t = math.Max(0, math.Min(1, -(px*dx+py*dy)/length)) + } + return math.Hypot(px+t*dx, py+t*dy) } func matchConstellationCode(ra, dec float64) string { diff --git a/basic/constellation_test.go b/basic/constellation_test.go index f3124f5..f1a223c 100644 --- a/basic/constellation_test.go +++ b/basic/constellation_test.go @@ -5,11 +5,21 @@ import ( ) func TestConstellationNameZH(t *testing.T) { - now := GetNowJD() - //finish on 30s - for i := 0.00; i <= 360.00; i += 0.5 { - for j := -90.00; j <= 90.00; j += 0.5 { - ConstellationNameZH(float64(i), float64(j), now) + for _, jde := range []float64{2451545.0, 2461328.5} { + for i := 0.00; i <= 360.00; i += 0.25 { + for j := -90.00; j <= 90.00; j += 0.25 { + if ConstellationNameZH(i, j, jde) == "" { + t.Fatalf("ConstellationNameZH(%v, %v, %v) is empty", i, j, jde) + } + } + } + } +} + +func TestConstellationBoundarySliver(t *testing.T) { + for _, dec := range []float64{3.25, 3.5, 3.75, 4.0} { + if got := ConstellationNameZH(278.25, dec, 2461328.5); got != "巨蛇座" && got != "蛇夫座" { + t.Fatalf("ConstellationNameZH(278.25, %v) = %q, want 巨蛇座 or 蛇夫座", dec, got) } } } diff --git a/basic/coordinate.go b/basic/coordinate.go index f27e26b..ef80d8a 100644 --- a/basic/coordinate.go +++ b/basic/coordinate.go @@ -68,20 +68,26 @@ func DecToBo(jde, ra, dec float64) float64 { /* * 地心坐标转站心坐标,参数分别为,地心赤经赤纬 纬度经度,jde,离地心位置au */ +// 站心视差沿用的旧地球椭球,与 geodata 的赤道半径不同,不可混用。 +const ( + topocentricEarthRadiusKM = 6378.14 + topocentricEarthPolarRadiusKM = 6356.755 +) + func pcosi(lat, h float64) float64 { - b := 6356.755 - a := 6378.14 + b := topocentricEarthPolarRadiusKM + a := topocentricEarthRadiusKM u := ArcTan(b / a * Tan(lat)) - //psin=b/a*Sin(u)+h/6378140*Sin(lat); - pcos := Cos(u) + h/6378140.0*Cos(lat) + //psin=b/a*Sin(u)+h/(topocentricEarthRadiusKM*1000)*Sin(lat); + pcos := Cos(u) + h/(topocentricEarthRadiusKM*1000)*Cos(lat) return pcos } func psini(lat, h float64) float64 { - b := 6356.755 - a := 6378.14 + b := topocentricEarthPolarRadiusKM + a := topocentricEarthRadiusKM u := ArcTan(b / a * Tan(lat)) - psin := b/a*Sin(u) + h/6378140*Sin(lat) - //pcos=Cos(u)+h/6378140*Cos(lat); + psin := b/a*Sin(u) + h/(topocentricEarthRadiusKM*1000)*Sin(lat) + //pcos=Cos(u)+h/(topocentricEarthRadiusKM*1000)*Cos(lat); return psin } @@ -130,18 +136,20 @@ func TopocentricBo(lo, bo, lat, lon, jde, au, h float64) float64 { return nbo } -func GXCLo(lo, bo, jde float64) float64 { //光行差修正 +// GXCLoWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GXCLoWithInstance(lo, bo, jde float64, inst *Instance) float64 { //光行差修正 k := 20.49552 - sunlo := SunTrueLo(jde) + sunlo := SunTrueLoWithInstance(jde, inst) e := Earthe(jde) epi := EarthPI(jde) tmp := (-k*Cos(sunlo-lo) + e*k*Cos(epi-lo)) / Cos(bo) return tmp } -func GXCBo(lo, bo, jde float64) float64 { +// GXCBoWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GXCBoWithInstance(lo, bo, jde float64, inst *Instance) float64 { k := 20.49552 - sunlo := SunTrueLo(jde) + sunlo := SunTrueLoWithInstance(jde, inst) e := Earthe(jde) epi := EarthPI(jde) tmp := -k * Sin(bo) * (Sin(sunlo-lo) - e*Sin(epi-lo)) diff --git a/basic/diameter.go b/basic/diameter.go index 2d5b9ea..bbc03e5 100644 --- a/basic/diameter.go +++ b/basic/diameter.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) const ( angularDiameterAstronomicalUnitKM = 149597870.7 @@ -30,9 +32,9 @@ func SunSemidiameter(jde float64) float64 { return SunSemidiameterN(jde, -1) } -// SunSemidiameterN 太阳视半径(截断版),单位角秒 / truncated apparent solar semidiameter in arcseconds. -func SunSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(sunEquatorialRadiusKM, EarthAwayN(jde, n)) +// SunSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SunSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(sunEquatorialRadiusKM, EarthAwayNWithInstance(jde, n, inst)) } // SunDiameter 太阳视直径,单位角秒 / apparent solar diameter in arcseconds. @@ -40,19 +42,19 @@ func SunDiameter(jde float64) float64 { return SunDiameterN(jde, -1) } -// SunDiameterN 太阳视直径(截断版),单位角秒 / truncated apparent solar diameter in arcseconds. -func SunDiameterN(jde float64, n int) float64 { - return 2 * SunSemidiameterN(jde, n) +// SunDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SunDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * SunSemidiameterNWithInstance(jde, n, inst) } -// MoonSemidiameter 月亮视半径,单位角秒 / apparent lunar semidiameter in arcseconds. -func MoonSemidiameter(jde float64) float64 { - return MoonSemidiameterN(jde, -1) +// MoonSemidiameterWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonSemidiameterWithInstance(jde float64, inst *Instance) float64 { + return MoonSemidiameterNWithInstance(jde, -1, inst) } -// MoonSemidiameterN 月亮视半径(截断版),单位角秒 / truncated apparent lunar semidiameter in arcseconds. -func MoonSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterArcsec(moonEquatorialRadiusKM, HMoonAwayN(jde, n)) +// MoonSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterArcsec(moonEquatorialRadiusKM, HMoonAwayNWithInstance(jde, n, inst)) } // MoonDiameter 月亮视直径,单位角秒 / apparent lunar diameter in arcseconds. @@ -60,9 +62,9 @@ func MoonDiameter(jde float64) float64 { return MoonDiameterN(jde, -1) } -// MoonDiameterN 月亮视直径(截断版),单位角秒 / truncated apparent lunar diameter in arcseconds. -func MoonDiameterN(jde float64, n int) float64 { - return 2 * MoonSemidiameterN(jde, n) +// MoonDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * MoonSemidiameterNWithInstance(jde, n, inst) } // MercurySemidiameter 水星视半径,单位角秒 / apparent Mercury semidiameter in arcseconds. @@ -70,9 +72,9 @@ func MercurySemidiameter(jde float64) float64 { return MercurySemidiameterN(jde, -1) } -// MercurySemidiameterN 水星视半径(截断版),单位角秒 / truncated apparent Mercury semidiameter in arcseconds. -func MercurySemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(mercuryEquatorialRadiusKM, EarthMercuryAwayN(jde, n)) +// MercurySemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercurySemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(mercuryEquatorialRadiusKM, EarthMercuryAwayNWithInstance(jde, n, inst)) } // MercuryDiameter 水星视直径,单位角秒 / apparent Mercury diameter in arcseconds. @@ -80,9 +82,9 @@ func MercuryDiameter(jde float64) float64 { return MercuryDiameterN(jde, -1) } -// MercuryDiameterN 水星视直径(截断版),单位角秒 / truncated apparent Mercury diameter in arcseconds. -func MercuryDiameterN(jde float64, n int) float64 { - return 2 * MercurySemidiameterN(jde, n) +// MercuryDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * MercurySemidiameterNWithInstance(jde, n, inst) } // VenusSemidiameter 金星视半径,单位角秒 / apparent Venus semidiameter in arcseconds. @@ -90,9 +92,9 @@ func VenusSemidiameter(jde float64) float64 { return VenusSemidiameterN(jde, -1) } -// VenusSemidiameterN 金星视半径(截断版),单位角秒 / truncated apparent Venus semidiameter in arcseconds. -func VenusSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(venusEquatorialRadiusKM, EarthVenusAwayN(jde, n)) +// VenusSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(venusEquatorialRadiusKM, EarthVenusAwayNWithInstance(jde, n, inst)) } // VenusDiameter 金星视直径,单位角秒 / apparent Venus diameter in arcseconds. @@ -100,9 +102,9 @@ func VenusDiameter(jde float64) float64 { return VenusDiameterN(jde, -1) } -// VenusDiameterN 金星视直径(截断版),单位角秒 / truncated apparent Venus diameter in arcseconds. -func VenusDiameterN(jde float64, n int) float64 { - return 2 * VenusSemidiameterN(jde, n) +// VenusDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * VenusSemidiameterNWithInstance(jde, n, inst) } // MarsSemidiameter 火星视半径,单位角秒 / apparent Mars semidiameter in arcseconds. @@ -110,9 +112,9 @@ func MarsSemidiameter(jde float64) float64 { return MarsSemidiameterN(jde, -1) } -// MarsSemidiameterN 火星视半径(截断版),单位角秒 / truncated apparent Mars semidiameter in arcseconds. -func MarsSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(marsEquatorialRadiusKM, EarthMarsAwayN(jde, n)) +// MarsSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(marsEquatorialRadiusKM, EarthMarsAwayNWithInstance(jde, n, inst)) } // MarsDiameter 火星视直径,单位角秒 / apparent Mars diameter in arcseconds. @@ -120,9 +122,9 @@ func MarsDiameter(jde float64) float64 { return MarsDiameterN(jde, -1) } -// MarsDiameterN 火星视直径(截断版),单位角秒 / truncated apparent Mars diameter in arcseconds. -func MarsDiameterN(jde float64, n int) float64 { - return 2 * MarsSemidiameterN(jde, n) +// MarsDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * MarsSemidiameterNWithInstance(jde, n, inst) } // JupiterSemidiameter 木星视半径,单位角秒 / apparent Jupiter semidiameter in arcseconds. @@ -130,9 +132,9 @@ func JupiterSemidiameter(jde float64) float64 { return JupiterSemidiameterN(jde, -1) } -// JupiterSemidiameterN 木星视半径(截断版),单位角秒 / truncated apparent Jupiter semidiameter in arcseconds. -func JupiterSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(jupiterEquatorialRadiusKM, EarthJupiterAwayN(jde, n)) +// JupiterSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(jupiterEquatorialRadiusKM, EarthJupiterAwayNWithInstance(jde, n, inst)) } // JupiterDiameter 木星视直径,单位角秒 / apparent Jupiter diameter in arcseconds. @@ -140,9 +142,9 @@ func JupiterDiameter(jde float64) float64 { return JupiterDiameterN(jde, -1) } -// JupiterDiameterN 木星视直径(截断版),单位角秒 / truncated apparent Jupiter diameter in arcseconds. -func JupiterDiameterN(jde float64, n int) float64 { - return 2 * JupiterSemidiameterN(jde, n) +// JupiterDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * JupiterSemidiameterNWithInstance(jde, n, inst) } // SaturnSemidiameter 土星视半径,单位角秒 / apparent Saturn semidiameter in arcseconds. @@ -150,9 +152,9 @@ func SaturnSemidiameter(jde float64) float64 { return SaturnSemidiameterN(jde, -1) } -// SaturnSemidiameterN 土星视半径(截断版),单位角秒 / truncated apparent Saturn semidiameter in arcseconds. -func SaturnSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(saturnEquatorialRadiusKM, EarthSaturnAwayN(jde, n)) +// SaturnSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(saturnEquatorialRadiusKM, EarthSaturnAwayNWithInstance(jde, n, inst)) } // SaturnDiameter 土星视直径,单位角秒 / apparent Saturn diameter in arcseconds. @@ -160,9 +162,9 @@ func SaturnDiameter(jde float64) float64 { return SaturnDiameterN(jde, -1) } -// SaturnDiameterN 土星视直径(截断版),单位角秒 / truncated apparent Saturn diameter in arcseconds. -func SaturnDiameterN(jde float64, n int) float64 { - return 2 * SaturnSemidiameterN(jde, n) +// SaturnDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * SaturnSemidiameterNWithInstance(jde, n, inst) } // UranusSemidiameter 天王星视半径,单位角秒 / apparent Uranus semidiameter in arcseconds. @@ -170,9 +172,9 @@ func UranusSemidiameter(jde float64) float64 { return UranusSemidiameterN(jde, -1) } -// UranusSemidiameterN 天王星视半径(截断版),单位角秒 / truncated apparent Uranus semidiameter in arcseconds. -func UranusSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(uranusEquatorialRadiusKM, EarthUranusAwayN(jde, n)) +// UranusSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(uranusEquatorialRadiusKM, EarthUranusAwayNWithInstance(jde, n, inst)) } // UranusDiameter 天王星视直径,单位角秒 / apparent Uranus diameter in arcseconds. @@ -180,9 +182,9 @@ func UranusDiameter(jde float64) float64 { return UranusDiameterN(jde, -1) } -// UranusDiameterN 天王星视直径(截断版),单位角秒 / truncated apparent Uranus diameter in arcseconds. -func UranusDiameterN(jde float64, n int) float64 { - return 2 * UranusSemidiameterN(jde, n) +// UranusDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * UranusSemidiameterNWithInstance(jde, n, inst) } // NeptuneSemidiameter 海王星视半径,单位角秒 / apparent Neptune semidiameter in arcseconds. @@ -190,9 +192,9 @@ func NeptuneSemidiameter(jde float64) float64 { return NeptuneSemidiameterN(jde, -1) } -// NeptuneSemidiameterN 海王星视半径(截断版),单位角秒 / truncated apparent Neptune semidiameter in arcseconds. -func NeptuneSemidiameterN(jde float64, n int) float64 { - return angularSemidiameterFromAU(neptuneEquatorialRadiusKM, EarthNeptuneAwayN(jde, n)) +// NeptuneSemidiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneSemidiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return angularSemidiameterFromAU(neptuneEquatorialRadiusKM, EarthNeptuneAwayNWithInstance(jde, n, inst)) } // NeptuneDiameter 海王星视直径,单位角秒 / apparent Neptune diameter in arcseconds. @@ -200,7 +202,7 @@ func NeptuneDiameter(jde float64) float64 { return NeptuneDiameterN(jde, -1) } -// NeptuneDiameterN 海王星视直径(截断版),单位角秒 / truncated apparent Neptune diameter in arcseconds. -func NeptuneDiameterN(jde float64, n int) float64 { - return 2 * NeptuneSemidiameterN(jde, n) +// NeptuneDiameterNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneDiameterNWithInstance(jde float64, n int, inst *Instance) float64 { + return 2 * NeptuneSemidiameterNWithInstance(jde, n, inst) } diff --git a/basic/diameter_test.go b/basic/diameter_test.go index 94aee4b..17ce530 100644 --- a/basic/diameter_test.go +++ b/basic/diameter_test.go @@ -14,7 +14,6 @@ type angularDiameterSample struct { } func TestAngularDiametersMatchHorizonsBaseline(t *testing.T) { - // Baseline is generated from JPL Horizons by scripts/generate_angular_diameter_baseline.sh. data, err := os.ReadFile("testdata/angular_diameter_baseline.json") if err != nil { t.Fatalf("read baseline: %v", err) diff --git a/basic/doc.go b/basic/doc.go new file mode 100644 index 0000000..c0ac58b --- /dev/null +++ b/basic/doc.go @@ -0,0 +1,5 @@ +// Package basic 提供底层天文算法、星历实例入口与内置模型口径。 +// Package basic provides the low-level astronomical algorithms, ephemeris-instance entry points and built-in model conventions. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +package basic diff --git a/basic/earth.go b/basic/earth.go index 96e3d47..396164f 100644 --- a/basic/earth.go +++ b/basic/earth.go @@ -11,7 +11,7 @@ import ( const ( EARTH_EQUATORIAL_RADIUS float64 = 6378137.0 EARTH_POLAR_RADIUS float64 = 6356752.3 - EARTH_AVERAGE_RADIUS float64 = 6371393.0 + EARTH_AVERAGE_RADIUS float64 = 6371008.8 ) // HeightDistance 高度与地平线距离的关系(单位:米) diff --git a/basic/earth_test.go b/basic/earth_test.go index 7b0a6e7..71ad14a 100644 --- a/basic/earth_test.go +++ b/basic/earth_test.go @@ -27,7 +27,7 @@ func TestHeightDegreeByLatMatchesSphericalArc(t *testing.T) { func TestHeightDegreeReferenceValue(t *testing.T) { got := HeightDegree(10000) - want := 3.20801665537668 + want := 3.208113256367627 if math.Abs(got-want) > 1e-12 { t.Fatalf("HeightDegree(10000) = %.15f want %.15f", got, want) } diff --git a/basic/ephemeris.go b/basic/ephemeris.go new file mode 100644 index 0000000..9901051 --- /dev/null +++ b/basic/ephemeris.go @@ -0,0 +1,35 @@ +package basic + +import ( + "b612.me/astro/internal/ephem" +) + +// Instance 显式只读星历实例 / explicit read-only ephemeris instance. +type Instance = ephem.Instance + +// EphemerisModel 几何位置所用的星历模型 / ephemeris model behind geometric positions. +type EphemerisModel int + +const ( + // EphemerisLegacy VSOP87 + ELP2000 截断级数,astro_legacy 构建下为默认 / truncated VSOP87 and ELP2000 series, default under astro_legacy. + EphemerisLegacy EphemerisModel = iota + // EphemerisSemianalytic VSOP2013/TOP2013 + ELP/MPP02 结构、按 DE441 重拟的半解析表 / semi-analytic tables refitted to DE441. + EphemerisSemianalytic +) + +// SetEphemerisModel 切换全局星历模型,未知值返回 false / switches the global ephemeris model; unknown values return false. +func SetEphemerisModel(m EphemerisModel) bool { + if m != EphemerisLegacy && m != EphemerisSemianalytic { + return false + } + // 依赖星历的缓存都按 ΔT 世代校验,切换时一并递增。 + deltaTFnMu.Lock() + if ephem.SetActive(ephem.Model(m)) { + deltaTGeneration++ + } + deltaTFnMu.Unlock() + return true +} + +// GetEphemerisModel 当前星历模型 / the active ephemeris model. +func GetEphemerisModel() EphemerisModel { return EphemerisModel(ephem.Active()) } diff --git a/basic/ephemeris_instance.go b/basic/ephemeris_instance.go new file mode 100644 index 0000000..eb97a61 --- /dev/null +++ b/basic/ephemeris_instance.go @@ -0,0 +1,120 @@ +package basic + +import ( + "errors" + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" +) + +func instanceValue(instances []*Instance) *Instance { + if len(instances) == 0 { + return nil + } + return instances[0] +} + +func instanceModel(inst *Instance) ephem.Model { + if inst == nil { + return ephem.Active() + } + return inst.Model() +} + +// PlanetCoordinateWithInstance 指定星历实例的行星日心经纬或距离 / heliocentric planetary coordinate or distance with an explicit instance. +func PlanetCoordinateWithInstance(index, coordinate int, jde float64, inst *Instance) float64 { + return PlanetCoordinateNWithInstance(index, coordinate, jde, -1, inst) +} + +// PlanetCoordinateNWithInstance 指定星历实例的行星日心经纬或距离(n 变体) / n-parameterised heliocentric planetary coordinate or distance with an explicit instance. +func PlanetCoordinateNWithInstance(index, coordinate int, jde float64, n int, inst *Instance) float64 { + if inst == nil { + return planet.WherePlanetN(index, coordinate, jde, n) + } + if inst.Model() != ephem.Custom { + return planet.WherePlanetModelN(inst.Model(), index, coordinate, jde, n) + } + value, ok := planetHeliocentricCoordinateWithInstance(index, coordinate, jde, inst) + if !ok { + return math.NaN() + } + return value +} + +func boundVectorApparent(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool) { + lo, bo, tau, distAU, ok, err := vectorApparentWithInstance(target, jde, n, inst) + if err != nil { + return math.NaN(), math.NaN(), math.NaN(), math.NaN(), true + } + return lo, bo, tau, distAU, ok +} + +func instanceStateEcliptic(target ephem.Body, jde float64, inst *Instance) (lo, bo, distAU float64, ok bool) { + return instanceStateEclipticRelative(target, ephem.Earth, jde, jde, inst) +} + +func instanceStateEclipticRelative(target, center ephem.Body, stateJDE, eclipticJDE float64, inst *Instance) (lo, bo, distAU float64, ok bool) { + // 几何状态捷径只给自定义引擎,内置模型必须走与包级相同的级数与视位置管线。 + if inst == nil || inst.Model() != ephem.Custom { + return 0, 0, 0, false + } + state, _, err := inst.State(target, center, stateJDE) + if err != nil { + return 0, 0, 0, false + } + distanceKM := math.Sqrt(state[0]*state[0] + state[1]*state[1] + state[2]*state[2]) + if distanceKM == 0 || math.IsNaN(distanceKM) || math.IsInf(distanceKM, 0) { + return 0, 0, 0, false + } + direction := [3]float64{state[0] / distanceKM, state[1] / distanceKM, state[2] / distanceKM} + lo, bo = icrfToMeanEcliptic(direction, eclipticJDE) + return lo, bo, distanceKM / astronomicalUnitKM, true +} + +func icrfToMeanEcliptic(direction [3]float64, jde float64) (lo, bo float64) { + matrix := ltpPBMAT(2000 + (jde-2451545)/365.25) + var equatorial [3]float64 + for axis := range equatorial { + equatorial[axis] = matrix[axis][0]*direction[0] + matrix[axis][1]*direction[1] + matrix[axis][2]*direction[2] + } + sine, cosine := math.Sincos(Obliquity1980(jde) * math.Pi / 180) + eclipticY := equatorial[1]*cosine + equatorial[2]*sine + eclipticZ := -equatorial[1]*sine + equatorial[2]*cosine + lo = math.Atan2(eclipticY, equatorial[0]) * 180 / math.Pi + bo = math.Atan2(eclipticZ, math.Hypot(equatorial[0], eclipticY)) * 180 / math.Pi + return math.Mod(lo+720, 360), bo +} + +func instanceApparent(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) { + if inst == nil || n >= 0 && inst.Model() != ephem.Custom { + return 0, 0, 0, 0, false, nil + } + return apparentState(target, jde, inst) +} + +// apparentState 求视位置并校验;引擎不支持时 ok=false 且 err 为 nil,状态非法时返回 err。 +func apparentState(target ephem.Body, jde float64, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) { + direction, distanceKM, tau, err := inst.Apparent(target, jde) + if errors.Is(err, ephem.ErrUnsupportedApparent) || inst.Model() != ephem.Custom && errors.Is(err, ephem.ErrNoEngine) { + return 0, 0, 0, 0, false, nil + } + if err != nil { + return 0, 0, 0, 0, false, err + } + norm := math.Hypot(math.Hypot(direction[0], direction[1]), direction[2]) + if !isFiniteFloat(distanceKM) || distanceKM <= 0 || !isFiniteFloat(norm) || norm <= 0 || !isFiniteFloat(tau) || tau < 0 { + return 0, 0, 0, 0, false, errors.New("ephem: invalid apparent state") + } + lo, bo = icrfToTrueEcliptic(direction, jde) + // 天文单位须与 vectorApparent 一致,否则实例与包级距离不逐位相等。 + return lo, bo, tau, distanceKM / 149597870.7, true, nil +} + +func vectorApparentWithInstance(target ephem.Body, jde float64, n int, inst *Instance) (lo, bo, tau, distAU float64, ok bool, err error) { + if inst != nil { + return instanceApparent(target, jde, n, inst) + } + lo, bo, tau, distAU, ok = vectorApparent(target, jde, n) + return lo, bo, tau, distAU, ok, nil +} diff --git a/basic/ephemeris_instance_contract_test.go b/basic/ephemeris_instance_contract_test.go new file mode 100644 index 0000000..3cb59c8 --- /dev/null +++ b/basic/ephemeris_instance_contract_test.go @@ -0,0 +1,121 @@ +package basic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" +) + +type p3StateEngine struct { + position [3]float64 + err error +} + +func (engine p3StateEngine) State(target, center ephem.Body, jde float64) ([3]float64, [3]float64, error) { + return engine.position, [3]float64{}, engine.err +} + +func (engine p3StateEngine) Coverage(target ephem.Body) (float64, float64, bool) { + return 2400000, 2500000, true +} + +type p3ApparentEngine struct { + p3StateEngine + direction [3]float64 + distance float64 + tau float64 + err error +} + +func (engine p3ApparentEngine) Apparent(target ephem.Body, jde float64) ([3]float64, float64, float64, error) { + return engine.direction, engine.distance, engine.tau, engine.err +} + +func TestP3BuiltInGeometryPreservesTruncation(t *testing.T) { + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, jde := range []float64{2451545, 2461319.5} { + for _, terms := range []int{-1, 0, 16} { + for _, sample := range []struct { + name string + got float64 + want float64 + }{ + {"SunLo", HSunTrueLoNWithInstance(jde, terms, inst), planet.WherePlanetModelN(model, 0, 0, jde, terms)}, + {"SunBo", HSunTrueBoNWithInstance(jde, terms, inst), planet.WherePlanetModelN(model, 0, 1, jde, terms)}, + {"SunDistance", EarthAwayNWithInstance(jde, terms, inst), planet.WherePlanetModelN(model, 0, 2, jde, terms)}, + {"MoonLo", MoonCalcNewNWithInstance(0, jde, terms, inst), planet.WhereMoonModelN(model, 0, jde, terms)}, + {"MoonBo", MoonCalcNewNWithInstance(1, jde, terms, inst), planet.WhereMoonModelN(model, 1, jde, terms)}, + {"MoonDistance", HMoonAwayNWithInstance(jde, terms, inst), planet.WhereMoonModelN(model, 2, jde, terms)}, + } { + if sample.got != sample.want { + t.Errorf("%s model=%d jde=%v n=%d: got %.17g, want %.17g", sample.name, model, jde, terms, sample.got, sample.want) + } + } + } + } + } +} + +func TestP3CustomGeometryUsesMeanEcliptic(t *testing.T) { + const jde = 2461319.5 + direction := [3]float64{1, 0, 0} + inst := ephem.NewInstance(p3StateEngine{position: [3]float64{astronomicalUnitKM, 0, 0}}) + trueLo, wantBo := icrfToTrueEcliptic(direction, jde) + wantLo := trueLo - Nutation2000Bi(jde) + for _, got := range []float64{HSunTrueLoWithInstance(jde, inst), HMoonTrueLoWithInstance(jde, inst), MercuryLWithInstance(jde, inst)} { + if math.Abs(angleDifference(got, wantLo)) > 1e-12 { + t.Errorf("geometric longitude includes nutation: got %.17g, want %.17g", got, wantLo) + } + } + if got := HSunTrueBoWithInstance(jde, inst); math.Abs(got-wantBo) > 1e-12 { + t.Errorf("latitude: got %.17g, want %.17g", got, wantBo) + } + if got := EarthAwayNWithInstance(jde, 16, inst); got != 1 { + t.Errorf("distance: got %v, want 1 AU", got) + } +} + +func TestP3CustomGeometryDoesNotFallback(t *testing.T) { + for _, engine := range []ephem.Engine{ + nil, + p3StateEngine{err: ephem.ErrOutOfRange}, + p3StateEngine{err: ephem.ErrUnsupportedBody}, + p3StateEngine{}, + p3StateEngine{position: [3]float64{math.NaN(), 0, 0}}, + } { + inst := ephem.NewInstance(engine) + for _, got := range []float64{ + HSunTrueLoWithInstance(2451545, inst), HSunTrueBoWithInstance(2451545, inst), EarthAwayNWithInstance(2451545, -1, inst), + HMoonTrueLoWithInstance(2451545, inst), HMoonTrueBoWithInstance(2451545, inst), HMoonAwayWithInstance(2451545, inst), + } { + if !math.IsNaN(got) { + t.Errorf("failed custom engine %T fell back to a built-in model: %v", engine, got) + } + } + } +} + +func TestP3CustomApparentFailureDoesNotFallback(t *testing.T) { + state := p3StateEngine{position: [3]float64{astronomicalUnitKM, 0, 0}} + for _, engine := range []p3ApparentEngine{ + {p3StateEngine: state, err: ephem.ErrOutOfRange}, + {p3StateEngine: state, direction: [3]float64{1, 0, 0}, distance: -1}, + {p3StateEngine: state, direction: [3]float64{math.NaN(), 0, 0}, distance: 1}, + {p3StateEngine: state, distance: 1}, + {p3StateEngine: state, direction: [3]float64{1, 0, 0}, distance: 1, tau: math.NaN()}, + } { + inst := ephem.NewInstance(engine) + for _, got := range []float64{HSunApparentLoWithInstance(2451545, inst), HMoonApparentLoWithInstance(2451545, inst), MercuryApparentLoWithInstance(2451545, inst)} { + if !math.IsNaN(got) { + t.Errorf("failed apparent engine fell back to geometry: %v", got) + } + } + } + inst := ephem.NewInstance(state) + if got := HSunApparentLoWithInstance(2451545, inst); !isFiniteFloat(got) { + t.Errorf("state-only engine must retain geometric fallback: %v", got) + } +} diff --git a/basic/ephemeris_instance_test.go b/basic/ephemeris_instance_test.go new file mode 100644 index 0000000..c4efc5c --- /dev/null +++ b/basic/ephemeris_instance_test.go @@ -0,0 +1,384 @@ +package basic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +type p3ProbeEngine struct { + angle float64 + distance float64 +} + +func (engine p3ProbeEngine) State(target, center ephem.Body, jdTDB float64) (pos, vel [3]float64, err error) { + angle := engine.angle + float64(target-center)*0.01 + distance := engine.distance + if distance == 0 { + distance = 1 + } + pos[0] = math.Cos(angle) * astronomicalUnitKM * distance + pos[1] = math.Sin(angle) * astronomicalUnitKM * distance + return pos, vel, nil +} + +func (engine p3ProbeEngine) Coverage(target ephem.Body) (start, end float64, ok bool) { + return 2400000, 2500000, target != ephem.SSB +} + +func (engine p3ProbeEngine) Apparent(target ephem.Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) { + angle := engine.angle + float64(target)*0.01 + dir[0] = math.Cos(angle) + dir[1] = math.Sin(angle) + distance := engine.distance + if distance == 0 { + distance = 1 + } + return dir, astronomicalUnitKM * distance, 0.005, nil +} + +func TestP3SunMoonFunctionsUseExplicitInstances(t *testing.T) { + previous := ephem.Active() + ephem.SetActive(ephem.Legacy) + t.Cleanup(func() { ephem.SetActive(previous) }) + + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + sunFirst := HSunApparentLoNWithInstance(jd, -1, first) + sunSecond := HSunApparentLoNWithInstance(jd, -1, second) + if math.Abs(angleDifference(sunFirst, sunSecond)) < 1 { + t.Fatalf("sun apparent longitude did not follow instance: %.6f vs %.6f", sunFirst, sunSecond) + } + + moonFirst := HMoonApparentLoNWithInstance(jd, -1, first) + moonSecond := HMoonApparentLoNWithInstance(jd, -1, second) + if math.Abs(angleDifference(moonFirst, moonSecond)) < 1 { + t.Fatalf("moon apparent longitude did not follow instance: %.6f vs %.6f", moonFirst, moonSecond) + } + + if got, want := HSunApparentLoN(jd, -1), HSunApparentLoNWithInstance(jd, -1, nil); math.Abs(angleDifference(got, want)) > 1e-12 { + t.Fatalf("nil instance changed legacy-compatible result: %.15f vs %.15f", got, want) + } +} + +func TestP3PlanetFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + for _, sample := range []struct { + name string + first float64 + second float64 + }{ + {"MercuryL", MercuryLWithInstance(jd, first), MercuryLWithInstance(jd, second)}, + {"VenusL", VenusLWithInstance(jd, first), VenusLWithInstance(jd, second)}, + {"MarsL", MarsLWithInstance(jd, first), MarsLWithInstance(jd, second)}, + {"JupiterL", JupiterLWithInstance(jd, first), JupiterLWithInstance(jd, second)}, + {"SaturnL", SaturnLWithInstance(jd, first), SaturnLWithInstance(jd, second)}, + {"UranusL", UranusLWithInstance(jd, first), UranusLWithInstance(jd, second)}, + {"NeptuneL", NeptuneLWithInstance(jd, first), NeptuneLWithInstance(jd, second)}, + {"MercuryApparentLo", MercuryApparentLoWithInstance(jd, first), MercuryApparentLoWithInstance(jd, second)}, + {"NeptuneApparentLo", NeptuneApparentLoWithInstance(jd, first), NeptuneApparentLoWithInstance(jd, second)}, + } { + if math.Abs(angleDifference(sample.first, sample.second)) < 1 { + t.Fatalf("%s did not follow explicit instance: %.6f vs %.6f", sample.name, sample.first, sample.second) + } + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"MercuryL", MercuryLWithInstance(jd, inst)}, + {"MercuryR", MercuryRWithInstance(jd, inst)}, + {"MercuryApparentRa", MercuryApparentRaWithInstance(jd, inst)}, + {"NeptuneApparentDec", NeptuneApparentDecWithInstance(jd, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + previous := ephem.Active() + ephem.SetActive(ephem.Legacy) + t.Cleanup(func() { ephem.SetActive(previous) }) + if got, want := MercuryLWithInstance(jd, nil), MercuryL(jd); got != want { + t.Fatalf("nil instance changed MercuryL: %.15f vs %.15f", got, want) + } + if got, want := NeptuneApparentRaWithInstance(jd, nil), NeptuneApparentRa(jd); got != want { + t.Fatalf("nil instance changed NeptuneApparentRa: %.15f vs %.15f", got, want) + } +} + +func TestP3MoonPhaseFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + phaseFirst := MoonPhaseWithInstance(jd, first) + phaseSecond := MoonPhaseWithInstance(jd, second) + if math.Abs(phaseFirst-phaseSecond) < 1e-6 { + t.Fatalf("moon phase did not follow explicit instance: %.12f vs %.12f", phaseFirst, phaseSecond) + } + seekFirst := SunMoonSeekWithInstance(jd, 0, first) + seekSecond := SunMoonSeekWithInstance(jd, 0, second) + if math.Abs(angleDifference(seekFirst, seekSecond)) < 1 { + t.Fatalf("sun-moon phase residual did not follow explicit instance: %.6f vs %.6f", seekFirst, seekSecond) + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"CalcMoonSHByJDE", CalcMoonSHByJDEWithInstance(jd, 0, inst)}, + {"CalcMoonXHByJDE", CalcMoonXHByJDEWithInstance(jd, 0, inst)}, + {"MoonPhase", MoonPhaseWithInstance(jd, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + if got, want := MoonPhaseWithInstance(jd, nil), MoonPhase(jd); got != want { + t.Fatalf("nil instance changed MoonPhase: %.15f vs %.15f", got, want) + } + if got, want := CalcMoonSHByJDEWithInstance(jd, 0, nil), CalcMoonSHByJDE(jd, 0); got != want { + t.Fatalf("nil instance changed CalcMoonSHByJDE: %.15f vs %.15f", got, want) + } +} + +func TestP3SolarTermFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + firstResidual := JQLospecWithInstance(jd, 0, first) + secondResidual := JQLospecWithInstance(jd, 0, second) + if math.Abs(angleDifference(firstResidual, secondResidual)) < 1 { + t.Fatalf("solar-term longitude did not follow explicit instance: %.6f vs %.6f", firstResidual, secondResidual) + } + if GetShiErCiWithInstance(jd, first) == GetShiErCiWithInstance(jd, second) { + t.Fatalf("twelve-division label did not follow explicit instance") + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"GetJQTime", GetJQTimeWithInstance(2024, 270, inst)}, + {"GetWuHouTime", GetWuHouTimeWithInstance(2024, 0, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + if got := GetJieqiLoopsWithInstance(2024, 3, inst); len(got) != 3 { + t.Fatalf("GetJieqiLoops model %d returned %d values", model, len(got)) + } + } + + if got, want := JQLospecWithInstance(jd, 0, nil), JQLospec(jd, 0); got != want { + t.Fatalf("nil instance changed JQLospec: %.15f vs %.15f", got, want) + } + if got, want := GetJQTimeWithInstance(2024, 270, nil), GetJQTime(2024, 270); got != want { + t.Fatalf("nil instance changed GetJQTime: %.15f vs %.15f", got, want) + } +} + +func TestP3SunObservationFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + firstHeight := SunHeightWithInstance(jd, 121.47, 31.23, 8, first) + secondHeight := SunHeightWithInstance(jd, 121.47, 31.23, 8, second) + if math.Abs(firstHeight-secondHeight) < 1 { + t.Fatalf("sun height did not follow explicit instance: %.6f vs %.6f", firstHeight, secondHeight) + } + firstAzimuth := SunAzimuthWithInstance(jd, 121.47, 31.23, 8, first) + secondAzimuth := SunAzimuthWithInstance(jd, 121.47, 31.23, 8, second) + if math.Abs(angleDifference(firstAzimuth, secondAzimuth)) < 1 { + t.Fatalf("sun azimuth did not follow explicit instance: %.6f vs %.6f", firstAzimuth, secondAzimuth) + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"SunTime", SunTimeWithInstance(jd, inst)}, + {"CulminationTime", CulminationTimeWithInstance(jd, 121.47, 8, inst)}, + {"SunTimeAngle", SunTimeAngleWithInstance(jd, 121.47, 31.23, 8, inst)}, + {"SunHeight", SunHeightWithInstance(jd, 121.47, 31.23, 8, inst)}, + {"SunAzimuth", SunAzimuthWithInstance(jd, 121.47, 31.23, 8, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + if got, want := SunHeightWithInstance(jd, 121.47, 31.23, 8, nil), SunHeight(jd, 121.47, 31.23, 8); got != want { + t.Fatalf("nil instance changed SunHeight: %.15f vs %.15f", got, want) + } + if got, want := SunAzimuthWithInstance(jd, 121.47, 31.23, 8, nil), SunAzimuth(jd, 121.47, 31.23, 8); got != want { + t.Fatalf("nil instance changed SunAzimuth: %.15f vs %.15f", got, want) + } +} + +// 显式 Legacy 实例的均时差与中天时刻必须复现包级 Legacy 结果。 +func TestSunTimeWithLegacyInstanceMatchesPackageLevel(t *testing.T) { + previous := ephem.Active() + ephem.SetActive(ephem.Legacy) + t.Cleanup(func() { ephem.SetActive(previous) }) + inst := ephem.NewModelInstance(ephem.Legacy) + for _, jd := range []float64{2451545.0, 2460384.5, 2460569.5} { + if got, want := SunTimeWithInstance(jd, inst), SunTime(jd); math.Abs(got-want) > 1e-9 { + t.Fatalf("SunTime jd %.1f: instance %.9f h, package %.9f h", jd, got, want) + } + if got, want := SunTimeNWithInstance(jd, 20, inst), SunTimeN(jd, 20); math.Abs(got-want) > 1e-9 { + t.Fatalf("SunTimeN jd %.1f: instance %.9f h, package %.9f h", jd, got, want) + } + if got, want := CulminationTimeWithInstance(jd, 116.4, 8, inst), CulminationTime(jd, 116.4, 8); math.Abs(got-want)*86400 > 1e-3 { + t.Fatalf("CulminationTime jd %.1f: differs by %.3f s", jd, (got-want)*86400) + } + } +} + +func TestP3SunMoonAliasFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2}) + jd := 2451545.0 + + if math.Abs(angleDifference(SunApparentRaWithInstance(jd, first), SunApparentRaWithInstance(jd, second))) < 1 { + t.Fatal("SunApparentRa alias did not follow explicit instance") + } + if math.Abs(angleDifference(MoonGeocentricApparentRaWithInstance(jd, first), MoonGeocentricApparentRaWithInstance(jd, second))) < 1 { + t.Fatal("MoonGeocentricApparentRa alias did not follow explicit instance") + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"SunApparentRa", SunApparentRaWithInstance(jd, inst)}, + {"SunTrueDec", SunTrueDecWithInstance(jd, inst)}, + {"MoonGeocentricTrueRa", MoonGeocentricTrueRaWithInstance(jd, inst)}, + {"MoonTrueDec", MoonTrueDecWithInstance(jd, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + if got, want := SunApparentRaWithInstance(jd, nil), SunApparentRa(jd); got != want { + t.Fatalf("nil instance changed SunApparentRa: %.15f vs %.15f", got, want) + } + if got, want := MoonGeocentricTrueRaWithInstance(jd, nil), HMoonGeocentricTrueRa(jd); got != want { + t.Fatalf("nil instance changed MoonGeocentricTrueRa: %.15f vs %.15f", got, want) + } +} + +func TestP3PlanetMagnitudeFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0, distance: 0.8}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2, distance: 1.2}) + jd := 2451545.0 + + for _, sample := range []struct { + name string + first float64 + second float64 + }{ + {"MercuryMag", MercuryMagWithInstance(jd, first), MercuryMagWithInstance(jd, second)}, + {"VenusMag", VenusMagWithInstance(jd, first), VenusMagWithInstance(jd, second)}, + {"MarsMag", MarsMagWithInstance(jd, first), MarsMagWithInstance(jd, second)}, + {"JupiterMag", JupiterMagWithInstance(jd, first), JupiterMagWithInstance(jd, second)}, + {"UranusMag", UranusMagWithInstance(jd, first), UranusMagWithInstance(jd, second)}, + {"NeptuneMag", NeptuneMagWithInstance(jd, first), NeptuneMagWithInstance(jd, second)}, + } { + if math.Abs(sample.first-sample.second) < 1e-6 { + t.Fatalf("%s did not follow explicit instance: %.12f vs %.12f", sample.name, sample.first, sample.second) + } + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"MercuryMag", MercuryMagWithInstance(jd, inst)}, + {"JupiterMag", JupiterMagWithInstance(jd, inst)}, + {"NeptuneMag", NeptuneMagWithInstance(jd, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + if got, want := MercuryMagWithInstance(jd, nil), MercuryMag(jd); got != want { + t.Fatalf("nil instance changed MercuryMag: %.15f vs %.15f", got, want) + } +} + +func TestP3SaturnRingFunctionsUseExplicitInstances(t *testing.T) { + first := ephem.NewInstance(p3ProbeEngine{angle: 0, distance: 0.8}) + second := ephem.NewInstance(p3ProbeEngine{angle: math.Pi / 2, distance: 1.2}) + jd := 2451545.0 + + firstRing := SaturnRingBWithInstance(jd, first) + secondRing := SaturnRingBWithInstance(jd, second) + if math.Abs(firstRing-secondRing) < 1e-6 { + t.Fatalf("Saturn ring opening angle did not follow explicit instance: %.12f vs %.12f", firstRing, secondRing) + } + firstMagnitude := SaturnMagWithInstance(jd, first) + secondMagnitude := SaturnMagWithInstance(jd, second) + if math.Abs(firstMagnitude-secondMagnitude) < 1e-6 { + t.Fatalf("Saturn magnitude did not follow explicit instance: %.12f vs %.12f", firstMagnitude, secondMagnitude) + } + + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + for _, sample := range []struct { + name string + value float64 + }{ + {"SaturnRingB", SaturnRingBWithInstance(jd, inst)}, + {"SaturnRingPositionAngle", SaturnRingPositionAngleWithInstance(jd, inst)}, + {"SaturnMag", SaturnMagWithInstance(jd, inst)}, + } { + if math.IsNaN(sample.value) || math.IsInf(sample.value, 0) { + t.Fatalf("%s model %d returned non-finite value: %v", sample.name, model, sample.value) + } + } + } + + if got, want := SaturnMagWithInstance(jd, nil), SaturnMag(jd); got != want { + t.Fatalf("nil instance changed SaturnMag: %.15f vs %.15f", got, want) + } +} + +func angleDifference(a, b float64) float64 { + difference := math.Mod(a-b+180, 360) + if difference < 0 { + difference += 360 + } + return difference - 180 +} diff --git a/basic/ephemeris_test.go b/basic/ephemeris_test.go new file mode 100644 index 0000000..10ae90c --- /dev/null +++ b/basic/ephemeris_test.go @@ -0,0 +1,41 @@ +// 被测契约:默认构建以旧级数开始;切换模型使缓存世代递增,重复设置同一模型不递增,未知模型被拒绝。 +package basic + +import ( + "testing" + + "b612.me/astro/internal/ephem" +) + +func TestEphemerisDefaultIsBuildDefault(t *testing.T) { + if ephem.BuildDefault != ephem.Legacy && ephem.BuildDefault != ephem.Semianalytic { + t.Fatalf("build default %d", ephem.BuildDefault) + } + if GetEphemerisModel() != EphemerisModel(ephem.BuildDefault) { + t.Fatalf("default model %d, build default %d", GetEphemerisModel(), ephem.BuildDefault) + } +} + +func TestSetEphemerisModelBumpsGeneration(t *testing.T) { + if SetEphemerisModel(EphemerisModel(99)) { + t.Fatal("unknown model accepted") + } + prev := GetEphemerisModel() + t.Cleanup(func() { SetEphemerisModel(prev) }) + SetEphemerisModel(EphemerisLegacy) + g := deltaTGenerationValue() + SetEphemerisModel(EphemerisSemianalytic) + if deltaTGenerationValue() == g { + t.Fatal("generation did not change") + } + g = deltaTGenerationValue() + SetEphemerisModel(EphemerisSemianalytic) + if deltaTGenerationValue() != g { + t.Fatal("setting the same model changed the generation") + } + before := HMoonTrueLo(2451545.0) + SetEphemerisModel(EphemerisLegacy) + if HMoonTrueLo(2451545.0) == before { + t.Fatal("moon longitude did not follow the model switch") + } +} diff --git a/basic/event_baseline_golden_test.go b/basic/event_baseline_golden_test.go new file mode 100644 index 0000000..7724b0e --- /dev/null +++ b/basic/event_baseline_golden_test.go @@ -0,0 +1,85 @@ +// 事件回归基线按星历模型分文件:旧模型用 testdata/.json,新模型用 testdata/semianalytic/.json。 +// ASTRO_UPDATE_GOLDEN=1 且当前为新模型时,以旧基线的输入时刻重算并重写新模型基线;旧模型基线从不改写。 +package basic + +import ( + "encoding/json" + "math" + "os" + "path/filepath" + "sort" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/ephemtest" +) + +type eventBaselineSample struct { + InputUTC string `json:"input_utc"` + TTJDBits uint64 `json:"tt_jd_bits"` + Events map[string]uint64 `json:"events"` +} + +type eventBaselineFile struct { + Samples []eventBaselineSample `json:"samples"` +} + +type namedEventFunc struct { + name string + fn func(float64) float64 +} + +func readEventBaseline(t *testing.T, path string) eventBaselineFile { + t.Helper() + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var b eventBaselineFile + if err := json.Unmarshal(data, &b); err != nil { + t.Fatal(err) + } + if len(b.Samples) == 0 { + t.Fatalf("empty event baseline %s", path) + } + return b +} + +// eventBaselineData 当前模型的基线原始字节;需要时先重写新模型基线。 +func eventBaselineData(t *testing.T, legacyPath string, cases []namedEventFunc) []byte { + t.Helper() + path := ephemtest.Golden(legacyPath) + if ephemtest.Update() && ephem.Active() == ephem.Semianalytic { + src := readEventBaseline(t, legacyPath) + out := eventBaselineFile{Samples: make([]eventBaselineSample, len(src.Samples))} + for i, s := range src.Samples { + jd := math.Float64frombits(s.TTJDBits) + ev := make(map[string]uint64, len(cases)) + for _, c := range cases { + ev[c.name] = math.Float64bits(c.fn(jd)) + } + out.Samples[i] = eventBaselineSample{InputUTC: s.InputUTC, TTJDBits: s.TTJDBits, Events: ev} + } + data, err := json.MarshalIndent(out, "", " ") + if err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, append(data, '\n'), 0o644); err != nil { + t.Fatal(err) + } + names := make([]string, 0, len(cases)) + for _, c := range cases { + names = append(names, c.name) + } + sort.Strings(names) + t.Logf("rewrote %s: %d samples × %d events", path, len(out.Samples), len(names)) + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("%v (regenerate with ASTRO_UPDATE_GOLDEN=1 go test)", err) + } + return data +} diff --git a/basic/event_refine.go b/basic/event_refine.go index b1e860a..05431b9 100644 --- a/basic/event_refine.go +++ b/basic/event_refine.go @@ -6,9 +6,7 @@ const ( eventNewtonMaxIterations = 24 eventDirectionalSearchIterations = 128 eventRiseSetScanStep = 1.0 / 1440 - // stationDerivativeStepDay 「留」精修用的中心差分步长(天)。 - // 0.5 秒级的步长会被浮点相消噪声支配(实测外行星留误差可达 42 s); - // 0.01 天(14.4 分钟)实测误差 <0.05 s,且求值次数不变。 + // stationDerivativeStepDay 「留」精修用的中心差分步长(天):更细的步长会被浮点相消噪声支配。 stationDerivativeStepDay = 0.01 ) diff --git a/basic/greatest_time_contour.go b/basic/greatest_time_contour.go index bfd8d21..fb05349 100644 --- a/basic/greatest_time_contour.go +++ b/basic/greatest_time_contour.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) // 等时线(食甚/掩甚时刻等值线)的共享常量与工具。 @@ -15,8 +17,6 @@ const ( greatestTimeContourCoverToleranceKM = 10.0 // greatestTimeContourCorrectionBacktracking 是牛顿投影跳出定义域时按二分回退的次数。 greatestTimeContourCorrectionBacktracking = 6 - // greatestTimeContourKMPerDegree 是地面大圆每度的近似长度,用于局部平面投影。 - greatestTimeContourKMPerDegree = 111.195 // greatestTimeContourMaxLevels 是单个事件允许的等时线条数上限;超出部分按时间截断, // 避免极长事件请求出上万条曲线。 greatestTimeContourMaxLevels = 64 @@ -107,9 +107,9 @@ func greatestTimeContourUTCToTT(value time.Time) float64 { func greatestTimeContourPointSegmentKM(longitude, latitude, startLongitude, startLatitude, endLongitude, endLatitude float64) float64 { cosine := math.Cos(latitude * rad) toX := func(value float64) float64 { - return normalizeLongitude(value-startLongitude) * rad * cosine * greatestTimeContourKMPerDegree + return geodata.NormalizeLongitude(value-startLongitude) * rad * cosine * geodata.EarthKMPerDegree } - toY := func(value float64) float64 { return (value - startLatitude) * greatestTimeContourKMPerDegree } + toY := func(value float64) float64 { return (value - startLatitude) * geodata.EarthKMPerDegree } pointX, pointY := toX(longitude), toY(latitude) endX, endY := toX(endLongitude), toY(endLatitude) lengthSquared := endX*endX + endY*endY diff --git a/basic/heliacal.go b/basic/heliacal.go new file mode 100644 index 0000000..2b2a633 --- /dev/null +++ b/basic/heliacal.go @@ -0,0 +1,272 @@ +package basic + +import ( + "math" + "sort" +) + +const ( + schaeferNightSkyNanolambert = 180.0 + schaeferNanolambertToErg = 1.02e-15 + schaeferSunMagnitude = -26.74 + schaeferZeroMagnitudeRef = -11.05 + schaeferMinSeparationDeg = 0.1 + schaeferTwilightLowSunDeg = -3.0 + schaeferDaylightHighSunDeg = 4.0 + schaeferScotopicLimitNL = 1479.1 +) + +// HeliacalOptions 偕日可见计算参数;零值使用消光系数 0.25 星等/大气质量、目标最低高度 0.5° / heliacal visibility options. +type HeliacalOptions struct { + // ExtinctionCoefficient 大气消光系数(星等/大气质量)/ extinction in magnitudes per airmass. + ExtinctionCoefficient float64 + // MinTargetAltitude 目标最低几何高度(度)/ minimum geometric target altitude in degrees. + MinTargetAltitude float64 + // Moonlight 为 true 时计入月光散射(Krisciunas–Schaefer);默认不计 / include scattered moonlight when true. + Moonlight bool +} + +func (options HeliacalOptions) normalized() HeliacalOptions { + if !(options.ExtinctionCoefficient > 0) { + options.ExtinctionCoefficient = 0.25 + } + if !(options.MinTargetAltitude > 0) { + options.MinTargetAltitude = 0.5 + } + return options +} + +func schaeferAirmass(altitudeDeg float64) float64 { + z := math.Min(90, math.Max(0, 90-altitudeDeg)) * rad + c := math.Cos(z) + return 1 / (c + 0.025*math.Exp(-11*c)) +} + +func schaeferTransmission(k, airmass float64) float64 { return math.Pow(10, -0.4*k*airmass) } + +func schaeferPhase(separationDeg float64) float64 { + s := math.Max(schaeferMinSeparationDeg, separationDeg) + c := math.Cos(s * rad) + return 6.2e7/(s*s) + math.Pow(10, 6.15-s/40) + math.Pow(10, 5.36)*(1.06+c*c) +} + +func validExtinction(extinction float64) bool { + return extinction > 0 && !math.IsInf(extinction, 0) +} + +// SkyBrightnessNanolambert 无月夜晨昏天光亮度(nL,Schaefer 模型),消光系数须为正,否则返回 NaN / twilight sky brightness without moonlight, Schaefer model. +func SkyBrightnessNanolambert(targetAltitude, sunAltitude, separation, extinction float64) float64 { + if !validExtinction(extinction) { + return math.NaN() + } + k := extinction + targetT := schaeferTransmission(k, schaeferAirmass(targetAltitude)) + sunT := schaeferTransmission(k, schaeferAirmass(sunAltitude)) + targetZ := math.Min(90, math.Max(0, 90-targetAltitude)) + sunZ := math.Min(180, math.Max(0, 90-sunAltitude)) + sep := math.Max(schaeferMinSeparationDeg, separation) + scale := math.Pow(10, -0.4*(schaeferSunMagnitude-schaeferZeroMagnitudeRef+43.27)) + day := scale * (1 - targetT) * (schaeferPhase(sep)*sunT + 440000*(1-sunT)) + twilight := math.Pow(10, -0.4*(schaeferSunMagnitude-schaeferZeroMagnitudeRef-57.5+sunZ-targetZ/(360*k))) * + (1 - targetT) * 100 / sep + sky := math.Min(day, twilight) + switch { + case sunAltitude < schaeferTwilightLowSunDeg: + sky = twilight + case sunAltitude > schaeferDaylightHighSunDeg: + sky = day + } + sz := math.Sin(targetZ * rad) + night := schaeferNightSkyNanolambert * (0.4 + 0.6/math.Sqrt(1-0.96*sz*sz)) * targetT + return sky/schaeferNanolambertToErg + night +} + +// LimitingMagnitude 肉眼极限星等(已扣除目标方向的大气消光),消光系数须为正,否则返回 NaN / naked-eye limiting magnitude after extinction at the target altitude. +func LimitingMagnitude(targetAltitude, sunAltitude, separation, extinction float64) float64 { + return limitingMagnitudeForSky(SkyBrightnessNanolambert(targetAltitude, sunAltitude, separation, extinction), targetAltitude, extinction) +} + +// MoonlightNanolambert 月光在目标方向造成的天光亮度(nL,Krisciunas–Schaefer);月在地平下为 0,消光系数非正为 NaN / +// sky brightness from scattered moonlight; zero when the Moon is below the horizon. +func MoonlightNanolambert(targetAltitude, moonAltitude, moonSeparation, moonPhaseAngle, extinction float64) float64 { + if !validExtinction(extinction) { + return math.NaN() + } + if moonAltitude <= 0 || targetAltitude <= 0 { + return 0 + } + alpha := math.Abs(moonPhaseAngle) + mag := -12.73 + 0.026*alpha + 4e-9*math.Pow(alpha, 4) + illuminance := math.Pow(10, -0.4*(mag+16.57)) + rho := math.Max(schaeferMinSeparationDeg, moonSeparation) + c := math.Cos(rho * rad) + scatter := math.Pow(10, 5.36)*(1.06+c*c) + math.Pow(10, 6.15-rho/40) + airmass := func(alt float64) float64 { + s := math.Sin((90 - alt) * rad) + return 1 / math.Sqrt(1-0.96*s*s) + } + return scatter * illuminance * math.Pow(10, -0.4*extinction*airmass(moonAltitude)) * + (1 - math.Pow(10, -0.4*extinction*airmass(targetAltitude))) +} + +func limitingMagnitudeForSky(b, targetAltitude, extinction float64) float64 { + c, k := 4.4668359215096e-9, 1.2589254117942e-6 + if b < schaeferScotopicLimitNL { + c, k = 1.5848931924611e-10, 0.012589254117942 + } + threshold := c * math.Pow(1+math.Sqrt(k*b), 2) + return -16.57 - 2.5*math.Log10(threshold) - extinction*schaeferAirmass(targetAltitude) +} + +// HeliacalEventKind 偕日事件类型 / heliacal event kind. +type HeliacalEventKind int + +const ( + // HeliacalMorningFirst 晨始见(偕日升)/ morning first visibility. + HeliacalMorningFirst HeliacalEventKind = iota + // HeliacalMorningLast 晨末见 / morning last visibility. + HeliacalMorningLast + // HeliacalEveningFirst 昏始见 / evening first visibility. + HeliacalEveningFirst + // HeliacalEveningLast 昏末见(偕日落)/ evening last visibility. + HeliacalEveningLast +) + +// HeliacalEvent 偕日事件:JD 为当天最佳可见时刻(UT 儒略日),Margin 为极限星等减目标星等 / heliacal event at the best moment of that twilight. +type HeliacalEvent struct { + Kind HeliacalEventKind + JD float64 + Margin float64 + TargetAltitude float64 + SunAltitude float64 +} + +// heliacalTarget 返回 TT 时刻目标的视赤经、赤纬(度)与视星等。 +type heliacalTarget func(jde float64) (ra, dec, magnitude float64) + +type heliacalBest struct { + jd, margin, targetAlt, sunAlt float64 + visible bool +} + +func heliacalAltitude(ra, dec, lst, lat float64) float64 { + h := (lst - ra) * rad + d, p := dec*rad, lat*rad + return math.Asin(math.Sin(p)*math.Sin(d)+math.Cos(p)*math.Cos(d)*math.Cos(h)) / rad +} + +// heliacalTwilight 扫描一次晨(morning)或昏的太阳高度 −18°..0° 时段,取可见余量最大的样本。 +// 日月位置在窗口中点取一次,窗口不足两小时,引入的高度误差远小于模型本身。 +func heliacalTwilight(dayJD, lon, lat float64, morning bool, target heliacalTarget, options HeliacalOptions, inst *Instance) heliacalBest { + center := dayJD - lon/360 + 0.5 + if morning { + center -= 0.25 + } else { + center += 0.25 + } + jde := UTC2TT(center) + sunRA, sunDec := HSunApparentRaDecWithInstance(jde, inst) + ra, dec, mag := target(jde) + sep := skyAngularDistance(ra, dec, sunRA, sunDec) + best := heliacalBest{margin: math.Inf(-1)} + if math.IsNaN(ra) || math.IsNaN(mag) { + return best + } + var moonRA, moonDec, moonSep, moonPhase float64 + if options.Moonlight { + lo, bo := SkyBodyApparentLoBoWithInstance(SkyMoon, jde, inst) + moonRA, moonDec = LoBoToRaDec(jde, lo, bo) + moonSep = skyAngularDistance(ra, dec, moonRA, moonDec) + moonPhase = 180 - skyAngularDistance(moonRA, moonDec, sunRA, sunDec) + } + lst0 := ApparentSiderealTime(UTC2UT1(center))*15 + lon + const step = 2.0 / 1440 + for dt := -0.25; dt <= 0.25; dt += step { + lst := lst0 + dt*360.98564736629 + sunAlt := heliacalAltitude(sunRA, sunDec, lst, lat) + if sunAlt > 0 || sunAlt < -18 { + continue + } + alt := heliacalAltitude(ra, dec, lst, lat) + if alt < options.MinTargetAltitude { + continue + } + sky := SkyBrightnessNanolambert(alt, sunAlt, sep, options.ExtinctionCoefficient) + if options.Moonlight { + sky += MoonlightNanolambert(alt, heliacalAltitude(moonRA, moonDec, lst, lat), moonSep, moonPhase, options.ExtinctionCoefficient) + } + margin := limitingMagnitudeForSky(sky, alt, options.ExtinctionCoefficient) - mag + if margin > best.margin { + best = heliacalBest{center + dt, margin, alt, sunAlt, margin >= 0} + } + } + return best +} + +// heliacalMaxDays 逐日扫描的天数上限(约 2.3 万年),挡住非法跨度造成的近乎无界扫描。 +const heliacalMaxDays = 1 << 23 + +// findHeliacalEvents 逐日扫描晨、昏可见性,从不可见变为可见记“始见”,反之记“末见”(取末个可见日)。 +func findHeliacalEvents(startJD, endJD, lon, lat float64, target heliacalTarget, options HeliacalOptions, inst *Instance) []HeliacalEvent { + options = options.normalized() + if !finite(startJD) || !finite(endJD) || !(endJD > startJD) || math.Abs(lat) >= 90 { + return nil + } + day0 := math.Floor(startJD-0.5) + 0.5 + span := math.Floor(endJD - day0) + if !(span < heliacalMaxDays) { + return nil + } + days := int(span) + var out []HeliacalEvent + for _, morning := range []bool{true, false} { + first, last := HeliacalEveningFirst, HeliacalEveningLast + if morning { + first, last = HeliacalMorningFirst, HeliacalMorningLast + } + prev := heliacalTwilight(day0-1, lon, lat, morning, target, options, inst) + for i := 0; i <= days; i++ { + cur := heliacalTwilight(day0+float64(i), lon, lat, morning, target, options, inst) + // 事件时刻是采样日内的细化值,可能越过 endJD,所以上界按事件时刻判断,不能按采样日判断。 + switch { + case cur.visible && !prev.visible && cur.jd >= startJD && cur.jd <= endJD: + out = append(out, HeliacalEvent{first, cur.jd, cur.margin, cur.targetAlt, cur.sunAlt}) + case !cur.visible && prev.visible && prev.jd >= startJD && prev.jd <= endJD: + out = append(out, HeliacalEvent{last, prev.jd, prev.margin, prev.targetAlt, prev.sunAlt}) + } + prev = cur + } + } + sortHeliacalEvents(out) + return out +} + +func sortHeliacalEvents(events []HeliacalEvent) { + sort.SliceStable(events, func(i, j int) bool { return events[i].JD < events[j].JD }) +} + +// FindPlanetHeliacalEventsWithInstance 行星偕日事件(UT 儒略日范围,经度东正)/ planet heliacal events in a UT JD range. +func FindPlanetHeliacalEventsWithInstance(body SkyBody, startJD, endJD, lon, lat float64, options HeliacalOptions, inst *Instance) []HeliacalEvent { + if body < SkyMercury || body > SkyNeptune { + return nil + } + index := int(body-SkyMercury) + 1 + target := func(jde float64) (float64, float64, float64) { + lo, bo := SkyBodyApparentLoBoWithInstance(body, jde, inst) + ra, dec := LoBoToRaDec(jde, lo, bo) + return ra, dec, planetMagnitude2018(index, jde, inst) + } + return findHeliacalEvents(startJD, endJD, lon, lat, target, options, inst) +} + +// FindStarHeliacalEventsWithInstance 恒星偕日事件;magnitude 为恒星 V 星等 / star heliacal events with the star's V magnitude. +func FindStarHeliacalEventsWithInstance(star StarCoordinate, magnitude, startJD, endJD, lon, lat float64, options HeliacalOptions, inst *Instance) []HeliacalEvent { + if math.IsNaN(magnitude) || math.IsInf(magnitude, 0) { + return nil + } + target := func(jde float64) (float64, float64, float64) { + ra, dec := starApparentRaDecGeocentric(jde, star, inst) + return ra, dec, magnitude + } + return findHeliacalEvents(startJD, endJD, lon, lat, target, options, inst) +} diff --git a/basic/heliacal_test.go b/basic/heliacal_test.go new file mode 100644 index 0000000..a3224f2 --- /dev/null +++ b/basic/heliacal_test.go @@ -0,0 +1,138 @@ +// 被测契约:天狼星在开罗约 8 月初晨始见;金星下合前后依次出现昏末见与晨始见;极限星等随天光单调变化;偕日事件不得越出请求区间。 +package basic + +import ( + "math" + "testing" + "time" +) + +func TestSiriusHeliacalRisingCairo(t *testing.T) { + sirius := StarCoordinate{ID: "Sirius", RA: 101.28715533, Dec: -16.71611586, Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -546.01, ProperMotionDecMasPerYear: -1223.07} + start := Date2JD(time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)) + end := Date2JD(time.Date(2025, 9, 30, 0, 0, 0, 0, time.UTC)) + events := FindStarHeliacalEventsWithInstance(sirius, -1.46, start, end, 31.23, 30.04, HeliacalOptions{}, nil) + var rising *HeliacalEvent + for i := range events { + if events[i].Kind == HeliacalMorningFirst { + rising = &events[i] + break + } + } + if rising == nil { + t.Fatalf("no morning first visibility: %+v", events) + } + when := JD2Date(rising.JD) + t.Logf("Sirius morning first %v margin %.2f alt %.2f sun %.2f", when, rising.Margin, rising.TargetAltitude, rising.SunAltitude) + if when.Month() != time.July && when.Month() != time.August { + t.Errorf("Sirius heliacal rising %v, expected late July to mid August", when) + } +} + +func TestVenusHeliacalAroundInferiorConjunction(t *testing.T) { + // 2025-03-23 金星下合。 + start := Date2JD(time.Date(2025, 2, 15, 0, 0, 0, 0, time.UTC)) + end := Date2JD(time.Date(2025, 4, 30, 0, 0, 0, 0, time.UTC)) + events := FindPlanetHeliacalEventsWithInstance(SkyVenus, start, end, 116.4, 39.9, HeliacalOptions{}, nil) + var eveningLast, morningFirst float64 + for _, e := range events { + t.Logf("kind %d %v margin %.2f", e.Kind, JD2Date(e.JD), e.Margin) + if e.Kind == HeliacalEveningLast && eveningLast == 0 { + eveningLast = e.JD + } + if e.Kind == HeliacalMorningFirst && morningFirst == 0 { + morningFirst = e.JD + } + } + conj := Date2JD(time.Date(2025, 3, 23, 0, 0, 0, 0, time.UTC)) + if eveningLast == 0 || morningFirst == 0 || !(eveningLast < conj && morningFirst > conj) { + t.Errorf("evening last %.1f, morning first %.1f around conjunction %.1f", eveningLast, morningFirst, conj) + } +} + +func TestLimitingMagnitudeDarkensWithTwilight(t *testing.T) { + prev := LimitingMagnitude(20, -2, 30, 0.25) + for _, sun := range []float64{-4, -6, -9, -12, -15, -18} { + cur := LimitingMagnitude(20, sun, 30, 0.25) + if !(cur > prev) { + t.Errorf("sun %.0f: limiting magnitude %.2f not fainter than %.2f", sun, cur, prev) + } + prev = cur + } + if prev < 5 || prev > 7.5 { + t.Errorf("dark-sky limiting magnitude at 20° altitude %.2f", prev) + } +} + +func TestMoonlightBrightensSky(t *testing.T) { + if MoonlightNanolambert(30, -5, 20, 10, 0.25) != 0 { + t.Error("moon below horizon must contribute nothing") + } + full := MoonlightNanolambert(30, 40, 20, 5, 0.25) + crescent := MoonlightNanolambert(30, 40, 20, 120, 0.25) + if !(full > crescent && crescent > 0 && full > 1000) { + t.Errorf("full %.1f crescent %.1f nL", full, crescent) + } + start := 2460676.5 + plain := FindPlanetHeliacalEventsWithInstance(SkyVenus, start, start+400, 116.4, 39.9, HeliacalOptions{}, nil) + lit := FindPlanetHeliacalEventsWithInstance(SkyVenus, start, start+400, 116.4, 39.9, HeliacalOptions{Moonlight: true}, nil) + if len(plain) == 0 || len(lit) == 0 { + t.Fatalf("events %d %d", len(plain), len(lit)) + } +} + +func TestHeliacalRejectsUnscannableRange(t *testing.T) { + sirius := StarCoordinate{RA: 101.287, Dec: -16.716} + for _, r := range [][2]float64{ + {2451545, math.Inf(1)}, {math.Inf(-1), 2451545}, {math.NaN(), 2451545}, + {2451545, math.NaN()}, {2451545, 2451545 + 1e10}, {1e300, math.MaxFloat64}, + } { + if got := FindStarHeliacalEventsWithInstance(sirius, -1.46, r[0], r[1], 31.23, 30.04, HeliacalOptions{}, nil); got != nil { + t.Errorf("star %v: %d events", r, len(got)) + } + if got := FindPlanetHeliacalEventsWithInstance(SkyVenus, r[0], r[1], 116.4, 39.9, HeliacalOptions{}, nil); got != nil { + t.Errorf("planet %v: %d events", r, len(got)) + } + } +} + +func TestSkyModelRejectsNonPositiveExtinction(t *testing.T) { + for _, k := range []float64{0, -1, math.NaN(), math.Inf(1)} { + if v := SkyBrightnessNanolambert(20, -10, 30, k); !math.IsNaN(v) { + t.Errorf("sky k=%g: %g", k, v) + } + if v := LimitingMagnitude(20, -10, 30, k); !math.IsNaN(v) { + t.Errorf("limit k=%g: %g", k, v) + } + if v := MoonlightNanolambert(30, 40, 20, 5, k); !math.IsNaN(v) { + t.Errorf("moonlight k=%g: %g", k, v) + } + } +} + +func TestHeliacalEventsStayWithinRequestedRange(t *testing.T) { + lon, lat := 116.4, 39.9 + narrow := 0 + for _, body := range []SkyBody{SkyMercury, SkyVenus, SkyMars, SkyJupiter, SkySaturn} { + wide := FindPlanetHeliacalEventsWithInstance(body, 2459000.5, 2460000.5, lon, lat, HeliacalOptions{}, nil) + for _, event := range wide { + // 事件时刻是采样日内的细化值;把 endJD 卡在两者之间,正是越界最容易出现的窗口。 + day := math.Floor(event.JD-0.5) + 0.5 + offset := event.JD - day + if offset <= 0.1 { + continue + } + start, end := day, day+offset-0.05 + narrow++ + for _, got := range FindPlanetHeliacalEventsWithInstance(body, start, end, lon, lat, HeliacalOptions{}, nil) { + if got.JD < start || got.JD > end { + t.Fatalf("body %v: event JD %.6f outside [%.6f, %.6f]", body, got.JD, start, end) + } + } + } + } + if narrow == 0 { + t.Fatal("no narrow window was exercised") + } +} diff --git a/basic/iau2006.go b/basic/iau2006.go new file mode 100644 index 0000000..e5ff0e2 --- /dev/null +++ b/basic/iau2006.go @@ -0,0 +1,32 @@ +package basic + +import "b612.me/astro/internal/iau2006" + +// RotationMatrix 3×3 旋转矩阵,按行存放,作用于列向量 / 3×3 rotation matrix, row-major, acting on column vectors. +type RotationMatrix = iau2006.Matrix + +// Nutation2000A IAU 2000A 章动,返回 (黄经章动, 交角章动),单位度;不进入默认计算链 / IAU 2000A nutation in degrees, opt-in only. +func Nutation2000A(jde float64) (float64, float64) { + dpsi, deps := iau2006.Nutation(jde) + return dpsi / 3600, deps / 3600 +} + +// MeanObliquity2006 IAU 2006 平黄赤交角(度)/ IAU 2006 mean obliquity in degrees. +func MeanObliquity2006(jde float64) float64 { return iau2006.MeanObliquity(jde) / 3600 } + +// EquationOfEquinoxes2006 春分点方程主项 Δψ·cos εA(度),按 IAU 2000A 章动 / equation of the equinoxes in degrees, IAU 2000A. +func EquationOfEquinoxes2006(jde float64) float64 { return iau2006.EquationOfEquinoxes(jde) / 3600 } + +// Precession2006Matrix J2000 平赤道到历元平赤道的 IAU 2006 岁差矩阵 / IAU 2006 precession matrix. +func Precession2006Matrix(jde float64) RotationMatrix { return iau2006.Precession(jde) } + +// Nutation2000AMatrix 历元平赤道到真赤道的 IAU 2000A 章动矩阵 / IAU 2000A nutation matrix. +func Nutation2000AMatrix(jde float64) RotationMatrix { return iau2006.NutationMatrix(jde) } + +// FrameBiasMatrix ICRS 到 J2000 平赤道的框架偏差矩阵 / frame bias from ICRS to the J2000 mean equator. +func FrameBiasMatrix() RotationMatrix { return iau2006.FrameBias() } + +// BiasPrecessionNutation2006 ICRS 到真赤道真春分点的完整旋转 N·P·B / full ICRS to true-of-date rotation. +func BiasPrecessionNutation2006(jde float64) RotationMatrix { + return iau2006.BiasPrecessionNutation(jde) +} diff --git a/basic/iau2006_test.go b/basic/iau2006_test.go new file mode 100644 index 0000000..388a696 --- /dev/null +++ b/basic/iau2006_test.go @@ -0,0 +1,25 @@ +// 被测契约:2000A 与默认 2000B 章动差在 1 mas 量级,单位换算正确。 +package basic + +import ( + "math" + "testing" +) + +func TestNutation2000ANearDefault(t *testing.T) { + for _, jde := range []float64{2451545.0, 2460676.5, 2415020.0, 2488069.5} { + a1, a2 := Nutation2000A(jde) + b1, b2 := Nutation2000B(jde) + if d := math.Max(math.Abs(a1-b1), math.Abs(a2-b2)) * 3600e3; d > 2 { + t.Errorf("jde %.1f: 2000A vs 2000B differ by %.3f mas", jde, d) + } + } + if d := math.Abs(MeanObliquity2006(2451545.0)*3600 - 84381.406); d > 1e-9 { + t.Errorf("J2000 obliquity off by %g arcsec", d) + } + m := BiasPrecessionNutation2006(2460676.5) + v := m.Apply([3]float64{1, 0, 0}) + if n := math.Sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); math.Abs(n-1) > 1e-14 { + t.Errorf("rotation not orthonormal: %g", n) + } +} diff --git a/basic/jupiter.go b/basic/jupiter.go index 309e560..53e39fd 100644 --- a/basic/jupiter.go +++ b/basic/jupiter.go @@ -115,59 +115,34 @@ func JupiterMag(jde float64) float64 { return FloatRound(mag, 2) } -func JupiterHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := JupiterApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// JupiterHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(4, localJD, lon, lat, timezone, -1, inst) } -func JupiterAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := JupiterApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// JupiterAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(4, localJD, lon, lat, timezone, -1, inst) } -func JupiterHourAngle(jd, lon, timezone float64) float64 { +// JupiterHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - JupiterApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - JupiterApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func JupiterCulminationTime(localJD, lon, timezone float64) float64 { +// JupiterCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-JupiterHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-JupiterHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := JupiterHourAngle(localJD, lon, timezone) + currentHourAngle := JupiterHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -185,14 +160,22 @@ func JupiterCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func JupiterRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// JupiterRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func JupiterSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// JupiterSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, JupiterCulminationTime, JupiterHeight, JupiterApparentDec) +func jupiterRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return JupiterCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return JupiterHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return JupiterApparentDecWithInstance(jde, inst) }) } diff --git a/basic/jupiter_events.go b/basic/jupiter_events.go index 919e723..2c58fc2 100644 --- a/basic/jupiter_events.go +++ b/basic/jupiter_events.go @@ -14,8 +14,10 @@ const ( jupiterPhaseCoarseTolerance = 30.0 / 86400.0 ) -func jupiterSunLongitudeDelta(jde, degree float64, filter bool) float64 { - sub := Limit360(Limit360(JupiterApparentLo(jde)-HSunApparentLo(jde)) - degree) +func jupiterSunLongitudeDelta(jde, degree float64, filter bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(JupiterApparentLoWithInstance(jde, inst)-HSunApparentLoWithInstance(jde, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -27,8 +29,10 @@ func jupiterSunLongitudeDelta(jde, degree float64, filter bool) float64 { return sub } -func jupiterSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { - sub := Limit360(Limit360(JupiterApparentLoN(jde, n)-HSunApparentLoN(jde, n)) - degree) +func jupiterSunLongitudeDeltaN(jde, degree float64, filter bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(JupiterApparentLoNWithInstance(jde, n, inst)-HSunApparentLoNWithInstance(jde, n, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -40,8 +44,10 @@ func jupiterSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 return sub } -func jupiterRADerivative(jde, delta float64) float64 { - sub := JupiterApparentRa(jde+delta) - JupiterApparentRa(jde-delta) +func jupiterRADerivative(jde, delta float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := JupiterApparentRaWithInstance(jde+delta, inst) - JupiterApparentRaWithInstance(jde-delta, inst) if sub > 180 { sub -= 360 } @@ -51,8 +57,10 @@ func jupiterRADerivative(jde, delta float64) float64 { return sub / (2 * delta) } -func jupiterRADerivativeN(jde, delta float64, n int) float64 { - sub := JupiterApparentRaN(jde+delta, n) - JupiterApparentRaN(jde-delta, n) +func jupiterRADerivativeN(jde, delta float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := JupiterApparentRaNWithInstance(jde+delta, n, inst) - JupiterApparentRaNWithInstance(jde-delta, n, inst) if sub > 180 { sub -= 360 } @@ -62,13 +70,15 @@ func jupiterRADerivativeN(jde, delta float64, n int) float64 { return sub / (2 * delta) } -func jupiterConjunctionFull(jde, degree float64, next uint8) float64 { +func jupiterConjunctionFull(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := JUPITER_S_PERIOD / 360 - currentDelta := jupiterSunLongitudeDelta(jde, degree, false) + currentDelta := jupiterSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -78,8 +88,8 @@ func jupiterConjunctionFull(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := jupiterSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (jupiterSunLongitudeDelta(prevJDE+0.000005, degree, true) - jupiterSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := jupiterSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (jupiterSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - jupiterSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -93,13 +103,15 @@ func jupiterConjunctionFull(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func jupiterConjunction(jde, degree float64, next uint8) float64 { +func jupiterConjunction(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := JUPITER_S_PERIOD / 360 - currentDelta := jupiterSunLongitudeDelta(jde, degree, false) + currentDelta := jupiterSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -109,8 +121,8 @@ func jupiterConjunction(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := jupiterSunLongitudeDeltaN(prevJDE, degree, true, jupiterEventSearchN) - longitudeSlope := (jupiterSunLongitudeDeltaN(prevJDE+0.000005, degree, true, jupiterEventSearchN) - jupiterSunLongitudeDeltaN(prevJDE-0.000005, degree, true, jupiterEventSearchN)) / 0.00001 + longitudeDelta := jupiterSunLongitudeDeltaN(prevJDE, degree, true, jupiterEventSearchN, inst) + longitudeSlope := (jupiterSunLongitudeDeltaN(prevJDE+0.000005, degree, true, jupiterEventSearchN, inst) - jupiterSunLongitudeDeltaN(prevJDE-0.000005, degree, true, jupiterEventSearchN, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= jupiterPhaseCoarseTolerance { @@ -124,8 +136,8 @@ func jupiterConjunction(jde, degree float64, next uint8) float64 { converged = false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := jupiterSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (jupiterSunLongitudeDelta(prevJDE+0.000005, degree, true) - jupiterSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := jupiterSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (jupiterSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - jupiterSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -139,39 +151,65 @@ func jupiterConjunction(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func LastJupiterConjunction(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 0, jupiterConjunction) +// LastJupiterConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func NextJupiterConjunction(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 0, jupiterConjunction) +// NextJupiterConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func LastJupiterOpposition(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 180, jupiterConjunction) +// LastJupiterOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func NextJupiterOpposition(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 180, jupiterConjunction) +// NextJupiterOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func NextJupiterEasternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 90, jupiterConjunction) +// NextJupiterEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func LastJupiterEasternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 90, jupiterConjunction) +// LastJupiterEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func NextJupiterWesternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 270, jupiterConjunction) +// NextJupiterWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func LastJupiterWesternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 270, jupiterConjunction) +// LastJupiterWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return jupiterConjunction(jde, degree, next, inst) + }) } -func jupiterRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool) float64 { +func jupiterRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(oppositionJD) { return math.NaN() } @@ -179,103 +217,107 @@ func jupiterRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposit startTT := oppositionTT endTT := oppositionTT if searchBeforeOpposition { - easternQuadratureUT := jupiterConjunction(oppositionTT, 90, 0) + easternQuadratureUT := jupiterConjunction(oppositionTT, 90, 0, inst) startTT = UTC2TT(easternQuadratureUT) } else { - westernQuadratureUT := jupiterConjunction(oppositionTT, 270, 1) + westernQuadratureUT := jupiterConjunction(oppositionTT, 270, 1, inst) endTT = UTC2TT(westernQuadratureUT) } bestJDE := zeroEventInWindow(startTT, endTT, 2.0, 2.0, 30.0/86400.0, func(jd float64) float64 { - return jupiterRADerivativeN(jd, stationDerivativeStepDay, jupiterEventSearchN) + return jupiterRADerivativeN(jd, stationDerivativeStepDay, jupiterEventSearchN, inst) }, func(jd float64) float64 { - return jupiterRADerivative(jd, stationDerivativeStepDay) + return jupiterRADerivative(jd, stationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func NextJupiterRetrogradeToPrograde(jde float64) float64 { +// NextJupiterRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := jupiterConjunctionFull(jde, 180, 0) - date := jupiterRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := jupiterConjunctionFull(jde, 180, 0, inst) + date := jupiterRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - nextOppositionJD := jupiterConjunctionFull(jde, 180, 1) + nextOppositionJD := jupiterConjunctionFull(jde, 180, 1, inst) if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - date = jupiterRetrogradeAroundOpposition(nextOppositionJD, false) + date = jupiterRetrogradeAroundOpposition(nextOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastJupiterRetrogradeToPrograde(jde float64) float64 { +// LastJupiterRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := jupiterConjunctionFull(jde, 180, 0) - date := jupiterRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := jupiterConjunctionFull(jde, 180, 0, inst) + date := jupiterRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - previousOppositionJD := jupiterConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0) + previousOppositionJD := jupiterConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0, inst) if !isFiniteFloat(previousOppositionJD) { return math.NaN() } - date = jupiterRetrogradeAroundOpposition(previousOppositionJD, false) + date = jupiterRetrogradeAroundOpposition(previousOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } return date } -func NextJupiterProgradeToRetrograde(jde float64) float64 { +// NextJupiterProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := jupiterConjunctionFull(jde, 180, 1) - date := jupiterRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := jupiterConjunctionFull(jde, 180, 1, inst) + date := jupiterRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - followingOppositionJD := jupiterConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1) + followingOppositionJD := jupiterConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1, inst) if !isFiniteFloat(followingOppositionJD) { return math.NaN() } - date = jupiterRetrogradeAroundOpposition(followingOppositionJD, true) + date = jupiterRetrogradeAroundOpposition(followingOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastJupiterProgradeToRetrograde(jde float64) float64 { +// LastJupiterProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := jupiterConjunctionFull(jde, 180, 1) - date := jupiterRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := jupiterConjunctionFull(jde, 180, 1, inst) + date := jupiterRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - lastOppositionJD := jupiterConjunctionFull(jde, 180, 0) + lastOppositionJD := jupiterConjunctionFull(jde, 180, 0, inst) if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - date = jupiterRetrogradeAroundOpposition(lastOppositionJD, true) + date = jupiterRetrogradeAroundOpposition(lastOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } diff --git a/basic/jupiter_physical.go b/basic/jupiter_physical.go index 062a0fc..fa2236e 100644 --- a/basic/jupiter_physical.go +++ b/basic/jupiter_physical.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -29,10 +28,10 @@ func JupiterCentralMeridians(jde float64) JupiterCentralMeridianInfo { return JupiterCentralMeridiansN(jde, -1) } -// JupiterCentralMeridiansN 木星 System I/II/III 中央经线(截断版) / truncated Jupiter System I/II/III central meridians. -func JupiterCentralMeridiansN(jde float64, n int) JupiterCentralMeridianInfo { - observations := jupiterPhysicalObservationsN(jde, n) - physical := JupiterPhysicalN(jde, n) +// JupiterCentralMeridiansNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterCentralMeridiansNWithInstance(jde float64, n int, inst *Instance) JupiterCentralMeridianInfo { + observations := jupiterPhysicalObservationsN(jde, n, inst) + physical := JupiterPhysicalNWithInstance(jde, n, inst) return JupiterCentralMeridianInfo{ SystemI: observations.SystemI, SystemII: observations.SystemII, @@ -45,13 +44,15 @@ func JupiterDSDE(jde float64) (ds, de float64) { return JupiterDSDEN(jde, -1) } -// JupiterDSDEN 木星 DS/DE 行星中心赤纬(截断版) / truncated Jupiter planetocentric declinations of Sun and Earth. -func JupiterDSDEN(jde float64, n int) (ds, de float64) { - observations := jupiterPhysicalObservationsN(jde, n) +// JupiterDSDENWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterDSDENWithInstance(jde float64, n int, inst *Instance) (ds, de float64) { + observations := jupiterPhysicalObservationsN(jde, n, inst) return observations.DS, observations.DE } -func jupiterPhysicalObservationsN(jde float64, n int) jupiterPhysicalObservationInfo { +func jupiterPhysicalObservationsN(jde float64, n int, instances ...*Instance) jupiterPhysicalObservationInfo { + inst := instanceValue(instances) + days := jde - 2433282.5 julianCentury := days / 36525.0 @@ -60,9 +61,9 @@ func jupiterPhysicalObservationsN(jde float64, n int) jupiterPhysicalObservation w1 := (17.71 + 877.90003539*days) * rad w2 := (16.838 + 870.27003539*days) * rad - earthLon := planet.WherePlanetN(-1, 0, jde, n) - earthLat := planet.WherePlanetN(-1, 1, jde, n) - earthRadius := planet.WherePlanetN(-1, 2, jde, n) + earthLon := PlanetCoordinateNWithInstance(-1, 0, jde, n, inst) + earthLat := PlanetCoordinateNWithInstance(-1, 1, jde, n, inst) + earthRadius := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) delta := 4.0 var jupiterLon float64 @@ -73,9 +74,9 @@ func jupiterPhysicalObservationsN(jde float64, n int) jupiterPhysicalObservation var z float64 for i := 0; i < 2; i++ { lightTimeDays := astronomicalUnitLightTimeDays * delta - jupiterLon = planet.WherePlanetN(4, 0, jde-lightTimeDays, n) - jupiterLat = planet.WherePlanetN(4, 1, jde-lightTimeDays, n) - jupiterRadius = planet.WherePlanetN(4, 2, jde-lightTimeDays, n) + jupiterLon = PlanetCoordinateNWithInstance(4, 0, jde-lightTimeDays, n, inst) + jupiterLat = PlanetCoordinateNWithInstance(4, 1, jde-lightTimeDays, n, inst) + jupiterRadius = PlanetCoordinateNWithInstance(4, 2, jde-lightTimeDays, n, inst) x = jupiterRadius*Cos(jupiterLat)*Cos(jupiterLon) - earthRadius*Cos(earthLat)*Cos(earthLon) y = jupiterRadius*Cos(jupiterLat)*Sin(jupiterLon) - earthRadius*Cos(earthLat)*Sin(earthLon) z = jupiterRadius*Sin(jupiterLat) - earthRadius*Sin(earthLat) diff --git a/basic/jupiter_physical_test.go b/basic/jupiter_physical_test.go index 1101a0f..6d94456 100644 --- a/basic/jupiter_physical_test.go +++ b/basic/jupiter_physical_test.go @@ -6,9 +6,12 @@ import ( "os" "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestJupiterCentralMeridiansMeeusExample43A(t *testing.T) { + ephemtest.PinLegacy(t) got := JupiterCentralMeridians(2448972.50068) ds, de := JupiterDSDE(2448972.50068) diff --git a/basic/jupiter_satellite_contact_events.go b/basic/jupiter_satellite_contact_events.go index 2a45a7e..a9c8c5c 100644 --- a/basic/jupiter_satellite_contact_events.go +++ b/basic/jupiter_satellite_contact_events.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) const ( jupiterGalileanContactBracketStepDays = 2.0 / 1440.0 @@ -59,28 +61,30 @@ type jupiterGalileanContactGeometry struct { effectiveRadius float64 } -// LastJupiterGalileanPhenomenonContactEvent 上一次 IMCCE 风格接触事件 / previous IMCCE-style contact event. -func LastJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { +// LastJupiterGalileanPhenomenonContactEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterGalileanPhenomenonContactEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonContactEvent { return jupiterGalileanPhenomenonContactEventFromEvent( - LastJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType), + LastJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, inst), inst, ) } -// NextJupiterGalileanPhenomenonContactEvent 下一次 IMCCE 风格接触事件 / next IMCCE-style contact event. -func NextJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { +// NextJupiterGalileanPhenomenonContactEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterGalileanPhenomenonContactEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonContactEvent { return jupiterGalileanPhenomenonContactEventFromEvent( - NextJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType), + NextJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, inst), inst, ) } -// ClosestJupiterGalileanPhenomenonContactEvent 最近一次 IMCCE 风格接触事件 / closest IMCCE-style contact event. -func ClosestJupiterGalileanPhenomenonContactEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonContactEvent { +// ClosestJupiterGalileanPhenomenonContactEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestJupiterGalileanPhenomenonContactEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonContactEvent { return jupiterGalileanPhenomenonContactEventFromEvent( - ClosestJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType), + ClosestJupiterGalileanPhenomenonEventWithInstance(jd, satellite, phenomenonType, inst), inst, ) } -func jupiterGalileanPhenomenonContactEventFromEvent(event JupiterGalileanPhenomenonEvent) JupiterGalileanPhenomenonContactEvent { +func jupiterGalileanPhenomenonContactEventFromEvent(event JupiterGalileanPhenomenonEvent, instances ...*Instance) JupiterGalileanPhenomenonContactEvent { + inst := instanceValue(instances) + if !event.Valid { return invalidJupiterGalileanPhenomenonContactEvent() } @@ -90,11 +94,11 @@ func jupiterGalileanPhenomenonContactEventFromEvent(event JupiterGalileanPhenome ok bool ) if event.Type == JupiterGalileanShadowTransit { - disappearance, reappearance, ok = refineJupiterGalileanShadowContactPair(event) + disappearance, reappearance, ok = refineJupiterGalileanShadowContactPair(event, inst) } else if event.Type == JupiterGalileanEclipse { - disappearance, reappearance, ok = refineJupiterGalileanEclipseContactPair(event) + disappearance, reappearance, ok = refineJupiterGalileanEclipseContactPair(event, inst) } else { - disappearance, reappearance, ok = refineJupiterGalileanContactPair(event.Greatest, event.Satellite, event.Type) + disappearance, reappearance, ok = refineJupiterGalileanContactPair(event.Greatest, event.Satellite, event.Type, inst) } if !ok { return invalidJupiterGalileanPhenomenonContactEvent() @@ -113,24 +117,25 @@ func jupiterGalileanPhenomenonContactEventFromEvent(event JupiterGalileanPhenome func refineJupiterGalileanContactPair( greatestJD float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + inst := instanceValue(instances) + signedDistance := func(jd float64) float64 { - geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType) + geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType, inst) if !ok { return math.NaN() } return geometry.signedDistance } disappearanceStartTarget := func(jd float64) float64 { - geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType) + geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType, inst) if !ok { return math.NaN() } return geometry.signedDistance - geometry.effectiveRadius } insideTarget := func(jd float64) float64 { - geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType) + geometry, ok := jupiterGalileanContactGeometryAt(jd, satellite, phenomenonType, inst) if !ok { return math.NaN() } @@ -179,8 +184,9 @@ func refineJupiterGalileanContactPair( } func refineJupiterGalileanEclipseContactPair( - event JupiterGalileanPhenomenonEvent, -) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + event JupiterGalileanPhenomenonEvent, instances ...*Instance) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + inst := instanceValue(instances) + satelliteRadius := jupiterGalileanSatelliteRadiusJupiterRadii(event.Satellite) if !isFinite(satelliteRadius) || satelliteRadius <= 0 { return JupiterGalileanPhenomenonContact{}, JupiterGalileanPhenomenonContact{}, false @@ -188,28 +194,28 @@ func refineJupiterGalileanEclipseContactPair( // IMCCE 的 EC.D/EC.F 更接近“目视消失/重现”而不是纯几何接触: // D 相用半影入段近似,F 相用本影/半影出段的中点近似。 penumbraOuterTarget := func(jd float64) float64 { - signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, true) + signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, true, inst) if !ok { return math.NaN() } return signedDistance - satelliteRadius } penumbraInnerTarget := func(jd float64) float64 { - signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, true) + signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, true, inst) if !ok { return math.NaN() } return signedDistance + satelliteRadius } umbraInnerTarget := func(jd float64) float64 { - signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, false) + signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, false, inst) if !ok { return math.NaN() } return signedDistance + satelliteRadius } umbraOuterTarget := func(jd float64) float64 { - signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, false) + signedDistance, ok := jupiterGalileanEclipseSignedDistanceAt(jd, event.Satellite, false, inst) if !ok { return math.NaN() } @@ -262,17 +268,18 @@ func refineJupiterGalileanEclipseContactPair( } func refineJupiterGalileanShadowContactPair( - event JupiterGalileanPhenomenonEvent, -) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + event JupiterGalileanPhenomenonEvent, instances ...*Instance) (JupiterGalileanPhenomenonContact, JupiterGalileanPhenomenonContact, bool) { + inst := instanceValue(instances) + penumbraMetric := func(jd float64) float64 { - value, ok := jupiterGalileanShadowLimbMetricAt(jd, event.Satellite, true) + value, ok := jupiterGalileanShadowLimbMetricAt(jd, event.Satellite, true, inst) if !ok { return math.NaN() } return value } umbraMetric := func(jd float64) float64 { - value, ok := jupiterGalileanShadowLimbMetricAt(jd, event.Satellite, false) + value, ok := jupiterGalileanShadowLimbMetricAt(jd, event.Satellite, false, inst) if !ok { return math.NaN() } @@ -459,13 +466,14 @@ func bisectJupiterGalileanContactRoot( func jupiterGalileanContactGeometryAt( jd float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) (jupiterGalileanContactGeometry, bool) { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) (jupiterGalileanContactGeometry, bool) { + inst := instanceValue(instances) + if !isFinite(jd) || satellite < 1 || satellite > 4 || !isValidJupiterGalileanPhenomenonType(phenomenonType) { return jupiterGalileanContactGeometry{}, false } evaluationJDE := UTC2TT(jd) - context := newJupiterGalileanObservationContext(evaluationJDE) + context := newJupiterGalileanObservationContext(evaluationJDE, inst) if context.jupiterDistance == 0 { return jupiterGalileanContactGeometry{}, false } @@ -523,12 +531,14 @@ func jupiterGalileanContactGeometryAt( } } -func jupiterGalileanEclipseSignedDistanceAt(jd float64, satellite int, penumbra bool) (float64, bool) { +func jupiterGalileanEclipseSignedDistanceAt(jd float64, satellite int, penumbra bool, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + if !isFinite(jd) || satellite < 1 || satellite > 4 { return math.NaN(), false } evaluationJDE := UTC2TT(jd) - context := newJupiterGalileanObservationContext(evaluationJDE) + context := newJupiterGalileanObservationContext(evaluationJDE, inst) if context.jupiterDistance == 0 { return math.NaN(), false } @@ -586,12 +596,14 @@ func jupiterGalileanPenumbraRadiusJupiterRadii(satellite int, pathLengthAU, sunD return penumbraRadiusAU / (jupiterGalileanEquatorialRadiusKM / astronomicalUnitKM) } -func jupiterGalileanShadowLimbMetricAt(jd float64, satellite int, penumbra bool) (float64, bool) { +func jupiterGalileanShadowLimbMetricAt(jd float64, satellite int, penumbra bool, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + if !isFinite(jd) || satellite < 1 || satellite > 4 { return math.NaN(), false } evaluationJDE := UTC2TT(jd) - context := newJupiterGalileanObservationContext(evaluationJDE) + context := newJupiterGalileanObservationContext(evaluationJDE, inst) if context.jupiterDistance == 0 { return math.NaN(), false } diff --git a/basic/jupiter_satellite_events.go b/basic/jupiter_satellite_events.go index d827367..d737623 100644 --- a/basic/jupiter_satellite_events.go +++ b/basic/jupiter_satellite_events.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) const ( jupiterGalileanEventSearchSpanDays = 8 * 365.25 @@ -56,28 +58,22 @@ type jupiterGalileanShadowPoint struct { yJupiterRadii float64 } -// LastJupiterGalileanPhenomenonEvent 上一次伽利略卫星现象 / previous Galilean-satellite event. -// -// jd 与返回时刻都是民用儒略日;需要瞬时状态时先用 UTC2TT(jd) 换成 TT。 -func LastJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { - event, _ := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, -1, true) +// LastJupiterGalileanPhenomenonEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastJupiterGalileanPhenomenonEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonEvent { + event, _ := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, -1, true, inst) return event } -// NextJupiterGalileanPhenomenonEvent 下一次伽利略卫星现象 / next Galilean-satellite event. -// -// jd 与返回时刻都是民用儒略日;需要瞬时状态时先用 UTC2TT(jd) 换成 TT。 -func NextJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { - event, _ := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, 1, false) +// NextJupiterGalileanPhenomenonEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextJupiterGalileanPhenomenonEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonEvent { + event, _ := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, 1, false, inst) return event } -// ClosestJupiterGalileanPhenomenonEvent 最近一次伽利略卫星现象 / closest Galilean-satellite event. -// -// jd 与返回时刻都是民用儒略日;需要瞬时状态时先用 UTC2TT(jd) 换成 TT。 -func ClosestJupiterGalileanPhenomenonEvent(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { - last, hasLast := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, -1, true) - next, hasNext := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, 1, false) +// ClosestJupiterGalileanPhenomenonEventWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestJupiterGalileanPhenomenonEventWithInstance(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, inst *Instance) JupiterGalileanPhenomenonEvent { + last, hasLast := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, -1, true, inst) + next, hasNext := searchJupiterGalileanPhenomenonEvent(jd, satellite, phenomenonType, 1, false, inst) switch { case hasLast && !hasNext: return last @@ -97,14 +93,15 @@ func searchJupiterGalileanPhenomenonEvent( satellite int, phenomenonType JupiterGalileanPhenomenonType, direction int, - includeCurrent bool, -) (JupiterGalileanPhenomenonEvent, bool) { + includeCurrent bool, instances ...*Instance) (JupiterGalileanPhenomenonEvent, bool) { + inst := instanceValue(instances) + if !isFinite(jd) || direction == 0 || satellite < 1 || satellite > 4 || !isValidJupiterGalileanPhenomenonType(phenomenonType) { return invalidJupiterGalileanPhenomenonEvent(), false } - if sample := jupiterGalileanPhenomenonMetricAt(jd, satellite, phenomenonType); sample.active { - current := findJupiterGalileanPhenomenonEventAround(jd, satellite, phenomenonType) + if sample := jupiterGalileanPhenomenonMetricAt(jd, satellite, phenomenonType, inst); sample.active { + current := findJupiterGalileanPhenomenonEventAround(jd, satellite, phenomenonType, inst) if current.Valid && includeCurrent { return current, true } @@ -122,16 +119,16 @@ func searchJupiterGalileanPhenomenonEvent( sign := float64(direction) prevTime := jd - prevSample := jupiterGalileanPhenomenonMetricAt(prevTime, satellite, phenomenonType) + prevSample := jupiterGalileanPhenomenonMetricAt(prevTime, satellite, phenomenonType, inst) midTime := jd + sign*stepDays - midSample := jupiterGalileanPhenomenonMetricAt(midTime, satellite, phenomenonType) + midSample := jupiterGalileanPhenomenonMetricAt(midTime, satellite, phenomenonType, inst) for i := 2; i <= maxSteps; i++ { nextTime := jd + sign*float64(i)*stepDays - nextSample := jupiterGalileanPhenomenonMetricAt(nextTime, satellite, phenomenonType) + nextSample := jupiterGalileanPhenomenonMetricAt(nextTime, satellite, phenomenonType, inst) if event, ok := jupiterGalileanIntervalCandidate( prevTime, nextTime, prevSample, midSample, nextSample, - jd, satellite, phenomenonType, direction, includeCurrent, + jd, satellite, phenomenonType, direction, includeCurrent, inst, ); ok { return event, true } @@ -150,8 +147,9 @@ func jupiterGalileanIntervalCandidate( satellite int, phenomenonType JupiterGalileanPhenomenonType, direction int, - includeCurrent bool, -) (JupiterGalileanPhenomenonEvent, bool) { + includeCurrent bool, instances ...*Instance) (JupiterGalileanPhenomenonEvent, bool) { + inst := instanceValue(instances) + var best JupiterGalileanPhenomenonEvent found := false consider := func(event JupiterGalileanPhenomenonEvent) { @@ -169,17 +167,17 @@ func jupiterGalileanIntervalCandidate( midSample.metric <= prevSample.metric && midSample.metric <= nextSample.metric { consider(findJupiterGalileanPhenomenonEventAround( - refineJupiterGalileanMetricMinimum(prevTime, nextTime, satellite, phenomenonType), - satellite, phenomenonType, + refineJupiterGalileanMetricMinimum(prevTime, nextTime, satellite, phenomenonType, inst), + satellite, phenomenonType, inst, )) } if jupiterGalileanSignedCrosses(prevSample.signed, nextSample.signed) && jupiterGalileanNearDisk(prevSample, midSample, nextSample) { - crossing := refineJupiterGalileanSignedCrossing(prevTime, nextTime, satellite, phenomenonType) + crossing := refineJupiterGalileanSignedCrossing(prevTime, nextTime, satellite, phenomenonType, inst) // 过零点只说明"靠近木星",只在它已贴近视面时按事件尺度细扫一次。 - if sample := jupiterGalileanPhenomenonMetricAt(crossing, satellite, phenomenonType); isFinite(sample.metric) && + if sample := jupiterGalileanPhenomenonMetricAt(crossing, satellite, phenomenonType, inst); isFinite(sample.metric) && sample.metric <= jupiterGalileanCrossingProbeMetric { - if event, ok := jupiterGalileanFineScanAround(crossing, satellite, phenomenonType); ok { + if event, ok := jupiterGalileanFineScanAround(crossing, satellite, phenomenonType, inst); ok { consider(event) } } @@ -210,8 +208,9 @@ func jupiterGalileanNearDisk(prevSample, midSample, nextSample jupiterGalileanMe func jupiterGalileanFineScanAround( jd float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) (JupiterGalileanPhenomenonEvent, bool) { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) (JupiterGalileanPhenomenonEvent, bool) { + inst := instanceValue(instances) + // 从过零点向两侧交替外扩,通常几步内就能落进事件窗口。 steps := int(math.Round(jupiterGalileanCrossingProbeDays / jupiterGalileanCrossingProbeStepDays)) for step := 0; step <= steps; step++ { @@ -220,10 +219,10 @@ func jupiterGalileanFineScanAround( continue } candidate := jd + sign*float64(step)*jupiterGalileanCrossingProbeStepDays - if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType).active { + if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType, inst).active { continue } - if event := findJupiterGalileanPhenomenonEventAround(candidate, satellite, phenomenonType); event.Valid { + if event := findJupiterGalileanPhenomenonEventAround(candidate, satellite, phenomenonType, inst); event.Valid { return event, true } } @@ -246,16 +245,17 @@ func jupiterGalileanSignedCrosses(first, second float64) bool { func refineJupiterGalileanSignedCrossing( left, right float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) float64 { - leftValue := jupiterGalileanPhenomenonMetricAt(left, satellite, phenomenonType).signed - rightValue := jupiterGalileanPhenomenonMetricAt(right, satellite, phenomenonType).signed + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) float64 { + inst := instanceValue(instances) + + leftValue := jupiterGalileanPhenomenonMetricAt(left, satellite, phenomenonType, inst).signed + rightValue := jupiterGalileanPhenomenonMetricAt(right, satellite, phenomenonType, inst).signed if !isFinite(leftValue) || !isFinite(rightValue) || (leftValue < 0) == (rightValue < 0) { return (left + right) / 2 } for i := 0; i < 80 && right-left > jupiterGalileanEventEpsilonDays; i++ { middle := (left + right) / 2 - value := jupiterGalileanPhenomenonMetricAt(middle, satellite, phenomenonType).signed + value := jupiterGalileanPhenomenonMetricAt(middle, satellite, phenomenonType, inst).signed if !isFinite(value) { return middle } @@ -268,8 +268,10 @@ func refineJupiterGalileanSignedCrossing( return (left + right) / 2 } -func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType) JupiterGalileanPhenomenonEvent { - sample := jupiterGalileanPhenomenonMetricAt(jd, satellite, phenomenonType) +func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) JupiterGalileanPhenomenonEvent { + inst := instanceValue(instances) + + sample := jupiterGalileanPhenomenonMetricAt(jd, satellite, phenomenonType, inst) if !sample.active { return invalidJupiterGalileanPhenomenonEvent() } @@ -281,7 +283,7 @@ func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenome inactiveStart := math.NaN() for i := 0; i < maxBoundarySteps; i++ { candidate := activeStart - stepDays - if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType).active { + if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType, inst).active { inactiveStart = candidate break } @@ -295,7 +297,7 @@ func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenome inactiveEnd := math.NaN() for i := 0; i < maxBoundarySteps; i++ { candidate := activeEnd + stepDays - if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType).active { + if !jupiterGalileanPhenomenonMetricAt(candidate, satellite, phenomenonType, inst).active { inactiveEnd = candidate break } @@ -305,14 +307,14 @@ func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenome return invalidJupiterGalileanPhenomenonEvent() } - start := refineJupiterGalileanEventStart(inactiveStart, activeStart, satellite, phenomenonType) - end := refineJupiterGalileanEventEnd(activeEnd, inactiveEnd, satellite, phenomenonType) + start := refineJupiterGalileanEventStart(inactiveStart, activeStart, satellite, phenomenonType, inst) + end := refineJupiterGalileanEventEnd(activeEnd, inactiveEnd, satellite, phenomenonType, inst) if !isFinite(start) || !isFinite(end) || end <= start { return invalidJupiterGalileanPhenomenonEvent() } - greatest := refineJupiterGalileanMetricMinimum(start, end, satellite, phenomenonType) - greatestSample := jupiterGalileanPhenomenonMetricAt(greatest, satellite, phenomenonType) + greatest := refineJupiterGalileanMetricMinimum(start, end, satellite, phenomenonType, inst) + greatestSample := jupiterGalileanPhenomenonMetricAt(greatest, satellite, phenomenonType, inst) if !greatestSample.active { return invalidJupiterGalileanPhenomenonEvent() } @@ -331,22 +333,23 @@ func findJupiterGalileanPhenomenonEventAround(jd float64, satellite int, phenome func refineJupiterGalileanEventStart( outsideJD, insideJD float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) float64 { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) float64 { + inst := instanceValue(instances) + if insideJD < outsideJD { outsideJD, insideJD = insideJD, outsideJD } - if jupiterGalileanPhenomenonMetricAt(outsideJD, satellite, phenomenonType).active { + if jupiterGalileanPhenomenonMetricAt(outsideJD, satellite, phenomenonType, inst).active { return math.NaN() } - if !jupiterGalileanPhenomenonMetricAt(insideJD, satellite, phenomenonType).active { + if !jupiterGalileanPhenomenonMetricAt(insideJD, satellite, phenomenonType, inst).active { return math.NaN() } left := outsideJD right := insideJD for i := 0; i < 80 && right-left > jupiterGalileanEventEpsilonDays; i++ { mid := (left + right) / 2 - if jupiterGalileanPhenomenonMetricAt(mid, satellite, phenomenonType).active { + if jupiterGalileanPhenomenonMetricAt(mid, satellite, phenomenonType, inst).active { right = mid } else { left = mid @@ -358,22 +361,23 @@ func refineJupiterGalileanEventStart( func refineJupiterGalileanEventEnd( insideJD, outsideJD float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) float64 { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) float64 { + inst := instanceValue(instances) + if outsideJD < insideJD { insideJD, outsideJD = outsideJD, insideJD } - if !jupiterGalileanPhenomenonMetricAt(insideJD, satellite, phenomenonType).active { + if !jupiterGalileanPhenomenonMetricAt(insideJD, satellite, phenomenonType, inst).active { return math.NaN() } - if jupiterGalileanPhenomenonMetricAt(outsideJD, satellite, phenomenonType).active { + if jupiterGalileanPhenomenonMetricAt(outsideJD, satellite, phenomenonType, inst).active { return math.NaN() } left := insideJD right := outsideJD for i := 0; i < 80 && right-left > jupiterGalileanEventEpsilonDays; i++ { mid := (left + right) / 2 - if jupiterGalileanPhenomenonMetricAt(mid, satellite, phenomenonType).active { + if jupiterGalileanPhenomenonMetricAt(mid, satellite, phenomenonType, inst).active { left = mid } else { right = mid @@ -385,8 +389,9 @@ func refineJupiterGalileanEventEnd( func refineJupiterGalileanMetricMinimum( jd1, jd2 float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) float64 { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) float64 { + inst := instanceValue(instances) + left := math.Min(jd1, jd2) right := math.Max(jd1, jd2) if right-left <= jupiterGalileanEventEpsilonDays { @@ -395,21 +400,21 @@ func refineJupiterGalileanMetricMinimum( const phi = 0.6180339887498948482 x1 := right - phi*(right-left) x2 := left + phi*(right-left) - f1 := jupiterGalileanPhenomenonMetricAt(x1, satellite, phenomenonType).metric - f2 := jupiterGalileanPhenomenonMetricAt(x2, satellite, phenomenonType).metric + f1 := jupiterGalileanPhenomenonMetricAt(x1, satellite, phenomenonType, inst).metric + f2 := jupiterGalileanPhenomenonMetricAt(x2, satellite, phenomenonType, inst).metric for i := 0; i < 80 && right-left > jupiterGalileanEventEpsilonDays; i++ { if f1 <= f2 { right = x2 x2 = x1 f2 = f1 x1 = right - phi*(right-left) - f1 = jupiterGalileanPhenomenonMetricAt(x1, satellite, phenomenonType).metric + f1 = jupiterGalileanPhenomenonMetricAt(x1, satellite, phenomenonType, inst).metric } else { left = x1 x1 = x2 f1 = f2 x2 = left + phi*(right-left) - f2 = jupiterGalileanPhenomenonMetricAt(x2, satellite, phenomenonType).metric + f2 = jupiterGalileanPhenomenonMetricAt(x2, satellite, phenomenonType, inst).metric } } return (left + right) / 2 @@ -418,8 +423,9 @@ func refineJupiterGalileanMetricMinimum( func jupiterGalileanPhenomenonMetricAt( jd float64, satellite int, - phenomenonType JupiterGalileanPhenomenonType, -) jupiterGalileanMetricSample { + phenomenonType JupiterGalileanPhenomenonType, instances ...*Instance) jupiterGalileanMetricSample { + inst := instanceValue(instances) + if !isFinite(jd) || satellite < 1 || satellite > 4 || !isValidJupiterGalileanPhenomenonType(phenomenonType) { return jupiterGalileanMetricSample{ metric: math.Inf(1), @@ -429,7 +435,7 @@ func jupiterGalileanPhenomenonMetricAt( } evaluationJDE := UTC2TT(jd) - context := newJupiterGalileanObservationContext(evaluationJDE) + context := newJupiterGalileanObservationContext(evaluationJDE, inst) if context.jupiterDistance == 0 { return jupiterGalileanMetricSample{ metric: math.Inf(1), diff --git a/basic/jupiter_satellite_phenomena.go b/basic/jupiter_satellite_phenomena.go index 7966930..bf1d4a1 100644 --- a/basic/jupiter_satellite_phenomena.go +++ b/basic/jupiter_satellite_phenomena.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) const solarRadiusAU = 695700.0 / astronomicalUnitKM @@ -30,10 +32,10 @@ func JupiterGalileanSatellitePhenomenon(jde float64, satellite int) JupiterGalil return context.phenomenonForSatellite(satellite - 1) } -// JupiterGalileanSatellitePhenomena 四颗伽利略卫星瞬时现象 / instantaneous phenomena of the four Galilean satellites. -func JupiterGalileanSatellitePhenomena(jde float64) [4]JupiterGalileanPhenomenon { +// JupiterGalileanSatellitePhenomenaWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterGalileanSatellitePhenomenaWithInstance(jde float64, inst *Instance) [4]JupiterGalileanPhenomenon { var phenomena [4]JupiterGalileanPhenomenon - context := newJupiterGalileanObservationContext(jde) + context := newJupiterGalileanObservationContext(jde, inst) for i := range phenomena { phenomena[i] = context.phenomenonForSatellite(i) } diff --git a/basic/jupiter_satellites.go b/basic/jupiter_satellites.go index a3e76cb..46cdc49 100644 --- a/basic/jupiter_satellites.go +++ b/basic/jupiter_satellites.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -105,13 +104,10 @@ func JupiterGalileanSatelliteObservation(jde float64, satellite int) JupiterGali return context.observationForSatellite(satellite - 1) } -// JupiterGalileanSatelliteObservations 四颗伽利略卫星视位置 / apparent geometry of the four Galilean satellites. -// -// 返回次序固定为 Io、Europa、Ganymede、Callisto。 -// The returned order is Io, Europa, Ganymede, Callisto. -func JupiterGalileanSatelliteObservations(jde float64) [4]JupiterGalileanObservation { +// JupiterGalileanSatelliteObservationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterGalileanSatelliteObservationsWithInstance(jde float64, inst *Instance) [4]JupiterGalileanObservation { var observations [4]JupiterGalileanObservation - context := newJupiterGalileanObservationContext(jde) + context := newJupiterGalileanObservationContext(jde, inst) for i := range observations { observations[i] = context.observationForSatellite(i) } @@ -119,6 +115,8 @@ func JupiterGalileanSatelliteObservations(jde float64) [4]JupiterGalileanObserva } type jupiterGalileanObservationContext struct { + inst *Instance + jd float64 targetJD float64 earthHelioJ2000 Vector3 @@ -141,13 +139,15 @@ type jupiterGalileanObservationContext struct { bodyZ Vector3 } -func newJupiterGalileanObservationContext(jde float64) jupiterGalileanObservationContext { - context := jupiterGalileanObservationContext{jd: jde} +func newJupiterGalileanObservationContext(jde float64, instances ...*Instance) jupiterGalileanObservationContext { + inst := instanceValue(instances) + + context := jupiterGalileanObservationContext{inst: inst, jd: jde} if !isFinite(jde) { return context } - context.earthHelioJ2000 = rotateEclipticToEquatorial(earthHeliocentricVectorJ2000(jde), orbitJ2000Obliquity) - context.jupiterGeoJ2000, context.jupiterLightTime = jupiterAstrometricGeocentricVectorJ2000(jde, context.earthHelioJ2000) + context.earthHelioJ2000 = rotateEclipticToEquatorial(earthHeliocentricVectorJ2000(jde, inst), orbitJ2000Obliquity) + context.jupiterGeoJ2000, context.jupiterLightTime = jupiterAstrometricGeocentricVectorJ2000(jde, context.earthHelioJ2000, inst) context.targetJD = jde - context.jupiterLightTime context.jupiterDistance = vectorMagnitude(context.jupiterGeoJ2000) if context.jupiterDistance == 0 { @@ -158,7 +158,7 @@ func newJupiterGalileanObservationContext(jde float64) jupiterGalileanObservatio ra, dec := vectorToRaDec(context.lineOfSight) context.east = Vector3{-Sin(ra), Cos(ra), 0} context.north = Vector3{-Cos(ra) * Sin(dec), -Sin(ra) * Sin(dec), Cos(dec)} - jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(context.targetJD), orbitJ2000Obliquity) + jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(context.targetJD, inst), orbitJ2000Obliquity) context.sunDistanceAU = vectorMagnitude(jupiterHelio) context.sunDirection = normalizeVector(Vector3{-jupiterHelio[0], -jupiterHelio[1], -jupiterHelio[2]}) context.sunLineOfSight = Vector3{-context.sunDirection[0], -context.sunDirection[1], -context.sunDirection[2]} @@ -178,7 +178,7 @@ func (context jupiterGalileanObservationContext) observationForSatellite(index i if index < 0 || index >= 4 || context.jupiterDistance == 0 { return invalidJupiterGalileanObservation() } - state, geocentric := jupiterGalileanSatelliteAstrometricGeocentric(index, context.jd, context.jupiterLightTime, context.earthHelioJ2000) + state, geocentric := jupiterGalileanSatelliteAstrometricGeocentric(index, context.jd, context.jupiterLightTime, context.earthHelioJ2000, context.inst) direction := normalizeVector(geocentric) ra, dec := vectorToRaDec(direction) distance := vectorMagnitude(geocentric) @@ -214,14 +214,16 @@ func tangentPlaneOffsetAngles(target, center, east, north Vector3) (float64, flo return math.Atan2(vectorDot(target, east), denominator), math.Atan2(vectorDot(target, north), denominator) } -func jupiterGalileanSatelliteAstrometricGeocentric(index int, jd, initialLightTime float64, earthHelioJ2000 Vector3) (JupiterGalileanState, Vector3) { +func jupiterGalileanSatelliteAstrometricGeocentric(index int, jd, initialLightTime float64, earthHelioJ2000 Vector3, instances ...*Instance) (JupiterGalileanState, Vector3) { + inst := instanceValue(instances) + lightTime := initialLightTime state := JupiterGalileanState{} result := Vector3{} includeSolarLongPeriod := jupiterGalileanUseSolarLongPeriod(jd) for i := 0; i < 8; i++ { targetJDE := jd - lightTime - jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(targetJDE), orbitJ2000Obliquity) + jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(targetJDE, inst), orbitJ2000Obliquity) state = jupiterGalileanSatelliteStateAtET(targetJDE-jupiterGalileanReferenceJD, index, includeSolarLongPeriod) result = Vector3{ jupiterHelio[0] + state.X - earthHelioJ2000[0], @@ -237,11 +239,13 @@ func jupiterGalileanSatelliteAstrometricGeocentric(index int, jd, initialLightTi return state, result } -func jupiterAstrometricGeocentricVectorJ2000(jd float64, earthHelioJ2000 Vector3) (Vector3, float64) { +func jupiterAstrometricGeocentricVectorJ2000(jd float64, earthHelioJ2000 Vector3, instances ...*Instance) (Vector3, float64) { + inst := instanceValue(instances) + lightTime := 0.0 result := Vector3{} for i := 0; i < 8; i++ { - jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(jd-lightTime), orbitJ2000Obliquity) + jupiterHelio := rotateEclipticToEquatorial(jupiterHeliocentricVectorJ2000(jd-lightTime, inst), orbitJ2000Obliquity) result = Vector3{ jupiterHelio[0] - earthHelioJ2000[0], jupiterHelio[1] - earthHelioJ2000[1], @@ -256,12 +260,14 @@ func jupiterAstrometricGeocentricVectorJ2000(jd float64, earthHelioJ2000 Vector3 return result, lightTime } -func jupiterHeliocentricVectorJ2000(jde float64) Vector3 { +func jupiterHeliocentricVectorJ2000(jde float64, instances ...*Instance) Vector3 { + inst := instanceValue(instances) + return eclipticVectorAtReferenceEpoch( eclipticCartesian( - planet.WherePlanet(4, 0, jde), - planet.WherePlanet(4, 1, jde), - planet.WherePlanet(4, 2, jde), + PlanetCoordinateWithInstance(4, 0, jde, inst), + PlanetCoordinateWithInstance(4, 1, jde, inst), + PlanetCoordinateWithInstance(4, 2, jde, inst), ), jde, orbitReferenceJD, diff --git a/basic/limb_limits.go b/basic/limb_limits.go new file mode 100644 index 0000000..8b98821 --- /dev/null +++ b/basic/limb_limits.go @@ -0,0 +1,198 @@ +package basic + +import ( + "math" + "time" +) + +const ( + // 月掩南北界:星迹相对月心是直线,偏离切点 δ 后月缘需高出 R(1/cosδ − 1),8° 时约 17 km,远超月面起伏。 + limbGrazeSpanDeg = 8.0 + limbGrazeStepDeg = 0.1 + // 界线点月缘平移上限;超出视为不收敛并保留圆月面点。 + limbLimitMaxShiftKM = 30.0 + limbLimitProbeKM = 1.0 + limbLimitIterations = 4 + // 太阳 / 月球高度低于此值的界线点不做月缘平移。 + limbLimitHorizonDeg = 0.5 +) + +// limbLimitGap 返回站点处的圆月面接触间隙与月缘要求的间隙(同一单位),二者相等即为月缘界线。 +type limbLimitGap func(longitude, latitude float64) (gap, target float64, ok bool) + +// limbLimitSolve 从圆月面界线点出发,沿间隙梯度做牛顿迭代,使 gap − gap0 = target;失败时返回原点。 +// gap0 是原点处的圆月面间隙:界线点与站心间隙零点之间的模型差由它吸收,只平移月缘带来的部分。 +func limbLimitSolve(longitude, latitude float64, evaluate limbLimitGap) (float64, float64, bool) { + kmPerDegLat := math.Pi / 180 * topocentricEarthRadiusKM + gap0, _, ok := evaluate(longitude, latitude) + if !ok { + return longitude, latitude, false + } + lon, lat := longitude, latitude + for iteration := 0; iteration < limbLimitIterations; iteration++ { + gap, target, ok := evaluate(lon, lat) + if !ok { + return longitude, latitude, false + } + residual := gap - gap0 - target + kmPerDegLon := kmPerDegLat * math.Cos(lat*rad) + if kmPerDegLon < 1 { + return longitude, latitude, false + } + dLon := limbLimitProbeKM / kmPerDegLon + dLat := limbLimitProbeKM / kmPerDegLat + east, _, okEast := evaluate(lon+dLon, lat) + north, _, okNorth := evaluate(lon, lat+dLat) + if !okEast || !okNorth { + return longitude, latitude, false + } + gx := (east - gap) / limbLimitProbeKM + gy := (north - gap) / limbLimitProbeKM + norm2 := gx*gx + gy*gy + if !finite(norm2) || norm2 <= 0 { + return longitude, latitude, false + } + stepX, stepY := -residual*gx/norm2, -residual*gy/norm2 + lon += stepX / kmPerDegLon + lat += stepY / kmPerDegLat + if math.Abs(stepX) < 1e-4 && math.Abs(stepY) < 1e-4 { + break + } + } + shiftKM := math.Hypot((lon-longitude)*kmPerDegLat*math.Cos(latitude*rad), (lat-latitude)*kmPerDegLat) + if !finite(shiftKM) || shiftKM > limbLimitMaxShiftKM || lat > 90 || lat < -90 { + return longitude, latitude, false + } + if lon > 180 { + lon -= 360 + } else if lon < -180 { + lon += 360 + } + return lon, lat, true +} + +func occultationVectorArray(vector occultationPathVector) [3]float64 { + return [3]float64{vector.x, vector.y, vector.z} +} + +// occultationLimbCorrectLimitSeries 把月掩外接触南北界平移到月缘掠掩线: +// 星迹在切点附近按直线处理,有效月缘半径取 max R(θ0+δ)·cosδ。只用于外接触(掠掩)界线。 +func occultationLimbCorrectLimitSeries( + points []OccultationPathPoint, + contextAt occultationRiseSetContextFunc, + limb LunarLimb, + location *time.Location, + inst *Instance, +) ([]OccultationPathPoint, bool) { + if limb == nil || len(points) == 0 || contextAt == nil { + return points, false + } + applied := false + corrected := append([]OccultationPathPoint(nil), points...) + for index, point := range points { + if math.Abs(point.MoonAltitude) < limbLimitHorizonDeg { + continue + } + tt := centerTimeTT(point.Time) + context := contextAt(tt) + if !context.valid || context.moon.distanceKM <= 0 { + continue + } + frame := newLunarLimbFrame(limb, tt, occultationVectorArray(context.moon.positionKM), inst) + if !frame.valid { + continue + } + evaluate := func(longitude, latitude float64) (float64, float64, bool) { + state := context.stateAt(longitude, latitude) + if !state.valid || !finite(state.contactMetric) { + return 0, 0, false + } + parallaxKM, distanceKM, _ := occultationRiseSetObserverVectors(context.siderealDegrees, longitude, latitude) + moonKM := occultationPathSub(context.moon.positionKM, parallaxKM) + var targetKM occultationPathVector + if context.target.distanceKM > 0 { + targetKM = occultationPathSub(context.target.positionKM, parallaxKM) + } else { + targetKM = context.target.direction + } + view, ok := frame.viewToward(occultationVectorArray(moonKM), occultationVectorArray(targetKM)) + if !ok { + return 0, 0, false + } + effective := 0.0 + for offset := -limbGrazeSpanDeg; offset <= limbGrazeSpanDeg+1e-9; offset += limbGrazeStepDeg { + radius, ok := frame.radiusAt(view, offset) + if !ok { + return 0, 0, false + } + if value := radius * math.Cos(offset*rad); value > effective { + effective = value + } + } + moonDistanceKM := occultationRiseSetTopocentricDistance(context.moon, distanceKM) + if !finite(moonDistanceKM) || moonDistanceKM <= effective { + return 0, 0, false + } + // 有效半径更大时掠掩线外移:需要的圆月面间隙为两者角半径之差(度)。 + target := (angularSemidiameterArcsec(effective, moonDistanceKM) - + angularSemidiameterArcsec(moonEquatorialRadiusKM, moonDistanceKM)) / 3600 + return state.contactMetric, target, true + } + longitude, latitude, ok := limbLimitSolve(point.Longitude, point.Latitude, evaluate) + if !ok { + continue + } + if !limbGroundMoved(point.Longitude, point.Latitude, longitude, latitude) { + continue + } + corrected[index].Longitude, corrected[index].Latitude = longitude, latitude + applied = true + state := context.stateAt(longitude, latitude) + if state.valid { + corrected[index].MoonAltitude = state.moonAltitude + } + } + return corrected, applied +} + +// limbCorrectCentralLimits 把全食 / 环食带南北界平移到月缘界线。 +func (solver solarEclipseSolver) limbCorrectCentralLimits(points []SolarEclipsePathPoint, limb LunarLimb) ([]SolarEclipsePathPoint, bool) { + if limb == nil || len(points) == 0 { + return points, false + } + limbSolver := solver.withLimbParams(limb) + applied := false + corrected := append([]SolarEclipsePathPoint(nil), points...) + for index, point := range points { + // 地平线上的 U1/U4 端点处地面梯度趋于零,平移不可信,保留圆月面点。 + if math.Abs(point.SunAltitude) < limbLimitHorizonDeg || !finite(point.JDE) || point.JDE <= 0 { + continue + } + context := solver.localStateContextAt(point.JDE) + limbContext := limbSolver.localStateContextAt(point.JDE) + if limbContext.limbFrame == nil { + continue + } + // 与站心同一判据:月缘内切间隙取自站心状态的等效内半径,目标是让它在界线上为零。 + evaluate := func(longitude, latitude float64) (float64, float64, bool) { + lonRad, latRad := longitude*rad, latitude*rad + state := context.stateAt(lonRad, latRad, 0) + limbState := limbContext.stateAt(lonRad, latRad, 0) + if !limbState.limbApplied { + return 0, 0, false + } + gap := state.movingDiskContactState().internalContactGap() + return gap, gap - limbState.movingDiskContactState().internalContactGap(), true + } + longitude, latitude, ok := limbLimitSolve(point.Longitude, point.Latitude, evaluate) + if !ok { + continue + } + corrected[index].Longitude, corrected[index].Latitude = longitude, latitude + // 牛顿迭代可能收敛回原点(常数月缘即如此),只有位移超过噪声容差才算真正修正。 + if limbGroundMoved(point.Longitude, point.Latitude, corrected[index].Longitude, corrected[index].Latitude) { + applied = true + } + } + return corrected, applied +} diff --git a/basic/limb_tolerance.go b/basic/limb_tolerance.go new file mode 100644 index 0000000..9b9cfdb --- /dev/null +++ b/basic/limb_tolerance.go @@ -0,0 +1,35 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/geodata" +) + +// 月缘修正是否生效的统一判据:每种量纲只有一个容差,所有层都经下面的函数判断,避免口径漂移。 +const ( + // limbAppliedTimeToleranceDays 接触时刻与时长的容差(0.05 秒)。 + limbAppliedTimeToleranceDays = 0.05 / 86400.0 + // limbAppliedDistanceToleranceKM 地面界线点的平移容差(0.05 km)。 + limbAppliedDistanceToleranceKM = 0.05 + // limbAppliedMagnitudeTolerance 食分与遮蔽率的容差。 + limbAppliedMagnitudeTolerance = 1e-9 +) + +// limbTimeChanged 报告两个儒略日(或以日为单位的时长)是否相差超过时刻容差;任一非有限时视为未变。 +func limbTimeChanged(corrected, original float64) bool { + return finite(corrected) && finite(original) && + math.Abs(corrected-original) > limbAppliedTimeToleranceDays +} + +// limbMagnitudeChanged 报告食分或遮蔽率是否相差超过容差;任一非有限时视为未变。 +func limbMagnitudeChanged(corrected, original float64) bool { + return finite(corrected) && finite(original) && + math.Abs(corrected-original) > limbAppliedMagnitudeTolerance +} + +// limbGroundMoved 报告地面点(度)是否平移超过距离容差。 +func limbGroundMoved(originalLon, originalLat, correctedLon, correctedLat float64) bool { + longitude := signedAngleDifference(correctedLon, originalLon) * math.Cos(originalLat*rad) + return math.Hypot(longitude, correctedLat-originalLat)*geodata.EarthKMPerDegree > limbAppliedDistanceToleranceKM +} diff --git a/basic/local_ephemeris.go b/basic/local_ephemeris.go index cf10b58..f02093c 100644 --- a/basic/local_ephemeris.go +++ b/basic/local_ephemeris.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) // localEphemerisVectorNode is a pair of Cartesian ephemeris samples at one // TT. Cartesian interpolation avoids right-ascension wraparound at 0/360. @@ -82,12 +84,14 @@ type solarEclipseLocalEphemeris struct { nodes []localEphemerisVectorNode } -func newSolarEclipseLocalEphemeris(center float64) *solarEclipseLocalEphemeris { +func newSolarEclipseLocalEphemeris(center float64, instances ...*Instance) *solarEclipseLocalEphemeris { + inst := instanceValue(instances) + half := solarLocalEphemerisNodeCount / 2 nodes := make([]localEphemerisVectorNode, solarLocalEphemerisNodeCount) for index := range nodes { tt := center + float64(index-half)*solarLocalEphemerisStepDays - sun, moon := solarEclipseSunMoonEquatorial(tt) + sun, moon := solarEclipseSunMoonEquatorial(tt, inst) nodes[index] = localEphemerisVectorNode{ tt: tt, first: solarEclipseLLRToXYZ(sun[0], sun[1], sun[2]), @@ -97,6 +101,50 @@ func newSolarEclipseLocalEphemeris(center float64) *solarEclipseLocalEphemeris { return &solarEclipseLocalEphemeris{nodes: nodes} } +// solarEclipseLimbAxes 与日月局部星历同一时间网格的月球本体坐标架插值节点。 +type solarEclipseLimbAxes struct { + base lunarLimbFrame + nodes []localEphemerisVectorNode +} + +func newSolarEclipseLimbAxes(ephemeris *solarEclipseLocalEphemeris, limb LunarLimb, inst *Instance) *solarEclipseLimbAxes { + if ephemeris == nil || limb == nil { + return nil + } + axes := &solarEclipseLimbAxes{nodes: make([]localEphemerisVectorNode, len(ephemeris.nodes))} + for index, node := range ephemeris.nodes { + frame := newLunarLimbFrame(limb, node.tt, node.next, inst) + if !frame.valid { + return nil + } + if index == len(ephemeris.nodes)/2 { + axes.base = frame + } + axes.nodes[index] = localEphemerisVectorNode{tt: node.tt, first: frame.axisX, next: frame.axisZ} + } + return axes +} + +func (axes *solarEclipseLimbAxes) frameAt(tt float64) (lunarLimbFrame, bool) { + if axes == nil { + return lunarLimbFrame{}, false + } + axisX, axisZ, ok := interpolateLocalEphemerisVectors(axes.nodes, tt) + if !ok { + return lunarLimbFrame{}, false + } + frame := axes.base + if frame.axisZ, ok = lunarLimbNormalize(axisZ); !ok { + return lunarLimbFrame{}, false + } + // 两轴分别插值后不再严格正交,按 z 轴重新正交化 x 轴。 + if frame.axisX, ok = lunarLimbNormalize(lunarLimbReject(axisX, frame.axisZ)); !ok { + return lunarLimbFrame{}, false + } + frame.axisY = lunarLimbCross(frame.axisZ, frame.axisX) + return frame, true +} + func (ephemeris *solarEclipseLocalEphemeris) equatorialAt(tt float64) ([3]float64, [3]float64, bool) { var empty [3]float64 if ephemeris == nil { @@ -119,12 +167,14 @@ type starOccultationLocalEphemeris struct { hasDistance bool } -func newStarOccultationLocalEphemeris(center float64, star StarCoordinate) *starOccultationLocalEphemeris { +func newStarOccultationLocalEphemeris(center float64, star StarCoordinate, instances ...*Instance) *starOccultationLocalEphemeris { + inst := instanceValue(instances) + half := occultationLocalEphemerisNodeCount / 2 nodes := make([]localEphemerisVectorNode, occultationLocalEphemerisNodeCount) for index := range nodes { tt := center + float64(index-half)*occultationLocalEphemerisStepDays - state := starOccultationEphemerisStateAt(tt, star) + state := starOccultationEphemerisStateAt(tt, star, inst) moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM) targetDistance := state.starDistanceKM if targetDistance <= 0 { @@ -138,12 +188,14 @@ func newStarOccultationLocalEphemeris(center float64, star StarCoordinate) *star next: [3]float64{target.x, target.y, target.z}, } } - return newStarOccultationLocalEphemerisFromNodes(center, star, nodes, false) + return newStarOccultationLocalEphemerisFromNodes(center, star, nodes, false, inst) } // newStarOccultationLocalEphemerisFromNodes 装配局部星历:距离必须与节点同源,密集分支同样要带上。 -func newStarOccultationLocalEphemerisFromNodes(center float64, star StarCoordinate, nodes []localEphemerisVectorNode, dense bool) *starOccultationLocalEphemeris { - _, _, distanceAU := starApparentRaDecDistanceGeocentric(center, star) +func newStarOccultationLocalEphemerisFromNodes(center float64, star StarCoordinate, nodes []localEphemerisVectorNode, dense bool, instances ...*Instance) *starOccultationLocalEphemeris { + inst := instanceValue(instances) + + _, _, distanceAU := starApparentRaDecDistanceGeocentric(center, star, inst) return &starOccultationLocalEphemeris{ star: star, nodes: nodes, dense: dense, distanceKM: distanceAU * occultationPathAstronomicalUnitKM, hasDistance: distanceAU > 0, @@ -195,12 +247,14 @@ type planetOccultationLocalEphemeris struct { dense bool } -func newPlanetOccultationLocalEphemeris(center float64, config planetOccultationConfig) *planetOccultationLocalEphemeris { +func newPlanetOccultationLocalEphemeris(center float64, config planetOccultationConfig, instances ...*Instance) *planetOccultationLocalEphemeris { + inst := instanceValue(instances) + half := occultationLocalEphemerisNodeCount / 2 nodes := make([]localEphemerisVectorNode, occultationLocalEphemerisNodeCount) for index := range nodes { tt := center + float64(index-half)*occultationLocalEphemerisStepDays - state := planetOccultationEphemerisStateAt(tt, config) + state := planetOccultationEphemerisStateAt(tt, config, inst) moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM) target := occultationPathRaDecVector(state.planetRA, state.planetDec, state.planetDistanceKM) nodes[index] = localEphemerisVectorNode{ diff --git a/basic/lunar_eclipse.go b/basic/lunar_eclipse.go index 7976a8a..7adea57 100644 --- a/basic/lunar_eclipse.go +++ b/basic/lunar_eclipse.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) // LunarEclipseType 表示月食类型。 type LunarEclipseType string @@ -22,6 +26,8 @@ const ( // 输入 seedJDE 只需要落在目标望月附近,允许相差数天。 type LunarEclipseResult struct { Type LunarEclipseType + // LunarLimbApplied 表示接触时刻已按月缘剖面修正 / reports whether lunar-limb refinement changed the contacts. + LunarLimbApplied bool // ShadowModel 是本次使用的影半径模型,决定影半径与食分的具体数值。 // ShadowModel is the shadow-radius model used, which fixes the shadow radii and magnitudes. ShadowModel LunarEclipseShadowModel @@ -86,14 +92,11 @@ const ( ) const ( - lunarEarthEquatorialRadiusKM = 6378.1366 - lunarAstronomicalUnitKM = 1.49597870691e8 - // 沿用月食常量: // - 0.2725076 用于月亮视半径和半影几何 // - 太阳视半径与日食共用标准档常量,太阳视差用常用角秒常量 8.794 lunarMoonRadiusRatio = 0.2725076 - lunarMoonRadiusScale = lunarMoonRadiusRatio * lunarEarthEquatorialRadiusKM * 1.0000036 + lunarMoonRadiusScale = lunarMoonRadiusRatio * geodata.EarthEquatorialRadiusKM * 1.0000036 lunarSolarParallaxArcsec = 8.794 lunarLongitudeAberration = -3.4e-6 lunarFiniteDifferenceStep = 60.0 / 86400.0 @@ -132,7 +135,15 @@ func LunarEclipseChauvenet(seedJDE float64) LunarEclipseResult { func lunarEclipse(seedJDE float64, shadowModel lunarEclipseShadowModel) LunarEclipseResult { fullMoonJDE := CalcMoonSHByJDE(seedJDE, 1) - maximumJDE, state, dxdt, dydt, minimumDistance := refineLunarEclipseMaximum(fullMoonJDE, shadowModel) + return lunarEclipseFromFullMoon(fullMoonJDE, shadowModel, func(jde float64) lunarShadowState { + return computeLunarShadowState(jde, shadowModel) + }) +} + +type lunarShadowEvaluator func(float64) lunarShadowState + +func lunarEclipseFromFullMoon(fullMoonJDE float64, shadowModel lunarEclipseShadowModel, evaluate lunarShadowEvaluator) LunarEclipseResult { + maximumJDE, state, dxdt, dydt, minimumDistance := refineLunarEclipseMaximumWithEvaluator(fullMoonJDE, evaluate) // 未发生的阶段保持 NaN:Has* 是权威判据,时刻字段不能拿 0 当哨兵。 model := LunarEclipseShadowModelDanjon @@ -162,11 +173,11 @@ func lunarEclipse(seedJDE float64, shadowModel lunarEclipseShadowModel) LunarEcl result.Type = LunarEclipsePenumbral result.HasPenumbral = true result.Magnitude = rawUmbralMagnitude - result.PenumbralStart = refineLunarEclipseContact( - state, dxdt, dydt, state.moonRadiusRad+state.penumbraRadiusRad, false, shadowModel, + result.PenumbralStart = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.moonRadiusRad+state.penumbraRadiusRad, false, evaluate, ) - result.PenumbralEnd = refineLunarEclipseContact( - state, dxdt, dydt, state.moonRadiusRad+state.penumbraRadiusRad, true, shadowModel, + result.PenumbralEnd = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.moonRadiusRad+state.penumbraRadiusRad, true, evaluate, ) } @@ -174,22 +185,22 @@ func lunarEclipse(seedJDE float64, shadowModel lunarEclipseShadowModel) LunarEcl result.Type = LunarEclipsePartial result.HasPartial = true result.Magnitude = rawUmbralMagnitude - result.PartialStart = refineLunarEclipseContact( - state, dxdt, dydt, state.moonRadiusRad+state.umbraRadiusRad, false, shadowModel, + result.PartialStart = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.moonRadiusRad+state.umbraRadiusRad, false, evaluate, ) - result.PartialEnd = refineLunarEclipseContact( - state, dxdt, dydt, state.moonRadiusRad+state.umbraRadiusRad, true, shadowModel, + result.PartialEnd = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.moonRadiusRad+state.umbraRadiusRad, true, evaluate, ) } if minimumDistance <= state.umbraRadiusRad-state.moonRadiusRad { result.Type = LunarEclipseTotal result.HasTotal = true - result.TotalStart = refineLunarEclipseContact( - state, dxdt, dydt, state.umbraRadiusRad-state.moonRadiusRad, false, shadowModel, + result.TotalStart = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.umbraRadiusRad-state.moonRadiusRad, false, evaluate, ) - result.TotalEnd = refineLunarEclipseContact( - state, dxdt, dydt, state.umbraRadiusRad-state.moonRadiusRad, true, shadowModel, + result.TotalEnd = refineLunarEclipseContactWithEvaluator( + state, dxdt, dydt, state.umbraRadiusRad-state.moonRadiusRad, true, evaluate, ) } @@ -203,17 +214,23 @@ func refineLunarEclipseMaximum( seedJDE float64, shadowModel lunarEclipseShadowModel, ) (float64, lunarShadowState, float64, float64, float64) { + return refineLunarEclipseMaximumWithEvaluator(seedJDE, func(jde float64) lunarShadowState { + return computeLunarShadowState(jde, shadowModel) + }) +} + +func refineLunarEclipseMaximumWithEvaluator(seedJDE float64, evaluate lunarShadowEvaluator) (float64, lunarShadowState, float64, float64, float64) { currentJDE := seedJDE for i := 0; i < 2; i++ { - state := computeLunarShadowState(currentJDE, shadowModel) - nextState := computeLunarShadowState(currentJDE+lunarFiniteDifferenceStep, shadowModel) + state := evaluate(currentJDE) + nextState := evaluate(currentJDE + lunarFiniteDifferenceStep) dxdt := (nextState.x - state.x) / lunarFiniteDifferenceStep dydt := (nextState.y - state.y) / lunarFiniteDifferenceStep denominator := dxdt*dxdt + dydt*dydt if denominator == 0 { - finalState := computeLunarShadowState(currentJDE, shadowModel) + finalState := evaluate(currentJDE) return currentJDE, finalState, 0, 0, math.Hypot(finalState.x, finalState.y) } @@ -221,8 +238,8 @@ func refineLunarEclipseMaximum( currentJDE += correction } - linearState := computeLunarShadowState(currentJDE, shadowModel) - nextLinearState := computeLunarShadowState(currentJDE+lunarFiniteDifferenceStep, shadowModel) + linearState := evaluate(currentJDE) + nextLinearState := evaluate(currentJDE + lunarFiniteDifferenceStep) dxdt := (nextLinearState.x - linearState.x) / lunarFiniteDifferenceStep dydt := (nextLinearState.y - linearState.y) / lunarFiniteDifferenceStep @@ -234,8 +251,8 @@ func refineLunarEclipseMaximum( correction := -(linearState.x*dxdt + linearState.y*dydt) / denominator maximumJDE := currentJDE + correction - finalState := computeLunarShadowState(maximumJDE, shadowModel) - nextState := computeLunarShadowState(maximumJDE+lunarFiniteDifferenceStep, shadowModel) + finalState := evaluate(maximumJDE) + nextState := evaluate(maximumJDE + lunarFiniteDifferenceStep) dxdt = (nextState.x - finalState.x) / lunarFiniteDifferenceStep dydt = (nextState.y - finalState.y) / lunarFiniteDifferenceStep @@ -250,12 +267,18 @@ func refineLunarEclipseContact( afterMaximum bool, shadowModel lunarEclipseShadowModel, ) float64 { + return refineLunarEclipseContactWithEvaluator(maximumState, dxdt, dydt, boundaryRadius, afterMaximum, func(jde float64) lunarShadowState { + return computeLunarShadowState(jde, shadowModel) + }) +} + +func refineLunarEclipseContactWithEvaluator(maximumState lunarShadowState, dxdt, dydt, boundaryRadius float64, afterMaximum bool, evaluate lunarShadowEvaluator) float64 { firstGuess, ok := solveLineCircleContact(maximumState, dxdt, dydt, boundaryRadius, afterMaximum) if !ok { return math.NaN() } - contactState := computeLunarShadowState(firstGuess, shadowModel) + contactState := evaluate(firstGuess) refinedRadius := boundaryRadius switch { @@ -309,16 +332,20 @@ type lunarEclipsePlaneState struct { } func lunarEclipsePlaneStateAt(jde float64) lunarEclipsePlaneState { + return lunarEclipsePlaneStateAtWithInstance(jde, nil) +} + +func lunarEclipsePlaneStateAtWithInstance(jde float64, inst *Instance) lunarEclipsePlaneState { julianCentury := (jde - 2451545.0) / 36525.0 - sunLongitude := HSunTrueLo(jde)*rad + sunLongitudeAberrationRad(julianCentury) - sunLatitude := HSunTrueBo(jde) * rad - moonLongitude := HMoonTrueLo(jde)*rad + lunarLongitudeAberration - moonLatitude := HMoonTrueBo(jde)*rad + moonLatitudeAberrationRad(julianCentury) + sunLongitude := HSunTrueLoWithInstance(jde, inst)*rad + sunLongitudeAberrationRad(julianCentury) + sunLatitude := HSunTrueBoWithInstance(jde, inst) * rad + moonLongitude := HMoonTrueLoWithInstance(jde, inst)*rad + lunarLongitudeAberration + moonLatitude := HMoonTrueBoWithInstance(jde, inst)*rad + moonLatitudeAberrationRad(julianCentury) return lunarEclipsePlaneState{ jde: jde, - x: normalizeRadians(moonLongitude+math.Pi-sunLongitude) * math.Cos((moonLatitude-sunLatitude)/2), + x: normalizeSignedRadians(moonLongitude+math.Pi-sunLongitude) * math.Cos((moonLatitude-sunLatitude)/2), y: moonLatitude + sunLatitude, } } @@ -328,13 +355,17 @@ func lunarEclipsePlaneStateAt(jde float64) lunarEclipsePlaneState { // 所有内部角量统一使用弧度。影半径模型允许在 Danjon 与 Chauvenet 之间切换, // 其余月心轨迹与几何求交框架保持一致。 func computeLunarShadowState(jde float64, shadowModel lunarEclipseShadowModel) lunarShadowState { - plane := lunarEclipsePlaneStateAt(jde) + return computeLunarShadowStateWithInstance(jde, shadowModel, nil) +} - moonDistanceKM := HMoonAway(jde) - sunDistanceAU := EarthAway(jde) +func computeLunarShadowStateWithInstance(jde float64, shadowModel lunarEclipseShadowModel, inst *Instance) lunarShadowState { + plane := lunarEclipsePlaneStateAtWithInstance(jde, inst) + + moonDistanceKM := HMoonAwayWithInstance(jde, inst) + sunDistanceAU := EarthAwayNWithInstance(jde, -1, inst) moonRadiusArcsec := lunarMoonRadiusScale * lunarArcsecPerRadian / moonDistanceKM - earthParallaxArcsec := lunarEarthEquatorialRadiusKM / moonDistanceKM * lunarArcsecPerRadian + earthParallaxArcsec := geodata.EarthEquatorialRadiusKM / moonDistanceKM * lunarArcsecPerRadian solarRadiusArcsec := eclipseSunRadiusStandardArcsec / sunDistanceAU solarParallaxArcsec := lunarSolarParallaxArcsec / sunDistanceAU umbraRadiusArcsec, penumbraRadiusArcsec := lunarEclipseShadowRadiiArcsec( @@ -370,17 +401,6 @@ func lunarEclipseShadowRadiiArcsec( } } -func normalizeRadians(angle float64) float64 { - angle = math.Mod(angle, 2*math.Pi) - if angle > math.Pi { - angle -= 2 * math.Pi - } - if angle <= -math.Pi { - angle += 2 * math.Pi - } - return angle -} - func sunLongitudeAberrationRad(julianCentury float64) float64 { meanAnomaly := -0.043126 + 628.301955*julianCentury - 0.000002732*julianCentury*julianCentury eccentricity := 0.016708634 - 0.000042037*julianCentury - 0.0000001267*julianCentury*julianCentury diff --git a/basic/lunar_eclipse_diagram.go b/basic/lunar_eclipse_diagram.go index c3bfa2a..7933b53 100644 --- a/basic/lunar_eclipse_diagram.go +++ b/basic/lunar_eclipse_diagram.go @@ -18,6 +18,8 @@ type LunarEclipseDiagramOptions struct { // StepDays 是路径采样步长,单位为日;<=0 时使用 5 分钟。 // StepDays is the path sampling step in days; values <= 0 use five minutes. StepDays float64 + // LunarLimb 月缘剖面,nil 为圆月面;只精修接触时刻,月盘仍按圆面绘制 / lunar limb profile for contacts. + LunarLimb LunarLimb } // LunarEclipseDiagramPoint 表示月食穿影图上的一个月心位置。 @@ -88,8 +90,12 @@ func lunarEclipseDiagram( shadowModel lunarEclipseShadowModel, options LunarEclipseDiagramOptions, ) LunarEclipseDiagramResult { + return lunarEclipseDiagramWithInstance(seedJDE, shadowModel, options, nil) +} + +func lunarEclipseDiagramWithInstance(seedJDE float64, shadowModel lunarEclipseShadowModel, options LunarEclipseDiagramOptions, inst *Instance) LunarEclipseDiagramResult { options = normalizeLunarEclipseDiagramOptions(options) - eclipse := lunarEclipse(seedJDE, shadowModel) + eclipse := LunarEclipseWithLunarLimb(lunarEclipseWithInstance(seedJDE, shadowModel, inst), options.LunarLimb, inst) result := LunarEclipseDiagramResult{ Eclipse: eclipse, StepDays: options.StepDays, @@ -98,7 +104,7 @@ func lunarEclipseDiagram( return result } - maximumState := computeLunarShadowState(eclipse.Maximum, shadowModel) + maximumState := computeLunarShadowStateWithInstance(eclipse.Maximum, shadowModel, inst) if maximumState.moonRadiusRad <= 0 { return result } @@ -111,7 +117,7 @@ func lunarEclipseDiagram( result.StepDays = stepDays result.Points = make([]LunarEclipseDiagramPoint, 0, len(times)) for _, item := range times { - plane := lunarEclipsePlaneStateAt(item.jde) + plane := lunarEclipsePlaneStateAtWithInstance(item.jde, inst) result.Points = append(result.Points, LunarEclipseDiagramPoint{ JDE: item.jde, X: plane.x / maximumState.moonRadiusRad, diff --git a/basic/lunar_eclipse_ephemeris_instance.go b/basic/lunar_eclipse_ephemeris_instance.go new file mode 100644 index 0000000..58ad3f7 --- /dev/null +++ b/basic/lunar_eclipse_ephemeris_instance.go @@ -0,0 +1,61 @@ +package basic + +import () + +// LunarEclipseWithInstance 指定星历实例的月食(TT,Danjon),计算失败时食甚为 NaN / lunar eclipse with an explicit instance; TT maximum is NaN on failure. +func LunarEclipseWithInstance(seedJDE float64, inst *Instance) LunarEclipseResult { + return LunarEclipseDanjonWithInstance(seedJDE, inst) +} + +// LunarEclipseDanjonWithInstance 指定星历实例的 Danjon 月食 / Danjon lunar eclipse with an explicit ephemeris instance. +func LunarEclipseDanjonWithInstance(seedJDE float64, inst *Instance) LunarEclipseResult { + return lunarEclipseWithInstance(seedJDE, lunarEclipseShadowDanjon, inst) +} + +// LunarEclipseChauvenetWithInstance 指定星历实例的 Chauvenet 月食 / Chauvenet lunar eclipse with an explicit ephemeris instance. +func LunarEclipseChauvenetWithInstance(seedJDE float64, inst *Instance) LunarEclipseResult { + return lunarEclipseWithInstance(seedJDE, lunarEclipseShadowChauvenet, inst) +} + +func lunarEclipseWithInstance(seedJDE float64, shadowModel lunarEclipseShadowModel, inst *Instance) LunarEclipseResult { + if inst == nil { + return lunarEclipse(seedJDE, shadowModel) + } + fullMoonJDE := CalcMoonSHByJDEWithInstance(seedJDE, 1, inst) + return lunarEclipseFromFullMoon(fullMoonJDE, shadowModel, func(jde float64) lunarShadowState { + return computeLunarShadowStateWithInstance(jde, shadowModel, inst) + }) +} + +// LunarEclipseShadowGeometryAtWithInstance 指定星历实例的 Danjon 地影几何 / Danjon shadow geometry with an explicit ephemeris instance. +func LunarEclipseShadowGeometryAtWithInstance(maximumJDE float64, inst *Instance) LunarEclipseShadowGeometry { + return lunarEclipseShadowGeometryAtWithInstance(maximumJDE, lunarEclipseShadowDanjon, inst) +} + +// LunarEclipseShadowGeometryChauvenetAtWithInstance 指定星历实例的 Chauvenet 地影几何 / Chauvenet shadow geometry with an explicit ephemeris instance. +func LunarEclipseShadowGeometryChauvenetAtWithInstance(maximumJDE float64, inst *Instance) LunarEclipseShadowGeometry { + return lunarEclipseShadowGeometryAtWithInstance(maximumJDE, lunarEclipseShadowChauvenet, inst) +} + +// LunarEclipseShadowGeometryAtModelWithInstance 指定星历实例及影半径模型的地影几何 / shadow geometry with an explicit instance and shadow-radius model. +func LunarEclipseShadowGeometryAtModelWithInstance(maximumJDE float64, model LunarEclipseShadowModel, inst *Instance) LunarEclipseShadowGeometry { + if model == LunarEclipseShadowModelChauvenet { + return LunarEclipseShadowGeometryChauvenetAtWithInstance(maximumJDE, inst) + } + return LunarEclipseShadowGeometryAtWithInstance(maximumJDE, inst) +} + +// LunarEclipseDiagramWithInstance 指定星历实例的 Danjon 月食穿影图 / Danjon lunar eclipse diagram with an explicit ephemeris instance. +func LunarEclipseDiagramWithInstance(seedJDE float64, options LunarEclipseDiagramOptions, inst *Instance) LunarEclipseDiagramResult { + return LunarEclipseDiagramDanjonWithInstance(seedJDE, options, inst) +} + +// LunarEclipseDiagramDanjonWithInstance 指定星历实例的 Danjon 月食穿影图 / Danjon lunar eclipse diagram with an explicit ephemeris instance. +func LunarEclipseDiagramDanjonWithInstance(seedJDE float64, options LunarEclipseDiagramOptions, inst *Instance) LunarEclipseDiagramResult { + return lunarEclipseDiagramWithInstance(seedJDE, lunarEclipseShadowDanjon, options, inst) +} + +// LunarEclipseDiagramChauvenetWithInstance 指定星历实例的 Chauvenet 月食穿影图 / Chauvenet lunar eclipse diagram with an explicit ephemeris instance. +func LunarEclipseDiagramChauvenetWithInstance(seedJDE float64, options LunarEclipseDiagramOptions, inst *Instance) LunarEclipseDiagramResult { + return lunarEclipseDiagramWithInstance(seedJDE, lunarEclipseShadowChauvenet, options, inst) +} diff --git a/basic/lunar_eclipse_ephemeris_instance_test.go b/basic/lunar_eclipse_ephemeris_instance_test.go new file mode 100644 index 0000000..ae4c30f --- /dev/null +++ b/basic/lunar_eclipse_ephemeris_instance_test.go @@ -0,0 +1,162 @@ +package basic + +import ( + "math" + "reflect" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/geodata" +) + +type p3LunarEngine struct { + epoch float64 + moonDistance float64 +} + +func (engine p3LunarEngine) State(target, center ephem.Body, jde float64) ([3]float64, [3]float64, error) { + if center != ephem.Earth || target != ephem.Sun && target != ephem.Moon { + return [3]float64{}, [3]float64{}, ephem.ErrUnsupportedBody + } + longitude, latitude, distance := 0.0, 0.0, astronomicalUnitKM + if target == ephem.Moon { + longitude = math.Pi + 0.23*(jde-engine.epoch) + latitude = 0.002 + distance = engine.moonDistance + } + ecliptic := [3]float64{distance * math.Cos(latitude) * math.Cos(longitude), distance * math.Cos(latitude) * math.Sin(longitude), distance * math.Sin(latitude)} + sine, cosine := math.Sincos(Obliquity1980(jde) * math.Pi / 180) + equatorial := [3]float64{ecliptic[0], ecliptic[1]*cosine - ecliptic[2]*sine, ecliptic[1]*sine + ecliptic[2]*cosine} + matrix := ltpPBMAT(2000 + (jde-2451545)/365.25) + var position [3]float64 + for axis := range position { + position[axis] = matrix[0][axis]*equatorial[0] + matrix[1][axis]*equatorial[1] + matrix[2][axis]*equatorial[2] + } + return position, [3]float64{}, nil +} + +func (engine p3LunarEngine) Coverage(target ephem.Body) (float64, float64, bool) { + return engine.epoch - 20, engine.epoch + 20, target == ephem.Sun || target == ephem.Moon +} + +func p3AssertLunarResultEqual(t *testing.T, got, want LunarEclipseResult) { + t.Helper() + gotValue, wantValue := reflect.ValueOf(got), reflect.ValueOf(want) + for field := 0; field < gotValue.NumField(); field++ { + gotField, wantField := gotValue.Field(field), wantValue.Field(field) + if gotField.Kind() == reflect.Float64 && math.IsNaN(gotField.Float()) && math.IsNaN(wantField.Float()) { + continue + } + if !reflect.DeepEqual(gotField.Interface(), wantField.Interface()) { + t.Errorf("%s: got %v, want %v", gotValue.Type().Field(field).Name, gotField.Interface(), wantField.Interface()) + } + } +} + +func TestP3LunarEclipseBuiltInCompatibility(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + ephem.SetActive(model) + inst := ephem.NewModelInstance(model) + for _, date := range [][3]int{{2022, 11, 8}, {2023, 5, 5}, {2023, 10, 28}, {2024, 4, 23}} { + seed := JDCalc(date[0], date[1], float64(date[2])) + for _, calculate := range []struct { + old func(float64) LunarEclipseResult + new func(float64, *ephem.Instance) LunarEclipseResult + }{ + {LunarEclipse, LunarEclipseWithInstance}, + {LunarEclipseDanjon, LunarEclipseDanjonWithInstance}, + {LunarEclipseChauvenet, LunarEclipseChauvenetWithInstance}, + } { + want := calculate.old(seed) + p3AssertLunarResultEqual(t, calculate.new(seed, nil), want) + p3AssertLunarResultEqual(t, calculate.new(seed, inst), want) + } + } + } +} + +func TestP3LunarEclipseInstancesStayIsolated(t *testing.T) { + const seed = 2459891.9 + first := ephem.NewInstance(p3LunarEngine{epoch: seed, moonDistance: 384400}) + second := ephem.NewInstance(p3LunarEngine{epoch: seed + 0.25, moonDistance: 370000}) + global, model, generation := ephem.Global(), ephem.Active(), ephem.Generation() + firstResult := LunarEclipseWithInstance(seed, first) + secondResult := LunarEclipseWithInstance(seed, second) + if !firstResult.HasTotal || !secondResult.HasTotal { + t.Fatalf("synthetic eclipses must be total: %+v / %+v", firstResult, secondResult) + } + if math.Abs(secondResult.Maximum-firstResult.Maximum-0.25) > 1e-6 { + t.Errorf("maximum did not follow instance: %.12f / %.12f", firstResult.Maximum, secondResult.Maximum) + } + if firstResult.TotalEnd-firstResult.TotalStart == secondResult.TotalEnd-secondResult.TotalStart { + t.Error("contact durations did not follow instance distances") + } + for _, result := range []LunarEclipseResult{firstResult, secondResult} { + contacts := []float64{result.PenumbralStart, result.PartialStart, result.TotalStart, result.Maximum, result.TotalEnd, result.PartialEnd, result.PenumbralEnd} + for index := 1; index < len(contacts); index++ { + if !isFiniteFloat(contacts[index]) || contacts[index] <= contacts[index-1] { + t.Errorf("invalid contact order: %v", contacts) + } + } + } + p3AssertLunarResultEqual(t, LunarEclipseWithInstance(seed, first), firstResult) + if ephem.Global() != global || ephem.Active() != model || ephem.Generation() != generation { + t.Error("explicit lunar eclipse changed the global ephemeris") + } +} + +func TestP3LunarEclipseGeometryAndDiagramUseInstance(t *testing.T) { + const seed = 2459891.9 + options := LunarEclipseDiagramOptions{StepDays: 0.02} + for _, shadowModel := range []LunarEclipseShadowModel{LunarEclipseShadowModelDanjon, LunarEclipseShadowModelChauvenet} { + inst := ephem.NewInstance(p3LunarEngine{epoch: seed + 0.15, moonDistance: 380000}) + calculate := LunarEclipseDiagramDanjonWithInstance + if shadowModel == LunarEclipseShadowModelChauvenet { + calculate = LunarEclipseDiagramChauvenetWithInstance + } + diagram := calculate(seed, options, inst) + geometry := LunarEclipseShadowGeometryAtModelWithInstance(diagram.Eclipse.Maximum, shadowModel, inst) + if len(diagram.Points) == 0 || math.Abs(geometry.MoonDistanceEarthRadii-380000/geodata.EarthEquatorialRadiusKM) > 1e-10 { + t.Fatalf("diagram/geometry lost instance: %+v", geometry) + } + if math.Abs(diagram.Eclipse.Maximum-seed-0.15) > 0.001 { + t.Errorf("diagram eclipse lost instance: %v", diagram.Eclipse.Maximum) + } + maximumState := computeLunarShadowStateWithInstance(diagram.Eclipse.Maximum, lunarEclipseShadowModel(shadowModel), inst) + for _, point := range diagram.Points { + plane := lunarEclipsePlaneStateAtWithInstance(point.JDE, inst) + if point.X != plane.x/maximumState.moonRadiusRad || point.Y != plane.y/maximumState.moonRadiusRad { + t.Errorf("diagram sample lost instance: %+v", point) + } + } + } + for _, calculate := range []struct { + old func(float64, LunarEclipseDiagramOptions) LunarEclipseDiagramResult + new func(float64, LunarEclipseDiagramOptions, *ephem.Instance) LunarEclipseDiagramResult + }{ + {LunarEclipseDiagram, LunarEclipseDiagramWithInstance}, + {LunarEclipseDiagramDanjon, LunarEclipseDiagramDanjonWithInstance}, + {LunarEclipseDiagramChauvenet, LunarEclipseDiagramChauvenetWithInstance}, + } { + got, want := calculate.new(seed, options, nil), calculate.old(seed, options) + p3AssertLunarResultEqual(t, got.Eclipse, want.Eclipse) + got.Eclipse, want.Eclipse = LunarEclipseResult{}, LunarEclipseResult{} + if !reflect.DeepEqual(got, want) { + t.Error("nil changed diagram geometry or samples") + } + } +} + +func TestP3LunarEclipseFailureDoesNotFallback(t *testing.T) { + inst := ephem.NewInstance(p3StateEngine{err: ephem.ErrOutOfRange}) + result := LunarEclipseWithInstance(2459891.9, inst) + if !math.IsNaN(result.Maximum) || result.HasPenumbral || result.HasPartial || result.HasTotal { + t.Errorf("failed engine produced an eclipse: %+v", result) + } + diagram := LunarEclipseDiagramWithInstance(2459891.9, LunarEclipseDiagramOptions{}, inst) + if !math.IsNaN(diagram.Eclipse.Maximum) || len(diagram.Points) != 0 { + t.Error("failed engine produced diagram samples") + } +} diff --git a/basic/lunar_eclipse_geometry.go b/basic/lunar_eclipse_geometry.go index 757ab6b..6e05e21 100644 --- a/basic/lunar_eclipse_geometry.go +++ b/basic/lunar_eclipse_geometry.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) // LunarEclipseShadowGeometry 是食甚时刻的地影几何。 // 注意单位口径:Gamma 用地球赤道半径,而两个影半径用度——后者是 NASA 月食图上 P./U. Radius 的口径, @@ -50,11 +54,18 @@ func LunarEclipseShadowGeometryAtModel(maximumJDE float64, model LunarEclipseSha } func lunarEclipseShadowGeometryAt(maximumJDE float64, shadowModel lunarEclipseShadowModel) LunarEclipseShadowGeometry { - state := computeLunarShadowState(maximumJDE, shadowModel) - moonDistanceKM := HMoonAway(maximumJDE) + return lunarEclipseShadowGeometryAtWithInstance(maximumJDE, shadowModel, nil) +} + +func lunarEclipseShadowGeometryAtWithInstance(maximumJDE float64, shadowModel lunarEclipseShadowModel, inst *Instance) LunarEclipseShadowGeometry { + evaluate := func(jde float64) lunarShadowState { + return computeLunarShadowStateWithInstance(jde, shadowModel, inst) + } + state := evaluate(maximumJDE) + moonDistanceKM := HMoonAwayWithInstance(maximumJDE, inst) // 影半径是以地心为顶点的角量,除以月球处的地球视差就换成地球赤道半径。 - earthParallax := lunarEarthEquatorialRadiusKM / moonDistanceKM - _, _, _, _, minimumDistance := refineLunarEclipseMaximum(maximumJDE, shadowModel) + earthParallax := geodata.EarthEquatorialRadiusKM / moonDistanceKM + _, _, _, _, minimumDistance := refineLunarEclipseMaximumWithEvaluator(maximumJDE, evaluate) if earthParallax <= 0 { return LunarEclipseShadowGeometry{} } @@ -62,7 +73,7 @@ func lunarEclipseShadowGeometryAt(maximumJDE float64, shadowModel lunarEclipseSh Gamma: minimumDistance / earthParallax, PenumbralRadiusDegrees: state.penumbraRadiusRad * 180 / math.Pi, UmbralRadiusDegrees: state.umbraRadiusRad * 180 / math.Pi, - MoonDistanceEarthRadii: moonDistanceKM / lunarEarthEquatorialRadiusKM, + MoonDistanceEarthRadii: moonDistanceKM / geodata.EarthEquatorialRadiusKM, // 平面 x 向东(黄经差)、y 向北(黄纬和),方位角自北向东量。 AxisDegrees: minimumDistance * 180 / math.Pi, } diff --git a/basic/lunar_eclipse_limb.go b/basic/lunar_eclipse_limb.go new file mode 100644 index 0000000..91fd413 --- /dev/null +++ b/basic/lunar_eclipse_limb.go @@ -0,0 +1,147 @@ +package basic + +import "math" + +// lunarEclipseLimbMaxShiftDays 是月缘精修允许偏离圆月面接触的上限(10 分钟),超出视为不收敛并保留原值。 +const lunarEclipseLimbMaxShiftDays = 600.0 / 86400.0 + +// lunarEclipseLimbStepDays 是割线法的初始步长(1 秒)。 +const lunarEclipseLimbStepDays = 1.0 / 86400.0 + +// LunarEclipseWithLunarLimb 用月缘剖面精修月食接触时刻,食甚、食分与食型仍按圆月面 / refine lunar eclipse contacts with a lunar limb profile. +func LunarEclipseWithLunarLimb(result LunarEclipseResult, limb LunarLimb, inst *Instance) LunarEclipseResult { + if limb == nil { + return result + } + original := result + shadowModel := lunarEclipseShadowDanjon + if result.ShadowModel == LunarEclipseShadowModelChauvenet { + shadowModel = lunarEclipseShadowChauvenet + } + evaluate := func(jde float64) lunarShadowState { + return computeLunarShadowStateWithInstance(jde, shadowModel, inst) + } + refine := func(seed float64, kind lunarEclipseLimbContact) float64 { + if !finite(seed) { + return seed + } + return refineLunarEclipseLimbContact(seed, kind, limb, evaluate, inst) + } + if result.HasPenumbral { + result.PenumbralStart = refine(result.PenumbralStart, lunarEclipseLimbPenumbral) + result.PenumbralEnd = refine(result.PenumbralEnd, lunarEclipseLimbPenumbral) + } + if result.HasPartial { + result.PartialStart = refine(result.PartialStart, lunarEclipseLimbUmbral) + result.PartialEnd = refine(result.PartialEnd, lunarEclipseLimbUmbral) + } + if result.HasTotal { + result.TotalStart = refine(result.TotalStart, lunarEclipseLimbTotal) + result.TotalEnd = refine(result.TotalEnd, lunarEclipseLimbTotal) + } + result.LunarLimbApplied = lunarEclipseLimbChanged(result, original) + return result +} + +func lunarEclipseLimbChanged(result, original LunarEclipseResult) bool { + for _, pair := range [][2]float64{ + {result.PenumbralStart, original.PenumbralStart}, + {result.PenumbralEnd, original.PenumbralEnd}, + {result.PartialStart, original.PartialStart}, + {result.PartialEnd, original.PartialEnd}, + {result.TotalStart, original.TotalStart}, + {result.TotalEnd, original.TotalEnd}, + } { + if limbTimeChanged(pair[0], pair[1]) { + return true + } + } + return false +} + +type lunarEclipseLimbContact int + +const ( + lunarEclipseLimbPenumbral lunarEclipseLimbContact = iota + lunarEclipseLimbUmbral + lunarEclipseLimbTotal +) + +// refineLunarEclipseLimbContact 以圆月面接触为初值用割线法求月缘间隙的零点,不收敛时返回初值。 +func refineLunarEclipseLimbContact(seed float64, kind lunarEclipseLimbContact, limb LunarLimb, evaluate lunarShadowEvaluator, inst *Instance) float64 { + t0, t1 := seed, seed+lunarEclipseLimbStepDays + g0, ok0 := lunarEclipseLimbGap(t0, kind, limb, evaluate, inst) + g1, ok1 := lunarEclipseLimbGap(t1, kind, limb, evaluate, inst) + if !ok0 || !ok1 { + return seed + } + for i := 0; i < 12; i++ { + if g1 == g0 { + return seed + } + t2 := t1 - g1*(t1-t0)/(g1-g0) + if !finite(t2) || math.Abs(t2-seed) > lunarEclipseLimbMaxShiftDays { + return seed + } + if math.Abs(t2-t1) < 1e-9 { + return t2 + } + g2, ok := lunarEclipseLimbGap(t2, kind, limb, evaluate, inst) + if !ok { + return seed + } + t0, g0, t1, g1 = t1, g1, t2, g2 + } + return seed +} + +// lunarEclipseLimbGap 是月缘版接触间隙(弧度),零点即接触。 +func lunarEclipseLimbGap(jde float64, kind lunarEclipseLimbContact, limb LunarLimb, evaluate lunarShadowEvaluator, inst *Instance) (float64, bool) { + state := evaluate(jde) + moonRA, moonDec := HMoonGeocentricApparentRaDecWithInstance(jde, inst) + sunRA, sunDec := HSunApparentRaDecWithInstance(jde, inst) + moon := lunarEclipseLimbVector(moonRA, moonDec, HMoonAwayWithInstance(jde, inst)) + shadow := lunarEclipseLimbVector(sunRA+180, -sunDec, 1) + + if !finite(state.moonRadiusRad) || state.moonRadiusRad <= 0 { + return 0, false + } + frame := newLunarLimbFrame(limb, jde, moon, inst) + view, ok := frame.viewToward(moon, shadow) + if !ok { + return 0, false + } + separation := math.Hypot(state.x, state.y) + contact := lunarLimbContact{ + frame: &frame, + view: view, + separation: separation, + anglePerKM: state.moonRadiusRad / lunarMoonRadiusScale, + } + switch kind { + case lunarEclipseLimbPenumbral: + contact.targetRadius = state.penumbraRadiusRad + default: + contact.targetRadius = state.umbraRadiusRad + } + // 半影、本影外切取全方向最外的月缘;全食内切取全方向最靠外、最后进入本影的月缘。 + var limbRad float64 + if kind == lunarEclipseLimbTotal { + limbRad, ok = contact.innerRadius(false) + } else { + limbRad, ok = contact.outerRadius() + } + if !ok { + return 0, false + } + if kind == lunarEclipseLimbTotal { + return separation - math.Abs(limbRad-contact.targetRadius), true + } + return separation - limbRad - contact.targetRadius, true +} + +func lunarEclipseLimbVector(raDeg, decDeg, distance float64) [3]float64 { + sinRA, cosRA := math.Sincos(raDeg * rad) + sinDec, cosDec := math.Sincos(decDeg * rad) + return [3]float64{distance * cosDec * cosRA, distance * cosDec * sinRA, distance * sinDec} +} diff --git a/basic/lunar_eclipse_test.go b/basic/lunar_eclipse_test.go index b27b203..55e7600 100644 --- a/basic/lunar_eclipse_test.go +++ b/basic/lunar_eclipse_test.go @@ -3,6 +3,8 @@ package basic import ( "math" "testing" + + "b612.me/astro/internal/ephemtest" ) type lunarEclipseBaseline struct { @@ -117,10 +119,20 @@ func TestLunarEclipseChauvenetAgainstLegacyBaseline(t *testing.T) { const timeTolerance = 1e-6 const magnitudeTolerance = 2e-5 + values := ephemtest.NewValues(t, "lunar_eclipse_chauvenet") for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := LunarEclipseChauvenet(tc.jde) + w := func(field string, legacy, got float64) float64 { return values.Want(tc.name+"/"+field, legacy, got) } + tc.expectedMax = w("Maximum", tc.expectedMax, result.Maximum) + tc.expectedMag = w("Magnitude", tc.expectedMag, result.Magnitude) + tc.expectedPenumbralStart = w("PenumbralStart", tc.expectedPenumbralStart, result.PenumbralStart) + tc.expectedPenumbralEnd = w("PenumbralEnd", tc.expectedPenumbralEnd, result.PenumbralEnd) + tc.expectedPartialStart = w("PartialStart", tc.expectedPartialStart, result.PartialStart) + tc.expectedPartialEnd = w("PartialEnd", tc.expectedPartialEnd, result.PartialEnd) + tc.expectedTotalStart = w("TotalStart", tc.expectedTotalStart, result.TotalStart) + tc.expectedTotalEnd = w("TotalEnd", tc.expectedTotalEnd, result.TotalEnd) if result.Type != tc.expectedType { t.Fatalf("Type mismatch: got %s want %s", result.Type, tc.expectedType) diff --git a/basic/lunar_limb.go b/basic/lunar_limb.go new file mode 100644 index 0000000..849f8fe --- /dev/null +++ b/basic/lunar_limb.go @@ -0,0 +1,424 @@ +package basic + +import "math" + +// LunarLimb 月缘剖面,nil 为圆月面,内置数据表在 moon/limb / lunar limb profile; nil means a circular limb. +type LunarLimb interface { + // LimbRadiusKM 按观测者总天平动经纬(度,东正北正)与自投影月北极起算的位置角(度)返回月心到月缘距离(km),无值时第二个返回 false / limb radius at a position angle for the given libration. + LimbRadiusKM(librationLongitude, librationLatitude, positionAngle float64) (float64, bool) +} + +// lunarLimbFrame 是某一瞬间的月球本体坐标架在真赤道坐标系下的表示, +// 由该时刻的地心天平动与自转轴位置角反解,可对任意站心方向复用。 +type lunarLimbFrame struct { + limb LunarLimb + // stepDeg 是剖面位置角网格步长;ranged 为真时 minKM/maxKM 是剖面可能取到的半径范围。 + stepDeg float64 + minKM, maxKM float64 + ranged bool + // axisX / axisY / axisZ 分别是月面经纬 (0°,0°)、(90°,0°) 与北极方向的单位矢量。 + axisX [3]float64 + axisY [3]float64 + axisZ [3]float64 + valid bool +} + +// newLunarLimbFrame 用地心月球方向与该时刻的物理天平动构造本体坐标架。 +// moonGeocentric 是真赤道坐标系下的地心月球矢量(km)。 +func newLunarLimbFrame(limb LunarLimb, jdTT float64, moonGeocentric [3]float64, instances ...*Instance) lunarLimbFrame { + if limb == nil { + return lunarLimbFrame{} + } + inst := instanceValue(instances) + + toEarth, ok := lunarLimbNormalize([3]float64{-moonGeocentric[0], -moonGeocentric[1], -moonGeocentric[2]}) + if !ok { + return lunarLimbFrame{} + } + physical := MoonPhysicalWithInstance(jdTT, inst) + north, east, ok := lunarLimbSkyBasis([3]float64{-toEarth[0], -toEarth[1], -toEarth[2]}) + if !ok { + return lunarLimbFrame{} + } + + // 月球北极在天空平面上的投影自天北起向东量 P,于是极轴方向 = sinB·(月心→地心) + cosB·N。 + sinP, cosP := math.Sincos(physical.PositionAngle * rad) + projected := lunarLimbCombine(cosP, north, sinP, east) + sinB, cosB := math.Sincos(physical.LibrationLatitude * rad) + axisZ, ok := lunarLimbNormalize(lunarLimbCombine(sinB, toEarth, cosB, projected)) + if !ok { + return lunarLimbFrame{} + } + + // 地心方向在赤道面内的分量对应本体系的 (cosL, sinL, 0),据此旋回 x 轴。 + meridian, ok := lunarLimbNormalize(lunarLimbReject(toEarth, axisZ)) + if !ok { + return lunarLimbFrame{} + } + sinL, cosL := math.Sincos(physical.LibrationLongitude * rad) + axisX, ok := lunarLimbNormalize(lunarLimbCombine(cosL, meridian, -sinL, lunarLimbCross(axisZ, meridian))) + if !ok { + return lunarLimbFrame{} + } + frame := lunarLimbFrame{ + limb: limb, + stepDeg: lunarLimbDefaultStepDeg, + axisX: axisX, + axisY: lunarLimbCross(axisZ, axisX), + axisZ: axisZ, + valid: true, + } + if ranged, ok := limb.(lunarLimbRangeProvider); ok { + low, high := ranged.RadiusRangeKM() + if step := ranged.AngleStepDeg(); finite(step) && step >= lunarLimbMinStepDeg && step <= 1 { + frame.stepDeg = step + } + if finite(low) && finite(high) && low > 0 && high >= low { + frame.minKM, frame.maxKM, frame.ranged = low, high, true + } + } + return frame +} + +// radiusKM 返回站心所见月缘在指定方向上的月心距(km);awayFromTarget 为真时取背向目标的一侧。 +func (frame lunarLimbFrame) radiusKM(moonTopocentric, targetTopocentric [3]float64, awayFromTarget bool) (float64, bool) { + if !frame.valid { + return 0, false + } + moon, ok := lunarLimbNormalize(moonTopocentric) + if !ok { + return 0, false + } + target, ok := lunarLimbNormalize(targetTopocentric) + if !ok { + return 0, false + } + north, east, ok := lunarLimbSkyBasis(moon) + if !ok { + return 0, false + } + toObserver := [3]float64{-moon[0], -moon[1], -moon[2]} + longitude := math.Atan2(lunarLimbDot(toObserver, frame.axisY), lunarLimbDot(toObserver, frame.axisX)) / rad + latitude := math.Asin(clampUnit(lunarLimbDot(toObserver, frame.axisZ))) / rad + + // 自转轴位置角 P 与目标方向位置角 Q 都自天北向东量;剖面表的 θ 自月球北极向月面东经一侧 + // (从地球看是天西)量,所以 θ = P − Q。 + axisAngle := math.Atan2(lunarLimbDot(frame.axisZ, east), lunarLimbDot(frame.axisZ, north)) + targetPlane, ok := lunarLimbNormalize(lunarLimbReject(target, moon)) + if !ok { + return 0, false + } + targetAngle := math.Atan2(lunarLimbDot(targetPlane, east), lunarLimbDot(targetPlane, north)) + positionAngle := (axisAngle - targetAngle) / rad + if awayFromTarget { + positionAngle += 180 + } + positionAngle = math.Mod(positionAngle, 360) + if positionAngle < 0 { + positionAngle += 360 + } + radius, ok := frame.limb.LimbRadiusKM(longitude, latitude, positionAngle) + if !ok || !finite(radius) || radius <= 0 { + return 0, false + } + return radius, true +} + +// lunarLimbView 是站心所见的月面经纬与指向目标方向的剖面位置角(度)。 +type lunarLimbView struct { + longitude, latitude, angle float64 +} + +// viewToward 求站心所见天平动与目标方向的剖面位置角,约定与 radiusKM 相同。 +func (frame lunarLimbFrame) viewToward(moonTopocentric, targetTopocentric [3]float64) (lunarLimbView, bool) { + if !frame.valid { + return lunarLimbView{}, false + } + moon, ok := lunarLimbNormalize(moonTopocentric) + if !ok { + return lunarLimbView{}, false + } + target, ok := lunarLimbNormalize(targetTopocentric) + if !ok { + return lunarLimbView{}, false + } + north, east, ok := lunarLimbSkyBasis(moon) + if !ok { + return lunarLimbView{}, false + } + toObserver := [3]float64{-moon[0], -moon[1], -moon[2]} + axisAngle := math.Atan2(lunarLimbDot(frame.axisZ, east), lunarLimbDot(frame.axisZ, north)) + targetPlane, ok := lunarLimbNormalize(lunarLimbReject(target, moon)) + if !ok { + return lunarLimbView{}, false + } + targetAngle := math.Atan2(lunarLimbDot(targetPlane, east), lunarLimbDot(targetPlane, north)) + return lunarLimbView{ + longitude: math.Atan2(lunarLimbDot(toObserver, frame.axisY), lunarLimbDot(toObserver, frame.axisX)) / rad, + latitude: math.Asin(clampUnit(lunarLimbDot(toObserver, frame.axisZ))) / rad, + angle: (axisAngle - targetAngle) / rad, + }, true +} + +// radiusAt 返回剖面位置角偏离目标方向 offset 度处的月缘月心距(km)。 +func (frame lunarLimbFrame) radiusAt(view lunarLimbView, offset float64) (float64, bool) { + angle := math.Mod(view.angle+offset, 360) + if angle < 0 { + angle += 360 + } + radius, ok := frame.limb.LimbRadiusKM(view.longitude, view.latitude, angle) + if !ok || !finite(radius) || radius <= 0 { + return 0, false + } + return radius, true +} + +// lunarLimbSkyBasis 返回视方向 direction(观测者指向天体)处的天球北、东单位矢量; +// 东向为赤经增加方向,e = n × direction。 +func lunarLimbSkyBasis(direction [3]float64) ([3]float64, [3]float64, bool) { + north, ok := lunarLimbNormalize(lunarLimbReject([3]float64{0, 0, 1}, direction)) + if !ok { + return [3]float64{}, [3]float64{}, false + } + return north, lunarLimbCross(north, direction), true +} + +func lunarLimbNormalize(vector [3]float64) ([3]float64, bool) { + norm := math.Sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]) + if !finite(norm) || norm <= 0 { + return [3]float64{}, false + } + return [3]float64{vector[0] / norm, vector[1] / norm, vector[2] / norm}, true +} + +// lunarLimbReject 返回 vector 中垂直于单位矢量 axis 的分量。 +func lunarLimbReject(vector, axis [3]float64) [3]float64 { + projection := lunarLimbDot(vector, axis) + return [3]float64{ + vector[0] - projection*axis[0], + vector[1] - projection*axis[1], + vector[2] - projection*axis[2], + } +} + +func lunarLimbCombine(scaleA float64, a [3]float64, scaleB float64, b [3]float64) [3]float64 { + return [3]float64{ + scaleA*a[0] + scaleB*b[0], + scaleA*a[1] + scaleB*b[1], + scaleA*a[2] + scaleB*b[2], + } +} + +func lunarLimbCross(a, b [3]float64) [3]float64 { + return [3]float64{ + a[1]*b[2] - a[2]*b[1], + a[2]*b[0] - a[0]*b[2], + a[0]*b[1] - a[1]*b[0], + } +} + +func lunarLimbDot(a, b [3]float64) float64 { + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] +} + +// lunarLimbDefaultStepDeg 剖面不报告网格步长时的位置角采样步长(度)。 +const lunarLimbDefaultStepDeg = 0.1 + +// lunarLimbMinStepDeg 剖面报告的步长低于此值时退回默认步长,防止全圈扫描次数失控。 +const lunarLimbMinStepDeg = 0.001 + +// lunarLimbRangeProvider 可选接口:报告剖面半径范围与位置角网格步长,用于门控与剪枝;步长不在 [0.001°, 1°] 内时改用默认 0.1°。 +type lunarLimbRangeProvider interface { + RadiusRangeKM() (float64, float64) + AngleStepDeg() float64 +} + +// lunarLimbContact 是某一站心时刻按全部位置角求日面接触余量的上下文,角量均为弧度,在天空平面上按平直几何计。 +type lunarLimbContact struct { + frame *lunarLimbFrame + view lunarLimbView + distanceKM float64 + separation float64 + targetRadius float64 + // anglePerKM 非零时月缘半径按线性比例换算成角量(月食影面坐标);为零时按 asin(r/distanceKM)。 + anglePerKM float64 +} + +// radius 返回该方向月缘角半径;exact 为假时用 r/距离 的线性近似,只供扫描选方向,极值处再按 exact 重算。 +func (contact lunarLimbContact) radius(offsetDeg float64, exact bool) (float64, bool) { + radiusKM, ok := contact.frame.radiusAt(contact.view, offsetDeg) + if !ok { + return 0, false + } + if contact.anglePerKM != 0 { + return radiusKM * contact.anglePerKM, true + } + if !exact { + return radiusKM / contact.distanceKM, true + } + return math.Asin(localSolarEclipseClampUnit(radiusKM / contact.distanceKM)), true +} + +// bounds 返回剖面半径的角量上下界;剖面不报告范围时 ok 为假,此时不门控、不剪枝。 +func (contact lunarLimbContact) bounds() (float64, float64, bool) { + if !contact.frame.ranged { + return 0, 0, false + } + if contact.anglePerKM != 0 { + return contact.frame.minKM * contact.anglePerKM, contact.frame.maxKM * contact.anglePerKM, true + } + return contact.frame.minKM / contact.distanceKM, contact.frame.maxKM / contact.distanceKM, true +} + +// rayChord 返回自月心沿偏离日心方向 δ 的射线与日面边界的近、远交点距离;射线不交日面时 ok 为假。 +func (contact lunarLimbContact) rayChord(offsetDeg float64) (float64, float64, bool) { + sine, cosine := math.Sincos(offsetDeg * rad) + d, s := contact.separation, contact.targetRadius + discriminant := s*s - d*d*sine*sine + if discriminant < 0 { + return 0, 0, false + } + root := math.Sqrt(discriminant) + return d*cosine - root, d*cosine + root, true +} + +// scan 自 baseDeg 向两侧按剖面位置角网格节点求余量极值并在极值方向按精确半径重算;maximize 为真取最大。剖面在节点间按位置角线性插值, +// 只取节点与 baseDeg 即不会漏掉剖面峰谷。bound(δ) 给出该方向余量可能达到的最好值,越过它不可能再刷新极值即停止该侧。 +func (contact lunarLimbContact) scan( + baseDeg float64, + maximize bool, + margin func(offsetDeg float64, exact bool) (float64, bool, bool), + bound func(offsetDeg float64) (float64, bool), +) (float64, bool) { + best, bestOffset, found := 0.0, baseDeg, false + visit := func(offset float64) bool { + value, hit, ok := margin(offset, false) + if !ok { + return false + } + if hit && (!found || (maximize && value > best) || (!maximize && value < best)) { + best, bestOffset, found = value, offset, true + } + return true + } + if !visit(baseDeg) { + return 0, false + } + step := contact.frame.stepDeg + absolute := contact.view.angle + baseDeg + first := [2]float64{math.Floor(absolute/step)*step - absolute, math.Ceil(absolute/step)*step - absolute} + steps := int(math.Ceil(180/step)) + 1 + for side, sign := range [2]float64{-1, 1} { + for index := 0; index <= steps; index++ { + delta := first[side] + sign*float64(index)*step + if delta == 0 || math.Abs(delta) > 180 { + continue + } + offset := baseDeg + delta + if limit, ok := bound(offset); ok && found && + ((maximize && limit <= best) || (!maximize && limit >= best)) { + break + } + if !visit(offset) { + return 0, false + } + } + } + if !found { + return 0, false + } + exact, _, ok := margin(bestOffset, true) + return exact, ok +} + +// outerRadius 返回外切等效月半径(弧度):取全部方向中月缘超出日面近侧边界最多者,使 d − R − s 为零即外切。 +// 只有剖面范围保证间隙离零至少一个起伏幅度时才跳过扫描,免得门控两侧的间隙在接触根附近不连续。 +func (contact lunarLimbContact) outerRadius() (float64, bool) { + d, s := contact.separation, contact.targetRadius + low, high, ranged := contact.bounds() + if d <= s || (ranged && (high <= d-s-(high-low) || low >= d-s+(high-low))) { + return contact.radius(0, true) + } + best, ok := contact.scan(0, true, + func(offset float64, exact bool) (float64, bool, bool) { + near, _, hit := contact.rayChord(offset) + if !hit { + return 0, false, true + } + radius, ok := contact.radius(offset, exact) + return radius - near, true, ok + }, + func(offset float64) (float64, bool) { + near, _, hit := contact.rayChord(offset) + if !hit { + return math.Inf(-1), true + } + return high - near, ranged + }) + if !ok { + return 0, false + } + return d - s + best, true +} + +// innerRadius 返回内切等效月半径(弧度),使内切间隙 d − |R − s| 等于 min(−余量, d)。total 为真按全食求月缘盖住日面 +// 远侧边界的最小余量,否则按环食求日面远侧边界超出月缘的最小余量;门控条件同 outerRadius。 +func (contact lunarLimbContact) innerRadius(total bool) (float64, bool) { + d, s := contact.separation, contact.targetRadius + low, high, ranged := contact.bounds() + relief := high - low + if total { + if ranged && (low >= d+s+relief || high <= d+s-relief) { + radius, ok := contact.radius(0, true) + return s + math.Max(radius-s, 0), ok + } + best, ok := contact.scan(0, false, + func(offset float64, exact bool) (float64, bool, bool) { + _, far, hit := contact.rayChord(offset) + if !hit { + return 0, false, true + } + radius, ok := contact.radius(offset, exact) + return radius - far, true, ok + }, + func(offset float64) (float64, bool) { + _, far, hit := contact.rayChord(offset) + if !hit { + return math.Inf(1), true + } + return low - far, ranged + }) + if !ok { + return 0, false + } + return s + math.Max(d+best, 0), true + } + // 月心在日面外时环食不可能,内切间隙恰为 d。 + if d >= s { + return s, true + } + if ranged && (high <= s-d-relief || low >= s-d+relief) { + radius, ok := contact.radius(180, true) + return math.Min(radius, s), ok + } + best, ok := contact.scan(180, false, + func(offset float64, exact bool) (float64, bool, bool) { + _, far, hit := contact.rayChord(offset) + if !hit { + return 0, false, true + } + radius, ok := contact.radius(offset, exact) + return far - radius, true, ok + }, + func(offset float64) (float64, bool) { + _, far, hit := contact.rayChord(offset) + if !hit { + return math.Inf(1), true + } + return far - high, ranged + }) + if !ok { + return 0, false + } + return s - math.Max(d+best, 0), true +} diff --git a/basic/lunar_limb_bench_test.go b/basic/lunar_limb_bench_test.go new file mode 100644 index 0000000..bca7be5 --- /dev/null +++ b/basic/lunar_limb_bench_test.go @@ -0,0 +1,105 @@ +package basic + +// 月缘路径基准:与同一调用的圆月面版本成对,改动扫描步长或剪枝时对照两者倍率。 + +import ( + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +func benchmarkLocalSolarEclipseLimb(b *testing.B, profile LunarLimb) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK, LunarLimb: profile} + b.ResetTimer() + for index := 0; index < b.N; index++ { + LocalSolarEclipseWithOptionsWithInstance(seed, 116.2310, 39.70, 0, options, nil) + } +} + +func BenchmarkLocalSolarEclipseCircular20350902(b *testing.B) { benchmarkLocalSolarEclipseLimb(b, nil) } + +func BenchmarkLocalSolarEclipseLunarLimb20350902(b *testing.B) { + benchmarkLocalSolarEclipseLimb(b, limb.Default()) +} + +func benchmarkStarOccultationLimb(b *testing.B, profile LunarLimb) { + start := time.Date(2026, 2, 10, 12, 0, 0, 0, time.UTC) + options := OccultationSearchOptions{LunarLimb: profile} + b.ResetTimer() + for index := 0; index < b.N; index++ { + if _, err := FindStarOccultationsWithInstance(start, start.Add(36*time.Hour), limbTestAntares, 41.5907, -55.07036, 0, options, nil); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkStarOccultationCircularAntares(b *testing.B) { benchmarkStarOccultationLimb(b, nil) } + +func BenchmarkStarOccultationLunarLimbAntares(b *testing.B) { + benchmarkStarOccultationLimb(b, limb.Default()) +} + +func benchmarkPlanetOccultationLimb(b *testing.B, profile LunarLimb) { + start := time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC) + end := time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC) + options := OccultationSearchOptions{LunarLimb: profile} + b.ResetTimer() + for index := 0; index < b.N; index++ { + if _, err := FindPlanetOccultationsWithInstance(start, end, OccultationSaturn, -30.072, 43.562592, 0, options, nil); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkPlanetOccultationCircularSaturnGraze(b *testing.B) { + benchmarkPlanetOccultationLimb(b, nil) +} + +func BenchmarkPlanetOccultationLunarLimbSaturnGraze(b *testing.B) { + benchmarkPlanetOccultationLimb(b, limb.Default()) +} + +func BenchmarkStarOccultationLimbProfileAntares(b *testing.B) { + info := StarOccultationInfo{Greatest: time.Date(2026, 2, 11, 3, 47, 15, 0, time.UTC), Observer: Observer{Longitude: 41.5907, Latitude: -55.07036}} + profile := limb.Default() + b.ResetTimer() + for index := 0; index < b.N; index++ { + if _, err := StarOccultationLimbProfileWithInstance(info, limbTestAntares, profile, StarOccultationLimbProfileOptions{}, nil); err != nil { + b.Fatal(err) + } + } +} + +func benchmarkSolarEclipseCentralPathLimb(b *testing.B, profile LunarLimb) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + b.ResetTimer() + for index := 0; index < b.N; index++ { + SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: profile}, nil) + } +} + +func BenchmarkSolarEclipseCentralPathCircular20350902(b *testing.B) { + benchmarkSolarEclipseCentralPathLimb(b, nil) +} + +func BenchmarkSolarEclipseCentralPathLunarLimb20350902(b *testing.B) { + benchmarkSolarEclipseCentralPathLimb(b, limb.Default()) +} + +func benchmarkSolarEclipsePartialFootprintsLimb(b *testing.B, profile LunarLimb) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + b.ResetTimer() + for index := 0; index < b.N; index++ { + SolarEclipsePartialFootprintsNASABulletinSplitKWithInstance(seed, SolarEclipsePartialFootprintOptions{LunarLimb: profile}, nil) + } +} + +func BenchmarkSolarEclipsePartialFootprintsCircular20350902(b *testing.B) { + benchmarkSolarEclipsePartialFootprintsLimb(b, nil) +} + +func BenchmarkSolarEclipsePartialFootprintsLunarLimb20350902(b *testing.B) { + benchmarkSolarEclipsePartialFootprintsLimb(b, limb.Default()) +} diff --git a/basic/lunar_limb_contact_test.go b/basic/lunar_limb_contact_test.go new file mode 100644 index 0000000..0e1d599 --- /dev/null +++ b/basic/lunar_limb_contact_test.go @@ -0,0 +1,277 @@ +package basic + +// 月缘等效半径必须与全部位置角的暴力判据一致,且站心全食与中心带多边形对同一地点结论一致。 + +import ( + "math" + "testing" + "time" + + "b612.me/astro/internal/geodata" + "b612.me/astro/moon/limb" +) + +func limbBruteMargins(contact lunarLimbContact) (outer, cover, inside float64) { + outer, cover, inside = math.Inf(-1), math.Inf(1), math.Inf(1) + step := contact.frame.stepDeg / 4 + start := math.Ceil(contact.view.angle/step)*step - contact.view.angle + for offset := start - 180; offset < 180; offset += step { + radius, ok := contact.radius(offset, true) + if !ok { + continue + } + // 远交点为负的射线背向日面,不是真实交点;月心在日面外时远交点为正即近交点为正。 + near, far, hit := contact.rayChord(offset) + if hit && far > 0 { + outer = math.Max(outer, radius-near) + cover = math.Min(cover, radius-far) + } + if _, far, hit := contact.rayChord(offset + 180); hit && far > 0 { + if radius, ok := contact.radius(offset+180, true); ok { + inside = math.Min(inside, far-radius) + } + } + } + return +} + +func TestLunarLimbContactMatchesBruteForce(t *testing.T) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + params := solarEclipseModelParams(SolarEclipseModelNASABulletinSplitK, SolarEclipseSunRadiusStandard) + params.lunarLimb = limb.Default() + greatest := localSolarEclipseGreatest(solarEclipseAnchorNewMoon(seed), 116.2310*rad, 39.7215*rad, 0, params) + const tolerance = 1e-4 / 3600 * rad + for _, dtSec := range []float64{-60, -20, -5, 0, 5, 20, 60} { + context := newLocalSolarEclipseStateContext(greatest+dtSec/86400, params) + for _, latDeg := range []float64{39.70, 39.7215, 39.73, 39.74} { + observer := localSolarEclipseObserverXYZ(context.gst, 116.2310*rad, latDeg*rad, 0) + moon := [3]float64{context.moonXYZ[0] - observer[0], context.moonXYZ[1] - observer[1], context.moonXYZ[2] - observer[2]} + sun := [3]float64{context.sunXYZ[0] - observer[0], context.sunXYZ[1] - observer[1], context.sunXYZ[2] - observer[2]} + view, ok := context.limbFrame.viewToward(moon, sun) + if !ok { + t.Fatal("view failed") + } + state := context.stateAt(116.2310*rad, latDeg*rad, 0) + contact := lunarLimbContact{frame: context.limbFrame, view: view, distanceKM: math.Sqrt(lunarLimbDot(moon, moon)), + separation: state.separationRad, targetRadius: state.sunRadiusRad} + outer, cover, _ := limbBruteMargins(contact) + low, high, _ := contact.bounds() + relief := high - low + d, sunRadius := state.separationRad, state.sunRadiusRad + // 门控区只保证间隙符号,门控外须与暴力判据逐值一致。 + gotOuter, _ := contact.outerRadius() + wantOuterGap := -outer + if gotGap := d - gotOuter - sunRadius; d > sunRadius && math.Abs(wantOuterGap) < relief { + if math.Abs(gotGap-wantOuterGap) > tolerance { + t.Errorf("dt %.0f lat %.4f: outer gap %.4f\" want %.4f\"", dtSec, latDeg, gotGap/rad*3600, wantOuterGap/rad*3600) + } + } else if (gotGap > 0) != (wantOuterGap > 0) { + t.Errorf("dt %.0f lat %.4f: outer gap sign %.4f\" want %.4f\"", dtSec, latDeg, gotGap/rad*3600, wantOuterGap/rad*3600) + } + gotInner, _ := contact.innerRadius(true) + gotGap := d - math.Abs(gotInner-sunRadius) + wantGap := math.Min(-cover, d) + if math.Abs(cover) < relief { + if math.Abs(gotGap-wantGap) > tolerance { + t.Errorf("dt %.0f lat %.4f: inner gap %.4f\" want %.4f\"", dtSec, latDeg, gotGap/rad*3600, wantGap/rad*3600) + } + } else if (gotGap > 0) != (wantGap > 0) { + t.Errorf("dt %.0f lat %.4f: inner gap sign %.4f\" want %.4f\"", dtSec, latDeg, gotGap/rad*3600, wantGap/rad*3600) + } + } + } +} + +func limbPointInBand(segments [][]SolarEclipsePathPoint, lon, lat float64) bool { + inside := false + for _, ring := range segments { + for i, j := 0, len(ring)-1; i < len(ring); j, i = i, i+1 { + a, b := ring[i], ring[j] + if (a.Latitude > lat) != (b.Latitude > lat) && + lon < (b.Longitude-a.Longitude)*(lat-a.Latitude)/(b.Latitude-a.Latitude)+a.Longitude { + inside = !inside + } + } + } + return inside +} + +func TestLimbCentralBandMatchesLocalTotality(t *testing.T) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + profile := limb.Default() + path := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: profile}, nil) + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK, LunarLimb: profile} + const lon, marginDeg = 116.2310, 0.5 / 111.2 + for lat := 39.60; lat <= 39.85; lat += 0.005 { + inBand := limbPointInBand(path.CentralBandSegments, lon, lat) + if inBand != limbPointInBand(path.CentralBandSegments, lon, lat-marginDeg) || + inBand != limbPointInBand(path.CentralBandSegments, lon, lat+marginDeg) { + continue + } + local := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, options, nil) + if local.HasCentral != inBand { + t.Errorf("lat %.4f: local central %v, band %v", lat, local.HasCentral, inBand) + } + } + local := LocalSolarEclipseWithOptionsWithInstance(seed, lon, 39.7215, 0, options, nil) + if local.HasCentral || limbPointInBand(path.CentralBandSegments, lon, 39.7215) { + t.Errorf("39.7215: local central %v must agree with band (outside)", local.HasCentral) + } +} + +// limbTestContact 2035-09-02 北京附近某一时刻的月缘接触上下文,日面半径与角距可替换以构造环食。 +func limbTestContact(t *testing.T) lunarLimbContact { + t.Helper() + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + params := solarEclipseModelParams(SolarEclipseModelNASABulletinSplitK, SolarEclipseSunRadiusStandard) + params.lunarLimb = limb.Default() + greatest := localSolarEclipseGreatest(solarEclipseAnchorNewMoon(seed), 116.2310*rad, 39.7215*rad, 0, params) + context := newLocalSolarEclipseStateContext(greatest, params) + observer := localSolarEclipseObserverXYZ(context.gst, 116.2310*rad, 39.7215*rad, 0) + moon := [3]float64{context.moonXYZ[0] - observer[0], context.moonXYZ[1] - observer[1], context.moonXYZ[2] - observer[2]} + sun := [3]float64{context.sunXYZ[0] - observer[0], context.sunXYZ[1] - observer[1], context.sunXYZ[2] - observer[2]} + view, ok := context.limbFrame.viewToward(moon, sun) + if !ok { + t.Fatal("view failed") + } + state := context.stateAt(116.2310*rad, 39.7215*rad, 0) + return lunarLimbContact{frame: context.limbFrame, view: view, distanceKM: math.Sqrt(lunarLimbDot(moon, moon)), + separation: state.separationRad, targetRadius: state.sunRadiusRad} +} + +func TestLunarLimbAnnularAndUnrangedMatchBruteForce(t *testing.T) { + base := limbTestContact(t) + const tolerance = 1e-4 / 3600 * rad + meanRadius := math.Asin(1737.4 / base.distanceKM) + unranged := *base.frame + unranged.ranged = false + for _, ranged := range []bool{true, false} { + for _, separationArcsec := range []float64{0, 1, 5, 12, 20} { + contact := base + if !ranged { + contact.frame = &unranged + } + contact.targetRadius = meanRadius + 6/3600.0*rad + contact.separation = separationArcsec / 3600 * rad + _, _, inside := limbBruteMargins(contact) + got, ok := contact.innerRadius(false) + if !ok { + t.Fatalf("ranged %v d %.0f\": annular radius failed", ranged, separationArcsec) + } + // 环食等效内半径必须小于日面半径,否则食分会大于 1;只比间隙看不出 R 关于 s 的镜像错误。 + if got > contact.targetRadius { + t.Errorf("ranged %v d %.0f\": annular radius %.4f\" exceeds the solar radius %.4f\"", ranged, separationArcsec, got/rad*3600, contact.targetRadius/rad*3600) + } + gotGap := contact.separation - math.Abs(got-contact.targetRadius) + wantGap := math.Min(-inside, contact.separation) + if math.Abs(gotGap-wantGap) > tolerance && !(gotGap > 0 && wantGap > 0) { + t.Errorf("ranged %v d %.0f\": annular gap %.4f\" want %.4f\"", ranged, separationArcsec, gotGap/rad*3600, wantGap/rad*3600) + } + if !ranged { + contact.targetRadius = meanRadius - 6/3600.0*rad + contact.separation = (separationArcsec + 1900) / 3600 * rad + outer, _, _ := limbBruteMargins(contact) + got, ok := contact.outerRadius() + if !ok { + t.Fatal("unranged outer failed") + } + if gap := contact.separation - got - contact.targetRadius; math.Abs(gap+outer) > tolerance { + t.Errorf("unranged d %.0f\": outer gap %.4f\" want %.4f\"", separationArcsec, gap/rad*3600, -outer/rad*3600) + } + } + } + } +} + +func TestLimbCentralLimitMatchesLocalLimit(t *testing.T) { + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + profile := limb.Default() + path := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: profile}, nil) + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK, LunarLimb: profile} + const lon = 116.2310 + bisect := func(inside func(float64) bool) float64 { + low, high := 39.5, 39.9 + for i := 0; i < 40; i++ { + mid := (low + high) / 2 + if inside(mid) { + high = mid + } else { + low = mid + } + } + return (low + high) / 2 + } + local := bisect(func(lat float64) bool { + return LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, options, nil).HasCentral + }) + band := bisect(func(lat float64) bool { return limbPointInBand(path.CentralBandSegments, lon, lat) }) + if d := math.Abs(local-band) * 111.2; d > 0.5 { + t.Errorf("southern limit: local %.5f, band %.5f, %.2f km apart", local, band, d) + } +} + +func TestLocalSolarEclipseLimbCanFlipTotalToAnnular(t *testing.T) { + seed := Date2JD(time.Date(2023, 4, 20, 0, 0, 0, 0, time.UTC)) + const lon, lat = 170.1481, 4.6940 + circular := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK}, nil) + if circular.Type != SolarEclipseTotal { + t.Fatalf("circular type %v, want total near the hybrid transition", circular.Type) + } + // 常数月缘比圆月面内半径小 2 km:圆月面判全食,月缘下日面在各方向都露出,应为环食。 + innerKM := geodata.EarthEquatorialRadiusKM * solarEclipseUmbralK * localSolarMoonRadiusScale + smaller := &recordingLunarLimb{radiusKM: innerKM - 2} + limbed := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, + SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK, LunarLimb: smaller}, nil) + if limbed.Type != SolarEclipseAnnular || !limbed.HasCentral || !limbed.HasAnnular || limbed.HasTotal { + t.Fatalf("smaller constant limb: type %v central %v annular %v total %v, want annular", limbed.Type, limbed.HasCentral, limbed.HasAnnular, limbed.HasTotal) + } + if limbed.CentralEnd <= limbed.CentralStart { + t.Errorf("annular phase has no duration: %.8f–%.8f", limbed.CentralStart, limbed.CentralEnd) + } +} + +// sectorLunarLimb 只在以 center 为中心、半宽 halfWidth 的位置角扇区内给出剖面值,并报告整张表的半径范围。 +type sectorLunarLimb struct { + center, halfWidth float64 + profile *limb.Profile +} + +func (sector sectorLunarLimb) LimbRadiusKM(lon, lat, angle float64) (float64, bool) { + if math.Abs(math.Mod(angle-sector.center+540, 360)-180) > sector.halfWidth { + return 0, false + } + return sector.profile.LimbRadiusKM(lon, lat, angle) +} + +func (sector sectorLunarLimb) RadiusRangeKM() (float64, float64) { + return sector.profile.RadiusRangeKM() +} + +func (sector sectorLunarLimb) AngleStepDeg() float64 { return sector.profile.AngleStepDeg() } + +func TestLocalSolarEclipseStateSeparatesOuterAndInnerLimb(t *testing.T) { + params := solarEclipseModelParams(SolarEclipseModelNASABulletinSplitK, SolarEclipseSunRadiusStandard) + seed := Date2JD(time.Date(2035, 9, 2, 0, 0, 0, 0, time.UTC)) + const lon, lat = 116.2310 * rad, 39.70 * rad + // 食甚附近月盘深入日面:外切间隙远离零点被门控,只查日心方向;内切接近零点必须全方向扫描。 + tt := localSolarEclipseGreatest(solarEclipseAnchorNewMoon(seed), lon, lat, 0, params) + circular := newLocalSolarEclipseStateContext(tt, params).stateAt(lon, lat, 0) + params.lunarLimb = limb.Default() + full := newLocalSolarEclipseStateContext(tt, params) + observer := localSolarEclipseObserverXYZ(full.gst, lon, lat, 0) + view, ok := full.limbFrame.viewToward( + [3]float64{full.moonXYZ[0] - observer[0], full.moonXYZ[1] - observer[1], full.moonXYZ[2] - observer[2]}, + [3]float64{full.sunXYZ[0] - observer[0], full.sunXYZ[1] - observer[1], full.sunXYZ[2] - observer[2]}) + if !ok { + t.Fatal("view failed") + } + params.lunarLimb = sectorLunarLimb{center: view.angle, halfWidth: 2, profile: limb.Default()} + state := newLocalSolarEclipseStateContext(tt, params).stateAt(lon, lat, 0) + if !state.outerLimbApplied || state.limbApplied { + t.Fatalf("outerLimbApplied %v limbApplied %v, want outer only", state.outerLimbApplied, state.limbApplied) + } + if state.moonInnerRadiusRad != circular.moonInnerRadiusRad || state.moonOuterRadiusRad == circular.moonOuterRadiusRad { + t.Errorf("inner radius must stay circular and outer must move: inner %.9g vs %.9g, outer %.9g vs %.9g", + state.moonInnerRadiusRad, circular.moonInnerRadiusRad, state.moonOuterRadiusRad, circular.moonOuterRadiusRad) + } +} diff --git a/basic/lunar_limb_test.go b/basic/lunar_limb_test.go new file mode 100644 index 0000000..b54cf39 --- /dev/null +++ b/basic/lunar_limb_test.go @@ -0,0 +1,489 @@ +package basic + +import ( + "math" + "testing" + "time" + + "b612.me/astro/internal/geodata" + "b612.me/astro/moon/limb" +) + +// recordingLunarLimb 记录最后一次查询参数,返回固定半径。 +type recordingLunarLimb struct { + lon, lat, angle float64 + radiusKM float64 +} + +func (limb *recordingLunarLimb) LimbRadiusKM(lon, lat, angle float64) (float64, bool) { + limb.lon, limb.lat, limb.angle = lon, lat, angle + return limb.radiusKM, true +} + +func lunarLimbAngleDiff(a, b float64) float64 { + d := math.Mod(a-b, 360) + if d > 180 { + d -= 360 + } + if d < -180 { + d += 360 + } + return d +} + +// 地心观测者所见的月面经纬必须还原该时刻的总天平动;目标位于月球北极投影方向时位置角为 0, +// 向天东转 90° 时为 90°。 +func TestLunarLimbFrameRoundTrip(t *testing.T) { + for _, jde := range []float64{2460409.25, 2451545.0, 2462000.7} { + _, moon := solarEclipseSunMoonEquatorial(jde) + moonXYZ := solarEclipseLLRToXYZ(moon[0], moon[1], moon[2]) + limb := &recordingLunarLimb{radiusKM: 1737.4} + frame := newLunarLimbFrame(limb, jde, moonXYZ) + if !frame.valid { + t.Fatalf("jde %v: frame invalid", jde) + } + physical := MoonPhysical(jde) + moonUnit, _ := lunarLimbNormalize(moonXYZ) + north, east, _ := lunarLimbSkyBasis(moonUnit) + + for _, offset := range []float64{0, 90, 200} { + pa := (physical.PositionAngle + offset) * rad + // 目标放在月心旁 0.5° 处、位置角为 P + offset。 + sky := lunarLimbCombine(math.Cos(pa), north, math.Sin(pa), east) + target := lunarLimbCombine(math.Cos(0.5*rad), moonUnit, math.Sin(0.5*rad), sky) + target = [3]float64{target[0] * 1.5e8, target[1] * 1.5e8, target[2] * 1.5e8} + if _, ok := frame.radiusKM(moonXYZ, target, false); !ok { + t.Fatalf("jde %v: radiusKM failed", jde) + } + if d := lunarLimbAngleDiff(limb.lon, physical.LibrationLongitude); math.Abs(d) > 1e-6 { + t.Errorf("jde %v: libration longitude %.9f, want %.9f", jde, limb.lon, physical.LibrationLongitude) + } + if d := limb.lat - physical.LibrationLatitude; math.Abs(d) > 1e-6 { + t.Errorf("jde %v: libration latitude %.9f, want %.9f", jde, limb.lat, physical.LibrationLatitude) + } + if d := lunarLimbAngleDiff(limb.angle, -offset); math.Abs(d) > 1e-6 { + t.Errorf("jde %v offset %v: position angle %.9f", jde, offset, limb.angle) + } + if _, ok := frame.radiusKM(moonXYZ, target, true); !ok { + t.Fatalf("jde %v: away radiusKM failed", jde) + } + if d := lunarLimbAngleDiff(limb.angle, 180-offset); math.Abs(d) > 1e-6 { + t.Errorf("jde %v offset %v: away position angle %.9f", jde, offset, limb.angle) + } + } + } +} + +// 参考球半径等于 k·R⊕ 的常数月缘只能让结果在末位附近变化,用来确认替换路径本身不引入偏差。 +func TestLocalSolarEclipseConstantLimbMatchesCircular(t *testing.T) { + const seed, lon, lat = 2460409.25, -96.8, 32.8 // 2024-04-08,达拉斯附近 + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK} + circular := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, options, nil) + if !circular.HasTotal { + t.Fatalf("expected total eclipse, got %v", circular.Type) + } + options.LunarLimb = &recordingLunarLimb{ + radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseIAUSingleRadiusK * localSolarMoonRadiusScale, + } + limbed := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, options, nil) + for _, pair := range [][2]float64{ + {circular.PartialStart, limbed.PartialStart}, + {circular.CentralStart, limbed.CentralStart}, + {circular.CentralEnd, limbed.CentralEnd}, + {circular.PartialEnd, limbed.PartialEnd}, + } { + if d := math.Abs(pair[0]-pair[1]) * 86400; d > 1e-3 { + t.Errorf("contact moved by %.6f s with a constant limb", d) + } + } +} + +func TestLunarEclipseConstantLimbMatchesCircular(t *testing.T) { + limb := &recordingLunarLimb{radiusKM: lunarMoonRadiusScale} + for _, chauvenet := range []bool{false, true} { + circular := LunarEclipseDanjonWithInstance(2460926.0, nil) + if chauvenet { + circular = LunarEclipseChauvenetWithInstance(2460926.0, nil) + } + limbed := LunarEclipseWithLunarLimb(circular, limb, nil) + for _, pair := range [][2]float64{ + {circular.PenumbralStart, limbed.PenumbralStart}, + {circular.PartialStart, limbed.PartialStart}, + {circular.TotalStart, limbed.TotalStart}, + {circular.TotalEnd, limbed.TotalEnd}, + {circular.PartialEnd, limbed.PartialEnd}, + {circular.PenumbralEnd, limbed.PenumbralEnd}, + } { + if d := math.Abs(pair[0]-pair[1]) * 86400; d > 0.05 { + t.Errorf("chauvenet=%v: contact moved by %.4f s with a constant limb", chauvenet, d) + } + } + if limbed.Maximum != circular.Maximum || limbed.Magnitude != circular.Magnitude { + t.Error("maximum or magnitude changed") + } + } + if got := LunarEclipseWithLunarLimb(LunarEclipseDanjonWithInstance(2460926.0, nil), nil, nil); got != LunarEclipseDanjonWithInstance(2460926.0, nil) { + t.Error("nil limb must return the input unchanged") + } +} + +func TestOccultationConstantLimbMatchesCircular(t *testing.T) { + limb := &recordingLunarLimb{radiusKM: moonEquatorialRadiusKM} + start := time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC) + end := time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC) + observer := Observer{Longitude: -30.072, Latitude: 16.21} + plain, err := FindPlanetOccultationsWithInstance(start, end, OccultationSaturn, + observer.Longitude, observer.Latitude, 0, OccultationSearchOptions{}, nil) + if err != nil || len(plain) != 1 { + t.Fatalf("circular planet search: %v, %d events", err, len(plain)) + } + limbed, err := FindPlanetOccultationsWithInstance(start, end, OccultationSaturn, + observer.Longitude, observer.Latitude, 0, OccultationSearchOptions{LunarLimb: limb}, nil) + if err != nil || len(limbed) != 1 { + t.Fatalf("limb planet search: %v, %d events", err, len(limbed)) + } + a, b := plain[0], limbed[0] + for i, pair := range [][2]time.Time{ + {a.ExternalImmersion, b.ExternalImmersion}, {a.InternalImmersion, b.InternalImmersion}, + {a.InternalEmersion, b.InternalEmersion}, {a.ExternalEmersion, b.ExternalEmersion}, + } { + if d := math.Abs(pair[0].Sub(pair[1]).Seconds()); d > 0.01 { + t.Errorf("planet contact %d moved by %.4f s with a constant limb", i, d) + } + } + if !a.Greatest.Equal(b.Greatest) || a.Type != b.Type { + t.Error("greatest or type changed") + } +} + +func TestGlobalSolarEclipseConstantLimbMatchesCircular(t *testing.T) { + // IAU Single-K 的内外 k 相同,常数月缘可同时复现外切与内切半径。 + limb := &recordingLunarLimb{radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseIAUSingleRadiusK * localSolarMoonRadiusScale} + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK} + for _, seed := range []float64{2460409.0, 2460232.0} { + circular := SolarEclipseWithOptionsWithInstance(seed, options, nil) + withLimb := options + withLimb.LunarLimb = limb + limbed := SolarEclipseWithOptionsWithInstance(seed, withLimb, nil) + for i, pair := range [][2]float64{ + {circular.PartialBeginOnEarth, limbed.PartialBeginOnEarth}, + {circular.PartialEndOnEarth, limbed.PartialEndOnEarth}, + } { + if d := math.Abs(pair[0]-pair[1]) * 86400; d > 0.05 { + t.Errorf("seed %.0f contact %d moved by %.4f s with a constant limb", seed, i, d) + } + } + if limbed.GreatestEclipse != circular.GreatestEclipse || limbed.Type != circular.Type || + limbed.CentralBeginOnEarth != circular.CentralBeginOnEarth || limbed.PathWidthKM != circular.PathWidthKM { + t.Errorf("seed %.0f: non-contact fields changed", seed) + } + if d := math.Abs(limbed.CentralDurationDays-circular.CentralDurationDays) * 86400; d > 0.05 { + t.Errorf("seed %.0f: central duration moved by %.4f s with a constant limb", seed, d) + } + } +} + +func TestCentralPathConstantLimbKeepsLimits(t *testing.T) { + limb := &recordingLunarLimb{radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseUmbralK * localSolarMoonRadiusScale} + for _, seed := range []float64{2460409.0, 2460232.0} { + circular := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{}, nil) + limbed := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: limb}, nil) + if len(circular.NorthernLimit) == 0 || len(limbed.NorthernLimit) != len(circular.NorthernLimit) || + len(limbed.SouthernLimit) != len(circular.SouthernLimit) { + t.Fatalf("seed %.0f: limit lengths %d/%d vs %d/%d", seed, + len(limbed.NorthernLimit), len(limbed.SouthernLimit), len(circular.NorthernLimit), len(circular.SouthernLimit)) + } + worst := 0.0 + for _, pair := range [][2][]SolarEclipsePathPoint{{circular.NorthernLimit, limbed.NorthernLimit}, {circular.SouthernLimit, limbed.SouthernLimit}} { + for i := range pair[0] { + d := math.Hypot((pair[0][i].Longitude-pair[1][i].Longitude)*math.Cos(pair[0][i].Latitude*rad), pair[0][i].Latitude-pair[1][i].Latitude) * 111.2 + worst = math.Max(worst, d) + } + } + if worst > 0.05 { + t.Errorf("seed %.0f: constant limb moved a limit by %.3f km", seed, worst) + } + } +} + +func TestOccultationPathConstantLimbKeepsLimits(t *testing.T) { + limb := &recordingLunarLimb{radiusKM: moonEquatorialRadiusKM} + star := StarCoordinate{ + ID: "HR 4799", RA: 189.1975, Dec: -5.831944444444, + Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -28, ProperMotionDecMasPerYear: -18, + } + start := time.Date(2025, 6, 4, 16, 0, 0, 0, time.UTC) + end := time.Date(2025, 6, 5, 16, 0, 0, 0, time.UTC) + options := OccultationPathOptions{Step: 5 * time.Minute, TargetSpacingKM: 200, DisableFootprints: true, DisableRiseSet: true} + circular, err := FindStarOccultationPathsWithInstance(start, end, star, options, nil) + if err != nil || len(circular) != 1 { + t.Fatalf("circular: %v %d", err, len(circular)) + } + options.LunarLimb = limb + limbed, err := FindStarOccultationPathsWithInstance(start, end, star, options, nil) + if err != nil || len(limbed) != 1 { + t.Fatalf("limbed: %v %d", err, len(limbed)) + } + worst := 0.0 + for _, pair := range [][2][]OccultationPathPoint{{circular[0].NorthernLimit, limbed[0].NorthernLimit}, {circular[0].SouthernLimit, limbed[0].SouthernLimit}} { + if len(pair[0]) == 0 || len(pair[0]) != len(pair[1]) { + t.Fatalf("limit lengths %d vs %d", len(pair[0]), len(pair[1])) + } + for i := range pair[0] { + d := math.Hypot((pair[0][i].Longitude-pair[1][i].Longitude)*math.Cos(pair[0][i].Latitude*rad), pair[0][i].Latitude-pair[1][i].Latitude) * 111.2 + worst = math.Max(worst, d) + } + } + if worst > 0.05 { + t.Errorf("constant limb moved a graze limit by %.3f km", worst) + } + if limbed[0].LunarLimbApplied { + t.Fatal("constant circular limb set LunarLimbApplied = true") + } +} + +func TestCentralPathConstantLimbKeepsWidthsAndBand(t *testing.T) { + limb := &recordingLunarLimb{radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseUmbralK * localSolarMoonRadiusScale} + for _, seed := range []float64{2460409.0, 2460232.0} { + circular := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{}, nil) + limbed := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: limb}, nil) + if len(limbed.CenterLine) != len(circular.CenterLine) || len(limbed.CentralBandSegments) != len(circular.CentralBandSegments) { + t.Fatalf("seed %.0f: sample counts changed", seed) + } + worstWidth := 0.0 + for i := range circular.CenterLine { + worstWidth = math.Max(worstWidth, math.Abs(limbed.CenterLine[i].WidthKM-circular.CenterLine[i].WidthKM)) + } + if worstWidth > 0.1 { + t.Errorf("seed %.0f: constant limb moved a width by %.3f km", seed, worstWidth) + } + if d := math.Abs(limbed.Greatest.WidthKM - circular.Greatest.WidthKM); d > 0.1 { + t.Errorf("seed %.0f: greatest width moved by %.3f km", seed, d) + } + if d := math.Abs(limbed.MaxCentralDurationDays-circular.MaxCentralDurationDays) * 86400; d > 0.05 { + t.Errorf("seed %.0f: max duration moved by %.4f s", seed, d) + } + worstBand := 0.0 + for s := range circular.CentralBandSegments { + if len(limbed.CentralBandSegments[s]) != len(circular.CentralBandSegments[s]) { + t.Fatalf("seed %.0f: band segment %d length changed", seed, s) + } + for i, a := range circular.CentralBandSegments[s] { + b := limbed.CentralBandSegments[s][i] + d := math.Hypot((a.Longitude-b.Longitude)*math.Cos(a.Latitude*rad), a.Latitude-b.Latitude) * 111.2 + worstBand = math.Max(worstBand, d) + } + } + if worstBand > 0.1 { + t.Errorf("seed %.0f: constant limb moved a band vertex by %.3f km", seed, worstBand) + } + } +} + +// failingLunarLimb 处处无值:月缘修正必须整体退回圆月面且不置 LunarLimbApplied。 +type failingLunarLimb struct{} + +func (failingLunarLimb) LimbRadiusKM(_, _, _ float64) (float64, bool) { return 0, false } + +func TestFailingLimbKeepsEclipsesCircular(t *testing.T) { + const seed, lon, lat = 2460409.25, -96.8, 32.8 // 2024-04-08,达拉斯附近 + limb := failingLunarLimb{} + for _, model := range []SolarEclipseRadiusModel{SolarEclipseModelNASABulletinSplitK, SolarEclipseModelIAUSingleK} { + options := SolarEclipseOptions{RadiusModel: model} + withLimb := options + withLimb.LunarLimb = limb + + circularLocal := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, options, nil) + limbedLocal := LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, 0, withLimb, nil) + if limbedLocal != circularLocal { + t.Errorf("model %v: failing limb changed the local result", model) + } + + circularGlobal := SolarEclipseWithOptionsWithInstance(seed, options, nil) + limbedGlobal := SolarEclipseWithOptionsWithInstance(seed, withLimb, nil) + if limbedGlobal.LunarLimbApplied || + limbedGlobal.PartialBeginOnEarth != circularGlobal.PartialBeginOnEarth || + limbedGlobal.PartialEndOnEarth != circularGlobal.PartialEndOnEarth || + limbedGlobal.CentralDurationDays != circularGlobal.CentralDurationDays { + t.Errorf("model %v: failing limb changed the global result or set LunarLimbApplied", model) + } + } + + circularLunar := LunarEclipseDanjonWithInstance(2460926.0, nil) + if got := LunarEclipseWithLunarLimb(circularLunar, limb, nil); got != circularLunar { + t.Error("failing limb changed the lunar eclipse result") + } +} + +func TestPartialBoundaryLimbCorrection(t *testing.T) { + const seed = 2460409.25 // 2024-04-08 + circularOptions := SolarEclipsePartialFootprintOptions{} + circular := solarEclipsePartialFootprints(seed, SolarEclipseModelIAUSingleK, circularOptions, nil) + if len(circular.PartialBandContours) == 0 || len(circular.RiseSetCurves) == 0 { + t.Fatal("circular partial boundary missing") + } + samePoints := func(a, b []SolarEclipsePathPoint, toleranceKM float64) float64 { + worst := 0.0 + for index := range a { + km := math.Hypot(signedAngleDifference(a[index].Longitude, b[index].Longitude)*111.19*math.Cos(a[index].Latitude*rad), + (a[index].Latitude-b[index].Latitude)*111.19) + worst = math.Max(worst, km) + } + return worst + } + compare := func(name string, limb LunarLimb, wantApplied bool, maxShiftKM float64) { + options := circularOptions + options.LunarLimb = limb + limbed := solarEclipsePartialFootprints(seed, SolarEclipseModelIAUSingleK, options, nil) + if limbed.PartialBoundaryLimbApplied != wantApplied { + t.Errorf("%s: PartialBoundaryLimbApplied = %v, want %v", name, limbed.PartialBoundaryLimbApplied, wantApplied) + } + if limbed.RiseSetTopologyDegraded { + t.Errorf("%s: rise/set topology degraded", name) + } + if len(limbed.PartialBandContours) != len(circular.PartialBandContours) || len(limbed.RiseSetCurves) != len(circular.RiseSetCurves) { + t.Fatalf("%s: boundary structure changed", name) + } + worst := 0.0 + for index := range circular.PartialBandContours { + worst = math.Max(worst, samePoints(circular.PartialBandContours[index], limbed.PartialBandContours[index], 0)) + } + for index, curve := range circular.RiseSetCurves { + for segment := range curve.Segments { + shift := samePoints(curve.Segments[segment], limbed.RiseSetCurves[index].Segments[segment], 0) + if curve.Phase == RiseSetPhaseGreatest && shift != 0 { + t.Errorf("%s: greatest rise/set curve moved by %.3f km", name, shift) + } + worst = math.Max(worst, shift) + } + } + if worst > maxShiftKM { + t.Errorf("%s: boundary moved by %.3f km, want <= %.3f km", name, worst, maxShiftKM) + } + if wantApplied && worst < 0.5 { + t.Errorf("%s: boundary moved only %.3f km", name, worst) + } + } + compare("failing", failingLunarLimb{}, false, 0) + compare("constant", &recordingLunarLimb{ + radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseIAUSingleRadiusK * localSolarMoonRadiusScale, + }, false, 0.05) + compare("profile", sawtoothLunarLimb{}, true, limbLimitMaxShiftKM) +} + +// sawtoothLunarLimb 以位置角 10° 为周期在参考半径上叠加 ±3 km 起伏,模拟月缘山谷。 +type sawtoothLunarLimb struct{} + +func (sawtoothLunarLimb) LimbRadiusKM(_, _, angle float64) (float64, bool) { + phase := math.Mod(angle, 10) / 10 + return geodata.EarthEquatorialRadiusKM*solarEclipseIAUSingleRadiusK*localSolarMoonRadiusScale + 6*math.Abs(phase-0.5) - 1.5, true +} + +// TestGlobalSolarEclipseLimbContactMatchesTime 检查月缘修正后的偏食始末接触点与修正时刻同源: +// 接触点必须落在该时刻重解的半影外切点上,而不是沿地球边缘扫描网格上的样点。 +func TestGlobalSolarEclipseLimbContactMatchesTime(t *testing.T) { + for _, seed := range []float64{2460409.25, 2460232.25, 2461275.25} { + options := SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK, LunarLimb: limb.Default()} + result := SolarEclipseWithOptionsWithInstance(seed, options, nil) + if !result.LunarLimbApplied { + t.Fatalf("seed %.2f: the default limb profile changed nothing", seed) + } + solver := newSolarEclipseSolverWithOptions(CalcMoonSHByJDE(seed, 0), options, nil) + for _, pair := range []struct { + name string + jde float64 + contact SolarEclipseLimbContact + }{ + {"P1", result.PartialBeginOnEarth, result.PartialBeginContact}, + {"P4", result.PartialEndOnEarth, result.PartialEndContact}, + } { + if !pair.contact.Defined { + t.Errorf("seed %.2f %s: the corrected contact location is missing", seed, pair.name) + continue + } + point, ok := solver.shadowContactPointAt(pair.jde, solarEclipsePenumbralShadow, false) + if !ok { + t.Errorf("seed %.2f %s: no penumbral tangent point at the corrected time", seed, pair.name) + continue + } + longitude := signedAngleDifference(pair.contact.Longitude, point.Longitude) * math.Cos(point.Latitude*rad) + if d := math.Hypot(longitude, pair.contact.Latitude-point.Latitude) * geodata.EarthKMPerDegree; d > 0.05 { + t.Errorf("seed %.2f %s: contact location is %.3f km from the tangent point at its own time", seed, pair.name, d) + } + } + } +} + +// TestLimbFlagsIgnoreNoiseWithConstantLimb 检查常数月缘等价于圆月面时,各层月缘标志都不因离散残差误报。 +func TestLimbFlagsIgnoreNoiseWithConstantLimb(t *testing.T) { + iau := &recordingLunarLimb{radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseIAUSingleRadiusK * localSolarMoonRadiusScale} + umbral := &recordingLunarLimb{radiusKM: geodata.EarthEquatorialRadiusKM * solarEclipseUmbralK * localSolarMoonRadiusScale} + for _, seed := range []float64{2460409.25, 2460232.25, 2461275.25} { + global := SolarEclipseWithOptionsWithInstance(seed, SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK, LunarLimb: iau}, nil) + if global.LunarLimbApplied || global.PartialBeginContact.Defined || global.PartialEndContact.Defined { + t.Errorf("seed %.2f: constant limb reported a global correction", seed) + } + central := SolarEclipseCentralPathNASABulletinSplitKWithInstance(seed, SolarEclipsePathOptions{LunarLimb: umbral}, nil) + if central.CentralLimitsLimbApplied { + t.Errorf("seed %.2f: constant limb reported moved central limits", seed) + } + local := LocalSolarEclipseWithOptionsWithInstance(seed, -96.8, 32.8, 0, SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK, LunarLimb: iau}, nil) + if local.LunarLimbApplied { + t.Errorf("seed %.2f: constant limb reported a local correction", seed) + } + } + real := SolarEclipseCentralPathNASABulletinSplitKWithInstance(2460409.25, SolarEclipsePathOptions{LunarLimb: limb.Default()}, nil) + if !real.CentralLimitsLimbApplied { + t.Error("default limb profile must move the central limits") + } +} + +// TestLocalSolarEclipseLimbFlagCoversMagnitude 检查只改变食分、遮蔽率而不改变接触时刻时站心标志仍为真。 +func TestLocalSolarEclipseLimbFlagCoversMagnitude(t *testing.T) { + circular := LocalSolarEclipseResult{Type: SolarEclipsePartial, PartialStart: 1, PartialEnd: 2, Magnitude: 0.5, Obscuration: 0.4} + limbed := circular + if localSolarEclipseLimbChanged(limbed, circular) { + t.Fatal("identical results reported a change") + } + limbed.Magnitude += 1e-12 + if localSolarEclipseLimbChanged(limbed, circular) { + t.Error("floating-point noise in magnitude reported a change") + } + limbed.Magnitude = circular.Magnitude + 1e-6 + if !localSolarEclipseLimbChanged(limbed, circular) { + t.Error("a visible magnitude change was not reported") + } + limbed = circular + limbed.Obscuration += 1e-6 + if !localSolarEclipseLimbChanged(limbed, circular) { + t.Error("a visible obscuration change was not reported") + } + limbed = circular + limbed.Type = SolarEclipseAnnular + if !localSolarEclipseLimbChanged(limbed, circular) { + t.Error("a type change was not reported") + } +} + +// steppedLunarLimb 报告指定网格步长的常半径剖面。 +type steppedLunarLimb struct{ step float64 } + +func (limb steppedLunarLimb) LimbRadiusKM(lon, lat, angle float64) (float64, bool) { + return 1737.4, true +} +func (limb steppedLunarLimb) RadiusRangeKM() (float64, float64) { return 1737.4, 1737.4 } +func (limb steppedLunarLimb) AngleStepDeg() float64 { return limb.step } + +func TestLunarLimbFrameRejectsTinyStep(t *testing.T) { + _, moon := solarEclipseSunMoonEquatorial(2460409.25) + moonXYZ := solarEclipseLLRToXYZ(moon[0], moon[1], moon[2]) + for _, c := range []struct{ step, want float64 }{ + {1e-300, lunarLimbDefaultStepDeg}, {1e-4, lunarLimbDefaultStepDeg}, {0.001, 0.001}, {0.5, 0.5}, {2, lunarLimbDefaultStepDeg}, + } { + frame := newLunarLimbFrame(steppedLunarLimb{c.step}, 2460409.25, moonXYZ) + if !frame.valid || frame.stepDeg != c.want { + t.Errorf("step %g: got %g valid=%v, want %g", c.step, frame.stepDeg, frame.valid, c.want) + } + } +} diff --git a/basic/mars.go b/basic/mars.go index bd6cf4f..5706eaf 100644 --- a/basic/mars.go +++ b/basic/mars.go @@ -125,59 +125,34 @@ func MarsMag(jde float64) float64 { return FloatRound(mag, 2) } -func MarsHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := MarsApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// MarsHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(3, localJD, lon, lat, timezone, -1, inst) } -func MarsAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := MarsApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// MarsAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(3, localJD, lon, lat, timezone, -1, inst) } -func MarsHourAngle(jd, lon, timezone float64) float64 { +// MarsHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - MarsApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - MarsApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func MarsCulminationTime(localJD, lon, timezone float64) float64 { +// MarsCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-MarsHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-MarsHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := MarsHourAngle(localJD, lon, timezone) + currentHourAngle := MarsHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -195,14 +170,22 @@ func MarsCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func MarsRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// MarsRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func MarsSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// MarsSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, MarsCulminationTime, MarsHeight, MarsApparentDec) +func marsRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return MarsCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return MarsHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return MarsApparentDecWithInstance(jde, inst) }) } diff --git a/basic/mars_event_regression_test.go b/basic/mars_event_regression_test.go index 5a2beca..daa1ff2 100644 --- a/basic/mars_event_regression_test.go +++ b/basic/mars_event_regression_test.go @@ -3,7 +3,6 @@ package basic import ( "encoding/json" "math" - "os" "testing" ) @@ -20,10 +19,11 @@ type marsEventBaseline struct { func loadMarsEventBaseline(t *testing.T) marsEventBaseline { t.Helper() - data, err := os.ReadFile("testdata/mars_event_baseline.json") - if err != nil { - t.Fatal(err) + var named []namedEventFunc + for _, c := range marsEventCases() { + named = append(named, namedEventFunc{c.name, c.fn}) } + data := eventBaselineData(t, "testdata/mars_event_baseline.json", named) var baseline marsEventBaseline if err := json.Unmarshal(data, &baseline); err != nil { diff --git a/basic/mars_events.go b/basic/mars_events.go index 775e298..c24d52c 100644 --- a/basic/mars_events.go +++ b/basic/mars_events.go @@ -17,8 +17,10 @@ const ( marsStationHalfWindowDay = 6.0 ) -func marsSunLongitudeDelta(jde, degree float64, filter bool) float64 { - sub := Limit360(Limit360(MarsApparentLo(jde)-HSunApparentLo(jde)) - degree) +func marsSunLongitudeDelta(jde, degree float64, filter bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(MarsApparentLoWithInstance(jde, inst)-HSunApparentLoWithInstance(jde, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -30,8 +32,10 @@ func marsSunLongitudeDelta(jde, degree float64, filter bool) float64 { return sub } -func marsSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { - sub := Limit360(Limit360(MarsApparentLoN(jde, n)-HSunApparentLoN(jde, n)) - degree) +func marsSunLongitudeDeltaN(jde, degree float64, filter bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(MarsApparentLoNWithInstance(jde, n, inst)-HSunApparentLoNWithInstance(jde, n, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -43,8 +47,10 @@ func marsSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { return sub } -func marsRADerivative(jde, val float64) float64 { - sub := MarsApparentRa(jde+val) - MarsApparentRa(jde-val) +func marsRADerivative(jde, val float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := MarsApparentRaWithInstance(jde+val, inst) - MarsApparentRaWithInstance(jde-val, inst) if sub > 180 { sub -= 360 } @@ -54,8 +60,10 @@ func marsRADerivative(jde, val float64) float64 { return sub / (2 * val) } -func marsRADerivativeN(jde, val float64, n int) float64 { - sub := MarsApparentRaN(jde+val, n) - MarsApparentRaN(jde-val, n) +func marsRADerivativeN(jde, val float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := MarsApparentRaNWithInstance(jde+val, n, inst) - MarsApparentRaNWithInstance(jde-val, n, inst) if sub > 180 { sub -= 360 } @@ -65,13 +73,15 @@ func marsRADerivativeN(jde, val float64, n int) float64 { return sub / (2 * val) } -func marsConjunctionFull(jde, degree float64, next uint8) float64 { +func marsConjunctionFull(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := MARS_S_PERIOD / 360 - currentDelta := marsSunLongitudeDelta(jde, degree, false) + currentDelta := marsSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -81,8 +91,8 @@ func marsConjunctionFull(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := marsSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (marsSunLongitudeDelta(prevJDE+0.000005, degree, true) - marsSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := marsSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (marsSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - marsSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -96,13 +106,15 @@ func marsConjunctionFull(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func marsConjunction(jde, degree float64, next uint8) float64 { +func marsConjunction(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := MARS_S_PERIOD / 360 - currentDelta := marsSunLongitudeDelta(jde, degree, false) + currentDelta := marsSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -112,8 +124,8 @@ func marsConjunction(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := marsSunLongitudeDeltaN(prevJDE, degree, true, marsEventSearchN) - longitudeSlope := (marsSunLongitudeDeltaN(prevJDE+0.000005, degree, true, marsEventSearchN) - marsSunLongitudeDeltaN(prevJDE-0.000005, degree, true, marsEventSearchN)) / 0.00001 + longitudeDelta := marsSunLongitudeDeltaN(prevJDE, degree, true, marsEventSearchN, inst) + longitudeSlope := (marsSunLongitudeDeltaN(prevJDE+0.000005, degree, true, marsEventSearchN, inst) - marsSunLongitudeDeltaN(prevJDE-0.000005, degree, true, marsEventSearchN, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= marsPhaseCoarseTolerance { @@ -127,8 +139,8 @@ func marsConjunction(jde, degree float64, next uint8) float64 { converged = false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := marsSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (marsSunLongitudeDelta(prevJDE+0.000005, degree, true) - marsSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := marsSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (marsSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - marsSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -142,39 +154,65 @@ func marsConjunction(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func LastMarsConjunction(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 0, marsConjunction) +// LastMarsConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func NextMarsConjunction(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 0, marsConjunction) +// NextMarsConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func LastMarsOpposition(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 180, marsConjunction) +// LastMarsOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func NextMarsOpposition(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 180, marsConjunction) +// NextMarsOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func NextMarsEasternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 90, marsConjunction) +// NextMarsEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func LastMarsEasternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 90, marsConjunction) +// LastMarsEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func NextMarsWesternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 270, marsConjunction) +// NextMarsWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func LastMarsWesternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 270, marsConjunction) +// LastMarsWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return marsConjunction(jde, degree, next, inst) + }) } -func marsRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool) float64 { +func marsRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(oppositionJD) { return math.NaN() } @@ -182,103 +220,107 @@ func marsRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition startTT := oppositionTT endTT := oppositionTT if searchBeforeOpposition { - easternQuadratureUT := marsConjunction(oppositionTT, 90, 0) + easternQuadratureUT := marsConjunction(oppositionTT, 90, 0, inst) startTT = UTC2TT(easternQuadratureUT) } else { - westernQuadratureUT := marsConjunction(oppositionTT, 270, 1) + westernQuadratureUT := marsConjunction(oppositionTT, 270, 1, inst) endTT = UTC2TT(westernQuadratureUT) } bestJDE := zeroEventInWindow(startTT, endTT, marsStationCoarseStepDay, marsStationHalfWindowDay, 30.0/86400.0, func(jd float64) float64 { - return marsRADerivativeN(jd, marsStationDerivativeStepDay, marsEventSearchN) + return marsRADerivativeN(jd, marsStationDerivativeStepDay, marsEventSearchN, inst) }, func(jd float64) float64 { - return marsRADerivative(jd, marsStationDerivativeStepDay) + return marsRADerivative(jd, marsStationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func NextMarsRetrogradeToPrograde(jde float64) float64 { +// NextMarsRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := marsConjunctionFull(jde, 180, 0) - date := marsRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := marsConjunctionFull(jde, 180, 0, inst) + date := marsRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - nextOppositionJD := marsConjunctionFull(jde, 180, 1) + nextOppositionJD := marsConjunctionFull(jde, 180, 1, inst) if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - date = marsRetrogradeAroundOpposition(nextOppositionJD, false) + date = marsRetrogradeAroundOpposition(nextOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastMarsRetrogradeToPrograde(jde float64) float64 { +// LastMarsRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := marsConjunctionFull(jde, 180, 0) - date := marsRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := marsConjunctionFull(jde, 180, 0, inst) + date := marsRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - previousOppositionJD := marsConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0) + previousOppositionJD := marsConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0, inst) if !isFiniteFloat(previousOppositionJD) { return math.NaN() } - date = marsRetrogradeAroundOpposition(previousOppositionJD, false) + date = marsRetrogradeAroundOpposition(previousOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } return date } -func NextMarsProgradeToRetrograde(jde float64) float64 { +// NextMarsProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMarsProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := marsConjunctionFull(jde, 180, 1) - date := marsRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := marsConjunctionFull(jde, 180, 1, inst) + date := marsRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - followingOppositionJD := marsConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1) + followingOppositionJD := marsConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1, inst) if !isFiniteFloat(followingOppositionJD) { return math.NaN() } - date = marsRetrogradeAroundOpposition(followingOppositionJD, true) + date = marsRetrogradeAroundOpposition(followingOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastMarsProgradeToRetrograde(jde float64) float64 { +// LastMarsProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMarsProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := marsConjunctionFull(jde, 180, 1) - date := marsRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := marsConjunctionFull(jde, 180, 1, inst) + date := marsRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - lastOppositionJD := marsConjunctionFull(jde, 180, 0) + lastOppositionJD := marsConjunctionFull(jde, 180, 0, inst) if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - date = marsRetrogradeAroundOpposition(lastOppositionJD, true) + date = marsRetrogradeAroundOpposition(lastOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } diff --git a/basic/mercury.go b/basic/mercury.go index 6434d7e..4b85f54 100644 --- a/basic/mercury.go +++ b/basic/mercury.go @@ -108,59 +108,34 @@ func MercuryMag(jde float64) float64 { return FloatRound(mag, 2) } -func MercuryHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := MercuryApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// MercuryHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(1, localJD, lon, lat, timezone, -1, inst) } -func MercuryAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := MercuryApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// MercuryAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(1, localJD, lon, lat, timezone, -1, inst) } -func MercuryHourAngle(jd, lon, timezone float64) float64 { +// MercuryHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - MercuryApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - MercuryApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func MercuryCulminationTime(localJD, lon, timezone float64) float64 { +// MercuryCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-MercuryHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-MercuryHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := MercuryHourAngle(localJD, lon, timezone) + currentHourAngle := MercuryHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -178,14 +153,22 @@ func MercuryCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func MercuryRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// MercuryRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func MercurySetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// MercurySetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercurySetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, MercuryCulminationTime, MercuryHeight, MercuryApparentDec) +func mercuryRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return MercuryCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return MercuryHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return MercuryApparentDecWithInstance(jde, inst) }) } diff --git a/basic/mercury_event_regression_test.go b/basic/mercury_event_regression_test.go index 5be0e75..f727d96 100644 --- a/basic/mercury_event_regression_test.go +++ b/basic/mercury_event_regression_test.go @@ -3,7 +3,6 @@ package basic import ( "encoding/json" "math" - "os" "testing" ) @@ -20,10 +19,11 @@ type mercuryEventBaseline struct { func loadMercuryEventBaseline(t *testing.T) mercuryEventBaseline { t.Helper() - data, err := os.ReadFile("testdata/mercury_event_baseline.json") - if err != nil { - t.Fatal(err) + var named []namedEventFunc + for _, c := range mercuryEventCases() { + named = append(named, namedEventFunc{c.name, c.fn}) } + data := eventBaselineData(t, "testdata/mercury_event_baseline.json", named) var baseline mercuryEventBaseline if err := json.Unmarshal(data, &baseline); err != nil { diff --git a/basic/mercury_events.go b/basic/mercury_events.go index 1af1a0d..43fa316 100644 --- a/basic/mercury_events.go +++ b/basic/mercury_events.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -51,11 +50,13 @@ type mercuryConjunctionResult struct { geoLightDays float64 } -func mercuryHelioN(planetIndex int, jde float64, n int) mercuryConjunctionLBR { +func mercuryHelioN(planetIndex int, jde float64, n int, instances ...*Instance) mercuryConjunctionLBR { + inst := instanceValue(instances) + return mercuryConjunctionLBR{ - lo: planet.WherePlanetN(planetIndex, 0, jde, n), - bo: planet.WherePlanetN(planetIndex, 1, jde, n), - r: planet.WherePlanetN(planetIndex, 2, jde, n), + lo: PlanetCoordinateNWithInstance(planetIndex, 0, jde, n, inst), + bo: PlanetCoordinateNWithInstance(planetIndex, 1, jde, n, inst), + r: PlanetCoordinateNWithInstance(planetIndex, 2, jde, n, inst), } } @@ -82,17 +83,21 @@ func mercuryConjunctionAngleDelta(diff float64) float64 { return diff } -func mercuryConjunctionHeliocentricDelta(jde, targetDeg float64, n int) float64 { - planetLo := planet.WherePlanetN(1, 0, jde, n) - earthLo := planet.WherePlanetN(-1, 0, jde, n) +func mercuryConjunctionHeliocentricDelta(jde, targetDeg float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + planetLo := PlanetCoordinateNWithInstance(1, 0, jde, n, inst) + earthLo := PlanetCoordinateNWithInstance(-1, 0, jde, n, inst) return mercuryConjunctionAngleDelta(planetLo - earthLo - targetDeg) } -func mercuryConjunctionDifference(jd float64, n int, targetDeg, sunLightDays, geoLightDays float64) mercuryConjunctionResult { - earthForSun := mercuryHelioN(-1, jd-sunLightDays, n) +func mercuryConjunctionDifference(jd float64, n int, targetDeg, sunLightDays, geoLightDays float64, instances ...*Instance) mercuryConjunctionResult { + inst := instanceValue(instances) + + earthForSun := mercuryHelioN(-1, jd-sunLightDays, n, inst) sunLo := Limit360(earthForSun.lo + 180) - earth := mercuryHelioN(-1, jd-geoLightDays, n) - planetPos := mercuryHelioN(1, jd-geoLightDays, n) + earth := mercuryHelioN(-1, jd-geoLightDays, n, inst) + planetPos := mercuryHelioN(1, jd-geoLightDays, n, inst) geo := mercuryGeocentric(planetPos, earth) return mercuryConjunctionResult{ diff: mercuryConjunctionAngleDelta(geo.lo - sunLo - targetDeg), @@ -101,38 +106,44 @@ func mercuryConjunctionDifference(jd float64, n int, targetDeg, sunLightDays, ge } } -func mercuryConjunctionExactDelta(jde float64) float64 { - return mercuryConjunctionAngleDelta(MercuryApparentLo(jde) - HSunApparentLo(jde)) +func mercuryConjunctionExactDelta(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return mercuryConjunctionAngleDelta(MercuryApparentLoWithInstance(jde, inst) - HSunApparentLoWithInstance(jde, inst)) } -func mercuryConjunctionApproxTT(seed float64, inferior bool) float64 { +func mercuryConjunctionApproxTT(seed float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + heliocentricTarget := 180.0 if inferior { heliocentricTarget = 0 } jde := seed for i := 0; i < 6; i++ { - jde -= mercuryConjunctionHeliocentricDelta(jde, heliocentricTarget, 8) / (360.0 / MERCURY_S_PERIOD) + jde -= mercuryConjunctionHeliocentricDelta(jde, heliocentricTarget, 8, inst) / (360.0 / MERCURY_S_PERIOD) } - startSample := mercuryConjunctionDifference(jde, 8, 0, 0, 0) - nextSample := mercuryConjunctionDifference(jde+mercuryConjunctionDerivativeStepDay, 8, 0, 0, 0) + startSample := mercuryConjunctionDifference(jde, 8, 0, 0, 0, inst) + nextSample := mercuryConjunctionDifference(jde+mercuryConjunctionDerivativeStepDay, 8, 0, 0, 0, inst) diffSlope := mercuryConjunctionAngleDelta(nextSample.diff-startSample.diff) / mercuryConjunctionDerivativeStepDay - refined := mercuryConjunctionDifference(jde, 40, 0, startSample.sunLightDays, startSample.geoLightDays) + refined := mercuryConjunctionDifference(jde, 40, 0, startSample.sunLightDays, startSample.geoLightDays, inst) jde -= refined.diff / diffSlope - final := mercuryConjunctionDifference(jde, -1, 0, refined.sunLightDays, refined.geoLightDays) + final := mercuryConjunctionDifference(jde, -1, 0, refined.sunLightDays, refined.geoLightDays, inst) jde -= final.diff / diffSlope return jde } -func mercuryConjunctionExactTT(seed float64, inferior bool) float64 { - estimateJD := mercuryConjunctionApproxTT(seed, inferior) +func mercuryConjunctionExactTT(seed float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + estimateJD := mercuryConjunctionApproxTT(seed, inferior, inst) converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJD - longitudeDelta := mercuryConjunctionExactDelta(prevJDE) - longitudeSlope := (mercuryConjunctionExactDelta(prevJDE+0.000005) - mercuryConjunctionExactDelta(prevJDE-0.000005)) / 0.00001 + longitudeDelta := mercuryConjunctionExactDelta(prevJDE, inst) + longitudeSlope := (mercuryConjunctionExactDelta(prevJDE+0.000005, inst) - mercuryConjunctionExactDelta(prevJDE-0.000005, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJD = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -153,15 +164,17 @@ func mercuryConjunctionExactTT(seed float64, inferior bool) float64 { // 并且下游的类型化「留」会因此返回错事件、甚至让 Last 返回未来。 // 新实现改成方向性括号扫描:截断级数逐段找异号区间(顺序扫描 ⇒ 一定取最近的一个合), // 再用全项级数在括号内抛光;扫满 640 天仍无括号时有界返回 NaN。 -func mercuryConjunction(jde float64, next uint8) float64 { +func mercuryConjunction(jde float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) { return math.NaN() } - if math.Abs(mercuryConjunctionExactDelta(jde)) <= mercuryConjunctionSameInstantDegrees { + if math.Abs(mercuryConjunctionExactDelta(jde, inst)) <= mercuryConjunctionSameInstantDegrees { best := math.NaN() consider := func(inferior bool) { - eventUT := TT2UTC(mercuryConjunctionExactTT(jde, inferior)) + eventUT := TT2UTC(mercuryConjunctionExactTT(jde, inferior, inst)) if !isFiniteFloat(eventUT) { return } @@ -187,18 +200,18 @@ func mercuryConjunction(jde float64, next uint8) float64 { direction = -1 } leftJDE := jde - leftValue := mercuryConjunctionDeltaN(leftJDE, mercuryEventSearchN) + leftValue := mercuryConjunctionDeltaN(leftJDE, mercuryEventSearchN, inst) if !isFiniteFloat(leftValue) { return math.NaN() } for i := 0; i < mercuryConjunctionScanMaxSteps; i++ { rightJDE := jde + direction*mercuryConjunctionScanStepDay*float64(i+1) - rightValue := mercuryConjunctionDeltaN(rightJDE, mercuryEventSearchN) + rightValue := mercuryConjunctionDeltaN(rightJDE, mercuryEventSearchN, inst) if !isFiniteFloat(rightValue) { return math.NaN() } if leftValue == 0 || rightValue == 0 || leftValue*rightValue < 0 { - return mercuryConjunctionPolish(jde, leftJDE, rightJDE, direction) + return mercuryConjunctionPolish(jde, leftJDE, rightJDE, direction, inst) } leftJDE, leftValue = rightJDE, rightValue } @@ -206,24 +219,28 @@ func mercuryConjunction(jde float64, next uint8) float64 { } // mercuryConjunctionDeltaN 截断级数下的水星-太阳视黄经差(度,[-180,180]),用于方向性括号扫描。 -func mercuryConjunctionDeltaN(jde float64, n int) float64 { - return mercuryConjunctionAngleDelta(MercuryApparentLoN(jde, n) - HSunApparentLoN(jde, n)) +func mercuryConjunctionDeltaN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return mercuryConjunctionAngleDelta(MercuryApparentLoNWithInstance(jde, n, inst) - HSunApparentLoNWithInstance(jde, n, inst)) } // mercuryConjunctionPolish 用全项级数在截断级数给出的括号内抛光。 // 截断误差可能让括号两端在全项函数上同号(罕见),此时沿扫描方向再扩一两个扫描步; // 若仍未被确认(典型情形:查询几乎正好落在合上,截断级数在根两侧的符号与全项不一致), // 退回全项级数的方向扫描,保证有界且不返回 NaN。 -func mercuryConjunctionPolish(jde, leftJDE, rightJDE, direction float64) float64 { +func mercuryConjunctionPolish(jde, leftJDE, rightJDE, direction float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + for attempt := 0; attempt < 3; attempt++ { - leftValue := mercuryConjunctionExactDelta(leftJDE) - rightValue := mercuryConjunctionExactDelta(rightJDE) + leftValue := mercuryConjunctionExactDelta(leftJDE, inst) + rightValue := mercuryConjunctionExactDelta(rightJDE, inst) if !isFiniteFloat(leftValue) || !isFiniteFloat(rightValue) { return math.NaN() } if leftValue == 0 || rightValue == 0 || leftValue*rightValue < 0 { root, ok := eventBracketSecantRoot(leftJDE, rightJDE, leftValue, rightValue, - mercuryConjunctionPolishToleranceDay, mercuryConjunctionExactDelta) + mercuryConjunctionPolishToleranceDay, func(jde float64) float64 { return mercuryConjunctionExactDelta(jde, inst) }) if !ok { return math.NaN() } @@ -235,25 +252,27 @@ func mercuryConjunctionPolish(jde, leftJDE, rightJDE, direction float64) float64 } leftJDE -= mercuryConjunctionScanStepDay } - return mercuryConjunctionFullDirectionalScan(jde, direction) + return mercuryConjunctionFullDirectionalScan(jde, direction, inst) } // mercuryConjunctionFullDirectionalScan 全项级数的方向扫描(截断括号未被确认时的兜底)。 -func mercuryConjunctionFullDirectionalScan(jde, direction float64) float64 { +func mercuryConjunctionFullDirectionalScan(jde, direction float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + leftJDE := jde - leftValue := mercuryConjunctionExactDelta(leftJDE) + leftValue := mercuryConjunctionExactDelta(leftJDE, inst) if !isFiniteFloat(leftValue) { return math.NaN() } for i := 0; i < mercuryConjunctionScanMaxSteps; i++ { rightJDE := jde + direction*mercuryConjunctionScanStepDay*float64(i+1) - rightValue := mercuryConjunctionExactDelta(rightJDE) + rightValue := mercuryConjunctionExactDelta(rightJDE, inst) if !isFiniteFloat(rightValue) { return math.NaN() } if leftValue == 0 || rightValue == 0 || leftValue*rightValue < 0 { root, ok := eventBracketSecantRoot(leftJDE, rightJDE, leftValue, rightValue, - mercuryConjunctionPolishToleranceDay, mercuryConjunctionExactDelta) + mercuryConjunctionPolishToleranceDay, func(jde float64) float64 { return mercuryConjunctionExactDelta(jde, inst) }) if !ok { return math.NaN() } @@ -264,56 +283,66 @@ func mercuryConjunctionFullDirectionalScan(jde, direction float64) float64 { return math.NaN() } -func LastMercuryConjunction(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryConjunctionStrict, NextMercuryConjunctionStrict) +// LastMercuryConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryConjunctionStrictWithInstance(jde, inst) }) } -func NextMercuryConjunction(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryConjunctionStrict, NextMercuryConjunctionStrict) +// NextMercuryConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryConjunctionStrictWithInstance(jde, inst) }) } -func LastMercuryConjunctionStrict(jde float64) float64 { - return mercuryConjunction(jde, 0) +// LastMercuryConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryConjunctionStrictWithInstance(jde float64, inst *Instance) float64 { + return mercuryConjunction(jde, 0, inst) } -func NextMercuryConjunctionStrict(jde float64) float64 { - return mercuryConjunction(jde, 1) +// NextMercuryConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryConjunctionStrictWithInstance(jde float64, inst *Instance) float64 { + return mercuryConjunction(jde, 1, inst) } -func NextMercuryInferiorConjunction(jde float64) float64 { - date := NextMercuryConjunctionStrict(jde) - if EarthMercuryAway(date) > EarthAway(date) { - return NextMercuryConjunctionStrict(date + 2) +// NextMercuryInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + date := NextMercuryConjunctionStrictWithInstance(jde, inst) + if EarthMercuryAwayWithInstance(date, inst) > EarthAwayWithInstance(date, inst) { + return NextMercuryConjunctionStrictWithInstance(date+2, inst) } return date } -func NextMercurySuperiorConjunction(jde float64) float64 { - date := NextMercuryConjunctionStrict(jde) - if EarthMercuryAway(date) < EarthAway(date) { - return NextMercuryConjunctionStrict(date + 2) +// NextMercurySuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercurySuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + date := NextMercuryConjunctionStrictWithInstance(jde, inst) + if EarthMercuryAwayWithInstance(date, inst) < EarthAwayWithInstance(date, inst) { + return NextMercuryConjunctionStrictWithInstance(date+2, inst) } return date } -func LastMercuryInferiorConjunction(jde float64) float64 { - date := LastMercuryConjunctionStrict(jde) - if EarthMercuryAway(date) > EarthAway(date) { - return LastMercuryConjunctionStrict(date - 2) +// LastMercuryInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + date := LastMercuryConjunctionStrictWithInstance(jde, inst) + if EarthMercuryAwayWithInstance(date, inst) > EarthAwayWithInstance(date, inst) { + return LastMercuryConjunctionStrictWithInstance(date-2, inst) } return date } -func LastMercurySuperiorConjunction(jde float64) float64 { - date := LastMercuryConjunctionStrict(jde) - if EarthMercuryAway(date) < EarthAway(date) { - return LastMercuryConjunctionStrict(date - 2) +// LastMercurySuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercurySuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + date := LastMercuryConjunctionStrictWithInstance(jde, inst) + if EarthMercuryAwayWithInstance(date, inst) < EarthAwayWithInstance(date, inst) { + return LastMercuryConjunctionStrictWithInstance(date-2, inst) } return date } -func mercuryRADerivative(jde, delta float64) float64 { - sub := MercuryApparentRa(jde+delta) - MercuryApparentRa(jde-delta) +func mercuryRADerivative(jde, delta float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := MercuryApparentRaWithInstance(jde+delta, inst) - MercuryApparentRaWithInstance(jde-delta, inst) if sub > 180 { sub -= 360 } @@ -323,8 +352,10 @@ func mercuryRADerivative(jde, delta float64) float64 { return sub / (2 * delta) } -func mercuryRADerivativeN(jde, delta float64, n int) float64 { - sub := MercuryApparentRaN(jde+delta, n) - MercuryApparentRaN(jde-delta, n) +func mercuryRADerivativeN(jde, delta float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := MercuryApparentRaNWithInstance(jde+delta, n, inst) - MercuryApparentRaNWithInstance(jde-delta, n, inst) if sub > 180 { sub -= 360 } @@ -334,16 +365,20 @@ func mercuryRADerivativeN(jde, delta float64, n int) float64 { return sub / (2 * delta) } -func mercuryStationInWindow(startTT, endTT float64) float64 { +func mercuryStationInWindow(startTT, endTT float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + bestJDE := zeroEventInWindow(startTT, endTT, mercuryStationCoarseStepDay, mercuryStationHalfWindowDay, 30.0/86400.0, func(jd float64) float64 { - return mercuryRADerivativeN(jd, mercuryStationDerivativeStepDay, mercuryEventSearchN) + return mercuryRADerivativeN(jd, mercuryStationDerivativeStepDay, mercuryEventSearchN, inst) }, func(jd float64) float64 { - return mercuryRADerivative(jd, mercuryStationDerivativeStepDay) + return mercuryRADerivative(jd, mercuryStationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func mercuryStationBetween(startTT, endTT float64) bool { +func mercuryStationBetween(startTT, endTT float64, instances ...*Instance) bool { + inst := instanceValue(instances) + if endTT < startTT { startTT, endTT = endTT, startTT } @@ -360,13 +395,13 @@ func mercuryStationBetween(startTT, endTT float64) bool { } // 截断扫描足以判断单候选快速路径是否安全 / A truncated scan is enough to decide whether the one-candidate fast path is safe. left := startTT - leftValue := mercuryRADerivativeN(left, mercuryStationDerivativeStepDay, mercuryEventSearchN) + leftValue := mercuryRADerivativeN(left, mercuryStationDerivativeStepDay, mercuryEventSearchN, inst) for left < endTT { right := left + mercuryStationCoarseStepDay if right > endTT { right = endTT } - rightValue := mercuryRADerivativeN(right, mercuryStationDerivativeStepDay, mercuryEventSearchN) + rightValue := mercuryRADerivativeN(right, mercuryStationDerivativeStepDay, mercuryEventSearchN, inst) if leftValue == 0 || leftValue*rightValue < 0 || rightValue == 0 { return true } @@ -376,76 +411,81 @@ func mercuryStationBetween(startTT, endTT float64) bool { return false } -func mercuryProgradeToRetrogradeAroundInferior(inferiorUT float64) float64 { +func mercuryProgradeToRetrogradeAroundInferior(inferiorUT float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + inferiorTT := UTC2TT(inferiorUT) - return mercuryStationInWindow(inferiorTT-mercuryStationWindowDays, inferiorTT) + return mercuryStationInWindow(inferiorTT-mercuryStationWindowDays, inferiorTT, inst) } -func mercuryRetrogradeToProgradeAroundInferior(inferiorUT float64) float64 { +func mercuryRetrogradeToProgradeAroundInferior(inferiorUT float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + inferiorTT := UTC2TT(inferiorUT) - return mercuryStationInWindow(inferiorTT, inferiorTT+mercuryStationWindowDays) + return mercuryStationInWindow(inferiorTT, inferiorTT+mercuryStationWindowDays, inst) } -// 类型化「留」统一结构:以下合为锚点求候选,再用侧向不变量(Next 不得早于查询、 -// Last 不得晚于查询)校验;不满足则把锚点推进/回退一个会合周期重试,最多 -// mercuryStationAnchorAttempts 次,最终仍有界失败为 NaN。 -// 这保证 Last* 永远不会返回未来、Next* 永远不会返回过去的事件。 -func NextMercuryProgradeToRetrograde(jde float64) float64 { +// NextMercuryProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - inferior := NextMercuryInferiorConjunction(jde) + // 以下合为锚点求候选,不满足 Next 不早于查询时推进一个会合周期,保证不返回过去的事件。 + inferior := NextMercuryInferiorConjunctionWithInstance(jde, inst) for i := 0; i < mercuryStationAnchorAttempts && isFiniteFloat(inferior); i++ { - date := mercuryProgradeToRetrogradeAroundInferior(inferior) + date := mercuryProgradeToRetrogradeAroundInferior(inferior, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - inferior = NextMercuryInferiorConjunction(eventUTNextQueryTT(inferior)) + inferior = NextMercuryInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst) } return math.NaN() } -func NextMercuryRetrogradeToPrograde(jde float64) float64 { +// NextMercuryRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - inferior := LastMercuryInferiorConjunction(jde) + inferior := LastMercuryInferiorConjunctionWithInstance(jde, inst) for i := 0; i < mercuryStationAnchorAttempts && isFiniteFloat(inferior); i++ { - date := mercuryRetrogradeToProgradeAroundInferior(inferior) + date := mercuryRetrogradeToProgradeAroundInferior(inferior, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - inferior = NextMercuryInferiorConjunction(eventUTNextQueryTT(inferior)) + inferior = NextMercuryInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst) } return math.NaN() } -func LastMercuryProgradeToRetrograde(jde float64) float64 { +// LastMercuryProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - inferior := NextMercuryInferiorConjunction(jde) + inferior := NextMercuryInferiorConjunctionWithInstance(jde, inst) for i := 0; i < mercuryStationAnchorAttempts && isFiniteFloat(inferior); i++ { - date := mercuryProgradeToRetrogradeAroundInferior(inferior) + date := mercuryProgradeToRetrogradeAroundInferior(inferior, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } - inferior = LastMercuryInferiorConjunction(eventUTLastQueryTT(inferior)) + inferior = LastMercuryInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) } return math.NaN() } -func LastMercuryRetrogradeToPrograde(jde float64) float64 { +// LastMercuryRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - inferior := LastMercuryInferiorConjunction(jde) + inferior := LastMercuryInferiorConjunctionWithInstance(jde, inst) for i := 0; i < mercuryStationAnchorAttempts && isFiniteFloat(inferior); i++ { - date := mercuryRetrogradeToProgradeAroundInferior(inferior) + date := mercuryRetrogradeToProgradeAroundInferior(inferior, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } - inferior = LastMercuryInferiorConjunction(eventUTLastQueryTT(inferior)) + inferior = LastMercuryInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) } return math.NaN() } @@ -477,72 +517,78 @@ func latestFiniteEventUT(candidates ...float64) float64 { return best } -func nextMercuryRetrogradeFromTyped(jde float64) float64 { - return earliestFiniteEventUT(NextMercuryProgradeToRetrograde(jde), NextMercuryRetrogradeToPrograde(jde)) +func nextMercuryRetrogradeFromTyped(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return earliestFiniteEventUT(NextMercuryProgradeToRetrogradeWithInstance(jde, inst), NextMercuryRetrogradeToProgradeWithInstance(jde, inst)) } -func NextMercuryRetrograde(jde float64) float64 { +// NextMercuryRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay) + motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay, inst) if motion > mercuryStationMotionTolerance { - p2r := NextMercuryProgradeToRetrograde(jde) - if isFiniteFloat(p2r) && !mercuryStationBetween(jde, UTC2TT(p2r)) { + p2r := NextMercuryProgradeToRetrogradeWithInstance(jde, inst) + if isFiniteFloat(p2r) && !mercuryStationBetween(jde, UTC2TT(p2r), inst) { return p2r } - best := earliestFiniteEventUT(p2r, NextMercuryRetrogradeToPrograde(jde)) + best := earliestFiniteEventUT(p2r, NextMercuryRetrogradeToProgradeWithInstance(jde, inst)) if !stationUTQueryAfterOrEqual(best, jde) { return math.NaN() } return best } if motion < -mercuryStationMotionTolerance { - r2p := NextMercuryRetrogradeToPrograde(jde) - if isFiniteFloat(r2p) && !mercuryStationBetween(jde, UTC2TT(r2p)) { + r2p := NextMercuryRetrogradeToProgradeWithInstance(jde, inst) + if isFiniteFloat(r2p) && !mercuryStationBetween(jde, UTC2TT(r2p), inst) { return r2p } - best := earliestFiniteEventUT(NextMercuryProgradeToRetrograde(jde), r2p) + best := earliestFiniteEventUT(NextMercuryProgradeToRetrogradeWithInstance(jde, inst), r2p) if !stationUTQueryAfterOrEqual(best, jde) { return math.NaN() } return best } - return nextMercuryRetrogradeFromTyped(jde) + return nextMercuryRetrogradeFromTyped(jde, inst) } -func lastMercuryRetrogradeFromTyped(jde float64) float64 { - return latestFiniteEventUT(LastMercuryProgradeToRetrograde(jde), LastMercuryRetrogradeToPrograde(jde)) +func lastMercuryRetrogradeFromTyped(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return latestFiniteEventUT(LastMercuryProgradeToRetrogradeWithInstance(jde, inst), LastMercuryRetrogradeToProgradeWithInstance(jde, inst)) } -func LastMercuryRetrograde(jde float64) float64 { +// LastMercuryRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay) + motion := mercuryRADerivative(jde, mercuryStationDerivativeStepDay, inst) if motion > mercuryStationMotionTolerance { - r2p := LastMercuryRetrogradeToPrograde(jde) - if isFiniteFloat(r2p) && !mercuryStationBetween(UTC2TT(r2p), jde) { + r2p := LastMercuryRetrogradeToProgradeWithInstance(jde, inst) + if isFiniteFloat(r2p) && !mercuryStationBetween(UTC2TT(r2p), jde, inst) { return r2p } - best := latestFiniteEventUT(LastMercuryProgradeToRetrograde(jde), r2p) + best := latestFiniteEventUT(LastMercuryProgradeToRetrogradeWithInstance(jde, inst), r2p) if !stationUTQueryBeforeOrEqual(best, jde) { return math.NaN() } return best } if motion < -mercuryStationMotionTolerance { - p2r := LastMercuryProgradeToRetrograde(jde) - if isFiniteFloat(p2r) && !mercuryStationBetween(UTC2TT(p2r), jde) { + p2r := LastMercuryProgradeToRetrogradeWithInstance(jde, inst) + if isFiniteFloat(p2r) && !mercuryStationBetween(UTC2TT(p2r), jde, inst) { return p2r } - best := latestFiniteEventUT(p2r, LastMercuryRetrogradeToPrograde(jde)) + best := latestFiniteEventUT(p2r, LastMercuryRetrogradeToProgradeWithInstance(jde, inst)) if !stationUTQueryBeforeOrEqual(best, jde) { return math.NaN() } return best } - return lastMercuryRetrogradeFromTyped(jde) + return lastMercuryRetrogradeFromTyped(jde, inst) } func LastMercuryRetrogradeStrict(jde float64) float64 { @@ -553,258 +599,298 @@ func NextMercuryRetrogradeStrict(jde float64) float64 { return NextMercuryRetrograde(jde) } -func MercurySunElongation(jde float64) float64 { - lo1, bo1 := MercuryApparentLoBo(jde) - lo2 := HSunApparentLo(jde) - bo2 := HSunTrueBo(jde) +// MercurySunElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercurySunElongationWithInstance(jde float64, inst *Instance) float64 { + lo1, bo1 := MercuryApparentLoBoWithInstance(jde, inst) + lo2 := HSunApparentLoWithInstance(jde, inst) + bo2 := HSunTrueBoWithInstance(jde, inst) return StarAngularSeparation(lo1, bo1, lo2, bo2) } -func mercurySunElongationN(jde float64, n int) float64 { - lo1, bo1 := MercuryApparentLoBoN(jde, n) - lo2 := HSunApparentLoN(jde, n) - bo2 := HSunTrueBoN(jde, n) +func mercurySunElongationN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + lo1, bo1 := MercuryApparentLoBoNWithInstance(jde, n, inst) + lo2 := HSunApparentLoNWithInstance(jde, n, inst) + bo2 := HSunTrueBoNWithInstance(jde, n, inst) return StarAngularSeparation(lo1, bo1, lo2, bo2) } // mercuryGreatestElongationInWindow 求窗口内大距:目标是公开 MercurySunElongation 的极大(视距角), // 而不是忽略光行差/视位置修正的真距角,否则返回的时刻不是调用方能量到的那个极值。 // 窗口两端是世界时,目标函数收力学时,因此逐次换算。 -func mercuryGreatestElongationInWindow(start, end float64) float64 { +func mercuryGreatestElongationInWindow(start, end float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + return maximizeInWindow(start, end, 2.0, func(utJD float64) float64 { - return mercurySunElongationN(UTC2TT(utJD), mercuryEventSearchN) + return mercurySunElongationN(UTC2TT(utJD), mercuryEventSearchN, inst) }, func(utJD float64) float64 { - return MercurySunElongation(UTC2TT(utJD)) + return MercurySunElongationWithInstance(UTC2TT(utJD), inst) }) } -func mercuryEastElongationWindowEndingAt(inferior float64) (float64, float64) { - lastSuperior := LastMercurySuperiorConjunction(eventUTLastQueryTT(inferior)) +func mercuryEastElongationWindowEndingAt(inferior float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + lastSuperior := LastMercurySuperiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) return lastSuperior + innerEventWindowPadding, inferior - innerEventWindowPadding } -func mercuryWestElongationWindowEndingAt(superior float64) (float64, float64) { - lastInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(superior)) +func mercuryWestElongationWindowEndingAt(superior float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + lastInferior := LastMercuryInferiorConjunctionWithInstance(eventUTLastQueryTT(superior), inst) return lastInferior + innerEventWindowPadding, superior - innerEventWindowPadding } -func mercuryEastElongationWindowContaining(jde float64) (float64, float64) { - nextInferior := NextMercuryInferiorConjunction(jde) - start, end := mercuryEastElongationWindowEndingAt(nextInferior) +func mercuryEastElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + nextInferior := NextMercuryInferiorConjunctionWithInstance(jde, inst) + start, end := mercuryEastElongationWindowEndingAt(nextInferior, inst) if eventUTQueryBeforeOrEqual(start, jde) { return start, end } - currentInferior := LastMercuryInferiorConjunction(jde) - return mercuryEastElongationWindowEndingAt(currentInferior) + currentInferior := LastMercuryInferiorConjunctionWithInstance(jde, inst) + return mercuryEastElongationWindowEndingAt(currentInferior, inst) } -func mercuryWestElongationWindowContaining(jde float64) (float64, float64) { - nextSuperior := NextMercurySuperiorConjunction(jde) - start, end := mercuryWestElongationWindowEndingAt(nextSuperior) +func mercuryWestElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + nextSuperior := NextMercurySuperiorConjunctionWithInstance(jde, inst) + start, end := mercuryWestElongationWindowEndingAt(nextSuperior, inst) if eventUTQueryBeforeOrEqual(start, jde) { return start, end } - currentSuperior := LastMercurySuperiorConjunction(jde) - return mercuryWestElongationWindowEndingAt(currentSuperior) + currentSuperior := LastMercurySuperiorConjunctionWithInstance(jde, inst) + return mercuryWestElongationWindowEndingAt(currentSuperior, inst) } -func nextMercuryGreatestElongationTyped(jde float64, east bool) float64 { +func nextMercuryGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(jde) { return math.NaN() } if east { - start, windowEnd := mercuryEastElongationWindowContaining(jde) + start, windowEnd := mercuryEastElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := mercuryGreatestElongationInWindow(start, windowEnd) + date := mercuryGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } - nextInferior := NextMercuryInferiorConjunction(eventUTNextQueryTT(windowEnd)) - start, windowEnd = mercuryEastElongationWindowEndingAt(nextInferior) + nextInferior := NextMercuryInferiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst) + start, windowEnd = mercuryEastElongationWindowEndingAt(nextInferior, inst) } return math.NaN() } - start, windowEnd := mercuryWestElongationWindowContaining(jde) + start, windowEnd := mercuryWestElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := mercuryGreatestElongationInWindow(start, windowEnd) + date := mercuryGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } - nextSuperior := NextMercurySuperiorConjunction(eventUTNextQueryTT(windowEnd)) - start, windowEnd = mercuryWestElongationWindowEndingAt(nextSuperior) + nextSuperior := NextMercurySuperiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst) + start, windowEnd = mercuryWestElongationWindowEndingAt(nextSuperior, inst) } return math.NaN() } -func lastMercuryGreatestElongationTyped(jde float64, east bool) float64 { +func lastMercuryGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(jde) { return math.NaN() } if east { - start, windowEnd := mercuryEastElongationWindowContaining(jde) + start, windowEnd := mercuryEastElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := mercuryGreatestElongationInWindow(start, windowEnd) + date := mercuryGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } - prevInferior := LastMercuryInferiorConjunction(eventUTLastQueryTT(start)) - start, windowEnd = mercuryEastElongationWindowEndingAt(prevInferior) + prevInferior := LastMercuryInferiorConjunctionWithInstance(eventUTLastQueryTT(start), inst) + start, windowEnd = mercuryEastElongationWindowEndingAt(prevInferior, inst) } return math.NaN() } - start, windowEnd := mercuryWestElongationWindowContaining(jde) + start, windowEnd := mercuryWestElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := mercuryGreatestElongationInWindow(start, windowEnd) + date := mercuryGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } - prevSuperior := LastMercurySuperiorConjunction(eventUTLastQueryTT(start)) - start, windowEnd = mercuryWestElongationWindowEndingAt(prevSuperior) + prevSuperior := LastMercurySuperiorConjunctionWithInstance(eventUTLastQueryTT(start), inst) + start, windowEnd = mercuryWestElongationWindowEndingAt(prevSuperior, inst) } return math.NaN() } // mercuryElongationWindowAt 返回包含 jde 的该侧大距窗口;查询落在合的 4 秒内边距里时两侧都不包含。 -func mercuryElongationWindowAt(jde float64, east bool) (float64, float64, bool) { +func mercuryElongationWindowAt(jde float64, east bool, instances ...*Instance) (float64, float64, bool) { + inst := instanceValue(instances) + if east { - start, end := mercuryEastElongationWindowEndingAt(NextMercuryInferiorConjunction(jde)) + start, end := mercuryEastElongationWindowEndingAt(NextMercuryInferiorConjunctionWithInstance(jde, inst), inst) return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) } - start, end := mercuryWestElongationWindowEndingAt(NextMercurySuperiorConjunction(jde)) + start, end := mercuryWestElongationWindowEndingAt(NextMercurySuperiorConjunctionWithInstance(jde, inst), inst) return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) } -// 无东西侧参数的 Next/Last 先只看查询所在窗口那一侧:该侧大距未过就是答案,已过则另一侧紧接着的 -// 下一个才是答案,因此只有在查询贴住合(两侧窗口都不含)时才退化为两侧都算。赤经差在合附近会提前 -// 变号,不能用它定窗口。 -func NextMercuryGreatestElongation(jde float64) float64 { +// NextMercuryGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } + // 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。 for _, east := range [2]bool{true, false} { - start, end, ok := mercuryElongationWindowAt(jde, east) + start, end, ok := mercuryElongationWindowAt(jde, east, inst) if !ok { continue } - if date := mercuryGreatestElongationInWindow(start, end); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { + if date := mercuryGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { return date } - if date := nextMercuryGreatestElongationTyped(jde, !east); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { + if date := nextMercuryGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { return date } break } - return earliestFiniteEventUT(nextMercuryGreatestElongationTyped(jde, true), nextMercuryGreatestElongationTyped(jde, false)) + return earliestFiniteEventUT(nextMercuryGreatestElongationTyped(jde, true, inst), nextMercuryGreatestElongationTyped(jde, false, inst)) } -func LastMercuryGreatestElongation(jde float64) float64 { +// LastMercuryGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } + // 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。 for _, east := range [2]bool{true, false} { - start, end, ok := mercuryElongationWindowAt(jde, east) + start, end, ok := mercuryElongationWindowAt(jde, east, inst) if !ok { continue } - if date := mercuryGreatestElongationInWindow(start, end); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { + if date := mercuryGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { return date } - if date := lastMercuryGreatestElongationTyped(jde, !east); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { + if date := lastMercuryGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { return date } break } - return latestFiniteEventUT(lastMercuryGreatestElongationTyped(jde, true), lastMercuryGreatestElongationTyped(jde, false)) + return latestFiniteEventUT(lastMercuryGreatestElongationTyped(jde, true, inst), lastMercuryGreatestElongationTyped(jde, false, inst)) } -func LastMercuryInferiorConjunctionInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryInferiorConjunction, NextMercuryInferiorConjunction) +// LastMercuryInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryInferiorConjunctionWithInstance(jde, inst) }) } -func NextMercuryInferiorConjunctionInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryInferiorConjunction, NextMercuryInferiorConjunction) +// NextMercuryInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryInferiorConjunctionWithInstance(jde, inst) }) } -func LastMercurySuperiorConjunctionInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercurySuperiorConjunction, NextMercurySuperiorConjunction) +// LastMercurySuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercurySuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercurySuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercurySuperiorConjunctionWithInstance(jde, inst) }) } -func NextMercurySuperiorConjunctionInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercurySuperiorConjunction, NextMercurySuperiorConjunction) +// NextMercurySuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercurySuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercurySuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercurySuperiorConjunctionWithInstance(jde, inst) }) } -func LastMercuryRetrogradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryRetrograde, NextMercuryRetrograde) +// LastMercuryRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryRetrogradeWithInstance(jde, inst) }) } -func NextMercuryRetrogradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryRetrograde, NextMercuryRetrograde) +// NextMercuryRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryRetrogradeWithInstance(jde, inst) }) } -func LastMercuryProgradeToRetrogradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryProgradeToRetrograde, NextMercuryProgradeToRetrograde) +// LastMercuryProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryProgradeToRetrogradeWithInstance(jde, inst) }) } -func NextMercuryProgradeToRetrogradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryProgradeToRetrograde, NextMercuryProgradeToRetrograde) +// NextMercuryProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryProgradeToRetrogradeWithInstance(jde, inst) }) } -func LastMercuryRetrogradeToProgradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryRetrogradeToPrograde, NextMercuryRetrogradeToPrograde) +// LastMercuryRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryRetrogradeToProgradeWithInstance(jde, inst) }) } -func NextMercuryRetrogradeToProgradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryRetrogradeToPrograde, NextMercuryRetrogradeToPrograde) +// NextMercuryRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryRetrogradeToProgradeWithInstance(jde, inst) }) } -func LastMercuryGreatestElongationInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongation, NextMercuryGreatestElongation) +// LastMercuryGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationWithInstance(jde, inst) }) } -func NextMercuryGreatestElongationInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongation, NextMercuryGreatestElongation) +// NextMercuryGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationWithInstance(jde, inst) }) } -func LastMercuryGreatestElongationEastInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongationEast, NextMercuryGreatestElongationEast) +// LastMercuryGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationEastWithInstance(jde, inst) }) } -func NextMercuryGreatestElongationEastInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongationEast, NextMercuryGreatestElongationEast) +// NextMercuryGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationEastWithInstance(jde, inst) }) } -func LastMercuryGreatestElongationWestInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastMercuryGreatestElongationWest, NextMercuryGreatestElongationWest) +// LastMercuryGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationWestWithInstance(jde, inst) }) } -func NextMercuryGreatestElongationWestInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastMercuryGreatestElongationWest, NextMercuryGreatestElongationWest) +// NextMercuryGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastMercuryGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextMercuryGreatestElongationWestWithInstance(jde, inst) }) } -func NextMercuryGreatestElongationEast(jde float64) float64 { - return nextMercuryGreatestElongationTyped(jde, true) +// NextMercuryGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 { + return nextMercuryGreatestElongationTyped(jde, true, inst) } -func NextMercuryGreatestElongationWest(jde float64) float64 { - return nextMercuryGreatestElongationTyped(jde, false) +// NextMercuryGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 { + return nextMercuryGreatestElongationTyped(jde, false, inst) } -func LastMercuryGreatestElongationEast(jde float64) float64 { - return lastMercuryGreatestElongationTyped(jde, true) +// LastMercuryGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 { + return lastMercuryGreatestElongationTyped(jde, true, inst) } -func LastMercuryGreatestElongationWest(jde float64) float64 { - return lastMercuryGreatestElongationTyped(jde, false) +// LastMercuryGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 { + return lastMercuryGreatestElongationTyped(jde, false, inst) } diff --git a/basic/moon_bright_limb.go b/basic/moon_bright_limb.go index aad1a53..f934534 100644 --- a/basic/moon_bright_limb.go +++ b/basic/moon_bright_limb.go @@ -1,16 +1,18 @@ package basic -import . "b612.me/astro/tools" +import ( + . "b612.me/astro/tools" +) // MoonBrightLimbPositionAngle 月亮明亮边缘位置角 / position angle of the Moon's bright limb. func MoonBrightLimbPositionAngle(jde float64) float64 { return MoonBrightLimbPositionAngleN(jde, -1) } -// MoonBrightLimbPositionAngleN 月亮明亮边缘位置角(截断版) / truncated position angle of the Moon's bright limb. -func MoonBrightLimbPositionAngleN(jde float64, n int) float64 { - sunRA, sunDec := HSunApparentRaDecN(jde, n) - moonRA, moonDec := HMoonTrueRaDecN(jde, n) +// MoonBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + sunRA, sunDec := HSunApparentRaDecNWithInstance(jde, n, inst) + moonRA, moonDec := HMoonTrueRaDecNWithInstance(jde, n, inst) return brightLimbPositionAngleFromRaDec(sunRA, sunDec, moonRA, moonDec) } @@ -19,23 +21,27 @@ func MoonTopocentricBrightLimbPositionAngle(jde, observerLon, observerLat, heigh return MoonTopocentricBrightLimbPositionAngleN(jde, observerLon, observerLat, height, -1) } -// MoonTopocentricBrightLimbPositionAngleN 月亮站心明亮边缘位置角(截断版) / truncated topocentric position angle of the Moon's bright limb. -func MoonTopocentricBrightLimbPositionAngleN(jde, observerLon, observerLat, height float64, n int) float64 { - sunRA, sunDec := sunTopocentricApparentRaDecN(jde, observerLon, observerLat, height, n) - moonRA, moonDec := moonTopocentricApparentRaDecN(jde, observerLon, observerLat, height, n) +// MoonTopocentricBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonTopocentricBrightLimbPositionAngleNWithInstance(jde, observerLon, observerLat, height float64, n int, inst *Instance) float64 { + sunRA, sunDec := sunTopocentricApparentRaDecN(jde, observerLon, observerLat, height, n, inst) + moonRA, moonDec := moonTopocentricApparentRaDecN(jde, observerLon, observerLat, height, n, inst) return brightLimbPositionAngleFromRaDec(sunRA, sunDec, moonRA, moonDec) } -func moonTopocentricApparentRaDecN(jde, observerLon, observerLat, height float64, n int) (float64, float64) { - geocentricRA := HMoonTrueRaN(jde, n) - geocentricDec := HMoonTrueDecN(jde, n) - distanceAU := HMoonAwayN(jde, n) / moonPhysicalAstronomicalUnitKM +func moonTopocentricApparentRaDecN(jde, observerLon, observerLat, height float64, n int, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + geocentricRA := HMoonTrueRaNWithInstance(jde, n, inst) + geocentricDec := HMoonTrueDecNWithInstance(jde, n, inst) + distanceAU := HMoonAwayNWithInstance(jde, n, inst) / moonPhysicalAstronomicalUnitKM return TopocentricRaDec(geocentricRA, geocentricDec, observerLat, observerLon, TT2UTC(jde), distanceAU, height) } -func sunTopocentricApparentRaDecN(jde, observerLon, observerLat, height float64, n int) (float64, float64) { - geocentricRA, geocentricDec := HSunApparentRaDecN(jde, n) - return TopocentricRaDec(geocentricRA, geocentricDec, observerLat, observerLon, TT2UTC(jde), EarthAwayN(jde, n), height) +func sunTopocentricApparentRaDecN(jde, observerLon, observerLat, height float64, n int, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + geocentricRA, geocentricDec := HSunApparentRaDecNWithInstance(jde, n, inst) + return TopocentricRaDec(geocentricRA, geocentricDec, observerLat, observerLon, TT2UTC(jde), EarthAwayNWithInstance(jde, n, inst), height) } func brightLimbPositionAngleFromRaDec(sunRA, sunDec, bodyRA, bodyDec float64) float64 { diff --git a/basic/moon_ephemeris_instance.go b/basic/moon_ephemeris_instance.go new file mode 100644 index 0000000..e99c75f --- /dev/null +++ b/basic/moon_ephemeris_instance.go @@ -0,0 +1,107 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" + . "b612.me/astro/tools" +) + +func moonCalcNewNWithInstance(coordIndex int, jd float64, n int, inst *Instance) float64 { + if inst == nil { + return planet.WhereMoonN(coordIndex, jd, n) + } + if inst.Model() != ephem.Custom { + return planet.WhereMoonModelN(inst.Model(), coordIndex, jd, n) + } + if lo, bo, distAU, ok := instanceStateEcliptic(ephem.Moon, jd, inst); ok { + switch coordIndex { + case 0: + return lo * math.Pi / 180 + case 1: + return bo * math.Pi / 180 + case 2: + return distAU * astronomicalUnitKM + } + } + return math.NaN() +} + +// MoonCalcNewWithInstance 指定星历实例的月球几何量 / lunar geometry with an explicit ephemeris instance. +func MoonCalcNewWithInstance(coordIndex int, jd float64, inst *Instance) float64 { + return moonCalcNewNWithInstance(coordIndex, jd, -1, inst) +} + +// MoonCalcNewNWithInstance 指定星历实例的月球几何量(n 变体) / n-parameterised lunar geometry with an explicit ephemeris instance. +func MoonCalcNewNWithInstance(coordIndex int, jd float64, n int, inst *Instance) float64 { + return moonCalcNewNWithInstance(coordIndex, jd, n, inst) +} + +// HMoonTrueLoWithInstance 指定星历实例的月球真黄经 / true lunar longitude with an explicit ephemeris instance. +func HMoonTrueLoWithInstance(jde float64, inst *Instance) float64 { + return HMoonTrueLoNWithInstance(jde, -1, inst) +} + +// HMoonTrueLoNWithInstance 指定星历实例的月球真黄经(n 变体) / n-parameterised true lunar longitude with an explicit ephemeris instance. +func HMoonTrueLoNWithInstance(jde float64, n int, inst *Instance) float64 { + return Limit360(moonCalcNewNWithInstance(0, jde, n, inst) * 180 / math.Pi) +} + +// HMoonTrueBoWithInstance 指定星历实例的月球真黄纬 / true lunar latitude with an explicit ephemeris instance. +func HMoonTrueBoWithInstance(jde float64, inst *Instance) float64 { + return HMoonTrueBoNWithInstance(jde, -1, inst) +} + +// HMoonTrueBoNWithInstance 指定星历实例的月球真黄纬(n 变体) / n-parameterised true lunar latitude with an explicit ephemeris instance. +func HMoonTrueBoNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonCalcNewNWithInstance(1, jde, n, inst) * 180 / math.Pi +} + +// HMoonAwayWithInstance 指定星历实例的月地距离 / lunar distance with an explicit ephemeris instance. +func HMoonAwayWithInstance(jde float64, inst *Instance) float64 { + return HMoonAwayNWithInstance(jde, -1, inst) +} + +// HMoonAwayNWithInstance 指定星历实例的月地距离(n 变体) / n-parameterised lunar distance with an explicit ephemeris instance. +func HMoonAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonCalcNewNWithInstance(2, jde, n, inst) +} + +// HMoonApparentLoWithInstance 指定星历实例的月球视黄经 / apparent lunar longitude with an explicit ephemeris instance. +func HMoonApparentLoWithInstance(jde float64, inst *Instance) float64 { + return HMoonApparentLoNWithInstance(jde, -1, inst) +} + +// HMoonApparentLoNWithInstance 指定星历实例的月球视黄经(n 变体) / n-parameterised apparent lunar longitude with an explicit ephemeris instance. +func HMoonApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + if lo, _, _, _, ok, err := vectorApparentWithInstance(ephem.Moon, jde, n, inst); err != nil { + return math.NaN() + } else if ok { + return lo + } + return HMoonTrueLoNWithInstance(jde, n, inst) + Nutation2000Bi(jde) +} + +// HMoonGeocentricApparentRaDecWithInstance 指定星历实例的月球地心视赤经纬 / apparent geocentric lunar right ascension and declination with an explicit ephemeris instance. +func HMoonGeocentricApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return HMoonGeocentricApparentRaDecNWithInstance(jde, -1, inst) +} + +// HMoonGeocentricApparentRaDecNWithInstance 指定星历实例的月球地心视赤经纬(n 变体) / n-parameterised apparent geocentric lunar right ascension and declination with an explicit ephemeris instance. +func HMoonGeocentricApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return moonApparentRaDecN(jde, n, inst) +} + +// HMoonTrueRaDecWithInstance 指定星历实例的月球真赤经纬 / true lunar right ascension and declination with an explicit ephemeris instance. +func HMoonTrueRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return moonApparentRaDecN(jde, -1, inst) +} + +// HMoonApparentRaDecWithInstance 指定星历实例的月球站心视赤经纬 / topocentric apparent lunar right ascension and declination with an explicit ephemeris instance. +func HMoonApparentRaDecWithInstance(jd, lon, lat, tz float64, inst *Instance) (float64, float64) { + jde := UTC2TT(jd) - tz/24 + ra, dec := HMoonTrueRaDecWithInstance(jde, inst) + away := HMoonAwayWithInstance(jde, inst) / astronomicalUnitKM + return TopocentricRaDec(ra, dec, lat, lon, jd-tz/24, away, 0) +} diff --git a/basic/moon_horizon.go b/basic/moon_horizon.go index adaa66d..7ec5fa1 100644 --- a/basic/moon_horizon.go +++ b/basic/moon_horizon.go @@ -1,15 +1,11 @@ package basic -// MoonHorizon 返回 UTC 儒略日下海平面几何月球中心地平圈(月球恰好在地平线上的观测者轨迹, -// 即月下点周围的地平圈)的 [经度, 纬度] 顶点,单位为度,不重复首点。视差与椭球口径同 -// HMoonHeight,不含折射;与 HMoonHeight(经, 纬, ..., 0) 配合时该圈上的点高度角为 0。 -// samples<=0 取 360,其余夹到 [12, 1440]。 -// MoonHorizon returns sea-level geometric Moon-centre horizon vertices in degrees for a UTC Julian -// day: the locus of observers that see the Moon exactly on the horizon. Parallax and the observer -// ellipsoid match HMoonHeight; refraction is excluded. -func MoonHorizon(jdUTC float64, samples int) [][2]float64 { +import () + +// MoonHorizonWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonHorizonWithInstance(jdUTC float64, samples int, inst *Instance) [][2]float64 { if !finite(jdUTC) { return nil } - return MoonStateAt(jdUTC).MoonHorizon(samples) + return MoonStateAtWithInstance(jdUTC, inst).MoonHorizon(samples) } diff --git a/basic/moon_max_declination.go b/basic/moon_max_declination.go index 32c0d73..7bbd533 100644 --- a/basic/moon_max_declination.go +++ b/basic/moon_max_declination.go @@ -81,47 +81,49 @@ var moonMaxDeclinationSouthCoefficients = moonMaxDeclinationCoefficients{ }, } -// MoonMaximumNorthDeclinations 指定年月内的所有月球最大北赤纬事件 / all maximum northern lunar declination events in the given Gregorian month. -func MoonMaximumNorthDeclinations(year int, month time.Month) []DeclinationEvent { - return moonMaximumDeclinationsInMonth(year, month, moonMaxDeclinationNorthCoefficients) +// MoonMaximumNorthDeclinationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonMaximumNorthDeclinationsWithInstance(year int, month time.Month, inst *Instance) []DeclinationEvent { + return moonMaximumDeclinationsInMonth(year, month, moonMaxDeclinationNorthCoefficients, inst) } -// MoonMaximumSouthDeclinations 指定年月内的所有月球最大南赤纬事件 / all maximum southern lunar declination events in the given Gregorian month. -func MoonMaximumSouthDeclinations(year int, month time.Month) []DeclinationEvent { - return moonMaximumDeclinationsInMonth(year, month, moonMaxDeclinationSouthCoefficients) +// MoonMaximumSouthDeclinationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonMaximumSouthDeclinationsWithInstance(year int, month time.Month, inst *Instance) []DeclinationEvent { + return moonMaximumDeclinationsInMonth(year, month, moonMaxDeclinationSouthCoefficients, inst) } -// LastMoonMaximumNorthDeclination 指定时刻之前最近一次月球最大北赤纬 / last maximum northern lunar declination at or before jd. -func LastMoonMaximumNorthDeclination(jd float64) DeclinationEvent { - return moonMaximumDeclinationSearch(jd, moonMaxDeclinationNorthCoefficients, -1, true) +// LastMoonMaximumNorthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMoonMaximumNorthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonMaximumDeclinationSearch(jd, moonMaxDeclinationNorthCoefficients, -1, true, inst) } -// NextMoonMaximumNorthDeclination 指定时刻之后最近一次月球最大北赤纬 / next maximum northern lunar declination after jd. -func NextMoonMaximumNorthDeclination(jd float64) DeclinationEvent { - return moonMaximumDeclinationSearch(jd, moonMaxDeclinationNorthCoefficients, 1, false) +// NextMoonMaximumNorthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMoonMaximumNorthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonMaximumDeclinationSearch(jd, moonMaxDeclinationNorthCoefficients, 1, false, inst) } -// ClosestMoonMaximumNorthDeclination 离指定时刻最近一次月球最大北赤纬 / closest maximum northern lunar declination to jd. -func ClosestMoonMaximumNorthDeclination(jd float64) DeclinationEvent { - return moonClosestMaximumDeclination(jd, moonMaxDeclinationNorthCoefficients) +// ClosestMoonMaximumNorthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestMoonMaximumNorthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonClosestMaximumDeclination(jd, moonMaxDeclinationNorthCoefficients, inst) } -// LastMoonMaximumSouthDeclination 指定时刻之前最近一次月球最大南赤纬 / last maximum southern lunar declination at or before jd. -func LastMoonMaximumSouthDeclination(jd float64) DeclinationEvent { - return moonMaximumDeclinationSearch(jd, moonMaxDeclinationSouthCoefficients, -1, true) +// LastMoonMaximumSouthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMoonMaximumSouthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonMaximumDeclinationSearch(jd, moonMaxDeclinationSouthCoefficients, -1, true, inst) } -// NextMoonMaximumSouthDeclination 指定时刻之后最近一次月球最大南赤纬 / next maximum southern lunar declination after jd. -func NextMoonMaximumSouthDeclination(jd float64) DeclinationEvent { - return moonMaximumDeclinationSearch(jd, moonMaxDeclinationSouthCoefficients, 1, false) +// NextMoonMaximumSouthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMoonMaximumSouthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonMaximumDeclinationSearch(jd, moonMaxDeclinationSouthCoefficients, 1, false, inst) } -// ClosestMoonMaximumSouthDeclination 离指定时刻最近一次月球最大南赤纬 / closest maximum southern lunar declination to jd. -func ClosestMoonMaximumSouthDeclination(jd float64) DeclinationEvent { - return moonClosestMaximumDeclination(jd, moonMaxDeclinationSouthCoefficients) +// ClosestMoonMaximumSouthDeclinationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestMoonMaximumSouthDeclinationWithInstance(jd float64, inst *Instance) DeclinationEvent { + return moonClosestMaximumDeclination(jd, moonMaxDeclinationSouthCoefficients, inst) } -func moonMaximumDeclinationsInMonth(year int, month time.Month, coeffs moonMaxDeclinationCoefficients) []DeclinationEvent { +func moonMaximumDeclinationsInMonth(year int, month time.Month, coeffs moonMaxDeclinationCoefficients, instances ...*Instance) []DeclinationEvent { + inst := instanceValue(instances) + startUTC := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) endUTC := startUTC.AddDate(0, 1, 0) startTT := UTC2TT(Date2JD(startUTC)) @@ -141,7 +143,7 @@ func moonMaximumDeclinationsInMonth(year int, month time.Month, coeffs moonMaxDe events := make([]DeclinationEvent, 0, 2) for k := kStart; k <= kEnd; k++ { - event := moonMaximumDeclinationEvent(k, coeffs, cfg) + event := moonMaximumDeclinationEvent(k, coeffs, cfg, inst) eventTimeUTC := JD2DateByZone(event.JD, time.UTC, false) if eventTimeUTC.Before(startUTC) || !eventTimeUTC.Before(endUTC) { continue @@ -155,10 +157,12 @@ func moonMaximumDeclinationsInMonth(year int, month time.Month, coeffs moonMaxDe return events } -func moonMaximumDeclinationEvent(k int, coeffs moonMaxDeclinationCoefficients, cfg apsisSearchConfig) DeclinationEvent { +func moonMaximumDeclinationEvent(k int, coeffs moonMaxDeclinationCoefficients, cfg apsisSearchConfig, instances ...*Instance) DeclinationEvent { + inst := instanceValue(instances) + seedTT := moonMaximumDeclinationSeedTT(k, coeffs) eventTT, declination := refineDistanceExtremum(seedTT, cfg, func(sampleTT float64) float64 { - return HMoonTrueDecN(sampleTT, -1) + return HMoonTrueDecNWithInstance(sampleTT, -1, inst) }) return DeclinationEvent{ JD: TT2UTC(eventTT), @@ -166,7 +170,9 @@ func moonMaximumDeclinationEvent(k int, coeffs moonMaxDeclinationCoefficients, c } } -func moonMaximumDeclinationSearch(jd float64, coeffs moonMaxDeclinationCoefficients, direction int, includeCurrent bool) DeclinationEvent { +func moonMaximumDeclinationSearch(jd float64, coeffs moonMaxDeclinationCoefficients, direction int, includeCurrent bool, instances ...*Instance) DeclinationEvent { + inst := instanceValue(instances) + if !finite(jd) { // 非有限查询算不出周期序号(centerK 会把 NaN/±Inf 压成 MinInt64,种子外推到 1e40 后步长小于一个 ULP)。 return DeclinationEvent{} @@ -177,12 +183,12 @@ func moonMaximumDeclinationSearch(jd float64, coeffs moonMaxDeclinationCoefficie if direction < 0 { step = -1 } - event := moonMaximumDeclinationEvent(centerK, coeffs, cfg) + event := moonMaximumDeclinationEvent(centerK, coeffs, cfg, inst) // 事件时刻随周期序号单调递增:该方向上最近的候选就是 centerK 沿该方向第一个满足方向不变量 // 的周期;命中后只需再回退检查更早的周期是否同样满足,可达范围仍旧是 ±3 个周期。 if !moonMaximumDeclinationMatchesDirection(event.JD-jd, direction, includeCurrent) { for offset := 1; offset <= moonMaxDeclinationSearchSpan; offset++ { - event = moonMaximumDeclinationEvent(centerK+step*offset, coeffs, cfg) + event = moonMaximumDeclinationEvent(centerK+step*offset, coeffs, cfg, inst) if moonMaximumDeclinationMatchesDirection(event.JD-jd, direction, includeCurrent) { return event } @@ -190,7 +196,7 @@ func moonMaximumDeclinationSearch(jd float64, coeffs moonMaxDeclinationCoefficie return DeclinationEvent{} } for offset := 1; offset <= moonMaxDeclinationSearchSpan; offset++ { - previous := moonMaximumDeclinationEvent(centerK-step*offset, coeffs, cfg) + previous := moonMaximumDeclinationEvent(centerK-step*offset, coeffs, cfg, inst) if !moonMaximumDeclinationMatchesDirection(previous.JD-jd, direction, includeCurrent) { break } @@ -199,20 +205,22 @@ func moonMaximumDeclinationSearch(jd float64, coeffs moonMaxDeclinationCoefficie return event } -func moonClosestMaximumDeclination(jd float64, coeffs moonMaxDeclinationCoefficients) DeclinationEvent { +func moonClosestMaximumDeclination(jd float64, coeffs moonMaxDeclinationCoefficients, instances ...*Instance) DeclinationEvent { + inst := instanceValue(instances) + if !finite(jd) { return DeclinationEvent{} } cfg := moonMaximumDeclinationSearchConfig(coeffs) centerK := moonMaximumDeclinationOffset(jd, coeffs) - center := moonMaximumDeclinationEvent(centerK, coeffs, cfg) + center := moonMaximumDeclinationEvent(centerK, coeffs, cfg, inst) var last, next DeclinationEvent if center.JD <= jd { last = center - next = moonMaximumDeclinationAround(jd, coeffs, cfg, centerK, 1, false) + next = moonMaximumDeclinationAround(jd, coeffs, cfg, centerK, 1, false, inst) } else { next = center - last = moonMaximumDeclinationAround(jd, coeffs, cfg, centerK, -1, true) + last = moonMaximumDeclinationAround(jd, coeffs, cfg, centerK, -1, true, inst) } lastDistance := math.Abs(jd - last.JD) nextDistance := math.Abs(next.JD - jd) @@ -224,13 +232,15 @@ func moonClosestMaximumDeclination(jd float64, coeffs moonMaxDeclinationCoeffici // moonMaximumDeclinationAround 从 centerK 沿 step 方向找第一个满足方向不变量的周期;±3 个周期内没有就返回零事件。 func moonMaximumDeclinationAround(jd float64, coeffs moonMaxDeclinationCoefficients, cfg apsisSearchConfig, - centerK, step int, includeCurrent bool) DeclinationEvent { + centerK, step int, includeCurrent bool, instances ...*Instance) DeclinationEvent { + inst := instanceValue(instances) + direction := 1 if step < 0 { direction = -1 } for offset := 1; offset <= moonMaxDeclinationSearchSpan; offset++ { - event := moonMaximumDeclinationEvent(centerK+step*offset, coeffs, cfg) + event := moonMaximumDeclinationEvent(centerK+step*offset, coeffs, cfg, inst) if moonMaximumDeclinationMatchesDirection(event.JD-jd, direction, includeCurrent) { return event } diff --git a/basic/moon_max_declination_perf_test.go b/basic/moon_max_declination_perf_test.go index 2eb2214..e2d24e8 100644 --- a/basic/moon_max_declination_perf_test.go +++ b/basic/moon_max_declination_perf_test.go @@ -47,6 +47,9 @@ func moonMaximumDeclinationReferenceClosest(jd float64, coeffs moonMaxDeclinatio } func TestMoonMaximumDeclinationPruningMatchesFullSearch(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } for _, coeffs := range []moonMaxDeclinationCoefficients{moonMaxDeclinationNorthCoefficients, moonMaxDeclinationSouthCoefficients} { for jd := JDCalc(2024, 1, 1); jd <= JDCalc(2025, 6, 1); jd += 2.5 { for _, direction := range []struct { diff --git a/basic/moon_max_declination_test.go b/basic/moon_max_declination_test.go index 41177eb..703b70e 100644 --- a/basic/moon_max_declination_test.go +++ b/basic/moon_max_declination_test.go @@ -24,7 +24,6 @@ type moonMaxDeclinationMonthState struct { } func TestMoonMaximumDeclinationsMatchHorizonsBaseline(t *testing.T) { - // Baseline is generated from JPL Horizons by scripts/generate_moon_max_declination_baseline.sh. data, err := os.ReadFile("testdata/moon_max_declination_baseline.json") if err != nil { t.Fatalf("read baseline: %v", err) diff --git a/basic/moon_observation.go b/basic/moon_observation.go index a9d108c..148fce2 100644 --- a/basic/moon_observation.go +++ b/basic/moon_observation.go @@ -55,15 +55,17 @@ func MoonHeight(jd, lon, lat, tz float64) float64 { return ArcSin(tmp2) } -func HMoonAzimuth(jd, lon, lat, tz float64) float64 { - return HMoonAzimuthN(jd, lon, lat, tz, -1) +// HMoonAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonAzimuthWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return HMoonAzimuthNWithInstance(jd, lon, lat, tz, -1, inst) } -func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 { +// HMoonAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonAzimuthNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { jde := UTC2TT(jd - tz/24) - ra := HMoonTrueRaN(jde, n) - dec := HMoonTrueDecN(jde, n) - away := HMoonAwayN(jde, n) / 149597870.7 + ra := HMoonTrueRaNWithInstance(jde, n, inst) + dec := HMoonTrueDecNWithInstance(jde, n, inst) + away := HMoonAwayNWithInstance(jde, n, inst) / 149597870.7 ndec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0) nra := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0) jdUT := jd - tz/24 @@ -86,15 +88,9 @@ func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 { } } -// HMoonHeight 当地民用时儒略日下的月心几何高度角(度,不含折射)/ geometric Moon-centre altitude in degrees for a local civil Julian day. -// -// jd 是该时区的当地民用时(墙上时刻)儒略日,tz 是时区偏移小时数,库内按 jd−tz/24 换成 UTC。 -// 只有 tz 给 0 时 jd 才是 UTC 儒略日;不要拿 UTC 数值再配非零 tz,那会多减一次时区。 -// jd is that zone's local civil (wall-clock) Julian day and tz is the zone offset in hours, -// converted internally as jd-tz/24. Only tz 0 makes jd a UTC Julian day: pairing a UTC value with a -// non-zero tz subtracts the offset twice. -func HMoonHeight(jd, lon, lat, tz float64) float64 { - return HMoonHeightN(jd, lon, lat, tz, -1) +// HMoonHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonHeightWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return HMoonHeightNWithInstance(jd, lon, lat, tz, -1, inst) } type moonObservationState struct { @@ -102,10 +98,12 @@ type moonObservationState struct { distanceKM float64 } -func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int) moonObservationState { +func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int, instances ...*Instance) moonObservationState { + inst := instanceValue(instances) + calculationJDE := UTC2TT(jd - tz/24) - ra, dec := HMoonTrueRaDecN(calculationJDE, n) - distanceKM := HMoonAwayN(calculationJDE, n) + ra, dec := HMoonTrueRaDecNWithInstance(calculationJDE, n, inst) + distanceKM := HMoonAwayNWithInstance(calculationJDE, n, inst) distanceAU := distanceKM / angularDiameterAstronomicalUnitKM topocentricRA, topocentricDec := TopocentricRaDec(ra, dec, lat, lon, jd-tz/24, distanceAU, height) siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon) @@ -117,8 +115,9 @@ func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int) moonObserva } } -func HMoonHeightN(jd, lon, lat, tz float64, n int) float64 { - return hMoonObservationStateN(jd, lon, lat, tz, 0, n).altitude +// HMoonHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonHeightNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + return hMoonObservationStateN(jd, lon, lat, tz, 0, n, inst).altitude } // MoonState 同一瞬间可对任意观测点复用的月球位置与恒星时 / one instant's lunar position and sidereal time, reusable across observers. @@ -129,14 +128,14 @@ type MoonState struct { siderealTime float64 } -// MoonStateAt 由 UTC 儒略日构造该瞬间的可复用月球状态 / builds the reusable state for one UTC Julian day. -func MoonStateAt(utcJD float64) MoonState { +// MoonStateAtWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonStateAtWithInstance(utcJD float64, inst *Instance) MoonState { jde := UTC2TT(utcJD) - rightAscension, declination := HMoonTrueRaDec(jde) + rightAscension, declination := HMoonTrueRaDecWithInstance(jde, inst) return MoonState{ rightAscension: rightAscension, declination: declination, - distanceAU: HMoonAway(jde) / angularDiameterAstronomicalUnitKM, + distanceAU: HMoonAwayWithInstance(jde, inst) / angularDiameterAstronomicalUnitKM, siderealTime: ApparentSiderealTime(UTC2UT1(utcJD)) * 15, } } @@ -204,12 +203,17 @@ func (state MoonState) MoonHorizon(samples int) [][2]float64 { return points } -func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int) float64 { - state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, n) +func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int, instances ...*Instance) float64 { + return moonRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height, + refractionStandardPressureHPa, refractionStandardTemperatureC, n, instanceValue(instances)) +} + +func moonRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height, pressureHPa, temperatureC float64, n int, inst *Instance) float64 { + state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, n, inst) // 相对观测者下沉地平线的视上缘高度角 / Apparent upper-limb altitude relative to the observer's depressed horizon. residual := state.altitude + HeightDegreeByLat(height, latitude) if zenithShift != 0 { - residual += RefractionFromTrueAltitude(state.altitude, refractionStandardPressureHPa, refractionStandardTemperatureC) + residual += RefractionFromTrueAltitude(state.altitude, pressureHPa, temperatureC) residual += angularSemidiameterArcsec(moonEquatorialRadiusKM, state.distanceKM) / 3600 } return residual @@ -218,13 +222,15 @@ func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height // moonRiseSetOnCivilDay 在民用日内求升/落时刻;找不到过零时的错误口径与 rise_set.go 的 ErrNeverRise/ErrNeverSet 一致, // fallbackErr 是调用方用中天/下中天残差预判的同一几何结论,命中时优先于扫描结果。 func moonRiseSetOnCivilDay(candidate, slope, civilDayStart, longitude, latitude, originalTimeZone, - localTimeZone, zenithShift, height float64, isRise bool, fallbackErr error) (float64, error) { + localTimeZone, zenithShift, height float64, isRise bool, fallbackErr error, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + if eventRiseSetCandidateValid(candidate, civilDayStart, slope, isRise) { return candidate, nil } return eventDirectionalRiseSetSearch(civilDayStart, isRise, fallbackErr, func(outputJD float64) float64 { localJD := outputJD + localTimeZone/24 - originalTimeZone/24 - return moonRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, -1) + return moonRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, -1, inst) }) } @@ -248,12 +254,13 @@ func GetMoonTZTime(jd, lon, lat, tz float64) float64 { //实际中天时间{ return estimateJD } -func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 { +// MoonCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonCulminationTimeWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时;ra/dec 为瞬时天球坐标,非 J2000 等固定历元。 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-MoonTimeAngle(localJD, lon, lat, timezone))/15.0/24.0/0.9 + estimateJD := localJD + Limit360(360-MoonTimeAngleWithInstance(localJD, lon, lat, timezone, inst))/15.0/24.0/0.9 limitHA := func(localJD, lon, timezone float64) float64 { - ha := MoonTimeAngle(localJD, lon, lat, timezone) + ha := MoonTimeAngleWithInstance(localJD, lon, lat, timezone, inst) if ha < 180 { ha += 360 } @@ -271,16 +278,18 @@ func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 { return estimateJD } -func MoonTimeAngle(jd, lon, lat, tz float64) float64 { +// MoonTimeAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonTimeAngleWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { startime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon) - timeangle := startime - HMoonApparentRa(jd, lon, lat, tz) + timeangle := startime - HMoonApparentRaWithInstance(jd, lon, lat, tz, inst) if timeangle < 0 { timeangle += 360 } return timeangle } -func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { +// GetMoonRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetMoonRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) { if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) { return 0, ErrInvalidObservationInput } @@ -291,11 +300,11 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig // 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。 julianDay = math.Floor(julianDay) + 0.5 estimatedTime := julianDay - moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1) + moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1, inst) moonAngle := StandardAltitudeMoon(zenithShift, height, latitude) - moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone) + moonAngleTime := MoonTimeAngleWithInstance(julianDay, longitude, latitude, originalTimeZone, inst) if moonResidual > 0 { // 月亮在地平线上或在落下与下中天之间 if moonAngleTime > 180 { @@ -314,32 +323,32 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig estimatedTime += (timeToMeridian/24 + (timeToMeridian/24*12.0)/15.0/24.0) } - currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone) + currentAngle := MoonTimeAngleWithInstance(estimatedTime, longitude, latitude, timeZone, inst) if math.Abs(currentAngle-180) > 0.5 { estimatedTime += (180 - currentAngle) * 4.0 / 60.0 / 24.0 } - currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) + currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) if !(currentResidual < -10 && math.Abs(latitude) < 60) { if currentResidual > 0 { // 下中天仍在地平线上:当日无落下(也无可升起),口径见 moonRiseSetOnCivilDay。 return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, true, ErrNeverSet) + originalTimeZone, timeZone, zenithShift, height, true, ErrNeverSet, inst) } checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0 - checkAngle := MoonTimeAngle(checkTime, longitude, latitude, timeZone) + checkAngle := MoonTimeAngleWithInstance(checkTime, longitude, latitude, timeZone, inst) if checkAngle < 90 { checkAngle += 360 } checkTime += (360 - checkAngle) * 4.0 / 60.0 / 24.0 - if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 { + if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) < 0 { // 上中天仍在地平线下:当日无升起。 return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, true, ErrNeverRise) + originalTimeZone, timeZone, zenithShift, height, true, ErrNeverRise, inst) } } - moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone) + moonDeclination := MoonApparentDecWithInstance(estimatedTime, longitude, latitude, timeZone, inst) tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude)) if math.Abs(tmp) <= 1 && latitude < 85 { @@ -347,7 +356,7 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig estimatedTime += hourAngle/24.00 + hourAngle/33.00/15.00 } else { i := 0 - for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 { + for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) < 0 { i++ estimatedTime += 15.0 / 60.0 / 24.0 if i > 48 { @@ -357,13 +366,14 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig } // 使用牛顿迭代法求精确解 - estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002) + estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002, inst) estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24 return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, true, nil) + originalTimeZone, timeZone, zenithShift, height, true, nil, inst) } -func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { +// GetMoonSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetMoonSetTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) { if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) { return 0, ErrInvalidObservationInput } @@ -374,11 +384,11 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh // 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。 julianDay = math.Floor(julianDay) + 0.5 estimatedTime := julianDay - moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1) + moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1, inst) moonAngle := StandardAltitudeMoon(zenithShift, height, latitude) - moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone) + moonAngleTime := MoonTimeAngleWithInstance(julianDay, longitude, latitude, originalTimeZone, inst) if moonResidual < 0 { timeToMeridian = (360 - moonAngleTime) / 15 @@ -394,7 +404,7 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh estimatedTime += (timeToMeridian/24.0 + (timeToMeridian/24.0*12.0)/15.0/24.0) } - currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone) + currentAngle := MoonTimeAngleWithInstance(estimatedTime, longitude, latitude, timeZone, inst) if currentAngle < 180 { currentAngle += 360 } @@ -403,24 +413,24 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh } // estimatedTime = 月球中天时间 - currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) + currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) if !(currentResidual > 10 && math.Abs(latitude) < 60) { if currentResidual < 0 { // 上中天仍在地平线下:当日无升起,也就无落下。 return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, false, ErrNeverRise) + originalTimeZone, timeZone, zenithShift, height, false, ErrNeverRise, inst) } checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0 - angleSubtraction := 180 - MoonTimeAngle(checkTime, longitude, latitude, timeZone) + angleSubtraction := 180 - MoonTimeAngleWithInstance(checkTime, longitude, latitude, timeZone, inst) checkTime += angleSubtraction * 4.0 / 60.0 / 24.0 - if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 { + if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) > 0 { // 下中天仍在地平线上:当日无落下。 return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, false, ErrNeverSet) + originalTimeZone, timeZone, zenithShift, height, false, ErrNeverSet, inst) } } - moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone) + moonDeclination := MoonApparentDecWithInstance(estimatedTime, longitude, latitude, timeZone, inst) tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude)) if math.Abs(tmp) <= 1 && latitude < 85 { @@ -428,7 +438,7 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh estimatedTime += hourAngle/24 + hourAngle/33.0/15.0 } else { i := 0 - for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 { + for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) > 0 { i++ estimatedTime += 15.0 / 60.0 / 24.0 if i > 48 { @@ -438,10 +448,10 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh } // 使用牛顿迭代法求精确解 - estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002) + estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002, inst) estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24 return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude, - originalTimeZone, timeZone, zenithShift, height, false, nil) + originalTimeZone, timeZone, zenithShift, height, false, nil, inst) } // heightFunction 高度函数类型定义,用于牛顿迭代法 @@ -468,14 +478,16 @@ func moonRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, timeZon return currentTime } -func moonRiseSetResidualIteration(initialTime, longitude, latitude, timeZone, zenithShift, height, tolerance float64) (float64, float64) { +func moonRiseSetResidualIteration(initialTime, longitude, latitude, timeZone, zenithShift, height, tolerance float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + const derivativeStep = 0.000005 slope := math.NaN() currentTime, ok := eventNewtonRefine(initialTime, tolerance, func(previousTime float64) float64 { - functionValue := moonRiseSetResidual(previousTime, longitude, latitude, timeZone, zenithShift, height, -1) - slope = (moonRiseSetResidual(previousTime+derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1) - - moonRiseSetResidual(previousTime-derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1)) / (2 * derivativeStep) + functionValue := moonRiseSetResidual(previousTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) + slope = (moonRiseSetResidual(previousTime+derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1, inst) - + moonRiseSetResidual(previousTime-derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1, inst)) / (2 * derivativeStep) return functionValue / slope }) if !ok { diff --git a/basic/moon_phase.go b/basic/moon_phase.go index f4a9358..b72fc9d 100644 --- a/basic/moon_phase.go +++ b/basic/moon_phase.go @@ -205,3 +205,15 @@ func CalcMoonX(year float64, quarterType int) float64 { } return jde } + +// solarEclipseAnchorNewMoon 种子所在朔望月的朔(TT);同一朔望月内任意种子返回逐位相同的结果。 +func solarEclipseAnchorNewMoon(seedJDE float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + first := CalcMoonSHByJDEWithInstance(seedJDE, 0, inst) + if math.IsNaN(first) { + return first + } + k := math.Round((first - 2451550.09765) / 29.530588853) + return CalcMoonSHByJDEWithInstance(2451550.09765+29.530588853*k, 0, inst) +} diff --git a/basic/moon_phase_ephemeris_instance.go b/basic/moon_phase_ephemeris_instance.go new file mode 100644 index 0000000..3a4b809 --- /dev/null +++ b/basic/moon_phase_ephemeris_instance.go @@ -0,0 +1,115 @@ +package basic + +import ( + "math" + + . "b612.me/astro/tools" +) + +func sunMoonSeekWithInstance(jde, degree float64, inst *Instance) float64 { + if inst == nil { + return SunMoonSeek(jde, degree) + } + p := HMoonApparentLoWithInstance(jde, inst) - HSunApparentLoWithInstance(jde, inst) - degree + for p < -180 { + p += 360 + } + for p > 180 { + p -= 360 + } + return p +} + +func refineSunMoonPhaseWithInstance(jde, degree float64, inst *Instance) float64 { + estimateJD, ok := eventNewtonRefine(jde, 0.00001, func(prevJD float64) float64 { + value := sunMoonSeekWithInstance(prevJD, degree, inst) + slope := (sunMoonSeekWithInstance(prevJD+0.000005, degree, inst) - sunMoonSeekWithInstance(prevJD-0.000005, degree, inst)) / 0.00001 + return value / slope + }) + if !ok { + return math.NaN() + } + return estimateJD +} + +// MoonPhaseWithInstance 月球照明比例 / illuminated lunar fraction with an explicit ephemeris instance. +func MoonPhaseWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return MoonPhase(jde) + } + moonBo := HMoonTrueBoWithInstance(jde, inst) + sunLo := HSunApparentLoWithInstance(jde, inst) + moonLo := HMoonApparentLoWithInstance(jde, inst) + tmp := Cos(moonBo) * Cos(sunLo-moonLo) + earthSunDistance := EarthAwayNWithInstance(jde, -1, inst) * astronomicalUnitKM + i := earthSunDistance * Sin(ArcCos(tmp)) / (HMoonAwayWithInstance(jde, inst) - earthSunDistance*tmp) + i = ArcTan(i) + if i < 0 { + i += 180 + } + if i > 180 { + i -= 180 + } + return (1 + Cos(i)) / 2 +} + +// SunMoonSeekWithInstance 日月黄经差相位残差 / Sun-Moon longitude phase residual with an explicit ephemeris instance. +func SunMoonSeekWithInstance(jde, degree float64, inst *Instance) float64 { + return sunMoonSeekWithInstance(jde, degree, inst) +} + +// CalcMoonSHByJDEWithInstance 按历元求朔望时刻 / calculate a syzygy from an epoch with an explicit ephemeris instance. +func CalcMoonSHByJDEWithInstance(jde float64, phaseType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonSHByJDE(jde, phaseType) + } + return refineSunMoonPhaseWithInstance(jde, float64(phaseType*180), inst) +} + +// CalcMoonSHWithInstance 按年份求朔望时刻 / calculate a syzygy from a year with an explicit ephemeris instance. +func CalcMoonSHWithInstance(year float64, phaseType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonSH(year, phaseType) + } + return refineSunMoonPhaseWithInstance(CalcMoonSWithInstance(year, phaseType, inst), float64(phaseType*180), inst) +} + +// CalcMoonSWithInstance 按年份估算朔望基准时刻 / estimate a syzygy base time from a year with an explicit ephemeris instance. +func CalcMoonSWithInstance(year float64, phaseType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonS(year, phaseType) + } + return CalcMoonS(year, phaseType) +} + +// CalcMoonXHByJDEWithInstance 按历元求弦月时刻 / calculate a quarter phase from an epoch with an explicit ephemeris instance. +func CalcMoonXHByJDEWithInstance(jde float64, quarterType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonXHByJDE(jde, quarterType) + } + degree := -90.0 + if quarterType == 0 { + degree = 90 + } + return refineSunMoonPhaseWithInstance(jde, degree, inst) +} + +// CalcMoonXHWithInstance 按年份求弦月时刻 / calculate a quarter phase from a year with an explicit ephemeris instance. +func CalcMoonXHWithInstance(year float64, quarterType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonXH(year, quarterType) + } + degree := -90.0 + if quarterType == 0 { + degree = 90 + } + return refineSunMoonPhaseWithInstance(CalcMoonXWithInstance(year, quarterType, inst), degree, inst) +} + +// CalcMoonXWithInstance 按年份估算弦月基准时刻 / estimate a quarter-phase base time from a year with an explicit ephemeris instance. +func CalcMoonXWithInstance(year float64, quarterType int, inst *Instance) float64 { + if inst == nil { + return CalcMoonX(year, quarterType) + } + return CalcMoonX(year, quarterType) +} diff --git a/basic/moon_physical.go b/basic/moon_physical.go index 53ab674..dd0b354 100644 --- a/basic/moon_physical.go +++ b/basic/moon_physical.go @@ -27,14 +27,15 @@ type MoonPhysicalInfo struct { PositionAngle float64 } -// MoonPhysical 月球物理观测参数 / physical observing parameters of the Moon. -func MoonPhysical(jde float64) MoonPhysicalInfo { - return MoonPhysicalN(jde, -1) +// MoonPhysicalWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonPhysicalWithInstance(jde float64, inst *Instance) MoonPhysicalInfo { + return MoonPhysicalNWithInstance(jde, -1, inst) } -// MoonPhysicalN 月球物理观测参数(截断版) / truncated physical observing parameters of the Moon. -func MoonPhysicalN(jde float64, n int) MoonPhysicalInfo { - return moonPhysicalNFromCoordinates(jde, n, HMoonApparentLoN(jde, n), HMoonTrueBoN(jde, n), HMoonTrueRaN(jde, n)) +// MoonPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonPhysicalNWithInstance(jde float64, n int, inst *Instance) MoonPhysicalInfo { + lo, bo := moonApparentLoBoN(jde, n, inst) + return moonPhysicalNFromCoordinates(jde, n, lo, bo, HMoonTrueRaNWithInstance(jde, n, inst)) } // MoonTopocentricPhysical 月球站心物理观测参数 / topocentric physical observing parameters of the Moon. @@ -42,9 +43,9 @@ func MoonTopocentricPhysical(jde, observerLon, observerLat, height float64) Moon return MoonTopocentricPhysicalN(jde, observerLon, observerLat, height, -1) } -// MoonTopocentricPhysicalN 月球站心物理观测参数(截断版) / truncated topocentric physical observing parameters of the Moon. -func MoonTopocentricPhysicalN(jde, observerLon, observerLat, height float64, n int) MoonPhysicalInfo { - lambda, beta, alpha := moonTopocentricPhysicalCoordinatesN(jde, observerLon, observerLat, height, n) +// MoonTopocentricPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonTopocentricPhysicalNWithInstance(jde, observerLon, observerLat, height float64, n int, inst *Instance) MoonPhysicalInfo { + lambda, beta, alpha := moonTopocentricPhysicalCoordinatesN(jde, observerLon, observerLat, height, n, inst) return moonPhysicalNFromCoordinates(jde, n, lambda, beta, alpha) } @@ -91,10 +92,12 @@ func moonPhysicalNFromCoordinates(jde float64, n int, lambda, beta, alpha float6 } } -func moonTopocentricPhysicalCoordinatesN(jde, observerLon, observerLat, height float64, n int) (lambda, beta, alpha float64) { - geocentricRA := HMoonTrueRaN(jde, n) - geocentricDec := HMoonTrueDecN(jde, n) - distanceAU := HMoonAwayN(jde, n) / moonPhysicalAstronomicalUnitKM +func moonTopocentricPhysicalCoordinatesN(jde, observerLon, observerLat, height float64, n int, instances ...*Instance) (lambda, beta, alpha float64) { + inst := instanceValue(instances) + + geocentricRA := HMoonTrueRaNWithInstance(jde, n, inst) + geocentricDec := HMoonTrueDecNWithInstance(jde, n, inst) + distanceAU := HMoonAwayNWithInstance(jde, n, inst) / moonPhysicalAstronomicalUnitKM utcJD := TT2UTC(jde) var topocentricDec float64 diff --git a/basic/moon_planet_conjunction.go b/basic/moon_planet_conjunction.go index 8d85a66..6ca6ab9 100644 --- a/basic/moon_planet_conjunction.go +++ b/basic/moon_planet_conjunction.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) const ( moonPlanetConjunctionEstimateN = 8 @@ -42,36 +46,30 @@ const ( MoonPlanetConjunctionNeptune ) -func moonPlanetConjunctionWrappedDelta(diff float64) float64 { - diff = math.Mod(diff+180, 360) - if diff < 0 { - diff += 360 - } - return diff - 180 -} +func moonPlanetConjunctionDeltaAt(jdTT float64, planet MoonPlanetConjunctionPlanet, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) -func moonPlanetConjunctionDeltaAt(jdTT float64, planet MoonPlanetConjunctionPlanet, n int) float64 { - moonRA := HMoonGeocentricApparentRaN(jdTT, n) + moonRA := HMoonGeocentricApparentRaNWithInstance(jdTT, n, inst) var planetRA float64 switch planet { case MoonPlanetConjunctionMercury: - planetRA = MercuryApparentRaN(jdTT, n) + planetRA = MercuryApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionVenus: - planetRA = VenusApparentRaN(jdTT, n) + planetRA = VenusApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionMars: - planetRA = MarsApparentRaN(jdTT, n) + planetRA = MarsApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionJupiter: - planetRA = JupiterApparentRaN(jdTT, n) + planetRA = JupiterApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionSaturn: - planetRA = SaturnApparentRaN(jdTT, n) + planetRA = SaturnApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionUranus: - planetRA = UranusApparentRaN(jdTT, n) + planetRA = UranusApparentRaNWithInstance(jdTT, n, inst) case MoonPlanetConjunctionNeptune: - planetRA = NeptuneApparentRaN(jdTT, n) + planetRA = NeptuneApparentRaNWithInstance(jdTT, n, inst) default: return math.NaN() } - return moonPlanetConjunctionWrappedDelta(moonRA - planetRA) + return geodata.NormalizeLongitude(moonRA - planetRA) } func moonPlanetConjunctionPeriodDays(planet MoonPlanetConjunctionPlanet) float64 { @@ -114,7 +112,9 @@ func moonPlanetConjunctionInDirection(eventUT, queryTT float64, direction int) b } } -func moonPlanetConjunctionFindBracket(centerTT, halfSpan, step float64, planet MoonPlanetConjunctionPlanet) (float64, float64, bool) { +func moonPlanetConjunctionFindBracket(centerTT, halfSpan, step float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) (float64, float64, bool) { + inst := instanceValue(instances) + if math.IsNaN(centerTT) || math.IsNaN(halfSpan) || math.IsNaN(step) || halfSpan <= 0 || step <= 0 { return 0, 0, false } @@ -122,7 +122,7 @@ func moonPlanetConjunctionFindBracket(centerTT, halfSpan, step float64, planet M end := centerTT + halfSpan samples := int(math.Ceil((end-start)/step)) + 1 prevTT := start - prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1) + prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1, inst) if math.IsNaN(prevVal) { return 0, 0, false } @@ -137,7 +137,7 @@ func moonPlanetConjunctionFindBracket(centerTT, halfSpan, step float64, planet M if tt > end { tt = end } - val := moonPlanetConjunctionDeltaAt(tt, planet, -1) + val := moonPlanetConjunctionDeltaAt(tt, planet, -1, inst) if math.IsNaN(val) { return 0, 0, false } @@ -165,7 +165,9 @@ func moonPlanetConjunctionFindBracket(centerTT, halfSpan, step float64, planet M return bestLeft, bestRight, true } -func moonPlanetConjunctionRefineBracket(leftTT, rightTT float64, planet MoonPlanetConjunctionPlanet) float64 { +func moonPlanetConjunctionRefineBracket(leftTT, rightTT float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) float64 { + inst := instanceValue(instances) + if leftTT > rightTT { leftTT, rightTT = rightTT, leftTT } @@ -175,13 +177,15 @@ func moonPlanetConjunctionRefineBracket(leftTT, rightTT float64, planet MoonPlan center := (leftTT + rightTT) / 2.0 halfWindow := (rightTT - leftTT) / 2.0 return eventZeroRefine(center, halfWindow, moonPlanetConjunctionRefineStepDays, func(sampleTT float64) float64 { - return moonPlanetConjunctionDeltaAt(sampleTT, planet, -1) + return moonPlanetConjunctionDeltaAt(sampleTT, planet, -1, inst) }) } -func moonPlanetConjunctionEventUT(leftTT, rightTT float64, planet MoonPlanetConjunctionPlanet) float64 { - eventTT := moonPlanetConjunctionRefineBracket(leftTT, rightTT, planet) - if math.Abs(moonPlanetConjunctionDeltaAt(eventTT, planet, -1)) > moonPlanetConjunctionEventTolerance { +func moonPlanetConjunctionEventUT(leftTT, rightTT float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) float64 { + inst := instanceValue(instances) + + eventTT := moonPlanetConjunctionRefineBracket(leftTT, rightTT, planet, inst) + if math.Abs(moonPlanetConjunctionDeltaAt(eventTT, planet, -1, inst)) > moonPlanetConjunctionEventTolerance { return math.NaN() } return TT2UTC(eventTT) @@ -203,21 +207,25 @@ func moonPlanetConjunctionCollectLocalEvent(result *moonPlanetConjunctionLocalRe } } -func moonPlanetConjunctionShouldCheckLocal(queryTT float64, planet MoonPlanetConjunctionPlanet) bool { - delta := moonPlanetConjunctionDeltaAt(queryTT, planet, moonPlanetConjunctionEstimateN) +func moonPlanetConjunctionShouldCheckLocal(queryTT float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) bool { + inst := instanceValue(instances) + + delta := moonPlanetConjunctionDeltaAt(queryTT, planet, moonPlanetConjunctionEstimateN, inst) if math.IsNaN(delta) { return false } return math.Abs(delta) <= moonPlanetConjunctionNearQueryDeltaDeg } -func moonPlanetConjunctionLocalEvents(queryTT float64, planet MoonPlanetConjunctionPlanet) moonPlanetConjunctionLocalResult { +func moonPlanetConjunctionLocalEvents(queryTT float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) moonPlanetConjunctionLocalResult { + inst := instanceValue(instances) + result := emptyMoonPlanetConjunctionLocalResult() start := queryTT - moonPlanetConjunctionNearQueryHalfSpan end := queryTT + moonPlanetConjunctionNearQueryHalfSpan step := moonPlanetConjunctionNearQueryStepDays prevTT := start - prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1) + prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1, inst) if math.IsNaN(prevVal) { return result } @@ -227,12 +235,12 @@ func moonPlanetConjunctionLocalEvents(queryTT float64, planet MoonPlanetConjunct if tt > end { tt = end } - val := moonPlanetConjunctionDeltaAt(tt, planet, -1) + val := moonPlanetConjunctionDeltaAt(tt, planet, -1, inst) if math.IsNaN(val) { return emptyMoonPlanetConjunctionLocalResult() } if prevVal == 0 || val == 0 || prevVal*val < 0 { - moonPlanetConjunctionCollectLocalEvent(&result, queryTT, moonPlanetConjunctionEventUT(prevTT, tt, planet)) + moonPlanetConjunctionCollectLocalEvent(&result, queryTT, moonPlanetConjunctionEventUT(prevTT, tt, planet, inst)) } if tt == end { break @@ -243,15 +251,19 @@ func moonPlanetConjunctionLocalEvents(queryTT float64, planet MoonPlanetConjunct return result } -func moonPlanetConjunctionMaybeLocalEvents(queryTT float64, planet MoonPlanetConjunctionPlanet) moonPlanetConjunctionLocalResult { - if !moonPlanetConjunctionShouldCheckLocal(queryTT, planet) { +func moonPlanetConjunctionMaybeLocalEvents(queryTT float64, planet MoonPlanetConjunctionPlanet, instances ...*Instance) moonPlanetConjunctionLocalResult { + inst := instanceValue(instances) + + if !moonPlanetConjunctionShouldCheckLocal(queryTT, planet, inst) { return emptyMoonPlanetConjunctionLocalResult() } - return moonPlanetConjunctionLocalEvents(queryTT, planet) + return moonPlanetConjunctionLocalEvents(queryTT, planet, inst) } -func moonPlanetConjunctionGuessTT(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int) float64 { - delta := moonPlanetConjunctionDeltaAt(queryTT, planet, moonPlanetConjunctionEstimateN) +func moonPlanetConjunctionGuessTT(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + delta := moonPlanetConjunctionDeltaAt(queryTT, planet, moonPlanetConjunctionEstimateN, inst) if math.IsNaN(delta) { return math.NaN() } @@ -269,7 +281,9 @@ func moonPlanetConjunctionGuessTT(queryTT float64, planet MoonPlanetConjunctionP } } -func moonPlanetConjunctionDirectionalFallback(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int) float64 { +func moonPlanetConjunctionDirectionalFallback(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int, instances ...*Instance) float64 { + inst := instanceValue(instances) + period := moonPlanetConjunctionPeriodDays(planet) if math.IsNaN(period) { return math.NaN() @@ -291,7 +305,7 @@ func moonPlanetConjunctionDirectionalFallback(queryTT float64, planet MoonPlanet } prevTT := start - prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1) + prevVal := moonPlanetConjunctionDeltaAt(prevTT, planet, -1, inst) if math.IsNaN(prevVal) { return math.NaN() } @@ -301,12 +315,12 @@ func moonPlanetConjunctionDirectionalFallback(queryTT float64, planet MoonPlanet if tt > end { tt = end } - val := moonPlanetConjunctionDeltaAt(tt, planet, -1) + val := moonPlanetConjunctionDeltaAt(tt, planet, -1, inst) if math.IsNaN(val) { return math.NaN() } if prevVal == 0 || val == 0 || prevVal*val < 0 { - eventUT := moonPlanetConjunctionEventUT(prevTT, tt, planet) + eventUT := moonPlanetConjunctionEventUT(prevTT, tt, planet, inst) if !math.IsNaN(eventUT) && moonPlanetConjunctionInDirection(eventUT, queryTT, direction) { if direction == 1 { return eventUT @@ -323,7 +337,9 @@ func moonPlanetConjunctionDirectionalFallback(queryTT float64, planet MoonPlanet return bestEventUT } -func moonPlanetConjunctionDirectionalEventWithLocal(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int, local moonPlanetConjunctionLocalResult) float64 { +func moonPlanetConjunctionDirectionalEventWithLocal(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int, local moonPlanetConjunctionLocalResult, instances ...*Instance) float64 { + inst := instanceValue(instances) + switch direction { case -1: if !math.IsNaN(local.lastUT) { @@ -334,15 +350,15 @@ func moonPlanetConjunctionDirectionalEventWithLocal(queryTT float64, planet Moon return local.nextUT } } - guessTT := moonPlanetConjunctionGuessTT(queryTT, planet, direction) + guessTT := moonPlanetConjunctionGuessTT(queryTT, planet, direction, inst) if math.IsNaN(guessTT) { return math.NaN() } halfSpan := moonPlanetConjunctionBracketHalfSpan for attempt := 0; attempt < moonPlanetConjunctionBracketAttempts; attempt++ { - left, right, ok := moonPlanetConjunctionFindBracket(guessTT, halfSpan, moonPlanetConjunctionBracketStepDays, planet) + left, right, ok := moonPlanetConjunctionFindBracket(guessTT, halfSpan, moonPlanetConjunctionBracketStepDays, planet, inst) if ok { - eventUT := moonPlanetConjunctionEventUT(left, right, planet) + eventUT := moonPlanetConjunctionEventUT(left, right, planet, inst) if math.IsNaN(eventUT) { halfSpan *= moonPlanetConjunctionBracketGrowth continue @@ -353,26 +369,28 @@ func moonPlanetConjunctionDirectionalEventWithLocal(queryTT float64, planet Moon } halfSpan *= moonPlanetConjunctionBracketGrowth } - return moonPlanetConjunctionDirectionalFallback(queryTT, planet, direction) + return moonPlanetConjunctionDirectionalFallback(queryTT, planet, direction, inst) } -func moonPlanetConjunctionDirectionalEvent(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int) float64 { - return moonPlanetConjunctionDirectionalEventWithLocal(queryTT, planet, direction, moonPlanetConjunctionMaybeLocalEvents(queryTT, planet)) +func moonPlanetConjunctionDirectionalEvent(queryTT float64, planet MoonPlanetConjunctionPlanet, direction int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return moonPlanetConjunctionDirectionalEventWithLocal(queryTT, planet, direction, moonPlanetConjunctionMaybeLocalEvents(queryTT, planet, inst), inst) } -// LastMoonPlanetConjunction 指定时刻之前最近一次行星合月(赤经合) / previous Moon-planet conjunction at or before jde. -func LastMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { - return moonPlanetConjunctionDirectionalEvent(jde, planet, -1) +// LastMoonPlanetConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMoonPlanetConjunctionWithInstance(jde float64, planet MoonPlanetConjunctionPlanet, inst *Instance) float64 { + return moonPlanetConjunctionDirectionalEvent(jde, planet, -1, inst) } -// NextMoonPlanetConjunction 指定时刻之后最近一次行星合月(赤经合) / next Moon-planet conjunction at or after jde. -func NextMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { - return moonPlanetConjunctionDirectionalEvent(jde, planet, 1) +// NextMoonPlanetConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMoonPlanetConjunctionWithInstance(jde float64, planet MoonPlanetConjunctionPlanet, inst *Instance) float64 { + return moonPlanetConjunctionDirectionalEvent(jde, planet, 1, inst) } -// ClosestMoonPlanetConjunction 离指定时刻最近一次行星合月(赤经合) / closest Moon-planet conjunction to jde. -func ClosestMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlanet) float64 { - local := moonPlanetConjunctionMaybeLocalEvents(jde, planet) +// ClosestMoonPlanetConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestMoonPlanetConjunctionWithInstance(jde float64, planet MoonPlanetConjunctionPlanet, inst *Instance) float64 { + local := moonPlanetConjunctionMaybeLocalEvents(jde, planet, inst) if !math.IsNaN(local.lastUT) && !math.IsNaN(local.nextUT) { if sameEventJD(local.lastUT, local.nextUT) { return local.lastUT @@ -385,8 +403,8 @@ func ClosestMoonPlanetConjunction(jde float64, planet MoonPlanetConjunctionPlane if !math.IsNaN(local.nextUT) { return local.nextUT } - last := moonPlanetConjunctionDirectionalEventWithLocal(jde, planet, -1, local) - next := moonPlanetConjunctionDirectionalEventWithLocal(jde, planet, 1, local) + last := moonPlanetConjunctionDirectionalEventWithLocal(jde, planet, -1, local, inst) + next := moonPlanetConjunctionDirectionalEventWithLocal(jde, planet, 1, local, inst) if math.IsNaN(last) { return next } diff --git a/basic/moon_precision.go b/basic/moon_precision.go index f19f3e3..14a438c 100644 --- a/basic/moon_precision.go +++ b/basic/moon_precision.go @@ -3,135 +3,24 @@ package basic import ( "math" + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" . "b612.me/astro/tools" ) -var moonCir = [][][]float64{{ - //ML0 - { - 22639.586, 0.78475822, 8328.691424623, 1.5229241, 25.0719, -0.123598, 4586.438, 0.1873974, 7214.06286536, -2.184756, -18.860, 0.08280, 2369.914, 2.5429520, 15542.75428998, -0.661832, 6.212, -0.04080, 769.026, 3.140313, 16657.38284925, 3.04585, 50.144, -0.2472, 666.418, 1.527671, 628.30195521, -0.02664, 0.062, -0.0054, 411.596, 4.826607, 16866.9323150, -1.28012, -1.07, -0.0059, 211.656, 4.115028, -1114.6285593, -3.70768, -43.93, 0.2064, 205.436, 0.230523, 6585.7609101, -2.15812, -18.92, 0.0882, 191.956, 4.898507, 23871.4457146, 0.86109, 31.28, -0.164, 164.729, 2.586078, 14914.4523348, -0.6352, 6.15, -0.035, 147.321, 5.45530, -7700.3894694, -1.5496, -25.01, 0.118, 124.988, 0.48608, 7771.3771450, -0.3309, 3.11, -0.020, 109.380, 3.88323, 8956.9933798, 1.4963, 25.13, -0.129, 55.177, 5.57033, -1324.1780250, 0.6183, 7.3, -0.035, 45.100, 0.89898, 25195.623740, 0.2428, 24.0, -0.129, 39.533, 3.81213, -8538.240890, 2.8030, 26.1, -0.118, 38.430, 4.30115, 22756.817155, -2.8466, -12.6, 0.042, 36.124, 5.49587, 24986.074274, 4.5688, 75.2, -0.371, 30.773, 1.94559, 14428.125731, -4.3695, -37.7, 0.166, 28.397, 3.28586, 7842.364821, -2.2114, -18.8, 0.077, 24.358, 5.64142, 16171.056245, -0.6885, 6.3, -0.046, 18.585, 4.41371, -557.314280, -1.8538, -22.0, 0.10, 17.954, 3.58454, 8399.679100, -0.3576, 3.2, -0.03, 14.530, 4.9416, 23243.143759, 0.888, 31.2, -0.16, 14.380, 0.9709, 32200.137139, 2.384, 56.4, -0.29, 14.251, 5.7641, -2.301200, 1.523, 25.1, -0.12, 13.899, 0.3735, 31085.508580, -1.324, 12.4, -0.08, 13.194, 1.7595, -9443.319984, -5.231, -69.0, 0.33, 9.679, 3.0997, -16029.080894, -3.072, -50.1, 0.24, 9.366, 0.3016, 24080.995180, -3.465, -19.9, 0.08, 8.606, 4.1582, -1742.930514, -3.681, -44.0, 0.21, 8.453, 2.8416, 16100.068570, 1.192, 28.2, -0.14, 8.050, 2.6292, 14286.150380, -0.609, 6.1, -0.03, 7.630, 6.2388, 17285.684804, 3.019, 50.2, -0.25, 7.447, 1.4845, 1256.603910, -0.053, 0.1, -0.01, 7.371, 0.2736, 5957.458955, -2.131, -19.0, 0.09, 7.063, 5.6715, 33.757047, -0.308, -3.6, 0.02, 6.383, 4.7843, 7004.513400, 2.141, 32.4, -0.16, 5.742, 2.6572, 32409.686605, -1.942, 5, -0.05, 4.374, 4.3443, 22128.51520, -2.820, -13, 0.05, 3.998, 3.2545, 33524.31516, 1.766, 49, -0.25, 3.210, 2.2443, 14985.44001, -2.516, -16, 0.06, 2.915, 1.7138, 24499.74767, 0.834, 31, -0.17, 2.732, 1.9887, 13799.82378, -4.343, -38, 0.17, 2.568, 5.4122, -7072.08751, -1.576, -25, 0.11, 2.521, 3.2427, 8470.66678, -2.238, -19, 0.07, 2.489, 4.0719, -486.32660, -3.734, -44, 0.20, 2.146, 5.6135, -1952.47998, 0.645, 7, -0.03, 1.978, 2.7291, 39414.20000, 0.199, 37, -0.21, 1.934, 1.5682, 33314.76570, 6.092, 100, -0.5, 1.871, 0.4166, 30457.20662, -1.297, 12, -0.1, 1.753, 2.0582, -8886.00570, -3.38, -47, 0.2, 1.437, 2.386, -695.87607, 0.59, 7, 0, 1.373, 3.026, -209.54947, 4.33, 51, -0.2, 1.262, 5.940, 16728.37052, 1.17, 28, -0.1, 1.224, 6.172, 6656.74859, -4.04, -41, 0.2, 1.187, 5.873, 6099.43431, -5.89, -63, 0.3, 1.177, 1.014, 31571.83518, 2.41, 56, -0.3, 1.162, 3.840, 9585.29534, 1.47, 25, -0.1, 1.143, 5.639, 8364.73984, -2.18, -19, 0.1, 1.078, 1.229, 70.98768, -1.88, -22, 0.1, 1.059, 3.326, 40528.82856, 3.91, 81, -0.4, 0.990, 5.013, 40738.37803, -0.42, 30, -0.2, 0.948, 5.687, -17772.01141, -6.75, -94, 0.5, 0.876, 0.298, -0.35232, 0, 0, 0, 0.822, 2.994, 393.02097, 0, 0, 0, 0.788, 1.836, 8326.39022, 3.05, 50, -0.2, 0.752, 4.985, 22614.84180, 0.91, 31, -0.2, 0.740, 2.875, 8330.99262, 0, 0, 0, 0.669, 0.744, -24357.77232, -4.60, -75, 0.4, 0.644, 1.314, 8393.12577, -2.18, -19, 0.1, 0.639, 5.888, 575.33849, 0, 0, 0, 0.635, 1.116, 23385.11911, -2.87, -13, 0, 0.584, 5.197, 24428.75999, 2.71, 53, -0.3, 0.583, 3.513, -9095.55517, 0.95, 4, 0, 0.572, 6.059, 29970.88002, -5.03, -32, 0.1, 0.565, 2.960, 0.32863, 1.52, 25, -0.1, 0.561, 4.001, -17981.56087, -2.43, -43, 0.2, 0.557, 0.529, 7143.07519, -0.30, 3, 0, 0.546, 2.311, 25614.37623, 4.54, 75, -0.4, 0.536, 4.229, 15752.30376, -4.99, -45, 0.2, 0.493, 3.316, -8294.9344, -1.83, -29, 0.1, 0.491, 1.744, 8362.4485, 1.21, 21, -0.1, 0.478, 1.803, -10071.6219, -5.20, -69, 0.3, 0.454, 0.857, 15333.2048, 3.66, 57, -0.3, 0.445, 2.071, 8311.7707, -2.18, -19, 0.1, 0.426, 0.345, 23452.6932, -3.44, -20, 0.1, 0.420, 4.941, 33733.8646, -2.56, -2, 0, 0.413, 1.642, 17495.2343, -1.31, -1, 0, 0.404, 1.458, 23314.1314, -0.99, 9, -0.1, 0.395, 2.132, 38299.5714, -3.51, -6, 0, 0.382, 2.700, 31781.3846, -1.92, 5, 0, 0.375, 4.827, 6376.2114, 2.17, 32, -0.2, 0.361, 3.867, 16833.1753, -0.97, 3, 0, 0.358, 5.044, 15056.4277, -4.40, -38, 0.2, 0.350, 5.157, -8257.7037, -3.40, -47, 0.2, 0.344, 4.233, 157.7344, 0, 0, 0, 0.340, 2.672, 13657.8484, -0.58, 6, 0, 0.329, 5.610, 41853.0066, 3.29, 74, -0.4, 0.325, 5.895, -39.8149, 0, 0, 0, 0.309, 4.387, 21500.2132, -2.79, -13, 0.1, 0.302, 1.278, 786.0419, 0, 0, 0, 0.302, 5.341, -24567.3218, -0.27, -24, 0.1, 0.301, 1.045, 5889.8848, -1.57, -12, 0, 0.294, 4.201, -2371.2325, -3.65, -44, 0.2, 0.293, 3.704, 21642.1886, -6.55, -57, 0.2, 0.290, 4.069, 32828.4391, 2.36, 56, -0.3, 0.289, 3.472, 31713.8105, -1.35, 12, -0.1, 0.285, 5.407, -33.7814, 0.31, 4, 0, 0.283, 5.998, -16.9207, -3.71, -44, 0.2, 0.283, 2.772, 38785.8980, 0.23, 37, -0.2, 0.274, 5.343, 15613.7420, -2.54, -16, 0.1, 0.263, 3.997, 25823.9257, 0.22, 24, -0.1, 0.254, 0.600, 24638.3095, -1.61, 2, 0, 0.253, 1.344, 6447.1991, 0.29, 10, -0.1, 0.250, 0.887, 141.9754, -3.76, -44, 0.2, 0.247, 0.317, 5329.1570, -2.10, -19, 0.1, 0.245, 0.141, 36.0484, -3.71, -44, 0.2, 0.231, 2.287, 14357.1381, -2.49, -16, 0.1, 0.227, 5.158, 2.6298, 0, 0, 0, 0.219, 5.085, 47742.8914, 1.72, 63, -0.3, 0.211, 2.145, 6638.7244, -2.18, -19, 0.1, 0.201, 4.415, 39623.7495, -4.13, -14, 0, 0.194, 2.091, 588.4927, 0, 0, 0, 0.193, 3.057, -15400.7789, -3.10, -50, 0, 0.186, 5.598, 16799.3582, -0.72, 6, 0, 0.185, 3.886, 1150.6770, 0, 0, 0, 0.183, 1.619, 7178.0144, 1.52, 25, 0, 0.181, 2.635, 8328.3391, 1.52, 25, 0, 0.181, 2.077, 8329.0437, 1.52, 25, 0, 0.179, 3.215, -9652.8694, -0.90, -18, 0, 0.176, 1.716, -8815.0180, -5.26, -69, 0, 0.175, 5.673, 550.7553, 0, 0, 0, 0.170, 2.060, 31295.0580, -5.6, -39, 0, 0.167, 1.239, 7211.7617, -0.7, 6, 0, 0.165, 4.499, 14967.4158, -0.7, 6, 0, 0.164, 3.595, 15540.4531, 0.9, 31, 0, 0.164, 4.237, 522.3694, 0, 0, 0, 0.163, 4.633, 15545.0555, -2.2, -19, 0, 0.161, 0.478, 6428.0209, -2.2, -19, 0, 0.158, 2.03, 13171.5218, -4.3, -38, 0, 0.157, 2.28, 7216.3641, -3.7, -44, 0, 0.154, 5.65, 7935.6705, 1.5, 25, 0, 0.152, 0.46, 29828.9047, -1.3, 12, 0, 0.151, 1.19, -0.7113, 0, 0, 0, 0.150, 1.42, 23942.4334, -1.0, 9, 0, 0.144, 2.75, 7753.3529, 1.5, 25, 0, 0.137, 2.08, 7213.7105, -2.2, -19, 0, 0.137, 1.44, 7214.4152, -2.2, -19, 0, 0.136, 4.46, -1185.6162, -1.8, -22, 0, 0.136, 3.03, 8000.1048, -2.2, -19, 0, 0.134, 2.83, 14756.7124, -0.7, 6, 0, 0.131, 5.05, 6821.0419, -2.2, -19, 0, 0.128, 5.99, -17214.6971, -4.9, -72, 0, 0.127, 5.35, 8721.7124, 1.5, 25, 0, 0.126, 4.49, 46628.2629, -2.0, 19, 0, 0.125, 5.94, 7149.6285, 1.5, 25, 0, 0.124, 1.09, 49067.0695, 1.1, 55, 0, 0.121, 2.88, 15471.7666, 1.2, 28, 0, 0.111, 3.92, 41643.4571, 7.6, 125, -1, 0.110, 1.96, 8904.0299, 1.5, 25, 0, 0.106, 3.30, -18.0489, -2.2, -19, 0, 0.105, 2.30, -4.9310, 1.5, 25, 0, 0.104, 2.22, -6.5590, -1.9, -22, 0, 0.101, 1.44, 1884.9059, -0.1, 0, 0, 0.100, 5.92, 5471.1324, -5.9, -63, 0, 0.099, 1.12, 15149.7333, -0.7, 6, 0, 0.096, 4.73, 15508.9972, -0.4, 10, 0, 0.095, 5.18, 7230.9835, 1.5, 25, 0, 0.093, 3.37, 39900.5266, 3.9, 81, 0, 0.092, 2.01, 25057.0619, 2.7, 53, 0, 0.092, 1.21, -79.6298, 0, 0, 0, 0.092, 1.65, -26310.2523, -4.0, -68, 0, 0.091, 1.01, 42062.5561, -1.0, 23, 0, 0.090, 6.10, 29342.5781, -5.0, -32, 0, 0.090, 4.43, 15542.4020, -0.7, 6, 0, 0.090, 3.80, 15543.1066, -0.7, 6, 0, 0.089, 4.15, 6063.3859, -2.2, -19, 0, 0.086, 4.03, 52.9691, 0, 0, 0, 0.085, 0.49, 47952.4409, -2.6, 11, 0, 0.085, 1.60, 7632.8154, 2.1, 32, 0, 0.084, 0.22, 14392.0773, -0.7, 6, 0, 0.083, 6.22, 6028.4466, -4.0, -41, 0, 0.083, 0.63, -7909.9389, 2.8, 26, 0, 0.083, 5.20, -77.5523, 0, 0, 0, 0.082, 2.74, 8786.1467, -2.2, -19, 0, 0.080, 2.43, 9166.5428, -2.8, -26, 0, 0.080, 3.70, -25405.1732, 4.1, 27, 0, 0.078, 5.68, 48857.5200, 5.4, 106, -1, 0.077, 1.85, 8315.5735, -2.2, -19, 0, 0.075, 5.46, -18191.1103, 1.9, 8, 0, 0.075, 1.41, -16238.6304, 1.3, 1, 0, 0.074, 5.06, 40110.0761, -0.4, 30, 0, 0.072, 2.10, 64.4343, -3.7, -44, 0, 0.071, 2.17, 37671.2695, -3.5, -6, 0, 0.069, 1.71, 16693.4313, -0.7, 6, 0, 0.069, 3.33, -26100.7028, -8.3, -119, 1, 0.068, 1.09, 8329.4028, 1.5, 25, 0, 0.068, 3.62, 8327.9801, 1.5, 25, 0, 0.068, 2.41, 16833.1509, -1.0, 3, 0, 0.067, 3.40, 24709.2971, -3.5, -20, 0, 0.067, 1.65, 8346.7156, -0.3, 3, 0, 0.066, 2.61, 22547.2677, 1.5, 39, 0, 0.066, 3.50, 15576.5113, -1.0, 3, 0, 0.065, 5.76, 33037.9886, -2.0, 5, 0, 0.065, 4.58, 8322.1325, -0.3, 3, 0, 0.065, 6.20, 17913.9868, 3.0, 50, 0, 0.065, 1.50, 22685.8295, -1.0, 9, 0, 0.065, 2.37, 7180.3058, -1.9, -15, 0, 0.064, 1.06, 30943.5332, 2.4, 56, 0, 0.064, 1.89, 8288.8765, 1.5, 25, 0, 0.064, 4.70, 6.0335, 0.3, 4, 0, 0.063, 2.83, 8368.5063, 1.5, 25, 0, 0.063, 5.66, -2580.7819, 0.7, 7, 0, 0.062, 3.78, 7056.3285, -2.2, -19, 0, 0.061, 1.49, 8294.9100, 1.8, 29, 0, 0.061, 0.12, -10281.1714, -0.9, -18, 0, 0.061, 3.06, -8362.4729, -1.2, -21, 0, 0.061, 4.43, 8170.9571, 1.5, 25, 0, 0.059, 5.78, -13.1179, -3.7, -44, 0, 0.059, 5.97, 6625.5702, -2.2, -19, 0, 0.058, 5.01, -0.5080, -0.3, 0, 0, 0.058, 2.73, 7161.0938, -2.2, -19, 0, 0.057, 0.19, 7214.0629, -2.2, -19, 0, 0.057, 4.00, 22199.5029, -4.7, -35, 0, 0.057, 5.38, 8119.1420, 5.8, 76, 0, 0.056, 1.07, 7542.6495, 1.5, 25, 0, 0.056, 0.28, 8486.4258, 1.5, 25, 0, 0.054, 4.19, 16655.0816, 4.6, 75, 0, 0.053, 0.72, 7267.0320, -2.2, -19, 0, 0.053, 3.12, 12.6192, 0.6, 7, 0, 0.052, 2.99, -32896.013, -1.8, -49, 0, 0.052, 3.46, 1097.708, 0, 0, 0, 0.051, 5.37, -6443.786, -1.6, -25, 0, 0.051, 1.35, 7789.401, -2.2, -19, 0, 0.051, 5.83, 40042.502, 0.2, 38, 0, 0.051, 3.63, 9114.733, 1.5, 25, 0, 0.050, 1.51, 8504.484, -2.5, -22, 0, 0.050, 5.23, 16659.684, 1.5, 25, 0, 0.050, 1.15, 7247.820, -2.5, -23, 0, 0.047, 0.25, -1290.421, 0.3, 0, 0, 0.047, 4.67, -32686.464, -6.1, -100, 0, 0.047, 3.49, 548.678, 0, 0, 0, 0.047, 2.37, 6663.308, -2.2, -19, 0, 0.046, 0.98, 1572.084, 0, 0, 0, 0.046, 2.04, 14954.262, -0.7, 6, 0, 0.046, 3.72, 6691.693, -2.2, -19, 0, 0.045, 6.19, -235.287, 0, 0, 0, 0.044, 2.96, 32967.001, -0.1, 27, 0, 0.044, 3.82, -1671.943, -5.6, -66, 0, 0.043, 5.82, 1179.063, 0, 0, 0, 0.043, 0.07, 34152.617, 1.7, 49, 0, 0.043, 3.71, 6514.773, -0.3, 0, 0, 0.043, 5.62, 15.732, -2.5, -23, 0, 0.043, 5.80, 8351.233, -2.2, -19, 0, 0.042, 0.27, 7740.199, 1.5, 25, 0, 0.042, 6.14, 15385.020, -0.7, 6, 0, 0.042, 6.13, 7285.051, -4.1, -41, 0, 0.041, 1.27, 32757.451, 4.2, 78, 0, 0.041, 4.46, 8275.722, 1.5, 25, 0, 0.040, 0.23, 8381.661, 1.5, 25, 0, 0.040, 5.87, -766.864, 2.5, 29, 0, 0.040, 1.66, 254.431, 0, 0, 0, 0.040, 0.40, 9027.981, -0.4, 0, 0, 0.040, 2.96, 7777.936, 1.5, 25, 0, 0.039, 4.67, 33943.068, 6.1, 100, 0, 0.039, 3.52, 8326.062, 1.5, 25, 0, 0.039, 3.75, 21013.887, -6.5, -57, 0, 0.039, 5.60, 606.978, 0, 0, 0, 0.039, 1.19, 8331.321, 1.5, 25, 0, 0.039, 2.84, 7211.433, -2.2, -19, 0, 0.038, 0.67, 7216.693, -2.2, -19, 0, 0.038, 6.22, 25161.867, 0.6, 28, 0, 0.038, 4.40, 7806.322, 1.5, 25, 0, 0.038, 4.16, 9179.168, -2.2, -19, 0, 0.037, 4.73, 14991.999, -0.7, 6, 0, 0.036, 0.35, 67.514, -0.6, -7, 0, 0.036, 3.70, 25266.611, -1.6, 0, 0, 0.036, 5.39, 16328.796, -0.7, 6, 0, 0.035, 1.44, 7174.248, -2.2, -19, 0, 0.035, 5.00, 15684.730, -4.4, -38, 0, 0.035, 0.39, -15.419, -2.2, -19, 0, 0.035, 6.07, 15020.385, -0.7, 6, 0, 0.034, 6.01, 7371.797, -2.2, -19, 0, 0.034, 0.96, -16623.626, -3.4, -54, 0, 0.033, 6.24, 9479.368, 1.5, 25, 0, 0.033, 3.21, 23661.896, 5.2, 82, 0, 0.033, 4.06, 8311.418, -2.2, -19, 0, 0.033, 2.40, 1965.105, 0, 0, 0, 0.033, 5.17, 15489.785, -0.7, 6, 0, 0.033, 5.03, 21986.540, 0.9, 31, 0, 0.033, 4.10, 16691.140, 2.7, 46, 0, 0.033, 5.13, 47114.589, 1.7, 63, 0, 0.033, 4.45, 8917.184, 1.5, 25, 0, 0.033, 4.23, 2.078, 0, 0, 0, 0.032, 2.33, 75.251, 1.5, 25, 0, 0.032, 2.10, 7253.878, -2.2, -19, 0, 0.032, 3.11, -0.224, 1.5, 25, 0, 0.032, 4.43, 16640.462, -0.7, 6, 0, 0.032, 5.68, 8328.363, 0, 0, 0, 0.031, 5.32, 8329.020, 3.0, 50, 0, 0.031, 3.70, 16118.093, -0.7, 6, 0, 0.030, 3.67, 16721.817, -0.7, 6, 0, 0.030, 5.27, -1881.492, -1.2, -15, 0, 0.030, 5.72, 8157.839, -2.2, -19, 0, 0.029, 5.73, -18400.313, -6.7, -94, 0, 0.029, 2.76, 16.000, -2.2, -19, 0, 0.029, 1.75, 8879.447, 1.5, 25, 0, 0.029, 0.32, 8851.061, 1.5, 25, 0, 0.029, 0.90, 14704.903, 3.7, 57, 0, 0.028, 2.90, 15595.723, -0.7, 6, 0, 0.028, 5.88, 16864.631, 0.2, 24, 0, 0.028, 0.63, 16869.234, -2.8, -26, 0, 0.028, 4.04, -18609.863, -2.4, -43, 0, 0.027, 5.83, 6727.736, -5.9, -63, 0, 0.027, 6.12, 418.752, 4.3, 51, 0, 0.027, 0.14, 41157.131, 3.9, 81, 0, 0.026, 3.80, 15.542, 0, 0, 0, 0.026, 1.68, 50181.698, 4.8, 99, -1, 0.026, 0.32, 315.469, 0, 0, 0, 0.025, 5.67, 19.188, 0.3, 0, 0, 0.025, 3.16, 62.133, -2.2, -19, 0, 0.025, 3.76, 15502.939, -0.7, 6, 0, 0.025, 4.53, 45999.961, -2.0, 19, 0, 0.024, 3.21, 837.851, -4.4, -51, 0, 0.024, 2.82, 38157.596, 0.3, 37, 0, 0.024, 5.21, 15540.124, -0.7, 6, 0, 0.024, 0.26, 14218.576, 0, 13, 0, 0.024, 3.01, 15545.384, -0.7, 6, 0, 0.024, 1.16, -17424.247, -0.6, -21, 0, 0.023, 2.34, -67.574, 0.6, 7, 0, 0.023, 2.44, 18.024, -1.9, -22, 0, 0.023, 3.70, 469.400, 0, 0, 0, 0.023, 0.72, 7136.511, -2.2, -19, 0, 0.023, 4.50, 15582.569, -0.7, 6, 0, 0.023, 2.80, -16586.395, -4.9, -72, 0, 0.023, 1.51, 80.182, 0, 0, 0, 0.023, 1.09, 5261.583, -1.5, -12, 0, 0.023, 0.56, 54956.954, -0.5, 44, 0, 0.023, 4.01, 8550.860, -2.2, -19, 0, 0.023, 4.46, 38995.448, -4.1, -14, 0, 0.023, 3.82, 2358.126, 0, 0, 0, 0.022, 3.77, 32271.125, 0.5, 34, 0, 0.022, 0.82, 15935.775, -0.7, 6, 0, 0.022, 1.07, 24013.421, -2.9, -13, 0, 0.022, 0.40, 8940.078, -2.2, -19, 0, 0.022, 2.06, 15700.489, -0.7, 6, 0, 0.022, 4.27, 15124.002, -5.0, -45, 0, 0.021, 1.16, 56071.583, 3.2, 88, 0, 0.021, 5.58, 9572.189, -2.2, -19, 0, 0.020, 1.70, -17.273, -3.7, -44, 0, 0.020, 3.05, 214.617, 0, 0, 0, 0.020, 4.41, 8391.048, -2.2, -19, 0, 0.020, 5.95, 23869.145, 2.4, 56, 0, 0.020, 0.42, 40947.927, -4.7, -21, 0, 0.019, 1.39, 5818.897, 0.3, 10, 0, 0.019, 0.71, 23873.747, -0.7, 6, 0, 0.019, 2.81, 7291.615, -2.2, -19, 0, 0.019, 5.09, 8428.018, -2.2, -19, 0, 0.019, 4.14, 6518.187, -1.6, -12, 0, 0.019, 3.85, 21.330, 0, 0, 0, 0.018, 0.66, 14445.046, -0.7, 6, 0, 0.018, 1.65, 0.966, -4.0, -48, 0, 0.018, 5.64, -17143.709, -6.8, -94, 0, 0.018, 6.01, 7736.432, -2.2, -19, 0, 0.018, 2.74, 31153.083, -1.9, 5, 0, 0.018, 4.58, 6116.355, -2.2, -19, 0, 0.018, 2.28, 46.401, 0.3, 0, 0, 0.018, 3.80, 10213.597, 1.4, 25, 0, 0.018, 2.84, 56281.132, -1.1, 36, 0, 0.018, 3.53, 8249.062, 1.5, 25, 0, 0.017, 4.43, 20871.911, -3, -13, 0, 0.017, 4.44, 627.596, 0, 0, 0, 0.017, 1.85, 628.308, 0, 0, 0, 0.017, 1.19, 8408.321, 2, 25, 0, 0.017, 1.95, 7214.056, -2, -19, 0, 0.017, 1.57, 7214.070, -2, -19, 0, 0.017, 1.65, 13870.811, -6, -60, 0, 0.017, 0.30, 22.542, -4, -44, 0, 0.017, 2.62, -119.445, 0, 0, 0, 0.016, 4.87, 5747.909, 2, 32, 0, 0.016, 4.45, 14339.108, -1, 6, 0, 0.016, 1.83, 41366.680, 0, 30, 0, 0.016, 4.53, 16309.618, -3, -23, 0, 0.016, 2.54, 15542.754, -1, 6, 0, 0.016, 6.05, 1203.646, 0, 0, 0, 0.015, 5.2, 2751.147, 0, 0, 0, 0.015, 1.8, -10699.924, -5, -69, 0, 0.015, 0.4, 22824.391, -3, -20, 0, 0.015, 2.1, 30666.756, -6, -39, 0, 0.015, 2.1, 6010.417, -2, -19, 0, 0.015, 0.7, -23729.470, -5, -75, 0, 0.015, 1.4, 14363.691, -1, 6, 0, 0.015, 5.8, 16900.689, -2, 0, 0, 0.015, 5.2, 23800.458, 3, 53, 0, 0.015, 5.3, 6035.000, -2, -19, 0, 0.015, 1.2, 8251.139, 2, 25, 0, 0.015, 3.6, -8.860, 0, 0, 0, 0.015, 0.8, 882.739, 0, 0, 0, 0.015, 3.0, 1021.329, 0, 0, 0, 0.015, 0.6, 23296.107, 1, 31, 0, 0.014, 5.4, 7227.181, 2, 25, 0, 0.014, 0.1, 7213.352, -2, -19, 0, 0.014, 4.0, 15506.706, 3, 50, 0, 0.014, 3.4, 7214.774, -2, -19, 0, 0.014, 4.6, 6665.385, -2, -19, 0, 0.014, 0.1, -8.636, -2, -22, 0, 0.014, 3.1, 15465.202, -1, 6, 0, 0.014, 4.9, 508.863, 0, 0, 0, 0.014, 3.5, 8406.244, 2, 25, 0, 0.014, 1.3, 13313.497, -8, -82, 0, 0.014, 2.8, 49276.619, -3, 0, 0, 0.014, 0.1, 30528.194, -3, -10, 0, 0.013, 1.7, 25128.050, 1, 31, 0, 0.013, 2.9, 14128.405, -1, 6, 0, 0.013, 3.4, 57395.761, 3, 80, 0, 0.013, 2.7, 13029.546, -1, 6, 0, 0.013, 3.9, 7802.556, -2, -19, 0, 0.013, 1.6, 8258.802, -2, -19, 0, 0.013, 2.2, 8417.709, -2, -19, 0, 0.013, 0.7, 9965.210, -2, -19, 0, 0.013, 3.4, 50391.247, 0, 48, 0, 0.013, 3.0, 7134.433, -2, -19, 0, 0.013, 2.9, 30599.182, -5, -31, 0, 0.013, 3.6, -9723.857, 1, 0, 0, 0.013, 4.8, 7607.084, -2, -19, 0, 0.012, 0.8, 23837.689, 1, 35, 0, 0.012, 3.6, 4.409, -4, -44, 0, 0.012, 5.0, 16657.031, 3, 50, 0, 0.012, 4.4, 16657.735, 3, 50, 0, 0.012, 1.1, 15578.803, -4, -38, 0, 0.012, 6.0, -11.490, 0, 0, 0, 0.012, 1.9, 8164.398, 0, 0, 0, 0.012, 2.4, 31852.372, -4, -17, 0, 0.012, 2.4, 6607.085, -2, -19, 0, 0.012, 4.2, 8359.870, 0, 0, 0, 0.012, 0.5, 5799.713, -2, -19, 0, 0.012, 2.7, 7220.622, 0, 0, 0, 0.012, 4.3, -139.720, 0, 0, 0, 0.012, 2.3, 13728.836, -2, -16, 0, 0.011, 3.6, 14912.146, 1, 31, 0, 0.011, 4.7, 14916.748, -2, -19, 0}, - //ML1 - { - 1.67680, 4.66926, 628.301955, -0.0266, 0.1, -0.005, 0.51642, 3.3721, 6585.760910, -2.158, -18.9, 0.09, 0.41383, 5.7277, 14914.452335, -0.635, 6.2, -0.04, 0.37115, 3.9695, 7700.389469, 1.550, 25.0, -0.12, 0.27560, 0.7416, 8956.993380, 1.496, 25.1, -0.13, 0.24599, 4.2253, -2.301200, 1.523, 25.1, -0.12, 0.07118, 0.1443, 7842.36482, -2.211, -19, 0.08, 0.06128, 2.4998, 16171.05625, -0.688, 6, 0, 0.04516, 0.443, 8399.67910, -0.36, 3, 0, 0.04048, 5.771, 14286.15038, -0.61, 6, 0, 0.03747, 4.626, 1256.60391, -0.05, 0, 0, 0.03707, 3.415, 5957.45895, -2.13, -19, 0.1, 0.03649, 1.800, 23243.14376, 0.89, 31, -0.2, 0.02438, 0.042, 16029.08089, 3.07, 50, -0.2, 0.02165, 1.017, -1742.93051, -3.68, -44, 0.2, 0.01923, 3.097, 17285.68480, 3.02, 50, -0.3, 0.01692, 1.280, 0.3286, 1.52, 25, -0.1, 0.01361, 0.298, 8326.3902, 3.05, 50, -0.2, 0.01293, 4.013, 7072.0875, 1.58, 25, -0.1, 0.01276, 4.413, 8330.9926, 0, 0, 0, 0.01270, 0.101, 8470.6668, -2.24, -19, 0.1, 0.01097, 1.203, 22128.5152, -2.82, -13, 0, 0.01088, 2.545, 15542.7543, -0.66, 6, 0, 0.00835, 0.190, 7214.0629, -2.18, -19, 0.1, 0.00734, 4.855, 24499.7477, 0.83, 31, -0.2, 0.00686, 5.130, 13799.8238, -4.34, -38, 0.2, 0.00631, 0.930, -486.3266, -3.73, -44, 0, 0.00585, 0.699, 9585.2953, 1.5, 25, 0, 0.00566, 4.073, 8328.3391, 1.5, 25, 0, 0.00566, 0.638, 8329.0437, 1.5, 25, 0, 0.00539, 2.472, -1952.4800, 0.6, 7, 0, 0.00509, 2.88, -0.7113, 0, 0, 0, 0.00469, 3.56, 30457.2066, -1.3, 12, 0, 0.00387, 0.78, -0.3523, 0, 0, 0, 0.00378, 1.84, 22614.8418, 0.9, 31, 0, 0.00362, 5.53, -695.8761, 0.6, 7, 0, 0.00317, 2.80, 16728.3705, 1.2, 28, 0, 0.00303, 6.07, 157.7344, 0, 0, 0, 0.00300, 2.53, 33.7570, -0.3, -4, 0, 0.00295, 4.16, 31571.8352, 2.4, 56, 0, 0.00289, 5.98, 7211.7617, -0.7, 6, 0, 0.00285, 2.06, 15540.4531, 0.9, 31, 0, 0.00283, 2.65, 2.6298, 0, 0, 0, 0.00282, 6.17, 15545.0555, -2.2, -19, 0, 0.00278, 1.23, -39.8149, 0, 0, 0, 0.00272, 3.82, 7216.3641, -3.7, -44, 0, 0.00270, 4.37, 70.9877, -1.9, -22, 0, 0.00256, 5.81, 13657.8484, -0.6, 6, 0, 0.00244, 5.64, -0.2237, 1.5, 25, 0, 0.00240, 2.96, 8311.7707, -2.2, -19, 0, 0.00239, 0.87, -33.7814, 0.3, 4, 0, 0.00216, 2.31, 15.9995, -2.2, -19, 0, 0.00186, 3.46, 5329.1570, -2.1, -19, 0, 0.00169, 2.40, 24357.772, 4.6, 75, 0, 0.00161, 5.80, 8329.403, 1.5, 25, 0, 0.00161, 5.20, 8327.980, 1.5, 25, 0, 0.00160, 4.26, 23385.119, -2.9, -13, 0, 0.00156, 1.26, 550.755, 0, 0, 0, 0.00155, 1.25, 21500.213, -2.8, -13, 0, 0.00152, 0.60, -16.921, -3.7, -44, 0, 0.00150, 2.71, -79.630, 0, 0, 0, 0.00150, 5.29, 15.542, 0, 0, 0, 0.00148, 1.06, -2371.232, -3.7, -44, 0, 0.00141, 0.77, 8328.691, 1.5, 25, 0, 0.00141, 3.67, 7143.075, -0.3, 0, 0, 0.00138, 5.45, 25614.376, 4.5, 75, 0, 0.00129, 4.90, 23871.446, 0.9, 31, 0, 0.00126, 4.03, 141.975, -3.8, -44, 0, 0.00124, 6.01, 522.369, 0, 0, 0, 0.00120, 4.94, -10071.622, -5.2, -69, 0, 0.00118, 5.07, -15.419, -2.2, -19, 0, 0.00107, 3.49, 23452.693, -3.4, -20, 0, 0.00104, 4.78, 17495.234, -1.3, 0, 0, 0.00103, 1.44, -18.049, -2.2, -19, 0, 0.00102, 5.63, 15542.402, -0.7, 6, 0, 0.00102, 2.59, 15543.107, -0.7, 6, 0, 0.00100, 4.11, -6.559, -1.9, -22, 0, 0.00097, 0.08, 15400.779, 3.1, 50, 0, 0.00096, 5.84, 31781.385, -1.9, 5, 0, 0.00094, 1.08, 8328.363, 0, 0, 0, 0.00094, 2.46, 16799.358, -0.7, 6, 0, 0.00094, 1.69, 6376.211, 2.2, 32, 0, 0.00093, 3.64, 8329.020, 3.0, 50, 0, 0.00093, 2.65, 16655.082, 4.6, 75, 0, 0.00090, 1.90, 15056.428, -4.4, -38, 0, 0.00089, 1.59, 52.969, 0, 0, 0, 0.00088, 2.02, -8257.704, -3.4, -47, 0, 0.00088, 3.02, 7213.711, -2.2, -19, 0, 0.00087, 0.50, 7214.415, -2.2, -19, 0, 0.00087, 0.49, 16659.684, 1.5, 25, 0, 0.00082, 5.64, -4.931, 1.5, 25, 0, 0.00079, 5.17, 13171.522, -4.3, -38, 0, 0.00076, 3.60, 29828.905, -1.3, 12, 0, 0.00076, 4.08, 24567.322, 0.3, 24, 0, 0.00076, 4.58, 1884.906, -0.1, 0, 0, 0.00073, 0.33, 31713.811, -1.4, 12, 0, 0.00073, 0.93, 32828.439, 2.4, 56, 0, 0.00071, 5.91, 38785.898, 0.2, 37, 0, 0.00069, 2.20, 15613.742, -2.5, -16, 0, 0.00066, 3.87, 15.732, -2.5, -23, 0, 0.00066, 0.86, 25823.926, 0.2, 24, 0, 0.00065, 2.52, 8170.957, 1.5, 25, 0, 0.00063, 0.18, 8322.132, -0.3, 0, 0, 0.00060, 5.84, 8326.062, 1.5, 25, 0, 0.00060, 5.15, 8331.321, 1.5, 25, 0, 0.00060, 2.18, 8486.426, 1.5, 25, 0, 0.00058, 2.30, -1.731, -4, -44, 0, 0.00058, 5.43, 14357.138, -2, -16, 0, 0.00057, 3.09, 8294.910, 2, 29, 0, 0.00057, 4.67, -8362.473, -1, -21, 0, 0.00056, 4.15, 16833.151, -1, 0, 0, 0.00054, 1.93, 7056.329, -2, -19, 0, 0.00054, 5.27, 8315.574, -2, -19, 0, 0.00052, 5.6, 8311.418, -2, -19, 0, 0.00052, 2.7, -77.552, 0, 0, 0, 0.00051, 4.3, 7230.984, 2, 25, 0, 0.00050, 0.4, -0.508, 0, 0, 0, 0.00049, 5.4, 7211.433, -2, -19, 0, 0.00049, 4.4, 7216.693, -2, -19, 0, 0.00049, 4.3, 16864.631, 0, 24, 0, 0.00049, 2.2, 16869.234, -3, -26, 0, 0.00047, 6.1, 627.596, 0, 0, 0, 0.00047, 5.0, 12.619, 1, 7, 0, 0.00045, 4.9, -8815.018, -5, -69, 0, 0.00044, 1.6, 62.133, -2, -19, 0, 0.00042, 2.9, -13.118, -4, -44, 0, 0.00042, 4.1, -119.445, 0, 0, 0, 0.00041, 4.3, 22756.817, -3, -13, 0, 0.00041, 3.6, 8288.877, 2, 25, 0, 0.00040, 0.5, 6663.308, -2, -19, 0, 0.00040, 1.1, 8368.506, 2, 25, 0, 0.00039, 4.1, 6443.786, 2, 25, 0, 0.00039, 3.1, 16657.383, 3, 50, 0, 0.00038, 0.1, 16657.031, 3, 50, 0, 0.00038, 3.0, 16657.735, 3, 50, 0, 0.00038, 4.6, 23942.433, -1, 9, 0, 0.00037, 4.3, 15385.020, -1, 6, 0, 0.00037, 5.0, 548.678, 0, 0, 0, 0.00036, 1.8, 7213.352, -2, -19, 0, 0.00036, 1.7, 7214.774, -2, -19, 0, 0.00035, 1.1, 7777.936, 2, 25, 0, 0.00035, 1.6, -8.860, 0, 0, 0, 0.00035, 4.4, 23869.145, 2, 56, 0, 0.00035, 2.0, 6691.693, -2, -19, 0, 0.00034, 1.3, -1185.616, -2, -22, 0, 0.00034, 2.2, 23873.747, -1, 6, 0, 0.00033, 2.0, -235.287, 0, 0, 0, 0.00033, 3.1, 17913.987, 3, 50, 0, 0.00033, 1.0, 8351.233, -2, -19, 0}, - //ML2 - {0.004870, 4.6693, 628.30196, -0.027, 0, -0.01, 0.002280, 2.6746, -2.30120, 1.523, 25, -0.12, 0.001500, 3.372, 6585.76091, -2.16, -19, 0.1, 0.001200, 5.728, 14914.45233, -0.64, 6, 0, 0.001080, 3.969, 7700.38947, 1.55, 25, -0.1, 0.000800, 0.742, 8956.99338, 1.50, 25, -0.1, 0.000254, 6.002, 0.3286, 1.52, 25, -0.1, 0.000210, 0.144, 7842.3648, -2.21, -19, 0, 0.000180, 2.500, 16171.0562, -0.7, 6, 0, 0.000130, 0.44, 8399.6791, -0.4, 3, 0, 0.000126, 5.03, 8326.3902, 3.0, 50, 0, 0.000120, 5.77, 14286.1504, -0.6, 6, 0, 0.000118, 5.96, 8330.9926, 0, 0, 0, 0.000110, 1.80, 23243.1438, 0.9, 31, 0, 0.000110, 3.42, 5957.4590, -2.1, -19, 0, 0.000110, 4.63, 1256.6039, -0.1, 0, 0, 0.000099, 4.70, -0.7113, 0, 0, 0, 0.000070, 0.04, 16029.0809, 3.1, 50, 0, 0.000070, 5.14, 8328.3391, 1.5, 25, 0, 0.000070, 5.85, 8329.0437, 1.5, 25, 0, 0.000060, 1.02, -1742.9305, -3.7, -44, 0, 0.000060, 3.10, 17285.6848, 3.0, 50, 0, 0.000054, 5.69, -0.352, 0, 0, 0, 0.000043, 0.52, 15.542, 0, 0, 0, 0.000041, 2.03, 2.630, 0, 0, 0, 0.000040, 0.10, 8470.667, -2.2, -19, 0, 0.000040, 4.01, 7072.088, 1.6, 25, 0, 0.000036, 2.93, -8.860, -0.3, 0, 0, 0.000030, 1.20, 22128.515, -2.8, -13, 0, 0.000030, 2.54, 15542.754, -0.7, 6, 0, 0.000027, 4.43, 7211.762, -0.7, 6, 0, 0.000026, 0.51, 15540.453, 0.9, 31, 0, 0.000026, 1.44, 15545.055, -2.2, -19, 0, 0.000025, 5.37, 7216.364, -3.7, -44, 0}, - //ML3 - {0.00001200, 1.041, -2.3012, 1.52, 25, -0.1, 0.00000170, 0.31, -0.711, 0, 0, 0}}, - - { //精度1角秒 - //MB0 - {18461.240, 0.05710892, 8433.466157492, -0.6400617, -0.5345, -0.00294, 1010.167, 2.412663, 16762.15758211, 0.88286, 24.537, -0.1265, 999.694, 5.440038, -104.77473287, 2.16299, 25.606, -0.1207, 623.652, 0.915047, 7109.28813249, -0.02177, 6.746, -0.0379, 199.484, 1.815303, 15647.5290228, -2.82482, -19.39, 0.0799, 166.574, 4.842677, -1219.4032921, -1.5447, -18.33, 0.086, 117.261, 4.17086, 23976.2204475, -1.3019, 5.68, -0.044, 61.912, 4.76822, 25090.8490067, 2.4058, 49.61, -0.250, 33.357, 3.27060, 15437.979557, 1.5012, 31.8, -0.161, 31.760, 1.51241, 8223.916692, 3.6859, 50.7, -0.244, 29.577, 0.95817, 6480.986177, 0.0049, 6.7, -0.032, 15.566, 2.4871, -9548.094717, -3.068, -43.4, 0.21, 15.122, 0.2432, 32304.911872, 0.221, 30.7, -0.17, 12.094, 4.0135, 7737.590088, -0.048, 6.8, -0.04, 8.868, 1.8584, 15019.227068, -2.798, -19.5, 0.09, 8.045, 5.3812, 8399.709110, -0.332, 3.1, -0.02, 7.959, 4.2140, 23347.918492, -1.275, 5.6, -0.04, 7.435, 4.8858, -1847.705247, -1.518, -18.4, 0.09, 6.731, 3.8274, -16133.855627, -0.910, -24.5, 0.12, 6.580, 2.6732, 14323.350998, -2.207, -12.1, 0.04, 6.460, 3.1556, 9061.768113, -0.667, -0.5, -0.01, 6.296, 0.1713, 25300.398472, -1.920, -1.6, -0.01, 5.632, 0.8000, 733.076688, -2.190, -26, 0.12, 5.368, 2.1140, 16204.843302, -0.971, 3, -0.02, 5.311, 5.5111, 17390.459537, 0.856, 25, -0.13, 5.076, 2.2553, 523.52722, 2.136, 26, -0.13, 4.840, 6.1830, -7805.16420, 0.613, 1, 0, 4.806, 5.1414, -662.08901, 0.309, 4, -0.02, 3.984, 0.8406, 33419.54043, 3.929, 75, -0.37, 3.674, 5.0288, 22652.04242, -0.684, 13, -0.08, 2.998, 5.9291, 31190.28331, -3.487, -13, 0.04, 2.799, 2.1842, -16971.70705, 3.443, 27, -0.11, 2.414, 3.5735, 22861.59189, -5.010, -38, 0.16, 2.186, 3.9424, -9757.64418, 1.258, 8, -0.03, 2.146, 5.6262, 23766.67098, 3.024, 57, -0.29, 1.766, 3.3137, 14809.67760, 1.528, 32, -0.2, 1.624, 2.6013, 7318.83760, -4.35, -44, 0.2, 1.581, 3.8680, 16552.60812, 5.21, 76, -0.4, 1.520, 2.599, 40633.60330, 1.74, 56, -0.3, 1.516, 0.132, -17876.78614, -4.59, -68, 0.3, 1.510, 3.927, 8399.68473, -0.33, 3, 0, 1.318, 4.914, 16275.83098, -2.85, -19, 0.1, 1.264, 0.986, 24604.52240, -1.33, 6, 0, 1.192, 2.001, 39518.97474, -1.96, 12, -0.1, 1.135, 0.286, 31676.60992, 0.25, 31, -0.2, 1.086, 1.001, 5852.68422, 0.03, 7, 0, 1.019, 2.527, 33629.08990, -0.40, 23, -0.1, 0.823, 0.086, 16066.28151, 1.47, 32, -0.2, 0.804, 1.957, -33.78706, 0.28, 4, 0, 0.803, 5.212, 16833.14526, -1.00, 3, 0, 0.793, 1.472, -24462.54705, -2.43, -50, 0.2, 0.791, 1.658, -591.10134, -1.57, -18, 0.1, 0.667, 4.470, 24533.53473, 0.55, 28, -0.1, 0.650, 2.530, -10176.39667, -3.04, -43, 0.2, 0.639, 1.583, 25719.15096, 2.38, 50, -0.3, 0.634, 0.318, 5994.65957, -3.73, -37, 0.2, 0.631, 2.147, 8435.76736, -2.16, -26, 0.1, 0.630, 1.109, 8431.16496, 0.88, 25, -0.1, 0.596, 2.716, 13695.04904, -2.18, -12, 0.1, 0.589, 1.214, 7666.60241, 1.83, 29, -0.1, 0.473, 1.101, 30980.7338, 0.84, 38, -0.2, 0.456, 0.116, -71.0177, 1.85, 22, -0.1, 0.430, 2.786, -8990.7804, -1.21, -21, 0.1, 0.416, 1.454, 16728.4005, 1.19, 28, -0.1, 0.415, 5.072, 22023.7405, -0.66, 13, -0.1, 0.383, 4.257, 22719.6165, -1.25, 6, 0, 0.352, 2.972, 14880.6653, -0.35, 10, -0.1, 0.339, 5.972, 30561.9814, -3.46, -13, 0, 0.329, 1.587, -18086.3356, -0.26, -17, 0.1, 0.326, 1.016, 8467.2232, -0.95, -4, 0, 0.315, 1.902, 14390.9251, -2.77, -20, 0.1, 0.313, 4.611, 8852.2186, 3.66, 51, -0.2, 0.305, 0.616, 6551.9739, -1.88, -15, 0.1, 0.301, 4.728, -7595.6147, -3.71, -51, 0.2, 0.299, 1.874, 7143.0452, -0.33, 3, 0, 0.291, 3.156, -1428.9528, 2.78, 33, -0.2, 0.269, 4.929, -2476.0072, -1.49, -18, 0.1, 0.263, 3.196, 41748.2319, 5.45, 100, -0.5, 0.254, 3.387, -1009.8538, -5.87, -70, 0.3, 0.245, 1.930, 32514.4613, -4.10, -20, 0.1, 0.237, 3.342, 32933.2138, 0.19, 31, -0.2, 0.214, 3.617, 22233.2899, -4.98, -38, 0.2, 0.213, 4.357, 47847.6662, -0.44, 37, -0.2, 0.206, 3.872, 23418.9062, -3.16, -16, 0.1, 0.172, 5.772, 14951.6530, -2.2, -12, 0, 0.158, 2.04, 38890.6728, -1.9, 12, 0, 0.146, 1.70, 32095.3624, 4.5, 82, 0, 0.145, 4.29, 40843.1528, -2.6, 5, 0, 0.139, 2.90, 7876.1519, -2.5, -23, 0, 0.138, 4.95, 48962.2947, 3.3, 81, 0, 0.134, 3.97, 8365.8920, -0.1, 7, 0, 0.134, 4.06, -26205.4776, -6.1, -94, 0, 0.130, 1.40, -8643.0156, 5.0, 52, 0, 0.129, 5.67, 23138.3690, 3.1, 57, 0, 0.124, 2.64, 40005.3013, 1.8, 56, 0, 0.118, 4.88, 41957.7813, 1.1, 49, 0, 0.113, 3.78, -15505.5537, -0.9, -24, 0, 0.113, 4.87, 16904.1329, -2.9, -19, 0, 0.113, 1.84, 23280.3444, -0.7, 13, 0, 0.110, 0.43, -17319.4719, -2.7, -47, 0, 0.105, 1.61, 37.2006, -1.6, -18, 0, 0.102, 1.28, 25161.8367, 0.5, 28, 0, 0.095, 0.76, 1361.3786, -2.2, -25, 0, 0.094, 0.50, 29866.1053, -2.9, -6, 0, 0.092, 6.22, 24881.2995, 6.7, 101, 0, 0.088, 3.99, -10385.9461, 1.3, 8, 0, 0.085, 4.71, 70.9933, -1.9, -22, 0, 0.084, 0.86, 15613.7720, -2.5, -16, 0, 0.081, 4.43, 21537.4139, -4.4, -31, 0, 0.080, 1.86, -8365.9521, 0, -7, 0, 0.080, 0, 16728.3762, 1.2, 28, 0, 0.079, 2.44, -8919.7928, -3.1, -43, 0, 0.078, 3.69, -452.5395, -4.0, -48, 0, 0.075, 5.40, -32791.2385, -4.0, -75, 0, 0.073, 5.80, -1185.6462, -1.9, -22, 0, 0.070, 3.46, 16759.8564, 2.4, 50, 0, 0.069, 3.36, 14181.3756, 1.6, 32, 0, 0.068, 4.50, 16764.4588, -0.6, -1, 0, 0.067, 4.74, 8446.0854, 0, 7, 0, 0.066, 5.86, 24185.7699, -5.6, -46, 0, 0.064, 0.54, 32862.2262, 2.1, 53, 0, 0.063, 2.44, 24394.9729, 3.0, 57, 0, 0.063, 1.77, 5785.1101, 0.6, 14, 0, 0.062, 2.64, 6690.5356, -4.3, -45, 0, 0.062, 2.21, 1151.8292, 2.1, 26, 0, 0.062, 3.94, 34047.8424, 3.9, 75, 0, 0.060, 1.40, 38404.3462, -5.7, -32, 0, 0.058, 0.33, 31048.3080, 0.3, 31, 0, 0.057, 3.11, 9690.0701, -0.7, 0, 0, 0.057, 1.14, 30352.4319, 0.9, 38, 0, 0.056, 6.00, 8504.4538, -2.5, -22, 0, 0.055, 5.47, 18018.7615, 0.8, 25, 0, 0.055, 0.17, -18505.0881, -4.6, -69, 0, 0.055, 0.76, -9129.3422, 1.2, 8, 0, 0.054, 5.70, 7947.1396, -4.4, -44, 0, 0.053, 0.36, 5366.358, -3.7, -37, 0, 0.052, 4.01, -68.726, -1.5, -18, 0, 0.051, 2.74, 31818.585, -3.5, -13, 0, 0.051, 0.98, 16798.206, -2.8, -19, 0, 0.050, 5.94, 8293.747, -0.3, 0, 0, 0.049, 1.52, 15090.215, -4.7, -41, 0, 0.048, 3.46, 39309.425, 2.4, 63, 0, 0.046, 3.21, 23942.463, -1.0, 9, 0, 0.046, 3.35, 7143.070, -0.3, 0, 0, 0.042, 3.76, 46733.038, -4.1, -7, 0, 0.042, 5.18, 8288.351, 0, 7, 0, 0.040, 3.37, 16795.915, 0.6, 21, 0, 0.039, 4.54, -1776.718, -3.4, -40, 0, 0.039, 0.04, 8439.500, -0.3, 0, 0, 0.037, 3.91, 8479.867, -0.3, 0, 0, 0.037, 2.86, 38194.797, -1.3, 19, 0, 0.036, 1.04, 5224.382, 0.1, 7, 0, 0.036, 3.57, 15995.294, 3.4, 54, 0, 0.036, 5.33, 23209.357, 1.2, 35, 0, 0.035, 1.02, 8452.654, -0.3, 0, 0, 0.035, 4.31, 8294.904, 1.8, 29, 0, 0.035, 2.76, 13066.747, -2.2, -12, 0, 0.034, 6.07, 15508.967, -0.4, 10, 0, 0.032, 1.89, -17529.021, 1.6, 0, 0, 0.031, 5.70, 41261.905, 1.7, 56, 0, 0.031, 5.33, 30075.655, -7.2, -57, 0, 0.031, 5.97, -40.340, -1.5, -18, 0, 0.030, 2.87, 6533.950, 0, 7, 0, 0.030, 0.36, 49171.844, -1.1, 30, 0, 0.030, 4.40, 47219.364, -0.4, 37, 0, 0.030, 0.39, 23489.894, -5.0, -38, 0, 0.029, 5.12, 21395.439, -0.6, 13, 0, 0.029, 2.62, 8715.153, -0.3, 0, 0, 0.029, 2.94, 16826.592, -2.8, -19, 0, 0.028, 6.23, 31747.598, -1.6, 9, 0, 0.028, 0.43, 56176.358, 1.1, 62, 0, 0.027, 2.32, 8792.706, -0.3, 0, 0, 0.026, 3.02, 14252.363, -0.3, 10, 0, 0.025, 5.10, 40147.277, -2.0, 12, 0, 0.025, 4.59, 8433.795, 0.9, 25, 0, 0.025, 4.95, 8433.138, -2.2, -26, 0, 0.025, 1.03, -9338.545, -7.4, -95, 0, 0.024, 0.68, 17180.910, 5.2, 76, 0, 0.024, 3.81, 25057.092, 2.7, 53, 0, 0.024, 6.02, 29933.679, -3.4, -13, 0, 0.024, 2.37, -15924.306, -5.2, -76, 0, 0.024, 3.46, 8681.372, 0, 7, 0, 0.023, 2.80, 7108.936, 0, 7, 0, 0.023, 2.17, 7109.640, 0, 7, 0, 0.023, 2.57, -10804.699, -3.0, -44, 0, 0.022, 1.21, 6323.246, 0, 7, 0, 0.022, 3.22, 8259.965, 0, 7, 0, 0.022, 1.97, 7106.987, 1.5, 32, 0, 0.022, 3.00, 7111.589, -1.5, -18, 0, 0.022, 1.22, 14532.900, -6.5, -63, 0, 0.021, 0.66, 5923.672, -1.8, -15, 0, 0.021, 0.69, 24047.208, -3.2, -16, 0, 0.020, 5.51, -26415.027, -1.8, -42, 0, 0.020, 3.70, 16745.237, -2.8, -19, 0, 0.020, 1.26, 7038.300, 1.9, 29, 0, 0.020, 5.78, 6716.267, 0, 7, 0, 0.020, 3.76, 7895.330, 0, 7, 0, 0.019, 0.44, -121.695, -1.5, -18, 0, 0.018, 2.16, 15576.541, -0.9, 0, 0, 0.018, 0.09, -17248.484, -4.6, -68, 0, 0.018, 6.14, -7176.862, 0.6, 0, 0, 0.018, 5.55, 50076.923, 7.0, 125, -1, 0.017, 2.47, 8257.674, 3, 47, 0, 0.017, 4.20, 31609.036, 1, 38, 0, 0.017, 0.50, 175.762, -4, -48, 0, 0.017, 3.20, -2057.255, 3, 33, 0}, - //MB1 - {0.07430, 4.0998, 6480.98618, 0.005, 7, -0.03, 0.03043, 0.872, 7737.59009, -0.05, 7, 0, 0.02229, 5.000, 15019.22707, -2.80, -19, 0.1, 0.01999, 1.072, 23347.91849, -1.28, 6, 0, 0.01869, 1.744, -1847.70525, -1.52, -18, 0.1, 0.01696, 5.597, 16133.8556, 0.91, 24, -0.1, 0.01623, 0.014, 9061.7681, -0.67, 0, 0, 0.01419, 3.942, 733.0767, -2.19, -26, 0.1, 0.01338, 2.370, 17390.4595, 0.86, 25, -0.1, 0.01304, 5.633, 8399.6847, -0.33, 3, 0, 0.01279, 0.886, -523.5272, -2.14, -26, 0.1, 0.01215, 3.242, 7805.1642, -0.61, -1, 0, 0.01088, 3.686, 8435.7674, -2.16, -26, 0.1, 0.01088, 5.853, 8431.1650, 0.88, 25, -0.1, 0.00546, 4.143, 5852.6842, 0, 7, 0, 0.00443, 0.17, 14809.6776, 1.5, 32, 0, 0.00342, 2.24, 8399.7091, -0.3, 3, 0, 0.00330, 1.77, 16275.8310, -2.9, -19, 0, 0.00318, 4.13, 24604.5224, -1.3, 6, 0, 0.00296, 0.90, 7109.2881, 0, 7, 0, 0.00285, 3.43, 31676.6099, 0.2, 31, 0, 0.00207, 3.23, 16066.2815, 1.5, 32, 0, 0.00202, 2.07, 16833.1453, -1.0, 3, 0, 0.00202, 5.10, -33.7871, 0.3, 4, 0, 0.00200, 1.67, 24462.5471, 2.4, 50, 0, 0.00198, 4.80, -591.1013, -1.6, -18, 0, 0.00193, 1.12, 22719.6165, -1.2, 6, 0, 0.00164, 5.67, -10176.397, -3.0, -43, 0, 0.00161, 4.73, 25719.151, 2.4, 50, 0, 0.00158, 5.04, 14390.925, -2.8, -20, 0, 0.00149, 5.86, 13695.049, -2.2, -12, 0, 0.00135, 1.79, -2476.007, -1.5, -18, 0, 0.00121, 1.93, 16759.856, 2.4, 50, 0, 0.00117, 6.04, 16764.459, -0.6, 0, 0, 0.00104, 1.93, 22023.740, -0.7, 13, 0, 0.00085, 2.83, 30561.981, -3.5, -13, 0, 0.00079, 4.81, -8852.219, -3.7, -51, 0, 0.00076, 1.59, -7595.615, -3.7, -51, 0, 0.00075, 2.91, 8433.795, 0.9, 25, 0, 0.00075, 0.35, 8433.138, -2.2, -26, 0, 0.00073, 0.13, 70.993, -1.9, -22, 0, 0.00069, 1.70, 16728.376, 1.2, 28, 0, 0.00068, 0.83, 8365.892, -0.1, 7, 0, 0.00060, 0.20, 32933.214, 0.2, 31, 0, 0.00060, 0.31, 8446.085, 0, 7, 0}, - //MB2 - {0.000220, 4.100, 6480.9862, 0, 7, 0, 0.000101, 5.24, 8435.7674, -2.2, -26, 0, 0.000101, 4.30, 8431.1650, 0.9, 25, 0, 0.000090, 0.87, 7737.5901, 0, 7, 0, 0.000060, 1.07, 23347.9185, -1.3, 6, 0, 0.000060, 5.00, 15019.2271, -2.8, -19, 0, 0.000050, 1.74, -1847.705, -1.5, -18, 0, 0.000050, 5.60, 16133.856, 0.9, 24, 0, 0.000050, 0.01, 9061.768, -0.7, 0, 0, 0.000040, 3.24, 7805.164, -0.6, 0, 0, 0.000040, 3.94, 733.077, -2.2, -26, 0, 0.000040, 0.89, -523.527, -2.1, -26, 0}}, - - { //精度1千米 - //MR0 - {385000.510, 0, 0, 0, 0, 0, 20905.354, 5.4971472, 8328.691424623, 1.522924, 25.0719, -0.12360, 3699.111, 4.8997864, 7214.06286536, -2.184756, -18.860, 0.0828, 2955.967, 0.972156, 15542.75428998, -0.66183, 6.212, -0.0408, 569.925, 1.569516, 16657.3828492, 3.04585, 50.14, -0.2472, 246.158, 5.68582, -1114.6285593, -3.7077, -43.93, 0.206, 204.586, 1.01528, 14914.4523348, -0.6352, 6.15, -0.035, 170.733, 3.32771, 23871.4457146, 0.8611, 31.28, -0.164, 152.138, 4.94291, 6585.7609101, -2.1581, -18.92, 0.088, 129.620, 0.74291, -7700.3894694, -1.5496, -25.01, 0.118, 108.743, 5.19847, 7771.3771450, -0.3309, 3.1, -0.020, 104.755, 2.31243, 8956.993380, 1.4963, 25.1, -0.129, 79.661, 5.38293, -8538.240890, 2.8030, 26.1, -0.118, 48.888, 6.24006, 628.301955, -0.0266, 0.1, -0.005, 34.783, 2.73035, 22756.817155, -2.847, -12.6, 0.04, 30.824, 4.0706, 16171.056245, -0.688, 6.3, -0.05, 24.208, 1.7151, 7842.364821, -2.211, -18.8, 0.08, 23.210, 3.9251, 24986.074274, 4.569, 75.2, -0.37, 21.636, 0.3748, 14428.125731, -4.370, -37.7, 0.17, 16.675, 2.0137, 8399.679100, -0.358, 3.2, -0.03, 14.403, 3.3303, -9443.319984, -5.231, -69.0, 0.33, 12.831, 3.3708, 23243.143759, 0.888, 31.2, -0.16, 11.650, 5.0859, 31085.508580, -1.324, 12, -0.08, 10.445, 5.6833, 32200.13714, 2.384, 56, -0.29, 10.321, 0.8579, -1324.17803, 0.618, 7, -0.03, 10.056, 5.7290, -1742.93051, -3.681, -44, 0.21, 9.884, 1.0584, 14286.15038, -0.609, 6, -0.03, 8.752, 4.7856, -9652.86945, -0.905, -18, 0.09, 8.379, 5.9845, -557.31428, -1.854, -22, 0.10, 7.003, 4.6705, -16029.08089, -3.072, -50, 0.24, 6.322, 1.2708, 16100.06857, 1.192, 28, -0.14, 5.751, 4.6680, 17285.68480, 3.019, 50, -0.25, 4.950, 4.9860, 5957.45895, -2.131, -19, 0.09, 4.421, 4.5969, -209.54947, 4.326, 51, -0.24, 4.131, 3.2135, 7004.51340, 2.141, 32, -0.16, 3.958, 2.7735, 22128.51520, -2.820, -13, 0.05, 3.258, 0.6735, 14985.44001, -2.52, -16, 0.1, 3.148, 0.114, 16866.93231, -1.28, -1, 0, 2.616, 0.143, 24499.74767, 0.83, 31, -0.2, 2.354, 1.672, 8470.66678, -2.24, -19, 0.1, 2.117, 0.700, -7072.08751, -1.58, -25, 0.1, 1.897, 0.418, 13799.82378, -4.34, -38, 0.2, 1.739, 3.629, -8886.00570, -3.38, -47, 0.2, 1.571, 5.129, 30457.20662, -1.30, 12, -0.1, 1.423, 1.158, 39414.20000, 0.20, 37, -0.2, 1.419, 6.171, 23314.13143, -0.99, 9, -0.1, 1.166, 2.269, 9585.29534, 1.47, 25, -0.1, 1.117, 6.281, 33314.76570, 6.09, 100, -0.5, 1.066, 6.197, 1256.60391, -0.05, 0, 0, 1.059, 4.068, 8364.73984, -2.18, -19, 0.1, 0.933, 4.369, 16728.3705, 1.17, 28, -0.1, 0.862, 4.601, 6656.7486, -4.04, -41, 0.2, 0.851, 2.800, 70.9877, -1.88, -22, 0.1, 0.849, 5.726, 31571.8352, 2.41, 56, -0.3, 0.796, 5.084, -9095.5552, 0.95, 4, 0, 0.779, 0.975, -17772.0114, -6.75, -94, 0.5, 0.774, 2.658, 15752.3038, -4.99, -45, 0.2, 0.728, 0.266, 8326.3902, 3.05, 50, -0.2, 0.683, 1.304, 8330.9926, 0, 0, 0, 0.670, 1.756, 40528.8286, 3.91, 81, -0.4, 0.658, 3.414, 22614.8418, 0.91, 31, -0.2, 0.657, 0.901, -1952.4800, 0.64, 7, 0, 0.598, 6.026, 8393.1258, -2.18, -19, 0.1, 0.596, 5.014, 24080.9952, -3.46, -20, 0.1, 0.579, 5.829, 23385.1191, -2.87, -13, 0, 0.514, 4.302, 6099.4343, -5.89, -63, 0.3, 0.508, 1.830, 14218.5763, -0.04, 13, -0.1, 0.498, 5.242, 7143.0752, -0.30, 3, 0, 0.495, 3.373, -10071.6219, -5.20, -69, 0.3, 0.473, 2.430, -17981.5609, -2.43, -43, 0.2, 0.456, 4.887, -8294.9344, -1.83, -29, 0.1, 0.453, 0.173, 8362.4485, 1.21, 21, -0.1, 0.423, 4.489, 29970.8800, -5.03, -32, 0.1, 0.422, 2.315, -24357.7723, -4.60, -75, 0.4, 0.411, 1.102, 13657.8484, -0.58, 6, 0, 0.410, 0.500, 8311.7707, -2.18, -19, 0.1, 0.379, 3.626, 24428.7600, 2.71, 53, 0, 0.355, 0.740, 25614.3762, 4.54, 75, 0, 0.343, 5.772, -2371.2325, -3.7, -44, 0, 0.335, 0.857, 9166.5428, -2.8, -26, 0, 0.332, 0.444, -8257.7037, -3.4, -47, 0, 0.323, 4.829, -10281.1714, -0.9, -18, 0, 0.322, 5.758, 5889.8848, -1.6, -12, 0, 0.287, 0.56, 38299.5714, -3.5, -6, 0, 0.284, 5.57, 15333.2048, 3.7, 57, 0, 0.279, 2.82, 21500.2132, -2.8, -13, 0, 0.256, 0.72, 14357.1381, -2.5, -16, 0, 0.248, 2.20, -7909.9389, 2.8, 26, 0, 0.245, 1.90, 31713.8105, -1.4, 12, 0, 0.237, 3.47, 15056.4277, -4.4, -38, 0, 0.213, 3.77, 15613.7420, -2.5, -16, 0, 0.213, 2.50, 32828.4391, 2.4, 56, 0, 0.209, 3.26, 6376.2114, 2.2, 32, 0, 0.205, 2.93, 14967.4158, -0.7, 6, 0, 0.205, 2.02, 15540.4531, 0.9, 31, 0, 0.204, 3.06, 15545.0555, -2.2, -19, 0, 0.203, 1.20, 38785.8980, 0.2, 37, 0, 0.201, 6.06, 6447.1991, 0.3, 10, 0, 0.186, 6.13, -16238.6304, 1.3, 1, 0, 0.183, 2.13, 21642.1886, -6.6, -57, 0, 0.169, 3.29, -8815.0180, -5.3, -69, 0, 0.167, 1.06, 8328.3391, 1.5, 25, 0, 0.167, 0.51, 8329.0437, 1.5, 25, 0, 0.167, 1.26, 14756.7124, -0.7, 6, 0, 0.158, 0.07, 17495.2343, -1.3, -1, 0, 0.157, 0.57, 6638.7244, -2.2, -19, 0, 0.157, 6.21, 22685.8295, -1.0, 9, 0, 0.148, 5.03, 5329.1570, -2.1, -19, 0, 0.148, 4.03, 16799.3582, -0.7, 6, 0, 0.145, 0.05, 7178.0144, 1.5, 25, 0, 0.144, 5.64, -486.3266, -3.7, -44, 0, 0.139, 3.51, 47742.8914, 1.7, 63, 0, 0.138, 4.07, 7935.6705, 1.5, 25, 0, 0.136, 4.63, -15400.7789, -3.1, -50, 0, 0.136, 3.96, -695.8761, 0.6, 7, 0, 0.135, 5.95, 7211.7617, -0.7, 6, 0, 0.128, 5.17, 29828.9047, -1.3, 12, 0, 0.127, 1.18, 7753.3529, 1.5, 25, 0, 0.127, 0.71, 7216.3641, -3.7, -44, 0, 0.124, 5.83, 15149.7333, -0.7, 6, 0, 0.121, 1.46, 8000.1048, -2.2, -19, 0, 0.120, 3.78, 8721.7124, 1.5, 25, 0, 0.116, 5.19, 6428.0209, -2.2, -19, 0, 0.114, 2.89, -1185.6162, -1.8, -22, 0, 0.112, 2.85, 15542.4020, -0.7, 6, 0, 0.112, 2.23, 15543.1066, -0.7, 6, 0, 0.110, 0.51, 7213.7105, -2.2, -19, 0, 0.110, 6.15, 7214.4152, -2.2, -19, 0, 0.110, 1.31, 15471.7666, 1.2, 28, 0, 0.109, 2.46, 141.9754, -3.8, -44, 0, 0.108, 0.46, 13171.5218, -4.3, -38, 0, 0.108, 6.13, 23942.4334, -1.0, 9, 0, 0.107, 3.15, 15508.9972, -0.4, 10, 0, 0.105, 0.39, 8904.030, 1.5, 25, 0, 0.105, 4.93, 14392.077, -0.7, 6, 0, 0.103, 2.47, 25195.624, 0.2, 24, 0, 0.101, 3.48, 6821.042, -2.2, -19, 0, 0.099, 4.37, 7149.629, 1.5, 25, 0, 0.099, 1.27, -17214.697, -4.9, -72, 0, 0.096, 1.93, 15576.511, -1.0, 0, 0, 0.086, 2.92, 46628.263, -2.0, 19, 0, 0.085, 6.22, 8504.484, -2.5, -22, 0, 0.080, 3.40, -2438.807, -3.1, -37, 0, 0.080, 1.17, 8786.147, -2.2, -19, 0, 0.077, 3.61, 7230.984, 1.5, 25, 0, 0.071, 0.28, 8315.574, -2.2, -19, 0, 0.067, 4.53, 29342.578, -5.0, -32, 0, 0.065, 2.24, 31642.823, 0.5, 34, 0, 0.063, 5.80, 8329.403, 1.5, 25, 0, 0.063, 2.05, 8327.980, 1.5, 25, 0, 0.062, 0.08, 8346.716, -0.3, 0, 0, 0.061, 4.85, 36.048, -3.7, -44, 0, 0.061, 2.58, 6063.386, -2.2, -19, 0, 0.061, 4.30, -766.864, 2.5, 29, 0, 0.060, 3.01, 8322.132, -0.3, 0, 0, 0.059, 0.44, 25057.062, 2.7, 53, 0, 0.059, 0.31, 8288.877, 1.5, 25, 0, 0.059, 2.35, 41643.457, 7.6, 125, -1, 0.059, 1.26, 8368.506, 1.5, 25, 0, 0.058, 1.80, 39900.527, 3.9, 81, 0, 0.058, 1.87, 13590.274, 0, 13, 0, 0.057, 0.47, 14954.262, -0.7, 6, 0, 0.057, 6.20, 8294.910, 1.8, 29, 0, 0.056, 4.63, -8362.473, -1.2, -21, 0, 0.055, 2.86, 8170.957, 1.5, 25, 0, 0.055, 0.03, 7632.815, 2.1, 32, 0, 0.053, 0.80, 7180.306, -1.9, -15, 0, 0.053, 4.64, 6028.447, -4.0, -41, 0, 0.053, 4.57, 15385.020, -0.7, 6, 0, 0.052, 0.60, 37671.269, -3.5, -6, 0, 0.052, 4.99, 8486.426, 1.5, 25, 0, 0.051, 4.62, 17913.987, 3.0, 50, 0, 0.050, 1.64, 837.851, -4.4, -51, 0, 0.049, 5.79, 7542.649, 1.5, 25, 0, 0.049, 2.06, 9114.733, 1.5, 25, 0, 0.049, 2.21, 7056.329, -2.2, -19, 0, 0.049, 4.90, 7214.063, -2.2, -19, 0, 0.048, 5.39, -1671.943, -5.6, -66, 0, 0.047, 4.90, -26100.703, -8.3, -119, 1, 0.047, 1.60, -9024.567, -0.9, -18, 0, 0.046, 1.16, 7161.094, -2.2, -19, 0, 0.046, 5.77, 30943.533, 2.4, 56, 0, 0.046, 2.43, 22199.503, -4.7, -35, 0, 0.046, 3.16, 14991.999, -0.7, 6, 0, 0.044, 4.11, 48857.520, 5.4, 106, -1, 0.044, 4.39, 6625.570, -2.2, -19, 0, 0.044, 6.06, 7789.401, -2.2, -19, 0, 0.043, 0.14, 16693.431, -0.7, 6, 0, 0.043, 4.50, 15020.385, -0.7, 6, 0, 0.043, 4.35, 5471.132, -5.9, -63, 0, 0.043, 4.32, 575.338, 0, 0, 0, 0.043, 5.43, 7267.032, -2.2, -19, 0, 0.043, 3.82, 16328.796, -0.7, 6, 0, 0.042, 2.73, -17424.247, -0.6, -21, 0, 0.041, 3.60, 15489.785, -0.7, 6, 0, 0.040, 2.62, 16655.082, 4.6, 75, 0, 0.040, 4.23, 8351.233, -2.2, -19, 0, 0.039, 0.66, -6443.786, -1.6, -25, 0, 0.039, 2.13, 16118.093, -0.7, 6, 0, 0.039, 5.86, 7247.820, -2.5, -23, 0, 0.038, 4.56, 7285.051, -4.1, -41, 0, 0.038, 2.59, 9179.168, -2.2, -19, 0, 0.038, 1.42, 393.021, 0, 0, 0, 0.038, 4.94, 8381.661, 1.5, 25, 0, 0.037, 5.06, 23452.693, -3.4, -20, 0, 0.037, 5.11, 9027.981, -0.4, 0, 0, 0.037, 4.98, 7740.199, 1.5, 25, 0, 0.037, 3.66, 16659.684, 1.5, 25, 0, 0.037, 2.89, 8275.722, 1.5, 25, 0, 0.037, 4.26, 40042.502, 0.2, 38, 0, 0.036, 1.95, 8326.062, 1.5, 25, 0, 0.036, 5.90, 8331.321, 1.5, 25, 0, 0.035, 1.33, 15595.723, -0.7, 6, 0, 0.035, 1.39, 7777.936, 2, 25, 0, 0.035, 0.80, 6663.308, -2, -19, 0, 0.035, 0.53, 64.434, -4, -44, 0, 0.034, 2.15, 6691.693, -2, -19, 0, 0.034, 1.90, -8467.253, 1, 0, 0, 0.033, 2.83, 7806.322, 2, 25, 0, 0.033, 4.67, 9479.368, 2, 25, 0, 0.033, 1.41, 418.752, 4, 51, 0}, - //MR1 - {0.5139, 4.1569, 14914.452335, -0.635, 6.2, -0.04, 0.3824, 1.8013, 6585.760910, -2.158, -19, 0.09, 0.3265, 2.3987, 7700.38947, 1.550, 25, -0.12, 0.2640, 5.4540, 8956.99338, 1.496, 25, -0.13, 0.1230, 3.0985, 628.30196, -0.027, 0, 0, 0.0775, 0.929, 16171.05625, -0.69, 6, 0, 0.0607, 4.857, 7842.36482, -2.21, -19, 0.1, 0.0497, 4.200, 14286.15038, -0.61, 6, 0, 0.0419, 5.155, 8399.67910, -0.36, 3, 0, 0.0322, 0.229, 23243.1438, 0.89, 31, -0.2, 0.0253, 2.587, -1742.9305, -3.68, -44, 0.2, 0.0249, 1.844, 5957.4590, -2.13, -19, 0.1, 0.0176, 4.754, 16029.0809, 3.07, 50, -0.2, 0.0145, 1.526, 17285.6848, 3.02, 50, -0.3, 0.0137, 1.004, 15542.7543, -0.66, 6, 0, 0.0126, 5.010, 8326.3902, 3.05, 50, 0, 0.0119, 4.814, 8470.6668, -2.24, -19, 0, 0.0118, 2.843, 8330.9926, 0, 0, 0, 0.0107, 2.442, 7072.0875, 1.6, 25, 0, 0.0099, 5.92, 22128.5152, -2.8, -13, 0, 0.0066, 3.28, 24499.7477, 0.8, 31, 0, 0.0065, 4.90, 7214.0629, -2.2, -19, 0, 0.0059, 5.41, 9585.2953, 1.5, 25, 0, 0.0054, 3.06, 1256.6039, -0.1, 0, 0, 0.0052, 2.50, 8328.3391, 1.5, 25, 0, 0.0052, 5.35, 8329.0437, 1.5, 25, 0, 0.0048, 3.56, 13799.8238, -4.3, -38, 0, 0.0039, 1.99, 30457.2066, -1.3, 12, 0, 0.0035, 0.49, 15540.4531, 0.9, 31, 0, 0.0035, 4.60, 15545.0555, -2.2, -19, 0, 0.0033, 0.27, 22614.842, 0.9, 31, 0, 0.0031, 4.24, 13657.848, -0.6, 6, 0, 0.0023, 1.23, 16728.371, 1.2, 28, 0, 0.0023, 5.50, 8328.691, 1.5, 25, 0, 0.0023, 0, 0, 0, 0, 0, 0.0023, 4.41, 7211.762, -0.7, 6, 0, 0.0022, 1.39, 8311.771, -2.2, -19, 0, 0.0022, 2.25, 7216.364, -3.7, -44, 0, 0.0021, 5.94, 70.988, -1.9, -22, 0, 0.0021, 2.58, 31571.835, 2.4, 56, 0, 0.0017, 2.63, -2371.232, -3.7, -44, 0, 0.0016, 4.04, -1952.480, 0.6, 7, 0, 0.0015, 4.23, 8329.403, 1.5, 25, 0, 0.0015, 3.63, 8327.980, 1.5, 25, 0, 0.0015, 2.69, 23385.119, -2.9, -13, 0, 0.0014, 5.96, 21500.213, -2.8, -13, 0, 0.0013, 4.06, 15542.402, -0.7, 6, 0, 0.0013, 1.02, 15543.107, -0.7, 6, 0, 0.0013, 2.10, 7143.075, -0.3, 0, 0, 0.0012, 0.23, -10071.622, -5.2, -69, 0, 0.0011, 3.33, 23871.446, 1, 31, 0, 0.0011, 1.89, 5329.157, -2, -19, 0}, - //MR2 - {0.001490, 4.157, 14914.45233, -0.64, 6, 0, 0.001110, 1.801, 6585.7609, -2.16, -19, 0.1, 0.000950, 2.399, 7700.3895, 1.55, 25, -0.1, 0.000770, 5.454, 8956.9934, 1.50, 25, -0.1, 0.000360, 3.098, 628.3020, 0, 0, 0, 0.000230, 0.93, 16171.0562, -0.7, 6, 0, 0.000180, 4.86, 7842.3648, -2.2, -19, 0, 0.000140, 4.20, 14286.1504, -0.6, 6, 0, 0.000120, 5.16, 8399.6791, -0.4, 0, 0, 0.000116, 3.46, 8326.390, 3.0, 50, 0, 0.000109, 4.39, 8330.993, 0, 0, 0, 0.000090, 0.23, 23243.144, 0.9, 31, 0}}} - -// GetMoonCir 返回月球级数系数的独立副本。 -// 月球计算直接读取包内的大型不可变数据,避免重复构建这组大型不可变数据。 -// GetMoonCir returns an independent copy of the lunar-series coefficients. -// Moon calculations read the package-owned table directly so repeated calls do not rebuild this large immutable data set. -func GetMoonCir() [][][]float64 { - result := make([][][]float64, len(moonCir)) - for coordinateIndex, coordinate := range moonCir { - result[coordinateIndex] = make([][]float64, len(coordinate)) - for seriesIndex, series := range coordinate { - result[coordinateIndex][seriesIndex] = append([]float64(nil), series...) - } - } - return result -} +// GetMoonCir 返回旧月球级数系数的独立副本 / an independent copy of the legacy lunar-series coefficients. +func GetMoonCir() [][][]float64 { return planet.GetMoonCir() } func MoonCalcNew(coordIndex int, jd float64) float64 { return MoonCalcNewN(coordIndex, jd, -1) } func MoonCalcNewN(coordIndex int, jd float64, n int) float64 { - return moonCalcNewN(coordIndex, jd, n) + return planet.WhereMoonN(coordIndex, jd, n) } -// moonPhaseCompensationThreshold 是启用线性相位补偿的最小 |t|(世纪数)倒数尺度: -// 更小的项本身已低于纳角秒级相位舍入,无需补偿。 -const moonPhaseCompensationThreshold = 1e6 - func moonCalcNewN(coordIndex int, jd float64, n int) float64 { - arcsecPerRadian := 180.0 * 3600.0 / math.Pi - // Preserve the low parts of both the epoch subtraction and century division. - days := jd - 2451545.0 - origin := days - jd - daysLow := (jd - (days - origin)) - (2451545 + origin) - t := days / 36525.0 - tLow := (math.FMA(-t, 36525, days) + daysLow) / 36525 - // Small angular terms have sub-nanoarcsecond phase roundoff already. - // Compensate only the larger terms; distance needs no angular precision. - phaseThreshold := moonPhaseCompensationThreshold / math.Abs(t) - ob := moonCir[coordIndex] - var v float64 - var tn float64 = 1 - t2 := t * t - t3 := t2 * t - t4 := t3 * t - t5 := t4 * t - tx := t - 10 - if coordIndex == 0 { - // 黄经线性项必须始终走补偿版本:MoonCalcNew 与 HMoonTrueLo 是同一个物理量的两个入口, - // 只在其中一个入口补偿会让两者差约 1e-9 度。 - longitude := moonReducedLinearPhase(3.81034409, 8399.684730072, t, tLow) - v += (longitude - 3.319e-05*t2 + 3.11e-08*t3 - 2.033e-10*t4) * arcsecPerRadian - v += 5028.792262*t + 1.1124406*t2 + 0.00007699*t3 - 0.000023479*t4 - 0.0000000178*t5 //岁差(角秒) - if tx > 0 { - v += -0.866 + 1.43*tx + 0.054*tx*tx //对公元3000年至公元5000年的拟合,最大误差小于10角秒 - } - } - t2 /= 1e4 - t3 /= 1e8 - t4 /= 1e8 - nScalars := n * 6 - if nScalars < 0 { - nScalars = len(ob[0]) - } - for i := 0; i < len(ob); i++ { - F := ob[i] - seriesPhaseThreshold := phaseThreshold / math.Abs(tn) - N := math.Floor(float64(nScalars*len(F))/float64(len(ob[0])) + 0.5) - if i != 0 { - N += 6 - } - if N >= float64(len(F)) { - N = float64(len(F)) - } - var c float64 = 0 - for j := 0; float64(j) < N; j += 6 { - phase := F[j+1] + t*F[j+2] - if coordIndex != 2 && math.Abs(F[j]*F[j+2]) > seriesPhaseThreshold { - phase = moonReducedLinearPhase(F[j+1], F[j+2], t, tLow) - } - c += F[j] * math.Cos(phase+t2*F[j+3]+t3*F[j+4]+t4*F[j+5]) - } - v += c * tn - tn *= t - } - if coordIndex != 2 { - v /= arcsecPerRadian - } - return v -} - -// Keep the small phase increment out of the rounded secular product. The -// low part of 2*pi also prevents a range-reduction jump at full revolutions. -func moonReducedLinearPhase(offset, rate, t, tLow float64) float64 { - const twoPiLow = 2.4492935982947064e-16 - product := rate * t - roundoff := math.FMA(rate, t, -product) + rate*tLow - turns := math.Round(product / (2 * math.Pi)) - return math.FMA(-turns, 2*math.Pi, product) + (roundoff - turns*twoPiLow) + offset + return planet.WhereMoonN(coordIndex, jd, n) } func HMoonTrueLo(jde float64) float64 { //计算月亮 @@ -168,28 +57,31 @@ func HMoonApparentLo(jde float64) float64 { } func HMoonApparentLoN(jde float64, n int) float64 { + if lo, _, _, _, ok := vectorApparent(ephem.Moon, jde, n); ok { + return lo + } return HMoonTrueLoN(jde, n) + Nutation2000Bi(jde) } -// HMoonGeocentricApparentRa 月亮地心视赤经 / apparent geocentric right ascension of the Moon. -func HMoonGeocentricApparentRa(jde float64) float64 { - return HMoonGeocentricApparentRaN(jde, -1) +// HMoonGeocentricApparentRaWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricApparentRaWithInstance(jde float64, inst *Instance) float64 { + return HMoonGeocentricApparentRaNWithInstance(jde, -1, inst) } -// HMoonGeocentricApparentRaN 月亮地心视赤经(截断版) / truncated apparent geocentric right ascension of the Moon. -func HMoonGeocentricApparentRaN(jde float64, n int) float64 { - return LoToRa(jde, HMoonApparentLoN(jde, n), HMoonTrueBoN(jde, n)) +// HMoonGeocentricApparentRaNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonApparentRaN(jde, n, inst) } -// HMoonGeocentricApparentDec 月亮地心视赤纬 / apparent geocentric declination of the Moon. -func HMoonGeocentricApparentDec(jde float64) float64 { - return HMoonGeocentricApparentDecN(jde, -1) +// HMoonGeocentricApparentDecWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricApparentDecWithInstance(jde float64, inst *Instance) float64 { + return HMoonGeocentricApparentDecNWithInstance(jde, -1, inst) } -// HMoonGeocentricApparentDecN 月亮地心视赤纬(截断版) / truncated apparent geocentric declination of the Moon. -func HMoonGeocentricApparentDecN(jde float64, n int) float64 { - return ArcSin(Sin(HMoonTrueBoN(jde, n))*Cos(TrueObliquity(jde)) + - Cos(HMoonTrueBoN(jde, n))*Sin(TrueObliquity(jde))*Sin(HMoonApparentLoN(jde, n))) +// HMoonGeocentricApparentDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := moonApparentRaDecN(jde, n, inst) + return dec } // HMoonGeocentricApparentRaDec 月亮地心视赤经、视赤纬 / apparent geocentric right ascension and declination of the Moon. @@ -197,48 +89,49 @@ func HMoonGeocentricApparentRaDec(jde float64) (float64, float64) { return HMoonGeocentricApparentRaDecN(jde, -1) } -// HMoonGeocentricApparentRaDecN 月亮地心视赤经、视赤纬(截断版) / truncated apparent geocentric right ascension and declination of the Moon. +// HMoonGeocentricApparentRaDecN 月亮地心视赤经、视赤纬(n 变体) / n-parameterised apparent geocentric right ascension and declination of the Moon. func HMoonGeocentricApparentRaDecN(jde float64, n int) (float64, float64) { - return LoBoToRaDec(jde, HMoonApparentLoN(jde, n), HMoonTrueBoN(jde, n)) + return moonApparentRaDecN(jde, n, nil) } -// HMoonGeocentricTrueRa 月亮地心真赤经 / true geocentric right ascension of the Moon. -func HMoonGeocentricTrueRa(jde float64) float64 { - return HMoonGeocentricTrueRaN(jde, -1) +// HMoonGeocentricTrueRaWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueRaWithInstance(jde float64, inst *Instance) float64 { + return HMoonGeocentricTrueRaNWithInstance(jde, -1, inst) } -// HMoonGeocentricTrueRaN 月亮地心真赤经(截断版) / truncated true geocentric right ascension of the Moon. -func HMoonGeocentricTrueRaN(jde float64, n int) float64 { - return LoToRa(jde, HMoonTrueLoN(jde, n), HMoonTrueBoN(jde, n)) +// HMoonGeocentricTrueRaNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return LoToRa(jde, HMoonTrueLoNWithInstance(jde, n, inst), HMoonTrueBoNWithInstance(jde, n, inst)) } -// HMoonGeocentricTrueDec 月亮地心真赤纬 / true geocentric declination of the Moon. -func HMoonGeocentricTrueDec(jde float64) float64 { - return HMoonGeocentricTrueDecN(jde, -1) +// HMoonGeocentricTrueDecWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueDecWithInstance(jde float64, inst *Instance) float64 { + return HMoonGeocentricTrueDecNWithInstance(jde, -1, inst) } -// HMoonGeocentricTrueDecN 月亮地心真赤纬(截断版) / truncated true geocentric declination of the Moon. -func HMoonGeocentricTrueDecN(jde float64, n int) float64 { - return ArcSin(Sin(HMoonTrueBoN(jde, n))*Cos(TrueObliquity(jde)) + - Cos(HMoonTrueBoN(jde, n))*Sin(TrueObliquity(jde))*Sin(HMoonTrueLoN(jde, n))) +// HMoonGeocentricTrueDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + return ArcSin(Sin(HMoonTrueBoNWithInstance(jde, n, inst))*Cos(TrueObliquity(jde)) + + Cos(HMoonTrueBoNWithInstance(jde, n, inst))*Sin(TrueObliquity(jde))*Sin(HMoonTrueLoNWithInstance(jde, n, inst))) } -// HMoonGeocentricTrueRaDec 月亮地心真赤经、真赤纬 / true geocentric right ascension and declination of the Moon. -func HMoonGeocentricTrueRaDec(jde float64) (float64, float64) { - return HMoonGeocentricTrueRaDecN(jde, -1) +// HMoonGeocentricTrueRaDecWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return HMoonGeocentricTrueRaDecNWithInstance(jde, -1, inst) } -// HMoonGeocentricTrueRaDecN 月亮地心真赤经、真赤纬(截断版) / truncated true geocentric right ascension and declination of the Moon. -func HMoonGeocentricTrueRaDecN(jde float64, n int) (float64, float64) { - return LoBoToRaDec(jde, HMoonTrueLoN(jde, n), HMoonTrueBoN(jde, n)) +// HMoonGeocentricTrueRaDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonGeocentricTrueRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return LoBoToRaDec(jde, HMoonTrueLoNWithInstance(jde, n, inst), HMoonTrueBoNWithInstance(jde, n, inst)) } func HMoonTrueRaDec(jde float64) (float64, float64) { return HMoonTrueRaDecN(jde, -1) } -func HMoonTrueRaDecN(jde float64, n int) (float64, float64) { - return LoBoToRaDec(jde, HMoonApparentLoN(jde, n), HMoonTrueBoN(jde, n)) +// HMoonTrueRaDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonTrueRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return moonApparentRaDecN(jde, n, inst) } /* @@ -248,12 +141,10 @@ func HMoonTrueDec(jde float64) float64 { return HMoonTrueDecN(jde, -1) } -func HMoonTrueDecN(jde float64, n int) float64 { - moonLo := HMoonApparentLoN(jde, n) - moonBo := HMoonTrueBoN(jde, n) - tmp := Sin(moonBo)*Cos(TrueObliquity(jde)) + Cos(moonBo)*Sin(TrueObliquity(jde))*Sin(moonLo) - res := ArcSin(tmp) - return res +// HMoonTrueDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := moonApparentRaDecN(jde, n, inst) + return dec } /* @@ -263,8 +154,9 @@ func HMoonTrueRa(jde float64) float64 { return HMoonTrueRaN(jde, -1) } -func HMoonTrueRaN(jde float64, n int) float64 { - return LoToRa(jde, HMoonApparentLoN(jde, n), HMoonTrueBoN(jde, n)) +// HMoonTrueRaNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonApparentRaN(jde, n, inst) } /* @@ -283,27 +175,32 @@ func HMoonApparentRaDecN(jd, lon, lat, tz float64, n int) (float64, float64) { return topoRA, topoDec } -func HMoonApparentRa(jd, lon, lat, tz float64) float64 { - return HMoonApparentRaN(jd, lon, lat, tz, -1) +// HMoonApparentRaWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonApparentRaWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return HMoonApparentRaNWithInstance(jd, lon, lat, tz, -1, inst) } -func HMoonApparentRaN(jd, lon, lat, tz float64, n int) float64 { +// HMoonApparentRaNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonApparentRaNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { jde := UTC2TT(jd) - tz/24 - ra := HMoonTrueRaN(jde, n) - dec := HMoonTrueDecN(jde, n) - away := HMoonAwayN(jde, n) / 149597870.7 + ra := HMoonTrueRaNWithInstance(jde, n, inst) + dec := HMoonTrueDecNWithInstance(jde, n, inst) + away := HMoonAwayNWithInstance(jde, n, inst) / 149597870.7 topoRA := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0) return topoRA } -func HMoonApparentDec(jd, lon, lat, tz float64) float64 { - return HMoonApparentDecN(jd, lon, lat, tz, -1) + +// HMoonApparentDecWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonApparentDecWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return HMoonApparentDecNWithInstance(jd, lon, lat, tz, -1, inst) } -func HMoonApparentDecN(jd, lon, lat, tz float64, n int) float64 { +// HMoonApparentDecNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func HMoonApparentDecNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { jde := UTC2TT(jd) - tz/24 - ra := HMoonTrueRaN(jde, n) - dec := HMoonTrueDecN(jde, n) - away := HMoonAwayN(jde, n) / 149597870.7 + ra := HMoonTrueRaNWithInstance(jde, n, inst) + dec := HMoonTrueDecNWithInstance(jde, n, inst) + away := HMoonAwayNWithInstance(jde, n, inst) / 149597870.7 topoDec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0) return topoDec } diff --git a/basic/moon_precision_test.go b/basic/moon_precision_test.go index d8c4995..3e1a412 100644 --- a/basic/moon_precision_test.go +++ b/basic/moon_precision_test.go @@ -3,9 +3,12 @@ package basic import ( "math" "testing" + + "b612.me/astro/internal/ephemtest" ) func TestMoonSeriesNumericalPrecision(t *testing.T) { + ephemtest.PinLegacy(t) // The existing binary64 coefficients evaluated at 70 decimal digits. // These check arithmetic precision, not the lunar theory's physical error. for _, sample := range []struct{ jd, longitude, latitude, distance, velocity float64 }{ @@ -65,8 +68,7 @@ func TestOccultationContactDerivativeNumericalStability(t *testing.T) { } } -// MoonCalcNew 与 HMoonTrueLo 是同一个物理量的两个入口,必须给出同一个值(此前只有一个入口 -// 走线性相位补偿,两者差约 1e-9 度)。 +// MoonCalcNew 与 HMoonTrueLo 是同一个物理量的两个入口,必须给出同一个值。 func TestMoonCalcNewMatchesHMoonTrueLo(t *testing.T) { for _, jd := range []float64{2451545.0, 2460310.5, 2415020.5, 2299160.5, 2500000.5} { direct := math.Mod(MoonCalcNew(0, jd)*180/math.Pi, 360) diff --git a/basic/moving_disk_engine.go b/basic/moving_disk_engine.go index 6a36922..b8272a0 100644 --- a/basic/moving_disk_engine.go +++ b/basic/moving_disk_engine.go @@ -197,11 +197,12 @@ func (engine movingDiskEventEngine) sampleTimes( if step <= 0 || math.IsNaN(step) || math.IsInf(step, 0) { step = duration } - baseSampleCount := int(math.Ceil(duration/step)) + 1 + // 先按浮点比较,避免极小步长下 int 转换溢出成负数而绕过上限。 + baseSampleCount := math.Ceil(duration/step) + 1 if engine.reserveAnchorSlot { baseSampleCount++ } - if baseSampleCount > maximum && engine.uniformOverflow { + if baseSampleCount > float64(maximum) && engine.uniformOverflow { interiorCount := maximum - 3 if interiorCount < 0 { interiorCount = 0 @@ -214,7 +215,7 @@ func (engine movingDiskEventEngine) sampleTimes( sort.Float64s(bounded) return movingDiskUniqueTimes(bounded), step } - if baseSampleCount > maximum { + if baseSampleCount > float64(maximum) { step = duration / float64(maximum-1) } diff --git a/basic/moving_disk_engine_test.go b/basic/moving_disk_engine_test.go index 5875a12..e05beac 100644 --- a/basic/moving_disk_engine_test.go +++ b/basic/moving_disk_engine_test.go @@ -182,3 +182,13 @@ func TestMovingDiskContactCacheClearsAtBoundedCapacity(t *testing.T) { t.Fatal("new entry missing after bounded cache clear") } } + +func TestMovingDiskEventEngineSampleTimesBoundsTinyStep(t *testing.T) { + engine := movingDiskEventEngine{maxSampleCount: 9} + for _, step := range []float64{1e-300, math.SmallestNonzeroFloat64} { + times, _ := engine.sampleTimes(0, 1, 0.5, step) + if len(times) > 9 { + t.Fatalf("step %g: sample count = %d, want <= 9", step, len(times)) + } + } +} diff --git a/basic/neptune.go b/basic/neptune.go index 48c82ce..bec3191 100644 --- a/basic/neptune.go +++ b/basic/neptune.go @@ -115,59 +115,34 @@ func NeptuneMag(jde float64) float64 { return FloatRound(mag, 2) } -func NeptuneHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := NeptuneApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// NeptuneHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(7, localJD, lon, lat, timezone, -1, inst) } -func NeptuneAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := NeptuneApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// NeptuneAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(7, localJD, lon, lat, timezone, -1, inst) } -func NeptuneHourAngle(jd, lon, timezone float64) float64 { +// NeptuneHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - NeptuneApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - NeptuneApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func NeptuneCulminationTime(localJD, lon, timezone float64) float64 { +// NeptuneCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-NeptuneHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-NeptuneHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := NeptuneHourAngle(localJD, lon, timezone) + currentHourAngle := NeptuneHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -185,14 +160,22 @@ func NeptuneCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func NeptuneRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// NeptuneRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func NeptuneSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// NeptuneSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, NeptuneCulminationTime, NeptuneHeight, NeptuneApparentDec) +func neptuneRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return NeptuneCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return NeptuneHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return NeptuneApparentDecWithInstance(jde, inst) }) } diff --git a/basic/neptune_events.go b/basic/neptune_events.go index 4f264c3..b1abe46 100644 --- a/basic/neptune_events.go +++ b/basic/neptune_events.go @@ -14,8 +14,10 @@ const ( neptunePhaseCoarseTolerance = 30.0 / 86400.0 ) -func neptuneSunLongitudeDelta(jde, degree float64, filter bool) float64 { - sub := Limit360(Limit360(NeptuneApparentLo(jde)-HSunApparentLo(jde)) - degree) +func neptuneSunLongitudeDelta(jde, degree float64, filter bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(NeptuneApparentLoWithInstance(jde, inst)-HSunApparentLoWithInstance(jde, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -27,8 +29,10 @@ func neptuneSunLongitudeDelta(jde, degree float64, filter bool) float64 { return sub } -func neptuneSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { - sub := Limit360(Limit360(NeptuneApparentLoN(jde, n)-HSunApparentLoN(jde, n)) - degree) +func neptuneSunLongitudeDeltaN(jde, degree float64, filter bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(NeptuneApparentLoNWithInstance(jde, n, inst)-HSunApparentLoNWithInstance(jde, n, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -40,8 +44,10 @@ func neptuneSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 return sub } -func neptuneRADerivative(jde, delta float64) float64 { - sub := NeptuneApparentRa(jde+delta) - NeptuneApparentRa(jde-delta) +func neptuneRADerivative(jde, delta float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := NeptuneApparentRaWithInstance(jde+delta, inst) - NeptuneApparentRaWithInstance(jde-delta, inst) if sub > 180 { sub -= 360 } @@ -51,8 +57,10 @@ func neptuneRADerivative(jde, delta float64) float64 { return sub / (2 * delta) } -func neptuneRADerivativeN(jde, delta float64, n int) float64 { - sub := NeptuneApparentRaN(jde+delta, n) - NeptuneApparentRaN(jde-delta, n) +func neptuneRADerivativeN(jde, delta float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := NeptuneApparentRaNWithInstance(jde+delta, n, inst) - NeptuneApparentRaNWithInstance(jde-delta, n, inst) if sub > 180 { sub -= 360 } @@ -62,13 +70,15 @@ func neptuneRADerivativeN(jde, delta float64, n int) float64 { return sub / (2 * delta) } -func neptuneConjunctionFull(jde, degree float64, next uint8) float64 { +func neptuneConjunctionFull(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := NEPTUNE_S_PERIOD / 360 - currentDelta := neptuneSunLongitudeDelta(jde, degree, false) + currentDelta := neptuneSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -78,8 +88,8 @@ func neptuneConjunctionFull(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := neptuneSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (neptuneSunLongitudeDelta(prevJDE+0.000005, degree, true) - neptuneSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := neptuneSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (neptuneSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - neptuneSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -93,13 +103,15 @@ func neptuneConjunctionFull(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func neptuneConjunction(jde, degree float64, next uint8) float64 { +func neptuneConjunction(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := NEPTUNE_S_PERIOD / 360 - currentDelta := neptuneSunLongitudeDelta(jde, degree, false) + currentDelta := neptuneSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -109,8 +121,8 @@ func neptuneConjunction(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := neptuneSunLongitudeDeltaN(prevJDE, degree, true, neptuneEventSearchN) - longitudeSlope := (neptuneSunLongitudeDeltaN(prevJDE+0.000005, degree, true, neptuneEventSearchN) - neptuneSunLongitudeDeltaN(prevJDE-0.000005, degree, true, neptuneEventSearchN)) / 0.00001 + longitudeDelta := neptuneSunLongitudeDeltaN(prevJDE, degree, true, neptuneEventSearchN, inst) + longitudeSlope := (neptuneSunLongitudeDeltaN(prevJDE+0.000005, degree, true, neptuneEventSearchN, inst) - neptuneSunLongitudeDeltaN(prevJDE-0.000005, degree, true, neptuneEventSearchN, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= neptunePhaseCoarseTolerance { @@ -124,8 +136,8 @@ func neptuneConjunction(jde, degree float64, next uint8) float64 { converged = false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := neptuneSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (neptuneSunLongitudeDelta(prevJDE+0.000005, degree, true) - neptuneSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := neptuneSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (neptuneSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - neptuneSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -139,39 +151,65 @@ func neptuneConjunction(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func LastNeptuneConjunction(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 0, neptuneConjunction) +// LastNeptuneConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func NextNeptuneConjunction(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 0, neptuneConjunction) +// NextNeptuneConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func LastNeptuneOpposition(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 180, neptuneConjunction) +// LastNeptuneOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func NextNeptuneOpposition(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 180, neptuneConjunction) +// NextNeptuneOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func NextNeptuneEasternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 90, neptuneConjunction) +// NextNeptuneEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func LastNeptuneEasternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 90, neptuneConjunction) +// LastNeptuneEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func NextNeptuneWesternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 270, neptuneConjunction) +// NextNeptuneWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func LastNeptuneWesternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 270, neptuneConjunction) +// LastNeptuneWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return neptuneConjunction(jde, degree, next, inst) + }) } -func neptuneRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool) float64 { +func neptuneRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(oppositionJD) { return math.NaN() } @@ -179,103 +217,107 @@ func neptuneRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposit startTT := oppositionTT endTT := oppositionTT if searchBeforeOpposition { - easternQuadratureUT := neptuneConjunction(oppositionTT, 90, 0) + easternQuadratureUT := neptuneConjunction(oppositionTT, 90, 0, inst) startTT = UTC2TT(easternQuadratureUT) } else { - westernQuadratureUT := neptuneConjunction(oppositionTT, 270, 1) + westernQuadratureUT := neptuneConjunction(oppositionTT, 270, 1, inst) endTT = UTC2TT(westernQuadratureUT) } bestJDE := zeroEventInWindow(startTT, endTT, 2.0, 2.0, 30.0/86400.0, func(jd float64) float64 { - return neptuneRADerivativeN(jd, stationDerivativeStepDay, neptuneEventSearchN) + return neptuneRADerivativeN(jd, stationDerivativeStepDay, neptuneEventSearchN, inst) }, func(jd float64) float64 { - return neptuneRADerivative(jd, stationDerivativeStepDay) + return neptuneRADerivative(jd, stationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func NextNeptuneRetrogradeToPrograde(jde float64) float64 { +// NextNeptuneRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := neptuneConjunctionFull(jde, 180, 0) - date := neptuneRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := neptuneConjunctionFull(jde, 180, 0, inst) + date := neptuneRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - nextOppositionJD := neptuneConjunctionFull(jde, 180, 1) + nextOppositionJD := neptuneConjunctionFull(jde, 180, 1, inst) if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - date = neptuneRetrogradeAroundOpposition(nextOppositionJD, false) + date = neptuneRetrogradeAroundOpposition(nextOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastNeptuneRetrogradeToPrograde(jde float64) float64 { +// LastNeptuneRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := neptuneConjunctionFull(jde, 180, 0) - date := neptuneRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := neptuneConjunctionFull(jde, 180, 0, inst) + date := neptuneRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - previousOppositionJD := neptuneConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0) + previousOppositionJD := neptuneConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0, inst) if !isFiniteFloat(previousOppositionJD) { return math.NaN() } - date = neptuneRetrogradeAroundOpposition(previousOppositionJD, false) + date = neptuneRetrogradeAroundOpposition(previousOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } return date } -func NextNeptuneProgradeToRetrograde(jde float64) float64 { +// NextNeptuneProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextNeptuneProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := neptuneConjunctionFull(jde, 180, 1) - date := neptuneRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := neptuneConjunctionFull(jde, 180, 1, inst) + date := neptuneRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - followingOppositionJD := neptuneConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1) + followingOppositionJD := neptuneConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1, inst) if !isFiniteFloat(followingOppositionJD) { return math.NaN() } - date = neptuneRetrogradeAroundOpposition(followingOppositionJD, true) + date = neptuneRetrogradeAroundOpposition(followingOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastNeptuneProgradeToRetrograde(jde float64) float64 { +// LastNeptuneProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastNeptuneProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := neptuneConjunctionFull(jde, 180, 1) - date := neptuneRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := neptuneConjunctionFull(jde, 180, 1, inst) + date := neptuneRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - lastOppositionJD := neptuneConjunctionFull(jde, 180, 0) + lastOppositionJD := neptuneConjunctionFull(jde, 180, 0, inst) if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - date = neptuneRetrogradeAroundOpposition(lastOppositionJD, true) + date = neptuneRetrogradeAroundOpposition(lastOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } diff --git a/basic/occultation.go b/basic/occultation.go index 8a80c6f..3bf2e04 100644 --- a/basic/occultation.go +++ b/basic/occultation.go @@ -55,9 +55,12 @@ const ( // OccultationPartial 表示掩甚时有限目标盘面只有部分被覆盖。 // OccultationPartial means only part of a finite target disk is covered at greatest occultation. OccultationPartial OccultationType = "partial" - // OccultationGrazing 表示两边缘相切,且没有正持续时间的重叠。 - // OccultationGrazing means the limbs are tangent without a positive-duration overlap. + // OccultationGrazing 表示掠掩:圆月面下两边缘相切、无正持续时间的重叠;按月缘剖面求解时指星迹始终在月缘起伏带内,可有多段遮挡。 + // OccultationGrazing is a graze: with a circular limb the limbs are tangent without a positive-duration overlap; with a limb profile the track stays inside the relief band and may be hidden several times. OccultationGrazing OccultationType = "grazing" + // OccultationNone 表示月缘剖面下恒星未被遮挡,只出现在精密剖面结果中。 + // OccultationNone means the star is never hidden by the limb profile; it only appears in precise profile results. + OccultationNone OccultationType = "none" ) // OccultationPlanet 标识有限盘面的行星目标。 @@ -153,9 +156,11 @@ type Observer struct { // 通用 MoonSemidiameterN 使用地心距离;月掩接触使用同一站心视差修正后的观测者到月球距离。 // moonTopocentricSemidiameterN returns the lunar angular radius as seen from the supplied site. // The usual MoonSemidiameterN uses geocentric distance; occultation contacts use the observer-to-Moon distance after the same topocentric parallax correction as the direction. -func moonTopocentricSemidiameterN(tt float64, observer Observer, n int) float64 { - moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, n) - moonDistanceKM := HMoonAwayN(tt, n) +func moonTopocentricSemidiameterN(tt float64, observer Observer, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, n, inst) + moonDistanceKM := HMoonAwayNWithInstance(tt, n, inst) if !finite(moonRA) || !finite(moonDec) || !finite(moonDistanceKM) || moonDistanceKM <= 0 { return math.NaN() } @@ -179,7 +184,6 @@ func topocentricDistanceKMWithSidereal( observer Observer, siderealDegrees float64, ) float64 { - const earthEquatorialRadius = 6378.14 const astronomicalUnitKM = angularDiameterAstronomicalUnitKM distanceAU := distanceKM / astronomicalUnitKM @@ -194,7 +198,7 @@ func topocentricDistanceKMWithSidereal( distanceAU * math.Sin(decRad), } theta := (siderealDegrees + observer.Longitude) * math.Pi / 180 - observerAU := earthEquatorialRadius / astronomicalUnitKM + observerAU := topocentricEarthRadiusKM / astronomicalUnitKM observerVector := [3]float64{ observerAU * pcosi(observer.Latitude, observer.Height) * math.Cos(theta), observerAU * pcosi(observer.Latitude, observer.Height) * math.Sin(theta), @@ -350,6 +354,9 @@ type OccultationSearchOptions struct { // MaxEvents 为正时限制返回事件数量。 // MaxEvents limits the number of returned events when positive. MaxEvents int + // LunarLimb 月缘剖面,nil 为圆月面。站心恒星、行星月掩按剖面重新判定是否成掩与食型(可新增或取消圆月面事件),接触取最外侧; + // 恒星多段遮挡只报首次掩入与最后复现,全部区间见 StarOccultationLimbProfile。全球路径只修正界线 / lunar limb profile; local events are re-decided on the profile. + LunarLimb LunarLimb } // OccultationPathAlgorithm 选择全球月掩路径的星历求解分支。 @@ -375,7 +382,9 @@ const ( type OccultationPathOptions struct { // Algorithm 的零值等同 optimized;exact 可选择原有精确分支。此选项不影响独立的单时刻月影与事件查询接口。 // Algorithm defaults to optimized; exact selects the original branch. Independent instant-footprint and event-search APIs are unaffected. - Algorithm OccultationPathAlgorithm + Algorithm OccultationPathAlgorithm + // LunarLimb 月缘剖面,nil 为圆月面;只修正外接触南北界(掠掩线),掩带填充与等时线仍按圆月面 / lunar limb profile for graze lines only. + LunarLimb LunarLimb Step time.Duration TargetSpacingKM float64 // RiseSetStep 独立控制六类升落阶段线的采样步长;零值使用 5 分钟。 @@ -472,6 +481,8 @@ type StarOccultationInfo struct { // ContactsComplete 表示两个月缘接触时刻均已求解。 // ContactsComplete is true when both lunar-limb contacts were solved. ContactsComplete bool + // LunarLimbApplied 表示接触时刻已按月缘剖面精修,否则为圆月面 / contacts refined with a lunar limb profile. + LunarLimbApplied bool MinimumSeparationArcsec float64 PositionAngleDeg float64 @@ -502,6 +513,8 @@ type PlanetOccultationInfo struct { HasInternalContacts bool ContactsComplete bool + // LunarLimbApplied 表示接触时刻已按月缘剖面精修,否则为圆月面 / contacts refined with a lunar limb profile. + LunarLimbApplied bool MinimumSeparationArcsec float64 PositionAngleDeg float64 @@ -602,6 +615,8 @@ type StarOccultationPath struct { // GreatestTimeContours 是按掩甚时刻采样的等时线。 // GreatestTimeContours are sampled local greatest-occultation time isolines. GreatestTimeContours []OccultationGreatestTimeContour + // LunarLimbApplied 表示南北限已按月缘剖面修正,否则为圆月面 / limits corrected with a lunar limb profile. + LunarLimbApplied bool Step time.Duration TargetSpacingKM float64 @@ -733,6 +748,8 @@ type PlanetOccultationPath struct { // GreatestTimeContours 是按掩甚时刻采样的等时线,判据用外接触锥,与偏掩可见域一致。 // GreatestTimeContours are sampled local greatest-occultation time isolines, gated by the outer-contact cone so they match the partial-occultation visibility area. GreatestTimeContours []OccultationGreatestTimeContour + // LunarLimbApplied 表示南北限已按月缘剖面修正,否则为圆月面 / limits corrected with a lunar limb profile. + LunarLimbApplied bool Step time.Duration TargetSpacingKM float64 diff --git a/basic/occultation_instant.go b/basic/occultation_instant.go index 6f0e36e..70090ac 100644 --- a/basic/occultation_instant.go +++ b/basic/occultation_instant.go @@ -44,9 +44,8 @@ type PlanetOccultationInstant struct { Total *PlanetOccultationFootprint } -// StarOccultationFootprintAt 返回指定时刻的精确点源恒星月掩可见足迹;它采用与路径时间线相同的分辨率,并对完整接触弧执行站心校正。 -// StarOccultationFootprintAt returns the exact visible lunar-occultation footprint of a point-source star at one instant, using timeline resolution and station-centred correction of the complete contact arc. -func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) { +// StarOccultationFootprintAtWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func StarOccultationFootprintAtWithInstance(at time.Time, star StarCoordinate, inst *Instance) (StarOccultationInstant, error) { result := StarOccultationInstant{Time: at, TargetID: star.ID} if at.IsZero() { return result, fmt.Errorf("%w: time is required", ErrInvalidOccultationInput) @@ -55,7 +54,7 @@ func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultat return result, err } - cache := newStarOccultationEventCache(star) + cache := newStarOccultationEventCache(star, inst) tt := occultationTimeToTT(at) result.DeltaTSeconds = DeltaT(tt, true) result.SublunarLongitude, result.SublunarLatitude = occultationSublunarPoint(tt, cache.frameAt) @@ -65,9 +64,8 @@ func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultat return result, nil } -// PlanetOccultationFootprintsAt 返回指定时刻有限行星盘面的精确外接触和内接触月掩可见足迹;它采用路径时间线分辨率,并对每条完整接触弧执行站心校正。 -// PlanetOccultationFootprintsAt returns the exact visible outer- and inner-contact lunar-occultation footprints of a finite planetary disk at one instant, using timeline resolution and station-centred correction of each complete contact arc. -func PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) { +// PlanetOccultationFootprintsAtWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func PlanetOccultationFootprintsAtWithInstance(at time.Time, planet OccultationPlanet, inst *Instance) (PlanetOccultationInstant, error) { result := PlanetOccultationInstant{Time: at, Planet: planet, TargetID: planet.String()} if at.IsZero() { return result, fmt.Errorf("%w: time is required", ErrInvalidOccultationInput) @@ -76,8 +74,8 @@ func PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (Plan return result, err } - config, _ := planetOccultationConfigFor(planet) - cache := newPlanetOccultationEventCache(config) + config, _ := planetOccultationConfigFor(planet, inst) + cache := newPlanetOccultationEventCache(config, inst) tt := occultationTimeToTT(at) result.DeltaTSeconds = DeltaT(tt, true) result.SublunarLongitude, result.SublunarLatitude = occultationSublunarPoint(tt, cache.outerFrameAt) diff --git a/basic/occultation_isochrone.go b/basic/occultation_isochrone.go index ecbaed7..8be9abf 100644 --- a/basic/occultation_isochrone.go +++ b/basic/occultation_isochrone.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) const ( @@ -80,7 +82,7 @@ func (arc occultationGreatestTimeArc) residual(longitude, latitude float64) floa func (arc occultationGreatestTimeArc) point(longitude, latitude float64, state occultationRiseSetState) OccultationPathPoint { return OccultationPathPoint{ Time: occultationTTToLocation(arc.evaluation.tt, arc.location), - Longitude: normalizeLongitude(longitude), + Longitude: geodata.NormalizeLongitude(longitude), Latitude: latitude, MoonAltitude: state.moonAltitude, } @@ -219,7 +221,7 @@ func traceOccultationGreatestTimeArc( next := arc.point(nextLongitude, nextLatitude, nextState) distance := occultationPathDistanceKM(points[len(points)-1], next) // 校正回到原点说明该方向已经走到支路端点,继续只会原地打转。 - if distance < 1e-9 || distance > 4*step*greatestTimeContourKMPerDegree { + if distance < 1e-9 || distance > 4*step*geodata.EarthKMPerDegree { break } points = append(points, next) diff --git a/basic/occultation_limb.go b/basic/occultation_limb.go new file mode 100644 index 0000000..6fe4724 --- /dev/null +++ b/basic/occultation_limb.go @@ -0,0 +1,360 @@ +package basic + +import ( + "math" + "time" +) + +// occultationLimbTarget 给出某时刻目标的地心视赤经、赤纬(度)与地心距(km,恒星为 0)及站心角半径(角秒)。 +type occultationLimbTarget func(tt float64) (ra, dec, distanceKM, radiusArcsec float64, ok bool) + +func planetOccultationLimbTarget(config planetOccultationConfig, observer Observer) occultationLimbTarget { + return func(tt float64) (float64, float64, float64, float64, bool) { + ra, dec := config.apparentRaDecN(tt, -1) + distanceKM := config.earthDistanceN(tt, -1) * angularDiameterAstronomicalUnitKM + radius := planetTopocentricSemidiameterN(tt, config, observer, -1) + return ra, dec, distanceKM, radius, finite(ra) && finite(dec) && finite(distanceKM) && finite(radius) + } +} + +const ( + occultationLimbCrossingStepDays = 60.0 / 86400.0 + occultationLimbCrossingSpanDays = 3.5 / 24.0 + occultationLimbRootDays = 1e-3 / 86400.0 + occultationLimbMinSamples = 64 + occultationLimbMaxSamples = 4000 + occultationLimbBandPadArcsec = 0.05 + occultationLimbUnrangedPadKM = 1.0 + planetOccultationLimbStepDays = 0.25 / 86400.0 +) + +// occultationLimbRangeKM 返回剖面半径范围;剖面不报告范围时在 tt 时刻朝目标扫一圈并外扩 1 km。 +func occultationLimbRangeKM(tt float64, limb LunarLimb, observer Observer, target occultationLimbTarget, inst *Instance) (float64, float64, bool) { + geoRA, geoDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + geoDistance := HMoonAwayNWithInstance(tt, -1, inst) + frame := newLunarLimbFrame(limb, tt, lunarEclipseLimbVector(geoRA, geoDec, geoDistance), inst) + if !frame.valid { + return 0, 0, false + } + if frame.ranged { + return frame.minKM, frame.maxKM, true + } + ra, dec, _, _, ok := target(tt) + if !ok { + return 0, 0, false + } + topoRA, topoDec := moonTopocentricApparentRaDec(tt, observer, -1, inst) + view, ok := frame.viewToward(lunarEclipseLimbVector(topoRA, topoDec, 1), lunarEclipseLimbVector(ra, dec, 1)) + if !ok { + return 0, 0, false + } + low, high := math.Inf(1), math.Inf(-1) + for offset := 0.0; offset < 360; offset += frame.stepDeg { + radius, ok := frame.radiusAt(view, offset) + if !ok { + return 0, 0, false + } + low, high = math.Min(low, radius), math.Max(high, radius) + } + return low - occultationLimbUnrangedPadKM, high + occultationLimbUnrangedPadKM, true +} + +// occultationLimbCrossing 自 greatestTT 向 direction 一侧求圆月面间隙升到 level 的时刻;间隙自掩甚单调增大,起点已不低于 level 时返回起点。 +func occultationLimbCrossing(greatestTT, direction, level float64, circular func(float64) (float64, bool)) (float64, bool) { + near := greatestTT + value, ok := circular(near) + if !ok { + return 0, false + } + if value >= level { + return greatestTT, true + } + for step := 1; float64(step)*occultationLimbCrossingStepDays <= occultationLimbCrossingSpanDays; step++ { + far := greatestTT + direction*float64(step)*occultationLimbCrossingStepDays + farValue, ok := circular(far) + if !ok { + return 0, false + } + if farValue < level { + near = far + continue + } + for i := 0; i < 64 && math.Abs(far-near) > occultationLimbRootDays; i++ { + mid := (near + far) / 2 + midValue, ok := circular(mid) + if !ok { + return 0, false + } + if midValue < level { + near = mid + } else { + far = mid + } + } + return (near + far) / 2, true + } + return 0, false +} + +// occultationLimbOuterRoots 在 [start, end] 上采样月缘间隙,返回首个由正转负与最后一个由负转正的零点; +// 端点状态由调用方保证(hiddenAtStart、hiddenAtEnd),ok 为假表示剖面无值或星历失败。 +func occultationLimbOuterRoots(start, end float64, hiddenAtStart, hiddenAtEnd bool, gap func(float64) (float64, bool)) (first, last float64, found, ok bool) { + if end <= start { + return 0, 0, false, true + } + count := int(math.Ceil((end - start) / planetOccultationLimbStepDays)) + if count < occultationLimbMinSamples { + count = occultationLimbMinSamples + } + if count > occultationLimbMaxSamples { + count = occultationLimbMaxSamples + } + first, last = math.NaN(), math.NaN() + previousTT, previousHidden := start, hiddenAtStart + for index := 0; index <= count; index++ { + tt := start + (end-start)*float64(index)/float64(count) + value, valueOK := gap(tt) + if !valueOK { + return 0, 0, false, false + } + hidden := value < 0 + if index == 0 { + hidden = hiddenAtStart + } else if index == count { + hidden = hiddenAtEnd + } + if index > 0 && hidden != previousHidden { + root := occultationLimbBisect(previousTT, tt, previousHidden, gap) + if hidden && math.IsNaN(first) { + first = root + } + if !hidden { + last = root + } + } + previousTT, previousHidden = tt, hidden + } + return first, last, !math.IsNaN(first) || !math.IsNaN(last), true +} + +// occultationLimbBisect 在相邻样本间求遮挡状态翻转的时刻;leftHidden 是左端的遮挡状态。 +func occultationLimbBisect(left, right float64, leftHidden bool, gap func(float64) (float64, bool)) float64 { + for i := 0; i < 64 && right-left > occultationLimbRootDays; i++ { + mid := (left + right) / 2 + value, ok := gap(mid) + if !ok { + break + } + if (value < 0) == leftHidden { + left = mid + } else { + right = mid + } + } + return (left + right) / 2 +} + +// planetOccultationInfoWithLimb 有月缘时按剖面判定行星月掩:只在圆月面间隙落进月缘起伏带的时段采样全方向月缘间隙, +// 取最外侧的外切与内切;月缘可新增或取消圆月面的掠掩与全掩,有正持续时间的接触一律按偏掩或全掩上报。剖面无值时退回圆月面结果。 +func planetOccultationInfoWithLimb(greatestTT float64, config planetOccultationConfig, observer Observer, safetyMarginArcsec float64, location *time.Location, limb LunarLimb, inst *Instance) (PlanetOccultationInfo, bool) { + circularInfo, circularOK := planetOccultationInfoAtGreatest(greatestTT, config, observer, safetyMarginArcsec, location, inst) + if limb == nil { + return circularInfo, circularOK + } + info, state, ok := planetOccultationGreatestInfo(greatestTT, config, observer, safetyMarginArcsec, location, inst) + if !ok { + return circularInfo, circularOK + } + target := planetOccultationLimbTarget(config, observer) + lowKM, highKM, ok := occultationLimbRangeKM(greatestTT, limb, observer, target, inst) + if !ok { + return circularInfo, circularOK + } + ephemeris, ok := newPlanetLimbEphemeris(greatestTT, config, limb, inst) + if !ok { + return circularInfo, circularOK + } + // 月缘间隙 ≈ 圆月面间隙 −(月缘半径 − 参考半径),所以圆月面间隙不低于 bandHigh 必不接触、不高于 bandLow 必已接触。 + // 起伏带以角秒计,外扩一点容差覆盖月心距在事件内的变化。 + scale := state.moonSemidiameter / moonEquatorialRadiusKM + bandLow := (lowKM-moonEquatorialRadiusKM)*scale - occultationLimbBandPadArcsec + bandHigh := (highKM-moonEquatorialRadiusKM)*scale + occultationLimbBandPadArcsec + circular := func(internal bool) func(float64) (float64, bool) { + return func(tt float64) (float64, bool) { return ephemeris.circularGap(tt, observer, internal) } + } + limbGap := func(internal bool) func(float64) (float64, bool) { + return func(tt float64) (float64, bool) { return ephemeris.limbGap(tt, observer, internal) } + } + // solve 求一种接触的最外侧掩入、复现:圆月面间隙在 [bandLow, bandHigh] 之外的两端遮挡状态已知,只在带内采样。 + solve := func(internal bool, circularMinimum float64) (float64, float64, bool, bool) { + if circularMinimum >= bandHigh { + return 0, 0, false, true + } + outerLeft, okLeft := occultationLimbCrossing(greatestTT, -1, bandHigh, circular(internal)) + outerRight, okRight := occultationLimbCrossing(greatestTT, 1, bandHigh, circular(internal)) + if !okLeft || !okRight { + return 0, 0, false, false + } + if circularMinimum > bandLow { + first, last, found, ok := occultationLimbOuterRoots(outerLeft, outerRight, false, false, limbGap(internal)) + return first, last, found && !math.IsNaN(first) && !math.IsNaN(last), ok + } + innerLeft, okLeft := occultationLimbCrossing(greatestTT, -1, bandLow, circular(internal)) + innerRight, okRight := occultationLimbCrossing(greatestTT, 1, bandLow, circular(internal)) + if !okLeft || !okRight { + return 0, 0, false, false + } + first, _, foundLeft, okLeftRoots := occultationLimbOuterRoots(outerLeft, innerLeft, false, true, limbGap(internal)) + _, last, foundRight, okRightRoots := occultationLimbOuterRoots(innerRight, outerRight, true, false, limbGap(internal)) + return first, last, foundLeft && foundRight && !math.IsNaN(first) && !math.IsNaN(last), okLeftRoots && okRightRoots + } + externalImmersion, externalEmersion, external, ok := solve(false, state.externalContactMetric) + if !ok { + return circularInfo, circularOK + } + if !external { + return PlanetOccultationInfo{}, false + } + internalImmersion, internalEmersion, internal, ok := solve(true, state.internalContactMetric) + if !ok { + return circularInfo, circularOK + } + info.Type = OccultationPartial + info.ExternalImmersion = occultationTTToLocation(externalImmersion, location) + info.ExternalEmersion = occultationTTToLocation(externalEmersion, location) + if internal && internalImmersion > externalImmersion && internalEmersion < externalEmersion && internalImmersion < internalEmersion { + info.Type = OccultationTotal + info.HasInternalContacts = true + info.InternalImmersion = occultationTTToLocation(internalImmersion, location) + info.InternalEmersion = occultationTTToLocation(internalEmersion, location) + } + info.ContactsComplete = true + info.LunarLimbApplied = true + return info, true +} + +// planetLimbEphemeris 掩甚前后 ±4 h 的地心月球、行星视位置与月球本体坐标架插值表;站心换算逐点精确计算。 +type planetLimbEphemeris struct { + bodies []localEphemerisVectorNode + axes []localEphemerisVectorNode + base lunarLimbFrame + radiusKM float64 +} + +func newPlanetLimbEphemeris(greatestTT float64, config planetOccultationConfig, limb LunarLimb, inst *Instance) (*planetLimbEphemeris, bool) { + half := int(math.Round(starOccultationLimbNodeSpan / starOccultationLimbNodeDays)) + ephemeris := &planetLimbEphemeris{ + bodies: make([]localEphemerisVectorNode, 2*half+1), + axes: make([]localEphemerisVectorNode, 2*half+1), + radiusKM: config.equatorialRadiusKM, + } + for index := range ephemeris.bodies { + tt := greatestTT + float64(index-half)*starOccultationLimbNodeDays + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + moon := lunarEclipseLimbVector(moonRA, moonDec, HMoonAwayNWithInstance(tt, -1, inst)) + planetRA, planetDec := config.apparentRaDecN(tt, -1) + planet := lunarEclipseLimbVector(planetRA, planetDec, config.earthDistanceN(tt, -1)*angularDiameterAstronomicalUnitKM) + frame := newLunarLimbFrame(limb, tt, moon, inst) + if !frame.valid || !finiteVector3(moon) || !finiteVector3(planet) { + return nil, false + } + if index == half { + ephemeris.base = frame + } + ephemeris.bodies[index] = localEphemerisVectorNode{tt: tt, first: moon, next: planet} + ephemeris.axes[index] = localEphemerisVectorNode{tt: tt, first: frame.axisX, next: frame.axisZ} + } + return ephemeris, true +} + +// planetLimbTopocentric 是插值得到的站心月球、行星方向与距离(km),以及该时刻的月球本体坐标架。 +type planetLimbTopocentric struct { + moon, planet [3]float64 + moonDistance, planetRadius float64 + frame lunarLimbFrame +} + +func (ephemeris *planetLimbEphemeris) topocentricAt(tt float64, observer Observer) (planetLimbTopocentric, bool) { + moon, planet, ok := interpolateLocalEphemerisVectors(ephemeris.bodies, tt) + if !ok { + return planetLimbTopocentric{}, false + } + axisX, axisZ, ok := interpolateLocalEphemerisVectors(ephemeris.axes, tt) + if !ok { + return planetLimbTopocentric{}, false + } + ut := TT2UTC(tt) + toTopocentric := func(vector [3]float64) (float64, float64, float64, bool) { + distance := math.Sqrt(lunarLimbDot(vector, vector)) + ra := normalizeRA(math.Atan2(vector[1], vector[0]) / rad) + dec := math.Asin(clampUnit(vector[2]/distance)) / rad + topoRA, topoDec := TopocentricRaDec(ra, dec, observer.Latitude, observer.Longitude, ut, distance/angularDiameterAstronomicalUnitKM, observer.Height) + topoDistance := topocentricDistanceKM(ra, dec, distance, observer, ut) + return normalizeRA(topoRA), topoDec, topoDistance, finite(topoRA) && finite(topoDec) && finite(topoDistance) && topoDistance > 0 + } + moonRA, moonDec, moonDistance, okMoon := toTopocentric(moon) + planetRA, planetDec, planetDistance, okPlanet := toTopocentric(planet) + if !okMoon || !okPlanet || planetDistance <= ephemeris.radiusKM { + return planetLimbTopocentric{}, false + } + frame := ephemeris.base + if frame.axisZ, ok = lunarLimbNormalize(axisZ); !ok { + return planetLimbTopocentric{}, false + } + if frame.axisX, ok = lunarLimbNormalize(lunarLimbReject(axisX, frame.axisZ)); !ok { + return planetLimbTopocentric{}, false + } + frame.axisY = lunarLimbCross(frame.axisZ, frame.axisX) + return planetLimbTopocentric{ + moon: lunarEclipseLimbVector(moonRA, moonDec, moonDistance), + planet: lunarEclipseLimbVector(planetRA, planetDec, 1), + moonDistance: moonDistance, + planetRadius: math.Asin(ephemeris.radiusKM / planetDistance), + frame: frame, + }, true +} + +// circularGap 圆月面外切或内切间隙(角秒),与 planetOccultationStateAt 同口径。 +func (ephemeris *planetLimbEphemeris) circularGap(tt float64, observer Observer, internal bool) (float64, bool) { + state, ok := ephemeris.topocentricAt(tt, observer) + if !ok { + return 0, false + } + separation := math.Acos(clampUnit(lunarLimbDot(state.moon, state.planet) / state.moonDistance)) + moonRadius := math.Asin(moonEquatorialRadiusKM / state.moonDistance) + if internal { + return (separation - moonRadius + state.planetRadius) * angularDiameterArcsecPerRadian, true + } + return (separation - moonRadius - state.planetRadius) * angularDiameterArcsecPerRadian, true +} + +// limbGap 全方向月缘外切或内切间隙(角秒)。 +func (ephemeris *planetLimbEphemeris) limbGap(tt float64, observer Observer, internal bool) (float64, bool) { + state, ok := ephemeris.topocentricAt(tt, observer) + if !ok { + return 0, false + } + view, ok := state.frame.viewToward(state.moon, state.planet) + if !ok { + return 0, false + } + contact := lunarLimbContact{ + frame: &state.frame, + view: view, + distanceKM: state.moonDistance, + separation: math.Acos(clampUnit(lunarLimbDot(state.moon, state.planet) / state.moonDistance)), + targetRadius: state.planetRadius, + } + if internal { + inner, ok := contact.innerRadius(true) + if !ok { + return 0, false + } + return (contact.separation - math.Abs(inner-contact.targetRadius)) * angularDiameterArcsecPerRadian, true + } + outer, ok := contact.outerRadius() + if !ok { + return 0, false + } + return (contact.separation - outer - contact.targetRadius) * angularDiameterArcsecPerRadian, true +} diff --git a/basic/occultation_limb_range_test.go b/basic/occultation_limb_range_test.go new file mode 100644 index 0000000..fc8f4b9 --- /dev/null +++ b/basic/occultation_limb_range_test.go @@ -0,0 +1,86 @@ +package basic + +// 剖面不报告半径范围时,月掩须在当时视向上扫满一圈求范围,结果与报告范围的同一剖面一致。 + +import ( + "math" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +// unrangedLunarLimb 只暴露 LimbRadiusKM,迫使月掩走整圈扫描。 +type unrangedLunarLimb struct{ profile *limb.Profile } + +func (u unrangedLunarLimb) LimbRadiusKM(lon, lat, angle float64) (float64, bool) { + return u.profile.LimbRadiusKM(lon, lat, angle) +} + +// limbBruteRangeKM 以剖面步长的 1/8 细扫一圈,作整圈扫描的对拍基准。 +func limbBruteRangeKM(t *testing.T, tt float64, profile LunarLimb, observer Observer, ra, dec float64) (float64, float64) { + t.Helper() + geoRA, geoDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, nil) + frame := newLunarLimbFrame(profile, tt, lunarEclipseLimbVector(geoRA, geoDec, HMoonAwayNWithInstance(tt, -1, nil)), nil) + topoRA, topoDec := moonTopocentricApparentRaDec(tt, observer, -1, nil) + view, ok := frame.viewToward(lunarEclipseLimbVector(topoRA, topoDec, 1), lunarEclipseLimbVector(ra, dec, 1)) + if !frame.valid || !ok { + t.Fatal("limb view failed") + } + low, high := math.Inf(1), math.Inf(-1) + for offset := 0.0; offset < 360; offset += frame.stepDeg / 8 { + radius, ok := frame.radiusAt(view, offset) + if !ok { + t.Fatal("limb radius failed") + } + low, high = math.Min(low, radius), math.Max(high, radius) + } + return low, high +} + +func TestOccultationLimbRangeScansFullCircleWithoutProvider(t *testing.T) { + profile := limb.Default() + unranged := unrangedLunarLimb{profile: profile} + observer := Observer{Longitude: 41.5907, Latitude: -55.07036} + tt := occultationTimeToTT(time.Date(2026, 2, 11, 3, 47, 15, 0, time.UTC)) + ra, dec := starApparentRaDec(tt, limbTestAntares, observer, nil) + wantLow, wantHigh := limbBruteRangeKM(t, tt, unranged, observer, ra, dec) + // 剖面网格节点即极值,整圈扫描与细扫只差插值误差;扫描步长放粗会漏掉极值。 + const tolerance = 0.05 + starLow, starHigh, ok := starOccultationLimbRange(tt, limbTestAntares, observer, unranged, nil) + if !ok { + t.Fatal("star range failed") + } + target := func(float64) (float64, float64, float64, float64, bool) { return ra, dec, 0, 0, true } + low, high, ok := occultationLimbRangeKM(tt, unranged, observer, target, nil) + if !ok { + t.Fatal("occultation range failed") + } + for _, got := range []struct { + name string + low, high float64 + pad float64 + }{{"star", starLow, starHigh, starOccultationLimbUnrangedPadKM}, {"planet", low, high, occultationLimbUnrangedPadKM}} { + if math.Abs(got.low+got.pad-wantLow) > tolerance || math.Abs(got.high-got.pad-wantHigh) > tolerance { + t.Errorf("%s: range %.3f–%.3f km, brute force %.3f–%.3f km", got.name, got.low+got.pad, got.high-got.pad, wantLow, wantHigh) + } + } + rangedLow, rangedHigh := profile.RadiusRangeKM() + if wantLow < rangedLow || wantHigh > rangedHigh || wantHigh-wantLow < 1 { + t.Errorf("brute force %.3f–%.3f km outside table range %.3f–%.3f km or too flat", wantLow, wantHigh, rangedLow, rangedHigh) + } +} + +func TestStarOccultationUnrangedLimbMatchesRanged(t *testing.T) { + const lon, lat = 41.5907, -55.07036 + 0.4/111.2 + ranged := limbTestAntaresEvent(t, lon, lat, limb.Default()) + unranged := limbTestAntaresEvent(t, lon, lat, unrangedLunarLimb{profile: limb.Default()}) + if len(ranged) != 1 || len(unranged) != 1 || ranged[0].Type != OccultationGrazing || unranged[0].Type != ranged[0].Type { + t.Fatalf("ranged %+v vs unranged %+v, want the same limb graze", ranged, unranged) + } + for _, pair := range [][2]time.Time{{ranged[0].Immersion, unranged[0].Immersion}, {ranged[0].Emersion, unranged[0].Emersion}} { + if d := math.Abs(pair[0].Sub(pair[1]).Seconds()); d > 0.05 { + t.Errorf("unranged limb moved a contact by %.3f s", d) + } + } +} diff --git a/basic/occultation_limit_separation_test.go b/basic/occultation_limit_separation_test.go index 9c0137d..a7bda62 100644 --- a/basic/occultation_limit_separation_test.go +++ b/basic/occultation_limit_separation_test.go @@ -4,6 +4,8 @@ import ( "math" "testing" "time" + + "b612.me/astro/internal/ephemtest" ) // 掩带两条横向口径的契约:WidthKM 是中心线采样处的地面横向宽度,南北限是外接触锥的擦边切点轨迹, @@ -240,6 +242,7 @@ func TestPlanetOccultationWidthKMIsGroundCrossTrackWidth(t *testing.T) { } func TestPlanetOccultationLimitSeparationMatchesExportedLimits(t *testing.T) { + values := ephemtest.NewValues(t, "occultation_limit_anchors") for _, fixture := range occultationLimitFixtures() { path := occultationLimitPath(t, fixture) nearestIndex, nearestDelta := 0, math.Inf(1) @@ -279,15 +282,17 @@ func TestPlanetOccultationLimitSeparationMatchesExportedLimits(t *testing.T) { {name: "greatest limit separation", got: path.GreatestLimitSeparationKM, want: fixture.limitSeparationKM}, {name: "greatest total width", got: path.GreatestTotalWidthKM, want: fixture.totalWidthKM}, } { - if difference := math.Abs(anchor.got - anchor.want); difference > occultationLimitAnchorToleranceKM { + want := values.Want(fixture.name+"/"+anchor.name, anchor.want, anchor.got) + if difference := math.Abs(anchor.got - want); difference > occultationLimitAnchorToleranceKM { t.Fatalf("%s: %s=%.6f km, want %.6f km within %.3f km", - fixture.name, anchor.name, anchor.got, anchor.want, occultationLimitAnchorToleranceKM) + fixture.name, anchor.name, anchor.got, want, occultationLimitAnchorToleranceKM) } } ratio := path.GreatestLimitSeparationKM / path.Greatest.WidthKM - if math.Abs(ratio-fixture.limitRatio) > occultationLimitRatioTolerance { + wantRatio := values.Want(fixture.name+"/limit ratio", fixture.limitRatio, ratio) + if math.Abs(ratio-wantRatio) > occultationLimitRatioTolerance { t.Fatalf("%s: limit separation/width=%.6f, want %.6f within %.4f", - fixture.name, ratio, fixture.limitRatio, occultationLimitRatioTolerance) + fixture.name, ratio, wantRatio, occultationLimitRatioTolerance) } if path.GreatestTotalWidthKM >= path.Greatest.WidthKM { t.Fatalf("%s: total width=%.6f must stay below the outer width=%.6f", @@ -342,6 +347,8 @@ func TestStarOccultationLimitSeparationMatchesExportedLimits(t *testing.T) { // 于是 Saturn 2024-08-21 的 CenterLine[160] 少 47.9 km(3810.7491 对 3858.7641)、 // Venus 1227-05-19 的 CenterLine[15] 少 158.6 km(6047.4858 对 6206.0535)。 func TestPlanetOccultationGrazingEndpointWidthAnchors(t *testing.T) { + // 夹具按旧模型几何选取,用来覆盖该边界分支。 + ephemtest.PinLegacy(t) testCases := []struct { name string planet OccultationPlanet diff --git a/basic/occultation_path.go b/basic/occultation_path.go index 7e6f31f..5d6937c 100644 --- a/basic/occultation_path.go +++ b/basic/occultation_path.go @@ -5,6 +5,8 @@ import ( "sort" "sync" "time" + + "b612.me/astro/internal/geodata" ) const ( @@ -29,16 +31,12 @@ const ( occultationPathBoundaryBranchJumpKM = 120.0 occultationPathBoundaryBranchSpeedKMPerSecond = 10.0 - occultationPathEarthEquatorialRadiusKM = 6378.1366 - occultationPathEarthPolarRatio = 0.99664719 - occultationPathAstronomicalUnitKM = 149597870.7 + occultationPathEarthPolarRatio = 0.99664719 + occultationPathAstronomicalUnitKM = 149597870.7 ) -// FindStarOccultationPaths 搜索单颗点源恒星月掩的全球掩带。 -// 查询窗口按全球几何掩甚点选择事件,端点容差为 10 ms,与数值根精度一致。返回路径扩展到完整全球起止点;函数不会加载内嵌星表,调用者需显式提供坐标。 -// FindStarOccultationPaths searches the global lunar-occultation footprint of one point-source star. -// The query window selects events by global geometric greatest, with a 10 ms endpoint tolerance matching the numerical root precision. Each returned path expands to its complete global start and end; the function does not load the embedded catalog. -func FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) { +// FindStarOccultationPathsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindStarOccultationPathsWithInstance(start, end time.Time, star StarCoordinate, options OccultationPathOptions, inst *Instance) ([]StarOccultationPath, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -60,11 +58,11 @@ func FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options candidateEndTT, starOccultationCoarseStepDays(coarseOptions), star, - 0, + 0, inst, ) paths := make([]StarOccultationPath, 0, len(candidates)) for _, seedTT := range candidates { - path, ok, err := starOccultationPathAtSeed(seedTT, star, options, start, end) + path, ok, err := starOccultationPathAtSeed(seedTT, star, options, start, end, inst) if err != nil { return nil, err } @@ -122,10 +120,11 @@ func starOccultationPathAtSeed( seedTT float64, star StarCoordinate, options OccultationPathOptions, - selectionStart, selectionEnd time.Time, -) (StarOccultationPath, bool, error) { + selectionStart, selectionEnd time.Time, instances ...*Instance) (StarOccultationPath, bool, error) { + inst := instanceValue(instances) + location := selectionStart.Location() - cache := newStarOccultationEventCache(star) + cache := newStarOccultationEventCache(star, inst) cache.prepareLocalEphemeris(seedTT) frameAt := cache.frameAt candidateFrameAt := cache.candidateFrameAt @@ -170,14 +169,14 @@ func starOccultationPathAtSeed( return StarOccultationPath{}, false, ErrOccultationPathSamplingLimit } - start := starOccultationPathBoundaryEndpointWithFrame(outerStart, frameAt, location, 1) - end := starOccultationPathBoundaryEndpointWithFrame(outerEnd, frameAt, location, -1) + start := starOccultationPathBoundaryEndpointWithFrame(outerStart, frameAt, location, 1, inst) + end := starOccultationPathBoundaryEndpointWithFrame(outerEnd, frameAt, location, -1, inst) if !start.valid || !end.valid { return StarOccultationPath{}, false, nil } exactFrameAt := frameAt if options.Algorithm != OccultationPathAlgorithmExact { - optimized := newStarOccultationEventCache(star) + optimized := newStarOccultationEventCache(star, inst) optimized.preparePathEphemeris(seedTT, options.Algorithm) if optimized.local.dense { cache = optimized @@ -220,6 +219,10 @@ func starOccultationPathAtSeed( path.SouthernLimit = occultationStationCorrectLimitSeries( path.SouthernLimit, frameAt, cache.riseSetContextAt, false, location, ) + var northApplied, southApplied bool + path.NorthernLimit, northApplied = occultationLimbCorrectLimitSeries(path.NorthernLimit, cache.riseSetContextAt, options.LunarLimb, location, inst) + path.SouthernLimit, southApplied = occultationLimbCorrectLimitSeries(path.SouthernLimit, cache.riseSetContextAt, options.LunarLimb, location, inst) + path.LunarLimbApplied = northApplied || southApplied path.GreatestLimitSeparationKM, _ = occultationPathLimitSeparations( path.NorthernLimit, path.SouthernLimit, greatestTT, ) @@ -458,9 +461,11 @@ func occultationPathContourStepDays(options OccultationPathOptions) float64 { return stepDays } -func starOccultationPathWindow(seedTT, startTT, endTT float64, star StarCoordinate, center bool) (float64, float64, bool) { +func starOccultationPathWindow(seedTT, startTT, endTT float64, star StarCoordinate, center bool, instances ...*Instance) (float64, float64, bool) { + inst := instanceValue(instances) + return starOccultationPathWindowWithFrame(seedTT, startTT, endTT, func(tt float64) (occultationPathFrame, bool) { - return starOccultationPathFrameAt(tt, star) + return starOccultationPathFrameAt(tt, star, inst) }, center) } @@ -511,9 +516,11 @@ func occultationPathWindowWithCandidateFrames( ) } -func starOccultationPathGreatest(seedTT, startTT, endTT float64, star StarCoordinate) float64 { +func starOccultationPathGreatest(seedTT, startTT, endTT float64, star StarCoordinate, instances ...*Instance) float64 { + inst := instanceValue(instances) + return starOccultationPathGreatestWithFrame(seedTT, startTT, endTT, func(tt float64) (occultationPathFrame, bool) { - return starOccultationPathFrameAt(tt, star) + return starOccultationPathFrameAt(tt, star, inst) }) } @@ -746,6 +753,8 @@ type starOccultationEphemerisState struct { } type starOccultationEventCache struct { + inst *Instance + star StarCoordinate states map[uint64]starOccultationEphemerisState frames map[uint64]planetOccultationFrameCacheEntry @@ -753,8 +762,10 @@ type starOccultationEventCache struct { local *starOccultationLocalEphemeris } -func newStarOccultationEventCache(star StarCoordinate) *starOccultationEventCache { - cache := &starOccultationEventCache{ +func newStarOccultationEventCache(star StarCoordinate, instances ...*Instance) *starOccultationEventCache { + inst := instanceValue(instances) + + cache := &starOccultationEventCache{inst: inst, star: star, states: make(map[uint64]starOccultationEphemerisState), frames: make(map[uint64]planetOccultationFrameCacheEntry), @@ -785,7 +796,7 @@ func (cache *starOccultationEventCache) stateAt(tt float64) starOccultationEphem state, interpolated = cache.local.stateAt(tt) } if !interpolated { - state = starOccultationEphemerisStateAt(tt, cache.star) + state = starOccultationEphemerisStateAt(tt, cache.star, cache.inst) } cache.states[key] = state return state @@ -793,7 +804,7 @@ func (cache *starOccultationEventCache) stateAt(tt float64) starOccultationEphem func (cache *starOccultationEventCache) prepareLocalEphemeris(center float64) { if cache.local == nil { - cache.local = newStarOccultationLocalEphemeris(center, cache.star) + cache.local = newStarOccultationLocalEphemeris(center, cache.star, cache.inst) } } @@ -851,15 +862,19 @@ type occultationPathEndpoint struct { func (f occultationPathFrame) moonProjectionX() float64 { return occultationPathDot(f.moon, f.first) } func (f occultationPathFrame) moonProjectionY() float64 { return occultationPathDot(f.moon, f.second) } -func starOccultationPathFrameAt(tt float64, star StarCoordinate) (occultationPathFrame, bool) { - return starOccultationPathFrameFromState(starOccultationEphemerisStateAt(tt, star)) +func starOccultationPathFrameAt(tt float64, star StarCoordinate, instances ...*Instance) (occultationPathFrame, bool) { + inst := instanceValue(instances) + + return starOccultationPathFrameFromState(starOccultationEphemerisStateAt(tt, star, inst)) } -func starOccultationEphemerisStateAt(tt float64, star StarCoordinate) starOccultationEphemerisState { - moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, -1) - moonDistanceKM := HMoonAwayN(tt, -1) +func starOccultationEphemerisStateAt(tt float64, star StarCoordinate, instances ...*Instance) starOccultationEphemerisState { + inst := instanceValue(instances) + + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + moonDistanceKM := HMoonAwayNWithInstance(tt, -1, inst) // 方向与距离必须来自同一次三维推进,否则视差修正会退回历元距离。 - starRA, starDec, starDistanceAU := starApparentRaDecDistanceGeocentric(tt, star) + starRA, starDec, starDistanceAU := starApparentRaDecDistanceGeocentric(tt, star, inst) starDistanceKM := starDistanceAU * occultationPathAstronomicalUnitKM return starOccultationEphemerisState{ moonRA: moonRA, moonDec: moonDec, moonDistanceKM: moonDistanceKM, @@ -906,8 +921,9 @@ func starOccultationPathBoundaryEndpointWithFrame( tt float64, frameAt occultationPathFrameFunc, location *time.Location, - direction int, -) occultationPathEndpoint { + direction int, instances ...*Instance) occultationPathEndpoint { + inst := instanceValue(instances) + if _, ok := frameAt(tt); !ok { return occultationPathEndpoint{} } @@ -919,15 +935,17 @@ func starOccultationPathBoundaryEndpointWithFrame( } vector, _, valid := occultationPathBoundaryTangent(candidateFrame) if valid { - return occultationPathEndpoint{point: occultationPathPointFromVector(candidateTT, vector, 0, location), valid: true} + return occultationPathEndpoint{point: occultationPathPointFromVector(candidateTT, vector, 0, location, inst), valid: true} } } return occultationPathEndpoint{} } -func starOccultationPathCenterPoint(tt float64, star StarCoordinate, location *time.Location) (OccultationPathPoint, bool) { +func starOccultationPathCenterPoint(tt float64, star StarCoordinate, location *time.Location, instances ...*Instance) (OccultationPathPoint, bool) { + inst := instanceValue(instances) + return starOccultationPathCenterPointWithFrame(tt, func(candidateTT float64) (occultationPathFrame, bool) { - return starOccultationPathFrameAt(candidateTT, star) + return starOccultationPathFrameAt(candidateTT, star, inst) }, location) } @@ -1227,7 +1245,7 @@ func occultationPathBoundaryLine(frame occultationPathFrame, theta float64) (dis polarRatioSquared := occultationPathEarthPolarRatio * occultationPathEarthPolarRatio a := direction.x*direction.x + direction.y*direction.y + direction.z*direction.z/polarRatioSquared b = origin.x*direction.x + origin.y*direction.y + origin.z*direction.z/polarRatioSquared - c := origin.x*origin.x + origin.y*origin.y + origin.z*origin.z/polarRatioSquared - occultationPathEarthEquatorialRadiusKM*occultationPathEarthEquatorialRadiusKM + c := origin.x*origin.x + origin.y*origin.y + origin.z*origin.z/polarRatioSquared - geodata.EarthEquatorialRadiusKM*geodata.EarthEquatorialRadiusKM discriminant = b*b - a*c scale = math.Max(math.Abs(b*b), math.Abs(a*c)) return discriminant, b, scale, a > 0 && finite(discriminant) @@ -1241,9 +1259,11 @@ func occultationPathBoundaryIntersection(frame occultationPathFrame, theta, tole return occultationEarthLineIntersectionWithTolerance(origin, direction, tolerance) } -func occultationPathPointFromVector(tt float64, vector occultationPathVector, width float64, location *time.Location) OccultationPathPoint { +func occultationPathPointFromVector(tt float64, vector occultationPathVector, width float64, location *time.Location, instances ...*Instance) OccultationPathPoint { + inst := instanceValue(instances) + lon, lat := occultationPathGeodetic(tt, vector) - moonRA, moonDec := moonTopocentricApparentRaDec(tt, Observer{Longitude: lon, Latitude: lat}, -1) + moonRA, moonDec := moonTopocentricApparentRaDec(tt, Observer{Longitude: lon, Latitude: lat}, -1, inst) return OccultationPathPoint{ Time: occultationTTToLocation(tt, location), Longitude: lon, @@ -1419,17 +1439,7 @@ func occultationPathDistanceKM(a, b OccultationPathPoint) float64 { } func occultationPathDistanceKMValues(lon1, lat1, lon2, lat2 float64) float64 { - lat1 *= math.Pi / 180 - lat2 *= math.Pi / 180 - dLat := lat2 - lat1 - dLon := (lon2 - lon1) * math.Pi / 180 - dLon = math.Mod(dLon+math.Pi, 2*math.Pi) - if dLon < 0 { - dLon += 2 * math.Pi - } - dLon -= math.Pi - h := math.Sin(dLat/2)*math.Sin(dLat/2) + math.Cos(lat1)*math.Cos(lat2)*math.Sin(dLon/2)*math.Sin(dLon/2) - return 2 * occultationPathEarthEquatorialRadiusKM * math.Asin(math.Sqrt(math.Min(1, h))) + return geodata.EarthEquatorialRadiusKM * geodata.CentralAngle(lon1, lat1, lon2, lat2) } type occultationPathVector struct{ x, y, z float64 } @@ -1504,14 +1514,14 @@ func occultationPathTrackReference(frame occultationPathFrame) (occultationPathV if metricRadius <= 1e-9 || !finite(metricRadius) { return occultationPathVector{}, false } - return occultationPathScale(closest, occultationPathEarthEquatorialRadiusKM/metricRadius), true + return occultationPathScale(closest, geodata.EarthEquatorialRadiusKM/metricRadius), true } func occultationEarthLineIntersectionWithTolerance(origin, direction occultationPathVector, tolerance float64) (occultationPathVector, float64, bool) { polarRatioSquared := occultationPathEarthPolarRatio * occultationPathEarthPolarRatio a := direction.x*direction.x + direction.y*direction.y + direction.z*direction.z/polarRatioSquared b := origin.x*direction.x + origin.y*direction.y + origin.z*direction.z/polarRatioSquared - c := origin.x*origin.x + origin.y*origin.y + origin.z*origin.z/polarRatioSquared - occultationPathEarthEquatorialRadiusKM*occultationPathEarthEquatorialRadiusKM + c := origin.x*origin.x + origin.y*origin.y + origin.z*origin.z/polarRatioSquared - geodata.EarthEquatorialRadiusKM*geodata.EarthEquatorialRadiusKM discriminant := b*b - a*c if discriminant < -tolerance || a <= 0 { return occultationPathVector{}, 0, false @@ -1542,7 +1552,7 @@ func occultationPathGeodeticWithSidereal( vector occultationPathVector, siderealDegrees float64, ) (float64, float64) { - longitude := normalizeLongitude(math.Atan2(vector.y, vector.x)*180/math.Pi - siderealDegrees) + longitude := geodata.NormalizeLongitude(math.Atan2(vector.y, vector.x)*180/math.Pi - siderealDegrees) return longitude, occultationPathGeodeticLatitude(vector) } @@ -1577,11 +1587,3 @@ func occultationPathEarthFixedVectorWithRotation( z: vector.z, } } - -func normalizeLongitude(longitude float64) float64 { - longitude = math.Mod(longitude+180, 360) - if longitude < 0 { - longitude += 360 - } - return longitude - 180 -} diff --git a/basic/occultation_path_ephemeris.go b/basic/occultation_path_ephemeris.go index ac49115..b5fa942 100644 --- a/basic/occultation_path_ephemeris.go +++ b/basic/occultation_path_ephemeris.go @@ -1,11 +1,13 @@ package basic -import "math" +import ( + "math" +) const ( occultationDenseEphemerisNodeCount = 193 occultationDenseEphemerisStepDays = 30.0 / 1440.0 - occultationDenseEphemerisTolerance = 5e-11 + occultationDenseEphemerisTolerance = 1e-8 // relaxed for vector-based apparent positions ) // Width extrema near a limb can change branches under tiny frame differences. @@ -23,7 +25,7 @@ func correctOccultationCenterWidths(points []OccultationPathPoint, frameAt occul func (cache *starOccultationEventCache) preparePathEphemeris(center float64, algorithm OccultationPathAlgorithm) { if algorithm != OccultationPathAlgorithmExact { nodes := newDenseOccultationEphemerisNodes(center, func(tt float64) ([3]float64, [3]float64) { - state := starOccultationEphemerisStateAt(tt, cache.star) + state := starOccultationEphemerisStateAt(tt, cache.star, cache.inst) moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM) distance := state.starDistanceKM if distance <= 0 { @@ -33,7 +35,7 @@ func (cache *starOccultationEventCache) preparePathEphemeris(center float64, alg return [3]float64{moon.x, moon.y, moon.z}, [3]float64{target.x, target.y, target.z} }) if len(nodes) > 0 { - cache.local = newStarOccultationLocalEphemerisFromNodes(center, cache.star, nodes, true) + cache.local = newStarOccultationLocalEphemerisFromNodes(center, cache.star, nodes, true, cache.inst) } } cache.prepareLocalEphemeris(center) @@ -42,7 +44,7 @@ func (cache *starOccultationEventCache) preparePathEphemeris(center float64, alg func (cache *planetOccultationEventCache) preparePathEphemeris(center float64, algorithm OccultationPathAlgorithm) { if algorithm != OccultationPathAlgorithmExact { nodes := newDenseOccultationEphemerisNodes(center, func(tt float64) ([3]float64, [3]float64) { - state := planetOccultationEphemerisStateAt(tt, cache.config) + state := planetOccultationEphemerisStateAt(tt, cache.config, cache.inst) moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM) target := occultationPathRaDecVector(state.planetRA, state.planetDec, state.planetDistanceKM) return [3]float64{moon.x, moon.y, moon.z}, [3]float64{target.x, target.y, target.z} diff --git a/basic/occultation_path_ephemeris_test.go b/basic/occultation_path_ephemeris_test.go index d63742d..55282ae 100644 --- a/basic/occultation_path_ephemeris_test.go +++ b/basic/occultation_path_ephemeris_test.go @@ -6,6 +6,8 @@ import ( "reflect" "testing" "time" + + _ "b612.me/astro/planet" // ensure semianalytic engine registration ) func TestOccultationPathAlgorithmOptions(t *testing.T) { diff --git a/basic/occultation_planet.go b/basic/occultation_planet.go index 6556b1a..483c2cd 100644 --- a/basic/occultation_planet.go +++ b/basic/occultation_planet.go @@ -52,12 +52,9 @@ func (state planetOccultationState) movingDiskContactState() movingDiskContactSt } } -// FindPlanetOccultations 搜索固定观测点的有限盘面行星月掩。 -// 经度东为正、纬度北为正,单位为度;高度为平均海平面以上米数。目标位置、视差和视半径会在每次候选、掩甚和接触计算时重新计算。 -// FindPlanetOccultations searches one finite-disk planet at a fixed observing site. -// Longitude is east-positive in degrees, latitude is north-positive in degrees, and height is the observer elevation above mean sea level in meters. The target position, parallax, and semidiameter are recomputed at every candidate, greatest, and contact evaluation. -func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, - options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { +// FindPlanetOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindPlanetOccultationsWithInstance(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, + options OccultationSearchOptions, inst *Instance) ([]PlanetOccultationInfo, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -72,16 +69,16 @@ func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, long return nil, err } - config, _ := planetOccultationConfigFor(planet) + config, _ := planetOccultationConfigFor(planet, inst) startTT := occultationTimeToTT(start) endTT := occultationTimeToTT(end) resultLocation := start.Location() candidates := planetOccultationCandidateGreatestTimes( - startTT, endTT, planetOccultationCoarseStepDays(options), config, &observer, options.SafetyMarginArcsec, + startTT, endTT, planetOccultationCoarseStepDays(options), config, &observer, options.SafetyMarginArcsec, inst, ) results := make([]PlanetOccultationInfo, 0, len(candidates)) for _, greatestTT := range candidates { - info, ok := planetOccultationInfoAtGreatest(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation) + info, ok := planetOccultationInfoWithLimb(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst) if !ok { continue } @@ -96,12 +93,9 @@ func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, long return results, nil } -// FindBestPlanetOccultations 返回窗口内每次有限盘面行星月掩的全球海平面几何掩甚点。 -// 地心数据只用于搜索初值;返回位置与 FindPlanetOccultationPaths 一致,地平线可见性只报告、不参与点选择。距离查询端点 10 ms 内的掩甚时刻也会包含,与数值根精度一致。 -// FindBestPlanetOccultations returns the global geometric greatest point at sea level for every finite-disk planetary occultation in the window. -// Geocentric data only seeds the search. The returned location matches FindPlanetOccultationPaths; horizon visibility is reported but does not select the point. A greatest instant within 10 ms of either query endpoint is included, matching the numerical root precision. -func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, - options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { +// FindBestPlanetOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindBestPlanetOccultationsWithInstance(start, end time.Time, planet OccultationPlanet, + options OccultationSearchOptions, inst *Instance) ([]PlanetOccultationInfo, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -112,7 +106,7 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, return nil, err } - config, _ := planetOccultationConfigFor(planet) + config, _ := planetOccultationConfigFor(planet, inst) startTT := occultationTimeToTT(start) endTT := occultationTimeToTT(end) selectionStartTT := startTT - occultationEventSelectionToleranceDays @@ -121,15 +115,15 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, candidateEndTT := endTT + occultationPathSearchSpanDays resultLocation := start.Location() candidates := planetOccultationCandidateGreatestTimes( - candidateStartTT, candidateEndTT, planetOccultationCoarseStepDays(options), config, nil, options.SafetyMarginArcsec, + candidateStartTT, candidateEndTT, planetOccultationCoarseStepDays(options), config, nil, options.SafetyMarginArcsec, inst, ) results := make([]PlanetOccultationInfo, 0, len(candidates)) for _, seedTT := range candidates { - greatestTT, observer, _, observerOK := planetOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, config) + greatestTT, observer, _, observerOK := planetOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, config, inst) if !observerOK { continue } - info, ok := planetOccultationInfoAtGreatest(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation) + info, ok := planetOccultationInfoWithLimb(greatestTT, config, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst) if !ok { continue } @@ -144,51 +138,53 @@ func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, return results, nil } -func planetOccultationConfigFor(planet OccultationPlanet) (planetOccultationConfig, bool) { +func planetOccultationConfigFor(planet OccultationPlanet, instances ...*Instance) (planetOccultationConfig, bool) { + inst := instanceValue(instances) + config := planetOccultationConfig{planet: planet} switch planet { case OccultationMercury: config.planetIndex = 1 config.equatorialRadiusKM = mercuryEquatorialRadiusKM - config.apparentRaDecN = MercuryApparentRaDecN - config.earthDistanceN = EarthMercuryAwayN - config.semidiameterN = MercurySemidiameterN + config.apparentRaDecN = func(jde float64, n int) (float64, float64) { return MercuryApparentRaDecNWithInstance(jde, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthMercuryAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return MercurySemidiameterNWithInstance(jde, n, inst) } case OccultationVenus: config.planetIndex = 2 config.equatorialRadiusKM = venusEquatorialRadiusKM - config.apparentRaDecN = VenusApparentRaDecN - config.earthDistanceN = EarthVenusAwayN - config.semidiameterN = VenusSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return VenusApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthVenusAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return VenusSemidiameterNWithInstance(jde, n, inst) } case OccultationMars: config.planetIndex = 3 config.equatorialRadiusKM = marsEquatorialRadiusKM - config.apparentRaDecN = MarsApparentRaDecN - config.earthDistanceN = EarthMarsAwayN - config.semidiameterN = MarsSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return MarsApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthMarsAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return MarsSemidiameterNWithInstance(jde, n, inst) } case OccultationJupiter: config.planetIndex = 4 config.equatorialRadiusKM = jupiterEquatorialRadiusKM - config.apparentRaDecN = JupiterApparentRaDecN - config.earthDistanceN = EarthJupiterAwayN - config.semidiameterN = JupiterSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return JupiterApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthJupiterAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return JupiterSemidiameterNWithInstance(jde, n, inst) } case OccultationSaturn: config.planetIndex = 5 config.equatorialRadiusKM = saturnEquatorialRadiusKM - config.apparentRaDecN = SaturnApparentRaDecN - config.earthDistanceN = EarthSaturnAwayN - config.semidiameterN = SaturnSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return SaturnApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthSaturnAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return SaturnSemidiameterNWithInstance(jde, n, inst) } case OccultationUranus: config.planetIndex = 6 config.equatorialRadiusKM = uranusEquatorialRadiusKM - config.apparentRaDecN = UranusApparentRaDecN - config.earthDistanceN = EarthUranusAwayN - config.semidiameterN = UranusSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return UranusApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthUranusAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return UranusSemidiameterNWithInstance(jde, n, inst) } case OccultationNeptune: config.planetIndex = 7 config.equatorialRadiusKM = neptuneEquatorialRadiusKM - config.apparentRaDecN = NeptuneApparentRaDecN - config.earthDistanceN = EarthNeptuneAwayN - config.semidiameterN = NeptuneSemidiameterN + config.apparentRaDecN = func(jd float64, n int) (float64, float64) { return NeptuneApparentRaDecNWithInstance(jd, n, inst) } + config.earthDistanceN = func(jd float64, n int) float64 { return EarthNeptuneAwayNWithInstance(jd, n, inst) } + config.semidiameterN = func(jde float64, n int) float64 { return NeptuneSemidiameterNWithInstance(jde, n, inst) } default: return planetOccultationConfig{}, false } @@ -199,8 +195,9 @@ func planetOccultationCandidateGreatestTimes( startTT, endTT, step float64, config planetOccultationConfig, observer *Observer, - safetyMarginArcsec float64, -) []float64 { + safetyMarginArcsec float64, instances ...*Instance) []float64 { + inst := instanceValue(instances) + if endTT <= startTT { return nil } @@ -214,20 +211,20 @@ func planetOccultationCandidateGreatestTimes( leftTT := scanStart centerTT := math.Min(leftTT+step, scanEnd) - leftValue := planetOccultationExternalContactMetric(leftTT, config, observer, 8) - centerValue := planetOccultationExternalContactMetric(centerTT, config, observer, 8) + leftValue := planetOccultationExternalContactMetric(leftTT, config, observer, 8, inst) + centerValue := planetOccultationExternalContactMetric(centerTT, config, observer, 8, inst) results := make([]float64, 0) for centerTT < scanEnd { rightTT := math.Min(centerTT+step, scanEnd) - rightValue := planetOccultationExternalContactMetric(rightTT, config, observer, 8) + rightValue := planetOccultationExternalContactMetric(rightTT, config, observer, 8, inst) candidateLimit := planetOccultationCandidateLimitArcsec + safetyMarginArcsec if finite(leftValue) && finite(centerValue) && finite(rightValue) && centerValue <= leftValue && centerValue <= rightValue && centerValue <= candidateLimit { // 最小外接触度量同时包含动态月面和行星盘面,因此定义事件是否存在以及报告的掩甚时刻。 // The minimum outer-contact metric includes both dynamic disks and therefore defines event existence and the reported greatest instant. - greatestTT := planetOccultationMinimizeExternalMetric(leftTT, rightTT, config, observer) + greatestTT := planetOccultationMinimizeExternalMetric(leftTT, rightTT, config, observer, inst) if greatestTT >= startTT && greatestTT <= endTT && - planetOccultationLatitudePass(greatestTT, config, observer, safetyMarginArcsec) { + planetOccultationLatitudePass(greatestTT, config, observer, safetyMarginArcsec, inst) { if len(results) == 0 || math.Abs(greatestTT-results[len(results)-1]) > 60.0/86400.0 { results = append(results, greatestTT) } @@ -239,34 +236,38 @@ func planetOccultationCandidateGreatestTimes( return results } -func planetOccultationMinimizeExternalMetric(left, right float64, config planetOccultationConfig, observer *Observer) float64 { +func planetOccultationMinimizeExternalMetric(left, right float64, config planetOccultationConfig, observer *Observer, instances ...*Instance) float64 { + inst := instanceValue(instances) + if right <= left { return left } const goldenRatio = 0.6180339887498949 x1 := right - goldenRatio*(right-left) x2 := left + goldenRatio*(right-left) - f1 := planetOccultationExternalContactMetric(x1, config, observer, -1) - f2 := planetOccultationExternalContactMetric(x2, config, observer, -1) + f1 := planetOccultationExternalContactMetric(x1, config, observer, -1, inst) + f2 := planetOccultationExternalContactMetric(x2, config, observer, -1, inst) for i := 0; i < 64 && right-left > planetOccultationRootToleranceDays; i++ { if f1 > f2 { left = x1 x1, f1 = x2, f2 x2 = left + goldenRatio*(right-left) - f2 = planetOccultationExternalContactMetric(x2, config, observer, -1) + f2 = planetOccultationExternalContactMetric(x2, config, observer, -1, inst) } else { right = x2 x2, f2 = x1, f1 x1 = right - goldenRatio*(right-left) - f1 = planetOccultationExternalContactMetric(x1, config, observer, -1) + f1 = planetOccultationExternalContactMetric(x1, config, observer, -1, inst) } } return (left + right) / 2 } -func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planetOccultationConfig) (float64, Observer, float64, bool) { +func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planetOccultationConfig, instances ...*Instance) (float64, Observer, float64, bool) { + inst := instanceValue(instances) + frameAt := func(tt float64) (occultationPathFrame, bool) { - return planetOccultationPathFrameAt(tt, config) + return planetOccultationPathFrameAt(tt, config, inst) } searchStart := seedTT - occultationPathSearchSpanDays searchEnd := seedTT + occultationPathSearchSpanDays @@ -288,14 +289,14 @@ func planetOccultationBestObserver(seedTT, startTT, endTT float64, config planet // outside the occultation, so it remains the last resort only. point, pointOK = occultationPathTrackPointForFrame(greatestTT, frameAt, time.UTC) if !pointOK { - point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC) + point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC, inst) } } if !pointOK { return 0, Observer{}, 0, false } observer := Observer{Longitude: point.Longitude, Latitude: point.Latitude} - state := planetOccultationStateAt(greatestTT, config, &observer, -1) + state := planetOccultationStateAt(greatestTT, config, &observer, -1, inst) if !state.valid { return 0, Observer{}, 0, false } @@ -307,14 +308,32 @@ func planetOccultationInfoAtGreatest( config planetOccultationConfig, observer Observer, safetyMarginArcsec float64, - location *time.Location, -) (PlanetOccultationInfo, bool) { - if !planetOccultationLatitudePass(greatestTT, config, &observer, safetyMarginArcsec) { + location *time.Location, instances ...*Instance) (PlanetOccultationInfo, bool) { + inst := instanceValue(instances) + + info, state, ok := planetOccultationGreatestInfo(greatestTT, config, observer, safetyMarginArcsec, location, inst) + if !ok || state.externalContactMetric > 0 { return PlanetOccultationInfo{}, false } - state := planetOccultationStateAt(greatestTT, config, &observer, -1) - if !state.valid || state.externalContactMetric > 0 { - return PlanetOccultationInfo{}, false + + if math.Abs(state.externalContactMetric) <= planetOccultationGrazingToleranceArcsec { + info.Type = OccultationGrazing + info.ExternalImmersion = info.Greatest + info.ExternalEmersion = info.Greatest + info.ContactsComplete = true + return info, true + } + return planetOccultationCircularContacts(info, state, greatestTT, config, observer, location, inst) +} + +// planetOccultationGreatestInfo 只填掩甚几何,不判断圆月面是否成掩、不求接触。 +func planetOccultationGreatestInfo(greatestTT float64, config planetOccultationConfig, observer Observer, safetyMarginArcsec float64, location *time.Location, inst *Instance) (PlanetOccultationInfo, planetOccultationState, bool) { + if !planetOccultationLatitudePass(greatestTT, config, &observer, safetyMarginArcsec, inst) { + return PlanetOccultationInfo{}, planetOccultationState{}, false + } + state := planetOccultationStateAt(greatestTT, config, &observer, -1, inst) + if !state.valid { + return PlanetOccultationInfo{}, planetOccultationState{}, false } info := PlanetOccultationInfo{ Planet: config.planet, @@ -330,16 +349,11 @@ func planetOccultationInfoAtGreatest( MoonAzimuthAtGreatest: occultationAzimuth(greatestTT, observer, state.position.moonRA, state.position.moonDec), } info.VisibleAtGreatest = info.MoonAltitudeAtGreatest >= 0 + return info, state, true +} - if math.Abs(state.externalContactMetric) <= planetOccultationGrazingToleranceArcsec { - info.Type = OccultationGrazing - info.ExternalImmersion = info.Greatest - info.ExternalEmersion = info.Greatest - info.ContactsComplete = true - return info, true - } - - contactEvaluator := newPlanetOccultationContactEvaluator(config, observer) +func planetOccultationCircularContacts(info PlanetOccultationInfo, state planetOccultationState, greatestTT float64, config planetOccultationConfig, observer Observer, location *time.Location, inst *Instance) (PlanetOccultationInfo, bool) { + contactEvaluator := newPlanetOccultationContactEvaluator(config, observer, inst) contactEvaluator.prime(greatestTT, state.movingDiskContactState(), state.valid) externalImmersionTT, externalImmersionOK := planetOccultationContactWithEvaluator(greatestTT, -1, false, contactEvaluator) externalEmersionTT, externalEmersionOK := planetOccultationContactWithEvaluator(greatestTT, 1, false, contactEvaluator) @@ -366,12 +380,14 @@ func planetOccultationInfoAtGreatest( return info, true } -func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer *Observer, n int) planetMoonPosition { - moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, n) +func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) planetMoonPosition { + inst := instanceValue(instances) + + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, n, inst) planetRA, planetDec := config.apparentRaDecN(tt, n) if observer != nil { ut := TT2UTC(tt) - moonDistanceAU := HMoonAwayN(tt, n) / angularDiameterAstronomicalUnitKM + moonDistanceAU := HMoonAwayNWithInstance(tt, n, inst) / angularDiameterAstronomicalUnitKM planetDistanceAU := config.earthDistanceN(tt, n) moonRA, moonDec = TopocentricRaDec(moonRA, moonDec, observer.Latitude, observer.Longitude, ut, moonDistanceAU, observer.Height) planetRA, planetDec = TopocentricRaDec(planetRA, planetDec, observer.Latitude, observer.Longitude, ut, planetDistanceAU, observer.Height) @@ -387,12 +403,14 @@ func planetMoonPositionAt(tt float64, config planetOccultationConfig, observer * } } -func planetOccultationStateAt(tt float64, config planetOccultationConfig, observer *Observer, n int) planetOccultationState { - position := planetMoonPositionAt(tt, config, observer, n) - moonRadius := MoonSemidiameterN(tt, n) +func planetOccultationStateAt(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) planetOccultationState { + inst := instanceValue(instances) + + position := planetMoonPositionAt(tt, config, observer, n, inst) + moonRadius := MoonSemidiameterNWithInstance(tt, n, inst) planetRadius := config.semidiameterN(tt, n) if observer != nil { - moonRadius = moonTopocentricSemidiameterN(tt, *observer, n) + moonRadius = moonTopocentricSemidiameterN(tt, *observer, n, inst) planetRadius = planetTopocentricSemidiameterN(tt, config, *observer, n) } if !position.valid || !finite(moonRadius) || !finite(planetRadius) || moonRadius <= planetRadius || planetRadius <= 0 { @@ -432,16 +450,20 @@ func planetTopocentricSemidiameterN(tt float64, config planetOccultationConfig, return angularSemidiameterArcsec(config.equatorialRadiusKM, distanceKM) } -func planetOccultationExternalContactMetric(tt float64, config planetOccultationConfig, observer *Observer, n int) float64 { - state := planetOccultationStateAt(tt, config, observer, n) +func planetOccultationExternalContactMetric(tt float64, config planetOccultationConfig, observer *Observer, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + state := planetOccultationStateAt(tt, config, observer, n, inst) if !state.valid { return math.Inf(1) } return state.externalContactMetric } -func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, observer *Observer, safetyMarginArcsec float64) bool { - state := planetOccultationStateAt(tt, config, observer, -1) +func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, observer *Observer, safetyMarginArcsec float64, instances ...*Instance) bool { + inst := instanceValue(instances) + + state := planetOccultationStateAt(tt, config, observer, -1, inst) if !state.valid { return false } @@ -453,10 +475,11 @@ func planetOccultationLatitudePass(tt float64, config planetOccultationConfig, o func newPlanetOccultationContactEvaluator( config planetOccultationConfig, - observer Observer, -) *movingDiskContactEvaluator { + observer Observer, instances ...*Instance) *movingDiskContactEvaluator { + inst := instanceValue(instances) + return newMovingDiskContactEvaluator(func(tt float64) (movingDiskContactState, bool) { - state := planetOccultationStateAt(tt, config, &observer, -1) + state := planetOccultationStateAt(tt, config, &observer, -1, inst) return state.movingDiskContactState(), state.valid }) } diff --git a/basic/occultation_planet_diagram.go b/basic/occultation_planet_diagram.go index 5d250d7..bac8ab8 100644 --- a/basic/occultation_planet_diagram.go +++ b/basic/occultation_planet_diagram.go @@ -72,28 +72,26 @@ type planetOccultationDiagramTime struct { labels []string } -// PlanetOccultationDiagram 为已求解的固定地点行星月掩计算以月心为原点的切平面轨迹。事件数据无效或不完整时,结果不含帧。 -// PlanetOccultationDiagram computes a Moon-centered tangent-plane track for an already solved fixed-site planetary occultation. Invalid or incomplete event data produces a result without frames. -func PlanetOccultationDiagram( +// PlanetOccultationDiagramWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func PlanetOccultationDiagramWithInstance( info PlanetOccultationInfo, - options PlanetOccultationDiagramOptions, -) PlanetOccultationDiagramResult { + options PlanetOccultationDiagramOptions, inst *Instance) PlanetOccultationDiagramResult { options = normalizePlanetOccultationDiagramOptions(options) result := PlanetOccultationDiagramResult{Occultation: info, StepDays: options.StepDays} if !planetOccultationDiagramInputValid(info) { return result } - config, ok := planetOccultationConfigFor(info.Planet) + config, ok := planetOccultationConfigFor(info.Planet, inst) if !ok { return result } startTT := occultationTimeToTT(info.ExternalImmersion) greatestTT := occultationTimeToTT(info.Greatest) endTT := occultationTimeToTT(info.ExternalEmersion) - externalImmersionFrame, externalImmersionOK := planetOccultationDiagramFrameAt(startTT, config, info.Observer) - greatestFrame, greatestOK := planetOccultationDiagramFrameAt(greatestTT, config, info.Observer) - externalEmersionFrame, externalEmersionOK := planetOccultationDiagramFrameAt(endTT, config, info.Observer) + externalImmersionFrame, externalImmersionOK := planetOccultationDiagramFrameAt(startTT, config, info.Observer, inst) + greatestFrame, greatestOK := planetOccultationDiagramFrameAt(greatestTT, config, info.Observer, inst) + externalEmersionFrame, externalEmersionOK := planetOccultationDiagramFrameAt(endTT, config, info.Observer, inst) if !externalImmersionOK || !greatestOK || !externalEmersionOK { return result } @@ -103,10 +101,10 @@ func PlanetOccultationDiagram( ) if info.HasInternalContacts { internalImmersionFrame, internalImmersionOK = planetOccultationDiagramFrameAt( - occultationTimeToTT(info.InternalImmersion), config, info.Observer, + occultationTimeToTT(info.InternalImmersion), config, info.Observer, inst, ) internalEmersionFrame, internalEmersionOK = planetOccultationDiagramFrameAt( - occultationTimeToTT(info.InternalEmersion), config, info.Observer, + occultationTimeToTT(info.InternalEmersion), config, info.Observer, inst, ) } if (info.HasInternalContacts && (!internalImmersionOK || !internalEmersionOK)) || @@ -120,7 +118,7 @@ func PlanetOccultationDiagram( result.StepDays = stepDays result.Frames = make([]PlanetOccultationDiagramFrame, 0, len(times)) for _, item := range times { - frame, frameOK := planetOccultationDiagramFrameAt(item.jde, config, info.Observer) + frame, frameOK := planetOccultationDiagramFrameAt(item.jde, config, info.Observer, inst) if !frameOK { return PlanetOccultationDiagramResult{Occultation: info, StepDays: stepDays} } @@ -142,8 +140,10 @@ func planetOccultationDiagramMatchesInfo( !finite(info.PlanetSemidiameterArcsec) || info.PlanetSemidiameterArcsec <= 0 { return false } - if math.Abs(externalImmersion.SeparationArcsec-externalImmersion.MoonRadiusArcsec-externalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec || - math.Abs(externalEmersion.SeparationArcsec-externalEmersion.MoonRadiusArcsec-externalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec { + // 已按月缘剖面收敛的接触不在圆月面上,圆月面接触判据只适用于未修正的结果。 + checkContacts := !info.LunarLimbApplied + if checkContacts && (math.Abs(externalImmersion.SeparationArcsec-externalImmersion.MoonRadiusArcsec-externalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec || + math.Abs(externalEmersion.SeparationArcsec-externalEmersion.MoonRadiusArcsec-externalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec) { return false } if math.Abs(greatest.SeparationArcsec-info.MinimumSeparationArcsec) > planetOccultationDiagramGeometryArcsec || @@ -153,6 +153,10 @@ func planetOccultationDiagramMatchesInfo( return false } + // 月缘结果的食型按剖面判定,圆月面掩甚间隙不再适用。 + if info.LunarLimbApplied { + return info.Type == OccultationPartial || info.Type == OccultationTotal || info.Type == OccultationGrazing + } externalMetric := greatest.SeparationArcsec - greatest.MoonRadiusArcsec - greatest.PlanetRadiusArcsec internalMetric := greatest.SeparationArcsec - greatest.MoonRadiusArcsec + greatest.PlanetRadiusArcsec switch info.Type { @@ -164,8 +168,8 @@ func planetOccultationDiagramMatchesInfo( math.Abs(startTT-greatestTT) <= planetOccultationDiagramContactTimeDays && math.Abs(endTT-greatestTT) <= planetOccultationDiagramContactTimeDays case OccultationTotal: - if math.Abs(internalImmersion.SeparationArcsec-internalImmersion.MoonRadiusArcsec+internalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec || - math.Abs(internalEmersion.SeparationArcsec-internalEmersion.MoonRadiusArcsec+internalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec { + if checkContacts && (math.Abs(internalImmersion.SeparationArcsec-internalImmersion.MoonRadiusArcsec+internalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec || + math.Abs(internalEmersion.SeparationArcsec-internalEmersion.MoonRadiusArcsec+internalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec) { return false } return externalMetric < -planetOccultationGrazingToleranceArcsec && @@ -291,9 +295,10 @@ func planetOccultationDiagramLabelPriority(labels []string) int { func planetOccultationDiagramFrameAt( tt float64, config planetOccultationConfig, - observer Observer, -) (PlanetOccultationDiagramFrame, bool) { - state := planetOccultationStateAt(tt, config, &observer, -1) + observer Observer, instances ...*Instance) (PlanetOccultationDiagramFrame, bool) { + inst := instanceValue(instances) + + state := planetOccultationStateAt(tt, config, &observer, -1, inst) if !state.valid { return PlanetOccultationDiagramFrame{}, false } diff --git a/basic/occultation_planet_footprint.go b/basic/occultation_planet_footprint.go index f29826d..2e51f9d 100644 --- a/basic/occultation_planet_footprint.go +++ b/basic/occultation_planet_footprint.go @@ -4,6 +4,8 @@ import ( "math" "sort" "time" + + "b612.me/astro/internal/geodata" ) const ( @@ -730,9 +732,9 @@ func planetOccultationHorizonArc( from, to := segment[len(segment)-1], segment[0] unitAt := func(point OccultationPathPoint) occultationPathVector { fixed := occultationStationSurfaceVector(point.Longitude, point.Latitude) - return occultationPathVector{x: fixed.x / occultationPathEarthEquatorialRadiusKM, - y: fixed.y / occultationPathEarthEquatorialRadiusKM, - z: fixed.z / (occultationPathEarthEquatorialRadiusKM * occultationPathEarthPolarRatio)} + return occultationPathVector{x: fixed.x / geodata.EarthEquatorialRadiusKM, + y: fixed.y / geodata.EarthEquatorialRadiusKM, + z: fixed.z / (geodata.EarthEquatorialRadiusKM * occultationPathEarthPolarRatio)} } moonFixed := occultationPathEarthFixedVector(tt, frame.moon) normal := occultationPathUnit(occultationPathVector{x: moonFixed.x, y: moonFixed.y, z: moonFixed.z / occultationPathEarthPolarRatio}) @@ -772,7 +774,7 @@ func planetOccultationHorizonArc( unit := occultationPathAdd(occultationPathScale(normal, centerDistance), occultationPathScale(occultationPathAdd(occultationPathScale(first, math.Cos(angle)), occultationPathScale(second, math.Sin(angle))), radius)) - fixed := occultationPathScale(unit, occultationPathEarthEquatorialRadiusKM) + fixed := occultationPathScale(unit, geodata.EarthEquatorialRadiusKM) fixed.z *= occultationPathEarthPolarRatio middle := from middle.Longitude, middle.Latitude = occultationStationGeodetic(fixed) @@ -822,13 +824,13 @@ func planetOccultationCenterCap( if math.Abs(cosine) < 1e-6 { cosine = 1e-6 } - latitudeOffset := radiusKM / occultationPathEarthEquatorialRadiusKM * degreesPerRadian + latitudeOffset := radiusKM / geodata.EarthEquatorialRadiusKM * degreesPerRadian longitudeOffset := latitudeOffset / cosine cap := make([]OccultationPathPoint, pointCount+1) for index := 0; index < pointCount; index++ { angle := 2 * math.Pi * float64(index) / float64(pointCount) cap[index] = center - cap[index].Longitude = normalizeLongitude(center.Longitude + longitudeOffset*math.Cos(angle)) + cap[index].Longitude = geodata.NormalizeLongitude(center.Longitude + longitudeOffset*math.Cos(angle)) cap[index].Latitude = math.Max(-90, math.Min(90, center.Latitude+latitudeOffset*math.Sin(angle))) cap[index].MoonAltitude = center.MoonAltitude cap[index].WidthKM = 0 @@ -872,8 +874,8 @@ func planetOccultationPointSegmentDistanceKM( point, start, end OccultationPathPoint, ) float64 { latitude := point.Latitude * math.Pi / 180 - scaleX := math.Cos(latitude) * occultationPathEarthEquatorialRadiusKM * math.Pi / 180 - scaleY := occultationPathEarthEquatorialRadiusKM * math.Pi / 180 + scaleX := math.Cos(latitude) * geodata.EarthEquatorialRadiusKM * math.Pi / 180 + scaleY := geodata.EarthEquatorialRadiusKM * math.Pi / 180 x := func(value OccultationPathPoint) float64 { return math.Remainder(value.Longitude-point.Longitude, 360) * scaleX } @@ -934,7 +936,7 @@ func planetOccultationHorizonCircle( if count < 3 { return nil } - equatorialRadius := occultationPathEarthEquatorialRadiusKM + equatorialRadius := geodata.EarthEquatorialRadiusKM polarRadius := equatorialRadius * occultationPathEarthPolarRatio scaledMoon := occultationPathVector{ x: frame.moon.x / equatorialRadius, diff --git a/basic/occultation_planet_path.go b/basic/occultation_planet_path.go index c68a08e..d3d247a 100644 --- a/basic/occultation_planet_path.go +++ b/basic/occultation_planet_path.go @@ -4,6 +4,8 @@ import ( "math" "sort" "time" + + "b612.me/astro/internal/geodata" ) const ( @@ -30,6 +32,8 @@ type planetOccultationFrameCacheEntry struct { } type planetOccultationEventCache struct { + inst *Instance + config planetOccultationConfig states map[uint64]planetOccultationEphemerisState outerFrames map[uint64]planetOccultationFrameCacheEntry @@ -39,8 +43,10 @@ type planetOccultationEventCache struct { local *planetOccultationLocalEphemeris } -func newPlanetOccultationEventCache(config planetOccultationConfig) *planetOccultationEventCache { - cache := &planetOccultationEventCache{ +func newPlanetOccultationEventCache(config planetOccultationConfig, instances ...*Instance) *planetOccultationEventCache { + inst := instanceValue(instances) + + cache := &planetOccultationEventCache{inst: inst, config: config, states: make(map[uint64]planetOccultationEphemerisState), outerFrames: make(map[uint64]planetOccultationFrameCacheEntry), @@ -57,12 +63,9 @@ func newPlanetOccultationEventCache(config planetOccultationConfig) *planetOccul return cache } -// FindPlanetOccultationPaths 搜索有限盘面行星月掩的全球外接触和内接触掩带。 -// 查询窗口按全球几何掩甚点选择事件;端点容差 10 ms 与数值根精度一致。求解成功时,每条路径扩展到完整全球起止点。 -// FindPlanetOccultationPaths searches the global outer- and inner-contact footprints of one finite-disk planet. -// The query window selects events by global geometric greatest, with a 10 ms endpoint tolerance matching the numerical root precision. Each returned path expands to its complete global start and end when solved. -func FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, - options OccultationPathOptions) ([]PlanetOccultationPath, error) { +// FindPlanetOccultationPathsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindPlanetOccultationPathsWithInstance(start, end time.Time, planet OccultationPlanet, + options OccultationPathOptions, inst *Instance) ([]PlanetOccultationPath, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -73,18 +76,18 @@ func FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, return nil, err } - config, _ := planetOccultationConfigFor(planet) + config, _ := planetOccultationConfigFor(planet, inst) options = normalizeOccultationPathOptions(options) startTT := occultationTimeToTT(start) endTT := occultationTimeToTT(end) candidateStartTT := startTT - occultationPathSearchSpanDays candidateEndTT := endTT + occultationPathSearchSpanDays candidates := planetOccultationCandidateGreatestTimes( - candidateStartTT, candidateEndTT, planetOccultationDefaultStepDays, config, nil, 0, + candidateStartTT, candidateEndTT, planetOccultationDefaultStepDays, config, nil, 0, inst, ) paths := make([]PlanetOccultationPath, 0, len(candidates)) for _, seedTT := range candidates { - path, ok, err := planetOccultationPathAtSeed(seedTT, config, options, start, end) + path, ok, err := planetOccultationPathAtSeed(seedTT, config, options, start, end, inst) if err != nil { return nil, err } @@ -106,10 +109,11 @@ func planetOccultationPathAtSeed( seedTT float64, config planetOccultationConfig, options OccultationPathOptions, - selectionStart, selectionEnd time.Time, -) (PlanetOccultationPath, bool, error) { + selectionStart, selectionEnd time.Time, instances ...*Instance) (PlanetOccultationPath, bool, error) { + inst := instanceValue(instances) + location := selectionStart.Location() - cache := newPlanetOccultationEventCache(config) + cache := newPlanetOccultationEventCache(config, inst) cache.prepareLocalEphemeris(seedTT) frameAt := cache.outerFrameAt totalFrameAt := cache.totalFrameAt @@ -182,14 +186,14 @@ func planetOccultationPathAtSeed( return PlanetOccultationPath{}, false, ErrOccultationPathSamplingLimit } - start := occultationPathBoundaryEndpointForFrame(outerStart, frameAt, location, 1) - end := occultationPathBoundaryEndpointForFrame(outerEnd, frameAt, location, -1) + start := occultationPathBoundaryEndpointForFrame(outerStart, frameAt, location, 1, inst) + end := occultationPathBoundaryEndpointForFrame(outerEnd, frameAt, location, -1, inst) if !start.valid || !end.valid { return PlanetOccultationPath{}, false, nil } exactFrameAt, exactTotalFrameAt := frameAt, totalFrameAt if options.Algorithm != OccultationPathAlgorithmExact { - optimized := newPlanetOccultationEventCache(config) + optimized := newPlanetOccultationEventCache(config, inst) optimized.preparePathEphemeris(seedTT, options.Algorithm) if optimized.local.dense { cache = optimized @@ -197,7 +201,7 @@ func planetOccultationPathAtSeed( } } centerLine, northern, southern, err := planetOccultationPathSamples( - outerStart, outerEnd, centerStart, centerEnd, hasCenter, greatestTT, frameAt, options, location, + outerStart, outerEnd, centerStart, centerEnd, hasCenter, greatestTT, frameAt, exactFrameAt, options, location, ) if err != nil { return PlanetOccultationPath{}, false, err @@ -228,6 +232,10 @@ func planetOccultationPathAtSeed( path.SouthernLimit = occultationStationCorrectLimitSeries( path.SouthernLimit, frameAt, cache.riseSetContextAt, false, location, ) + var northApplied, southApplied bool + path.NorthernLimit, northApplied = occultationLimbCorrectLimitSeries(path.NorthernLimit, cache.riseSetContextAt, options.LunarLimb, location, inst) + path.SouthernLimit, southApplied = occultationLimbCorrectLimitSeries(path.SouthernLimit, cache.riseSetContextAt, options.LunarLimb, location, inst) + path.LunarLimbApplied = northApplied || southApplied path.GreatestLimitSeparationKM, _ = occultationPathLimitSeparations( path.NorthernLimit, path.SouthernLimit, greatestTT, ) @@ -282,8 +290,8 @@ func planetOccultationPathAtSeed( if !hasTotal { return path, true, nil } - totalStart := occultationPathBoundaryEndpointForFrame(totalStartTT, exactTotalFrameAt, location, 1) - totalEnd := occultationPathBoundaryEndpointForFrame(totalEndTT, exactTotalFrameAt, location, -1) + totalStart := occultationPathBoundaryEndpointForFrame(totalStartTT, exactTotalFrameAt, location, 1, inst) + totalEnd := occultationPathBoundaryEndpointForFrame(totalEndTT, exactTotalFrameAt, location, -1, inst) _, _, totalWidth, totalWidthOK := occultationPathLimitsAndWidthForFrame(greatestTT, exactTotalFrameAt) if !totalStart.valid || !totalEnd.valid || !totalWidthOK || totalWidth <= 0 { return path, true, nil @@ -464,14 +472,14 @@ func planetOccultationPathSamples( centerStartTT, centerEndTT float64, hasCenter bool, greatestTT float64, - frameAt occultationPathFrameFunc, + frameAt, exactFrameAt occultationPathFrameFunc, options OccultationPathOptions, location *time.Location, ) ([]OccultationPathPoint, []OccultationPathPoint, []OccultationPathPoint, error) { var centerLine []OccultationPathPoint if hasCenter { var err error - centerLine, err = occultationPathCenterSamplesForFrame(centerStartTT, centerEndTT, greatestTT, frameAt, options, location) + centerLine, err = occultationPathCenterSamplesForFrame(centerStartTT, centerEndTT, greatestTT, frameAt, exactFrameAt, options, location) if err != nil { return nil, nil, nil, err } @@ -613,11 +621,6 @@ func appendOccultationPathBoundaryPairSegment( ) } -const ( - occultationPathWebMercatorRadiusKM = 6378.1366 - occultationPathWebMercatorMaxLat = 85.05112878 -) - func occultationPathBoundaryPairWebMercatorSpacing( first, second occultationPathBoundaryPairSample, ) float64 { @@ -656,13 +659,10 @@ func occultationPathWebMercatorPointSpacing( ) float64 { firstLongitude, firstLatitude := occultationPathGeodetic(firstTT, firstVector) secondLongitude, secondLatitude := occultationPathGeodetic(secondTT, secondVector) - firstLatitude = math.Max(-occultationPathWebMercatorMaxLat, math.Min(occultationPathWebMercatorMaxLat, firstLatitude)) - secondLatitude = math.Max(-occultationPathWebMercatorMaxLat, math.Min(occultationPathWebMercatorMaxLat, secondLatitude)) - longitudeDelta := math.Remainder(secondLongitude-firstLongitude, 360) * math.Pi / 180 - firstY := math.Log(math.Tan(math.Pi/4 + firstLatitude*math.Pi/360)) - secondY := math.Log(math.Tan(math.Pi/4 + secondLatitude*math.Pi/360)) - latitudeDelta := secondY - firstY - return occultationPathWebMercatorRadiusKM * math.Hypot(longitudeDelta, latitudeDelta) + return geodata.WebMercatorDistanceKM( + geodata.GeoPoint{Longitude: firstLongitude, Latitude: firstLatitude}, + geodata.GeoPoint{Longitude: secondLongitude, Latitude: secondLatitude}, + ) } func occultationPathContourBoundaryPairAt( @@ -1074,7 +1074,7 @@ func occultationPathOrientBoundarySamples( func occultationPathCenterSamplesForFrame( startTT, endTT, greatestTT float64, - frameAt occultationPathFrameFunc, + frameAt, exactFrameAt occultationPathFrameFunc, options OccultationPathOptions, location *time.Location, ) ([]OccultationPathPoint, error) { @@ -1083,6 +1083,10 @@ func occultationPathCenterSamplesForFrame( points := make([]OccultationPathPoint, 0, len(times)) for _, tt := range times { point, ok := occultationPathCenterPointForFrame(tt, frameAt, location) + // 中心窗口端点由精确帧求得,插值帧在端点附近可能判无交点,此时回退精确帧。 + if !ok && exactFrameAt != nil { + point, ok = occultationPathCenterPointForFrame(tt, exactFrameAt, location) + } if ok && (len(points) == 0 || point.Time.After(points[len(points)-1].Time)) { points = append(points, point) } @@ -1156,23 +1160,29 @@ func appendOccultationPathSegmentForFrame( return appendOccultationPathSegmentForFrame(points, mid, end, frameAt, targetSpacingKM, location, depth+1) } -func planetOccultationPathFrameAt(tt float64, config planetOccultationConfig) (occultationPathFrame, bool) { - return planetOccultationContactPathFrameAt(tt, config, false) +func planetOccultationPathFrameAt(tt float64, config planetOccultationConfig, instances ...*Instance) (occultationPathFrame, bool) { + inst := instanceValue(instances) + + return planetOccultationContactPathFrameAt(tt, config, false, inst) } func planetOccultationTotalPathFrameAt(tt float64, config planetOccultationConfig) (occultationPathFrame, bool) { return planetOccultationContactPathFrameAt(tt, config, true) } -func planetOccultationContactPathFrameAt(tt float64, config planetOccultationConfig, total bool) (occultationPathFrame, bool) { - state := planetOccultationEphemerisStateAt(tt, config) +func planetOccultationContactPathFrameAt(tt float64, config planetOccultationConfig, total bool, instances ...*Instance) (occultationPathFrame, bool) { + inst := instanceValue(instances) + + state := planetOccultationEphemerisStateAt(tt, config, inst) return planetOccultationContactPathFrameFromState(state, config, total) } -func planetOccultationEphemerisStateAt(tt float64, config planetOccultationConfig) planetOccultationEphemerisState { - moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, -1) - moonDistance := HMoonAwayN(tt, -1) - planetRA, planetDec, planetDistanceAU := planetOccultationApparentPositionAndDistanceN(tt, config, -1) +func planetOccultationEphemerisStateAt(tt float64, config planetOccultationConfig, instances ...*Instance) planetOccultationEphemerisState { + inst := instanceValue(instances) + + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + moonDistance := HMoonAwayNWithInstance(tt, -1, inst) + planetRA, planetDec, planetDistanceAU := planetOccultationApparentPositionAndDistanceN(tt, config, -1, inst) planetDistance := planetDistanceAU * occultationPathAstronomicalUnitKM return planetOccultationEphemerisState{ moonRA: moonRA, moonDec: moonDec, moonDistanceKM: moonDistance, @@ -1185,9 +1195,10 @@ func planetOccultationEphemerisStateAt(tt float64, config planetOccultationConfi func planetOccultationApparentPositionAndDistanceN( tt float64, config planetOccultationConfig, - n int, -) (float64, float64, float64) { - position, _, distanceAU := planetApparentGeocentricPositionAndDistanceN(config.planetIndex, tt, n) + n int, instances ...*Instance) (float64, float64, float64) { + inst := instanceValue(instances) + + position, _, distanceAU := planetApparentGeocentricPositionAndDistanceN(config.planetIndex, tt, n, inst) ra, dec := planetApparentRaDecFromLoBo(tt, position.lo, position.bo) return ra, dec, distanceAU } @@ -1253,7 +1264,7 @@ func (cache *planetOccultationEventCache) stateAt(tt float64) planetOccultationE state, interpolated = cache.local.stateAt(tt) } if !interpolated { - state = planetOccultationEphemerisStateAt(tt, cache.config) + state = planetOccultationEphemerisStateAt(tt, cache.config, cache.inst) } cache.states[key] = state return state @@ -1261,7 +1272,7 @@ func (cache *planetOccultationEventCache) stateAt(tt float64) planetOccultationE func (cache *planetOccultationEventCache) prepareLocalEphemeris(center float64) { if cache.local == nil { - cache.local = newPlanetOccultationLocalEphemeris(center, cache.config) + cache.local = newPlanetOccultationLocalEphemeris(center, cache.config, cache.inst) } } @@ -1353,8 +1364,9 @@ func occultationPathBoundaryEndpointForFrame( tt float64, frameAt occultationPathFrameFunc, location *time.Location, - direction int, -) occultationPathEndpoint { + direction int, instances ...*Instance) occultationPathEndpoint { + inst := instanceValue(instances) + if _, ok := frameAt(tt); !ok { return occultationPathEndpoint{} } @@ -1366,7 +1378,7 @@ func occultationPathBoundaryEndpointForFrame( } vector, _, valid := occultationPathBoundaryTangent(frame) if valid { - return occultationPathEndpoint{point: occultationPathPointFromVector(candidateTT, vector, 0, location), valid: true} + return occultationPathEndpoint{point: occultationPathPointFromVector(candidateTT, vector, 0, location, inst), valid: true} } } return occultationPathEndpoint{} @@ -1411,8 +1423,9 @@ func occultationPathCenterPointForFrameWithoutWidth( func occultationPathBoundaryPointForFrame( tt float64, frameAt occultationPathFrameFunc, - location *time.Location, -) (OccultationPathPoint, bool) { + location *time.Location, instances ...*Instance) (OccultationPathPoint, bool) { + inst := instanceValue(instances) + frame, ok := frameAt(tt) if !ok { return OccultationPathPoint{}, false @@ -1422,7 +1435,7 @@ func occultationPathBoundaryPointForFrame( return OccultationPathPoint{}, false } north, south, limitsOK := occultationPathScannedLimitsAtFrame(tt, frame) - return occultationPathPointFromVector(tt, point, occultationPathBoundaryWidth(north, south, limitsOK), location), true + return occultationPathPointFromVector(tt, point, occultationPathBoundaryWidth(north, south, limitsOK), location, inst), true } func occultationPathTrackPointForFrame( diff --git a/basic/occultation_planet_path_test.go b/basic/occultation_planet_path_test.go index b949b27..b505e5f 100644 --- a/basic/occultation_planet_path_test.go +++ b/basic/occultation_planet_path_test.go @@ -5,6 +5,8 @@ import ( "strings" "testing" "time" + + "b612.me/astro/internal/geodata" ) func TestPlanetOccultationCombinedPositionMatchesSeparateEphemerides(t *testing.T) { @@ -490,7 +492,7 @@ func TestPlanetOccultationInnerConeUsesSignedTargetRadius(t *testing.T) { func TestOccultationPathBoundaryTangentFindsBetweenSamples(t *testing.T) { const boundaryRadiusKM = 1737.4 theta := math.Pi / float64(occultationPathBoundaryScanPoints) - offset := occultationPathEarthEquatorialRadiusKM + boundaryRadiusKM - 0.01 + offset := geodata.EarthEquatorialRadiusKM + boundaryRadiusKM - 0.01 moon := occultationPathVector{ x: 384000, y: -offset * math.Cos(theta), @@ -517,7 +519,7 @@ func TestOccultationPathBoundaryTangentFindsBetweenSamples(t *testing.T) { } polarRatioSquared := occultationPathEarthPolarRatio * occultationPathEarthPolarRatio ellipsoidResidual := point.x*point.x + point.y*point.y + point.z*point.z/polarRatioSquared - - occultationPathEarthEquatorialRadiusKM*occultationPathEarthEquatorialRadiusKM + geodata.EarthEquatorialRadiusKM*geodata.EarthEquatorialRadiusKM if math.Abs(ellipsoidResidual) > 1e-3 { t.Fatalf("tangent point ellipsoid residual = %.9f", ellipsoidResidual) } diff --git a/basic/occultation_planet_test.go b/basic/occultation_planet_test.go index 2926a38..8be9636 100644 --- a/basic/occultation_planet_test.go +++ b/basic/occultation_planet_test.go @@ -4,6 +4,8 @@ import ( "math" "testing" "time" + + "b612.me/astro/internal/geodata" ) func TestPlanetOccultationSupportsAllPlanetTargets(t *testing.T) { @@ -103,9 +105,9 @@ func TestPlanetOccultationUsesStationMoonDistanceForRadius(t *testing.T) { t.Fatal("Saturn occultation config is unavailable") } moonRA, _ := HMoonGeocentricApparentRaDecN(tt, -1) - subMoonLongitude := normalizeLongitude180(moonRA - ApparentSiderealTime(TT2UTC(tt))*15) + subMoonLongitude := geodata.NormalizeLongitude(moonRA - ApparentSiderealTime(TT2UTC(tt))*15) near := Observer{Longitude: subMoonLongitude, Latitude: 0} - far := Observer{Longitude: normalizeLongitude180(subMoonLongitude + 180), Latitude: 0} + far := Observer{Longitude: geodata.NormalizeLongitude(subMoonLongitude + 180), Latitude: 0} nearState := planetOccultationStateAt(tt, config, &near, -1) farState := planetOccultationStateAt(tt, config, &far, -1) if !nearState.valid || !farState.valid { @@ -128,9 +130,9 @@ func TestPlanetOccultationUsesStationPlanetDistanceForRadius(t *testing.T) { t.Fatal("Mercury occultation config is unavailable") } planetRA, _ := config.apparentRaDecN(tt, -1) - subPlanetLongitude := normalizeLongitude180(planetRA - ApparentSiderealTime(TT2UTC(tt))*15) + subPlanetLongitude := geodata.NormalizeLongitude(planetRA - ApparentSiderealTime(TT2UTC(tt))*15) near := Observer{Longitude: subPlanetLongitude, Latitude: 0} - far := Observer{Longitude: normalizeLongitude180(subPlanetLongitude + 180), Latitude: 0} + far := Observer{Longitude: geodata.NormalizeLongitude(subPlanetLongitude + 180), Latitude: 0} nearState := planetOccultationStateAt(tt, config, &near, -1) farState := planetOccultationStateAt(tt, config, &far, -1) if !nearState.valid || !farState.valid { diff --git a/basic/occultation_rise_set.go b/basic/occultation_rise_set.go index 22960bf..0ffca75 100644 --- a/basic/occultation_rise_set.go +++ b/basic/occultation_rise_set.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) const ( @@ -26,8 +28,6 @@ const ( occultationRiseSetEvaluationCacheMaximumEntries = 16384 occultationRiseSetTimeEpsilonDays = 1e-8 occultationRiseSetTargetSpacingKM = 100.0 - occultationTopocentricEarthRadiusKM = 6378.14 - occultationTopocentricEarthPolarRadiusKM = 6356.755 ) // 站心赤经赤纬的历史公式使用固定地平视差常数;保留该尺度可使向量算法与既有结果数值等价。 @@ -372,7 +372,7 @@ func (context occultationRiseSetContext) moonHorizonResidual(longitude, latitude sinTheta, cosTheta := math.Sincos(theta) moonDotZenith := cosLatitude*(context.moon.positionKM.x*cosTheta+context.moon.positionKM.y*sinTheta) + context.moon.positionKM.z*sinLatitude - polarRatio := occultationTopocentricEarthPolarRadiusKM / occultationTopocentricEarthRadiusKM + polarRatio := topocentricEarthPolarRadiusKM / topocentricEarthRadiusKM shapeDotZenith := math.Sqrt(cosLatitude*cosLatitude + polarRatio*polarRatio*sinLatitude*sinLatitude) residual := moonDotZenith - occultationLegacyParallaxRadiusKM*shapeDotZenith return residual, finite(residual) @@ -385,7 +385,7 @@ func occultationRiseSetObserverVectors( theta := (siderealDegrees + longitude) * rad sinLatitude, cosLatitude := math.Sincos(latitudeRad) sinTheta, cosTheta := math.Sincos(theta) - polarRatio := occultationTopocentricEarthPolarRadiusKM / occultationTopocentricEarthRadiusKM + polarRatio := topocentricEarthPolarRadiusKM / topocentricEarthRadiusKM u := math.Atan(polarRatio * math.Tan(latitudeRad)) sinU, cosU := math.Sincos(u) shape := occultationPathVector{ @@ -395,7 +395,7 @@ func occultationRiseSetObserverVectors( } zenith := occultationPathVector{x: cosLatitude * cosTheta, y: cosLatitude * sinTheta, z: sinLatitude} return occultationPathScale(shape, occultationLegacyParallaxRadiusKM), - occultationPathScale(shape, occultationTopocentricEarthRadiusKM), zenith + occultationPathScale(shape, topocentricEarthRadiusKM), zenith } func occultationRiseSetTopocentricDirection( @@ -636,7 +636,7 @@ func (evaluation occultationRiseSetEvaluation) pointsAt( return result } gst := ApparentSiderealTime(TT2UT1(evaluation.tt)) * 15 - centerLongitude := normalizeLongitude(evaluation.center.moonRA - gst) + centerLongitude := geodata.NormalizeLongitude(evaluation.center.moonRA - gst) centerLatitude := evaluation.center.moonDec appendRoots := func(greatest bool) { // Keep the historical geographic refinement for ordinary latitudes; it diff --git a/basic/occultation_rise_set_refine.go b/basic/occultation_rise_set_refine.go index 4b85811..01409e9 100644 --- a/basic/occultation_rise_set_refine.go +++ b/basic/occultation_rise_set_refine.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) func refineOccultationRiseSetFold( @@ -182,7 +184,7 @@ func occultationRiseSetFoldExtremumAt( } func occultationRiseSetHorizonCenter(context occultationRiseSetContext) (float64, float64) { - return normalizeLongitude(context.moonRA - context.siderealDegrees), context.moonDec + return geodata.NormalizeLongitude(context.moonRA - context.siderealDegrees), context.moonDec } func occultationRiseSetHorizonPoint( @@ -307,7 +309,7 @@ func occultationRiseSetCirclePointFromTangent(center, tangent [3]float64, radius center[1]*cosRadius + tangent[1]*sinRadius, center[2]*cosRadius + tangent[2]*sinRadius, } - return normalizeLongitude(math.Atan2(point[1], point[0]) / rad), + return geodata.NormalizeLongitude(math.Atan2(point[1], point[0]) / rad), math.Asin(math.Max(-1, math.Min(1, point[2]))) / rad } @@ -385,7 +387,7 @@ func refineOccultationRiseSetFoldPoint( for index := range coordinates { coordinates[index] += delta[index] } - coordinates[0] = normalizeLongitude(coordinates[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0]) if coordinates[1] <= -89.999999 || coordinates[1] >= 89.999999 { return OccultationPathPoint{}, false } @@ -477,7 +479,7 @@ func occultationRiseSetPhaseMidpoint( ); ok && occultationRiseSetRefinementPointIsContinuous(start, middle, end) { return middle, true } - longitude := normalizeLongitude(start.Longitude + math.Remainder(end.Longitude-start.Longitude, 360)/2) + longitude := geodata.NormalizeLongitude(start.Longitude + math.Remainder(end.Longitude-start.Longitude, 360)/2) latitude := (start.Latitude + end.Latitude) / 2 evaluation := cache.evaluation(tt) longitude, latitude, ok := riseSetRefineGeographicRoot(longitude, latitude, func(lon, lat float64) (float64, float64, bool) { diff --git a/basic/occultation_rise_set_topology.go b/basic/occultation_rise_set_topology.go index c4db6ea..89e6097 100644 --- a/basic/occultation_rise_set_topology.go +++ b/basic/occultation_rise_set_topology.go @@ -4,6 +4,8 @@ import ( "math" "sort" "time" + + "b612.me/astro/internal/geodata" ) func appendOccultationRiseSetSamples( @@ -485,7 +487,7 @@ func occultationRiseSetRecoverNarrowPhaseJunction( if !middleOK || !occultationRiseSetDirectionMatches(evaluation, middle, direction) { outside = OccultationPathPoint{ Time: occultationTTToLocation(middleTT, location), - Longitude: normalizeLongitude( + Longitude: geodata.NormalizeLongitude( inside.Longitude + math.Remainder(outside.Longitude-inside.Longitude, 360)/2, ), Latitude: (inside.Latitude + outside.Latitude) / 2, @@ -506,6 +508,24 @@ func occultationRiseSetRecoverNarrowPhaseJunction( if math.Abs(outsideContact) < math.Abs(insideContact) { secondary = outside } + // 时间区间收缩后两地平根仍差求根精度,沿短弦插值接触零点而不是取较优端点。 + if denominator := insideContact - outsideContact; denominator != 0 { + weight := insideContact / denominator + insideTT := occultationTimeToTT(inside.Time) + interpolatedTT := insideTT + (occultationTimeToTT(outside.Time)-insideTT)*weight + interpolated := OccultationPathPoint{ + Time: occultationTTToLocation(interpolatedTT, location), + Longitude: geodata.NormalizeLongitude( + inside.Longitude + math.Remainder(outside.Longitude-inside.Longitude, 360)*weight, + ), + Latitude: inside.Latitude + (outside.Latitude-inside.Latitude)*weight, + } + state := cache.evaluation(interpolatedTT).center.stateAt(interpolated.Longitude, interpolated.Latitude) + if state.valid && finite(state.contactMetric) && + math.Abs(state.contactMetric) < math.Min(math.Abs(insideContact), math.Abs(outsideContact)) { + secondary = interpolated + } + } secondaryTT := occultationTimeToTT(secondary.Time) secondaryEvaluation := cache.evaluation(secondaryTT) secondaryState := secondaryEvaluation.center.stateAt(secondary.Longitude, secondary.Latitude) @@ -624,7 +644,7 @@ func occultationRiseSetMidpoint(first, second OccultationPathPoint) OccultationP secondTT := occultationTimeToTT(second.Time) return OccultationPathPoint{ Time: occultationTTToLocation((firstTT+secondTT)/2, first.Time.Location()), - Longitude: normalizeLongitude( + Longitude: geodata.NormalizeLongitude( first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)/2, ), Latitude: (first.Latitude + second.Latitude) / 2, @@ -672,7 +692,7 @@ func completeOccultationRiseSetDirectionJunctions( riseEndpoint.point, setEndpoint.point, phase == RiseSetPhaseGreatest, location, cache, ) if !ok { - longitude := normalizeLongitude(riseEndpoint.point.Longitude + math.Remainder(setEndpoint.point.Longitude-riseEndpoint.point.Longitude, 360)/2) + longitude := geodata.NormalizeLongitude(riseEndpoint.point.Longitude + math.Remainder(setEndpoint.point.Longitude-riseEndpoint.point.Longitude, 360)/2) latitude := (riseEndpoint.point.Latitude + setEndpoint.point.Latitude) / 2 junction, ok = refineOccultationRiseSetDirectionJunction( (occultationTimeToTT(riseEndpoint.point.Time)+occultationTimeToTT(setEndpoint.point.Time))/2, @@ -919,7 +939,7 @@ func refineOccultationRiseSetPhaseJunction( return OccultationPathPoint{}, false } occultationRiseSetLimitJunctionDelta(&delta) - coordinates[0] = normalizeLongitude(coordinates[0] + delta[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0] + delta[0]) coordinates[1] += delta[1] coordinates[2] += delta[2] if coordinates[1] <= -89.999999 || coordinates[1] >= 89.999999 { @@ -1031,7 +1051,7 @@ func refineOccultationRiseSetDirectionJunction( return OccultationPathPoint{}, false } occultationRiseSetLimitJunctionDelta(&delta) - coordinates[0] = normalizeLongitude(coordinates[0] + delta[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0] + delta[0]) coordinates[1] += delta[1] coordinates[2] += delta[2] if coordinates[1] <= -89.999999 || coordinates[1] >= 89.999999 { diff --git a/basic/occultation_rise_set_tracking_test.go b/basic/occultation_rise_set_tracking_test.go index 999728e..c21b161 100644 --- a/basic/occultation_rise_set_tracking_test.go +++ b/basic/occultation_rise_set_tracking_test.go @@ -3,13 +3,15 @@ package basic import ( "testing" "time" + + "b612.me/astro/internal/geodata" ) func TestOccultationRiseSetNewRootKeepsExistingBranch(t *testing.T) { start := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) for _, offset := range []float64{0, 160, -200} { point := func(lon float64, at time.Time) OccultationPathPoint { - return OccultationPathPoint{Time: at, Longitude: normalizeLongitude(lon + offset), Latitude: 58} + return OccultationPathPoint{Time: at, Longitude: geodata.NormalizeLongitude(lon + offset), Latitude: 58} } tracks := []*occultationRiseSetTrack{{segments: [][]OccultationPathPoint{{point(20, start)}}}} tracks = appendOccultationRiseSetSamples(tracks, []OccultationPathPoint{ @@ -18,7 +20,7 @@ func TestOccultationRiseSetNewRootKeepsExistingBranch(t *testing.T) { if len(tracks) != 2 || len(tracks[0].segments[0]) != 2 || len(tracks[1].segments[0]) != 1 { t.Fatalf("offset=%g: new root did not get its own track", offset) } - if got := tracks[0].segments[0][1].Longitude; got != normalizeLongitude(19+offset) { + if got := tracks[0].segments[0][1].Longitude; got != geodata.NormalizeLongitude(19+offset) { t.Errorf("offset=%g: existing track jumped to longitude %g", offset, got) } } diff --git a/basic/occultation_star.go b/basic/occultation_star.go index 4abe9be..e91166c 100644 --- a/basic/occultation_star.go +++ b/basic/occultation_star.go @@ -4,6 +4,8 @@ import ( "math" "sort" "time" + + "b612.me/astro/internal/iau2006" ) const ( @@ -18,15 +20,9 @@ const ( starOccultationMaxContactSteps = 10000 ) -// FindStarOccultations 搜索单颗点源恒星的月掩星。 -// -// 输入坐标会从历元传播并转换到当日视坐标系;若提供视差,还会修正观测者的恒星视差。 -// 搜索不会加载内嵌 9100 星表;需要全星表搜索时,调用者必须显式加载并选择恒星。经度东为正、纬度北为正,单位为度;高度为平均海平面以上米数。 -// FindStarOccultations searches for lunar occultations of one point-source star. -// The input coordinate is propagated from its epoch, converted to the apparent frame of date, and corrected for the observer's stellar parallax when one is supplied. -// The search does not load the embedded 9100-star catalog; callers that need a catalog-wide search must load and select stars explicitly. Longitude is east-positive in degrees, latitude is north-positive in degrees, and height is the observer elevation above mean sea level in meters. -func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, - options OccultationSearchOptions) ([]StarOccultationInfo, error) { +// FindStarOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindStarOccultationsWithInstance(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, + options OccultationSearchOptions, inst *Instance) ([]StarOccultationInfo, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -45,8 +41,8 @@ func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, endTT := occultationTimeToTT(end) resultLocation := start.Location() results := make([]StarOccultationInfo, 0) - for _, greatestTT := range starOccultationCandidateGreatestTimes(startTT, endTT, starOccultationCoarseStepDays(options), star, observer, options.SafetyMarginArcsec) { - info, ok := starOccultationInfoAtGreatest(greatestTT, star, observer, options.SafetyMarginArcsec, resultLocation) + for _, greatestTT := range starOccultationCandidateGreatestTimes(startTT, endTT, starOccultationCoarseStepDays(options), star, observer, options.SafetyMarginArcsec, inst) { + info, ok := starOccultationInfoWithLimb(greatestTT, star, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst) if !ok { continue } @@ -62,14 +58,8 @@ func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, return results, nil } -// FindBestStarOccultations 返回窗口内每次恒星月掩在地球上的全球几何掩甚点。 -// 返回的 StarOccultationInfo.Observer 是海平面大地测量位置,由月掩几何选择,不使用地平线或可见性评分。 -// -// 地心数据只用于月周期搜索初值;最终点是与 FindStarOccultationPaths 一致的标准全球掩甚路径点,然后在该处重新进行站心接触和可见性计算。查询端点 10 ms 内的掩甚时刻也会包含,与数值根精度一致。 -// FindBestStarOccultations returns the global geometric greatest point on Earth for each stellar occultation in the window. -// The returned StarOccultationInfo.Observer is the geodetic location at sea level; it is selected from the occultation geometry, without a horizon or visibility score. -// Geocentric data only seeds each lunar-month search. The final point is the canonical global greatest-path point, matching FindStarOccultationPaths; event contacts and visibility are then recomputed topocentrically there. A greatest instant within 10 ms of either query endpoint is included, matching the numerical root precision. -func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) { +// FindBestStarOccultationsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func FindBestStarOccultationsWithInstance(start, end time.Time, star StarCoordinate, options OccultationSearchOptions, inst *Instance) ([]StarOccultationInfo, error) { if err := validateOccultationTimeRange(start, end); err != nil { return nil, err } @@ -88,12 +78,12 @@ func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options candidateEndTT := endTT + occultationPathSearchSpanDays resultLocation := start.Location() results := make([]StarOccultationInfo, 0) - for _, seedTT := range starOccultationGeocentricCandidateGreatestTimes(candidateStartTT, candidateEndTT, starOccultationCoarseStepDays(options), star, options.SafetyMarginArcsec) { - greatestTT, observer, _, observerOK := starOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, star) + for _, seedTT := range starOccultationGeocentricCandidateGreatestTimes(candidateStartTT, candidateEndTT, starOccultationCoarseStepDays(options), star, options.SafetyMarginArcsec, inst) { + greatestTT, observer, _, observerOK := starOccultationBestObserver(seedTT, selectionStartTT, selectionEndTT, star, inst) if !observerOK { continue } - info, ok := starOccultationInfoAtGreatest(greatestTT, star, observer, options.SafetyMarginArcsec, resultLocation) + info, ok := starOccultationInfoWithLimb(greatestTT, star, observer, options.SafetyMarginArcsec, resultLocation, options.LunarLimb, inst) if !ok { continue } @@ -108,40 +98,44 @@ func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options return results, nil } -func starOccultationCandidateGreatestTimes(startTT, endTT, step float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64) []float64 { +func starOccultationCandidateGreatestTimes(startTT, endTT, step float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, instances ...*Instance) []float64 { + inst := instanceValue(instances) + results := make([]float64, 0) for cycleStart := startTT; cycleStart < endTT; cycleStart += starOccultationSiderealMonthDays { cycleEnd := math.Min(cycleStart+starOccultationSiderealMonthDays, endTT) scanStart := math.Max(startTT-step, cycleStart-step) scanEnd := math.Min(endTT+step, cycleEnd+step) - if !starOccultationLatitudeEnvelopePass(scanStart, scanEnd, star, &observer, safetyMarginArcsec) { + if !starOccultationLatitudeEnvelopePass(scanStart, scanEnd, star, &observer, safetyMarginArcsec, inst) { continue } results = append(results, starOccultationScanCandidates( scanStart, scanEnd, step, - func(tt float64) float64 { return starMoonSeparationArcsec(tt, star, observer) }, + func(tt float64) float64 { return starMoonSeparationArcsec(tt, star, observer, inst) }, func(tt float64) bool { - return starOccultationLatitudePass(tt, star, observer, safetyMarginArcsec) + return starOccultationLatitudePass(tt, star, observer, safetyMarginArcsec, inst) }, )...) } return uniqueOccultationCandidateTimes(results, startTT, endTT) } -func starOccultationGeocentricCandidateGreatestTimes(startTT, endTT, step float64, star StarCoordinate, safetyMarginArcsec float64) []float64 { +func starOccultationGeocentricCandidateGreatestTimes(startTT, endTT, step float64, star StarCoordinate, safetyMarginArcsec float64, instances ...*Instance) []float64 { + inst := instanceValue(instances) + results := make([]float64, 0) for cycleStart := startTT; cycleStart < endTT; cycleStart += starOccultationSiderealMonthDays { cycleEnd := math.Min(cycleStart+starOccultationSiderealMonthDays, endTT) scanStart := math.Max(startTT-step, cycleStart-step) scanEnd := math.Min(endTT+step, cycleEnd+step) - if !starOccultationLatitudeEnvelopePass(scanStart, scanEnd, star, nil, safetyMarginArcsec) { + if !starOccultationLatitudeEnvelopePass(scanStart, scanEnd, star, nil, safetyMarginArcsec, inst) { continue } results = append(results, starOccultationScanCandidates( scanStart, scanEnd, step, - func(tt float64) float64 { return starOccultationGeocentricSeparationArcsec(tt, star) }, + func(tt float64) float64 { return starOccultationGeocentricSeparationArcsec(tt, star, inst) }, func(tt float64) bool { - return starOccultationLatitudePassGeocentric(tt, star, safetyMarginArcsec) + return starOccultationLatitudePassGeocentric(tt, star, safetyMarginArcsec, inst) }, )...) } @@ -216,23 +210,25 @@ func uniqueOccultationCandidateTimes(times []float64, startTT, endTT float64) [] return unique } -func starOccultationLatitudeEnvelopePass(startTT, endTT float64, star StarCoordinate, observer *Observer, safetyMarginArcsec float64) bool { +func starOccultationLatitudeEnvelopePass(startTT, endTT float64, star StarCoordinate, observer *Observer, safetyMarginArcsec float64, instances ...*Instance) bool { + inst := instanceValue(instances) + minimumLatitude := math.Inf(1) maximumLatitude := math.Inf(-1) maximumMoonRadiusDeg := 0.0 for _, tt := range []float64{startTT, (startTT + endTT) / 2, endTT} { var ra, dec float64 if observer == nil { - ra, dec = starApparentRaDecGeocentric(tt, star) + ra, dec = starApparentRaDecGeocentric(tt, star, inst) } else { - ra, dec = starApparentRaDec(tt, star, *observer) + ra, dec = starApparentRaDec(tt, star, *observer, inst) } _, latitude := RaDecToLoBo(tt, ra, dec) minimumLatitude = math.Min(minimumLatitude, latitude) maximumLatitude = math.Max(maximumLatitude, latitude) - moonRadius := MoonSemidiameter(tt) + moonRadius := MoonSemidiameterWithInstance(tt, inst) if observer != nil { - moonRadius = moonTopocentricSemidiameterN(tt, *observer, -1) + moonRadius = moonTopocentricSemidiameterN(tt, *observer, -1, inst) } maximumMoonRadiusDeg = math.Max(maximumMoonRadiusDeg, moonRadius/3600) } @@ -271,27 +267,31 @@ func starOccultationMinimizeValue(left, right float64, value func(float64) float return (left + right) / 2 } -func starOccultationGeocentricSeparationArcsec(tt float64, star StarCoordinate) float64 { - moonRA, moonDec := HMoonGeocentricApparentRaDecN(tt, -1) - starRA, starDec, _ := starApparentRaDecDistanceGeocentric(tt, star) +func starOccultationGeocentricSeparationArcsec(tt float64, star StarCoordinate, instances ...*Instance) float64 { + inst := instanceValue(instances) + + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + starRA, starDec, _ := starApparentRaDecDistanceGeocentric(tt, star, inst) return angularSeparationDegrees(moonRA, moonDec, starRA, starDec) * 3600 } -func starOccultationBestObserver(seedTT, startTT, endTT float64, star StarCoordinate) (float64, Observer, float64, bool) { +func starOccultationBestObserver(seedTT, startTT, endTT float64, star StarCoordinate, instances ...*Instance) (float64, Observer, float64, bool) { + inst := instanceValue(instances) + searchStart := seedTT - occultationPathSearchSpanDays searchEnd := seedTT + occultationPathSearchSpanDays - outerStart, outerEnd, ok := starOccultationPathWindow(seedTT, searchStart, searchEnd, star, false) + outerStart, outerEnd, ok := starOccultationPathWindow(seedTT, searchStart, searchEnd, star, false, inst) if !ok { return 0, Observer{}, 0, false } - greatestTT := starOccultationPathGreatest(seedTT, outerStart, outerEnd, star) + greatestTT := starOccultationPathGreatest(seedTT, outerStart, outerEnd, star, inst) if greatestTT < startTT || greatestTT > endTT { return 0, Observer{}, 0, false } - point, pointOK := starOccultationPathCenterPoint(greatestTT, star, time.UTC) + point, pointOK := starOccultationPathCenterPoint(greatestTT, star, time.UTC, inst) if !pointOK { frameAt := func(tt float64) (occultationPathFrame, bool) { - return starOccultationPathFrameAt(tt, star) + return starOccultationPathFrameAt(tt, star, inst) } // 同行星最佳事件搜索:非中心路径的最佳站心取离影轴最近的椭球点, // 外接触切点位于掩带边缘,只作最后回退。 @@ -300,15 +300,15 @@ func starOccultationBestObserver(seedTT, startTT, endTT float64, star StarCoordi // the outer contact tangent on the band edge stays the last resort. point, pointOK = occultationPathTrackPointForFrame(greatestTT, frameAt, time.UTC) if !pointOK { - point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC) + point, pointOK = occultationPathBoundaryPointForFrame(greatestTT, frameAt, time.UTC, inst) } } if !pointOK { return 0, Observer{}, 0, false } observer := Observer{Longitude: point.Longitude, Latitude: point.Latitude} - position := starMoonPositionAt(greatestTT, star, observer) - moonRadius := moonTopocentricSemidiameterN(greatestTT, observer, -1) + position := starMoonPositionAt(greatestTT, star, observer, inst) + moonRadius := moonTopocentricSemidiameterN(greatestTT, observer, -1, inst) if !position.valid || !finite(moonRadius) { return 0, Observer{}, 0, false } @@ -316,24 +316,43 @@ func starOccultationBestObserver(seedTT, startTT, endTT float64, star StarCoordi return greatestTT, observer, metric, finite(metric) } -func normalizeLongitude180(longitude float64) float64 { - longitude = math.Mod(longitude+180, 360) - if longitude < 0 { - longitude += 360 +func starOccultationInfoAtGreatest(greatestTT float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, location *time.Location, instances ...*Instance) (StarOccultationInfo, bool) { + inst := instanceValue(instances) + + info, ok := starOccultationGreatestInfo(greatestTT, star, observer, safetyMarginArcsec, location, inst) + if !ok || info.MinimumSeparationArcsec > info.MoonSemidiameterArcsec { + return StarOccultationInfo{}, false } - return longitude - 180 + minimumResidual := info.MinimumSeparationArcsec - info.MoonSemidiameterArcsec + if math.Abs(minimumResidual) <= starOccultationGrazingTolerance { + info.Type = OccultationGrazing + info.Immersion = info.Greatest + info.Emersion = info.Greatest + info.ContactsComplete = true + return info, true + } + immersionTT, immersionOK := starOccultationContact(greatestTT, greatestTT-starOccultationContactSpanDays, -1, star, observer, inst) + emersionTT, emersionOK := starOccultationContact(greatestTT, greatestTT+starOccultationContactSpanDays, 1, star, observer, inst) + if !immersionOK || !emersionOK { + return StarOccultationInfo{}, false + } + info.Immersion = occultationTTToLocation(immersionTT, location) + info.Emersion = occultationTTToLocation(emersionTT, location) + info.ContactsComplete = true + return info, true } -func starOccultationInfoAtGreatest(greatestTT float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, location *time.Location) (StarOccultationInfo, bool) { - if !starOccultationLatitudePass(greatestTT, star, observer, safetyMarginArcsec) { +// starOccultationGreatestInfo 只填掩甚几何,不判断圆月面是否成掩、不求接触。 +func starOccultationGreatestInfo(greatestTT float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, location *time.Location, inst *Instance) (StarOccultationInfo, bool) { + if !starOccultationLatitudePass(greatestTT, star, observer, safetyMarginArcsec, inst) { return StarOccultationInfo{}, false } - minimumSeparation := starMoonSeparationArcsec(greatestTT, star, observer) - moonRadius := moonTopocentricSemidiameterN(greatestTT, observer, -1) - if !finite(minimumSeparation) || !finite(moonRadius) || minimumSeparation > moonRadius { + minimumSeparation := starMoonSeparationArcsec(greatestTT, star, observer, inst) + moonRadius := moonTopocentricSemidiameterN(greatestTT, observer, -1, inst) + if !finite(minimumSeparation) || !finite(moonRadius) { return StarOccultationInfo{}, false } - greatestPosition := starMoonPositionAt(greatestTT, star, observer) + greatestPosition := starMoonPositionAt(greatestTT, star, observer, inst) if !greatestPosition.valid { return StarOccultationInfo{}, false } @@ -349,23 +368,6 @@ func starOccultationInfoAtGreatest(greatestTT float64, star StarCoordinate, obse MoonAzimuthAtGreatest: occultationAzimuth(greatestTT, observer, greatestPosition.moonRA, greatestPosition.moonDec), } info.VisibleAtGreatest = info.MoonAltitudeAtGreatest >= 0 - - minimumResidual := minimumSeparation - moonRadius - if math.Abs(minimumResidual) <= starOccultationGrazingTolerance { - info.Type = OccultationGrazing - info.Immersion = info.Greatest - info.Emersion = info.Greatest - info.ContactsComplete = true - return info, true - } - immersionTT, immersionOK := starOccultationContact(greatestTT, greatestTT-starOccultationContactSpanDays, -1, star, observer) - emersionTT, emersionOK := starOccultationContact(greatestTT, greatestTT+starOccultationContactSpanDays, 1, star, observer) - if !immersionOK || !emersionOK { - return StarOccultationInfo{}, false - } - info.Immersion = occultationTTToLocation(immersionTT, location) - info.Emersion = occultationTTToLocation(emersionTT, location) - info.ContactsComplete = true return info, true } @@ -375,9 +377,11 @@ type starMoonPosition struct { valid bool } -func starMoonPositionAt(tt float64, star StarCoordinate, observer Observer) starMoonPosition { - moonRA, moonDec := moonTopocentricApparentRaDec(tt, observer, -1) - starRA, starDec := starApparentRaDec(tt, star, observer) +func starMoonPositionAt(tt float64, star StarCoordinate, observer Observer, instances ...*Instance) starMoonPosition { + inst := instanceValue(instances) + + moonRA, moonDec := moonTopocentricApparentRaDec(tt, observer, -1, inst) + starRA, starDec := starApparentRaDec(tt, star, observer, inst) return starMoonPosition{ moonRA: moonRA, moonDec: moonDec, @@ -387,16 +391,20 @@ func starMoonPositionAt(tt float64, star StarCoordinate, observer Observer) star } } -func moonTopocentricApparentRaDec(tt float64, observer Observer, n int) (float64, float64) { - ra, dec := HMoonGeocentricApparentRaDecN(tt, n) +func moonTopocentricApparentRaDec(tt float64, observer Observer, n int, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + ra, dec := HMoonGeocentricApparentRaDecNWithInstance(tt, n, inst) ut := TT2UTC(tt) - distanceAU := HMoonAwayN(tt, n) / 149597870.7 + distanceAU := HMoonAwayNWithInstance(tt, n, inst) / 149597870.7 ra, dec = TopocentricRaDec(ra, dec, observer.Latitude, observer.Longitude, ut, distanceAU, observer.Height) return normalizeRA(ra), dec } -func starApparentRaDec(tt float64, star StarCoordinate, observer Observer) (float64, float64) { - ra, dec, distanceAU := starApparentRaDecDistanceGeocentric(tt, star) +func starApparentRaDec(tt float64, star StarCoordinate, observer Observer, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + ra, dec, distanceAU := starApparentRaDecDistanceGeocentric(tt, star, inst) if distanceAU > 0 { ra, dec = TopocentricRaDec(ra, dec, observer.Latitude, observer.Longitude, TT2UTC(tt), distanceAU, observer.Height) ra = normalizeRA(ra) @@ -404,13 +412,17 @@ func starApparentRaDec(tt float64, star StarCoordinate, observer Observer) (floa return ra, dec } -func starApparentRaDecGeocentric(tt float64, star StarCoordinate) (float64, float64) { - ra, dec, _ := starApparentRaDecDistanceGeocentric(tt, star) +func starApparentRaDecGeocentric(tt float64, star StarCoordinate, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + ra, dec, _ := starApparentRaDecDistanceGeocentric(tt, star, inst) return ra, dec } // starApparentRaDecDistanceGeocentric 同时给出视位置与推进后的距离(天文单位,0 表示距离未知)。 -func starApparentRaDecDistanceGeocentric(tt float64, star StarCoordinate) (float64, float64, float64) { +func starApparentRaDecDistanceGeocentric(tt float64, star StarCoordinate, instances ...*Instance) (float64, float64, float64) { + inst := instanceValue(instances) + epochJD := occultationTimeToTT(star.Epoch) parallaxMas := star.parallaxMas() ra := star.RA @@ -419,13 +431,13 @@ func starApparentRaDecDistanceGeocentric(tt float64, star StarCoordinate) (float if star.Frame == CoordinateFrameICRS { ra, dec = starICRSToMeanJ2000RaDec(ra, dec) } else if star.Frame == CoordinateFrameApparentOfDate { - ra, dec = starApparentToMeanRaDec(epochJD, ra, dec, parallaxMas) + ra, dec = starApparentToMeanRaDec(epochJD, ra, dec, parallaxMas, inst) precessionEpoch = epochJD } epochDistanceAU := starEpochDistanceAU(parallaxMas) ra, dec, distanceAU := starProperMotionRaDec(tt, star, ra, dec, epochDistanceAU) ra, dec = Precess(ra, dec, precessionEpoch, tt) - ra, dec = starMeanToApparentRaDec(tt, ra, dec, parallaxMasAtDistance(parallaxMas, distanceAU)) + ra, dec = starMeanToApparentRaDec(tt, ra, dec, parallaxMasAtDistance(parallaxMas, distanceAU), inst) return ra, dec, distanceAU } @@ -493,47 +505,39 @@ func starProperMotionRaDec3D(ra, dec, years, epochDistanceAU float64, star StarC } func starICRSToMeanJ2000RaDec(ra, dec float64) (float64, float64) { - // IAU SOFA 框架偏差矩阵,将 GCRS/ICRS 向量转换为 J2000.0 平均赤道和春分点。 - // IAU SOFA frame-bias matrix, transforming a GCRS/ICRS vector to the mean equator and equinox of J2000.0. - const ( - b00 = 0.9999999999999942 - b01 = -0.7078279744199197e-7 - b02 = 0.8056217146976134e-7 - b10 = 0.7078279477857337e-7 - b11 = 0.9999999999999969 - b12 = 0.3306041454222148e-7 - b20 = -0.8056217380986972e-7 - b21 = -0.3306040883980553e-7 - b22 = 0.9999999999999962 - ) + b := iau2006.FrameBias() raRad := ra * math.Pi / 180 decRad := dec * math.Pi / 180 x := math.Cos(decRad) * math.Cos(raRad) y := math.Cos(decRad) * math.Sin(raRad) z := math.Sin(decRad) - biasedX := b00*x + b01*y + b02*z - biasedY := b10*x + b11*y + b12*z - biasedZ := b20*x + b21*y + b22*z + biasedX := b[0][0]*x + b[0][1]*y + b[0][2]*z + biasedY := b[1][0]*x + b[1][1]*y + b[1][2]*z + biasedZ := b[2][0]*x + b[2][1]*y + b[2][2]*z return normalizeRA(math.Atan2(biasedY, biasedX) * 180 / math.Pi), math.Atan2(biasedZ, math.Hypot(biasedX, biasedY)) * 180 / math.Pi } -func starMeanToApparentRaDec(tt, ra, dec, parallaxMas float64) (float64, float64) { +func starMeanToApparentRaDec(tt, ra, dec, parallaxMas float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + longitude, latitude := starMeanEquatorialToEcliptic(tt, ra, dec) if parallaxMas > 0 { - longitude, latitude = starAnnualParallaxEcliptic(tt, longitude, latitude, parallaxMas) + longitude, latitude = starAnnualParallaxEcliptic(tt, longitude, latitude, parallaxMas, inst) } meanLongitude, meanLatitude := longitude, latitude - longitude = normalizeRA(meanLongitude + GXCLo(meanLongitude, meanLatitude, tt)/3600 + Nutation2000Bi(tt)) - latitude = meanLatitude + GXCBo(meanLongitude, meanLatitude, tt)/3600 + longitude = normalizeRA(meanLongitude + GXCLoWithInstance(meanLongitude, meanLatitude, tt, inst)/3600 + Nutation2000Bi(tt)) + latitude = meanLatitude + GXCBoWithInstance(meanLongitude, meanLatitude, tt, inst)/3600 ra, dec = LoBoToRaDec(tt, longitude, latitude) return normalizeRA(ra), dec } -func starApparentToMeanRaDec(tt, apparentRA, apparentDec, parallaxMas float64) (float64, float64) { +func starApparentToMeanRaDec(tt, apparentRA, apparentDec, parallaxMas float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + meanRA, meanDec := apparentRA, apparentDec for i := 0; i < 8; i++ { - computedRA, computedDec := starMeanToApparentRaDec(tt, meanRA, meanDec, parallaxMas) + computedRA, computedDec := starMeanToApparentRaDec(tt, meanRA, meanDec, parallaxMas, inst) meanRA = normalizeRA(meanRA - signedAngleDifference(computedRA, apparentRA)) meanDec -= computedDec - apparentDec } @@ -554,7 +558,9 @@ func starMeanEquatorialToEcliptic(tt, ra, dec float64) (float64, float64) { return normalizeRA(longitude), latitude } -func starAnnualParallaxEcliptic(tt, longitude, latitude, parallaxMas float64) (float64, float64) { +func starAnnualParallaxEcliptic(tt, longitude, latitude, parallaxMas float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + distanceAU := 206264806.247 / parallaxMas longitudeRad := longitude * math.Pi / 180 latitudeRad := latitude * math.Pi / 180 @@ -563,8 +569,8 @@ func starAnnualParallaxEcliptic(tt, longitude, latitude, parallaxMas float64) (f starY := distanceAU * cosLatitude * math.Sin(longitudeRad) starZ := distanceAU * math.Sin(latitudeRad) - earthLongitude := normalizeRA(HSunTrueLoN(tt, -1)+180) * math.Pi / 180 - earthDistance := EarthAwayN(tt, -1) + earthLongitude := normalizeRA(HSunTrueLoNWithInstance(tt, -1, inst)+180) * math.Pi / 180 + earthDistance := EarthAwayNWithInstance(tt, -1, inst) starX -= earthDistance * math.Cos(earthLongitude) starY -= earthDistance * math.Sin(earthLongitude) @@ -580,33 +586,41 @@ func starOccultationLongitudeDistance(tt float64, star StarCoordinate, observer return math.Abs(signedAngleDifference(moonLongitude, starLongitude)) } -func starMoonSeparationArcsec(tt float64, star StarCoordinate, observer Observer) float64 { - position := starMoonPositionAt(tt, star, observer) +func starMoonSeparationArcsec(tt float64, star StarCoordinate, observer Observer, instances ...*Instance) float64 { + inst := instanceValue(instances) + + position := starMoonPositionAt(tt, star, observer, inst) if !position.valid { return math.Inf(1) } return angularSeparationDegrees(position.moonRA, position.moonDec, position.starRA, position.starDec) * 3600 } -func starOccultationLatitudePass(tt float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64) bool { - starRA, starDec := starApparentRaDec(tt, star, observer) +func starOccultationLatitudePass(tt float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, instances ...*Instance) bool { + inst := instanceValue(instances) + + starRA, starDec := starApparentRaDec(tt, star, observer, inst) _, starLatitude := RaDecToLoBo(tt, starRA, starDec) - moonLatitude := HMoonTrueBoN(tt, 8) - moonRadius := moonTopocentricSemidiameterN(tt, observer, -1) + moonLatitude := HMoonTrueBoNWithInstance(tt, 8, inst) + moonRadius := moonTopocentricSemidiameterN(tt, observer, -1, inst) limit := moonRadius + starOccultationLatitudeMarginAS + safetyMarginArcsec return math.Abs(starLatitude-moonLatitude)*3600 <= limit } -func starOccultationLatitudePassGeocentric(tt float64, star StarCoordinate, safetyMarginArcsec float64) bool { - starRA, starDec := starApparentRaDecGeocentric(tt, star) +func starOccultationLatitudePassGeocentric(tt float64, star StarCoordinate, safetyMarginArcsec float64, instances ...*Instance) bool { + inst := instanceValue(instances) + + starRA, starDec := starApparentRaDecGeocentric(tt, star, inst) _, starLatitude := RaDecToLoBo(tt, starRA, starDec) - moonLatitude := HMoonTrueBoN(tt, 8) - limit := MoonSemidiameter(tt) + starOccultationLatitudeMarginAS + safetyMarginArcsec + moonLatitude := HMoonTrueBoNWithInstance(tt, 8, inst) + limit := MoonSemidiameterWithInstance(tt, inst) + starOccultationLatitudeMarginAS + safetyMarginArcsec return math.Abs(starLatitude-moonLatitude)*3600 <= limit } -func starOccultationContact(greatestTT, boundaryTT float64, direction int, star StarCoordinate, observer Observer) (float64, bool) { - valueAtGreatest := starMoonSeparationArcsec(greatestTT, star, observer) - moonTopocentricSemidiameterN(greatestTT, observer, -1) +func starOccultationContact(greatestTT, boundaryTT float64, direction int, star StarCoordinate, observer Observer, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + + valueAtGreatest := starMoonSeparationArcsec(greatestTT, star, observer, inst) - moonTopocentricSemidiameterN(greatestTT, observer, -1, inst) if !finite(valueAtGreatest) || valueAtGreatest > 0 { return math.NaN(), false } @@ -621,9 +635,9 @@ func starOccultationContact(greatestTT, boundaryTT float64, direction int, star if direction > 0 && nextTT > boundaryTT { nextTT = boundaryTT } - nextValue := starMoonSeparationArcsec(nextTT, star, observer) - moonTopocentricSemidiameterN(nextTT, observer, -1) + nextValue := starMoonSeparationArcsec(nextTT, star, observer, inst) - moonTopocentricSemidiameterN(nextTT, observer, -1, inst) if finite(nextValue) && nextValue >= 0 { - return starOccultationRoot(currentTT, nextTT, currentValue, nextValue, star, observer) + return starOccultationRoot(currentTT, nextTT, currentValue, nextValue, star, observer, inst) } if nextTT == boundaryTT { return math.NaN(), false @@ -634,7 +648,9 @@ func starOccultationContact(greatestTT, boundaryTT float64, direction int, star return math.NaN(), false } -func starOccultationRoot(leftTT, rightTT, leftValue, rightValue float64, star StarCoordinate, observer Observer) (float64, bool) { +func starOccultationRoot(leftTT, rightTT, leftValue, rightValue float64, star StarCoordinate, observer Observer, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + if !finite(leftValue) || !finite(rightValue) || leftValue*rightValue > 0 { return math.NaN(), false } @@ -646,7 +662,7 @@ func starOccultationRoot(leftTT, rightTT, leftValue, rightValue float64, star St } for i := 0; i < 64 && math.Abs(rightTT-leftTT) > starOccultationRootToleranceDays; i++ { midTT := (leftTT + rightTT) / 2 - midValue := starMoonSeparationArcsec(midTT, star, observer) - moonTopocentricSemidiameterN(midTT, observer, -1) + midValue := starMoonSeparationArcsec(midTT, star, observer, inst) - moonTopocentricSemidiameterN(midTT, observer, -1, inst) if !finite(midValue) { return math.NaN(), false } diff --git a/basic/occultation_star_diagram.go b/basic/occultation_star_diagram.go index eb8b028..fda21d2 100644 --- a/basic/occultation_star_diagram.go +++ b/basic/occultation_star_diagram.go @@ -67,13 +67,11 @@ type starOccultationDiagramTime struct { labels []string } -// StarOccultationDiagram 为已求解的固定地点恒星月掩计算以月心为原点的切平面轨迹。事件数据无效或不完整时,结果不含帧。 -// StarOccultationDiagram computes a Moon-centered tangent-plane track for an already solved fixed-site stellar occultation. Invalid or incomplete event data produces a result without frames. -func StarOccultationDiagram( +// StarOccultationDiagramWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func StarOccultationDiagramWithInstance( info StarOccultationInfo, star StarCoordinate, - options StarOccultationDiagramOptions, -) StarOccultationDiagramResult { + options StarOccultationDiagramOptions, inst *Instance) StarOccultationDiagramResult { options = normalizeStarOccultationDiagramOptions(options) result := StarOccultationDiagramResult{Occultation: info, StepDays: options.StepDays} if star.Validate() != nil || info.Observer.Validate() != nil || @@ -86,9 +84,9 @@ func StarOccultationDiagram( startTT := occultationTimeToTT(info.Immersion) greatestTT := occultationTimeToTT(info.Greatest) endTT := occultationTimeToTT(info.Emersion) - immersionFrame, immersionOK := starOccultationDiagramFrameAt(startTT, star, info.Observer) - greatestFrame, greatestOK := starOccultationDiagramFrameAt(greatestTT, star, info.Observer) - emersionFrame, emersionOK := starOccultationDiagramFrameAt(endTT, star, info.Observer) + immersionFrame, immersionOK := starOccultationDiagramFrameAt(startTT, star, info.Observer, inst) + greatestFrame, greatestOK := starOccultationDiagramFrameAt(greatestTT, star, info.Observer, inst) + emersionFrame, emersionOK := starOccultationDiagramFrameAt(endTT, star, info.Observer, inst) if !immersionOK || !greatestOK || !emersionOK || !starOccultationDiagramMatchesInfo(info, immersionFrame, greatestFrame, emersionFrame) { return result @@ -97,7 +95,7 @@ func StarOccultationDiagram( result.StepDays = stepDays result.Frames = make([]StarOccultationDiagramFrame, 0, len(times)) for _, item := range times { - frame, ok := starOccultationDiagramFrameAt(item.jde, star, info.Observer) + frame, ok := starOccultationDiagramFrameAt(item.jde, star, info.Observer, inst) if !ok { return StarOccultationDiagramResult{Occultation: info, StepDays: stepDays} } @@ -116,8 +114,9 @@ func starOccultationDiagramMatchesInfo( !finite(info.PositionAngleDeg) { return false } - if math.Abs(immersion.SeparationArcsec-immersion.MoonRadiusArcsec) > starOccultationDiagramGeometryArcsec || - math.Abs(emersion.SeparationArcsec-emersion.MoonRadiusArcsec) > starOccultationDiagramGeometryArcsec { + // 已按月缘剖面收敛的接触不在圆月面上,圆月面接触判据只适用于未修正的结果。 + if !info.LunarLimbApplied && (math.Abs(immersion.SeparationArcsec-immersion.MoonRadiusArcsec) > starOccultationDiagramGeometryArcsec || + math.Abs(emersion.SeparationArcsec-emersion.MoonRadiusArcsec) > starOccultationDiagramGeometryArcsec) { return false } return math.Abs(greatest.SeparationArcsec-info.MinimumSeparationArcsec) <= starOccultationDiagramGeometryArcsec && @@ -221,9 +220,11 @@ func starOccultationDiagramLabelPriority(labels []string) int { } } -func starOccultationDiagramFrameAt(tt float64, star StarCoordinate, observer Observer) (StarOccultationDiagramFrame, bool) { - position := starMoonPositionAt(tt, star, observer) - moonRadius := moonTopocentricSemidiameterN(tt, observer, -1) +func starOccultationDiagramFrameAt(tt float64, star StarCoordinate, observer Observer, instances ...*Instance) (StarOccultationDiagramFrame, bool) { + inst := instanceValue(instances) + + position := starMoonPositionAt(tt, star, observer, inst) + moonRadius := moonTopocentricSemidiameterN(tt, observer, -1, inst) if !position.valid || !finite(moonRadius) || moonRadius <= 0 { return StarOccultationDiagramFrame{}, false } diff --git a/basic/occultation_star_limb.go b/basic/occultation_star_limb.go new file mode 100644 index 0000000..57d6fb2 --- /dev/null +++ b/basic/occultation_star_limb.go @@ -0,0 +1,461 @@ +package basic + +import ( + "fmt" + "math" + "sort" + "time" +) + +const ( + starOccultationLimbDefaultStep = 200 * time.Millisecond + starOccultationLimbMinimumStep = 10 * time.Millisecond + starOccultationLimbMaxSamples = 20000 + starOccultationLimbMinSamples = 64 + starOccultationLimbBoundStep = 60.0 / 86400.0 + starOccultationLimbBoundSpan = 3.5 / 24.0 + starOccultationLimbNodeDays = 10.0 / 1440.0 + starOccultationLimbNodeSpan = 4.0 / 24.0 + starOccultationLimbRootDays = 1e-3 / 86400.0 + starOccultationLimbUnrangedPadKM = 1.0 +) + +// StarOccultationLimbProfileOptions 控制精密恒星月掩的星迹采样 / sampling of a precise stellar occultation. +type StarOccultationLimbProfileOptions struct { + // Step 是星迹采样步长;零值 200 ms,小于 10 ms 提升到 10 ms;每段至少 64 个样本、总数不超过 20000,越界时自动调整。短于步长的遮挡或复现可能漏检 / track sampling step. + Step time.Duration +} + +// StarOccultationLimbSample 星迹上一个时刻的月缘几何,角量均为角秒、度 / limb geometry at one instant on the star track. +type StarOccultationLimbSample struct { + Time time.Time + // SeparationArcsec 恒星到站心月心的角距 / star–Moon-centre separation. + SeparationArcsec float64 + // LimbRadiusArcsec 恒星方向上的月缘角半径 / limb radius toward the star. + LimbRadiusArcsec float64 + // MeanRadiusArcsec 参考圆月面角半径 / circular reference radius. + MeanRadiusArcsec float64 + // PositionAngleDeg 恒星相对月心的位置角,自天北向东 / position angle of the star. + PositionAngleDeg float64 + // Hidden 恒星位于月缘以内 / star is behind the limb. + Hidden bool +} + +// OccultationInterval 一段连续遮挡 / one continuous hidden interval. +type OccultationInterval struct { + Start time.Time + End time.Time +} + +// StarOccultationLimbProfileResult 一次恒星月掩沿星迹的月缘剖面与全部遮挡区间 / limb profile and every hidden interval of a stellar occultation. +type StarOccultationLimbProfileResult struct { + // Occultation 按同一剖面求得的标准结果,掩始、掩终为首次遮挡与最后复现 / standard result on the same profile. + Occultation StarOccultationInfo + // Segments 月缘起伏带内的采样段,按时间排序;深掩时为掩入侧与复现侧两段,掠掩时通常为一段 / samples inside the limb relief band. + Segments [][]StarOccultationLimbSample + // Hidden 全部遮挡区间,按时间排序 / every hidden interval. + Hidden []OccultationInterval + // ReferenceRadiusKM 样本 MeanRadiusArcsec 对应的参考月球半径 / reference lunar radius behind MeanRadiusArcsec. + ReferenceRadiusKM float64 + // ReliefMinKM / ReliefMaxKM 判定所用的月缘半径范围 / limb radius range used for gating. + ReliefMinKM float64 + ReliefMaxKM float64 + // Step 实际采用的采样步长 / effective step. + Step time.Duration +} + +type starLimbState struct { + separation, limbRadius, meanRadius, positionAngle float64 + distanceKM float64 +} + +// starLimbEphemeris 掩甚前后 ±4 h 的地心月球、恒星视位置与月球本体坐标架插值表;站心换算逐点精确计算。 +type starLimbEphemeris struct { + bodies []localEphemerisVectorNode + axes []localEphemerisVectorNode + base lunarLimbFrame + hasDistance bool +} + +func newStarLimbEphemeris(greatestTT float64, star StarCoordinate, limb LunarLimb, inst *Instance) (*starLimbEphemeris, bool) { + half := int(math.Round(starOccultationLimbNodeSpan / starOccultationLimbNodeDays)) + ephemeris := &starLimbEphemeris{ + bodies: make([]localEphemerisVectorNode, 2*half+1), + axes: make([]localEphemerisVectorNode, 2*half+1), + } + for index := range ephemeris.bodies { + tt := greatestTT + float64(index-half)*starOccultationLimbNodeDays + moonRA, moonDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + moon := lunarEclipseLimbVector(moonRA, moonDec, HMoonAwayNWithInstance(tt, -1, inst)) + starRA, starDec, distanceAU := starApparentRaDecDistanceGeocentric(tt, star, inst) + if distanceAU <= 0 { + distanceAU = 1 + } else { + ephemeris.hasDistance = true + } + frame := newLunarLimbFrame(limb, tt, moon, inst) + if !frame.valid || !finiteVector3(moon) { + return nil, false + } + if index == half { + ephemeris.base = frame + } + ephemeris.bodies[index] = localEphemerisVectorNode{tt: tt, first: moon, next: lunarEclipseLimbVector(starRA, starDec, distanceAU)} + ephemeris.axes[index] = localEphemerisVectorNode{tt: tt, first: frame.axisX, next: frame.axisZ} + } + return ephemeris, true +} + +func (ephemeris *starLimbEphemeris) stateAt(tt float64, observer Observer) (starLimbState, bool) { + moon, starVector, ok := interpolateLocalEphemerisVectors(ephemeris.bodies, tt) + if !ok { + return starLimbState{}, false + } + axisX, axisZ, ok := interpolateLocalEphemerisVectors(ephemeris.axes, tt) + if !ok { + return starLimbState{}, false + } + geoDistance := math.Sqrt(lunarLimbDot(moon, moon)) + geoRA := normalizeRA(math.Atan2(moon[1], moon[0]) / rad) + geoDec := math.Asin(clampUnit(moon[2]/geoDistance)) / rad + ut := TT2UTC(tt) + topoRA, topoDec := TopocentricRaDec(geoRA, geoDec, observer.Latitude, observer.Longitude, ut, geoDistance/149597870.7, observer.Height) + topoRA = normalizeRA(topoRA) + topoDistance := topocentricDistanceKM(geoRA, geoDec, geoDistance, observer, ut) + starDistance := math.Sqrt(lunarLimbDot(starVector, starVector)) + starRA := normalizeRA(math.Atan2(starVector[1], starVector[0]) / rad) + starDec := math.Asin(clampUnit(starVector[2]/starDistance)) / rad + if ephemeris.hasDistance { + starRA, starDec = TopocentricRaDec(starRA, starDec, observer.Latitude, observer.Longitude, ut, starDistance, observer.Height) + starRA = normalizeRA(starRA) + } + if !finite(topoRA) || !finite(topoDec) || !finite(topoDistance) || topoDistance <= 0 || !finite(starRA) || !finite(starDec) { + return starLimbState{}, false + } + frame := ephemeris.base + if frame.axisX, ok = lunarLimbNormalize(axisX); !ok { + return starLimbState{}, false + } + if frame.axisZ, ok = lunarLimbNormalize(axisZ); !ok { + return starLimbState{}, false + } + // 两轴分别插值后不再严格正交,按 z 轴重新正交化 x 轴。 + if frame.axisX, ok = lunarLimbNormalize(lunarLimbReject(frame.axisX, frame.axisZ)); !ok { + return starLimbState{}, false + } + frame.axisY = lunarLimbCross(frame.axisZ, frame.axisX) + radiusKM, ok := frame.radiusKM(lunarEclipseLimbVector(topoRA, topoDec, topoDistance), lunarEclipseLimbVector(starRA, starDec, 1), false) + if !ok { + return starLimbState{}, false + } + return starLimbState{ + separation: angularSeparationDegrees(topoRA, topoDec, starRA, starDec) * 3600, + limbRadius: angularSemidiameterArcsec(radiusKM, topoDistance), + meanRadius: angularSemidiameterArcsec(moonEquatorialRadiusKM, topoDistance), + positionAngle: occultationPositionAngle(topoRA, topoDec, starRA, starDec), + distanceKM: topoDistance, + }, true +} + +// starOccultationLimbRange 返回剖面半径范围;剖面不报告范围时在掩甚扫一圈并外扩 1 km。 +func starOccultationLimbRange(tt float64, star StarCoordinate, observer Observer, limb LunarLimb, inst *Instance) (float64, float64, bool) { + geoRA, geoDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + geoDistance := HMoonAwayNWithInstance(tt, -1, inst) + frame := newLunarLimbFrame(limb, tt, lunarEclipseLimbVector(geoRA, geoDec, geoDistance), inst) + if !frame.valid { + return 0, 0, false + } + if frame.ranged { + return frame.minKM, frame.maxKM, true + } + topoRA, topoDec := moonTopocentricApparentRaDec(tt, observer, -1, inst) + starRA, starDec := starApparentRaDec(tt, star, observer, inst) + view, ok := frame.viewToward(lunarEclipseLimbVector(topoRA, topoDec, 1), lunarEclipseLimbVector(starRA, starDec, 1)) + if !ok { + return 0, 0, false + } + low, high := math.Inf(1), math.Inf(-1) + for offset := 0.0; offset < 360; offset += frame.stepDeg { + radius, ok := frame.radiusAt(view, offset) + if !ok { + return 0, 0, false + } + low, high = math.Min(low, radius), math.Max(high, radius) + } + return low - starOccultationLimbUnrangedPadKM, high + starOccultationLimbUnrangedPadKM, true +} + +// starOccultationSeparationCrossing 自掩甚向 direction 一侧求角距升到 radiusKM 对应角半径的时刻;角距自掩甚单调增大。 +func starOccultationSeparationCrossing(greatestTT float64, direction float64, radiusKM float64, ephemeris *starLimbEphemeris, observer Observer) (float64, bool) { + value := func(tt float64) float64 { + state, ok := ephemeris.stateAt(tt, observer) + if !ok { + return math.NaN() + } + return state.separation - angularSemidiameterArcsec(radiusKM, state.distanceKM) + } + near, nearValue := greatestTT, value(greatestTT) + if !finite(nearValue) { + return 0, false + } + if nearValue >= 0 { + return greatestTT, true + } + for step := 1; float64(step)*starOccultationLimbBoundStep <= starOccultationLimbBoundSpan; step++ { + far := greatestTT + direction*float64(step)*starOccultationLimbBoundStep + farValue := value(far) + if !finite(farValue) { + return 0, false + } + if farValue < 0 { + near, nearValue = far, farValue + continue + } + for i := 0; i < 64 && math.Abs(far-near) > starOccultationLimbRootDays; i++ { + mid := (near + far) / 2 + if v := value(mid); v < 0 { + near = mid + } else { + far = mid + } + } + return (near + far) / 2, true + } + return 0, false +} + +type starLimbRoot struct { + tt float64 + disappear bool +} + +type starLimbScan struct { + segments [][]StarOccultationLimbSample + hidden [][2]float64 + lowKM float64 + highKM float64 + stepDays float64 + // grazing 为真表示星迹始终在月缘起伏带内(单段采样)。 + grazing bool +} + +// starOccultationLimbScanAt 按剖面起伏带把星迹分必见、必掩与带内三段,带外两端遮挡状态由构造保证;ok 为假表示剖面无值或星历失败。 +func starOccultationLimbScanAt(greatestTT float64, star StarCoordinate, observer Observer, limb LunarLimb, stepDays float64, location *time.Location, inst *Instance) (starLimbScan, bool) { + lowKM, highKM, ok := starOccultationLimbRange(greatestTT, star, observer, limb, inst) + if !ok { + return starLimbScan{}, false + } + scan := starLimbScan{lowKM: lowKM, highKM: highKM, stepDays: stepDays} + ephemeris, ok := newStarLimbEphemeris(greatestTT, star, limb, inst) + if !ok { + return starLimbScan{}, false + } + outerLeft, okLeft := starOccultationSeparationCrossing(greatestTT, -1, highKM, ephemeris, observer) + outerRight, okRight := starOccultationSeparationCrossing(greatestTT, 1, highKM, ephemeris, observer) + if !okLeft || !okRight { + return starLimbScan{}, false + } + if outerLeft == greatestTT && outerRight == greatestTT { + return scan, true + } + innerLeft, okLeft := starOccultationSeparationCrossing(greatestTT, -1, lowKM, ephemeris, observer) + innerRight, okRight := starOccultationSeparationCrossing(greatestTT, 1, lowKM, ephemeris, observer) + if !okLeft || !okRight { + return starLimbScan{}, false + } + type span struct { + start, end float64 + hiddenAtStart bool + hiddenAtEnd bool + forceStart, forceEnd bool + } + spans := []span{{outerLeft, innerLeft, false, true, true, true}, {innerRight, outerRight, true, false, true, true}} + if innerLeft == greatestTT && innerRight == greatestTT { + spans = []span{{outerLeft, outerRight, false, false, true, true}} + } + total, shortest := 0.0, math.Inf(1) + for _, item := range spans { + total += item.end - item.start + shortest = math.Min(shortest, item.end-item.start) + } + // 步长过大时掠掩两端都可见、中间的遮挡会整段漏掉,所以每段至少保留 64 个样本。 + if limit := shortest / starOccultationLimbMinSamples; shortest > 0 && stepDays > limit { + stepDays = math.Max(limit, starOccultationLimbMinimumStep.Hours()/24) + } + if total/stepDays > starOccultationLimbMaxSamples { + stepDays = total / starOccultationLimbMaxSamples + } + scan.stepDays = stepDays + scan.grazing = len(spans) == 1 + gapAt := func(tt float64) (starLimbState, float64, bool) { + state, ok := ephemeris.stateAt(tt, observer) + return state, state.separation - state.limbRadius, ok + } + var roots []starLimbRoot + for _, item := range spans { + // 剖面范围零宽时带也零长,遮挡状态在该点翻转;不产样本,避免同刻样本。 + if item.end-item.start < starOccultationLimbRootDays { + if item.hiddenAtStart != item.hiddenAtEnd { + roots = append(roots, starLimbRoot{tt: item.start, disappear: item.hiddenAtEnd}) + } + continue + } + count := int(math.Ceil((item.end - item.start) / stepDays)) + if count < 1 { + count = 1 + } + samples := make([]StarOccultationLimbSample, 0, count+1) + previousTT, previousHidden := 0.0, false + var previousGap float64 + for index := 0; index <= count; index++ { + tt := item.start + (item.end-item.start)*float64(index)/float64(count) + state, gap, ok := gapAt(tt) + if !ok { + return starLimbScan{}, false + } + hidden := gap < 0 + // 带两端的遮挡状态由角距保证,消除端点上的舍入抖动。 + if index == 0 && item.forceStart { + hidden = item.hiddenAtStart + } + if index == count && item.forceEnd { + hidden = item.hiddenAtEnd + } + samples = append(samples, StarOccultationLimbSample{ + Time: occultationTTToLocation(tt, location), + SeparationArcsec: state.separation, + LimbRadiusArcsec: state.limbRadius, + MeanRadiusArcsec: state.meanRadius, + PositionAngleDeg: state.positionAngle, + Hidden: hidden, + }) + if index > 0 && hidden != previousHidden { + root := starOccultationLimbBisect(previousTT, tt, previousGap, gap, gapAt) + roots = append(roots, starLimbRoot{tt: root, disappear: hidden}) + } + previousTT, previousHidden, previousGap = tt, hidden, gap + } + scan.segments = append(scan.segments, samples) + } + sort.SliceStable(roots, func(i, j int) bool { return roots[i].tt < roots[j].tt }) + start := math.NaN() + for _, root := range roots { + if root.disappear { + start = root.tt + } else if !math.IsNaN(start) { + scan.hidden = append(scan.hidden, [2]float64{start, root.tt}) + start = math.NaN() + } + } + return scan, true +} + +// starOccultationLimbBisect 在相邻样本间求月缘间隙零点;端点被强制定号时零点可能落在端点上。 +func starOccultationLimbBisect(left, right, leftGap, rightGap float64, gapAt func(float64) (starLimbState, float64, bool)) float64 { + if leftGap*rightGap > 0 { + if math.Abs(leftGap) < math.Abs(rightGap) { + return left + } + return right + } + for i := 0; i < 64 && right-left > starOccultationLimbRootDays; i++ { + mid := (left + right) / 2 + _, gap, ok := gapAt(mid) + if !ok { + break + } + if (gap < 0) == (leftGap < 0) { + left, leftGap = mid, gap + } else { + right = mid + } + } + return (left + right) / 2 +} + +// starOccultationInfoWithLimb 有月缘时按剖面判定是否成掩,掩始、掩终取首次遮挡与最后复现;剖面无值时退回圆月面。 +func starOccultationInfoWithLimb(greatestTT float64, star StarCoordinate, observer Observer, safetyMarginArcsec float64, location *time.Location, limb LunarLimb, inst *Instance) (StarOccultationInfo, bool) { + if limb == nil { + return starOccultationInfoAtGreatest(greatestTT, star, observer, safetyMarginArcsec, location, inst) + } + info, ok := starOccultationGreatestInfo(greatestTT, star, observer, safetyMarginArcsec, location, inst) + if !ok { + return StarOccultationInfo{}, false + } + scan, ok := starOccultationLimbScanAt(greatestTT, star, observer, limb, starOccultationLimbDefaultStep.Hours()/24, location, inst) + if !ok { + return starOccultationInfoAtGreatest(greatestTT, star, observer, safetyMarginArcsec, location, inst) + } + return starOccultationInfoFromScan(info, scan, location), len(scan.hidden) > 0 +} + +func starOccultationInfoFromScan(info StarOccultationInfo, scan starLimbScan, location *time.Location) StarOccultationInfo { + if len(scan.hidden) == 0 { + info.Type = OccultationNone + info.Immersion, info.Emersion = time.Time{}, time.Time{} + info.ContactsComplete = false + info.LunarLimbApplied = true + return info + } + info.Type = OccultationTotal + if scan.grazing { + info.Type = OccultationGrazing + } + info.Immersion = occultationTTToLocation(scan.hidden[0][0], location) + info.Emersion = occultationTTToLocation(scan.hidden[len(scan.hidden)-1][1], location) + info.ContactsComplete = true + info.LunarLimbApplied = true + return info +} + +// StarOccultationLimbProfileWithInstance 沿星迹按月缘剖面精密求解一次恒星月掩,返回全部遮挡区间与带内采样;info 只取掩甚时刻与观测者,须为 UTC 口径结果(不能传 UT1 标注副本),恒星未被遮挡时 Occultation.Type 为 OccultationNone / precise stellar occultation along the track with a lunar limb profile. +func StarOccultationLimbProfileWithInstance(info StarOccultationInfo, star StarCoordinate, limb LunarLimb, options StarOccultationLimbProfileOptions, inst *Instance) (StarOccultationLimbProfileResult, error) { + if limb == nil { + return StarOccultationLimbProfileResult{}, fmt.Errorf("%w: lunar limb profile is required", ErrInvalidOccultationInput) + } + if err := star.Validate(); err != nil { + return StarOccultationLimbProfileResult{}, err + } + if err := info.Observer.Validate(); err != nil { + return StarOccultationLimbProfileResult{}, err + } + if info.Greatest.IsZero() { + return StarOccultationLimbProfileResult{}, fmt.Errorf("%w: greatest occultation time is required", ErrInvalidOccultationInput) + } + if options.Step < 0 { + return StarOccultationLimbProfileResult{}, fmt.Errorf("%w: profile step cannot be negative", ErrInvalidOccultationInput) + } + step := options.Step + if step == 0 { + step = starOccultationLimbDefaultStep + } + if step < starOccultationLimbMinimumStep { + step = starOccultationLimbMinimumStep + } + location := info.Greatest.Location() + greatestTT := occultationTimeToTT(info.Greatest) + base, ok := starOccultationGreatestInfo(greatestTT, star, info.Observer, math.Inf(1), location, inst) + if !ok { + return StarOccultationLimbProfileResult{}, fmt.Errorf("%w: geometry is unavailable at greatest occultation", ErrInvalidOccultationInput) + } + base.TargetID = star.ID + scan, ok := starOccultationLimbScanAt(greatestTT, star, info.Observer, limb, step.Hours()/24, location, inst) + if !ok { + return StarOccultationLimbProfileResult{}, fmt.Errorf("%w: lunar limb profile has no value along the track", ErrInvalidOccultationInput) + } + profile := StarOccultationLimbProfileResult{ + Occultation: starOccultationInfoFromScan(base, scan, location), + Segments: scan.segments, + ReferenceRadiusKM: moonEquatorialRadiusKM, + ReliefMinKM: scan.lowKM, + ReliefMaxKM: scan.highKM, + Step: time.Duration(math.Round(scan.stepDays * 86400 * float64(time.Second))), + } + for _, interval := range scan.hidden { + profile.Hidden = append(profile.Hidden, OccultationInterval{ + Start: occultationTTToLocation(interval[0], location), + End: occultationTTToLocation(interval[1], location), + }) + } + return profile, nil +} diff --git a/basic/occultation_star_limb_test.go b/basic/occultation_star_limb_test.go new file mode 100644 index 0000000..a1569fd --- /dev/null +++ b/basic/occultation_star_limb_test.go @@ -0,0 +1,336 @@ +package basic + +// 恒星月掩按月缘剖面判定成掩:标准结果的掩始、掩终等于精密剖面的首次遮挡与最后复现,常数剖面退化为圆月面。 + +import ( + "math" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +var limbTestAntares = StarCoordinate{ID: "Antares", RA: 247.3516666666667, Dec: -26.431944444444444, + Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -10, ProperMotionDecMasPerYear: -20, ParallaxMas: 24} + +func limbTestAntaresEvent(t *testing.T, lon, lat float64, profile LunarLimb) []StarOccultationInfo { + t.Helper() + start := time.Date(2026, 2, 10, 12, 0, 0, 0, time.UTC) + events, err := FindStarOccultationsWithInstance(start, start.Add(36*time.Hour), limbTestAntares, lon, lat, 0, + OccultationSearchOptions{LunarLimb: profile}, nil) + if err != nil { + t.Fatal(err) + } + return events +} + +func TestStarOccultationLimbGrazeHasMultipleIntervals(t *testing.T) { + const lon, lat = 41.5907, -55.07036 + if circular := limbTestAntaresEvent(t, lon, lat, nil); len(circular) != 0 { + t.Fatalf("circular limb: %d events, want none just outside the circular limit", len(circular)) + } + events := limbTestAntaresEvent(t, lon, lat, limb.Default()) + if len(events) != 1 || !events[0].LunarLimbApplied || events[0].Type != OccultationGrazing { + t.Fatalf("limb events = %+v, want one limb-applied graze", events) + } + profile, err := StarOccultationLimbProfileWithInstance(events[0], limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{}, nil) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) < 2 { + t.Fatalf("graze hidden intervals = %d, want several", len(profile.Hidden)) + } + first, last := profile.Hidden[0], profile.Hidden[len(profile.Hidden)-1] + if !first.Start.Equal(events[0].Immersion) || !last.End.Equal(events[0].Emersion) { + t.Errorf("standard contacts %v–%v differ from profile %v–%v", events[0].Immersion, events[0].Emersion, first.Start, last.End) + } + for index, interval := range profile.Hidden { + if !interval.End.After(interval.Start) || (index > 0 && !interval.Start.After(profile.Hidden[index-1].End)) { + t.Fatalf("interval %d not ordered: %+v", index, profile.Hidden) + } + } + for _, segment := range profile.Segments { + for _, sample := range segment { + inside := profile.Occultation.Immersion.Before(sample.Time) && sample.Time.Before(profile.Occultation.Emersion) + if sample.Hidden && !inside { + t.Fatalf("hidden sample %v outside %v–%v", sample.Time, profile.Occultation.Immersion, profile.Occultation.Emersion) + } + } + } +} + +func TestStarOccultationLimbCanRemoveCircularEvent(t *testing.T) { + const lon, lat = 24.4934, -49.66243 + if circular := limbTestAntaresEvent(t, lon, lat, nil); len(circular) != 1 { + t.Fatalf("circular limb: %d events, want one", len(circular)) + } + if events := limbTestAntaresEvent(t, lon, lat, limb.Default()); len(events) != 0 { + t.Fatalf("limb profile: %d events, want none (star passes a limb valley)", len(events)) + } +} + +func TestStarOccultationConstantLimbMatchesCircular(t *testing.T) { + constant := &recordingLunarLimb{radiusKM: moonEquatorialRadiusKM} + for _, site := range [][2]float64{{41.5907, -55.2}, {24.4934, -50.5}} { + circular := limbTestAntaresEvent(t, site[0], site[1], nil) + limbed := limbTestAntaresEvent(t, site[0], site[1], constant) + if len(circular) != 1 || len(limbed) != 1 { + t.Fatalf("site %v: %d circular, %d constant-limb events", site, len(circular), len(limbed)) + } + for _, pair := range [][2]time.Time{{circular[0].Immersion, limbed[0].Immersion}, {circular[0].Emersion, limbed[0].Emersion}} { + if d := math.Abs(pair[0].Sub(pair[1]).Seconds()); d > 0.01 { + t.Errorf("site %v: constant limb moved a contact by %.4f s", site, d) + } + } + } +} + +func TestStarOccultationLimbProfileRejectsBadInput(t *testing.T) { + info := StarOccultationInfo{Greatest: time.Date(2026, 2, 11, 3, 47, 0, 0, time.UTC), Observer: Observer{Longitude: 41.5907, Latitude: -55.07}} + if _, err := StarOccultationLimbProfileWithInstance(info, limbTestAntares, nil, StarOccultationLimbProfileOptions{}, nil); err == nil { + t.Error("nil limb accepted") + } + if _, err := StarOccultationLimbProfileWithInstance(info, limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{Step: -time.Second}, nil); err == nil { + t.Error("negative step accepted") + } + if _, err := StarOccultationLimbProfileWithInstance(StarOccultationInfo{Observer: info.Observer}, limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{}, nil); err == nil { + t.Error("zero greatest accepted") + } +} + +func TestStarLimbEphemerisMatchesExact(t *testing.T) { + observer := Observer{Longitude: 41.5907, Latitude: -55.07036} + greatest := occultationTimeToTT(time.Date(2026, 2, 11, 3, 47, 20, 0, time.UTC)) + profile := limb.Default() + ephemeris, ok := newStarLimbEphemeris(greatest, limbTestAntares, profile, nil) + if !ok { + t.Fatal("ephemeris failed") + } + for _, offsetMinutes := range []float64{-200, -61.3, -7.7, 0, 3.1, 44.4, 199} { + tt := greatest + offsetMinutes/1440 + got, ok := ephemeris.stateAt(tt, observer) + if !ok { + t.Fatalf("%+.1f min: interpolation failed", offsetMinutes) + } + geoRA, geoDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, nil) + geoDistance := HMoonAwayNWithInstance(tt, -1, nil) + topoRA, topoDec := moonTopocentricApparentRaDec(tt, observer, -1, nil) + topoDistance := topocentricDistanceKM(geoRA, geoDec, geoDistance, observer, TT2UTC(tt)) + starRA, starDec := starApparentRaDec(tt, limbTestAntares, observer, nil) + frame := newLunarLimbFrame(profile, tt, lunarEclipseLimbVector(geoRA, geoDec, geoDistance), nil) + radiusKM, ok := frame.radiusKM(lunarEclipseLimbVector(topoRA, topoDec, topoDistance), lunarEclipseLimbVector(starRA, starDec, 1), false) + if !ok { + t.Fatal("exact limb failed") + } + separation := angularSeparationDegrees(topoRA, topoDec, starRA, starDec) * 3600 + limbRadius := angularSemidiameterArcsec(radiusKM, topoDistance) + if d := math.Abs(got.separation - separation); d > 1e-3 { + t.Errorf("%+.1f min: separation off by %.2g arcsec", offsetMinutes, d) + } + if d := math.Abs(got.limbRadius - limbRadius); d > 2e-3 { + t.Errorf("%+.1f min: limb radius off by %.2g arcsec", offsetMinutes, d) + } + } +} + +func TestStarOccultationLimbTypes(t *testing.T) { + deep := limbTestAntaresEvent(t, 41.5907, -57.0, limb.Default()) + if len(deep) != 1 || deep[0].Type != OccultationTotal { + t.Fatalf("deep occultation = %+v, want total", deep) + } + info := StarOccultationInfo{Greatest: time.Date(2026, 2, 11, 3, 47, 15, 0, time.UTC), Observer: Observer{Longitude: 41.5907, Latitude: -55.07036 + 0.4/111.2}} + profile, err := StarOccultationLimbProfileWithInstance(info, limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{}, nil) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) == 0 || len(profile.Segments) != 1 || profile.Occultation.Type != OccultationGrazing { + t.Fatalf("0.4 km outside the circular limit: %d intervals, %d segments, type %v; want a limb graze", len(profile.Hidden), len(profile.Segments), profile.Occultation.Type) + } + info.Observer.Latitude = -55.07036 + 0.8/111.2 + profile, err = StarOccultationLimbProfileWithInstance(info, limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{}, nil) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) != 0 || profile.Occultation.Type != OccultationNone || !profile.Occultation.Immersion.IsZero() || profile.Occultation.ContactsComplete { + t.Fatalf("missed graze reported %d intervals, type %v", len(profile.Hidden), profile.Occultation.Type) + } +} + +func TestStarOccultationLimbProfileLargeStepKeepsGraze(t *testing.T) { + events := limbTestAntaresEvent(t, 41.5907, -55.07036, limb.Default()) + if len(events) != 1 { + t.Fatal("graze missing") + } + profile, err := StarOccultationLimbProfileWithInstance(events[0], limbTestAntares, limb.Default(), StarOccultationLimbProfileOptions{Step: time.Hour}, nil) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) == 0 || profile.Step >= time.Hour { + t.Fatalf("hour step: %d intervals with effective step %v, want the graze kept", len(profile.Hidden), profile.Step) + } +} + +func TestStarLimbEphemerisFrameIsOrthonormal(t *testing.T) { + greatest := occultationTimeToTT(time.Date(2026, 2, 11, 3, 47, 20, 0, time.UTC)) + ephemeris, ok := newStarLimbEphemeris(greatest, limbTestAntares, limb.Default(), nil) + if !ok { + t.Fatal("ephemeris failed") + } + for _, offsetMinutes := range []float64{-230, -97.3, 0, 41.1, 233} { + axisX, axisZ, ok := interpolateLocalEphemerisVectors(ephemeris.axes, greatest+offsetMinutes/1440) + if !ok { + t.Fatal("interpolation failed") + } + axisZ, _ = lunarLimbNormalize(axisZ) + axisX, _ = lunarLimbNormalize(lunarLimbReject(axisX, axisZ)) + if d := math.Abs(lunarLimbDot(axisX, axisZ)); d > 1e-12 { + t.Errorf("%+.1f min: axes not orthogonal (%.2g)", offsetMinutes, d) + } + } +} + +func TestPlanetOccultationLimbHandlesGraze(t *testing.T) { + start := time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC) + end := time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC) + const lon, edge = -30.072, 43.562592 + find := func(lat float64, profile LunarLimb) []PlanetOccultationInfo { + events, err := FindPlanetOccultationsWithInstance(start, end, OccultationSaturn, lon, lat, 0, OccultationSearchOptions{LunarLimb: profile}, nil) + if err != nil { + t.Fatal(err) + } + return events + } + if circular := find(edge, nil); len(circular) != 0 { + t.Fatalf("circular edge = %+v, want no occultation just outside the circular graze line", circular) + } + limbed := find(edge, limb.Default()) + if len(limbed) != 1 || !limbed[0].LunarLimbApplied || limbed[0].Type == OccultationGrazing || + !limbed[0].ExternalEmersion.After(limbed[0].ExternalImmersion) { + t.Fatalf("limb at circular graze line = %+v, want a partial occultation with positive duration", limbed) + } + if far := find(edge+6/111.2, limb.Default()); len(far) != 0 { + t.Errorf("6 km outside the limit the limb still reports %d events", len(far)) + } + constant := &recordingLunarLimb{radiusKM: moonEquatorialRadiusKM} + for _, dkm := range []float64{-3, -1} { + circular, withConstant := find(edge+dkm/111.2, nil), find(edge+dkm/111.2, constant) + if len(circular) != 1 || len(withConstant) != 1 || circular[0].Type != withConstant[0].Type { + t.Fatalf("%+.0f km: circular %v vs constant limb %v", dkm, circular, withConstant) + } + for _, pair := range [][2]time.Time{{circular[0].ExternalImmersion, withConstant[0].ExternalImmersion}, {circular[0].ExternalEmersion, withConstant[0].ExternalEmersion}} { + if d := math.Abs(pair[0].Sub(pair[1]).Seconds()); d > 0.05 { + t.Errorf("%+.0f km: constant limb moved a contact by %.3f s", dkm, d) + } + } + } +} + +func TestPlanetLimbEphemerisMatchesExact(t *testing.T) { + config, _ := planetOccultationConfigFor(OccultationSaturn, nil) + observer := Observer{Longitude: -30.072, Latitude: 16.21} + greatest := occultationTimeToTT(time.Date(2024, time.August, 21, 2, 45, 0, 0, time.UTC)) + ephemeris, ok := newPlanetLimbEphemeris(greatest, config, limb.Default(), nil) + if !ok { + t.Fatal("ephemeris failed") + } + target := planetOccultationLimbTarget(config, observer) + for _, offsetMinutes := range []float64{-200, -33.3, 0, 7.7, 199} { + tt := greatest + offsetMinutes/1440 + state := planetOccultationStateAt(tt, config, &observer, -1, nil) + for _, internal := range []bool{false, true} { + got, ok := ephemeris.circularGap(tt, observer, internal) + want := state.externalContactMetric + if internal { + want = state.internalContactMetric + } + if !ok || math.Abs(got-want) > 2e-3 { + t.Errorf("%+.1f min internal=%v: circular gap %.4f\" want %.4f\"", offsetMinutes, internal, got, want) + } + gotLimb, okLimb := ephemeris.limbGap(tt, observer, internal) + wantLimb, okExact := occultationLimbGap(tt, limb.Default(), observer, target, internal, nil) + if !okLimb || !okExact || math.Abs(gotLimb-wantLimb) > 3e-3 { + t.Errorf("%+.1f min internal=%v: limb gap %.4f\" want %.4f\"", offsetMinutes, internal, gotLimb, wantLimb) + } + } + } +} + +// occultationLimbGap 逐点精确求站心月缘接触间隙(角秒),作插值表的对拍基准。 +func occultationLimbGap(tt float64, limb LunarLimb, observer Observer, target occultationLimbTarget, internal bool, inst *Instance) (float64, bool) { + geoRA, geoDec := HMoonGeocentricApparentRaDecNWithInstance(tt, -1, inst) + geoDistance := HMoonAwayNWithInstance(tt, -1, inst) + topoRA, topoDec := moonTopocentricApparentRaDec(tt, observer, -1, inst) + ut := TT2UTC(tt) + topoDistance := topocentricDistanceKM(geoRA, geoDec, geoDistance, observer, ut) + ra, dec, distance, radius, ok := target(tt) + if !ok || !finite(topoRA) || !finite(topoDec) || !finite(topoDistance) || topoDistance <= 0 { + return 0, false + } + if distance > 0 { + ra, dec = TopocentricRaDec(ra, dec, observer.Latitude, observer.Longitude, ut, distance/angularDiameterAstronomicalUnitKM, observer.Height) + ra = normalizeRA(ra) + } + frame := newLunarLimbFrame(limb, tt, lunarEclipseLimbVector(geoRA, geoDec, geoDistance), inst) + view, ok := frame.viewToward(lunarEclipseLimbVector(topoRA, topoDec, topoDistance), lunarEclipseLimbVector(ra, dec, 1)) + if !ok { + return 0, false + } + contact := lunarLimbContact{ + frame: &frame, + view: view, + distanceKM: topoDistance, + separation: angularSeparationDegrees(topoRA, topoDec, ra, dec) * rad, + targetRadius: radius / angularDiameterArcsecPerRadian, + } + if internal { + inner, ok := contact.innerRadius(true) + if !ok { + return 0, false + } + return (contact.separation - math.Abs(inner-contact.targetRadius)) * angularDiameterArcsecPerRadian, true + } + outer, ok := contact.outerRadius() + if !ok { + return 0, false + } + return (contact.separation - outer - contact.targetRadius) * angularDiameterArcsecPerRadian, true +} + +// constantRangedLunarLimb 常数半径且报告零宽范围,起伏带退化成一点。 +type constantRangedLunarLimb struct{ radiusKM float64 } + +func (c constantRangedLunarLimb) LimbRadiusKM(lon, lat, angle float64) (float64, bool) { + return c.radiusKM, true +} + +func (c constantRangedLunarLimb) RadiusRangeKM() (float64, float64) { return c.radiusKM, c.radiusKM } + +func (c constantRangedLunarLimb) AngleStepDeg() float64 { return 0.5 } + +func TestStarOccultationLimbProfileZeroWidthRangeKeepsSamplesOrdered(t *testing.T) { + profileLimb := constantRangedLunarLimb{radiusKM: moonEquatorialRadiusKM} + circular := limbTestAntaresEvent(t, 41.5907, -57.0, nil) + if len(circular) != 1 { + t.Fatalf("circular events = %d, want one", len(circular)) + } + profile, err := StarOccultationLimbProfileWithInstance(circular[0], limbTestAntares, profileLimb, StarOccultationLimbProfileOptions{}, nil) + if err != nil { + t.Fatal(err) + } + for index, segment := range profile.Segments { + for k := 1; k < len(segment); k++ { + if !segment[k].Time.After(segment[k-1].Time) { + t.Fatalf("segment %d sample %d: time %v not after %v", index, k, segment[k].Time, segment[k-1].Time) + } + } + } + if len(profile.Hidden) != 1 || profile.Occultation.Type != OccultationTotal { + t.Fatalf("zero-width range: %d intervals, type %v; want one total occultation", len(profile.Hidden), profile.Occultation.Type) + } + for _, pair := range [][2]time.Time{{profile.Hidden[0].Start, circular[0].Immersion}, {profile.Hidden[0].End, circular[0].Emersion}} { + if d := math.Abs(pair[0].Sub(pair[1]).Seconds()); d > 0.05 { + t.Errorf("zero-width range moved a contact by %.3f s", d) + } + } +} diff --git a/basic/occultation_station_correction.go b/basic/occultation_station_correction.go index 8ee6447..90d8858 100644 --- a/basic/occultation_station_correction.go +++ b/basic/occultation_station_correction.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) // occultationStationBoundarySample is the result of correcting one geocentric @@ -322,8 +324,8 @@ func occultationStationCorrectContactPoint( ), true } const coordinateStepDeg = 0.005 - plusLongitude := context.stateAt(normalizeLongitude(longitude+coordinateStepDeg), latitude) - minusLongitude := context.stateAt(normalizeLongitude(longitude-coordinateStepDeg), latitude) + plusLongitude := context.stateAt(geodata.NormalizeLongitude(longitude+coordinateStepDeg), latitude) + minusLongitude := context.stateAt(geodata.NormalizeLongitude(longitude-coordinateStepDeg), latitude) plusLatitude := context.stateAt(longitude, math.Min(89.999999, latitude+coordinateStepDeg)) minusLatitude := context.stateAt(longitude, math.Max(-89.999999, latitude-coordinateStepDeg)) if !plusLongitude.valid || !minusLongitude.valid || !plusLatitude.valid || !minusLatitude.valid { @@ -344,7 +346,7 @@ func occultationStationCorrectContactPoint( deltaX *= scale deltaY *= scale } - longitude = normalizeLongitude(longitude + deltaX/cosLatitude) + longitude = geodata.NormalizeLongitude(longitude + deltaX/cosLatitude) latitude = math.Max(-89.999999, math.Min(89.999999, latitude+deltaY)) } state := context.stateAt(longitude, latitude) @@ -436,8 +438,8 @@ func occultationStationCorrectEnvelopePoint( const coordinateStepDeg = 0.002 cosLatitude := math.Max(0.05, math.Cos(latitude*rad)) - plusLongitude := evaluate(normalizeLongitude(longitude+coordinateStepDeg/cosLatitude), latitude) - minusLongitude := evaluate(normalizeLongitude(longitude-coordinateStepDeg/cosLatitude), latitude) + plusLongitude := evaluate(geodata.NormalizeLongitude(longitude+coordinateStepDeg/cosLatitude), latitude) + minusLongitude := evaluate(geodata.NormalizeLongitude(longitude-coordinateStepDeg/cosLatitude), latitude) plusLatitude := evaluate(longitude, math.Min(89.999999, latitude+coordinateStepDeg)) minusLatitude := evaluate(longitude, math.Max(-89.999999, latitude-coordinateStepDeg)) if !plusLongitude.valid || !minusLongitude.valid || !plusLatitude.valid || !minusLatitude.valid { @@ -465,7 +467,7 @@ func occultationStationCorrectEnvelopePoint( currentNorm := residualNorm(state) accepted := false for damping := 1.0; damping >= 1.0/128; damping /= 2 { - candidateLongitude := normalizeLongitude(longitude + damping*deltaX/cosLatitude) + candidateLongitude := geodata.NormalizeLongitude(longitude + damping*deltaX/cosLatitude) candidateLatitude := math.Max(-89.999999, math.Min(89.999999, latitude+damping*deltaY)) if occultationPathDistanceKMValues( seed.Longitude, seed.Latitude, candidateLongitude, candidateLatitude, @@ -845,7 +847,7 @@ func occultationStationCorrectEnvelopeArc( residual, jacobian, ok := occultationStationEnvelopeJacobian(coordinates, referenceTT, jacobianCache, model) if iterationCache == cache && ok { tt := referenceTT + coordinates[2]/occultationStationEnvelopeTimeScale - residual, ok = model.residual(cache.evaluation(tt), normalizeLongitude(coordinates[0]), coordinates[1]) + residual, ok = model.residual(cache.evaluation(tt), geodata.NormalizeLongitude(coordinates[0]), coordinates[1]) } if !ok { if jacobianCache != cache { @@ -959,7 +961,7 @@ func occultationStationValidEnvelopeArcState( iterations int, ) (occultationStationEnvelopeArcState, int, bool) { tt := referenceTT + coordinates[2]/occultationStationEnvelopeTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := cache.evaluation(tt) state := evaluation.center.stateAt(longitude, latitude) residual, ok := model.residual(evaluation, longitude, latitude) @@ -989,7 +991,7 @@ func occultationStationEnvelopeJacobian( model occultationStationEnvelopeModel, ) ([2]float64, [2][3]float64, bool) { tt := referenceTT + coordinates[2]/occultationStationEnvelopeTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := cache.evaluation(tt) // 点源目标(恒星)没有盘面半径项,接触度量可用解析速率;有限盘面行星保持中心差分。 residualAt := model.residual @@ -1008,7 +1010,7 @@ func occultationStationEnvelopeJacobian( shiftedTT := referenceTT + shifted[2]/occultationStationEnvelopeTimeScale shiftedEvaluation := cache.evaluation(shiftedTT) shiftedResidual, shiftedOK := residualAt( - shiftedEvaluation, normalizeLongitude(shifted[0]), shifted[1], + shiftedEvaluation, geodata.NormalizeLongitude(shifted[0]), shifted[1], ) if !shiftedOK { return [2]float64{}, [2][3]float64{}, false @@ -1174,6 +1176,74 @@ func occultationStationDeduplicateEnvelopePoints(points []OccultationPathPoint) return result } +// occultationStationEnvelopeTieBreak 是同时间戳平台内的最大时间步长。追踪点时间来自 JD 的 +// float64,现代历元分辨率约 40 µs;1 µs 远低于这一量化,只用于恢复真实的先后次序。 +const occultationStationEnvelopeTieBreak = time.Microsecond + +// occultationStationCollapseTimePlateaus 消除包络追踪中"不同位置、同一时间戳"的平台。 +// 平台是 JD 量化的产物:时间导数在折返顶点或结点附近趋零时,相邻追踪点落进同一量化格。 +// 若把平台当成真折返切开,平台内的边不属于任何严格单调段,轮廓在此断开(Saturn +// 2025-01-05 顶点处断开 2.8 km,结点处断开时端点被丢弃)。这里沿进入平台的时间方向 +// (平台在首部时取离开方向)为平台各点补上亚量化的递增偏移:非折返平台连成一段, +// 折返平台的末点成为两段共享的顶点,所有点与边都被保留。整条曲线同一时间戳时没有方向,原样返回。 +func occultationStationCollapseTimePlateaus(points []OccultationPathPoint) []OccultationPathPoint { + sign := func(first, second time.Time) int { + switch { + case second.After(first): + return 1 + case second.Before(first): + return -1 + } + return 0 + } + var result []OccultationPathPoint + for start := 0; start < len(points); { + end := start + 1 + for end < len(points) && points[end].Time.Equal(points[start].Time) { + end++ + } + if end-start == 1 { + start = end + continue + } + direction := 0 + if start > 0 { + direction = sign(points[start-1].Time, points[start].Time) + } + if direction == 0 && end < len(points) { + direction = sign(points[end-1].Time, points[end].Time) + } + if direction == 0 { + return points + } + step := occultationStationEnvelopeTieBreak + // 不折返时偏移不能越过下一点,否则平台出口会变成假折返。 + if end < len(points) && sign(points[end-1].Time, points[end].Time) == direction { + if gap := points[end].Time.Sub(points[start].Time) * time.Duration(direction) / time.Duration(end-start); gap < step { + step = gap + } + } + if result == nil { + result = append([]OccultationPathPoint(nil), points...) + } + if step <= 0 { + // 相邻点只差纳秒级时无法再细分,保留平台首点即可,丢弃的点与首点同一时刻。 + result = append(result[:start+1], result[end:]...) + points = result + start++ + continue + } + for index := start + 1; index < end; index++ { + result[index].Time = result[start].Time.Add(time.Duration(direction*(index-start)) * step) + } + start = end + } + if result == nil { + return points + } + return result +} + func occultationStationSplitEnvelopeAtTimeFolds( points []OccultationPathPoint, ) [][]OccultationPathPoint { @@ -1193,7 +1263,7 @@ func occultationStationSplitEnvelopeAtTimeFolds( } normalized = append(normalized, point) } - points = normalized + points = occultationStationCollapseTimePlateaus(normalized) if len(points) < 2 { return nil } @@ -1291,8 +1361,8 @@ func occultationStationCorrectHorizonContactPoint( } const coordinateStepDeg = 0.002 - plusLongitude := context.stateAt(normalizeLongitude(longitude+coordinateStepDeg), latitude) - minusLongitude := context.stateAt(normalizeLongitude(longitude-coordinateStepDeg), latitude) + plusLongitude := context.stateAt(geodata.NormalizeLongitude(longitude+coordinateStepDeg), latitude) + minusLongitude := context.stateAt(geodata.NormalizeLongitude(longitude-coordinateStepDeg), latitude) plusLatitude := context.stateAt(longitude, math.Min(89.999999, latitude+coordinateStepDeg)) minusLatitude := context.stateAt(longitude, math.Max(-89.999999, latitude-coordinateStepDeg)) if !plusLongitude.valid || !minusLongitude.valid || !plusLatitude.valid || !minusLatitude.valid { @@ -1321,7 +1391,7 @@ func occultationStationCorrectHorizonContactPoint( currentNorm := residualNorm(state) accepted := false for damping := 1.0; damping >= 1.0/64; damping /= 2 { - candidateLongitude := normalizeLongitude(longitude + damping*deltaX/cosLatitude) + candidateLongitude := geodata.NormalizeLongitude(longitude + damping*deltaX/cosLatitude) candidateLatitude := math.Max(-89.999999, math.Min(89.999999, latitude+damping*deltaY)) if occultationPathDistanceKMValues( seed.Longitude, seed.Latitude, candidateLongitude, candidateLatitude, @@ -1633,7 +1703,7 @@ func occultationStationSurfaceVector(longitude, latitude float64) occultationPat x: cosU * cosLongitude, y: cosU * sinLongitude, z: polarRatio * sinU, - }, occultationPathEarthEquatorialRadiusKM) + }, geodata.EarthEquatorialRadiusKM) } func occultationStationOffsetSurfaceVector( @@ -1645,11 +1715,11 @@ func occultationStationOffsetSurfaceVector( if metric <= 0 || !finite(metric) { return seedFixed } - return occultationPathScale(point, occultationPathEarthEquatorialRadiusKM/metric) + return occultationPathScale(point, geodata.EarthEquatorialRadiusKM/metric) } func occultationStationGeodetic(fixed occultationPathVector) (float64, float64) { - return normalizeLongitude(math.Atan2(fixed.y, fixed.x) / rad), occultationPathGeodeticLatitude(fixed) + return geodata.NormalizeLongitude(math.Atan2(fixed.y, fixed.x) / rad), occultationPathGeodeticLatitude(fixed) } func occultationStationInverseEarthRotation( diff --git a/basic/occultation_station_correction_test.go b/basic/occultation_station_correction_test.go index a913e22..8a6c9ff 100644 --- a/basic/occultation_station_correction_test.go +++ b/basic/occultation_station_correction_test.go @@ -8,51 +8,66 @@ import ( ) func TestOccultationStationEnvelopeEqualTimeBranches(t *testing.T) { + // 同时间戳平台是 JD 量化的产物:平台内的点与边都必须保留,只有真折返才切段。 for _, test := range []struct { name string times []int + unit time.Duration segments int }{ - {"leading plateau", []int{0, 0, 0, 1, 2}, 1}, - {"interior plateau", []int{0, 1, 1, 1, 2}, 2}, - {"trailing plateau", []int{0, 1, 1, 1}, 1}, - {"entire plateau", []int{0, 0, 0}, 0}, - {"reverse branches", []int{2, 1, 1, 1, 0}, 2}, - {"ordinary fold", []int{0, 1, 2, 1, 0}, 2}, + {"leading plateau", []int{0, 0, 0, 1, 2}, time.Second, 1}, + {"interior plateau", []int{0, 1, 1, 1, 2}, time.Second, 1}, + {"trailing plateau", []int{0, 1, 1, 1}, time.Second, 1}, + {"entire plateau", []int{0, 0, 0}, time.Second, 0}, + {"reverse branches", []int{2, 1, 1, 1, 0}, time.Second, 1}, + {"ordinary fold", []int{0, 1, 2, 1, 0}, time.Second, 2}, + // Saturn 2025-01-05:时间折返的顶点恰好落进同一量化格。 + {"plateau at fold", []int{0, 1, 2, 2, 1, 0}, time.Second, 2}, + // 平台出口只晚 1.5 µs:亚量化偏移不能越过出口点。 + {"tight plateau exit", []int{0, 4, 4, 4, 5}, 375 * time.Nanosecond, 1}, } { t.Run(test.name, func(t *testing.T) { points := make([]OccultationPathPoint, len(test.times)) - for index, seconds := range test.times { - points[index] = OccultationPathPoint{Time: time.Unix(int64(seconds), 0), Longitude: float64(index)} + for index, value := range test.times { + points[index] = OccultationPathPoint{ + Time: time.Unix(1000, 0).Add(time.Duration(value) * test.unit), Longitude: float64(index), + } } segments := occultationStationSplitEnvelopeAtTimeFolds(points) if len(segments) != test.segments { t.Fatalf("segments=%d, want %d", len(segments), test.segments) } + if test.segments == 0 { + return + } + kept := make([]bool, len(points)) + edges := map[[2]int]bool{} for _, segment := range segments { - for index := 1; index < len(segment); index++ { - if !segment[index].Time.After(segment[index-1].Time) { - t.Fatalf("non-increasing segment: %+v", segment) + for index, point := range segment { + // 平台点只允许亚量化(< 1 ms)的时间调整,位置不变。 + original := points[int(point.Longitude)] + if delta := point.Time.Sub(original.Time); delta < -time.Millisecond || delta > time.Millisecond { + t.Fatalf("point %v moved by %v", point.Longitude, delta) + } + kept[int(point.Longitude)] = true + if index > 0 { + if !point.Time.After(segment[index-1].Time) { + t.Fatalf("non-increasing segment: %+v", segment) + } + first, second := int(segment[index-1].Longitude), int(point.Longitude) + if first > second { + first, second = second, first + } + edges[[2]int{first, second}] = true } } } - // Every edge with elapsed time must survive the split unchanged. - for index := 1; index < len(points); index++ { - first, second := points[index-1], points[index] - if first.Time.Equal(second.Time) { - continue + for index := range points { + if !kept[index] { + t.Fatalf("point %d dropped: %+v", index, segments) } - if first.Time.After(second.Time) { - first, second = second, first - } - found := false - for _, segment := range segments { - for offset := 1; offset < len(segment); offset++ { - found = found || segment[offset-1] == first && segment[offset] == second - } - } - if !found { - t.Fatalf("lost monotone edge %d", index) + if index > 0 && !edges[[2]int{index - 1, index}] { + t.Fatalf("lost edge %d-%d: %+v", index-1, index, segments) } } }) diff --git a/basic/occultation_station_envelope.go b/basic/occultation_station_envelope.go index 1323a3a..160a1b1 100644 --- a/basic/occultation_station_envelope.go +++ b/basic/occultation_station_envelope.go @@ -3,6 +3,8 @@ package basic import ( "math" "time" + + "b612.me/astro/internal/geodata" ) // occultationStationDensifyContours applies the map's projection metric after @@ -68,11 +70,8 @@ func occultationStationDensifyContours( } func occultationStationProjectedSpacingKM(first, second OccultationPathPoint) float64 { - const maxLatitude = 85.05112878 - firstLatitude := math.Max(-maxLatitude, math.Min(maxLatitude, first.Latitude)) * rad - secondLatitude := math.Max(-maxLatitude, math.Min(maxLatitude, second.Latitude)) * rad - longitudeDelta := math.Remainder(second.Longitude-first.Longitude, 360) * rad - firstY := math.Log(math.Tan(math.Pi/4 + firstLatitude/2)) - secondY := math.Log(math.Tan(math.Pi/4 + secondLatitude/2)) - return 6378.1366 * math.Hypot(longitudeDelta, secondY-firstY) + return geodata.WebMercatorDistanceKM( + geodata.GeoPoint{Longitude: first.Longitude, Latitude: first.Latitude}, + geodata.GeoPoint{Longitude: second.Longitude, Latitude: second.Latitude}, + ) } diff --git a/basic/orbit_coordinates.go b/basic/orbit_coordinates.go index dd0244f..f55d89a 100644 --- a/basic/orbit_coordinates.go +++ b/basic/orbit_coordinates.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -122,18 +121,19 @@ func OrbitAstrometricGeocentricEquatorialJ2000(jde float64, elements OrbitElemen return orbitVectorToEquatorial(vector) } -// OrbitApparentGeocentricEcliptic 返回光行时与章动修正后的地心视黄道坐标,单位度/AU。 +// OrbitApparentGeocentricEcliptic 返回光行时、周年光行差与章动修正后的地心视黄道坐标,单位度/AU。 func OrbitApparentGeocentricEcliptic(jde float64, elements OrbitElements) (lon, lat, distance float64) { vectorDate := eclipticVectorAtReferenceEpoch(OrbitAstrometricGeocentricXYZJ2000(jde, elements), orbitReferenceJD, jde) lon, lat, distance = orbitVectorToEcliptic(vectorDate) if math.IsNaN(lon) { return math.NaN(), math.NaN(), math.NaN() } - lon = Limit360(lon + Nutation2000Bi(jde)) - return lon, lat, distance + aberrationLon, aberrationLat := GXCLo(lon, lat, jde), GXCBo(lon, lat, jde) + lon = Limit360(lon + aberrationLon/3600 + Nutation2000Bi(jde)) + return lon, lat + aberrationLat/3600, distance } -// OrbitApparentGeocentricEquatorial 返回光行时与章动修正后的地心视赤道坐标,单位度/AU。 +// OrbitApparentGeocentricEquatorial 返回光行时、周年光行差与章动修正后的地心视赤道坐标,单位度/AU。 func OrbitApparentGeocentricEquatorial(jde float64, elements OrbitElements) (ra, dec, distance float64) { lon, lat, distance := OrbitApparentGeocentricEcliptic(jde, elements) if math.IsNaN(lon) { @@ -143,7 +143,7 @@ func OrbitApparentGeocentricEquatorial(jde float64, elements OrbitElements) (ra, return ra, dec, distance } -// OrbitApparentTopocentricEquatorial 返回光行时、章动与站心修正后的视赤道坐标,单位度/AU。 +// OrbitApparentTopocentricEquatorial 返回光行时、周年光行差、章动与站心修正后的视赤道坐标,单位度/AU。 func OrbitApparentTopocentricEquatorial(jde, observerLon, observerLat, observerHeight float64, elements OrbitElements) (ra, dec, distance float64) { geocentricRA, geocentricDec, geocentricDistance := OrbitApparentGeocentricEquatorial(jde, elements) if math.IsNaN(geocentricRA) { @@ -159,16 +159,20 @@ func OrbitApparentTopocentricEquatorial(jde, observerLon, observerLat, observerH return orbitVectorToEquatorial(topocentricVector) } -func earthHeliocentricVectorOfDate(jde float64) Vector3 { +func earthHeliocentricVectorOfDate(jde float64, instances ...*Instance) Vector3 { + inst := instanceValue(instances) + return eclipticCartesian( - planet.WherePlanet(-1, 0, jde), - planet.WherePlanet(-1, 1, jde), - planet.WherePlanet(-1, 2, jde), + PlanetCoordinateWithInstance(-1, 0, jde, inst), + PlanetCoordinateWithInstance(-1, 1, jde, inst), + PlanetCoordinateWithInstance(-1, 2, jde, inst), ) } -func earthHeliocentricVectorJ2000(jde float64) Vector3 { - return eclipticVectorAtReferenceEpoch(earthHeliocentricVectorOfDate(jde), jde, orbitReferenceJD) +func earthHeliocentricVectorJ2000(jde float64, instances ...*Instance) Vector3 { + inst := instanceValue(instances) + + return eclipticVectorAtReferenceEpoch(earthHeliocentricVectorOfDate(jde, inst), jde, orbitReferenceJD) } func orbitVectorToEcliptic(vector Vector3) (lon, lat, distance float64) { diff --git a/basic/orbit_kepler.go b/basic/orbit_kepler.go index 70988fd..dccfe43 100644 --- a/basic/orbit_kepler.go +++ b/basic/orbit_kepler.go @@ -209,11 +209,9 @@ func orbitParabolicTrueAnomalyAndRadius(jd float64, elements OrbitElements) (tru return math.NaN(), math.NaN(), elements, false } w := 1.5 * gaussianGravitationalConstant * (jd - elements.TpJD) / (math.Sqrt2 * math.Pow(elements.Q, 1.5)) - y := math.Cbrt(w + math.Sqrt(w*w+1)) - if y == 0 { - return 0, elements.Q, elements, true - } - d := y - 1/y + // tan(v/2) 是 w 的奇函数;按 |w| 求再补符号,避免 w 很负时 w+sqrt(w²+1) 相消为 0。 + y := math.Cbrt(math.Abs(w) + math.Sqrt(w*w+1)) + d := math.Copysign(y-1/y, w) trueAnomaly = 2 * math.Atan(d) radius = elements.Q * (1 + d*d) resolved = elements diff --git a/basic/orbit_test.go b/basic/orbit_test.go index 700f22a..33ed6ba 100644 --- a/basic/orbit_test.go +++ b/basic/orbit_test.go @@ -124,3 +124,21 @@ func TestOrbitInvalidEllipticElementsReturnNaN(t *testing.T) { } } } + +func TestParabolicRadiusMonotonicFarBeforePerihelion(t *testing.T) { + elements := OrbitElements{Q: 0.005, E: 1, I: 0, Omega: 0, W: 0, TpJD: orbitReferenceJD} + previous := math.Inf(1) + for years := 1000.0; years <= 20000; years += 1000 { + before := OrbitHeliocentricXYZJ2000(orbitReferenceJD-years*365.25, elements) + after := OrbitHeliocentricXYZJ2000(orbitReferenceJD+years*365.25, elements) + rb := math.Hypot(math.Hypot(before[0], before[1]), before[2]) + ra := math.Hypot(math.Hypot(after[0], after[1]), after[2]) + if math.Abs(rb-ra) > 1e-9*ra { + t.Fatalf("%v yr: radius before %.9f, after %.9f", years, rb, ra) + } + if previous != math.Inf(1) && ra <= previous { + t.Fatalf("%v yr: radius %.9f not increasing from %.9f", years, ra, previous) + } + previous = ra + } +} diff --git a/basic/orbital_events.go b/basic/orbital_events.go new file mode 100644 index 0000000..ecd085a --- /dev/null +++ b/basic/orbital_events.go @@ -0,0 +1,191 @@ +package basic + +import "math" + +// OrbitalEventKind 轨道事件类型 / orbital event kind. +type OrbitalEventKind int + +const ( + OrbitalPerihelion OrbitalEventKind = iota // 近日点 / perihelion + OrbitalAphelion // 远日点 / aphelion + OrbitalAscendingNode // 升交点 / ascending node + OrbitalDescendingNode // 降交点 / descending node +) + +// OrbitalEvent 轨道事件:TT 儒略日与日心距(AU)/ orbital event at TT JDE with heliocentric distance in AU. +type OrbitalEvent struct { + JDE float64 + Kind OrbitalEventKind + DistanceAU float64 +} + +// 恒星周期(日),只用于定采样步长和支配窗口。 +var orbitalPeriodDays = map[SkyBody]float64{ + SkyMercury: 87.969, SkyVenus: 224.701, SkyMars: 686.98, SkyJupiter: 4332.59, + SkySaturn: 10759.22, SkyUranus: 30688.5, SkyNeptune: 60182, +} + +// SkyEarth 地球,只用于近日点、远日点查询 / Earth, valid only for apsis queries. +const SkyEarth SkyBody = -1 + +func orbitalIndex(body SkyBody) (int, float64, bool) { + if body == SkyEarth { + return -1, 365.256, true + } + period, ok := orbitalPeriodDays[body] + if !ok { + return 0, 0, false + } + return int(body-SkyMercury) + 1, period, true +} + +// PlanetApsidesWithInstance 行星或地球(SkyEarth)在 [startJDE, endJDE] 内的近日点与远日点(TT)/ perihelia and aphelia in the TT window. +// +// 极值取日心距本身;太阳绕质心的反射运动会让天王星、海王星的日心距在近日点附近出现多个小极值, +// 这里只保留前后 0.3 个周期内最深的一个,与吻切轨道近日点可相差数年。 +func PlanetApsidesWithInstance(body SkyBody, startJDE, endJDE float64, inst *Instance) []OrbitalEvent { + index, period, ok := orbitalIndex(body) + if !ok || !finite(startJDE) || !finite(endJDE) || endJDE < startJDE { + return nil + } + radius := func(t float64) float64 { return PlanetCoordinateNWithInstance(index, 2, t, -1, inst) } + step := period / 200 + from, to := startJDE-period/2, endJDE+period/2 + // 与 skyGrid 同一上限,同时挡住 to-from 溢出为 Inf。 + ratio := math.Ceil((to - from) / step) + if !(ratio < skyGridMaxIntervals) { + return nil + } + n := int(ratio) + 1 + window := int(math.Ceil(0.3 * period / step)) + ring := make([]float64, 2*window+1) + sample := func(j int) float64 { return ring[j%len(ring)] } + var out []OrbitalEvent + check := func(i int) { + for _, aphelion := range []bool{false, true} { + lo, hi := i-window, i+window + if lo < 0 { + lo = 0 + } + if hi > n-1 { + hi = n - 1 + } + if !orbitalDominant(sample, i, lo, hi, aphelion) { + continue + } + center := from + float64(i)*step + t, r := orbitalGoldenExtremum(radius, center-step, center+step, aphelion) + if t < startJDE || t > endJDE { + continue + } + kind := OrbitalPerihelion + if aphelion { + kind = OrbitalAphelion + } + out = append(out, OrbitalEvent{JDE: t, Kind: kind, DistanceAU: r}) + } + } + for k := 0; k < n; k++ { + ring[k%len(ring)] = radius(from + float64(k)*step) + if i := k - window; i >= 1 && i <= n-2 { + check(i) + } + } + tail := n - window + if tail < 1 { + tail = 1 + } + for i := tail; i <= n-2; i++ { + check(i) + } + return out +} + +// orbitalDominant 报告第 i 个样本是否为 [lo, hi] 内的唯一最值(并列时只取最左侧)。 +func orbitalDominant(value func(int) float64, i, lo, hi int, maximize bool) bool { + better := func(a, b float64) bool { + if maximize { + return a > b + } + return a < b + } + vi := value(i) + for j := lo; j <= hi; j++ { + if j == i { + continue + } + if vj := value(j); better(vj, vi) || (vj == vi && j < i) { + return false + } + } + return !math.IsNaN(vi) +} + +func orbitalGoldenExtremum(f func(float64) float64, a, b float64, maximize bool) (float64, float64) { + sign := 1.0 + if maximize { + sign = -1 + } + const ratio = 0.6180339887498949 + c, d := b-ratio*(b-a), a+ratio*(b-a) + fc, fd := sign*f(c), sign*f(d) + for i := 0; i < 100 && b-a > 1e-5; i++ { + if fc < fd { + b, d, fd = d, c, fc + c = b - ratio*(b-a) + fc = sign * f(c) + } else { + a, c, fc = c, d, fd + d = a + ratio*(b-a) + fd = sign * f(d) + } + } + t := (a + b) / 2 + return t, f(t) +} + +// PlanetNodesWithInstance 行星在 [startJDE, endJDE] 内穿过历元平黄道的升交点与降交点时刻(TT)/ ascending and descending node passages. +func PlanetNodesWithInstance(body SkyBody, startJDE, endJDE float64, inst *Instance) []OrbitalEvent { + index, period, ok := orbitalIndex(body) + if !ok || index < 0 || !finite(startJDE) || !finite(endJDE) || endJDE < startJDE { + return nil + } + latitude := func(t float64) float64 { return PlanetCoordinateNWithInstance(index, 1, t, -1, inst) } + count, at, ok := skyGrid(startJDE, endJDE, period/200, 1) + if !ok { + return nil + } + var out []OrbitalEvent + last := map[OrbitalEventKind]float64{} + prevT, prevB := startJDE, latitude(startJDE) + for step := 1; step <= count; step++ { + t := at(step) + b := latitude(t) + if (prevB < 0) != (b < 0) && !math.IsNaN(b) && !math.IsNaN(prevB) { + kind := OrbitalAscendingNode + if b < 0 { + kind = OrbitalDescendingNode + } + root := orbitalBisect(latitude, prevT, t, prevB) + if seen, ok := last[kind]; !ok || root-seen > 0.3*period { + last[kind] = root + out = append(out, OrbitalEvent{JDE: root, Kind: kind, DistanceAU: PlanetCoordinateNWithInstance(index, 2, root, -1, inst)}) + } + } + prevT, prevB = t, b + } + return out +} + +func orbitalBisect(f func(float64) float64, a, b, fa float64) float64 { + for i := 0; i < 80 && b-a > 1e-6; i++ { + m := (a + b) / 2 + fm := f(m) + if (fm < 0) == (fa < 0) { + a, fa = m, fm + } else { + b = m + } + } + return (a + b) / 2 +} diff --git a/basic/orbital_events_test.go b/basic/orbital_events_test.go new file mode 100644 index 0000000..5c2f9a8 --- /dev/null +++ b/basic/orbital_events_test.go @@ -0,0 +1,85 @@ +// 被测契约:近日点与远日点交替出现且日心距为局部极值;过交点处日心黄纬为零、升降交点交替,并带该时刻的日心距。 +package basic + +import ( + "math" + "testing" +) + +func TestPlanetApsidesKnownDates(t *testing.T) { + cases := []struct { + body SkyBody + kind OrbitalEventKind + startTT float64 + wantTT float64 + tolDays float64 + }{ + {SkyEarth, OrbitalPerihelion, 2451545, 2451547.2, 1.5}, + {SkyMars, OrbitalPerihelion, 2451545, 2452195, 3}, + {SkyJupiter, OrbitalPerihelion, 2459000, 2459965, 15}, + } + for _, c := range cases { + _, period, _ := orbitalIndex(c.body) + var got float64 + for _, e := range PlanetApsidesWithInstance(c.body, c.startTT, c.startTT+1.1*period, nil) { + if e.Kind == c.kind { + got = e.JDE + break + } + } + if math.Abs(got-c.wantTT) > c.tolDays { + t.Errorf("body %d kind %d: got %.2f want %.2f", c.body, c.kind, got, c.wantTT) + } + } +} + +func TestPlanetApsidesAlternateAndNodesAreZeroLatitude(t *testing.T) { + for _, body := range []SkyBody{SkyMercury, SkyVenus, SkyMars, SkyJupiter, SkySaturn} { + index, period, _ := orbitalIndex(body) + ap := PlanetApsidesWithInstance(body, 2451545, 2451545+3*period, nil) + if len(ap) < 5 { + t.Fatalf("body %d: only %d apsides", body, len(ap)) + } + for i := 1; i < len(ap); i++ { + if ap[i].Kind == ap[i-1].Kind { + t.Errorf("body %d: apsides do not alternate at %d", body, i) + } + } + nodes := PlanetNodesWithInstance(body, 2451545, 2451545+3*period, nil) + if len(nodes) < 5 { + t.Fatalf("body %d: only %d nodes", body, len(nodes)) + } + for i, e := range nodes { + if b := PlanetCoordinateNWithInstance(index, 1, e.JDE, -1, nil); math.Abs(b) > 1e-6 { + t.Errorf("body %d node %d: latitude %.3g", body, i, b) + } + if want := PlanetCoordinateNWithInstance(index, 2, e.JDE, -1, nil); math.IsNaN(e.DistanceAU) || math.Abs(e.DistanceAU-want) > 1e-12 { + t.Errorf("body %d node %d: distance %.9f want %.9f", body, i, e.DistanceAU, want) + } + if i > 0 && e.Kind == nodes[i-1].Kind { + t.Errorf("body %d: nodes do not alternate at %d", body, i) + } + } + } + if PlanetNodesWithInstance(SkyEarth, 2451545, 2452545, nil) != nil || PlanetApsidesWithInstance(SkySun, 2451545, 2452545, nil) != nil { + t.Error("unsupported body must return nil") + } +} + +func TestPlanetApsidesRejectsUnscannableRange(t *testing.T) { + if got := PlanetApsidesWithInstance(SkyMars, -1e308, 1e308, nil); got != nil { + t.Errorf("huge range: %d events", len(got)) + } + if got := PlanetApsidesWithInstance(SkyMars, -1e15, 1e15, nil); got != nil { + t.Errorf("over grid limit: %d events", len(got)) + } + if got := PlanetApsidesWithInstance(SkyEarth, 0, 1e12, nil); got != nil { + t.Errorf("over 32-bit grid limit: %d events", len(got)) + } + if got := PlanetNodesWithInstance(SkyMars, 0, 1e12, nil); got != nil { + t.Errorf("nodes over grid limit: %d events", len(got)) + } + if got := PlanetNodesWithInstance(SkyMars, 0, 686.98/200*float64(skyGridMaxIntervals), nil); got != nil { + t.Errorf("nodes at grid limit: %d events", len(got)) + } +} diff --git a/basic/orbital_nodes.go b/basic/orbital_nodes.go index 75395a1..44a015c 100644 --- a/basic/orbital_nodes.go +++ b/basic/orbital_nodes.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -19,9 +18,9 @@ func MoonAscendingNode(jd float64) float64 { return MoonAscendingNodeN(jd, -1) } -// MoonAscendingNodeN 月球升交点黄经(截断版) / truncated ascending node longitude of the Moon. -func MoonAscendingNodeN(jd float64, n int) float64 { - return orbitalAscendingNodeLongitude(jd, n, orbitalNodeStepFastBodyDays, moonGeocentricNodePositionN) +// MoonAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return orbitalAscendingNodeLongitude(jd, n, orbitalNodeStepFastBodyDays, func(jde float64, n int) Vector3 { return moonGeocentricNodePositionN(jde, n, inst) }) } // MoonDescendingNode 月球降交点黄经 / descending node longitude of the Moon. @@ -29,9 +28,9 @@ func MoonDescendingNode(jd float64) float64 { return MoonDescendingNodeN(jd, -1) } -// MoonDescendingNodeN 月球降交点黄经(截断版) / truncated descending node longitude of the Moon. -func MoonDescendingNodeN(jd float64, n int) float64 { - return Limit360(MoonAscendingNodeN(jd, n) + 180) +// MoonDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MoonDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(MoonAscendingNodeNWithInstance(jd, n, inst) + 180) } // MercuryAscendingNode 水星升交点黄经 / ascending node longitude of Mercury. @@ -39,9 +38,9 @@ func MercuryAscendingNode(jd float64) float64 { return MercuryAscendingNodeN(jd, -1) } -// MercuryAscendingNodeN 水星升交点黄经(截断版) / truncated ascending node longitude of Mercury. -func MercuryAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(1, jd, n) +// MercuryAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(1, jd, n, inst) } // MercuryDescendingNode 水星降交点黄经 / descending node longitude of Mercury. @@ -49,9 +48,9 @@ func MercuryDescendingNode(jd float64) float64 { return MercuryDescendingNodeN(jd, -1) } -// MercuryDescendingNodeN 水星降交点黄经(截断版) / truncated descending node longitude of Mercury. -func MercuryDescendingNodeN(jd float64, n int) float64 { - return Limit360(MercuryAscendingNodeN(jd, n) + 180) +// MercuryDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(MercuryAscendingNodeNWithInstance(jd, n, inst) + 180) } // VenusAscendingNode 金星升交点黄经 / ascending node longitude of Venus. @@ -59,9 +58,9 @@ func VenusAscendingNode(jd float64) float64 { return VenusAscendingNodeN(jd, -1) } -// VenusAscendingNodeN 金星升交点黄经(截断版) / truncated ascending node longitude of Venus. -func VenusAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(2, jd, n) +// VenusAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(2, jd, n, inst) } // VenusDescendingNode 金星降交点黄经 / descending node longitude of Venus. @@ -69,9 +68,9 @@ func VenusDescendingNode(jd float64) float64 { return VenusDescendingNodeN(jd, -1) } -// VenusDescendingNodeN 金星降交点黄经(截断版) / truncated descending node longitude of Venus. -func VenusDescendingNodeN(jd float64, n int) float64 { - return Limit360(VenusAscendingNodeN(jd, n) + 180) +// VenusDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(VenusAscendingNodeNWithInstance(jd, n, inst) + 180) } // MarsAscendingNode 火星升交点黄经 / ascending node longitude of Mars. @@ -79,9 +78,9 @@ func MarsAscendingNode(jd float64) float64 { return MarsAscendingNodeN(jd, -1) } -// MarsAscendingNodeN 火星升交点黄经(截断版) / truncated ascending node longitude of Mars. -func MarsAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(3, jd, n) +// MarsAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(3, jd, n, inst) } // MarsDescendingNode 火星降交点黄经 / descending node longitude of Mars. @@ -89,9 +88,9 @@ func MarsDescendingNode(jd float64) float64 { return MarsDescendingNodeN(jd, -1) } -// MarsDescendingNodeN 火星降交点黄经(截断版) / truncated descending node longitude of Mars. -func MarsDescendingNodeN(jd float64, n int) float64 { - return Limit360(MarsAscendingNodeN(jd, n) + 180) +// MarsDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(MarsAscendingNodeNWithInstance(jd, n, inst) + 180) } // JupiterAscendingNode 木星升交点黄经 / ascending node longitude of Jupiter. @@ -99,9 +98,9 @@ func JupiterAscendingNode(jd float64) float64 { return JupiterAscendingNodeN(jd, -1) } -// JupiterAscendingNodeN 木星升交点黄经(截断版) / truncated ascending node longitude of Jupiter. -func JupiterAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(4, jd, n) +// JupiterAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(4, jd, n, inst) } // JupiterDescendingNode 木星降交点黄经 / descending node longitude of Jupiter. @@ -109,9 +108,9 @@ func JupiterDescendingNode(jd float64) float64 { return JupiterDescendingNodeN(jd, -1) } -// JupiterDescendingNodeN 木星降交点黄经(截断版) / truncated descending node longitude of Jupiter. -func JupiterDescendingNodeN(jd float64, n int) float64 { - return Limit360(JupiterAscendingNodeN(jd, n) + 180) +// JupiterDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(JupiterAscendingNodeNWithInstance(jd, n, inst) + 180) } // SaturnAscendingNode 土星升交点黄经 / ascending node longitude of Saturn. @@ -119,9 +118,9 @@ func SaturnAscendingNode(jd float64) float64 { return SaturnAscendingNodeN(jd, -1) } -// SaturnAscendingNodeN 土星升交点黄经(截断版) / truncated ascending node longitude of Saturn. -func SaturnAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(5, jd, n) +// SaturnAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(5, jd, n, inst) } // SaturnDescendingNode 土星降交点黄经 / descending node longitude of Saturn. @@ -129,9 +128,9 @@ func SaturnDescendingNode(jd float64) float64 { return SaturnDescendingNodeN(jd, -1) } -// SaturnDescendingNodeN 土星降交点黄经(截断版) / truncated descending node longitude of Saturn. -func SaturnDescendingNodeN(jd float64, n int) float64 { - return Limit360(SaturnAscendingNodeN(jd, n) + 180) +// SaturnDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(SaturnAscendingNodeNWithInstance(jd, n, inst) + 180) } // UranusAscendingNode 天王星升交点黄经 / ascending node longitude of Uranus. @@ -139,9 +138,9 @@ func UranusAscendingNode(jd float64) float64 { return UranusAscendingNodeN(jd, -1) } -// UranusAscendingNodeN 天王星升交点黄经(截断版) / truncated ascending node longitude of Uranus. -func UranusAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(6, jd, n) +// UranusAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(6, jd, n, inst) } // UranusDescendingNode 天王星降交点黄经 / descending node longitude of Uranus. @@ -149,9 +148,9 @@ func UranusDescendingNode(jd float64) float64 { return UranusDescendingNodeN(jd, -1) } -// UranusDescendingNodeN 天王星降交点黄经(截断版) / truncated descending node longitude of Uranus. -func UranusDescendingNodeN(jd float64, n int) float64 { - return Limit360(UranusAscendingNodeN(jd, n) + 180) +// UranusDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(UranusAscendingNodeNWithInstance(jd, n, inst) + 180) } // NeptuneAscendingNode 海王星升交点黄经 / ascending node longitude of Neptune. @@ -159,9 +158,9 @@ func NeptuneAscendingNode(jd float64) float64 { return NeptuneAscendingNodeN(jd, -1) } -// NeptuneAscendingNodeN 海王星升交点黄经(截断版) / truncated ascending node longitude of Neptune. -func NeptuneAscendingNodeN(jd float64, n int) float64 { - return planetAscendingNodeLongitudeN(7, jd, n) +// NeptuneAscendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneAscendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetAscendingNodeLongitudeN(7, jd, n, inst) } // NeptuneDescendingNode 海王星降交点黄经 / descending node longitude of Neptune. @@ -169,18 +168,20 @@ func NeptuneDescendingNode(jd float64) float64 { return NeptuneDescendingNodeN(jd, -1) } -// NeptuneDescendingNodeN 海王星降交点黄经(截断版) / truncated descending node longitude of Neptune. -func NeptuneDescendingNodeN(jd float64, n int) float64 { - return Limit360(NeptuneAscendingNodeN(jd, n) + 180) +// NeptuneDescendingNodeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneDescendingNodeNWithInstance(jd float64, n int, inst *Instance) float64 { + return Limit360(NeptuneAscendingNodeNWithInstance(jd, n, inst) + 180) } -func planetAscendingNodeLongitudeN(planetIndex int, jd float64, n int) float64 { +func planetAscendingNodeLongitudeN(planetIndex int, jd float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + step := orbitalNodeStepSlowBodyDays if planetIndex <= 3 { step = orbitalNodeStepFastBodyDays } return orbitalAscendingNodeLongitude(jd, n, step, func(sampleJD float64, seriesTerms int) Vector3 { - return planetHeliocentricNodePositionN(planetIndex, sampleJD, seriesTerms) + return planetHeliocentricNodePositionN(planetIndex, sampleJD, seriesTerms, inst) }) } @@ -265,17 +266,21 @@ func applyMatrix3(matrix Matrix3, vector Vector3) Vector3 { } } -func planetHeliocentricNodePositionN(planetIndex int, jde float64, n int) Vector3 { - longitude := planet.WherePlanetN(planetIndex, 0, jde, n) - latitude := planet.WherePlanetN(planetIndex, 1, jde, n) - radius := planet.WherePlanetN(planetIndex, 2, jde, n) +func planetHeliocentricNodePositionN(planetIndex int, jde float64, n int, instances ...*Instance) Vector3 { + inst := instanceValue(instances) + + longitude := PlanetCoordinateNWithInstance(planetIndex, 0, jde, n, inst) + latitude := PlanetCoordinateNWithInstance(planetIndex, 1, jde, n, inst) + radius := PlanetCoordinateNWithInstance(planetIndex, 2, jde, n, inst) return eclipticCartesian(longitude, latitude, radius) } -func moonGeocentricNodePositionN(jde float64, n int) Vector3 { - longitude := HMoonTrueLoN(jde, n) - latitude := HMoonTrueBoN(jde, n) - radius := HMoonAwayN(jde, n) +func moonGeocentricNodePositionN(jde float64, n int, instances ...*Instance) Vector3 { + inst := instanceValue(instances) + + longitude := HMoonTrueLoNWithInstance(jde, n, inst) + latitude := HMoonTrueBoNWithInstance(jde, n, inst) + radius := HMoonAwayNWithInstance(jde, n, inst) return eclipticCartesian(longitude, latitude, radius) } diff --git a/basic/outer_planet_event_regression_test.go b/basic/outer_planet_event_regression_test.go index f0af68e..0050756 100644 --- a/basic/outer_planet_event_regression_test.go +++ b/basic/outer_planet_event_regression_test.go @@ -3,7 +3,6 @@ package basic import ( "encoding/json" "math" - "os" "testing" ) @@ -17,13 +16,14 @@ type outerPlanetEventBaseline struct { Samples []outerPlanetEventBaselineSample `json:"samples"` } -func loadOuterPlanetEventBaseline(t *testing.T, path string) outerPlanetEventBaseline { +func loadOuterPlanetEventBaseline(t *testing.T, path string, cases []outerPlanetEventCase) outerPlanetEventBaseline { t.Helper() - data, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) + var named []namedEventFunc + for _, c := range cases { + named = append(named, namedEventFunc{c.name, c.fn}) } + data := eventBaselineData(t, path, named) var baseline outerPlanetEventBaseline if err := json.Unmarshal(data, &baseline); err != nil { @@ -38,8 +38,8 @@ func loadOuterPlanetEventBaseline(t *testing.T, path string) outerPlanetEventBas func TestOuterPlanetEventBaselineRegression(t *testing.T) { for _, plan := range outerPlanetEventPlans() { t.Run(plan.planet, func(t *testing.T) { - baseline := loadOuterPlanetEventBaseline(t, plan.baselineFile) cases := plan.allCases() + baseline := loadOuterPlanetEventBaseline(t, plan.baselineFile, cases) for _, sample := range baseline.Samples { jd := math.Float64frombits(sample.TTJDBits) diff --git a/basic/planet_apparent.go b/basic/planet_apparent.go index 4a4842e..659aee0 100644 --- a/basic/planet_apparent.go +++ b/basic/planet_apparent.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -15,17 +14,21 @@ type planetGeocentricPosition struct { bo float64 } -func planetHeliocentricXYZN(planetIndex int, jde float64, n int) (float64, float64, float64) { - l := planet.WherePlanetN(planetIndex, 0, jde, n) - b := planet.WherePlanetN(planetIndex, 1, jde, n) - r := planet.WherePlanetN(planetIndex, 2, jde, n) +func planetHeliocentricXYZN(planetIndex int, jde float64, n int, instances ...*Instance) (float64, float64, float64) { + inst := instanceValue(instances) + + l := PlanetCoordinateNWithInstance(planetIndex, 0, jde, n, inst) + b := PlanetCoordinateNWithInstance(planetIndex, 1, jde, n, inst) + r := PlanetCoordinateNWithInstance(planetIndex, 2, jde, n, inst) return sphericalToRectangular(l, b, r) } -func earthHeliocentricXYZN(jde float64, n int) (float64, float64, float64) { - l := planet.WherePlanetN(-1, 0, jde, n) - b := planet.WherePlanetN(-1, 1, jde, n) - r := planet.WherePlanetN(-1, 2, jde, n) +func earthHeliocentricXYZN(jde float64, n int, instances ...*Instance) (float64, float64, float64) { + inst := instanceValue(instances) + + l := PlanetCoordinateNWithInstance(-1, 0, jde, n, inst) + b := PlanetCoordinateNWithInstance(-1, 1, jde, n, inst) + r := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) return sphericalToRectangular(l, b, r) } @@ -54,8 +57,10 @@ func planetGeocentricPositionN(planetIndex int, planetJD, earthJDE float64, n in return geocentricPositionFromRectangular(px-ex, py-ey, pz-ez) } -func planetGeocentricPositionWithEarthN(planetIndex int, planetJD float64, ex, ey, ez float64, n int) planetGeocentricPosition { - px, py, pz := planetHeliocentricXYZN(planetIndex, planetJD, n) +func planetGeocentricPositionWithEarthN(planetIndex int, planetJD float64, ex, ey, ez float64, n int, instances ...*Instance) planetGeocentricPosition { + inst := instanceValue(instances) + + px, py, pz := planetHeliocentricXYZN(planetIndex, planetJD, n, inst) return geocentricPositionFromRectangular(px-ex, py-ey, pz-ez) } @@ -67,17 +72,24 @@ func planetApparentGeocentricPositionN(planetIndex int, jd float64, n int) (plan func planetApparentGeocentricPositionAndDistanceN( planetIndex int, jde float64, - n int, -) (planetGeocentricPosition, float64, float64) { - ex, ey, ez := earthHeliocentricXYZN(jde, n) - geoNow := planetGeocentricPositionWithEarthN(planetIndex, jde, ex, ey, ez, n) + n int, instances ...*Instance) (planetGeocentricPosition, float64, float64) { + inst := instanceValue(instances) + + if planetIndex >= 1 && planetIndex <= 7 { + if lo, bo, tau, dist, ok := boundVectorApparent(planetBody[planetIndex], jde, n, inst); ok { + x, y, z := sphericalToRectangular(lo, bo, dist) + return planetGeocentricPosition{x: x, y: y, z: z, lo: lo, bo: bo}, tau, dist + } + } + ex, ey, ez := earthHeliocentricXYZN(jde, n, inst) + geoNow := planetGeocentricPositionWithEarthN(planetIndex, jde, ex, ey, ez, n, inst) distance := math.Sqrt(geoNow.x*geoNow.x + geoNow.y*geoNow.y + geoNow.z*geoNow.z) tau := 0.0057755183 * distance - geo := planetGeocentricPositionWithEarthN(planetIndex, jde-tau, ex, ey, ez, n) + geo := planetGeocentricPositionWithEarthN(planetIndex, jde-tau, ex, ey, ez, n, inst) baseLo := geo.lo baseBo := geo.bo - geo.lo = Limit360(baseLo + GXCLo(baseLo, baseBo, jde)/3600.0 + Nutation2000Bi(jde)) - geo.bo = baseBo + GXCBo(baseLo, baseBo, jde)/3600.0 + geo.lo = Limit360(baseLo + GXCLoWithInstance(baseLo, baseBo, jde, inst)/3600.0 + Nutation2000Bi(jde)) + geo.bo = baseBo + GXCBoWithInstance(baseLo, baseBo, jde, inst)/3600.0 return geo, tau, distance } @@ -89,6 +101,12 @@ func planetTrueGeocentricPositionN(planetIndex int, jde float64, n int) (planetG } func planetEarthAwayExplicitN(planetIndex int, jd float64, n int) float64 { + // 新模型全精度时与视位置管线同源,掩星等组合路径取到的距离逐位一致。 + if planetIndex >= 1 && planetIndex <= 7 { + if _, _, _, dist, ok := vectorApparent(planetBody[planetIndex], jd, n); ok { + return dist + } + } geoNow := planetGeocentricPositionN(planetIndex, jd, jd, n) return math.Sqrt(geoNow.x*geoNow.x + geoNow.y*geoNow.y + geoNow.z*geoNow.z) } diff --git a/basic/planet_apparent_vector_test.go b/basic/planet_apparent_vector_test.go new file mode 100644 index 0000000..553785c --- /dev/null +++ b/basic/planet_apparent_vector_test.go @@ -0,0 +1,51 @@ +// 被测契约:新模型的矢量视位置管线与 DE441 视赤经赤纬(历元真赤道)一致到半解析表本身的精度。 +package basic + +import ( + "bufio" + "math" + "os" + "strconv" + "strings" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/ephemtest" +) + +func TestPlanetApparentVectorMatchesDE441(t *testing.T) { + ephemtest.Pin(t, ephem.Semianalytic) + f, err := os.Open("testdata/semianalytic/planet_apparent_de441.csv") + if err != nil { + t.Fatal(err) + } + defer f.Close() + fns := [8]func(float64) (float64, float64){ + nil, MercuryApparentRaDec, VenusApparentRaDec, MarsApparentRaDec, JupiterApparentRaDec, + SaturnApparentRaDec, UranusApparentRaDec, NeptuneApparentRaDec, + } + rmsLimit := [8]float64{0, 15, 35, 30, 15, 10, 15, 20} + maxLimit := [8]float64{0, 35, 110, 150, 40, 20, 45, 50} + var ss, mx [8]float64 + var n [8]int + sc := bufio.NewScanner(f) + for sc.Scan() { + p := strings.Split(sc.Text(), ",") + b, _ := strconv.Atoi(p[0]) + jd, _ := strconv.ParseFloat(p[1], 64) + wantRA, _ := strconv.ParseFloat(p[2], 64) + wantDec, _ := strconv.ParseFloat(p[3], 64) + ra, dec := fns[b](jd) + dra := math.Mod(ra-wantRA+540, 360) - 180 + d := math.Hypot(dra*math.Cos(wantDec*math.Pi/180), dec-wantDec) * 3.6e6 + ss[b] += d * d + n[b]++ + mx[b] = math.Max(mx[b], d) + } + for b := 1; b <= 7; b++ { + rms := math.Sqrt(ss[b] / float64(n[b])) + if rms > rmsLimit[b] || mx[b] > maxLimit[b] { + t.Errorf("planet %d: rms %.1f mas (limit %.0f), max %.1f mas (limit %.0f)", b, rms, rmsLimit[b], mx[b], maxLimit[b]) + } + } +} diff --git a/basic/planet_ephemeris_instance.go b/basic/planet_ephemeris_instance.go new file mode 100644 index 0000000..4df0b66 --- /dev/null +++ b/basic/planet_ephemeris_instance.go @@ -0,0 +1,1070 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" + . "b612.me/astro/tools" +) + +func planetBodyForInstance(index int) (ephem.Body, bool) { + if index == -1 { + return ephem.Earth, true + } + if index < 1 || index > 7 { + return 0, false + } + return planetBody[index], true +} + +func planetHeliocentricCoordinateWithInstance(index, coordinate int, jde float64, inst *Instance) (float64, bool) { + if coordinate < 0 || coordinate > 2 { + return math.NaN(), false + } + if inst == nil { + return planet.WherePlanet(index, coordinate, jde), true + } + body, ok := planetBodyForInstance(index) + if !ok { + return math.NaN(), false + } + if lo, bo, distance, ok := instanceStateEclipticRelative(body, ephem.Sun, jde, jde, inst); ok { + switch coordinate { + case 0: + return lo, true + case 1: + return bo, true + default: + return distance, true + } + } + model := inst.Model() + if model == ephem.Legacy || model == ephem.Semianalytic { + return planet.WherePlanetModelN(model, index, coordinate, jde, -1), true + } + return math.NaN(), false +} + +func planetGeocentricXYZWithInstance(index int, jde float64, inst *Instance) (x, y, z float64, ok bool) { + if inst != nil { + body, bodyOK := planetBodyForInstance(index) + if bodyOK { + if lo, bo, distance, stateOK := instanceStateEclipticRelative(body, ephem.Earth, jde, jde, inst); stateOK { + x, y, z := sphericalToRectangular(lo, bo, distance) + return x, y, z, true + } + } + } + pl, plOK := planetHeliocentricCoordinateWithInstance(index, 0, jde, inst) + pb, pbOK := planetHeliocentricCoordinateWithInstance(index, 1, jde, inst) + pr, prOK := planetHeliocentricCoordinateWithInstance(index, 2, jde, inst) + el, elOK := planetHeliocentricCoordinateWithInstance(-1, 0, jde, inst) + eb, ebOK := planetHeliocentricCoordinateWithInstance(-1, 1, jde, inst) + er, erOK := planetHeliocentricCoordinateWithInstance(-1, 2, jde, inst) + if !plOK || !pbOK || !prOK || !elOK || !ebOK || !erOK { + return 0, 0, 0, false + } + px, py, pz := sphericalToRectangular(pl, pb, pr) + ex, ey, ez := sphericalToRectangular(el, eb, er) + return px - ex, py - ey, pz - ez, true +} + +func planetApparentPositionModel(index int, jde float64, n int, model ephem.Model) (planetGeocentricPosition, float64, float64, bool) { + ex, ey, ez := planetHeliocentricXYZModel(-1, jde, n, model) + px, py, pz := planetHeliocentricXYZModel(index, jde, n, model) + geoNow := geocentricPositionFromRectangular(px-ex, py-ey, pz-ez) + distance := math.Sqrt(geoNow.x*geoNow.x + geoNow.y*geoNow.y + geoNow.z*geoNow.z) + if !isFiniteFloat(distance) || distance <= 0 { + return planetGeocentricPosition{}, 0, 0, false + } + tau := 0.0057755183 * distance + px, py, pz = planetHeliocentricXYZModel(index, jde-tau, n, model) + geo := geocentricPositionFromRectangular(px-ex, py-ey, pz-ez) + baseLo, baseBo := geo.lo, geo.bo + geo.lo = Limit360(baseLo + GXCLo(baseLo, baseBo, jde)/3600.0 + Nutation2000Bi(jde)) + geo.bo = baseBo + GXCBo(baseLo, baseBo, jde)/3600.0 + return geo, tau, distance, true +} + +func planetHeliocentricXYZModel(index int, jde float64, n int, model ephem.Model) (x, y, z float64) { + lo := planet.WherePlanetModelN(model, index, 0, jde, n) + bo := planet.WherePlanetModelN(model, index, 1, jde, n) + distance := planet.WherePlanetModelN(model, index, 2, jde, n) + return sphericalToRectangular(lo, bo, distance) +} + +func planetApparentPositionWithInstance(index int, jde float64, n int, inst *Instance) (planetGeocentricPosition, float64, float64, bool) { + if inst == nil { + geo, tau, distance := planetApparentGeocentricPositionAndDistanceN(index, jde, n) + return geo, tau, distance, true + } + if lo, bo, tau, distance, ok, err := vectorApparentWithInstance(planetBody[index], jde, n, inst); err != nil { + return planetGeocentricPosition{}, 0, 0, false + } else if ok { + x, y, z := sphericalToRectangular(lo, bo, distance) + return planetGeocentricPosition{x: x, y: y, z: z, lo: lo, bo: bo}, tau, distance, true + } + if model := inst.Model(); model == ephem.Legacy || model == ephem.Semianalytic { + return planetApparentPositionModel(index, jde, n, model) + } + body := planetBody[index] + _, _, currentDistance, ok := instanceStateEclipticRelative(body, ephem.Earth, jde, jde, inst) + if !ok || currentDistance <= 0 { + return planetGeocentricPosition{}, 0, 0, false + } + tau := 0.0057755183 * currentDistance + lo, bo, _, ok := instanceStateEclipticRelative(body, ephem.Earth, jde-tau, jde, inst) + if !ok { + return planetGeocentricPosition{}, 0, 0, false + } + x, y, z := sphericalToRectangular(lo, bo, currentDistance) + return planetGeocentricPosition{x: x, y: y, z: z, lo: lo, bo: bo}, tau, currentDistance, true +} + +func planetEarthAwayWithInstance(index int, jde float64, inst *Instance) float64 { + return planetEarthAwayNWithInstance(index, jde, -1, inst) +} + +func planetEarthAwayNWithInstance(index int, jde float64, n int, inst *Instance) float64 { + if inst == nil { + return planetEarthAwayExplicitN(index, jde, n) + } + if _, _, distance, ok := instanceStateEclipticRelative(planetBody[index], ephem.Earth, jde, jde, inst); ok { + return distance + } + // 与 planetEarthAwayExplicitN 对称:新模型全精度时取视位置管线的距离。 + if index >= 1 && index <= 7 { + if _, _, _, distance, ok, err := vectorApparentWithInstance(planetBody[index], jde, n, inst); err == nil && ok { + return distance + } + } + model := inst.Model() + if model != ephem.Legacy && model != ephem.Semianalytic { + return math.NaN() + } + x, y, z := planetHeliocentricXYZModel(index, jde, n, model) + ex, ey, ez := planetHeliocentricXYZModel(-1, jde, n, model) + return math.Sqrt((x-ex)*(x-ex) + (y-ey)*(y-ey) + (z-ez)*(z-ez)) +} + +func planetLWithInstance(index int, jde float64, inst *Instance) float64 { + value, _ := planetHeliocentricCoordinateWithInstance(index, 0, jde, inst) + return value +} + +func planetBWithInstance(index int, jde float64, inst *Instance) float64 { + value, _ := planetHeliocentricCoordinateWithInstance(index, 1, jde, inst) + return value +} + +func planetRWithInstance(index int, jde float64, inst *Instance) float64 { + value, _ := planetHeliocentricCoordinateWithInstance(index, 2, jde, inst) + return value +} + +func planetApparentLoBoWithInstance(index int, jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(index, jde, -1, inst) +} + +func planetApparentLoBoNWithInstance(index int, jde float64, n int, inst *Instance) (float64, float64) { + geo, _, _, ok := planetApparentPositionWithInstance(index, jde, n, inst) + if !ok { + return math.NaN(), math.NaN() + } + return geo.lo, geo.bo +} + +func planetApparentRaDecWithInstance(index int, jde float64, inst *Instance) (float64, float64) { + lo, bo := planetApparentLoBoWithInstance(index, jde, inst) + if math.IsNaN(lo) || math.IsNaN(bo) { + return math.NaN(), math.NaN() + } + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +func planetMagnitudeWithInstance(index int, jde float64, inst *Instance, base, phaseLinear, phaseQuadratic, phaseCubic float64) float64 { + sunDistance := planetRWithInstance(index, jde, inst) + earthDistance := planetEarthAwayWithInstance(index, jde, inst) + earthSunDistance := planetRWithInstance(-1, jde, inst) + phase := (sunDistance*sunDistance + earthDistance*earthDistance - earthSunDistance*earthSunDistance) / (2 * sunDistance * earthDistance) + phase = ArcCos(phase) + mag := base + 5*math.Log10(sunDistance*earthDistance) + phaseLinear*phase + phaseQuadratic*phase*phase + phaseCubic*phase*phase*phase + return FloatRound(mag, 2) +} + +// MercuryLWithInstance 水星日心黄经 / heliocentric longitude of Mercury with an explicit ephemeris instance. +func MercuryLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(1, jde, inst) +} + +// MercuryBWithInstance 水星日心黄纬 / heliocentric latitude of Mercury with an explicit ephemeris instance. +func MercuryBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(1, jde, inst) +} + +// MercuryRWithInstance 水星日心距离 / heliocentric distance of Mercury with an explicit ephemeris instance. +func MercuryRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(1, jde, inst) +} + +// AMercuryXYZWithInstance 水星地心直角坐标 / geocentric rectangular coordinates of Mercury with an explicit ephemeris instance. +func AMercuryXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(1, jde, inst) + return x, y, z +} + +// AMercuryXWithInstance 水星地心 X 坐标 / geocentric X coordinate of Mercury with an explicit ephemeris instance. +func AMercuryXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := AMercuryXYZWithInstance(jde, inst) + return x +} + +// AMercuryYWithInstance 水星地心 Y 坐标 / geocentric Y coordinate of Mercury with an explicit ephemeris instance. +func AMercuryYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := AMercuryXYZWithInstance(jde, inst) + return y +} + +// AMercuryZWithInstance 水星地心 Z 坐标 / geocentric Z coordinate of Mercury with an explicit ephemeris instance. +func AMercuryZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := AMercuryXYZWithInstance(jde, inst) + return z +} + +// MercuryApparentLoBoWithInstance 水星视黄经纬 / apparent ecliptic longitude and latitude of Mercury with an explicit ephemeris instance. +func MercuryApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(1, jde, inst) +} + +// MercuryApparentLoWithInstance 水星视黄经 / apparent ecliptic longitude of Mercury with an explicit ephemeris instance. +func MercuryApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := MercuryApparentLoBoWithInstance(jde, inst) + return lo +} + +// MercuryApparentBoWithInstance 水星视黄纬 / apparent ecliptic latitude of Mercury with an explicit ephemeris instance. +func MercuryApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := MercuryApparentLoBoWithInstance(jde, inst) + return bo +} + +// MercuryApparentRaDecWithInstance 水星视赤经纬 / apparent right ascension and declination of Mercury with an explicit ephemeris instance. +func MercuryApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(1, jde, inst) +} + +// MercuryApparentRaWithInstance 水星视赤经 / apparent right ascension of Mercury with an explicit ephemeris instance. +func MercuryApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := MercuryApparentRaDecWithInstance(jde, inst) + return ra +} + +// MercuryApparentDecWithInstance 水星视赤纬 / apparent declination of Mercury with an explicit ephemeris instance. +func MercuryApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := MercuryApparentRaDecWithInstance(jde, inst) + return dec +} + +// MercuryApparentLoBoNWithInstance 水星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Mercury with an explicit ephemeris instance. +func MercuryApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(1, jde, n, inst) +} + +// MercuryApparentLoNWithInstance 水星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Mercury with an explicit ephemeris instance. +func MercuryApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := MercuryApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// MercuryApparentBoNWithInstance 水星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Mercury with an explicit ephemeris instance. +func MercuryApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := MercuryApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// MercuryApparentRaDecNWithInstance 水星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Mercury with an explicit ephemeris instance. +func MercuryApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := MercuryApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// MercuryApparentRaNWithInstance 水星视赤经(n 变体) / n-parameterised apparent right ascension of Mercury with an explicit ephemeris instance. +func MercuryApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := MercuryApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// MercuryApparentDecNWithInstance 水星视赤纬(n 变体) / n-parameterised apparent declination of Mercury with an explicit ephemeris instance. +func MercuryApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := MercuryApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthMercuryAwayWithInstance 地水距离 / Earth-Mercury distance with an explicit ephemeris instance. +func EarthMercuryAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(1, jde, inst) +} + +// EarthMercuryAwayNWithInstance 水星地心距离(n 变体) / n-parameterised Earth-Mercury distance with an explicit ephemeris instance. +func EarthMercuryAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(1, jde, n, inst) +} + +// VenusLWithInstance 金星日心黄经 / heliocentric longitude of Venus with an explicit ephemeris instance. +func VenusLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(2, jde, inst) +} + +// VenusBWithInstance 金星日心黄纬 / heliocentric latitude of Venus with an explicit ephemeris instance. +func VenusBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(2, jde, inst) +} + +// VenusRWithInstance 金星日心距离 / heliocentric distance of Venus with an explicit ephemeris instance. +func VenusRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(2, jde, inst) +} + +// AVenusXYZWithInstance 金星地心直角坐标 / geocentric rectangular coordinates of Venus with an explicit ephemeris instance. +func AVenusXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(2, jde, inst) + return x, y, z +} + +// AVenusXWithInstance 金星地心 X 坐标 / geocentric X coordinate of Venus with an explicit ephemeris instance. +func AVenusXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := AVenusXYZWithInstance(jde, inst) + return x +} + +// AVenusYWithInstance 金星地心 Y 坐标 / geocentric Y coordinate of Venus with an explicit ephemeris instance. +func AVenusYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := AVenusXYZWithInstance(jde, inst) + return y +} + +// AVenusZWithInstance 金星地心 Z 坐标 / geocentric Z coordinate of Venus with an explicit ephemeris instance. +func AVenusZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := AVenusXYZWithInstance(jde, inst) + return z +} + +// VenusApparentLoBoWithInstance 金星视黄经纬 / apparent ecliptic longitude and latitude of Venus with an explicit ephemeris instance. +func VenusApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(2, jde, inst) +} + +// VenusApparentLoWithInstance 金星视黄经 / apparent ecliptic longitude of Venus with an explicit ephemeris instance. +func VenusApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := VenusApparentLoBoWithInstance(jde, inst) + return lo +} + +// VenusApparentBoWithInstance 金星视黄纬 / apparent ecliptic latitude of Venus with an explicit ephemeris instance. +func VenusApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := VenusApparentLoBoWithInstance(jde, inst) + return bo +} + +// VenusApparentRaDecWithInstance 金星视赤经纬 / apparent right ascension and declination of Venus with an explicit ephemeris instance. +func VenusApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(2, jde, inst) +} + +// VenusApparentRaWithInstance 金星视赤经 / apparent right ascension of Venus with an explicit ephemeris instance. +func VenusApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := VenusApparentRaDecWithInstance(jde, inst) + return ra +} + +// VenusApparentDecWithInstance 金星视赤纬 / apparent declination of Venus with an explicit ephemeris instance. +func VenusApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := VenusApparentRaDecWithInstance(jde, inst) + return dec +} + +// VenusApparentLoBoNWithInstance 金星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Venus with an explicit ephemeris instance. +func VenusApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(2, jde, n, inst) +} + +// VenusApparentLoNWithInstance 金星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Venus with an explicit ephemeris instance. +func VenusApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := VenusApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// VenusApparentBoNWithInstance 金星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Venus with an explicit ephemeris instance. +func VenusApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := VenusApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// VenusApparentRaDecNWithInstance 金星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Venus with an explicit ephemeris instance. +func VenusApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := VenusApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// VenusApparentRaNWithInstance 金星视赤经(n 变体) / n-parameterised apparent right ascension of Venus with an explicit ephemeris instance. +func VenusApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := VenusApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// VenusApparentDecNWithInstance 金星视赤纬(n 变体) / n-parameterised apparent declination of Venus with an explicit ephemeris instance. +func VenusApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := VenusApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthVenusAwayWithInstance 地金距离 / Earth-Venus distance with an explicit ephemeris instance. +func EarthVenusAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(2, jde, inst) +} + +// EarthVenusAwayNWithInstance 金星地心距离(n 变体) / n-parameterised Earth-Venus distance with an explicit ephemeris instance. +func EarthVenusAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(2, jde, n, inst) +} + +// MarsLWithInstance 火星日心黄经 / heliocentric longitude of Mars with an explicit ephemeris instance. +func MarsLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(3, jde, inst) +} + +// MarsBWithInstance 火星日心黄纬 / heliocentric latitude of Mars with an explicit ephemeris instance. +func MarsBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(3, jde, inst) +} + +// MarsRWithInstance 火星日心距离 / heliocentric distance of Mars with an explicit ephemeris instance. +func MarsRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(3, jde, inst) +} + +// AMarsXYZWithInstance 火星地心直角坐标 / geocentric rectangular coordinates of Mars with an explicit ephemeris instance. +func AMarsXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(3, jde, inst) + return x, y, z +} + +// AMarsXWithInstance 火星地心 X 坐标 / geocentric X coordinate of Mars with an explicit ephemeris instance. +func AMarsXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := AMarsXYZWithInstance(jde, inst) + return x +} + +// AMarsYWithInstance 火星地心 Y 坐标 / geocentric Y coordinate of Mars with an explicit ephemeris instance. +func AMarsYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := AMarsXYZWithInstance(jde, inst) + return y +} + +// AMarsZWithInstance 火星地心 Z 坐标 / geocentric Z coordinate of Mars with an explicit ephemeris instance. +func AMarsZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := AMarsXYZWithInstance(jde, inst) + return z +} + +// MarsApparentLoBoWithInstance 火星视黄经纬 / apparent ecliptic longitude and latitude of Mars with an explicit ephemeris instance. +func MarsApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(3, jde, inst) +} + +// MarsApparentLoWithInstance 火星视黄经 / apparent ecliptic longitude of Mars with an explicit ephemeris instance. +func MarsApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := MarsApparentLoBoWithInstance(jde, inst) + return lo +} + +// MarsApparentBoWithInstance 火星视黄纬 / apparent ecliptic latitude of Mars with an explicit ephemeris instance. +func MarsApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := MarsApparentLoBoWithInstance(jde, inst) + return bo +} + +// MarsApparentRaDecWithInstance 火星视赤经纬 / apparent right ascension and declination of Mars with an explicit ephemeris instance. +func MarsApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(3, jde, inst) +} + +// MarsApparentRaWithInstance 火星视赤经 / apparent right ascension of Mars with an explicit ephemeris instance. +func MarsApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := MarsApparentRaDecWithInstance(jde, inst) + return ra +} + +// MarsApparentDecWithInstance 火星视赤纬 / apparent declination of Mars with an explicit ephemeris instance. +func MarsApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := MarsApparentRaDecWithInstance(jde, inst) + return dec +} + +// MarsApparentLoBoNWithInstance 火星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Mars with an explicit ephemeris instance. +func MarsApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(3, jde, n, inst) +} + +// MarsApparentLoNWithInstance 火星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Mars with an explicit ephemeris instance. +func MarsApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := MarsApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// MarsApparentBoNWithInstance 火星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Mars with an explicit ephemeris instance. +func MarsApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := MarsApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// MarsApparentRaDecNWithInstance 火星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Mars with an explicit ephemeris instance. +func MarsApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := MarsApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// MarsApparentRaNWithInstance 火星视赤经(n 变体) / n-parameterised apparent right ascension of Mars with an explicit ephemeris instance. +func MarsApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := MarsApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// MarsApparentDecNWithInstance 火星视赤纬(n 变体) / n-parameterised apparent declination of Mars with an explicit ephemeris instance. +func MarsApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := MarsApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthMarsAwayWithInstance 地火距离 / Earth-Mars distance with an explicit ephemeris instance. +func EarthMarsAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(3, jde, inst) +} + +// EarthMarsAwayNWithInstance 火星地心距离(n 变体) / n-parameterised Earth-Mars distance with an explicit ephemeris instance. +func EarthMarsAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(3, jde, n, inst) +} + +// JupiterLWithInstance 木星日心黄经 / heliocentric longitude of Jupiter with an explicit ephemeris instance. +func JupiterLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(4, jde, inst) +} + +// JupiterBWithInstance 木星日心黄纬 / heliocentric latitude of Jupiter with an explicit ephemeris instance. +func JupiterBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(4, jde, inst) +} + +// JupiterRWithInstance 木星日心距离 / heliocentric distance of Jupiter with an explicit ephemeris instance. +func JupiterRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(4, jde, inst) +} + +// AJupiterXYZWithInstance 木星地心直角坐标 / geocentric rectangular coordinates of Jupiter with an explicit ephemeris instance. +func AJupiterXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(4, jde, inst) + return x, y, z +} + +// AJupiterXWithInstance 木星地心 X 坐标 / geocentric X coordinate of Jupiter with an explicit ephemeris instance. +func AJupiterXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := AJupiterXYZWithInstance(jde, inst) + return x +} + +// AJupiterYWithInstance 木星地心 Y 坐标 / geocentric Y coordinate of Jupiter with an explicit ephemeris instance. +func AJupiterYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := AJupiterXYZWithInstance(jde, inst) + return y +} + +// AJupiterZWithInstance 木星地心 Z 坐标 / geocentric Z coordinate of Jupiter with an explicit ephemeris instance. +func AJupiterZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := AJupiterXYZWithInstance(jde, inst) + return z +} + +// JupiterApparentLoBoWithInstance 木星视黄经纬 / apparent ecliptic longitude and latitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(4, jde, inst) +} + +// JupiterApparentLoWithInstance 木星视黄经 / apparent ecliptic longitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := JupiterApparentLoBoWithInstance(jde, inst) + return lo +} + +// JupiterApparentBoWithInstance 木星视黄纬 / apparent ecliptic latitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := JupiterApparentLoBoWithInstance(jde, inst) + return bo +} + +// JupiterApparentRaDecWithInstance 木星视赤经纬 / apparent right ascension and declination of Jupiter with an explicit ephemeris instance. +func JupiterApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(4, jde, inst) +} + +// JupiterApparentRaWithInstance 木星视赤经 / apparent right ascension of Jupiter with an explicit ephemeris instance. +func JupiterApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := JupiterApparentRaDecWithInstance(jde, inst) + return ra +} + +// JupiterApparentDecWithInstance 木星视赤纬 / apparent declination of Jupiter with an explicit ephemeris instance. +func JupiterApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := JupiterApparentRaDecWithInstance(jde, inst) + return dec +} + +// JupiterApparentLoBoNWithInstance 木星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(4, jde, n, inst) +} + +// JupiterApparentLoNWithInstance 木星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := JupiterApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// JupiterApparentBoNWithInstance 木星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Jupiter with an explicit ephemeris instance. +func JupiterApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := JupiterApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// JupiterApparentRaDecNWithInstance 木星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Jupiter with an explicit ephemeris instance. +func JupiterApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := JupiterApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// JupiterApparentRaNWithInstance 木星视赤经(n 变体) / n-parameterised apparent right ascension of Jupiter with an explicit ephemeris instance. +func JupiterApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := JupiterApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// JupiterApparentDecNWithInstance 木星视赤纬(n 变体) / n-parameterised apparent declination of Jupiter with an explicit ephemeris instance. +func JupiterApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := JupiterApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthJupiterAwayWithInstance 地木距离 / Earth-Jupiter distance with an explicit ephemeris instance. +func EarthJupiterAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(4, jde, inst) +} + +// EarthJupiterAwayNWithInstance 木星地心距离(n 变体) / n-parameterised Earth-Jupiter distance with an explicit ephemeris instance. +func EarthJupiterAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(4, jde, n, inst) +} + +// SaturnLWithInstance 土星日心黄经 / heliocentric longitude of Saturn with an explicit ephemeris instance. +func SaturnLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(5, jde, inst) +} + +// SaturnBWithInstance 土星日心黄纬 / heliocentric latitude of Saturn with an explicit ephemeris instance. +func SaturnBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(5, jde, inst) +} + +// SaturnRWithInstance 土星日心距离 / heliocentric distance of Saturn with an explicit ephemeris instance. +func SaturnRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(5, jde, inst) +} + +// ASaturnXYZWithInstance 土星地心直角坐标 / geocentric rectangular coordinates of Saturn with an explicit ephemeris instance. +func ASaturnXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(5, jde, inst) + return x, y, z +} + +// ASaturnXWithInstance 土星地心 X 坐标 / geocentric X coordinate of Saturn with an explicit ephemeris instance. +func ASaturnXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := ASaturnXYZWithInstance(jde, inst) + return x +} + +// ASaturnYWithInstance 土星地心 Y 坐标 / geocentric Y coordinate of Saturn with an explicit ephemeris instance. +func ASaturnYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := ASaturnXYZWithInstance(jde, inst) + return y +} + +// ASaturnZWithInstance 土星地心 Z 坐标 / geocentric Z coordinate of Saturn with an explicit ephemeris instance. +func ASaturnZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := ASaturnXYZWithInstance(jde, inst) + return z +} + +// SaturnApparentLoBoWithInstance 土星视黄经纬 / apparent ecliptic longitude and latitude of Saturn with an explicit ephemeris instance. +func SaturnApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(5, jde, inst) +} + +// SaturnApparentLoWithInstance 土星视黄经 / apparent ecliptic longitude of Saturn with an explicit ephemeris instance. +func SaturnApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := SaturnApparentLoBoWithInstance(jde, inst) + return lo +} + +// SaturnApparentBoWithInstance 土星视黄纬 / apparent ecliptic latitude of Saturn with an explicit ephemeris instance. +func SaturnApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := SaturnApparentLoBoWithInstance(jde, inst) + return bo +} + +// SaturnApparentRaDecWithInstance 土星视赤经纬 / apparent right ascension and declination of Saturn with an explicit ephemeris instance. +func SaturnApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(5, jde, inst) +} + +// SaturnApparentRaWithInstance 土星视赤经 / apparent right ascension of Saturn with an explicit ephemeris instance. +func SaturnApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := SaturnApparentRaDecWithInstance(jde, inst) + return ra +} + +// SaturnApparentDecWithInstance 土星视赤纬 / apparent declination of Saturn with an explicit ephemeris instance. +func SaturnApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := SaturnApparentRaDecWithInstance(jde, inst) + return dec +} + +// SaturnApparentLoBoNWithInstance 土星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Saturn with an explicit ephemeris instance. +func SaturnApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(5, jde, n, inst) +} + +// SaturnApparentLoNWithInstance 土星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Saturn with an explicit ephemeris instance. +func SaturnApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := SaturnApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// SaturnApparentBoNWithInstance 土星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Saturn with an explicit ephemeris instance. +func SaturnApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := SaturnApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// SaturnApparentRaDecNWithInstance 土星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Saturn with an explicit ephemeris instance. +func SaturnApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := SaturnApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// SaturnApparentRaNWithInstance 土星视赤经(n 变体) / n-parameterised apparent right ascension of Saturn with an explicit ephemeris instance. +func SaturnApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := SaturnApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// SaturnApparentDecNWithInstance 土星视赤纬(n 变体) / n-parameterised apparent declination of Saturn with an explicit ephemeris instance. +func SaturnApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := SaturnApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthSaturnAwayWithInstance 地土距离 / Earth-Saturn distance with an explicit ephemeris instance. +func EarthSaturnAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(5, jde, inst) +} + +// EarthSaturnAwayNWithInstance 土星地心距离(n 变体) / n-parameterised Earth-Saturn distance with an explicit ephemeris instance. +func EarthSaturnAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(5, jde, n, inst) +} + +// UranusLWithInstance 天王星日心黄经 / heliocentric longitude of Uranus with an explicit ephemeris instance. +func UranusLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(6, jde, inst) +} + +// UranusBWithInstance 天王星日心黄纬 / heliocentric latitude of Uranus with an explicit ephemeris instance. +func UranusBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(6, jde, inst) +} + +// UranusRWithInstance 天王星日心距离 / heliocentric distance of Uranus with an explicit ephemeris instance. +func UranusRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(6, jde, inst) +} + +// AUranusXYZWithInstance 天王星地心直角坐标 / geocentric rectangular coordinates of Uranus with an explicit ephemeris instance. +func AUranusXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(6, jde, inst) + return x, y, z +} + +// AUranusXWithInstance 天王星地心 X 坐标 / geocentric X coordinate of Uranus with an explicit ephemeris instance. +func AUranusXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := AUranusXYZWithInstance(jde, inst) + return x +} + +// AUranusYWithInstance 天王星地心 Y 坐标 / geocentric Y coordinate of Uranus with an explicit ephemeris instance. +func AUranusYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := AUranusXYZWithInstance(jde, inst) + return y +} + +// AUranusZWithInstance 天王星地心 Z 坐标 / geocentric Z coordinate of Uranus with an explicit ephemeris instance. +func AUranusZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := AUranusXYZWithInstance(jde, inst) + return z +} + +// UranusApparentLoBoWithInstance 天王星视黄经纬 / apparent ecliptic longitude and latitude of Uranus with an explicit ephemeris instance. +func UranusApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(6, jde, inst) +} + +// UranusApparentLoWithInstance 天王星视黄经 / apparent ecliptic longitude of Uranus with an explicit ephemeris instance. +func UranusApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := UranusApparentLoBoWithInstance(jde, inst) + return lo +} + +// UranusApparentBoWithInstance 天王星视黄纬 / apparent ecliptic latitude of Uranus with an explicit ephemeris instance. +func UranusApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := UranusApparentLoBoWithInstance(jde, inst) + return bo +} + +// UranusApparentRaDecWithInstance 天王星视赤经纬 / apparent right ascension and declination of Uranus with an explicit ephemeris instance. +func UranusApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(6, jde, inst) +} + +// UranusApparentRaWithInstance 天王星视赤经 / apparent right ascension of Uranus with an explicit ephemeris instance. +func UranusApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := UranusApparentRaDecWithInstance(jde, inst) + return ra +} + +// UranusApparentDecWithInstance 天王星视赤纬 / apparent declination of Uranus with an explicit ephemeris instance. +func UranusApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := UranusApparentRaDecWithInstance(jde, inst) + return dec +} + +// UranusApparentLoBoNWithInstance 天王星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Uranus with an explicit ephemeris instance. +func UranusApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(6, jde, n, inst) +} + +// UranusApparentLoNWithInstance 天王星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Uranus with an explicit ephemeris instance. +func UranusApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := UranusApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// UranusApparentBoNWithInstance 天王星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Uranus with an explicit ephemeris instance. +func UranusApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := UranusApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// UranusApparentRaDecNWithInstance 天王星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Uranus with an explicit ephemeris instance. +func UranusApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := UranusApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// UranusApparentRaNWithInstance 天王星视赤经(n 变体) / n-parameterised apparent right ascension of Uranus with an explicit ephemeris instance. +func UranusApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := UranusApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// UranusApparentDecNWithInstance 天王星视赤纬(n 变体) / n-parameterised apparent declination of Uranus with an explicit ephemeris instance. +func UranusApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := UranusApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthUranusAwayWithInstance 地天王距离 / Earth-Uranus distance with an explicit ephemeris instance. +func EarthUranusAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(6, jde, inst) +} + +// EarthUranusAwayNWithInstance 天王星地心距离(n 变体) / n-parameterised Earth-Uranus distance with an explicit ephemeris instance. +func EarthUranusAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(6, jde, n, inst) +} + +// NeptuneLWithInstance 海王星日心黄经 / heliocentric longitude of Neptune with an explicit ephemeris instance. +func NeptuneLWithInstance(jde float64, inst *Instance) float64 { + return planetLWithInstance(7, jde, inst) +} + +// NeptuneBWithInstance 海王星日心黄纬 / heliocentric latitude of Neptune with an explicit ephemeris instance. +func NeptuneBWithInstance(jde float64, inst *Instance) float64 { + return planetBWithInstance(7, jde, inst) +} + +// NeptuneRWithInstance 海王星日心距离 / heliocentric distance of Neptune with an explicit ephemeris instance. +func NeptuneRWithInstance(jde float64, inst *Instance) float64 { + return planetRWithInstance(7, jde, inst) +} + +// ANeptuneXYZWithInstance 海王星地心直角坐标 / geocentric rectangular coordinates of Neptune with an explicit ephemeris instance. +func ANeptuneXYZWithInstance(jde float64, inst *Instance) (float64, float64, float64) { + x, y, z, _ := planetGeocentricXYZWithInstance(7, jde, inst) + return x, y, z +} + +// ANeptuneXWithInstance 海王星地心 X 坐标 / geocentric X coordinate of Neptune with an explicit ephemeris instance. +func ANeptuneXWithInstance(jde float64, inst *Instance) float64 { + x, _, _ := ANeptuneXYZWithInstance(jde, inst) + return x +} + +// ANeptuneYWithInstance 海王星地心 Y 坐标 / geocentric Y coordinate of Neptune with an explicit ephemeris instance. +func ANeptuneYWithInstance(jde float64, inst *Instance) float64 { + _, y, _ := ANeptuneXYZWithInstance(jde, inst) + return y +} + +// ANeptuneZWithInstance 海王星地心 Z 坐标 / geocentric Z coordinate of Neptune with an explicit ephemeris instance. +func ANeptuneZWithInstance(jde float64, inst *Instance) float64 { + _, _, z := ANeptuneXYZWithInstance(jde, inst) + return z +} + +// NeptuneApparentLoBoWithInstance 海王星视黄经纬 / apparent ecliptic longitude and latitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentLoBoWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentLoBoWithInstance(7, jde, inst) +} + +// NeptuneApparentLoWithInstance 海王星视黄经 / apparent ecliptic longitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentLoWithInstance(jde float64, inst *Instance) float64 { + lo, _ := NeptuneApparentLoBoWithInstance(jde, inst) + return lo +} + +// NeptuneApparentBoWithInstance 海王星视黄纬 / apparent ecliptic latitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentBoWithInstance(jde float64, inst *Instance) float64 { + _, bo := NeptuneApparentLoBoWithInstance(jde, inst) + return bo +} + +// NeptuneApparentRaDecWithInstance 海王星视赤经纬 / apparent right ascension and declination of Neptune with an explicit ephemeris instance. +func NeptuneApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return planetApparentRaDecWithInstance(7, jde, inst) +} + +// NeptuneApparentRaWithInstance 海王星视赤经 / apparent right ascension of Neptune with an explicit ephemeris instance. +func NeptuneApparentRaWithInstance(jde float64, inst *Instance) float64 { + ra, _ := NeptuneApparentRaDecWithInstance(jde, inst) + return ra +} + +// NeptuneApparentDecWithInstance 海王星视赤纬 / apparent declination of Neptune with an explicit ephemeris instance. +func NeptuneApparentDecWithInstance(jde float64, inst *Instance) float64 { + _, dec := NeptuneApparentRaDecWithInstance(jde, inst) + return dec +} + +// NeptuneApparentLoBoNWithInstance 海王星视黄经纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentLoBoNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return planetApparentLoBoNWithInstance(7, jde, n, inst) +} + +// NeptuneApparentLoNWithInstance 海王星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + lo, _ := NeptuneApparentLoBoNWithInstance(jde, n, inst) + return lo +} + +// NeptuneApparentBoNWithInstance 海王星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Neptune with an explicit ephemeris instance. +func NeptuneApparentBoNWithInstance(jde float64, n int, inst *Instance) float64 { + _, bo := NeptuneApparentLoBoNWithInstance(jde, n, inst) + return bo +} + +// NeptuneApparentRaDecNWithInstance 海王星视赤经纬(n 变体) / n-parameterised apparent right ascension and declination of Neptune with an explicit ephemeris instance. +func NeptuneApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + lo, bo := NeptuneApparentLoBoNWithInstance(jde, n, inst) + return planetApparentRaDecFromLoBo(jde, lo, bo) +} + +// NeptuneApparentRaNWithInstance 海王星视赤经(n 变体) / n-parameterised apparent right ascension of Neptune with an explicit ephemeris instance. +func NeptuneApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + ra, _ := NeptuneApparentRaDecNWithInstance(jde, n, inst) + return ra +} + +// NeptuneApparentDecNWithInstance 海王星视赤纬(n 变体) / n-parameterised apparent declination of Neptune with an explicit ephemeris instance. +func NeptuneApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := NeptuneApparentRaDecNWithInstance(jde, n, inst) + return dec +} + +// EarthNeptuneAwayWithInstance 地海距离 / Earth-Neptune distance with an explicit ephemeris instance. +func EarthNeptuneAwayWithInstance(jde float64, inst *Instance) float64 { + return planetEarthAwayWithInstance(7, jde, inst) +} + +// EarthNeptuneAwayNWithInstance 海王星地心距离(n 变体) / n-parameterised Earth-Neptune distance with an explicit ephemeris instance. +func EarthNeptuneAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetEarthAwayNWithInstance(7, jde, n, inst) +} + +// MercuryMagWithInstance 水星视星等 / apparent magnitude of Mercury with an explicit ephemeris instance. +func MercuryMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return MercuryMag(jde) + } + return planetMagnitudeWithInstance(1, jde, inst, -0.42, 0.0380, -0.000273, 0.000002) +} + +// VenusMagWithInstance 金星视星等 / apparent magnitude of Venus with an explicit ephemeris instance. +func VenusMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return VenusMag(jde) + } + return planetMagnitudeWithInstance(2, jde, inst, -4.40, 0.0009, 0.000239, -0.00000065) +} + +// MarsMagWithInstance 火星视星等 / apparent magnitude of Mars with an explicit ephemeris instance. +func MarsMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return MarsMag(jde) + } + return planetMagnitudeWithInstance(3, jde, inst, -1.52, 0.016, 0, 0) +} + +// JupiterMagWithInstance 木星视星等 / apparent magnitude of Jupiter with an explicit ephemeris instance. +func JupiterMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return JupiterMag(jde) + } + return planetMagnitudeWithInstance(4, jde, inst, -9.40, 0.0005, 0, 0) +} + +// UranusMagWithInstance 天王星视星等 / apparent magnitude of Uranus with an explicit ephemeris instance. +func UranusMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return UranusMag(jde) + } + return planetMagnitudeWithInstance(6, jde, inst, -7.19, 0.016, 0, 0) +} + +// NeptuneMagWithInstance 海王星视星等 / apparent magnitude of Neptune with an explicit ephemeris instance. +func NeptuneMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return NeptuneMag(jde) + } + sunDistance := planetRWithInstance(7, jde, inst) + earthDistance := planetEarthAwayWithInstance(7, jde, inst) + return FloatRound(-6.87+5*math.Log10(sunDistance*earthDistance), 2) +} diff --git a/basic/planet_magnitude_2018.go b/basic/planet_magnitude_2018.go new file mode 100644 index 0000000..a4662d3 --- /dev/null +++ b/basic/planet_magnitude_2018.go @@ -0,0 +1,199 @@ +package basic + +import "math" + +// Uranus 北极 J2000 赤经、赤纬与扁率。 +const ( + uranusPoleRA = 257.311 + uranusPoleDec = -15.175 + uranusFlattening = 0.02293 + j2000ObliquityDeg = 23.4392911 +) + +// planetMagnitude2018 用 Mallama & Hilton 2018 模型求视星等;index 为 1..7(水星..海王星)。 +// 超出模型相位角范围返回 NaN。 +func planetMagnitude2018(index int, jde float64, inst *Instance) float64 { + r := planetRWithInstance(index, jde, inst) + delta := planetEarthAwayWithInstance(index, jde, inst) + earthR := planetRWithInstance(-1, jde, inst) + if !(r > 0) || !(delta > 0) || !(earthR > 0) { + return math.NaN() + } + cosPhase := (r*r + delta*delta - earthR*earthR) / (2 * r * delta) + a := math.Acos(math.Max(-1, math.Min(1, cosPhase))) * 180 / math.Pi + distance := 5 * math.Log10(r*delta) + switch index { + case 1: + return -0.613 + a*(6.3280e-2+a*(-1.6336e-3+a*(3.3644e-5+a*(-3.4265e-7+a*(1.6893e-9-3.0334e-12*a))))) + distance + case 2: + if a <= 163.7 { + return -4.384 + a*(-1.044e-3+a*(3.687e-4+a*(-2.814e-6+8.938e-9*a))) + distance + } + return 236.05828 + a*(-2.81914+8.39034e-3*a) + distance + case 3: + phys := MarsPhysicalNWithInstance(jde, -1, inst) + lon := PlanetCoordinateNWithInstance(3, 0, jde, -1, inst) + return marsMagnitude2018(r, delta, a, phys.SubEarthLongitude, phys.SubSolarLongitude, lon) + case 4: + if a <= 12 { + return -9.395 + a*(-3.7e-4+6.16e-4*a) + distance + } + x := a / 180 + albedo := 1 + x*(-1.507+x*(-0.363+x*(-0.062+x*(2.809-1.876*x)))) + if !(albedo > 0) { + return math.NaN() + } + return -9.428 - 2.5*math.Log10(albedo) + distance + case 5: + if a > 6.5 { + return math.NaN() + } + earthLat, sunLat, _, _, _, _ := saturnRingParametersWithInstance(jde, inst) + be, bs := earthLat*rad, sunLat*rad + tilt := 0.0 + if be*bs > 0 { + tilt = math.Abs(math.Sin(math.Sqrt(be * bs))) + } + return -8.914 - 1.825*tilt + 0.026*a - 0.378*tilt*math.Exp(-2.25*a) + distance + case 6: + phase := 0.0 + if a > 3.1 { + phase = a * (6.587e-3 + 1.045e-4*a) + } + return -7.110 - 0.00084*uranusEffectiveSubLatitude(jde, inst) + phase + distance + case 7: + year := 2000 + (jde-2451545)/365.25 + unit := -7.00 + switch { + case year < 1980: + unit = -6.89 + case year <= 2000: + unit = -6.89 - 0.0054*(year-1980) + } + if a > 1.9 && year > 2000 { + unit += a * (7.944e-3 + 9.617e-5*a) + } + return unit + distance + } + return math.NaN() +} + +// uranusEffectiveSubLatitude 天王星星下点与日下点行星面纬度绝对值的平均(度)。 +func uranusEffectiveSubLatitude(jde float64, inst *Instance) float64 { + pole := eclipticUnitFromEquatorial(uranusPoleRA, uranusPoleDec) + geoLon, geoLat := planetApparentLoBoWithInstance(6, jde, inst) + helioLon, helioLat := planetLWithInstance(6, jde, inst), planetBWithInstance(6, jde, inst) + sub := func(lon, lat float64) float64 { + d := sphericalUnit(lon, lat) + // 星下点方向自行星指向观测者,与视方向相反。 + centric := math.Asin(math.Max(-1, math.Min(1, -(pole[0]*d[0]+pole[1]*d[1]+pole[2]*d[2])))) + graphic := math.Atan(math.Tan(centric) / ((1 - uranusFlattening) * (1 - uranusFlattening))) + return math.Abs(graphic) * 180 / math.Pi + } + return (sub(geoLon, geoLat) + sub(helioLon, helioLat)) / 2 +} + +func sphericalUnit(lonDeg, latDeg float64) [3]float64 { + lon, lat := lonDeg*rad, latDeg*rad + return [3]float64{math.Cos(lat) * math.Cos(lon), math.Cos(lat) * math.Sin(lon), math.Sin(lat)} +} + +func eclipticUnitFromEquatorial(raDeg, decDeg float64) [3]float64 { + v := sphericalUnit(raDeg, decDeg) + s, c := math.Sincos(j2000ObliquityDeg * rad) + return [3]float64{v[0], v[1]*c + v[2]*s, -v[1]*s + v[2]*c} +} + +// MercuryMag2018WithInstance 水星视星等(Mallama & Hilton 2018)/ Mercury magnitude, Mallama & Hilton 2018. +func MercuryMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(1, jde, inst) +} + +// VenusMag2018WithInstance 金星视星等(Mallama & Hilton 2018)/ Venus magnitude, Mallama & Hilton 2018. +func VenusMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(2, jde, inst) +} + +// MarsMag2018WithInstance 火星视星等(Mallama & Hilton 2018,含自转与季节修正)/ Mars magnitude, Mallama & Hilton 2018 with rotation and season terms. +func MarsMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(3, jde, inst) +} + +// JupiterMag2018WithInstance 木星视星等(Mallama & Hilton 2018)/ Jupiter magnitude, Mallama & Hilton 2018. +func JupiterMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(4, jde, inst) +} + +// SaturnMag2018WithInstance 土星视星等(Mallama & Hilton 2018,含环)/ Saturn magnitude with rings, Mallama & Hilton 2018. +func SaturnMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(5, jde, inst) +} + +// UranusMag2018WithInstance 天王星视星等(Mallama & Hilton 2018)/ Uranus magnitude, Mallama & Hilton 2018. +func UranusMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(6, jde, inst) +} + +// NeptuneMag2018WithInstance 海王星视星等(Mallama & Hilton 2018)/ Neptune magnitude, Mallama & Hilton 2018. +func NeptuneMag2018WithInstance(jde float64, inst *Instance) float64 { + return planetMagnitude2018(7, jde, inst) +} + +// 火星自转(有效中央经线,0°..390° 每 10°,首两项为 −20°、−10°)与季节(Ls)星等修正表。 +var ( + marsRotationCorrection = [40]float64{ + 0.024, 0.034, 0.036, 0.045, 0.038, 0.023, 0.015, 0.011, + 0.000, -0.012, -0.018, -0.036, -0.044, -0.059, -0.060, -0.055, + -0.043, -0.041, -0.041, -0.036, -0.036, -0.018, -0.038, -0.011, + 0.002, 0.004, 0.018, 0.019, 0.035, 0.050, 0.035, 0.027, + 0.037, 0.048, 0.025, 0.022, 0.024, 0.034, 0.036, 0.045, + } + marsSeasonCorrection = [40]float64{ + -0.030, -0.017, -0.029, -0.017, -0.014, -0.006, -0.018, -0.020, + -0.014, -0.030, -0.008, -0.040, -0.024, -0.037, -0.036, -0.032, + 0.010, 0.010, -0.001, 0.044, 0.025, -0.004, -0.016, -0.008, + 0.029, -0.054, -0.033, 0.055, 0.017, 0.052, 0.006, 0.087, + 0.006, 0.064, 0.030, 0.019, -0.030, -0.017, -0.029, -0.017, + } +) + +// marsStirling 四阶 Stirling 插值;angle 取 [0, 360),表中 angle=10k 对应下标 k+2。 +func marsStirling(table *[40]float64, angle float64) float64 { + angle = math.Mod(angle, 360) + if angle < 0 { + angle += 360 + } + zero := int(angle / 10) + p := angle/10 - float64(zero) + var d1 [3]float64 + for i := range d1 { + d1[i] = table[zero+i+1] - table[zero+i] + } + d2 := [2]float64{d1[1] - d1[0], d1[2] - d1[1]} + // 四阶差分需要第 4 个一阶差分,表首尾各补两项即为此预留。 + d1b := table[zero+4] - table[zero+3] + d2c := d1b - d1[2] + d3 := [2]float64{d2[1] - d2[0], d2c - d2[1]} + d4 := d3[1] - d3[0] + a4 := d4 / 24 + a3 := (d3[0] + d3[1]) / 12 + a2 := d2[1]/2 - a4 + a1 := (d1[1]+d1[2])/2 - a3 + a0 := table[zero+2] + return a0 + p*(a1+p*(a2+p*(a3+p*a4))) +} + +// marsMagnitude2018 火星视星等:r、delta 为 AU,alpha 为相位角(度),子地、子日经度为西经(度),helioLon 为日心黄经(度)。 +func marsMagnitude2018(r, delta, alpha, subEarthLon, subSunLon, helioLon float64) float64 { + distance := 5 * math.Log10(r*delta) + base := -0.367 + alpha*(-0.02573+0.0003445*alpha) + if alpha <= 50 { + base = -1.601 + alpha*(0.02267-0.0001302*alpha) + } + cm := (subEarthLon + subSunLon) / 2 + if math.Abs(subEarthLon-subSunLon) > 180 { + cm += 180 + } + // Ls 由日心黄经平移 −85° 得到。 + return base + distance + marsStirling(&marsRotationCorrection, cm) + marsStirling(&marsSeasonCorrection, helioLon-85) +} diff --git a/basic/planet_magnitude_2018_test.go b/basic/planet_magnitude_2018_test.go new file mode 100644 index 0000000..9b5dfeb --- /dev/null +++ b/basic/planet_magnitude_2018_test.go @@ -0,0 +1,59 @@ +// 被测契约:2018 模型在中小相位角下与旧模型同量级,且不产生 NaN;大相位角两者本就分歧,只查有限。 +package basic + +import ( + "math" + "testing" +) + +func TestPlanetMagnitude2018CloseToClassic(t *testing.T) { + classic := []func(float64) float64{nil, MercuryMag, VenusMag, MarsMag, JupiterMag, SaturnMag, UranusMag, NeptuneMag} + limits := []float64{0, 0.6, 0.3, 0.3, 0.1, 0.5, 0.1, 0.2} + for jd := 2451545.0; jd < 2462502; jd += 37.3 { + for i := 1; i <= 7; i++ { + got := planetMagnitude2018(i, jd, nil) + if math.IsNaN(got) { + t.Fatalf("index %d jd %.1f: NaN", i, jd) + } + if magnitudeTestPhase(i, jd) > 100 { + continue + } + if d := math.Abs(got - classic[i](jd)); d > limits[i] { + t.Errorf("index %d jd %.1f: 2018 %.3f vs classic %.2f", i, jd, got, classic[i](jd)) + } + } + } +} + +func TestNeptuneMagnitude2018UnitEpochs(t *testing.T) { + for _, c := range []struct{ jd, unit float64 }{ + {2444239.5 - 3653, -6.89}, {2451545.0 + 3653, -7.00}, + } { + r, delta := NeptuneR(c.jd), EarthNeptuneAway(c.jd) + got := planetMagnitude2018(7, c.jd, nil) - 5*math.Log10(r*delta) + if math.Abs(got-c.unit) > 0.02 { + t.Errorf("jd %.1f: unit magnitude %.3f, want about %.2f", c.jd, got, c.unit) + } + } +} + +func magnitudeTestPhase(index int, jd float64) float64 { + r, delta, earth := planetRWithInstance(index, jd, nil), planetEarthAwayWithInstance(index, jd, nil), planetRWithInstance(-1, jd, nil) + return math.Acos((r*r+delta*delta-earth*earth)/(2*r*delta)) * 180 / math.Pi +} + +// 输入取自模型作者随源码发布的测试行(日心距、地心距、相位角、子地/子日西经、日心黄经),期望值保留三位小数。 +func TestMarsMagnitude2018RotationAndSeason(t *testing.T) { + for _, c := range [][7]float64{ + {1.381191244505, 0.37274381097911, 4.8948, 329.27, 330.76, 334.4996, -2.862}, + {1.664150453905, 2.58995164518460, 11.5877, 76.84, 64.66, 147.3485, 1.788}, + {1.591952180003, 3.85882552272013, 167.9000, 29.50, 221.11, 212.8886, 8.977}, + } { + if got := marsMagnitude2018(c[0], c[1], c[2], c[3], c[4], c[5]); math.Abs(got-c[6]) > 0.001 { + t.Errorf("got %.4f want %.3f", got, c[6]) + } + } + if d := marsStirling(&marsRotationCorrection, 359.999) - marsStirling(&marsRotationCorrection, 0); math.Abs(d) > 1e-3 { + t.Errorf("rotation correction not continuous across 0°: %.4f", d) + } +} diff --git a/basic/planet_phase.go b/basic/planet_phase.go index 6ce9a88..ad31de9 100644 --- a/basic/planet_phase.go +++ b/basic/planet_phase.go @@ -1,7 +1,6 @@ package basic import ( - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -12,9 +11,9 @@ func MercuryPhaseAngle(jde float64) float64 { return MercuryPhaseAngleN(jde, -1) } -// MercuryPhaseAngleN 水星相位角(截断版) / truncated phase angle of Mercury. -func MercuryPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(1, jd, n) +// MercuryPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(1, jd, n, inst) } // MercuryIlluminatedFraction 水星被照亮比例 / illuminated fraction of Mercury. @@ -22,9 +21,9 @@ func MercuryIlluminatedFraction(jd float64) float64 { return MercuryIlluminatedFractionN(jd, -1) } -// MercuryIlluminatedFractionN 水星被照亮比例(截断版) / truncated illuminated fraction of Mercury. -func MercuryIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(1, jd, n) +// MercuryIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(1, jd, n, inst) } // MercuryBrightLimbPositionAngle 水星亮面中心位置角 / position angle of Mercury bright limb. @@ -32,9 +31,9 @@ func MercuryBrightLimbPositionAngle(jde float64) float64 { return MercuryBrightLimbPositionAngleN(jde, -1) } -// MercuryBrightLimbPositionAngleN 水星亮面中心位置角(截断版) / truncated position angle of Mercury bright limb. -func MercuryBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, MercuryApparentRaDecN) +// MercuryBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jde float64, n int) (float64, float64) { return MercuryApparentRaDecNWithInstance(jde, n, inst) }, inst) } // VenusPhaseAngle 金星相位角 / phase angle of Venus. @@ -42,9 +41,9 @@ func VenusPhaseAngle(jde float64) float64 { return VenusPhaseAngleN(jde, -1) } -// VenusPhaseAngleN 金星相位角(截断版) / truncated phase angle of Venus. -func VenusPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(2, jd, n) +// VenusPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(2, jd, n, inst) } // VenusIlluminatedFraction 金星被照亮比例 / illuminated fraction of Venus. @@ -52,9 +51,9 @@ func VenusIlluminatedFraction(jd float64) float64 { return VenusIlluminatedFractionN(jd, -1) } -// VenusIlluminatedFractionN 金星被照亮比例(截断版) / truncated illuminated fraction of Venus. -func VenusIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(2, jd, n) +// VenusIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(2, jd, n, inst) } // VenusBrightLimbPositionAngle 金星亮面中心位置角 / position angle of Venus bright limb. @@ -62,9 +61,9 @@ func VenusBrightLimbPositionAngle(jde float64) float64 { return VenusBrightLimbPositionAngleN(jde, -1) } -// VenusBrightLimbPositionAngleN 金星亮面中心位置角(截断版) / truncated position angle of Venus bright limb. -func VenusBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, VenusApparentRaDecN) +// VenusBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return VenusApparentRaDecNWithInstance(jd, n, inst) }, inst) } // MarsPhaseAngle 火星相位角 / phase angle of Mars. @@ -72,9 +71,9 @@ func MarsPhaseAngle(jde float64) float64 { return MarsPhaseAngleN(jde, -1) } -// MarsPhaseAngleN 火星相位角(截断版) / truncated phase angle of Mars. -func MarsPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(3, jd, n) +// MarsPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(3, jd, n, inst) } // MarsIlluminatedFraction 火星被照亮比例 / illuminated fraction of Mars. @@ -82,9 +81,9 @@ func MarsIlluminatedFraction(jd float64) float64 { return MarsIlluminatedFractionN(jd, -1) } -// MarsIlluminatedFractionN 火星被照亮比例(截断版) / truncated illuminated fraction of Mars. -func MarsIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(3, jd, n) +// MarsIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(3, jd, n, inst) } // MarsBrightLimbPositionAngle 火星亮面中心位置角 / position angle of Mars bright limb. @@ -92,9 +91,9 @@ func MarsBrightLimbPositionAngle(jde float64) float64 { return MarsBrightLimbPositionAngleN(jde, -1) } -// MarsBrightLimbPositionAngleN 火星亮面中心位置角(截断版) / truncated position angle of Mars bright limb. -func MarsBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, MarsApparentRaDecN) +// MarsBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return MarsApparentRaDecNWithInstance(jd, n, inst) }, inst) } // JupiterPhaseAngle 木星相位角 / phase angle of Jupiter. @@ -102,9 +101,9 @@ func JupiterPhaseAngle(jde float64) float64 { return JupiterPhaseAngleN(jde, -1) } -// JupiterPhaseAngleN 木星相位角(截断版) / truncated phase angle of Jupiter. -func JupiterPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(4, jd, n) +// JupiterPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(4, jd, n, inst) } // JupiterIlluminatedFraction 木星被照亮比例 / illuminated fraction of Jupiter. @@ -112,9 +111,9 @@ func JupiterIlluminatedFraction(jd float64) float64 { return JupiterIlluminatedFractionN(jd, -1) } -// JupiterIlluminatedFractionN 木星被照亮比例(截断版) / truncated illuminated fraction of Jupiter. -func JupiterIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(4, jd, n) +// JupiterIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(4, jd, n, inst) } // JupiterBrightLimbPositionAngle 木星亮面中心位置角 / position angle of Jupiter bright limb. @@ -122,9 +121,9 @@ func JupiterBrightLimbPositionAngle(jde float64) float64 { return JupiterBrightLimbPositionAngleN(jde, -1) } -// JupiterBrightLimbPositionAngleN 木星亮面中心位置角(截断版) / truncated position angle of Jupiter bright limb. -func JupiterBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, JupiterApparentRaDecN) +// JupiterBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return JupiterApparentRaDecNWithInstance(jd, n, inst) }, inst) } // SaturnPhaseAngle 土星相位角 / phase angle of Saturn. @@ -132,9 +131,9 @@ func SaturnPhaseAngle(jde float64) float64 { return SaturnPhaseAngleN(jde, -1) } -// SaturnPhaseAngleN 土星相位角(截断版) / truncated phase angle of Saturn. -func SaturnPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(5, jd, n) +// SaturnPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(5, jd, n, inst) } // SaturnIlluminatedFraction 土星被照亮比例 / illuminated fraction of Saturn. @@ -142,9 +141,9 @@ func SaturnIlluminatedFraction(jd float64) float64 { return SaturnIlluminatedFractionN(jd, -1) } -// SaturnIlluminatedFractionN 土星被照亮比例(截断版) / truncated illuminated fraction of Saturn. -func SaturnIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(5, jd, n) +// SaturnIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(5, jd, n, inst) } // SaturnBrightLimbPositionAngle 土星亮面中心位置角 / position angle of Saturn bright limb. @@ -152,9 +151,9 @@ func SaturnBrightLimbPositionAngle(jde float64) float64 { return SaturnBrightLimbPositionAngleN(jde, -1) } -// SaturnBrightLimbPositionAngleN 土星亮面中心位置角(截断版) / truncated position angle of Saturn bright limb. -func SaturnBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, SaturnApparentRaDecN) +// SaturnBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return SaturnApparentRaDecNWithInstance(jd, n, inst) }, inst) } // UranusPhaseAngle 天王星相位角 / phase angle of Uranus. @@ -162,9 +161,9 @@ func UranusPhaseAngle(jde float64) float64 { return UranusPhaseAngleN(jde, -1) } -// UranusPhaseAngleN 天王星相位角(截断版) / truncated phase angle of Uranus. -func UranusPhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(6, jd, n) +// UranusPhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusPhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(6, jd, n, inst) } // UranusIlluminatedFraction 天王星被照亮比例 / illuminated fraction of Uranus. @@ -172,9 +171,9 @@ func UranusIlluminatedFraction(jd float64) float64 { return UranusIlluminatedFractionN(jd, -1) } -// UranusIlluminatedFractionN 天王星被照亮比例(截断版) / truncated illuminated fraction of Uranus. -func UranusIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(6, jd, n) +// UranusIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(6, jd, n, inst) } // UranusBrightLimbPositionAngle 天王星亮面中心位置角 / position angle of Uranus bright limb. @@ -182,9 +181,9 @@ func UranusBrightLimbPositionAngle(jde float64) float64 { return UranusBrightLimbPositionAngleN(jde, -1) } -// UranusBrightLimbPositionAngleN 天王星亮面中心位置角(截断版) / truncated position angle of Uranus bright limb. -func UranusBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, UranusApparentRaDecN) +// UranusBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return UranusApparentRaDecNWithInstance(jd, n, inst) }, inst) } // NeptunePhaseAngle 海王星相位角 / phase angle of Neptune. @@ -192,9 +191,9 @@ func NeptunePhaseAngle(jde float64) float64 { return NeptunePhaseAngleN(jde, -1) } -// NeptunePhaseAngleN 海王星相位角(截断版) / truncated phase angle of Neptune. -func NeptunePhaseAngleN(jd float64, n int) float64 { - return planetPhaseAngleN(7, jd, n) +// NeptunePhaseAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptunePhaseAngleNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetPhaseAngleN(7, jd, n, inst) } // NeptuneIlluminatedFraction 海王星被照亮比例 / illuminated fraction of Neptune. @@ -202,9 +201,9 @@ func NeptuneIlluminatedFraction(jd float64) float64 { return NeptuneIlluminatedFractionN(jd, -1) } -// NeptuneIlluminatedFractionN 海王星被照亮比例(截断版) / truncated illuminated fraction of Neptune. -func NeptuneIlluminatedFractionN(jd float64, n int) float64 { - return planetIlluminatedFractionN(7, jd, n) +// NeptuneIlluminatedFractionNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneIlluminatedFractionNWithInstance(jd float64, n int, inst *Instance) float64 { + return planetIlluminatedFractionN(7, jd, n, inst) } // NeptuneBrightLimbPositionAngle 海王星亮面中心位置角 / position angle of Neptune bright limb. @@ -212,29 +211,37 @@ func NeptuneBrightLimbPositionAngle(jde float64) float64 { return NeptuneBrightLimbPositionAngleN(jde, -1) } -// NeptuneBrightLimbPositionAngleN 海王星亮面中心位置角(截断版) / truncated position angle of Neptune bright limb. -func NeptuneBrightLimbPositionAngleN(jde float64, n int) float64 { - return planetBrightLimbPositionAngleN(jde, n, NeptuneApparentRaDecN) +// NeptuneBrightLimbPositionAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneBrightLimbPositionAngleNWithInstance(jde float64, n int, inst *Instance) float64 { + return planetBrightLimbPositionAngleN(jde, n, func(jd float64, n int) (float64, float64) { return NeptuneApparentRaDecNWithInstance(jd, n, inst) }, inst) } -func planetPhaseAngleN(planetIndex int, jd float64, n int) float64 { - return ArcCos(planetPhaseCosineN(planetIndex, jd, n)) +func planetPhaseAngleN(planetIndex int, jd float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return ArcCos(planetPhaseCosineN(planetIndex, jd, n, inst)) } -func planetIlluminatedFractionN(planetIndex int, jd float64, n int) float64 { - return (1 + planetPhaseCosineN(planetIndex, jd, n)) / 2 +func planetIlluminatedFractionN(planetIndex int, jd float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return (1 + planetPhaseCosineN(planetIndex, jd, n, inst)) / 2 } -func planetPhaseCosineN(planetIndex int, jde float64, n int) float64 { - planetSunDistance := planet.WherePlanetN(planetIndex, 2, jde, n) - planetEarthDistance := planetEarthAwayN(planetIndex, jde, n) - earthSunDistance := EarthAwayN(jde, n) +func planetPhaseCosineN(planetIndex int, jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + planetSunDistance := PlanetCoordinateNWithInstance(planetIndex, 2, jde, n, inst) + planetEarthDistance := planetEarthAwayNWithInstance(planetIndex, jde, n, inst) + earthSunDistance := EarthAwayNWithInstance(jde, n, inst) cosine := (planetSunDistance*planetSunDistance + planetEarthDistance*planetEarthDistance - earthSunDistance*earthSunDistance) / (2 * planetSunDistance * planetEarthDistance) return clampUnit(cosine) } -func planetBrightLimbPositionAngleN(jde float64, n int, apparentRaDec planetRaDecNFunc) float64 { - sunRa, sunDec := HSunApparentRaDecN(jde, n) +func planetBrightLimbPositionAngleN(jde float64, n int, apparentRaDec planetRaDecNFunc, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sunRa, sunDec := HSunApparentRaDecNWithInstance(jde, n, inst) planetRa, planetDec := apparentRaDec(jde, n) y := Cos(sunDec) * Sin(sunRa-planetRa) x := Sin(sunDec)*Cos(planetDec) - Cos(sunDec)*Sin(planetDec)*Cos(sunRa-planetRa) diff --git a/basic/planet_physical.go b/basic/planet_physical.go index 0a151d9..7c7a974 100644 --- a/basic/planet_physical.go +++ b/basic/planet_physical.go @@ -3,7 +3,6 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -94,9 +93,9 @@ func MercuryPhysical(jd float64) PlanetPhysicalInfo { return MercuryPhysicalN(jd, -1) } -// MercuryPhysicalN 水星物理观测参数(截断版) / truncated physical observing parameters of Mercury. -func MercuryPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, mercuryPhysicalModel) +// MercuryPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, mercuryPhysicalModel, inst) } // VenusPhysical 金星物理观测参数 / physical observing parameters of Venus. @@ -104,9 +103,9 @@ func VenusPhysical(jd float64) PlanetPhysicalInfo { return VenusPhysicalN(jd, -1) } -// VenusPhysicalN 金星物理观测参数(截断版) / truncated physical observing parameters of Venus. -func VenusPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, venusPhysicalModel) +// VenusPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, venusPhysicalModel, inst) } // MarsPhysical 火星物理观测参数 / physical observing parameters of Mars. @@ -114,9 +113,9 @@ func MarsPhysical(jd float64) PlanetPhysicalInfo { return MarsPhysicalN(jd, -1) } -// MarsPhysicalN 火星物理观测参数(截断版) / truncated physical observing parameters of Mars. -func MarsPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, marsPhysicalModel) +// MarsPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, marsPhysicalModel, inst) } // JupiterPhysical 木星物理观测参数 / physical observing parameters of Jupiter. @@ -124,9 +123,9 @@ func JupiterPhysical(jd float64) PlanetPhysicalInfo { return JupiterPhysicalN(jd, -1) } -// JupiterPhysicalN 木星物理观测参数(截断版) / truncated physical observing parameters of Jupiter. -func JupiterPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, jupiterPhysicalModel) +// JupiterPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, jupiterPhysicalModel, inst) } // SaturnPhysical 土星物理观测参数 / physical observing parameters of Saturn. @@ -134,9 +133,9 @@ func SaturnPhysical(jd float64) PlanetPhysicalInfo { return SaturnPhysicalN(jd, -1) } -// SaturnPhysicalN 土星物理观测参数(截断版) / truncated physical observing parameters of Saturn. -func SaturnPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, saturnPhysicalModel) +// SaturnPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, saturnPhysicalModel, inst) } // UranusPhysical 天王星物理观测参数 / physical observing parameters of Uranus. @@ -144,9 +143,9 @@ func UranusPhysical(jd float64) PlanetPhysicalInfo { return UranusPhysicalN(jd, -1) } -// UranusPhysicalN 天王星物理观测参数(截断版) / truncated physical observing parameters of Uranus. -func UranusPhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, uranusPhysicalModel) +// UranusPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusPhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, uranusPhysicalModel, inst) } // NeptunePhysical 海王星物理观测参数 / physical observing parameters of Neptune. @@ -154,23 +153,25 @@ func NeptunePhysical(jd float64) PlanetPhysicalInfo { return NeptunePhysicalN(jd, -1) } -// NeptunePhysicalN 海王星物理观测参数(截断版) / truncated physical observing parameters of Neptune. -func NeptunePhysicalN(jd float64, n int) PlanetPhysicalInfo { - return planetPhysicalN(jd, n, neptunePhysicalModel) +// NeptunePhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptunePhysicalNWithInstance(jd float64, n int, inst *Instance) PlanetPhysicalInfo { + return planetPhysicalN(jd, n, neptunePhysicalModel, inst) } -func planetPhysicalN(jd float64, n int, model planetPhysicalModel) PlanetPhysicalInfo { - initialX, initialY, initialZ := planetXYZN(model.planetIndex, jd, n) +func planetPhysicalN(jd float64, n int, model planetPhysicalModel, instances ...*Instance) PlanetPhysicalInfo { + inst := instanceValue(instances) + + initialX, initialY, initialZ := planetXYZN(model.planetIndex, jd, n, inst) targetVector := Vector3{initialX, initialY, initialZ} lightTimeDays := astronomicalUnitLightTimeDays * vectorMagnitude(targetVector) targetJDE := jd - lightTimeDays - geoX, geoY, geoZ := planetXYZN(model.planetIndex, targetJDE, n) + geoX, geoY, geoZ := planetXYZN(model.planetIndex, targetJDE, n, inst) geocentricVector := Vector3{geoX, geoY, geoZ} observerDirection := normalizeVector(Vector3{-geocentricVector[0], -geocentricVector[1], -geocentricVector[2]}) - heliocentricLongitude := planet.WherePlanetN(model.planetIndex, 0, targetJDE, n) - heliocentricLatitude := planet.WherePlanetN(model.planetIndex, 1, targetJDE, n) + heliocentricLongitude := PlanetCoordinateNWithInstance(model.planetIndex, 0, targetJDE, n, inst) + heliocentricLatitude := PlanetCoordinateNWithInstance(model.planetIndex, 1, targetJDE, n, inst) solarDirection := normalizeVector(eclipticCartesian(heliocentricLongitude+180, -heliocentricLatitude, 1)) obliquity := EclipticObliquity(targetJDE, false) diff --git a/basic/planet_transit.go b/basic/planet_transit.go index 79ca182..00040ef 100644 --- a/basic/planet_transit.go +++ b/basic/planet_transit.go @@ -75,7 +75,9 @@ type planetTransitState struct { internalContactMetric float64 } -func mercuryTransitConfig() planetTransitConfig { +func mercuryTransitConfig(instances ...*Instance) planetTransitConfig { + inst := instanceValue(instances) + return planetTransitConfig{ planetIndex: 1, synodicPeriodDays: MERCURY_S_PERIOD, @@ -83,16 +85,18 @@ func mercuryTransitConfig() planetTransitConfig { seasonWindowDays: 12, latitudePrefilter: 1.0, conjunctionStepDay: 0.00001, - apparentLoN: MercuryApparentLoN, - apparentBoN: MercuryApparentBoN, - apparentRaDecN: MercuryApparentRaDecN, - semidiameterN: MercurySemidiameterN, - earthDistanceN: EarthMercuryAwayN, - nodeN: MercuryAscendingNodeN, + apparentLoN: func(jd float64, n int) float64 { return MercuryApparentLoNWithInstance(jd, n, inst) }, + apparentBoN: func(jd float64, n int) float64 { return MercuryApparentBoNWithInstance(jd, n, inst) }, + apparentRaDecN: func(jde float64, n int) (float64, float64) { return MercuryApparentRaDecNWithInstance(jde, n, inst) }, + semidiameterN: func(jde float64, n int) float64 { return MercurySemidiameterNWithInstance(jde, n, inst) }, + earthDistanceN: func(jd float64, n int) float64 { return EarthMercuryAwayNWithInstance(jd, n, inst) }, + nodeN: func(jd float64, n int) float64 { return MercuryAscendingNodeNWithInstance(jd, n, inst) }, } } -func venusTransitConfig() planetTransitConfig { +func venusTransitConfig(instances ...*Instance) planetTransitConfig { + inst := instanceValue(instances) + return planetTransitConfig{ planetIndex: 2, synodicPeriodDays: VENUS_S_PERIOD, @@ -100,52 +104,54 @@ func venusTransitConfig() planetTransitConfig { seasonWindowDays: 8, latitudePrefilter: 0.8, conjunctionStepDay: 0.00001, - apparentLoN: VenusApparentLoN, - apparentBoN: VenusApparentBoN, - apparentRaDecN: VenusApparentRaDecN, - semidiameterN: VenusSemidiameterN, - earthDistanceN: EarthVenusAwayN, - nodeN: VenusAscendingNodeN, + apparentLoN: func(jd float64, n int) float64 { return VenusApparentLoNWithInstance(jd, n, inst) }, + apparentBoN: func(jd float64, n int) float64 { return VenusApparentBoNWithInstance(jd, n, inst) }, + apparentRaDecN: func(jd float64, n int) (float64, float64) { return VenusApparentRaDecNWithInstance(jd, n, inst) }, + semidiameterN: func(jde float64, n int) float64 { return VenusSemidiameterNWithInstance(jde, n, inst) }, + earthDistanceN: func(jd float64, n int) float64 { return EarthVenusAwayNWithInstance(jd, n, inst) }, + nodeN: func(jd float64, n int) float64 { return VenusAscendingNodeNWithInstance(jd, n, inst) }, } } -// NextMercuryTransit 返回给定时刻之后的下一次地心水星凌日。 -func NextMercuryTransit(jde float64) PlanetTransitResult { - result, _ := searchPlanetTransit(jde, mercuryTransitConfig(), 1, false) +// NextMercuryTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextMercuryTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + result, _ := searchPlanetTransit(jde, mercuryTransitConfig(inst), 1, false, inst) return result } -// LastMercuryTransit 返回给定时刻之前的上一次地心水星凌日。 -func LastMercuryTransit(jde float64) PlanetTransitResult { - result, _ := searchPlanetTransit(jde, mercuryTransitConfig(), -1, true) +// LastMercuryTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastMercuryTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + result, _ := searchPlanetTransit(jde, mercuryTransitConfig(inst), -1, true, inst) return result } -// ClosestMercuryTransit 返回距给定时刻最近的一次地心水星凌日。 -func ClosestMercuryTransit(jde float64) PlanetTransitResult { - return closestPlanetTransit(jde, mercuryTransitConfig()) +// ClosestMercuryTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestMercuryTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + return closestPlanetTransit(jde, mercuryTransitConfig(inst), inst) } -// NextVenusTransit 返回给定时刻之后的下一次地心金星凌日。 -func NextVenusTransit(jde float64) PlanetTransitResult { - result, _ := searchPlanetTransit(jde, venusTransitConfig(), 1, false) +// NextVenusTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + result, _ := searchPlanetTransit(jde, venusTransitConfig(inst), 1, false, inst) return result } -// LastVenusTransit 返回给定时刻之前的上一次地心金星凌日。 -func LastVenusTransit(jde float64) PlanetTransitResult { - result, _ := searchPlanetTransit(jde, venusTransitConfig(), -1, true) +// LastVenusTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + result, _ := searchPlanetTransit(jde, venusTransitConfig(inst), -1, true, inst) return result } -// ClosestVenusTransit 返回距给定时刻最近的一次地心金星凌日。 -func ClosestVenusTransit(jde float64) PlanetTransitResult { - return closestPlanetTransit(jde, venusTransitConfig()) +// ClosestVenusTransitWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func ClosestVenusTransitWithInstance(jde float64, inst *Instance) PlanetTransitResult { + return closestPlanetTransit(jde, venusTransitConfig(inst), inst) } -func closestPlanetTransit(jde float64, cfg planetTransitConfig) PlanetTransitResult { - last, hasLast := searchPlanetTransit(jde, cfg, -1, true) - next, hasNext := searchPlanetTransit(jde, cfg, 1, false) +func closestPlanetTransit(jde float64, cfg planetTransitConfig, instances ...*Instance) PlanetTransitResult { + inst := instanceValue(instances) + + last, hasLast := searchPlanetTransit(jde, cfg, -1, true, inst) + next, hasNext := searchPlanetTransit(jde, cfg, 1, false, inst) switch { case hasLast && !hasNext: return last @@ -160,24 +166,27 @@ func closestPlanetTransit(jde float64, cfg planetTransitConfig) PlanetTransitRes return next } -func searchPlanetTransit(jd float64, cfg planetTransitConfig, direction int, includeCurrent bool) (PlanetTransitResult, bool) { +func searchPlanetTransit(jd float64, cfg planetTransitConfig, direction int, includeCurrent bool, instances ...*Instance) (PlanetTransitResult, bool) { + inst := instanceValue(instances) + if !isFiniteFloat(jd) || direction == 0 { return PlanetTransitResult{}, false } targetTT := UTC2TT(jd) - probeTT := targetTT + // 节点季可能略早于查询时刻而凌甚仍在其后,从一个季窗之前起探,方向由凌甚时刻再判。 + probeTT := targetTT - float64(direction)*cfg.seasonWindowDays for i := 0; i < planetTransitSearchLimit; i++ { - seasonTT, ok := nextPlanetTransitSeasonTT(probeTT, cfg, direction) + seasonTT, ok := nextPlanetTransitSeasonTT(probeTT, cfg, direction, inst) if !ok { return PlanetTransitResult{}, false } seedTT := nearestPlanetTransitInferiorSeedTT(seasonTT, cfg) if math.Abs(seedTT-seasonTT) <= cfg.seasonWindowDays { - conjunctionTT := refinePlanetTransitInferiorConjunctionTT(seedTT, cfg) - if math.Abs(conjunctionTT-seasonTT) <= cfg.seasonWindowDays+1 && isPotentialPlanetTransit(conjunctionTT, cfg) { - resultTT, ok := planetTransitAtInferiorConjunctionTT(conjunctionTT, cfg) + conjunctionTT := refinePlanetTransitInferiorConjunctionTT(seedTT, cfg, inst) + if math.Abs(conjunctionTT-seasonTT) <= cfg.seasonWindowDays+1 && isPotentialPlanetTransit(conjunctionTT, cfg, inst) { + resultTT, ok := planetTransitAtInferiorConjunctionTT(conjunctionTT, cfg, inst) if ok && planetTransitMatchesDirection(resultTT.Greatest, targetTT, direction, includeCurrent) { return planetTransitResultTTToUT(resultTT), true } @@ -190,11 +199,13 @@ func searchPlanetTransit(jd float64, cfg planetTransitConfig, direction int, inc return PlanetTransitResult{}, false } -func nextPlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, direction int) (float64, bool) { +func nextPlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, direction int, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + best := math.NaN() for nodeOffset := 0; nodeOffset <= 1; nodeOffset++ { - candidate := estimatePlanetTransitSeasonTT(jdTT, cfg, nodeOffset, direction) - candidate = refinePlanetTransitSeasonTT(candidate, cfg, nodeOffset) + candidate := estimatePlanetTransitSeasonTT(jdTT, cfg, nodeOffset, direction, inst) + candidate = refinePlanetTransitSeasonTT(candidate, cfg, nodeOffset, inst) if !isFiniteFloat(candidate) { continue } @@ -205,7 +216,7 @@ func nextPlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, direction break } candidate += float64(direction) * planetTransitTropicalYearDays - candidate = refinePlanetTransitSeasonTT(candidate, cfg, nodeOffset) + candidate = refinePlanetTransitSeasonTT(candidate, cfg, nodeOffset, inst) if !isFiniteFloat(candidate) { break } @@ -223,8 +234,10 @@ func nextPlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, direction return best, true } -func estimatePlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, nodeOffset int, direction int) float64 { - sunLongitude := HSunApparentLoN(jdTT, planetTransitCoarseN) +func estimatePlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, nodeOffset int, direction int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sunLongitude := HSunApparentLoNWithInstance(jdTT, planetTransitCoarseN, inst) nodeLongitude := planetTransitNodeLongitude(jdTT, cfg, nodeOffset, planetTransitCoarseN) if direction > 0 { delta := Limit360(nodeLongitude - sunLongitude) @@ -240,13 +253,15 @@ func estimatePlanetTransitSeasonTT(jdTT float64, cfg planetTransitConfig, nodeOf return jdTT - delta/planetTransitMeanSolarMotionDegPerDay } -func refinePlanetTransitSeasonTT(seedTT float64, cfg planetTransitConfig, nodeOffset int) float64 { +func refinePlanetTransitSeasonTT(seedTT float64, cfg planetTransitConfig, nodeOffset int, instances ...*Instance) float64 { + inst := instanceValue(instances) + current := seedTT for i := 0; i < 8; i++ { prev := current - value := planetTransitSunNodeLongitudeDelta(prev, cfg, nodeOffset) - slope := (planetTransitSunNodeLongitudeDelta(prev+0.5, cfg, nodeOffset) - - planetTransitSunNodeLongitudeDelta(prev-0.5, cfg, nodeOffset)) / 1.0 + value := planetTransitSunNodeLongitudeDelta(prev, cfg, nodeOffset, inst) + slope := (planetTransitSunNodeLongitudeDelta(prev+0.5, cfg, nodeOffset, inst) - + planetTransitSunNodeLongitudeDelta(prev-0.5, cfg, nodeOffset, inst)) / 1.0 if slope == 0 || !isFiniteFloat(slope) { break } @@ -258,8 +273,10 @@ func refinePlanetTransitSeasonTT(seedTT float64, cfg planetTransitConfig, nodeOf return current } -func planetTransitSunNodeLongitudeDelta(jdTT float64, cfg planetTransitConfig, nodeOffset int) float64 { - return planetTransitAngleDelta(HSunApparentLoN(jdTT, planetTransitCoarseN) - +func planetTransitSunNodeLongitudeDelta(jdTT float64, cfg planetTransitConfig, nodeOffset int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return planetTransitAngleDelta(HSunApparentLoNWithInstance(jdTT, planetTransitCoarseN, inst) - planetTransitNodeLongitude(jdTT, cfg, nodeOffset, planetTransitCoarseN)) } @@ -272,13 +289,15 @@ func nearestPlanetTransitInferiorSeedTT(seasonTT float64, cfg planetTransitConfi return cfg.anchorInferiorTT + k*cfg.synodicPeriodDays } -func refinePlanetTransitInferiorConjunctionTT(seedTT float64, cfg planetTransitConfig) float64 { +func refinePlanetTransitInferiorConjunctionTT(seedTT float64, cfg planetTransitConfig, instances ...*Instance) float64 { + inst := instanceValue(instances) + current := seedTT for i := 0; i < 4; i++ { prev := current - value := planetTransitLongitudeDeltaN(prev, cfg, planetTransitCoarseN) - slope := (planetTransitLongitudeDeltaN(prev+cfg.conjunctionStepDay, cfg, planetTransitCoarseN) - - planetTransitLongitudeDeltaN(prev-cfg.conjunctionStepDay, cfg, planetTransitCoarseN)) / (2 * cfg.conjunctionStepDay) + value := planetTransitLongitudeDeltaN(prev, cfg, planetTransitCoarseN, inst) + slope := (planetTransitLongitudeDeltaN(prev+cfg.conjunctionStepDay, cfg, planetTransitCoarseN, inst) - + planetTransitLongitudeDeltaN(prev-cfg.conjunctionStepDay, cfg, planetTransitCoarseN, inst)) / (2 * cfg.conjunctionStepDay) if slope == 0 || !isFiniteFloat(slope) { break } @@ -289,9 +308,9 @@ func refinePlanetTransitInferiorConjunctionTT(seedTT float64, cfg planetTransitC } for i := 0; i < 8; i++ { prev := current - value := planetTransitLongitudeDeltaN(prev, cfg, -1) - slope := (planetTransitLongitudeDeltaN(prev+cfg.conjunctionStepDay, cfg, -1) - - planetTransitLongitudeDeltaN(prev-cfg.conjunctionStepDay, cfg, -1)) / (2 * cfg.conjunctionStepDay) + value := planetTransitLongitudeDeltaN(prev, cfg, -1, inst) + slope := (planetTransitLongitudeDeltaN(prev+cfg.conjunctionStepDay, cfg, -1, inst) - + planetTransitLongitudeDeltaN(prev-cfg.conjunctionStepDay, cfg, -1, inst)) / (2 * cfg.conjunctionStepDay) if slope == 0 || !isFiniteFloat(slope) { break } @@ -303,20 +322,26 @@ func refinePlanetTransitInferiorConjunctionTT(seedTT float64, cfg planetTransitC return current } -func planetTransitLongitudeDeltaN(jdTT float64, cfg planetTransitConfig, n int) float64 { - return planetTransitAngleDelta(cfg.apparentLoN(jdTT, n) - HSunApparentLoN(jdTT, n)) +func planetTransitLongitudeDeltaN(jdTT float64, cfg planetTransitConfig, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return planetTransitAngleDelta(cfg.apparentLoN(jdTT, n) - HSunApparentLoNWithInstance(jdTT, n, inst)) } -func isPotentialPlanetTransit(conjunctionTT float64, cfg planetTransitConfig) bool { - if cfg.earthDistanceN(conjunctionTT, planetTransitCoarseN) > EarthAwayN(conjunctionTT, planetTransitCoarseN) { +func isPotentialPlanetTransit(conjunctionTT float64, cfg planetTransitConfig, instances ...*Instance) bool { + inst := instanceValue(instances) + + if cfg.earthDistanceN(conjunctionTT, planetTransitCoarseN) > EarthAwayNWithInstance(conjunctionTT, planetTransitCoarseN, inst) { return false } return math.Abs(cfg.apparentBoN(conjunctionTT, planetTransitCoarseN)) <= cfg.latitudePrefilter } -func planetTransitAtInferiorConjunctionTT(conjunctionTT float64, cfg planetTransitConfig) (PlanetTransitResult, bool) { - greatestTT := greatestPlanetTransitTT(conjunctionTT, cfg) - greatestState := planetTransitStateAt(greatestTT, cfg, -1) +func planetTransitAtInferiorConjunctionTT(conjunctionTT float64, cfg planetTransitConfig, instances ...*Instance) (PlanetTransitResult, bool) { + inst := instanceValue(instances) + + greatestTT := greatestPlanetTransitTT(conjunctionTT, cfg, inst) + greatestState := planetTransitStateAt(greatestTT, cfg, -1, inst) if !isFiniteFloat(greatestState.externalContactMetric) || greatestState.externalContactMetric > 0 { return PlanetTransitResult{}, false } @@ -332,11 +357,11 @@ func planetTransitAtInferiorConjunctionTT(conjunctionTT float64, cfg planetTrans HasInternal: greatestState.internalContactMetric <= 0, } - externalIngress, ok := refinePlanetTransitContactTT(greatestTT, cfg, -1, false) + externalIngress, ok := refinePlanetTransitContactTT(greatestTT, cfg, -1, false, inst) if !ok { return PlanetTransitResult{}, false } - externalEgress, ok := refinePlanetTransitContactTT(greatestTT, cfg, 1, false) + externalEgress, ok := refinePlanetTransitContactTT(greatestTT, cfg, 1, false, inst) if !ok || externalEgress <= externalIngress { return PlanetTransitResult{}, false } @@ -344,11 +369,11 @@ func planetTransitAtInferiorConjunctionTT(conjunctionTT float64, cfg planetTrans result.ExternalEgress = externalEgress if result.HasInternal { - internalIngress, ok := refinePlanetTransitContactTT(greatestTT, cfg, -1, true) + internalIngress, ok := refinePlanetTransitContactTT(greatestTT, cfg, -1, true, inst) if ok { result.InternalIngress = internalIngress } - internalEgress, ok := refinePlanetTransitContactTT(greatestTT, cfg, 1, true) + internalEgress, ok := refinePlanetTransitContactTT(greatestTT, cfg, 1, true, inst) if ok && internalEgress > internalIngress { result.InternalEgress = internalEgress } @@ -358,15 +383,17 @@ func planetTransitAtInferiorConjunctionTT(conjunctionTT float64, cfg planetTrans return result, true } -func greatestPlanetTransitTT(seedTT float64, cfg planetTransitConfig) float64 { +func greatestPlanetTransitTT(seedTT float64, cfg planetTransitConfig, instances ...*Instance) float64 { + inst := instanceValue(instances) + left := seedTT - planetTransitGreatestWindowDays right := seedTT + planetTransitGreatestWindowDays goldenRatio := (math.Sqrt(5) - 1) / 2 x1 := right - goldenRatio*(right-left) x2 := left + goldenRatio*(right-left) - f1 := planetTransitStateAt(x1, cfg, planetTransitCoarseN).separationSquared - f2 := planetTransitStateAt(x2, cfg, planetTransitCoarseN).separationSquared + f1 := planetTransitStateAt(x1, cfg, planetTransitCoarseN, inst).separationSquared + f2 := planetTransitStateAt(x2, cfg, planetTransitCoarseN, inst).separationSquared for i := 0; i < 80 && right-left > planetTransitGreatestToleranceDays; i++ { if f1 <= f2 { @@ -374,14 +401,14 @@ func greatestPlanetTransitTT(seedTT float64, cfg planetTransitConfig) float64 { x2 = x1 f2 = f1 x1 = right - goldenRatio*(right-left) - f1 = planetTransitStateAt(x1, cfg, planetTransitCoarseN).separationSquared + f1 = planetTransitStateAt(x1, cfg, planetTransitCoarseN, inst).separationSquared continue } left = x1 x1 = x2 f1 = f2 x2 = left + goldenRatio*(right-left) - f2 = planetTransitStateAt(x2, cfg, planetTransitCoarseN).separationSquared + f2 = planetTransitStateAt(x2, cfg, planetTransitCoarseN, inst).separationSquared } center := (left + right) / 2 @@ -389,31 +416,33 @@ func greatestPlanetTransitTT(seedTT float64, cfg planetTransitConfig) float64 { right = center + 2.0/24.0 x1 = right - goldenRatio*(right-left) x2 = left + goldenRatio*(right-left) - f1 = planetTransitStateAt(x1, cfg, -1).separationSquared - f2 = planetTransitStateAt(x2, cfg, -1).separationSquared + f1 = planetTransitStateAt(x1, cfg, -1, inst).separationSquared + f2 = planetTransitStateAt(x2, cfg, -1, inst).separationSquared for i := 0; i < 80 && right-left > planetTransitGreatestToleranceDays; i++ { if f1 <= f2 { right = x2 x2 = x1 f2 = f1 x1 = right - goldenRatio*(right-left) - f1 = planetTransitStateAt(x1, cfg, -1).separationSquared + f1 = planetTransitStateAt(x1, cfg, -1, inst).separationSquared continue } left = x1 x1 = x2 f1 = f2 x2 = left + goldenRatio*(right-left) - f2 = planetTransitStateAt(x2, cfg, -1).separationSquared + f2 = planetTransitStateAt(x2, cfg, -1, inst).separationSquared } return (left + right) / 2 } -func planetTransitStateAt(jdTT float64, cfg planetTransitConfig, n int) planetTransitState { +func planetTransitStateAt(jdTT float64, cfg planetTransitConfig, n int, instances ...*Instance) planetTransitState { + inst := instanceValue(instances) + planetRA, planetDec := cfg.apparentRaDecN(jdTT, n) - sunRA, sunDec := HSunApparentRaDecN(jdTT, n) + sunRA, sunDec := HSunApparentRaDecNWithInstance(jdTT, n, inst) separationArcsec := StarAngularSeparation(planetRA, planetDec, sunRA, sunDec) * 3600 - sunSemidiameter := SunSemidiameterN(jdTT, n) + sunSemidiameter := SunSemidiameterNWithInstance(jdTT, n, inst) planetSemidiameter := cfg.semidiameterN(jdTT, n) return planetTransitState{ jdTT: jdTT, @@ -426,12 +455,14 @@ func planetTransitStateAt(jdTT float64, cfg planetTransitConfig, n int) planetTr } } -func refinePlanetTransitContactTT(greatestTT float64, cfg planetTransitConfig, direction int, internal bool) (float64, bool) { +func refinePlanetTransitContactTT(greatestTT float64, cfg planetTransitConfig, direction int, internal bool, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + if direction != -1 && direction != 1 { return 0, false } metric := func(jdTT float64) float64 { - state := planetTransitStateAt(jdTT, cfg, -1) + state := planetTransitStateAt(jdTT, cfg, -1, inst) if internal { return state.internalContactMetric } diff --git a/basic/planet_transit_local.go b/basic/planet_transit_local.go new file mode 100644 index 0000000..c25c7d5 --- /dev/null +++ b/basic/planet_transit_local.go @@ -0,0 +1,1094 @@ +package basic + +import ( + "math" + "sort" + "sync" + "time" +) + +// 站心凌日:以地心凌日为种子,对太阳与行星的视赤经赤纬分别做观测者视差修正后重解接触与凌甚。 +// 太阳视半径按站心距离缩放(影响最多约 0.04″);行星视半径沿用地心值,站心修正低于 0.01″。 +// 结果里的太阳高度一律为几何中心高度;可见性、日出日落与等时线裁剪按太阳中心视高度 0° 判定, +// 即计入标准大气(1010 hPa、10 °C)下的地平折射,几何高度阈值约 -34′。 + +const ( + // planetTransitLocalWindowDays 是站心接触相对地心接触的搜索半宽。金星视差差约 22″, + // 接触偏移最多约十分钟;四十分钟同时容纳近掠凌时变慢的接触。 + planetTransitLocalWindowDays = 40.0 / 1440 + // planetTransitLocalScanStepDays 是站点求根前的分档步长。 + planetTransitLocalScanStepDays = 1.0 / 1440 + // planetTransitLocalToleranceDays 是站点接触与凌甚的收敛容差。 + planetTransitLocalToleranceDays = 0.05 / 86400 + // planetTransitLocalSunStepDays 是判定凌日期间日出日落的采样步长。 + planetTransitLocalSunStepDays = 2.0 / 1440 +) + +// planetTransitSunHorizonAltitudeDeg 是太阳中心视高度为 0° 时的几何高度(标准大气地平折射取负),单位度。 +var planetTransitSunHorizonAltitudeDeg = -RefractionFromApparentAltitude(0, refractionStandardPressureHPa, refractionStandardTemperatureC) + +// PlanetTransitContactKind 标识凌日接触 / identifies a transit contact. +type PlanetTransitContactKind int + +const ( + // PlanetTransitC1 是外切入凌(一触) / external ingress (first contact). + PlanetTransitC1 PlanetTransitContactKind = 1 + iota + // PlanetTransitC2 是内切入凌(二触) / internal ingress (second contact). + PlanetTransitC2 + // PlanetTransitC3 是内切出凌(三触) / internal egress (third contact). + PlanetTransitC3 + // PlanetTransitC4 是外切出凌(四触) / external egress (fourth contact). + PlanetTransitC4 + // PlanetTransitGreatest 是凌甚 / greatest transit. + PlanetTransitGreatest +) + +// PlanetTransitLocalContact 是站心凌日的一个接触或凌甚时刻。 +// PlanetTransitLocalContact is one topocentric transit contact or the greatest instant. +type PlanetTransitLocalContact struct { + // Valid 表示该时刻存在 / Valid reports whether the instant exists. + Valid bool + // JD 是 UTC 儒略日 / JD is the UTC Julian day. + JD float64 + // SunAltitudeDeg 与 SunAzimuthDeg 是站心太阳中心几何高度与方位(北起向东),单位度。 + // SunAltitudeDeg and SunAzimuthDeg are the topocentric geometric altitude and azimuth (from north through east) of the Sun's centre. + SunAltitudeDeg float64 + SunAzimuthDeg float64 + // PositionAngleDeg 是行星中心相对日心的位置角,北起向东,单位度。 + // PositionAngleDeg is the position angle of the planet centre from the solar centre, measured from north through east. + PositionAngleDeg float64 +} + +// PlanetTransitLocalResult 是一个观测地点的站心凌日结果。 +// PlanetTransitLocalResult is the topocentric transit seen from one observer. +type PlanetTransitLocalResult struct { + // Valid 为 false 表示该地点看不到行星圆面与太阳圆面相交(几何上,与太阳是否在地平上无关)。 + // Valid is false when the planet and solar disks do not overlap from this site, regardless of the Sun's altitude. + Valid bool + // PlanetIndex 为 1 表示水星,2 表示金星 / PlanetIndex is 1 for Mercury and 2 for Venus. + PlanetIndex int + Observer Observer + // 四触与凌甚;掠凌没有内切接触时 InternalIngress、InternalEgress 的 Valid 为 false。 + // Contacts and greatest; a grazing transit has invalid internal contacts. + ExternalIngress PlanetTransitLocalContact + InternalIngress PlanetTransitLocalContact + Greatest PlanetTransitLocalContact + InternalEgress PlanetTransitLocalContact + ExternalEgress PlanetTransitLocalContact + HasInternal bool + // 凌甚时的站心角距与视半径,单位角秒;太阳视半径为站心值,行星视半径沿用地心值(站心差约 0.006″,忽略)。 + // Separation and semidiameters at greatest, in arcseconds: separation and solar semidiameter are topocentric, + // the planet semidiameter keeps the geocentric value (topocentric difference ≈0.006″, neglected). + MinimumSeparationArcsec float64 + SunSemidiameterArcsec float64 + PlanetSemidiameterArcsec float64 + // Visible 表示一触到四触之间太阳中心至少有一刻在视地平上(计入标准大气折射)。 + // Visible reports whether the Sun's centre is above the apparent horizon (standard refraction) at some instant between C1 and C4. + Visible bool + // Sunrise 与 Sunset 是一触到四触之间太阳中心第一次升出、落入视地平的 UTC 儒略日;没有时为 0。 + // Sunrise and Sunset are the first UTC Julian days between C1 and C4 when the Sun's centre crosses the apparent horizon, or 0 when absent. + Sunrise float64 + Sunset float64 +} + +// PlanetTransitLocal 返回地心凌日 transit 在观测者 observer 处的站心接触与凌甚。 +// PlanetTransitLocal returns the topocentric contacts and greatest instant of the geocentric transit seen by observer. +func PlanetTransitLocal(transit PlanetTransitResult, observer Observer) PlanetTransitLocalResult { + return PlanetTransitLocalWithInstance(transit, observer, nil) +} + +// PlanetTransitLocalWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func PlanetTransitLocalWithInstance(transit PlanetTransitResult, observer Observer, inst *Instance) PlanetTransitLocalResult { + result := PlanetTransitLocalResult{PlanetIndex: transit.PlanetIndex, Observer: observer} + if observer.Validate() != nil { + return result + } + snapshot, ok := newPlanetTransitSnapshot(transit, inst) + if !ok { + return result + } + return snapshot.local(transit, observer) +} + +// planetTransitSnapshot 缓存凌日窗口内的地心太阳、行星位置与恒星时,站点求解只做插值与视差修正。 +// 等时线要在全球网格上逐点求接触,直接调用星历会慢上千倍。 +type planetTransitSnapshot struct { + planetIndex int + startTT float64 + step float64 + // 按列存储:太阳赤经、赤纬、距离,行星赤经、赤纬、距离,太阳与行星视半径,格林尼治视恒星时(度)。 + // 赤经与恒星时已展开为连续值,避免插值跨越 0°/360°。 + columns [planetTransitSnapshotColumns][]float64 +} + +const ( + planetTransitSnapshotSunRA = iota + planetTransitSnapshotSunDec + planetTransitSnapshotSunDistance + planetTransitSnapshotPlanetRA + planetTransitSnapshotPlanetDec + planetTransitSnapshotPlanetDistance + planetTransitSnapshotSunRadius + planetTransitSnapshotPlanetRadius + planetTransitSnapshotSidereal + planetTransitSnapshotColumns +) + +const ( + // planetTransitSnapshotStepDays 是缓存节点间距;四点拉格朗日插值在十分钟间距下误差远低于 1e-6″。 + planetTransitSnapshotStepDays = 10.0 / 1440 + // planetTransitSnapshotMarginDays 是缓存相对地心一触、四触向外扩展的余量。 + planetTransitSnapshotMarginDays = 2.0 / 24 + // planetTransitMaxSpanDays 是地心一触到四触的跨度上限。实际凌日最长约 8 小时(水星)、 + // 金星不到 9 小时;超出视为异常输入,避免按跨度分配巨大缓存。 + planetTransitMaxSpanDays = 1.0 +) + +// planetTransitSnapshotCache 保留最近一次构建的快照:全球图四类等时线、站心结果与日面帧 +// 对同一凌日连续调用,复用可省掉重复的星历求值。快照构建后只读,可并发共享。 +var planetTransitSnapshotCache struct { + sync.Mutex + transit PlanetTransitResult + inst *Instance + snapshot *planetTransitSnapshot +} + +func newPlanetTransitSnapshot(transit PlanetTransitResult, inst *Instance) (*planetTransitSnapshot, bool) { + planetTransitSnapshotCache.Lock() + if planetTransitSnapshotCache.snapshot != nil && planetTransitSnapshotCache.transit == transit && planetTransitSnapshotCache.inst == inst { + snapshot := planetTransitSnapshotCache.snapshot + planetTransitSnapshotCache.Unlock() + return snapshot, true + } + planetTransitSnapshotCache.Unlock() + snapshot, ok := buildPlanetTransitSnapshot(transit, inst) + if ok { + planetTransitSnapshotCache.Lock() + planetTransitSnapshotCache.transit, planetTransitSnapshotCache.inst, planetTransitSnapshotCache.snapshot = transit, inst, snapshot + planetTransitSnapshotCache.Unlock() + } + return snapshot, ok +} + +func buildPlanetTransitSnapshot(transit PlanetTransitResult, inst *Instance) (*planetTransitSnapshot, bool) { + if !transit.Valid || !transit.HasExternal || !isFiniteFloat(transit.ExternalIngress) || !isFiniteFloat(transit.ExternalEgress) || + !isFiniteFloat(transit.Greatest) || transit.ExternalEgress <= transit.ExternalIngress || + transit.ExternalEgress-transit.ExternalIngress > planetTransitMaxSpanDays || + transit.Greatest < transit.ExternalIngress || transit.Greatest > transit.ExternalEgress { + return nil, false + } + var cfg planetTransitConfig + switch transit.PlanetIndex { + case 1: + cfg = mercuryTransitConfig(inst) + case 2: + cfg = venusTransitConfig(inst) + default: + return nil, false + } + startTT := UTC2TT(transit.ExternalIngress) - planetTransitSnapshotMarginDays + endTT := UTC2TT(transit.ExternalEgress) + planetTransitSnapshotMarginDays + count := int(math.Ceil((endTT-startTT)/planetTransitSnapshotStepDays)) + 1 + snapshot := &planetTransitSnapshot{planetIndex: transit.PlanetIndex, startTT: startTT, step: planetTransitSnapshotStepDays} + for column := range snapshot.columns { + snapshot.columns[column] = make([]float64, count) + } + for index := 0; index < count; index++ { + tt := startTT + float64(index)*planetTransitSnapshotStepDays + sunRA, sunDec := HSunApparentRaDecNWithInstance(tt, -1, inst) + planetRA, planetDec := cfg.apparentRaDecN(tt, -1) + values := [planetTransitSnapshotColumns]float64{ + sunRA, sunDec, EarthAwayNWithInstance(tt, -1, inst), + planetRA, planetDec, cfg.earthDistanceN(tt, -1), + SunSemidiameterNWithInstance(tt, -1, inst), cfg.semidiameterN(tt, -1), + ApparentSiderealTime(TT2UT1(tt)) * 15, + } + for column, value := range values { + if !isFiniteFloat(value) { + return nil, false + } + snapshot.columns[column][index] = value + } + } + for _, column := range []int{planetTransitSnapshotSunRA, planetTransitSnapshotPlanetRA, planetTransitSnapshotSidereal} { + unwrapDegrees(snapshot.columns[column]) + } + return snapshot, true +} + +// unwrapDegrees 把相邻差值折回 (-180°,180°],得到连续的角度序列。 +func unwrapDegrees(values []float64) { + for index := 1; index < len(values); index++ { + difference := math.Mod(values[index]-values[index-1], 360) + if difference > 180 { + difference -= 360 + } else if difference <= -180 { + difference += 360 + } + values[index] = values[index-1] + difference + } +} + +// interpolate 对缓存做四点拉格朗日插值;越出缓存范围时返回 false。 +func (snapshot *planetTransitSnapshot) interpolate(tt float64) ([planetTransitSnapshotColumns]float64, bool) { + var values [planetTransitSnapshotColumns]float64 + count := len(snapshot.columns[0]) + position := (tt - snapshot.startTT) / snapshot.step + if !isFiniteFloat(position) || position < 0 || position > float64(count-1) { + return values, false + } + first := int(math.Floor(position)) - 1 + if first < 0 { + first = 0 + } + if first > count-4 { + first = count - 4 + } + u := position - float64(first) + weights := [4]float64{ + -(u - 1) * (u - 2) * (u - 3) / 6, + u * (u - 2) * (u - 3) / 2, + -u * (u - 1) * (u - 3) / 2, + u * (u - 1) * (u - 2) / 6, + } + for column := range values { + data := snapshot.columns[column] + values[column] = weights[0]*data[first] + weights[1]*data[first+1] + weights[2]*data[first+2] + weights[3]*data[first+3] + } + return values, true +} + +// planetTransitLocalState 是一个站点在一个时刻的站心日面几何。 +type planetTransitLocalState struct { + separationArcsec float64 + positionAngleDeg float64 + sunRadiusArcsec float64 + planetRadius float64 + sunAltitudeDeg float64 + sunAzimuthDeg float64 + external float64 + internal float64 +} + +func (snapshot *planetTransitSnapshot) stateAt(tt float64, observer Observer) (planetTransitLocalState, bool) { + values, ok := snapshot.interpolate(tt) + if !ok { + return planetTransitLocalState{}, false + } + sidereal := values[planetTransitSnapshotSidereal] + sunRA, sunDec := topocentricRaDecWithSidereal(values[planetTransitSnapshotSunRA], values[planetTransitSnapshotSunDec], + observer.Latitude, observer.Longitude, sidereal, values[planetTransitSnapshotSunDistance], observer.Height) + planetRA, planetDec := topocentricRaDecWithSidereal(values[planetTransitSnapshotPlanetRA], values[planetTransitSnapshotPlanetDec], + observer.Latitude, observer.Longitude, sidereal, values[planetTransitSnapshotPlanetDistance], observer.Height) + // 以日心为切点的球面偏移:角距用 atan2 求,几百角秒的小角也不丢精度。 + deltaRA := (planetRA - sunRA) * math.Pi / 180 + sunDecRad := sunDec * math.Pi / 180 + planetDecRad := planetDec * math.Pi / 180 + east := math.Cos(planetDecRad) * math.Sin(deltaRA) + north := math.Cos(sunDecRad)*math.Sin(planetDecRad) - math.Sin(sunDecRad)*math.Cos(planetDecRad)*math.Cos(deltaRA) + along := math.Sin(sunDecRad)*math.Sin(planetDecRad) + math.Cos(sunDecRad)*math.Cos(planetDecRad)*math.Cos(deltaRA) + separation := math.Atan2(math.Hypot(east, north), along) * 180 / math.Pi * 3600 + // 太阳视半径按站心距离缩放:站心距离比地心近 ρ·cos z,最多约 4.3e-5,相当于 0.04″。 + // 行星同一比例约 2e-4·r ≈ 0.006″(金星),可忽略。 + geocentricHourAngle := (sidereal + observer.Longitude - values[planetTransitSnapshotSunRA]) * math.Pi / 180 + geocentricSunDec := values[planetTransitSnapshotSunDec] * math.Pi / 180 + sunDistanceRatio := 1 - math.Sin(0.0024427777777*math.Pi/180)/values[planetTransitSnapshotSunDistance]* + (psini(observer.Latitude, observer.Height)*math.Sin(geocentricSunDec)+ + pcosi(observer.Latitude, observer.Height)*math.Cos(geocentricSunDec)*math.Cos(geocentricHourAngle)) + sunRadius := values[planetTransitSnapshotSunRadius] / sunDistanceRatio + planetRadius := values[planetTransitSnapshotPlanetRadius] + hourAngle := signedAngleDifference(sidereal+observer.Longitude, sunRA) * math.Pi / 180 + latitude := observer.Latitude * math.Pi / 180 + azimuth := normalizeRA(math.Atan2(math.Sin(hourAngle), + math.Cos(hourAngle)*math.Sin(latitude)-math.Tan(sunDecRad)*math.Cos(latitude))*180/math.Pi + 180) + return planetTransitLocalState{ + separationArcsec: separation, + positionAngleDeg: normalizeRA(math.Atan2(east, north) * 180 / math.Pi), + sunRadiusArcsec: sunRadius, + planetRadius: planetRadius, + sunAltitudeDeg: occultationAltitudeWithSidereal(sidereal, observer, sunRA, sunDec), + sunAzimuthDeg: azimuth, + external: separation - (sunRadius + planetRadius), + internal: separation - (sunRadius - planetRadius), + }, true +} + +func (snapshot *planetTransitSnapshot) local(transit PlanetTransitResult, observer Observer) PlanetTransitLocalResult { + result := PlanetTransitLocalResult{PlanetIndex: snapshot.planetIndex, Observer: observer} + greatestTT, ok := snapshot.localGreatestTT(UTC2TT(transit.Greatest), observer) + if !ok { + return result + } + greatest, ok := snapshot.stateAt(greatestTT, observer) + if !ok || greatest.external >= 0 { + return result + } + contact := func(kind PlanetTransitContactKind) (PlanetTransitLocalContact, bool) { + tt, ok := snapshot.localContactTT(kind, planetTransitGeocentricContactTT(transit, kind, greatestTT), greatestTT, observer) + if !ok { + return PlanetTransitLocalContact{}, false + } + return snapshot.contactAt(tt, observer) + } + if result.ExternalIngress, ok = contact(PlanetTransitC1); !ok { + return result + } + if result.ExternalEgress, ok = contact(PlanetTransitC4); !ok { + return result + } + if greatest.internal < 0 { + c2, ok2 := contact(PlanetTransitC2) + c3, ok3 := contact(PlanetTransitC3) + if ok2 && ok3 { + result.InternalIngress, result.InternalEgress, result.HasInternal = c2, c3, true + } + } + result.Greatest, _ = snapshot.contactAt(greatestTT, observer) + result.Valid = true + result.MinimumSeparationArcsec = greatest.separationArcsec + result.SunSemidiameterArcsec = greatest.sunRadiusArcsec + result.PlanetSemidiameterArcsec = greatest.planetRadius + snapshot.localSunEvents(&result, UTC2TT(result.ExternalIngress.JD), UTC2TT(result.ExternalEgress.JD), observer) + return result +} + +func (snapshot *planetTransitSnapshot) contactAt(tt float64, observer Observer) (PlanetTransitLocalContact, bool) { + state, ok := snapshot.stateAt(tt, observer) + if !ok { + return PlanetTransitLocalContact{}, false + } + return PlanetTransitLocalContact{ + Valid: true, + JD: TT2UTC(tt), + SunAltitudeDeg: state.sunAltitudeDeg, + SunAzimuthDeg: state.sunAzimuthDeg, + PositionAngleDeg: state.positionAngleDeg, + }, true +} + +// planetTransitGeocentricContactTT 给出站点求根的种子:地心有该接触时取地心时刻,否则取站心凌甚。 +func planetTransitGeocentricContactTT(transit PlanetTransitResult, kind PlanetTransitContactKind, fallbackTT float64) float64 { + var jd float64 + switch kind { + case PlanetTransitC1: + jd = transit.ExternalIngress + case PlanetTransitC2: + jd = transit.InternalIngress + case PlanetTransitC3: + jd = transit.InternalEgress + case PlanetTransitC4: + jd = transit.ExternalEgress + } + if jd == 0 { + return fallbackTT + } + return UTC2TT(jd) +} + +func (snapshot *planetTransitSnapshot) localGreatestTT(seedTT float64, observer Observer) (float64, bool) { + const goldenRatio = 0.6180339887498949 + separation := func(tt float64) float64 { + state, ok := snapshot.stateAt(tt, observer) + if !ok { + return math.Inf(1) + } + return state.separationArcsec + } + left, right := seedTT-planetTransitLocalWindowDays, seedTT+planetTransitLocalWindowDays + x1, x2 := right-goldenRatio*(right-left), left+goldenRatio*(right-left) + f1, f2 := separation(x1), separation(x2) + for iteration := 0; iteration < 100 && right-left > planetTransitLocalToleranceDays; iteration++ { + if f1 <= f2 { + right, x2, f2 = x2, x1, f1 + x1 = right - goldenRatio*(right-left) + f1 = separation(x1) + } else { + left, x1, f1 = x1, x2, f2 + x2 = left + goldenRatio*(right-left) + f2 = separation(x2) + } + } + center := (left + right) / 2 + return center, isFiniteFloat(separation(center)) +} + +// localContactTT 从种子出发按分钟分档找变号区间再二分。入凌时接触量随时间由正变负,出凌相反; +// 种子在盘内时向外走,在盘外时向凌甚走,两种情形都不越过凌甚。 +func (snapshot *planetTransitSnapshot) localContactTT( + kind PlanetTransitContactKind, + seedTT, greatestTT float64, + observer Observer, +) (float64, bool) { + internal := kind == PlanetTransitC2 || kind == PlanetTransitC3 + outward := 1.0 + if kind == PlanetTransitC1 || kind == PlanetTransitC2 { + outward = -1 + } + metric := func(tt float64) float64 { + state, ok := snapshot.stateAt(tt, observer) + if !ok { + return math.NaN() + } + if internal { + return state.internal + } + return state.external + } + if (seedTT-greatestTT)*outward < 0 { + seedTT = greatestTT + } + seedValue := metric(seedTT) + if !isFiniteFloat(seedValue) { + return 0, false + } + direction := outward + if seedValue > 0 { + direction = -outward + } + limit := int(math.Ceil(planetTransitLocalWindowDays / planetTransitLocalScanStepDays)) + near, nearValue := seedTT, seedValue + for step := 1; step <= limit; step++ { + far := seedTT + direction*float64(step)*planetTransitLocalScanStepDays + if direction != outward && (far-greatestTT)*outward < 0 { + far = greatestTT + } + farValue := metric(far) + if !isFiniteFloat(farValue) { + return 0, false + } + if (nearValue > 0) != (farValue > 0) { + return bisectPlanetTransitLocal(near, nearValue, far, metric) + } + if far == greatestTT { + return 0, false + } + near, nearValue = far, farValue + } + return 0, false +} + +func bisectPlanetTransitLocal(left, leftValue, right float64, metric func(float64) float64) (float64, bool) { + for iteration := 0; iteration < 80 && math.Abs(right-left) > planetTransitLocalToleranceDays; iteration++ { + middle := (left + right) / 2 + value := metric(middle) + if !isFiniteFloat(value) { + return 0, false + } + if (value > 0) == (leftValue > 0) { + left, leftValue = middle, value + } else { + right = middle + } + } + return (left + right) / 2, true +} + +// localSunEvents 判定一触到四触之间太阳中心是否在视地平上,并找出其间第一次日出、日落。 +func (snapshot *planetTransitSnapshot) localSunEvents(result *PlanetTransitLocalResult, startTT, endTT float64, observer Observer) { + altitude := func(tt float64) float64 { + state, ok := snapshot.stateAt(tt, observer) + if !ok { + return math.NaN() + } + return state.sunAltitudeDeg - planetTransitSunHorizonAltitudeDeg + } + count := int(math.Ceil((endTT-startTT)/planetTransitLocalSunStepDays)) + 1 + previousTT, previous := startTT, altitude(startTT) + result.Visible = previous > 0 + for index := 1; index < count; index++ { + tt := math.Min(endTT, startTT+float64(index)*planetTransitLocalSunStepDays) + value := altitude(tt) + if !isFiniteFloat(value) || !isFiniteFloat(previous) { + previousTT, previous = tt, value + continue + } + if value > 0 { + result.Visible = true + } + if (previous > 0) != (value > 0) { + crossing, ok := bisectPlanetTransitLocal(previousTT, previous, tt, altitude) + if ok && value > 0 && result.Sunrise == 0 { + result.Sunrise = TT2UTC(crossing) + } else if ok && value <= 0 && result.Sunset == 0 { + result.Sunset = TT2UTC(crossing) + } + } + previousTT, previous = tt, value + } +} + +const ( + // planetTransitIsochroneGridDegrees 是等时线网格间距。站心接触时刻是观测者地心矢量的近线性函数, + // 2° 网格的线性插值误差在地图上不到一像素。 + planetTransitIsochroneGridDegrees = 2.0 + // planetTransitIsochroneMaxLevels 是每个接触的等时线条数上限;超出时步长自动加倍。 + planetTransitIsochroneMaxLevels = 64 +) + +// PlanetTransitIsochronePoint 是等时线上的一点 / one point on an isochrone. +type PlanetTransitIsochronePoint struct { + // Longitude 东经为正,Latitude 北纬为正,单位度 / east-positive longitude and north-positive latitude in degrees. + Longitude float64 + Latitude float64 +} + +// PlanetTransitIsochrone 是同一站心接触时刻的地点连线,只保留该时刻太阳中心在视地平上(计入标准大气折射)的部分。 +// PlanetTransitIsochrone joins the sites that see one contact at the same instant; only the part where the Sun's centre is above the apparent horizon (standard refraction) is kept. +type PlanetTransitIsochrone struct { + Kind PlanetTransitContactKind + // JD 是该等时线表示的 UTC 儒略日 / JD is the UTC Julian day represented by the line. + JD float64 + // Segments 是连续支路;一条支路止于地平线或网格边缘 / continuous branches ending at the horizon or the grid edge. + Segments [][]PlanetTransitIsochronePoint +} + +// PlanetTransitContactIsochrones 返回接触 kind 的站心等时线,时刻按 step 对齐到整刻度(UTC); +// 条数超过 64 时步长按倍数放大,保证覆盖整个接触时段; +// step 非正、kind 不是 C1–C4 或地心凌日没有该接触时返回 nil。 +// 时刻是站心接触时刻,受视差影响可落在地心 C1–C4 之外(金星约数分钟); +// 一触、四触与远离掠凌边界的二触、三触,线上地点的站心接触时刻与 PlanetTransitLocal 相差不到 1 秒; +// 掠凌边界附近(站心内切余量 <0.5″)内切时刻梯度发散,偏差可达数十秒。 +// PlanetTransitContactIsochrones returns topocentric isochrones of contact kind at UTC instants aligned to step. +// When more than 64 lines would be needed, the step is doubled until the whole contact span fits. +// It returns nil for a non-positive step, a kind other than C1-C4, or a contact the geocentric transit lacks. +// Points agree with PlanetTransitLocal to within a second, except C2/C3 near the grazing limit (internal margin +// below 0.5″), where the contact-time gradient diverges and errors reach tens of seconds. +func PlanetTransitContactIsochrones(transit PlanetTransitResult, kind PlanetTransitContactKind, step time.Duration) []PlanetTransitIsochrone { + return PlanetTransitContactIsochronesWithInstance(transit, kind, step, nil) +} + +// PlanetTransitContactIsochronesWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func PlanetTransitContactIsochronesWithInstance( + transit PlanetTransitResult, + kind PlanetTransitContactKind, + step time.Duration, + inst *Instance, +) []PlanetTransitIsochrone { + if step <= 0 || kind < PlanetTransitC1 || kind > PlanetTransitC4 { + return nil + } + if (kind == PlanetTransitC2 || kind == PlanetTransitC3) && !transit.HasInternal { + return nil + } + snapshot, ok := newPlanetTransitSnapshot(transit, inst) + if !ok { + return nil + } + grid := snapshot.contactGrid(transit, kind) + minimum, maximum := math.Inf(1), math.Inf(-1) + for _, row := range grid.times { + for _, value := range row { + if isFiniteFloat(value) { + minimum, maximum = math.Min(minimum, value), math.Max(maximum, value) + } + } + } + if !(maximum > minimum) { + return nil + } + // 条数超过上限时步长加倍,保证等时线覆盖整个接触时段,而不是截掉后半段。 + levels := greatestTimeContourAlignedLevels(minimum, maximum, step, planetTransitIsochroneMaxLevels+1) + for len(levels) > planetTransitIsochroneMaxLevels { + step *= 2 + levels = greatestTimeContourAlignedLevels(minimum, maximum, step, planetTransitIsochroneMaxLevels+1) + } + var result []PlanetTransitIsochrone + for _, level := range levels { + segments := grid.contour(level.tt) + segments = snapshot.clipToDaylight(segments, level.tt) + if len(segments) == 0 { + continue + } + result = append(result, PlanetTransitIsochrone{Kind: kind, JD: Date2JD(level.at), Segments: segments}) + } + return result +} + +// planetTransitContactGrid 是全球网格上的站心接触时刻(TT),times[row][column],无解处为 NaN。 +// 行从南极到北极,列从 -180° 起向东,经度方向首尾相接。 +type planetTransitContactGrid struct { + times [][]float64 +} + +func (grid planetTransitContactGrid) point(row, column int) PlanetTransitIsochronePoint { + return PlanetTransitIsochronePoint{ + Longitude: -180 + float64(column)*planetTransitIsochroneGridDegrees, + Latitude: -90 + float64(row)*planetTransitIsochroneGridDegrees, + } +} + +func (snapshot *planetTransitSnapshot) contactGrid(transit PlanetTransitResult, kind PlanetTransitContactKind) planetTransitContactGrid { + rows := int(math.Round(180/planetTransitIsochroneGridDegrees)) + 1 + columns := int(math.Round(360 / planetTransitIsochroneGridDegrees)) + greatestTT := UTC2TT(transit.Greatest) + seedTT := planetTransitGeocentricContactTT(transit, kind, greatestTT) + grid := planetTransitContactGrid{times: make([][]float64, rows)} + for row := range grid.times { + grid.times[row] = make([]float64, columns) + for column := range grid.times[row] { + point := grid.point(row, column) + observer := Observer{Longitude: point.Longitude, Latitude: point.Latitude} + grid.times[row][column] = snapshot.fastContactTT(kind, seedTT, greatestTT, observer) + } + } + return grid +} + +// fastContactTT 先用割线法从地心接触出发求站心接触:接触量对时间近似线性,通常四五次就收敛; +// 不收敛(近掠凌)时退回分档求根。 +func (snapshot *planetTransitSnapshot) fastContactTT(kind PlanetTransitContactKind, seedTT, greatestTT float64, observer Observer) float64 { + internal := kind == PlanetTransitC2 || kind == PlanetTransitC3 + metric := func(tt float64) float64 { + state, ok := snapshot.stateAt(tt, observer) + if !ok { + return math.NaN() + } + if internal { + return state.internal + } + return state.external + } + x0, x1 := seedTT, seedTT+1.0/1440 + f0, f1 := metric(x0), metric(x1) + for iteration := 0; iteration < 12 && isFiniteFloat(f0) && isFiniteFloat(f1) && f1 != f0; iteration++ { + x2 := x1 - f1*(x1-x0)/(f1-f0) + if !isFiniteFloat(x2) || math.Abs(x2-seedTT) > planetTransitLocalWindowDays { + break + } + if math.Abs(x2-x1) <= planetTransitLocalToleranceDays { + // 割线根可能落在另一侧接触上:入凌接触必须早于凌甚,出凌接触必须晚于凌甚。 + if (kind == PlanetTransitC1 || kind == PlanetTransitC2) == (x2 < greatestTT) { + return x2 + } + break + } + x0, f0, x1, f1 = x1, f1, x2, metric(x2) + } + localGreatestTT, ok := snapshot.localGreatestTT(greatestTT, observer) + if !ok { + return math.NaN() + } + if tt, ok := snapshot.localContactTT(kind, seedTT, localGreatestTT, observer); ok { + return tt + } + return math.NaN() +} + +// contour 用 marching squares 求 level 等值线,跨单元的交点按网格边编号拼接成折线; +// 经度方向首尾相接,输出经度规范到 [-180°,180°),跨日界线的拆分交给绘图层。 +func (grid planetTransitContactGrid) contour(level float64) [][]PlanetTransitIsochronePoint { + rows := len(grid.times) + if rows < 2 { + return nil + } + columns := len(grid.times[0]) + // 边编号:水平边 (row,column)-(row,column+1) 为 2k,竖直边 (row,column)-(row+1,column) 为 2k+1,k=row*columns+column。 + edgeID := func(row, column int, vertical bool) int { + id := (row*columns + column) * 2 + if vertical { + id++ + } + return id + } + points := map[int]PlanetTransitIsochronePoint{} + edgePoint := func(row, column int, vertical bool) int { + id := edgeID(row, column, vertical) + if _, ok := points[id]; ok { + return id + } + nextRow, nextColumn := row, (column+1)%columns + if vertical { + nextRow, nextColumn = row+1, column + } + a, b := grid.times[row][column], grid.times[nextRow][nextColumn] + fraction := (level - a) / (b - a) + start := grid.point(row, column) + if vertical { + start.Latitude += fraction * planetTransitIsochroneGridDegrees + } else { + start.Longitude += fraction * planetTransitIsochroneGridDegrees + } + start.Longitude = normalizeLongitude180(start.Longitude) + points[id] = start + return id + } + links := map[int][]int{} + link := func(a, b int) { + links[a] = append(links[a], b) + links[b] = append(links[b], a) + } + for row := 0; row < rows-1; row++ { + for column := 0; column < columns; column++ { + next := (column + 1) % columns + corners := [4]float64{grid.times[row][column], grid.times[row][next], grid.times[row+1][next], grid.times[row+1][column]} + mask, valid := 0, true + for index, value := range corners { + if !isFiniteFloat(value) { + valid = false + break + } + if value > level { + mask |= 1 << index + } + } + if !valid || mask == 0 || mask == 15 { + continue + } + // 四条边:0 下(水平 row)、1 右(竖直 next)、2 上(水平 row+1)、3 左(竖直 column)。 + edge := func(side int) int { + switch side { + case 0: + return edgePoint(row, column, false) + case 1: + return edgePoint(row, next, true) + case 2: + return edgePoint(row+1, column, false) + } + return edgePoint(row, column, true) + } + crossed := make([]int, 0, 4) + for side := 0; side < 4; side++ { + if (mask>>side)&1 != (mask>>((side+1)%4))&1 { + crossed = append(crossed, side) + } + } + if len(crossed) == 2 { + link(edge(crossed[0]), edge(crossed[1])) + continue + } + // 鞍点:按中心均值决定连法,中心与角 0 同侧时把角 0 所在的两条边分开连。 + center := (corners[0] + corners[1] + corners[2] + corners[3]) / 4 + if (center > level) == (mask&1 != 0) { + link(edge(0), edge(1)) + link(edge(2), edge(3)) + } else { + link(edge(3), edge(0)) + link(edge(1), edge(2)) + } + } + } + return chainPlanetTransitContour(points, links) +} + +// chainPlanetTransitContour 把边交点按邻接关系串成折线:先从端点(度为 1)出发,剩下的是闭环。 +func chainPlanetTransitContour(points map[int]PlanetTransitIsochronePoint, links map[int][]int) [][]PlanetTransitIsochronePoint { + ids := make([]int, 0, len(links)) + for id := range links { + ids = append(ids, id) + } + sort.Ints(ids) + used := map[[2]int]bool{} + key := func(a, b int) [2]int { + if a > b { + a, b = b, a + } + return [2]int{a, b} + } + walk := func(start int) []PlanetTransitIsochronePoint { + line := []PlanetTransitIsochronePoint{points[start]} + current := start + for { + next := -1 + for _, candidate := range links[current] { + if !used[key(current, candidate)] { + next = candidate + break + } + } + if next < 0 { + return line + } + used[key(current, next)] = true + line = append(line, points[next]) + current = next + } + } + var result [][]PlanetTransitIsochronePoint + for pass := 0; pass < 2; pass++ { + for _, id := range ids { + if pass == 0 && len(links[id]) != 1 { + continue + } + if line := walk(id); len(line) >= 2 { + result = append(result, line) + } + } + } + return result +} + +func normalizeLongitude180(longitude float64) float64 { + longitude = math.Mod(longitude+180, 360) + if longitude < 0 { + longitude += 360 + } + return longitude - 180 +} + +// clipToDaylight 只保留 tt 时刻太阳中心在视地平上的部分,切点沿折线段二分求出。 +func (snapshot *planetTransitSnapshot) clipToDaylight(segments [][]PlanetTransitIsochronePoint, tt float64) [][]PlanetTransitIsochronePoint { + values, ok := snapshot.interpolate(tt) + if !ok { + return nil + } + sidereal := values[planetTransitSnapshotSidereal] + altitude := func(point PlanetTransitIsochronePoint) float64 { + observer := Observer{Longitude: point.Longitude, Latitude: point.Latitude} + ra, dec := topocentricRaDecWithSidereal(values[planetTransitSnapshotSunRA], values[planetTransitSnapshotSunDec], + observer.Latitude, observer.Longitude, sidereal, values[planetTransitSnapshotSunDistance], 0) + return occultationAltitudeWithSidereal(sidereal, observer, ra, dec) - planetTransitSunHorizonAltitudeDeg + } + between := func(a, b PlanetTransitIsochronePoint, fraction float64) PlanetTransitIsochronePoint { + delta := normalizeLongitude180(b.Longitude - a.Longitude) + return PlanetTransitIsochronePoint{ + Longitude: normalizeLongitude180(a.Longitude + fraction*delta), + Latitude: a.Latitude + fraction*(b.Latitude-a.Latitude), + } + } + crossing := func(a, b PlanetTransitIsochronePoint, aUp bool) PlanetTransitIsochronePoint { + left, right := 0.0, 1.0 + for iteration := 0; iteration < 30; iteration++ { + middle := (left + right) / 2 + if (altitude(between(a, b, middle)) > 0) == aUp { + left = middle + } else { + right = middle + } + } + return between(a, b, (left+right)/2) + } + var result [][]PlanetTransitIsochronePoint + for _, segment := range segments { + var current []PlanetTransitIsochronePoint + previousUp := false + for index, point := range segment { + up := altitude(point) > 0 + if index > 0 && up != previousUp { + cut := crossing(segment[index-1], point, previousUp) + current = append(current, cut) + if previousUp { + if len(current) >= 2 { + result = append(result, current) + } + current = nil + } + } + if up { + current = append(current, point) + } + previousUp = up + } + if len(current) >= 2 { + result = append(result, current) + } + } + return result +} + +// PlanetTransitLocalContactInfo 是以 time.Time 表示的站心凌日接触。 +// PlanetTransitLocalContactInfo is a topocentric transit contact expressed with time.Time. +type PlanetTransitLocalContactInfo struct { + // Time 为零值表示该接触不存在 / a zero Time means the contact does not exist. + Time time.Time + // SunAltitude 与 SunAzimuth 是太阳中心几何高度与方位(北起向东),不含折射,单位度。 + // SunAltitude and SunAzimuth are the geometric altitude and azimuth (north through east) of the Sun's centre, without refraction. + SunAltitude float64 + SunAzimuth float64 + // PositionAngle 是行星中心相对日心的位置角,北起向东,单位度。 + // PositionAngle is the position angle of the planet centre from the solar centre, north through east. + PositionAngle float64 +} + +// PlanetTransitLocalInfo 是以 time.Time 表示的站心凌日,供水星、金星包共用。 +// PlanetTransitLocalInfo is the topocentric transit expressed with time.Time, shared by the Mercury and Venus packages. +type PlanetTransitLocalInfo struct { + // Valid 为 false 表示该地点几何上看不到凌日,其余字段为零值。 + // Valid is false when the site sees no geometric transit; other fields are then zero. + Valid bool + // Start、InternalStart、Greatest、InternalEnd、End 是一触、二触、凌甚、三触、四触。 + // Start, InternalStart, Greatest, InternalEnd and End are C1, C2, greatest, C3 and C4. + Start PlanetTransitLocalContactInfo + InternalStart PlanetTransitLocalContactInfo + Greatest PlanetTransitLocalContactInfo + InternalEnd PlanetTransitLocalContactInfo + End PlanetTransitLocalContactInfo + HasInternal bool + // 凌甚时的站心角距与视半径,单位角秒;太阳视半径为站心值,行星视半径沿用地心值(站心差约 0.006″,忽略)。 + // Separation and semidiameters at greatest, in arcseconds: separation and solar semidiameter are topocentric, + // the planet semidiameter keeps the geocentric value (topocentric difference ≈0.006″, neglected). + MinimumSeparationArcsec float64 + SunSemidiameterArcsec float64 + PlanetSemidiameterArcsec float64 + // Visible 表示一触到四触之间太阳中心至少有一刻在视地平上(计入标准大气折射)。 + // Visible reports whether the Sun's centre is above the apparent horizon (standard refraction) at some instant between C1 and C4. + Visible bool + // Sunrise 与 Sunset 是凌日期间太阳中心第一次升出、落入视地平;没有时为零值。 + // Sunrise and Sunset are the first sunrise and sunset during the transit, or zero when absent. + Sunrise time.Time + Sunset time.Time +} + +// PlanetTransitLocalInfoFromResult 把站心凌日结果换成 loc 时区的 time.Time 表示。 +// PlanetTransitLocalInfoFromResult converts a topocentric transit result to time.Time values in loc. +func PlanetTransitLocalInfoFromResult(result PlanetTransitLocalResult, loc *time.Location) PlanetTransitLocalInfo { + if !result.Valid { + return PlanetTransitLocalInfo{} + } + if loc == nil { + loc = time.UTC + } + at := func(jd float64) time.Time { + if jd == 0 { + return time.Time{} + } + return JD2DateByZone(jd, loc, false) + } + contact := func(value PlanetTransitLocalContact) PlanetTransitLocalContactInfo { + if !value.Valid { + return PlanetTransitLocalContactInfo{} + } + return PlanetTransitLocalContactInfo{ + Time: at(value.JD), + SunAltitude: value.SunAltitudeDeg, + SunAzimuth: value.SunAzimuthDeg, + PositionAngle: value.PositionAngleDeg, + } + } + return PlanetTransitLocalInfo{ + Valid: true, + Start: contact(result.ExternalIngress), + InternalStart: contact(result.InternalIngress), + Greatest: contact(result.Greatest), + InternalEnd: contact(result.InternalEgress), + End: contact(result.ExternalEgress), + HasInternal: result.HasInternal, + MinimumSeparationArcsec: result.MinimumSeparationArcsec, + SunSemidiameterArcsec: result.SunSemidiameterArcsec, + PlanetSemidiameterArcsec: result.PlanetSemidiameterArcsec, + Visible: result.Visible, + Sunrise: at(result.Sunrise), + Sunset: at(result.Sunset), + } +} + +// PlanetTransitResultFromTimes 由各接触时刻重建地心凌日结果,零值时刻表示该接触不存在。 +// 行星包的 TransitInfo 只保存 time.Time,站心计算用它还原 basic 结果。 +// PlanetTransitResultFromTimes rebuilds a geocentric transit result from contact times; a zero time means the contact is absent. +func PlanetTransitResultFromTimes(planetIndex int, start, internalStart, greatest, internalEnd, end time.Time) PlanetTransitResult { + jd := func(value time.Time) float64 { + if value.IsZero() { + return 0 + } + return Date2JD(value.UTC()) + } + result := PlanetTransitResult{ + Valid: !start.IsZero() && !greatest.IsZero() && !end.IsZero(), + PlanetIndex: planetIndex, + ExternalIngress: jd(start), + InternalIngress: jd(internalStart), + Greatest: jd(greatest), + InternalEgress: jd(internalEnd), + ExternalEgress: jd(end), + HasExternal: true, + } + result.HasInternal = result.InternalIngress != 0 && result.InternalEgress != 0 + return result +} + +// PlanetTransitLocalFrame 是站心日面上一个时刻的行星位置。 +// PlanetTransitLocalFrame is the topocentric planet position on the solar disk at one instant. +type PlanetTransitLocalFrame struct { + // JD 是 UTC 儒略日 / JD is the UTC Julian day. + JD float64 + // PlanetXArcsec 与 PlanetYArcsec 是行星中心相对日心的切平面偏移,X 向东、Y 向北为正,单位角秒。 + // PlanetXArcsec and PlanetYArcsec are tangent-plane offsets from the solar centre; X is east and Y is north. + PlanetXArcsec float64 + PlanetYArcsec float64 + // SunRadiusArcsec 与 PlanetRadiusArcsec 是视半径 / apparent semidiameters. + SunRadiusArcsec float64 + PlanetRadiusArcsec float64 + // SunAltitudeDeg 是太阳中心几何高度 / geometric altitude of the Sun's centre. + SunAltitudeDeg float64 +} + +const ( + // planetTransitLocalMaxFrames 是 PlanetTransitLocalFrames 返回帧数上限(含事件帧)。 + planetTransitLocalMaxFrames = 2000 + // planetTransitLocalFrameEpsilonDays 内的相邻时刻视为同一帧,约 0.01 秒。 + planetTransitLocalFrameEpsilonDays = 1e-7 +) + +// PlanetTransitLocalFrames 在站心一触到四触之间按 stepDays 采样行星日面位置,首尾与各接触、凌甚时刻都保留; +// stepDays 非正时取两分钟;返回帧数(含接触与凌甚帧)上限 2000,步长过细时自动放宽。 +// PlanetTransitLocalFrames samples the planet position on the solar disk between topocentric C1 and C4 every stepDays, +// always including the contacts and greatest. Non-positive stepDays uses two minutes; at most 2000 frames are returned, contacts and greatest included, widening the step when needed. +func PlanetTransitLocalFrames(transit PlanetTransitResult, local PlanetTransitLocalResult, stepDays float64) []PlanetTransitLocalFrame { + return PlanetTransitLocalFramesWithInstance(transit, local, stepDays, nil) +} + +// PlanetTransitLocalFramesWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func PlanetTransitLocalFramesWithInstance( + transit PlanetTransitResult, + local PlanetTransitLocalResult, + stepDays float64, + inst *Instance, +) []PlanetTransitLocalFrame { + if !local.Valid { + return nil + } + snapshot, ok := newPlanetTransitSnapshot(transit, inst) + if !ok { + return nil + } + if !(stepDays > 0) || !isFiniteFloat(stepDays) { + stepDays = 2.0 / 1440 + } + startTT, endTT := UTC2TT(local.ExternalIngress.JD), UTC2TT(local.ExternalEgress.JD) + events := []float64{} + for _, contact := range []PlanetTransitLocalContact{local.InternalIngress, local.Greatest, local.InternalEgress, local.ExternalEgress} { + if contact.Valid { + events = append(events, UTC2TT(contact.JD)) + } + } + // 事件帧计入 2000 帧预算:常规采样(含一触)最多 2000-len(events) 点。 + maxRegular := float64(planetTransitLocalMaxFrames - len(events)) + if span := endTT - startTT; span/stepDays > maxRegular { + stepDays = span / maxRegular + } + times := make([]float64, 0, planetTransitLocalMaxFrames) + for index := 0; ; index++ { + tt := startTT + float64(index)*stepDays + if tt >= endTT || float64(index) >= maxRegular { + break + } + times = append(times, tt) + } + times = append(times, events...) + sort.Float64s(times) + // 去掉与事件重合的常规采样,避免重复帧。 + deduped := times[:0] + for _, tt := range times { + if len(deduped) > 0 && tt-deduped[len(deduped)-1] < planetTransitLocalFrameEpsilonDays { + continue + } + deduped = append(deduped, tt) + } + times = deduped + frames := make([]PlanetTransitLocalFrame, 0, len(times)) + for _, tt := range times { + state, ok := snapshot.stateAt(tt, local.Observer) + if !ok { + return nil + } + angle := state.positionAngleDeg * math.Pi / 180 + frames = append(frames, PlanetTransitLocalFrame{ + JD: TT2UTC(tt), + PlanetXArcsec: state.separationArcsec * math.Sin(angle), + PlanetYArcsec: state.separationArcsec * math.Cos(angle), + SunRadiusArcsec: state.sunRadiusArcsec, + PlanetRadiusArcsec: state.planetRadius, + SunAltitudeDeg: state.sunAltitudeDeg, + }) + } + return frames +} diff --git a/basic/planet_transit_local_test.go b/basic/planet_transit_local_test.go new file mode 100644 index 0000000..3898be3 --- /dev/null +++ b/basic/planet_transit_local_test.go @@ -0,0 +1,237 @@ +package basic + +import ( + "math" + "testing" + "time" +) + +func TestPlanetTransitLocalContactsNearGeocentric(t *testing.T) { + cases := []struct { + name string + transit PlanetTransitResult + maxShift float64 // 站心与地心接触的最大偏移,单位分钟 + }{ + {"venus-2012", NextVenusTransit(JDCalc(2012, 1, 1)), 9}, + {"mercury-2016", NextMercuryTransit(JDCalc(2016, 1, 1)), 2}, + } + observers := []Observer{ + {Longitude: 151.2, Latitude: -33.9}, + {Longitude: 116.4, Latitude: 39.9}, + {Longitude: -157.8, Latitude: 21.3}, + {Longitude: -0.1, Latitude: 51.5, Height: 100}, + } + for _, tc := range cases { + if !tc.transit.Valid { + t.Fatalf("%s: geocentric transit not found", tc.name) + } + snapshot, ok := newPlanetTransitSnapshot(tc.transit, nil) + if !ok { + t.Fatalf("%s: snapshot failed", tc.name) + } + for _, observer := range observers { + local := PlanetTransitLocal(tc.transit, observer) + if !local.Valid || !local.HasInternal { + t.Fatalf("%s %+v: local transit invalid", tc.name, observer) + } + pairs := []struct { + local PlanetTransitLocalContact + geo float64 + kind PlanetTransitContactKind + }{ + {local.ExternalIngress, tc.transit.ExternalIngress, PlanetTransitC1}, + {local.InternalIngress, tc.transit.InternalIngress, PlanetTransitC2}, + {local.InternalEgress, tc.transit.InternalEgress, PlanetTransitC3}, + {local.ExternalEgress, tc.transit.ExternalEgress, PlanetTransitC4}, + } + for _, pair := range pairs { + shift := math.Abs(pair.local.JD-pair.geo) * 1440 + if shift > tc.maxShift { + t.Fatalf("%s %+v C%d: shift %.2f min", tc.name, observer, pair.kind, shift) + } + state, _ := snapshot.stateAt(UTC2TT(pair.local.JD), observer) + metric := state.external + if pair.kind == PlanetTransitC2 || pair.kind == PlanetTransitC3 { + metric = state.internal + } + if math.Abs(metric) > 0.01 { + t.Fatalf("%s %+v C%d: residual %.4f arcsec", tc.name, observer, pair.kind, metric) + } + } + // 直接星历求值核对插值缓存:凌甚时刻的站心角距与缓存结果一致。 + tt := UTC2TT(local.Greatest.JD) + cfg := venusTransitConfig() + if tc.transit.PlanetIndex == 1 { + cfg = mercuryTransitConfig() + } + sidereal := ApparentSiderealTime(TT2UT1(tt)) * 15 + ra, dec := HSunApparentRaDecN(tt, -1) + sunRA, sunDec := topocentricRaDecWithSidereal(ra, dec, observer.Latitude, observer.Longitude, sidereal, EarthAwayN(tt, -1), observer.Height) + ra, dec = cfg.apparentRaDecN(tt, -1) + planetRA, planetDec := topocentricRaDecWithSidereal(ra, dec, observer.Latitude, observer.Longitude, sidereal, cfg.earthDistanceN(tt, -1), observer.Height) + direct := StarAngularSeparation(sunRA, sunDec, planetRA, planetDec) * 3600 + if diff := math.Abs(direct - local.MinimumSeparationArcsec); diff > 0.01 { + t.Fatalf("%s %+v: cached separation off by %.4f arcsec", tc.name, observer, diff) + } + } + } +} + +func TestPlanetTransitContactIsochronesMatchLocalTimes(t *testing.T) { + transit := NextVenusTransit(JDCalc(2012, 1, 1)) + for _, kind := range []PlanetTransitContactKind{PlanetTransitC1, PlanetTransitC4} { + lines := PlanetTransitContactIsochrones(transit, kind, 2*time.Minute) + if len(lines) == 0 { + t.Fatalf("C%d: no isochrones", kind) + } + checked := 0 + for _, line := range lines { + for _, segment := range line.Segments { + for index := 0; index < len(segment); index += 7 { + point := segment[index] + local := PlanetTransitLocal(transit, Observer{Longitude: point.Longitude, Latitude: point.Latitude}) + contact := local.ExternalIngress + if kind == PlanetTransitC4 { + contact = local.ExternalEgress + } + if diff := math.Abs(contact.JD-line.JD) * 86400; diff > 3 { + t.Fatalf("C%d %.5f at (%.2f,%.2f): local contact off by %.1f s", kind, line.JD, point.Longitude, point.Latitude, diff) + } + if contact.SunAltitudeDeg < planetTransitSunHorizonAltitudeDeg-0.01 { + t.Fatalf("C%d at (%.2f,%.2f): Sun below horizon %.2f", kind, point.Longitude, point.Latitude, contact.SunAltitudeDeg) + } + checked++ + } + } + } + if checked < 20 { + t.Fatalf("C%d: only %d points checked", kind, checked) + } + } +} + +func TestPlanetTransitLocalFramesLimitAndDedup(t *testing.T) { + cases := []struct { + name string + transit PlanetTransitResult + }{ + {"venus-2012", NextVenusTransit(JDCalc(2012, 1, 1))}, + {"mercury-2016", NextMercuryTransit(JDCalc(2016, 1, 1))}, + } + observer := Observer{Longitude: 116.4, Latitude: 39.9} + for _, tc := range cases { + if !tc.transit.Valid { + t.Fatalf("%s: geocentric transit not found", tc.name) + } + local := PlanetTransitLocal(tc.transit, observer) + if !local.Valid || !local.HasInternal { + t.Fatalf("%s: local transit invalid", tc.name) + } + frames := PlanetTransitLocalFrames(tc.transit, local, 1e-9) + if len(frames) == 0 || len(frames) > planetTransitLocalMaxFrames { + t.Fatalf("%s: got %d frames, want 1..%d", tc.name, len(frames), planetTransitLocalMaxFrames) + } + for index := 1; index < len(frames); index++ { + if frames[index].JD <= frames[index-1].JD { + t.Fatalf("%s: duplicate or unsorted frame at %d: %.12f <= %.12f", tc.name, index, frames[index].JD, frames[index-1].JD) + } + } + } +} + +// 1999-11-15 水星掠凌:地心最小角距只比内切限小约 1.9″,南半球站心视差把它推出内切限, +// 这些地点只剩一触、四触;北半球仍有二触、三触。内切等时线只能落在仍有内切接触的地点。 +func TestPlanetTransitLocalGrazingMercury1999(t *testing.T) { + transit := NextMercuryTransit(JDCalc(1999, 1, 1)) + if !transit.Valid || !transit.HasInternal { + t.Fatalf("1999 geocentric transit must exist with internal contacts: %+v", transit) + } + limit := transit.SunSemidiameterArcsec - transit.PlanetSemidiameterArcsec + if margin := limit - transit.MinimumSeparationArcsec; margin <= 0 || margin > 5 { + t.Fatalf("geocentric margin %.2f arcsec, want a near-grazing transit", margin) + } + grazing := PlanetTransitLocal(transit, Observer{Longitude: 0, Latitude: -60}) + if !grazing.Valid || grazing.HasInternal || grazing.InternalIngress.Valid || grazing.InternalEgress.Valid { + t.Fatalf("southern site must see a grazing transit: %+v", grazing) + } + if grazing.MinimumSeparationArcsec <= grazing.SunSemidiameterArcsec-grazing.PlanetSemidiameterArcsec { + t.Fatalf("grazing site separation %.2f inside the internal limit", grazing.MinimumSeparationArcsec) + } + if !(grazing.ExternalIngress.JD < grazing.Greatest.JD && grazing.Greatest.JD < grazing.ExternalEgress.JD) { + t.Fatalf("grazing site contacts out of order: %+v", grazing) + } + internal := PlanetTransitLocal(transit, Observer{Longitude: 0, Latitude: 40}) + if !internal.Valid || !internal.HasInternal || + !(internal.ExternalIngress.JD < internal.InternalIngress.JD && internal.InternalIngress.JD < internal.Greatest.JD && + internal.Greatest.JD < internal.InternalEgress.JD && internal.InternalEgress.JD < internal.ExternalEgress.JD) { + t.Fatalf("northern site must see ordered internal contacts: %+v", internal) + } + lines := PlanetTransitContactIsochrones(transit, PlanetTransitC2, 20*time.Second) + if len(lines) == 0 { + t.Fatal("C2 isochrones missing") + } + checked := 0 + for _, line := range lines { + for _, segment := range line.Segments { + for index := 0; index < len(segment); index += 5 { + point := segment[index] + local := PlanetTransitLocal(transit, Observer{Longitude: point.Longitude, Latitude: point.Latitude}) + if !local.HasInternal { + t.Fatalf("C2 isochrone point (%.2f,%.2f) has no internal contact", point.Longitude, point.Latitude) + } + // 掠凌边缘内切时刻随余量按平方根变化,梯度发散,网格插值在余量 <0.5″ 处可差到 20 s。 + if local.SunSemidiameterArcsec-local.PlanetSemidiameterArcsec-local.MinimumSeparationArcsec < 0.5 { + continue + } + if diff := math.Abs(local.InternalIngress.JD-line.JD) * 86400; diff > 3 { + t.Fatalf("C2 at (%.2f,%.2f): local contact off by %.1f s", point.Longitude, point.Latitude, diff) + } + checked++ + } + } + } + if checked < 10 { + t.Fatalf("only %d C2 points checked", checked) + } +} + +// 步长过细时等时线不应被截断:条数不超上限,且仍覆盖整个接触时段。 +func TestPlanetTransitContactIsochronesFineStepCoversSpan(t *testing.T) { + transit := NextVenusTransit(JDCalc(2012, 1, 1)) + coarse := PlanetTransitContactIsochrones(transit, PlanetTransitC1, 2*time.Minute) + fine := PlanetTransitContactIsochrones(transit, PlanetTransitC1, 5*time.Second) + if len(coarse) == 0 || len(fine) == 0 { + t.Fatal("isochrones missing") + } + if len(fine) > planetTransitIsochroneMaxLevels { + t.Fatalf("%d lines exceed the cap", len(fine)) + } + coarseSpan := (coarse[len(coarse)-1].JD - coarse[0].JD) * 1440 + fineSpan := (fine[len(fine)-1].JD - fine[0].JD) * 1440 + if fineSpan < coarseSpan-4 { + t.Fatalf("fine step covers %.1f min, coarse %.1f min", fineSpan, coarseSpan) + } +} + +func TestPlanetTransitSnapshotRejectsBadInput(t *testing.T) { + transit := NextVenusTransit(JDCalc(2012, 1, 1)) + cases := map[string]func(*PlanetTransitResult){ + "no external": func(r *PlanetTransitResult) { r.HasExternal = false }, + "huge span": func(r *PlanetTransitResult) { r.ExternalEgress = r.ExternalIngress + 1e6 }, + "greatest outside": func(r *PlanetTransitResult) { r.Greatest = r.ExternalEgress + 0.1 }, + "greatest NaN": func(r *PlanetTransitResult) { r.Greatest = math.NaN() }, + } + for name, mutate := range cases { + bad := transit + mutate(&bad) + if _, ok := newPlanetTransitSnapshot(bad, nil); ok { + t.Fatalf("%s: snapshot must be rejected", name) + } + if local := PlanetTransitLocal(bad, Observer{}); local.Valid { + t.Fatalf("%s: local result must be invalid", name) + } + } + if _, ok := newPlanetTransitSnapshot(transit, nil); !ok { + t.Fatal("valid transit rejected") + } +} diff --git a/basic/planet_transit_test.go b/basic/planet_transit_test.go index 07ee65b..513ffc8 100644 --- a/basic/planet_transit_test.go +++ b/basic/planet_transit_test.go @@ -143,3 +143,30 @@ func BenchmarkNextVenusTransitFrom2026(b *testing.B) { } } } + +// 查询时刻落在凌日当天、早于凌甚时,Next/Closest 必须给出当天这次;节点季略早于查询时刻不能把它跳过。 +func TestPlanetTransitSearchFromSameDay(t *testing.T) { + cases := []struct { + name string + find func(float64) PlanetTransitResult + query float64 + year int + month int + day int + }{ + {"mercury next 1999", NextMercuryTransit, JDCalc(1999, 11, 15), 1999, 11, 15}, + {"mercury closest 1999", ClosestMercuryTransit, JDCalc(1999, 11, 15), 1999, 11, 15}, + {"venus next 2012", NextVenusTransit, JDCalc(2012, 6, 5), 2012, 6, 6}, + {"mercury last after 1999", LastMercuryTransit, JDCalc(1999, 11, 15.95), 1999, 11, 15}, + } + for _, tc := range cases { + got := tc.find(tc.query) + if !got.Valid { + t.Fatalf("%s: no transit", tc.name) + } + date := JD2DateByZone(got.Greatest, time.UTC, false) + if date.Year() != tc.year || int(date.Month()) != tc.month || date.Day() != tc.day { + t.Fatalf("%s: greatest %s, want %04d-%02d-%02d", tc.name, date.Format("2006-01-02"), tc.year, tc.month, tc.day) + } + } +} diff --git a/basic/planet_truncated.go b/basic/planet_truncated.go index ce6919d..c834563 100644 --- a/basic/planet_truncated.go +++ b/basic/planet_truncated.go @@ -3,20 +3,21 @@ package basic import ( "math" - "b612.me/astro/planet" . "b612.me/astro/tools" ) // Exported N variants below keep the same jd semantics as the non-N APIs; n < 0 means full series. type planetDeclinationFuncN func(float64, int) float64 -func planetXYZN(planetIndex int, jde float64, n int) (float64, float64, float64) { - l := planet.WherePlanetN(planetIndex, 0, jde, n) - b := planet.WherePlanetN(planetIndex, 1, jde, n) - r := planet.WherePlanetN(planetIndex, 2, jde, n) - el := planet.WherePlanetN(-1, 0, jde, n) - eb := planet.WherePlanetN(-1, 1, jde, n) - er := planet.WherePlanetN(-1, 2, jde, n) +func planetXYZN(planetIndex int, jde float64, n int, instances ...*Instance) (float64, float64, float64) { + inst := instanceValue(instances) + + l := PlanetCoordinateNWithInstance(planetIndex, 0, jde, n, inst) + b := PlanetCoordinateNWithInstance(planetIndex, 1, jde, n, inst) + r := PlanetCoordinateNWithInstance(planetIndex, 2, jde, n, inst) + el := PlanetCoordinateNWithInstance(-1, 0, jde, n, inst) + eb := PlanetCoordinateNWithInstance(-1, 1, jde, n, inst) + er := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) x := r*Cos(b)*Cos(l) - er*Cos(eb)*Cos(el) y := r*Cos(b)*Sin(l) - er*Cos(eb)*Sin(el) z := r*Sin(b) - er*Sin(eb) @@ -58,34 +59,6 @@ func planetEarthAwayN(planetIndex int, jd float64, n int) float64 { return planetEarthAwayExplicitN(planetIndex, jd, n) } -func planetHeightN(localJD, lon, lat, timezone float64, n int, apparentRaDec func(float64, int) (float64, float64)) float64 { - utcJD := localJD - timezone/24.0 - ra, dec := apparentRaDec(UTC2TT(utcJD), n) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - H := Limit360(st - ra) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(H) - return ArcSin(sinHeight) -} - -func planetAzimuthN(localJD, lon, lat, timezone float64, n int, apparentRaDec func(float64, int) (float64, float64)) float64 { - utcJD := localJD - timezone/24.0 - ra, dec := apparentRaDec(UTC2TT(utcJD), n) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - H := Limit360(st - ra) - tanAzimuth := Sin(H) / (Cos(H)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if H/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if H/15 < 12 { - return azimuth + 180 - } - return azimuth -} - func planetHourAngleN(jd, lon, timezone float64, n int, apparentRa func(float64, int) float64) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) hourAngle := siderealLongitude - apparentRa(UTC2TT(jd-timezone/24.0), n) @@ -181,548 +154,618 @@ func planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight floa return estimateJD - localTimezone/24 + timezone/24, nil } -// MercuryApparentLoN 水星视黄经(截断版) / truncated apparent ecliptic longitude of Mercury. +// MercuryApparentLoN 水星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Mercury. func MercuryApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(1, jd, n) return lo } -// MercuryApparentBoN 水星视黄纬(截断版) / truncated apparent ecliptic latitude of Mercury. +// MercuryApparentBoN 水星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Mercury. func MercuryApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(1, jd, n) return bo } -// MercuryApparentLoBoN 水星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Mercury. +// MercuryApparentLoBoN 水星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Mercury. func MercuryApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(1, jd, n) } -// MercuryApparentRaN 水星视赤经(截断版) / truncated apparent right ascension of Mercury. +// MercuryApparentRaN 水星视赤经(n 变体) / n-parameterised apparent right ascension of Mercury. func MercuryApparentRaN(jde float64, n int) float64 { lo, bo := MercuryApparentLoBoN(jde, n) return LoToRa(jde, lo, bo) } -// MercuryApparentDecN 水星视赤纬(截断版) / truncated apparent declination of Mercury. +// MercuryApparentDecN 水星视赤纬(n 变体) / n-parameterised apparent declination of Mercury. func MercuryApparentDecN(jde float64, n int) float64 { lo, bo := MercuryApparentLoBoN(jde, n) eps := TrueObliquity(jde) return ArcSin(Sin(bo)*Cos(eps) + Cos(bo)*Sin(eps)*Sin(lo)) } -// MercuryApparentRaDecN 水星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Mercury. +// MercuryApparentRaDecN 水星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Mercury. func MercuryApparentRaDecN(jde float64, n int) (float64, float64) { lo, bo := MercuryApparentLoBoN(jde, n) return LoBoToRaDec(jde, lo, bo) } -// EarthMercuryAwayN 地水距离(截断版) / truncated Earth-Mercury distance. +// EarthMercuryAwayN 地水距离(n 变体) / n-parameterised Earth-Mercury distance. func EarthMercuryAwayN(jd float64, n int) float64 { return planetEarthAwayN(1, jd, n) } -// MercuryMagN 水星视星等(截断版) / truncated apparent magnitude of Mercury. -func MercuryMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(1, 2, jde, n) - awayEarth := EarthMercuryAwayN(jde, n) - away := planet.WherePlanetN(-1, 2, jde, n) +// MercuryMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(1, 2, jde, n, inst) + awayEarth := EarthMercuryAwayNWithInstance(jde, n, inst) + away := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) i := (awaySun*awaySun + awayEarth*awayEarth - away*away) / (2 * awaySun * awayEarth) i = ArcCos(i) mag := -0.42 + 5*math.Log10(awaySun*awayEarth) + 0.0380*i - 0.000273*i*i + 0.000002*i*i*i return FloatRound(mag, 2) } -// MercuryHeightN 水星高度角(截断版) / truncated altitude of Mercury. -func MercuryHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, MercuryApparentRaDecN) +// MercuryHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(1, localJD, lon, lat, timezone, n, inst) } -// MercuryAzimuthN 水星方位角(截断版) / truncated azimuth of Mercury. -func MercuryAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, MercuryApparentRaDecN) +// MercuryAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(1, localJD, lon, lat, timezone, n, inst) } -// MercuryHourAngleN 水星时角(截断版) / truncated hour angle of Mercury. -func MercuryHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, MercuryApparentRaN) +// MercuryHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jde float64, n int) float64 { return MercuryApparentRaNWithInstance(jde, n, inst) }) } -// MercuryCulminationTimeN 水星中天时间(截断版) / truncated culmination time of Mercury. -func MercuryCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, MercuryHourAngleN) +// MercuryCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return MercuryHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// MercuryRiseTimeN 水星升起时间(截断版) / truncated rise time of Mercury. -func MercuryRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, MercuryCulminationTimeN, MercuryHeightN, MercuryApparentDecN) +// MercuryRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercuryRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return MercuryCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return MercuryHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jde float64, n int) float64 { return MercuryApparentDecNWithInstance(jde, n, inst) }) } -// MercurySetTimeN 水星落下时间(截断版) / truncated set time of Mercury. -func MercurySetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, MercuryCulminationTimeN, MercuryHeightN, MercuryApparentDecN) +// MercurySetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MercurySetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return MercuryCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return MercuryHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jde float64, n int) float64 { return MercuryApparentDecNWithInstance(jde, n, inst) }) } -// VenusApparentLoN 金星视黄经(截断版) / truncated apparent ecliptic longitude of Venus. +// VenusApparentLoN 金星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Venus. func VenusApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(2, jd, n) return lo } -// VenusApparentBoN 金星视黄纬(截断版) / truncated apparent ecliptic latitude of Venus. +// VenusApparentBoN 金星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Venus. func VenusApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(2, jd, n) return bo } -// VenusApparentLoBoN 金星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Venus. +// VenusApparentLoBoN 金星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Venus. func VenusApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(2, jd, n) } -// VenusApparentRaN 金星视赤经(截断版) / truncated apparent right ascension of Venus. +// VenusApparentRaN 金星视赤经(n 变体) / n-parameterised apparent right ascension of Venus. func VenusApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(2, jd, n) } -// VenusApparentDecN 金星视赤纬(截断版) / truncated apparent declination of Venus. +// VenusApparentDecN 金星视赤纬(n 变体) / n-parameterised apparent declination of Venus. func VenusApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(2, jd, n) } -// VenusApparentRaDecN 金星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Venus. +// VenusApparentRaDecN 金星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Venus. func VenusApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(2, jd, n) } -// EarthVenusAwayN 地金距离(截断版) / truncated Earth-Venus distance. +// EarthVenusAwayN 地金距离(n 变体) / n-parameterised Earth-Venus distance. func EarthVenusAwayN(jd float64, n int) float64 { return planetEarthAwayN(2, jd, n) } -// VenusMagN 金星视星等(截断版) / truncated apparent magnitude of Venus. -func VenusMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(2, 2, jde, n) - awayEarth := EarthVenusAwayN(jde, n) - away := planet.WherePlanetN(-1, 2, jde, n) +// VenusMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(2, 2, jde, n, inst) + awayEarth := EarthVenusAwayNWithInstance(jde, n, inst) + away := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) i := (awaySun*awaySun + awayEarth*awayEarth - away*away) / (2 * awaySun * awayEarth) i = ArcCos(i) mag := -4.40 + 5*math.Log10(awaySun*awayEarth) + 0.0009*i + 0.000239*i*i - 0.00000065*i*i*i return FloatRound(mag, 2) } -// VenusHeightN 金星高度角(截断版) / truncated altitude of Venus. -func VenusHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, VenusApparentRaDecN) +// VenusHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(2, localJD, lon, lat, timezone, n, inst) } -// VenusAzimuthN 金星方位角(截断版) / truncated azimuth of Venus. -func VenusAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, VenusApparentRaDecN) +// VenusAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(2, localJD, lon, lat, timezone, n, inst) } -// VenusHourAngleN 金星时角(截断版) / truncated hour angle of Venus. -func VenusHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, VenusApparentRaN) +// VenusHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return VenusApparentRaNWithInstance(jd, n, inst) }) } -// VenusCulminationTimeN 金星中天时间(截断版) / truncated culmination time of Venus. -func VenusCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, VenusHourAngleN) +// VenusCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return VenusHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// VenusRiseTimeN 金星升起时间(截断版) / truncated rise time of Venus. -func VenusRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, VenusCulminationTimeN, VenusHeightN, VenusApparentDecN) +// VenusRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return VenusCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return VenusHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return VenusApparentDecNWithInstance(jd, n, inst) }) } -// VenusSetTimeN 金星落下时间(截断版) / truncated set time of Venus. -func VenusSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, VenusCulminationTimeN, VenusHeightN, VenusApparentDecN) +// VenusSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return VenusCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return VenusHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return VenusApparentDecNWithInstance(jd, n, inst) }) } -// MarsApparentLoN 火星视黄经(截断版) / truncated apparent ecliptic longitude of Mars. +// MarsApparentLoN 火星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Mars. func MarsApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(3, jd, n) return lo } -// MarsApparentBoN 火星视黄纬(截断版) / truncated apparent ecliptic latitude of Mars. +// MarsApparentBoN 火星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Mars. func MarsApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(3, jd, n) return bo } -// MarsApparentLoBoN 火星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Mars. +// MarsApparentLoBoN 火星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Mars. func MarsApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(3, jd, n) } -// MarsApparentRaN 火星视赤经(截断版) / truncated apparent right ascension of Mars. +// MarsApparentRaN 火星视赤经(n 变体) / n-parameterised apparent right ascension of Mars. func MarsApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(3, jd, n) } -// MarsApparentDecN 火星视赤纬(截断版) / truncated apparent declination of Mars. +// MarsApparentDecN 火星视赤纬(n 变体) / n-parameterised apparent declination of Mars. func MarsApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(3, jd, n) } -// MarsApparentRaDecN 火星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Mars. +// MarsApparentRaDecN 火星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Mars. func MarsApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(3, jd, n) } -// EarthMarsAwayN 地火距离(截断版) / truncated Earth-Mars distance. +// EarthMarsAwayN 地火距离(n 变体) / n-parameterised Earth-Mars distance. func EarthMarsAwayN(jd float64, n int) float64 { return planetEarthAwayN(3, jd, n) } -// MarsMagN 火星视星等(截断版) / truncated apparent magnitude of Mars. -func MarsMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(3, 2, jde, n) - awayEarth := EarthMarsAwayN(jde, n) - away := planet.WherePlanetN(-1, 2, jde, n) +// MarsMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(3, 2, jde, n, inst) + awayEarth := EarthMarsAwayNWithInstance(jde, n, inst) + away := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) i := (awaySun*awaySun + awayEarth*awayEarth - away*away) / (2 * awaySun * awayEarth) i = ArcCos(i) mag := -1.52 + 5*math.Log10(awaySun*awayEarth) + 0.016*i return FloatRound(mag, 2) } -// MarsHeightN 火星高度角(截断版) / truncated altitude of Mars. -func MarsHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, MarsApparentRaDecN) +// MarsHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(3, localJD, lon, lat, timezone, n, inst) } -// MarsAzimuthN 火星方位角(截断版) / truncated azimuth of Mars. -func MarsAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, MarsApparentRaDecN) +// MarsAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(3, localJD, lon, lat, timezone, n, inst) } -// MarsHourAngleN 火星时角(截断版) / truncated hour angle of Mars. -func MarsHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, MarsApparentRaN) +// MarsHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return MarsApparentRaNWithInstance(jd, n, inst) }) } -// MarsCulminationTimeN 火星中天时间(截断版) / truncated culmination time of Mars. -func MarsCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, MarsHourAngleN) +// MarsCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return MarsHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// MarsRiseTimeN 火星升起时间(截断版) / truncated rise time of Mars. -func MarsRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, MarsCulminationTimeN, MarsHeightN, MarsApparentDecN) +// MarsRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return MarsCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return MarsHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return MarsApparentDecNWithInstance(jd, n, inst) }) } -// MarsSetTimeN 火星落下时间(截断版) / truncated set time of Mars. -func MarsSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, MarsCulminationTimeN, MarsHeightN, MarsApparentDecN) +// MarsSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MarsSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return MarsCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return MarsHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return MarsApparentDecNWithInstance(jd, n, inst) }) } -// JupiterApparentLoN 木星视黄经(截断版) / truncated apparent ecliptic longitude of Jupiter. +// JupiterApparentLoN 木星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Jupiter. func JupiterApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(4, jd, n) return lo } -// JupiterApparentBoN 木星视黄纬(截断版) / truncated apparent ecliptic latitude of Jupiter. +// JupiterApparentBoN 木星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Jupiter. func JupiterApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(4, jd, n) return bo } -// JupiterApparentLoBoN 木星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Jupiter. +// JupiterApparentLoBoN 木星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Jupiter. func JupiterApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(4, jd, n) } -// JupiterApparentRaN 木星视赤经(截断版) / truncated apparent right ascension of Jupiter. +// JupiterApparentRaN 木星视赤经(n 变体) / n-parameterised apparent right ascension of Jupiter. func JupiterApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(4, jd, n) } -// JupiterApparentDecN 木星视赤纬(截断版) / truncated apparent declination of Jupiter. +// JupiterApparentDecN 木星视赤纬(n 变体) / n-parameterised apparent declination of Jupiter. func JupiterApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(4, jd, n) } -// JupiterApparentRaDecN 木星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Jupiter. +// JupiterApparentRaDecN 木星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Jupiter. func JupiterApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(4, jd, n) } -// EarthJupiterAwayN 地木距离(截断版) / truncated Earth-Jupiter distance. +// EarthJupiterAwayN 地木距离(n 变体) / n-parameterised Earth-Jupiter distance. func EarthJupiterAwayN(jd float64, n int) float64 { return planetEarthAwayN(4, jd, n) } -// JupiterMagN 木星视星等(截断版) / truncated apparent magnitude of Jupiter. -func JupiterMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(4, 2, jde, n) - awayEarth := EarthJupiterAwayN(jde, n) - away := planet.WherePlanetN(-1, 2, jde, n) +// JupiterMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(4, 2, jde, n, inst) + awayEarth := EarthJupiterAwayNWithInstance(jde, n, inst) + away := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) i := (awaySun*awaySun + awayEarth*awayEarth - away*away) / (2 * awaySun * awayEarth) i = ArcCos(i) mag := -9.40 + 5*math.Log10(awaySun*awayEarth) + 0.0005*i return FloatRound(mag, 2) } -// JupiterHeightN 木星高度角(截断版) / truncated altitude of Jupiter. -func JupiterHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, JupiterApparentRaDecN) +// JupiterHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(4, localJD, lon, lat, timezone, n, inst) } -// JupiterAzimuthN 木星方位角(截断版) / truncated azimuth of Jupiter. -func JupiterAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, JupiterApparentRaDecN) +// JupiterAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(4, localJD, lon, lat, timezone, n, inst) } -// JupiterHourAngleN 木星时角(截断版) / truncated hour angle of Jupiter. -func JupiterHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, JupiterApparentRaN) +// JupiterHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return JupiterApparentRaNWithInstance(jd, n, inst) }) } -// JupiterCulminationTimeN 木星中天时间(截断版) / truncated culmination time of Jupiter. -func JupiterCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, JupiterHourAngleN) +// JupiterCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return JupiterHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// JupiterRiseTimeN 木星升起时间(截断版) / truncated rise time of Jupiter. -func JupiterRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, JupiterCulminationTimeN, JupiterHeightN, JupiterApparentDecN) +// JupiterRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return JupiterCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return JupiterHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return JupiterApparentDecNWithInstance(jd, n, inst) }) } -// JupiterSetTimeN 木星落下时间(截断版) / truncated set time of Jupiter. -func JupiterSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, JupiterCulminationTimeN, JupiterHeightN, JupiterApparentDecN) +// JupiterSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func JupiterSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return JupiterCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return JupiterHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return JupiterApparentDecNWithInstance(jd, n, inst) }) } -// SaturnApparentLoN 土星视黄经(截断版) / truncated apparent ecliptic longitude of Saturn. +// SaturnApparentLoN 土星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Saturn. func SaturnApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(5, jd, n) return lo } -// SaturnApparentBoN 土星视黄纬(截断版) / truncated apparent ecliptic latitude of Saturn. +// SaturnApparentBoN 土星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Saturn. func SaturnApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(5, jd, n) return bo } -// SaturnApparentLoBoN 土星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Saturn. +// SaturnApparentLoBoN 土星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Saturn. func SaturnApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(5, jd, n) } -// SaturnApparentRaN 土星视赤经(截断版) / truncated apparent right ascension of Saturn. +// SaturnApparentRaN 土星视赤经(n 变体) / n-parameterised apparent right ascension of Saturn. func SaturnApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(5, jd, n) } -// SaturnApparentDecN 土星视赤纬(截断版) / truncated apparent declination of Saturn. +// SaturnApparentDecN 土星视赤纬(n 变体) / n-parameterised apparent declination of Saturn. func SaturnApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(5, jd, n) } -// SaturnApparentRaDecN 土星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Saturn. +// SaturnApparentRaDecN 土星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Saturn. func SaturnApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(5, jd, n) } -// EarthSaturnAwayN 地土距离(截断版) / truncated Earth-Saturn distance. +// EarthSaturnAwayN 地土距离(n 变体) / n-parameterised Earth-Saturn distance. func EarthSaturnAwayN(jd float64, n int) float64 { return planetEarthAwayN(5, jd, n) } -// SaturnMagN 土星视星等(截断版) / truncated apparent magnitude of Saturn. -func SaturnMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(5, 2, jde, n) - awayEarth := EarthSaturnAwayN(jde, n) - ringB, _, _, deltaU, _, _ := SaturnRingParametersN(jde, n) +// SaturnMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(5, 2, jde, n, inst) + awayEarth := EarthSaturnAwayNWithInstance(jde, n, inst) + ringB, _, _, deltaU, _, _ := SaturnRingParametersNWithInstance(jde, n, inst) ringB = math.Abs(ringB) mag := -8.68 + 5*math.Log10(awaySun*awayEarth) + 0.044*deltaU - 2.6*Sin(ringB) + 1.25*Sin(ringB)*Sin(ringB) return FloatRound(mag, 2) } -// SaturnHeightN 土星高度角(截断版) / truncated altitude of Saturn. -func SaturnHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, SaturnApparentRaDecN) +// SaturnHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(5, localJD, lon, lat, timezone, n, inst) } -// SaturnAzimuthN 土星方位角(截断版) / truncated azimuth of Saturn. -func SaturnAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, SaturnApparentRaDecN) +// SaturnAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(5, localJD, lon, lat, timezone, n, inst) } -// SaturnHourAngleN 土星时角(截断版) / truncated hour angle of Saturn. -func SaturnHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, SaturnApparentRaN) +// SaturnHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return SaturnApparentRaNWithInstance(jd, n, inst) }) } -// SaturnCulminationTimeN 土星中天时间(截断版) / truncated culmination time of Saturn. -func SaturnCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, SaturnHourAngleN) +// SaturnCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return SaturnHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// SaturnRiseTimeN 土星升起时间(截断版) / truncated rise time of Saturn. -func SaturnRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, SaturnCulminationTimeN, SaturnHeightN, SaturnApparentDecN) +// SaturnRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return SaturnCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return SaturnHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return SaturnApparentDecNWithInstance(jd, n, inst) }) } -// SaturnSetTimeN 土星落下时间(截断版) / truncated set time of Saturn. -func SaturnSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, SaturnCulminationTimeN, SaturnHeightN, SaturnApparentDecN) +// SaturnSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return SaturnCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return SaturnHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return SaturnApparentDecNWithInstance(jd, n, inst) }) } -// UranusApparentLoN 天王星视黄经(截断版) / truncated apparent ecliptic longitude of Uranus. +// UranusApparentLoN 天王星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Uranus. func UranusApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(6, jd, n) return lo } -// UranusApparentBoN 天王星视黄纬(截断版) / truncated apparent ecliptic latitude of Uranus. +// UranusApparentBoN 天王星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Uranus. func UranusApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(6, jd, n) return bo } -// UranusApparentLoBoN 天王星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Uranus. +// UranusApparentLoBoN 天王星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Uranus. func UranusApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(6, jd, n) } -// UranusApparentRaN 天王星视赤经(截断版) / truncated apparent right ascension of Uranus. +// UranusApparentRaN 天王星视赤经(n 变体) / n-parameterised apparent right ascension of Uranus. func UranusApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(6, jd, n) } -// UranusApparentDecN 天王星视赤纬(截断版) / truncated apparent declination of Uranus. +// UranusApparentDecN 天王星视赤纬(n 变体) / n-parameterised apparent declination of Uranus. func UranusApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(6, jd, n) } -// UranusApparentRaDecN 天王星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Uranus. +// UranusApparentRaDecN 天王星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Uranus. func UranusApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(6, jd, n) } -// EarthUranusAwayN 地天距离(截断版) / truncated Earth-Uranus distance. +// EarthUranusAwayN 地天距离(n 变体) / n-parameterised Earth-Uranus distance. func EarthUranusAwayN(jd float64, n int) float64 { return planetEarthAwayN(6, jd, n) } -// UranusMagN 天王星视星等(截断版) / truncated apparent magnitude of Uranus. -func UranusMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(6, 2, jde, n) - awayEarth := EarthUranusAwayN(jde, n) - away := planet.WherePlanetN(-1, 2, jde, n) +// UranusMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(6, 2, jde, n, inst) + awayEarth := EarthUranusAwayNWithInstance(jde, n, inst) + away := PlanetCoordinateNWithInstance(-1, 2, jde, n, inst) i := (awaySun*awaySun + awayEarth*awayEarth - away*away) / (2 * awaySun * awayEarth) i = ArcCos(i) mag := -7.19 + 5*math.Log10(awaySun*awayEarth) + 0.016*i return FloatRound(mag, 2) } -// UranusHeightN 天王星高度角(截断版) / truncated altitude of Uranus. -func UranusHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, UranusApparentRaDecN) +// UranusHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(6, localJD, lon, lat, timezone, n, inst) } -// UranusAzimuthN 天王星方位角(截断版) / truncated azimuth of Uranus. -func UranusAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, UranusApparentRaDecN) +// UranusAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(6, localJD, lon, lat, timezone, n, inst) } -// UranusHourAngleN 天王星时角(截断版) / truncated hour angle of Uranus. -func UranusHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, UranusApparentRaN) +// UranusHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return UranusApparentRaNWithInstance(jd, n, inst) }) } -// UranusCulminationTimeN 天王星中天时间(截断版) / truncated culmination time of Uranus. -func UranusCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, UranusHourAngleN) +// UranusCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return UranusHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// UranusRiseTimeN 天王星升起时间(截断版) / truncated rise time of Uranus. -func UranusRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, UranusCulminationTimeN, UranusHeightN, UranusApparentDecN) +// UranusRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return UranusCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return UranusHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return UranusApparentDecNWithInstance(jd, n, inst) }) } -// UranusSetTimeN 天王星落下时间(截断版) / truncated set time of Uranus. -func UranusSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, UranusCulminationTimeN, UranusHeightN, UranusApparentDecN) +// UranusSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return UranusCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return UranusHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return UranusApparentDecNWithInstance(jd, n, inst) }) } -// NeptuneApparentLoN 海王星视黄经(截断版) / truncated apparent ecliptic longitude of Neptune. +// NeptuneApparentLoN 海王星视黄经(n 变体) / n-parameterised apparent ecliptic longitude of Neptune. func NeptuneApparentLoN(jd float64, n int) float64 { lo, _ := planetApparentLoBoN(7, jd, n) return lo } -// NeptuneApparentBoN 海王星视黄纬(截断版) / truncated apparent ecliptic latitude of Neptune. +// NeptuneApparentBoN 海王星视黄纬(n 变体) / n-parameterised apparent ecliptic latitude of Neptune. func NeptuneApparentBoN(jd float64, n int) float64 { _, bo := planetApparentLoBoN(7, jd, n) return bo } -// NeptuneApparentLoBoN 海王星视黄经黄纬(截断版) / truncated apparent ecliptic longitude and latitude of Neptune. +// NeptuneApparentLoBoN 海王星视黄经黄纬(n 变体) / n-parameterised apparent ecliptic longitude and latitude of Neptune. func NeptuneApparentLoBoN(jd float64, n int) (float64, float64) { return planetApparentLoBoN(7, jd, n) } -// NeptuneApparentRaN 海王星视赤经(截断版) / truncated apparent right ascension of Neptune. +// NeptuneApparentRaN 海王星视赤经(n 变体) / n-parameterised apparent right ascension of Neptune. func NeptuneApparentRaN(jd float64, n int) float64 { return planetApparentRaManualN(7, jd, n) } -// NeptuneApparentDecN 海王星视赤纬(截断版) / truncated apparent declination of Neptune. +// NeptuneApparentDecN 海王星视赤纬(n 变体) / n-parameterised apparent declination of Neptune. func NeptuneApparentDecN(jd float64, n int) float64 { return planetApparentDecManualN(7, jd, n) } -// NeptuneApparentRaDecN 海王星视赤经赤纬(截断版) / truncated apparent right ascension and declination of Neptune. +// NeptuneApparentRaDecN 海王星视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination of Neptune. func NeptuneApparentRaDecN(jd float64, n int) (float64, float64) { return planetApparentRaDecManualN(7, jd, n) } -// EarthNeptuneAwayN 地海距离(截断版) / truncated Earth-Neptune distance. +// EarthNeptuneAwayN 地海距离(n 变体) / n-parameterised Earth-Neptune distance. func EarthNeptuneAwayN(jd float64, n int) float64 { return planetEarthAwayN(7, jd, n) } -// NeptuneMagN 海王星视星等(截断版) / truncated apparent magnitude of Neptune. -func NeptuneMagN(jde float64, n int) float64 { - awaySun := planet.WherePlanetN(7, 2, jde, n) - awayEarth := EarthNeptuneAwayN(jde, n) +// NeptuneMagNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneMagNWithInstance(jde float64, n int, inst *Instance) float64 { + awaySun := PlanetCoordinateNWithInstance(7, 2, jde, n, inst) + awayEarth := EarthNeptuneAwayNWithInstance(jde, n, inst) mag := -6.87 + 5*math.Log10(awaySun*awayEarth) return FloatRound(mag, 2) } -// NeptuneHeightN 海王星高度角(截断版) / truncated altitude of Neptune. -func NeptuneHeightN(localJD, lon, lat, timezone float64, n int) float64 { - return planetHeightN(localJD, lon, lat, timezone, n, NeptuneApparentRaDecN) +// NeptuneHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneHeightNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetHeightN(7, localJD, lon, lat, timezone, n, inst) } -// NeptuneAzimuthN 海王星方位角(截断版) / truncated azimuth of Neptune. -func NeptuneAzimuthN(localJD, lon, lat, timezone float64, n int) float64 { - return planetAzimuthN(localJD, lon, lat, timezone, n, NeptuneApparentRaDecN) +// NeptuneAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneAzimuthNWithInstance(localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + return planetAzimuthN(7, localJD, lon, lat, timezone, n, inst) } -// NeptuneHourAngleN 海王星时角(截断版) / truncated hour angle of Neptune. -func NeptuneHourAngleN(jd, lon, timezone float64, n int) float64 { - return planetHourAngleN(jd, lon, timezone, n, NeptuneApparentRaN) +// NeptuneHourAngleNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneHourAngleNWithInstance(jd, lon, timezone float64, n int, inst *Instance) float64 { + return planetHourAngleN(jd, lon, timezone, n, func(jd float64, n int) float64 { return NeptuneApparentRaNWithInstance(jd, n, inst) }) } -// NeptuneCulminationTimeN 海王星中天时间(截断版) / truncated culmination time of Neptune. -func NeptuneCulminationTimeN(localJD, lon, timezone float64, n int) float64 { - return planetCulminationTimeN(localJD, lon, timezone, n, NeptuneHourAngleN) +// NeptuneCulminationTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneCulminationTimeNWithInstance(localJD, lon, timezone float64, n int, inst *Instance) float64 { + return planetCulminationTimeN(localJD, lon, timezone, n, func(jd, lon, timezone float64, n int) float64 { + return NeptuneHourAngleNWithInstance(jd, lon, timezone, n, inst) + }) } -// NeptuneRiseTimeN 海王星升起时间(截断版) / truncated rise time of Neptune. -func NeptuneRiseTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, NeptuneCulminationTimeN, NeptuneHeightN, NeptuneApparentDecN) +// NeptuneRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneRiseTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, n, func(localJD, lon, timezone float64, n int) float64 { + return NeptuneCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return NeptuneHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return NeptuneApparentDecNWithInstance(jd, n, inst) }) } -// NeptuneSetTimeN 海王星落下时间(截断版) / truncated set time of Neptune. -func NeptuneSetTimeN(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int) (float64, error) { - return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, NeptuneCulminationTimeN, NeptuneHeightN, NeptuneApparentDecN) +// NeptuneSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NeptuneSetTimeNWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, n int, inst *Instance) (float64, error) { + return planetRiseDownN(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, n, func(localJD, lon, timezone float64, n int) float64 { + return NeptuneCulminationTimeNWithInstance(localJD, lon, timezone, n, inst) + }, func(localJD, lon, lat, timezone float64, n int) float64 { + return NeptuneHeightNWithInstance(localJD, lon, lat, timezone, n, inst) + }, func(jd float64, n int) float64 { return NeptuneApparentDecNWithInstance(jd, n, inst) }) } diff --git a/basic/precession.go b/basic/precession.go index 6866a7c..ef218b1 100644 --- a/basic/precession.go +++ b/basic/precession.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/ltp" +) // Precess 函数实现了基于 Explanatory Supplement 2nd ed. table 3.211.1 的岁差计算 // ra0, dec0: 初始赤经和赤纬(弧度) @@ -54,142 +58,15 @@ type Vector3 [3]float64 // 三维矩阵 type Matrix3 [3][3]float64 -// 计算长期岁差的黄极向量 -func ltpPECL(epj float64) Vector3 { - // 多项式系数 - pqPol := [2][4]float64{ - {+5851.607687, -0.1189000, -0.00028913, +0.000000101}, - {-1600.886300, +1.1689818, -0.00000020, -0.000000437}, - } +func ltpPECL(epj float64) Vector3 { return Vector3(ltp.EclipticPole(epj)) } - // 周期项系数 - pqPer := [8][5]float64{ - {708.15, -5486.751211, -684.661560, 667.666730, -5523.863691}, - {2309.00, -17.127623, 2446.283880, -2354.886252, -549.747450}, - {1620.00, -617.517403, 399.671049, -428.152441, -310.998056}, - {492.20, 413.442940, -356.652376, 376.202861, 421.535876}, - {1183.00, 78.614193, -186.387003, 184.778874, -36.776172}, - {622.00, -180.732815, -316.800070, 335.321713, -145.278396}, - {882.00, -87.676083, 198.296071, -185.138669, -34.744450}, - {547.00, 46.140315, 101.135679, -120.972830, 22.885731}, - } +func ltpPEQU(epj float64) Vector3 { return Vector3(ltp.EquatorPole(epj)) } - // 计算从J2000.0起算的儒略世纪数 - t := (epj - 2000.0) / 100.0 +func pxp(a, b Vector3) Vector3 { return Vector3(ltp.Cross(ltp.Vector3(a), ltp.Vector3(b))) } - // 初始化P和Q累加器 - p, q := 0.0, 0.0 - - // 周期项 - for i := 0; i < 8; i++ { - w := D2PI * t - a := w / pqPer[i][0] - s := math.Sin(a) - c := math.Cos(a) - p += c*pqPer[i][1] + s*pqPer[i][3] - q += c*pqPer[i][2] + s*pqPer[i][4] - } - - // 多项式项 - w := 1.0 - for i := 0; i < 4; i++ { - p += pqPol[0][i] * w - q += pqPol[1][i] * w - w *= t - } - - // 转换为弧度 - p *= AS2R - q *= AS2R - - // 形成黄极向量 - z := math.Sqrt(math.Max(1.0-p*p-q*q, 0.0)) - s := math.Sin(EPS0) - c := math.Cos(EPS0) - - return Vector3{p, -q*c - z*s, -q*s + z*c} -} - -// 计算长期岁差的赤道极向量 -func ltpPEQU(epj float64) Vector3 { - // 多项式系数 - xyPol := [2][4]float64{ - {+5453.282155, +0.4252841, -0.00037173, -0.000000152}, - {-73750.930350, -0.7675452, -0.00018725, +0.000000231}, - } - - // 周期项系数 - xyPer := [14][5]float64{ - {256.75, -819.940624, 75004.344875, 81491.287984, 1558.515853}, - {708.15, -8444.676815, 624.033993, 787.163481, 7774.939698}, - {274.20, 2600.009459, 1251.136893, 1251.296102, -2219.534038}, - {241.45, 2755.175630, -1102.212834, -1257.950837, -2523.969396}, - {2309.00, -167.659835, -2660.664980, -2966.799730, 247.850422}, - {492.20, 871.855056, 699.291817, 639.744522, -846.485643}, - {396.10, 44.769698, 153.167220, 131.600209, -1393.124055}, - {288.90, -512.313065, -950.865637, -445.040117, 368.526116}, - {231.10, -819.415595, 499.754645, 584.522874, 749.045012}, - {1610.00, -538.071099, -145.188210, -89.756563, 444.704518}, - {620.00, -189.793622, 558.116553, 524.429630, 235.934465}, - {157.87, -402.922932, -23.923029, -13.549067, 374.049623}, - {220.30, 179.516345, -165.405086, -210.157124, -171.330180}, - {1200.00, -9.814756, 9.344131, -44.919798, -22.899655}, - } - - // 计算从J2000.0起算的儒略世纪数 - t := (epj - 2000.0) / 100.0 - - // 初始化X和Y累加器 - x, y := 0.0, 0.0 - - // 周期项 - for i := 0; i < 14; i++ { - w := D2PI * t - a := w / xyPer[i][0] - s := math.Sin(a) - c := math.Cos(a) - x += c*xyPer[i][1] + s*xyPer[i][3] - y += c*xyPer[i][2] + s*xyPer[i][4] - } - - // 多项式项 - w := 1.0 - for i := 0; i < 4; i++ { - x += xyPol[0][i] * w - y += xyPol[1][i] * w - w *= t - } - - // 转换为弧度 - x *= AS2R - y *= AS2R - - // 形成赤道极向量 - w = x*x + y*y - z := 0.0 - if w < 1.0 { - z = math.Sqrt(1.0 - w) - } - - return Vector3{x, y, z} -} - -// 向量叉乘 -func pxp(a, b Vector3) Vector3 { - return Vector3{ - a[1]*b[2] - a[2]*b[1], - a[2]*b[0] - a[0]*b[2], - a[0]*b[1] - a[1]*b[0], - } -} - -// 向量归一化 func pn(p Vector3) (Vector3, float64) { - w := math.Sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]) - if w == 0.0 { - return Vector3{0, 0, 0}, 0 - } - return Vector3{p[0] / w, p[1] / w, p[2] / w}, w + u, w := ltp.Normalize(ltp.Vector3(p)) + return Vector3(u), w } // 计算长期岁差矩阵 diff --git a/basic/refraction.go b/basic/refraction.go index 56b6079..dd37a8f 100644 --- a/basic/refraction.go +++ b/basic/refraction.go @@ -1,167 +1,32 @@ package basic -import "math" +import "b612.me/astro/internal/refraction" const ( - refractionStandardPressureHPa = 1010.0 - refractionStandardTemperatureC = 10.0 - refractionStandardTemperatureK = 283.15 - refractionAbsoluteZeroC = -273.15 - refractionLowerLimitAltitudeDeg = -5.0 - refractionUpperLimitAltitudeDeg = 90.0 + refractionStandardPressureHPa = refraction.StandardPressureHPa + refractionStandardTemperatureC = refraction.StandardTemperatureC ) // RefractionFromApparentAltitude 大气折射修正量,单位度;输入为视高度角。 // 返回值应从视高度角减去后得到真高度角。 // 若模型在支持的真高度范围内没有逆解,则返回 NaN。 func RefractionFromApparentAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { - if !validRefractionInputs(apparentAltitude, pressureHPa, temperatureC) { - return math.NaN() - } - if apparentAltitude <= refractionLowerLimitAltitudeDeg || apparentAltitude >= refractionUpperLimitAltitudeDeg { - return 0 - } - trueAltitude := trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC) - return apparentAltitude - trueAltitude + return refraction.RefractionFromApparentAltitude(apparentAltitude, pressureHPa, temperatureC) } // TrueAltitude 真高度角,单位度;输入为视高度角。若折射模型在支持的 // 真高度范围内没有逆解,则返回 NaN。 func TrueAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { - if !validRefractionInputs(apparentAltitude, pressureHPa, temperatureC) { - return math.NaN() - } - if apparentAltitude <= refractionLowerLimitAltitudeDeg || apparentAltitude >= refractionUpperLimitAltitudeDeg { - return apparentAltitude - } - return trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC) + return refraction.TrueAltitude(apparentAltitude, pressureHPa, temperatureC) } // ApparentAltitude 视高度角,单位度;输入为真高度角。 func ApparentAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { - if !validRefractionInputs(trueAltitude, pressureHPa, temperatureC) { - return math.NaN() - } - return trueAltitude + refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) + return refraction.ApparentAltitude(trueAltitude, pressureHPa, temperatureC) } // RefractionFromTrueAltitude 大气折射修正量,单位度;输入为真高度角。 // 返回值应从真高度角加上后得到视高度角。 func RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { - if !validRefractionInputs(trueAltitude, pressureHPa, temperatureC) { - return math.NaN() - } - return refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) -} - -// Saemundsson 公式以真高度角为输入;逆 API 对同一模型做数值求解,保持公开真/视高度语义一致。 -// Saemundsson's formula takes true altitude. The inverse APIs solve the same model numerically so the public true/apparent semantics remain consistent. -func refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { - if trueAltitude <= refractionLowerLimitAltitudeDeg || trueAltitude >= refractionUpperLimitAltitudeDeg { - return 0 - } - angle := (trueAltitude + 10.3/(trueAltitude+5.11)) * math.Pi / 180 - return refractionScale(pressureHPa, temperatureC) * (1.02 / math.Tan(angle)) / 60 -} - -func trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC float64) float64 { - lower := math.Nextafter(refractionLowerLimitAltitudeDeg, math.Inf(1)) - upper := apparentAltitude - // Saemundsson 近似在 90 度以下极窄范围会略为负值;此时真高度角高于视高度角。若用视高度角作为根区间上界会漏掉有效解,因此保留模型明确的 90 度边界,允许逆解越过输入的视高度角。 - // Saemundsson's approximation becomes slightly negative just below 90 degrees. In that narrow range the true altitude is above the apparent altitude, so using the apparent altitude as the root bracket would omit the valid solution. Keep the model's explicit 90-degree boundary while allowing the inverse to cross above the apparent input. - if refractionFromTrueAltitude(apparentAltitude, pressureHPa, temperatureC) < 0 { - upper = math.Nextafter(refractionUpperLimitAltitudeDeg, math.Inf(-1)) - } - if estimate, ok := trueAltitudeFromApparentNewton(apparentAltitude, pressureHPa, temperatureC, lower, upper); ok { - return estimate - } - return trueAltitudeFromApparentBisection(apparentAltitude, pressureHPa, temperatureC, lower, upper) -} - -func trueAltitudeFromApparentNewton(apparentAltitude, pressureHPa, temperatureC, lower, upper float64) (float64, bool) { - estimate := apparentAltitude - refractionFromTrueAltitude(apparentAltitude, pressureHPa, temperatureC) - const delta = 1e-6 - for i := 0; i < 12; i++ { - if estimate < lower || estimate > upper || !finiteRefractionValue(estimate) { - return 0, false - } - value := refractionInverseResidual(estimate, apparentAltitude, pressureHPa, temperatureC) - if math.Abs(value) < 1e-12 { - return estimate, true - } - refractionPlus := refractionFromTrueAltitude(estimate+delta, pressureHPa, temperatureC) - refractionMinus := refractionFromTrueAltitude(estimate-delta, pressureHPa, temperatureC) - derivative := 1 + (refractionPlus-refractionMinus)/(2*delta) - if math.Abs(derivative) < 1e-12 || !finiteRefractionValue(derivative) { - return 0, false - } - next := estimate - value/derivative - if next < lower || next > upper || !finiteRefractionValue(next) { - return 0, false - } - estimate = next - } - return 0, false -} - -func trueAltitudeFromApparentBisection(apparentAltitude, pressureHPa, temperatureC, lower, upper float64) float64 { - lowerValue := refractionInverseResidual(lower, apparentAltitude, pressureHPa, temperatureC) - upperValue := refractionInverseResidual(upper, apparentAltitude, pressureHPa, temperatureC) - if !finiteRefractionValue(lowerValue) || !finiteRefractionValue(upperValue) || lowerValue > 0 || upperValue < 0 { - return math.NaN() - } - if math.Abs(lowerValue) < 1e-12 { - return lower - } - if math.Abs(upperValue) < 1e-12 { - return upper - } - - best, bestResidual := lower, math.Abs(lowerValue) - if math.Abs(upperValue) < bestResidual { - best, bestResidual = upper, math.Abs(upperValue) - } - for i := 0; i < 96; i++ { - midpoint := lower + (upper-lower)/2 - if midpoint == lower || midpoint == upper { - break - } - value := refractionInverseResidual(midpoint, apparentAltitude, pressureHPa, temperatureC) - if !finiteRefractionValue(value) { - return math.NaN() - } - if residual := math.Abs(value); residual < bestResidual { - best, bestResidual = midpoint, residual - } - if math.Abs(value) < 1e-12 { - return midpoint - } - if value > 0 { - upper = midpoint - } else { - lower = midpoint - } - } - if bestResidual <= 1e-9 { - return best - } - return math.NaN() -} - -func refractionInverseResidual(trueAltitude, apparentAltitude, pressureHPa, temperatureC float64) float64 { - return trueAltitude + refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) - apparentAltitude -} - -func finiteRefractionValue(value float64) bool { - return !math.IsNaN(value) && !math.IsInf(value, 0) -} - -func validRefractionInputs(altitude, pressureHPa, temperatureC float64) bool { - return !(math.IsNaN(altitude) || math.IsInf(altitude, 0) || - math.IsNaN(pressureHPa) || math.IsInf(pressureHPa, 0) || pressureHPa <= 0 || - math.IsNaN(temperatureC) || math.IsInf(temperatureC, 0) || temperatureC <= refractionAbsoluteZeroC) -} - -func refractionScale(pressureHPa, temperatureC float64) float64 { - return pressureHPa / refractionStandardPressureHPa * refractionStandardTemperatureK / (temperatureC - refractionAbsoluteZeroC) + return refraction.RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) } diff --git a/basic/refraction_test.go b/basic/refraction_test.go index 02ec8c9..c4b1cef 100644 --- a/basic/refraction_test.go +++ b/basic/refraction_test.go @@ -72,7 +72,7 @@ func TestRefractionExtremeTemperatureInverse(t *testing.T) { assertClose(t, "ExtremeTemperatureRoundTrip", ApparentAltitude(trueAltitude, pressureHPa, -273), apparentAltitude, 1e-9) - for _, temperatureC := range []float64{-273.14, math.Nextafter(refractionAbsoluteZeroC, math.Inf(1))} { + for _, temperatureC := range []float64{-273.14, math.Nextafter(-273.15, math.Inf(1))} { if got := TrueAltitude(apparentAltitude, pressureHPa, temperatureC); !math.IsNaN(got) { t.Errorf("temperature %.17g C inverse = %v, want NaN when the model has no root", temperatureC, got) } diff --git a/basic/rise_set_altitude.go b/basic/rise_set_altitude.go new file mode 100644 index 0000000..116cdd4 --- /dev/null +++ b/basic/rise_set_altitude.go @@ -0,0 +1,298 @@ +package basic + +import ( + "math" + "time" +) + +// AltitudeLimb 日月取高度的位置;行星与恒星忽略 / limb used for Sun and Moon altitude. +type AltitudeLimb int + +const ( + // AltitudeLimbUpper 上缘,与日出日落口径相同 / upper limb. + AltitudeLimbUpper AltitudeLimb = iota + // AltitudeLimbCenter 中心 / centre. + AltitudeLimbCenter + // AltitudeLimbLower 下缘 / lower limb. + AltitudeLimbLower +) + +// AltitudeCrossingOptions 任意高度升落选项;零值为上缘、标准气象(1010 hPa、10 °C)、计入折射 / options for altitude crossings. +type AltitudeCrossingOptions struct { + Limb AltitudeLimb + // NoRefraction 为 true 时目标高度按几何高度比较 / compare geometric altitude when true. + NoRefraction bool + // PressureHPa 为 0 时用标准气压与气温 / zero selects the standard atmosphere. + PressureHPa float64 + TemperatureC float64 +} + +// AltitudeCrossingState 一个民用日内的穿越状态 / crossing state over one civil day. +type AltitudeCrossingState int + +const ( + // AltitudeCrosses 至少有一次升或落 / at least one crossing. + AltitudeCrosses AltitudeCrossingState = iota + // AltitudeAlwaysAbove 全天高于目标高度 / always above the target altitude. + AltitudeAlwaysAbove + // AltitudeAlwaysBelow 全天低于目标高度 / always below the target altitude. + AltitudeAlwaysBelow + // AltitudeTangent 极值与目标高度相差不足 1″,视为相切 / extremum within 1″ of the target. + AltitudeTangent +) + +// AltitudeCrossing 民用日内首次升、落(本地儒略日,无则为 NaN)与残差范围(度)/ first rise and set in local JD with residual range in degrees. +type AltitudeCrossing struct { + Rise, Set float64 + State AltitudeCrossingState + MinResidual float64 + MaxResidual float64 +} + +// AltitudeCrossingResult date 所在民用日内首次升、落(无则为零值,对应 HasRise/HasSet 为 false)与残差范围(度)/ first rise and set on the civil day. +type AltitudeCrossingResult struct { + Rise, Set time.Time + HasRise, HasSet bool + State AltitudeCrossingState + MinResidual float64 + MaxResidual float64 +} + +const ( + altitudeScanStepDays = 10.0 / 1440 + altitudeTangentDegrees = 1.0 / 3600 +) + +func (options AltitudeCrossingOptions) refraction(trueAltitude float64) float64 { + if options.NoRefraction { + return 0 + } + if options.PressureHPa == 0 { + return RefractionFromTrueAltitude(trueAltitude, refractionStandardPressureHPa, refractionStandardTemperatureC) + } + return RefractionFromTrueAltitude(trueAltitude, options.PressureHPa, options.TemperatureC) +} + +func (options AltitudeCrossingOptions) limbOffset(semidiameterDeg float64) float64 { + switch options.Limb { + case AltitudeLimbUpper: + return semidiameterDeg + case AltitudeLimbLower: + return -semidiameterDeg + } + return 0 +} + +func (options AltitudeCrossingOptions) valid() bool { + if options.Limb < AltitudeLimbUpper || options.Limb > AltitudeLimbLower { + return false + } + return options.NoRefraction || options.PressureHPa == 0 || !math.IsNaN(RefractionFromTrueAltitude(0, options.PressureHPa, options.TemperatureC)) +} + +// SkyAltitudeCrossingWithInstance 日、月或行星在本地民用日 julianDay 内穿过视高度 targetAltitude(度,相对天文地平)的时刻 / +// Sun, Moon or planet crossings of an apparent altitude on the local civil day. +// +// height 只影响月球站心视差;需要以下沉地平为基准时由调用方从 targetAltitude 中减去地平俯角。 +// +// 这是按儒略日窗口求解的低级接口:julianDay 与 timeZone(小时)由调用方拼出本地民用日, +// 固定时区不处理夏令时切换。julianDay 须取本地零点(x.5),正午及之后的值会落到次日窗口。一般用途请用 SkyAltitudeCrossingOnDate,它从 time.Time 推出 +// 当天的真实时区窗口 / low-level julian-day window API; prefer SkyAltitudeCrossingOnDate. +func SkyAltitudeCrossingWithInstance(body SkyBody, julianDay, longitude, latitude, timeZone, height, targetAltitude float64, + options AltitudeCrossingOptions, inst *Instance) (AltitudeCrossing, error) { + altitude, err := skyAltitudeFunc(body, longitude, latitude, timeZone, height, inst) + if err != nil { + return AltitudeCrossing{}, err + } + start := math.Floor(julianDay) + 0.5 + return altitudeCrossing(start, start+1, latitude, height, targetAltitude, options, altitude) +} + +// SkyAltitudeCrossingOnDate 日、月或行星在 date 所在民用日内穿过视高度 targetAltitude 的时刻;夏令时切换日按实际日长搜索 / +// Sun, Moon or planet crossings of an apparent altitude on the civil day of date. +func SkyAltitudeCrossingOnDate(body SkyBody, date time.Time, longitude, latitude, height, targetAltitude float64, + options AltitudeCrossingOptions, inst *Instance) (AltitudeCrossingResult, error) { + start, end, timeZone := civilDayWindow(date) + altitude, err := skyAltitudeFunc(body, longitude, latitude, timeZone, height, inst) + if err != nil { + return AltitudeCrossingResult{}, err + } + raw, err := altitudeCrossing(start, end, latitude, height, targetAltitude, options, altitude) + return altitudeCrossingOnDate(date, timeZone, raw, err) +} + +// StarAltitudeCrossing 恒星(视赤经、视赤纬,度)在本地民用日内穿过视高度 targetAltitude 的时刻 / star crossings of an apparent altitude. +// +// julianDay 须取本地零点(x.5),正午及之后的值会落到次日窗口;一般用途请用 StarAltitudeCrossingOnDate。 +func StarAltitudeCrossing(julianDay, ra, dec, longitude, latitude, timeZone, targetAltitude float64, options AltitudeCrossingOptions) (AltitudeCrossing, error) { + if !isFiniteFloat(ra) || !isFiniteFloat(dec) { + return AltitudeCrossing{}, ErrInvalidObservationInput + } + start := math.Floor(julianDay) + 0.5 + return altitudeCrossing(start, start+1, latitude, 0, targetAltitude, options, starAltitudeFunc(ra, dec, longitude, latitude, timeZone)) +} + +// StarAltitudeCrossingOnDate 恒星在 date 所在民用日内穿过视高度 targetAltitude 的时刻;夏令时切换日按实际日长搜索 / +// star crossings of an apparent altitude on the civil day of date. +func StarAltitudeCrossingOnDate(date time.Time, ra, dec, longitude, latitude, targetAltitude float64, options AltitudeCrossingOptions) (AltitudeCrossingResult, error) { + if !isFiniteFloat(ra) || !isFiniteFloat(dec) { + return AltitudeCrossingResult{}, ErrInvalidObservationInput + } + start, end, timeZone := civilDayWindow(date) + raw, err := altitudeCrossing(start, end, latitude, 0, targetAltitude, options, starAltitudeFunc(ra, dec, longitude, latitude, timeZone)) + return altitudeCrossingOnDate(date, timeZone, raw, err) +} + +// civilDayWindow 以当天零点的偏移固定本地时标,窗口为真实日长(夏令时切换日 23 或 25 小时)。 +func civilDayWindow(date time.Time) (start, end, timeZone float64) { + year, month, day := date.Date() + midnight := time.Date(year, month, day, 0, 0, 0, 0, date.Location()) + next := time.Date(year, month, day+1, 0, 0, 0, 0, date.Location()) + _, offset := midnight.Zone() + start = Date2JD(midnight) + return start, start + next.Sub(midnight).Hours()/24, float64(offset) / 3600 +} + +func altitudeCrossingOnDate(date time.Time, timeZone float64, raw AltitudeCrossing, err error) (AltitudeCrossingResult, error) { + if err != nil { + return AltitudeCrossingResult{}, err + } + convert := func(jd float64) (time.Time, bool) { + if math.IsNaN(jd) { + return time.Time{}, false + } + return JD2DateByZone(jd-timeZone/24, date.Location(), false), true + } + out := AltitudeCrossingResult{State: raw.State, MinResidual: raw.MinResidual, MaxResidual: raw.MaxResidual} + out.Rise, out.HasRise = convert(raw.Rise) + out.Set, out.HasSet = convert(raw.Set) + return out, nil +} + +func skyAltitudeFunc(body SkyBody, longitude, latitude, timeZone, height float64, inst *Instance) (func(jd float64) (float64, float64), error) { + switch { + case body == SkySun: + return func(jd float64) (float64, float64) { + state := sunObservationStateN(jd, longitude, latitude, timeZone, -1, inst) + return state.altitude, angularSemidiameterFromAU(sunEquatorialRadiusKM, state.distanceAU) / 3600 + }, nil + case body == SkyMoon: + return func(jd float64) (float64, float64) { + state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, -1, inst) + return state.altitude, angularSemidiameterArcsec(moonEquatorialRadiusKM, state.distanceKM) / 3600 + }, nil + case body.valid(): + heightFn := skyPlanetHeight(body) + return func(jd float64) (float64, float64) { return heightFn(jd, longitude, latitude, timeZone, inst), 0 }, nil + } + return nil, ErrInvalidObservationInput +} + +func starAltitudeFunc(ra, dec, longitude, latitude, timeZone float64) func(jd float64) (float64, float64) { + return func(jd float64) (float64, float64) { + return StarHeight(jd, ra, dec, longitude, latitude, timeZone), 0 + } +} + +func skyPlanetHeight(body SkyBody) func(localJD, lon, lat, timezone float64, inst *Instance) float64 { + switch body { + case SkyMercury: + return MercuryHeightWithInstance + case SkyVenus: + return VenusHeightWithInstance + case SkyMars: + return MarsHeightWithInstance + case SkyJupiter: + return JupiterHeightWithInstance + case SkySaturn: + return SaturnHeightWithInstance + case SkyUranus: + return UranusHeightWithInstance + } + return NeptuneHeightWithInstance +} + +func altitudeCrossing(start, end, latitude, height, targetAltitude float64, options AltitudeCrossingOptions, + altitude func(jd float64) (float64, float64)) (AltitudeCrossing, error) { + if !isFiniteFloat(start) || !isFiniteFloat(end) || !(end > start) || !isFiniteFloat(latitude) || math.Abs(latitude) > 90 || + !isFiniteFloat(height) || !isFiniteFloat(targetAltitude) || math.Abs(targetAltitude) > 90 || !options.valid() { + return AltitudeCrossing{}, ErrInvalidObservationInput + } + residual := func(jd float64) float64 { + alt, semidiameter := altitude(jd) + return alt + options.refraction(alt) + options.limbOffset(semidiameter) - targetAltitude + } + result := AltitudeCrossing{Rise: math.NaN(), Set: math.NaN(), MinResidual: math.Inf(1), MaxResidual: math.Inf(-1)} + prevT, prevR := start, residual(start) + if math.IsNaN(prevR) { + return AltitudeCrossing{}, ErrInvalidObservationInput + } + track := func(t, r float64) { + result.MinResidual = math.Min(result.MinResidual, r) + result.MaxResidual = math.Max(result.MaxResidual, r) + } + track(prevT, prevR) + record := func(root float64, rising bool) { + if rising && math.IsNaN(result.Rise) { + result.Rise = root + } else if !rising && math.IsNaN(result.Set) { + result.Set = root + } + } + // 同号区间内朝目标一侧的极值可能越过目标高度,采样点看不到这两次穿越。 + refine := func(a, b, ra float64) { + peak, rp := orbitalGoldenExtremum(residual, a, b, ra < 0) + if math.IsNaN(rp) { + return + } + track(peak, rp) + if (rp < 0) == (ra < 0) || peak <= a || peak >= b { + return + } + record(orbitalBisect(residual, a, peak, ra), ra < 0) + record(orbitalBisect(residual, peak, b, rp), ra >= 0) + } + prev2T, prev2R, samples := prevT, prevR, 1 + for t := start + altitudeScanStepDays; prevT < end; t += altitudeScanStepDays { + t = math.Min(t, end) + r := residual(t) + if math.IsNaN(r) { + return AltitudeCrossing{}, ErrInvalidObservationInput + } + track(t, r) + sameSign := (prevR < 0) == (r < 0) + switch { + case !sameSign: + record(orbitalBisect(residual, prevT, t, prevR), r >= 0) + case samples == 1: + refine(prevT, t, prevR) + case (prev2R < 0) == (prevR < 0) && altitudeTowardTarget(prev2R, prevR, r): + refine(prev2T, t, prev2R) + } + prev2T, prev2R = prevT, prevR + prevT, prevR = t, r + samples++ + } + if samples > 2 && (prev2R < 0) == (prevR < 0) { + refine(prev2T, prevT, prev2R) + } + switch { + case !math.IsNaN(result.Rise) || !math.IsNaN(result.Set): + result.State = AltitudeCrosses + case math.Abs(result.MinResidual) < altitudeTangentDegrees || math.Abs(result.MaxResidual) < altitudeTangentDegrees: + result.State = AltitudeTangent + case result.MinResidual > 0: + result.State = AltitudeAlwaysAbove + default: + result.State = AltitudeAlwaysBelow + } + return result, nil +} + +// altitudeTowardTarget 报告中间样本是否为朝零点一侧的局部极值:负残差取极大,正残差取极小。 +func altitudeTowardTarget(left, middle, right float64) bool { + if middle < 0 { + return middle >= left && middle >= right + } + return middle <= left && middle <= right +} diff --git a/basic/rise_set_altitude_test.go b/basic/rise_set_altitude_test.go new file mode 100644 index 0000000..4e7e695 --- /dev/null +++ b/basic/rise_set_altitude_test.go @@ -0,0 +1,86 @@ +// 被测契约:上缘、标准气象下的 0° 穿越与现有日出日落一致;高纬状态与相切判定正确。 +package basic + +import ( + "math" + "testing" + "time" +) + +func beijingDate(y int, m time.Month, d int) time.Time { + return time.Date(y, m, d, 0, 0, 0, 0, time.FixedZone("CST", 8*3600)) +} + +func TestSkyAltitudeCrossingMatchesSunriseAtHorizon(t *testing.T) { + jd := Date2JD(beijingDate(2025, 3, 20)) + rise, err := GetSunRiseTimeWithAtmosphere(jd, 116.4, 39.9, 8, 0, 1010, 10, nil) + if err != nil { + t.Fatal(err) + } + set, _ := GetSunSetTimeWithAtmosphere(jd, 116.4, 39.9, 8, 0, 1010, 10, nil) + got, err := SkyAltitudeCrossingWithInstance(SkySun, jd, 116.4, 39.9, 8, 0, 0, AltitudeCrossingOptions{}, nil) + if err != nil || got.State != AltitudeCrosses { + t.Fatalf("state %v err %v", got.State, err) + } + if d := math.Abs(got.Rise-rise) * 86400; d > 2 { + t.Errorf("rise differs by %.2f s", d) + } + if d := math.Abs(got.Set-set) * 86400; d > 2 { + t.Errorf("set differs by %.2f s", d) + } + civil, _ := SkyAltitudeCrossingWithInstance(SkySun, jd, 116.4, 39.9, 8, 0, -6, AltitudeCrossingOptions{Limb: AltitudeLimbCenter, NoRefraction: true}, nil) + if !(civil.Rise < got.Rise && civil.Set > got.Set) { + t.Error("civil twilight must bracket sunrise and sunset") + } +} + +func TestSkyAltitudeCrossingStates(t *testing.T) { + summer := Date2JD(beijingDate(2025, 6, 21)) + winter := Date2JD(beijingDate(2025, 12, 21)) + above, _ := SkyAltitudeCrossingWithInstance(SkySun, summer, 20, 78, 0, 0, 0, AltitudeCrossingOptions{}, nil) + below, _ := SkyAltitudeCrossingWithInstance(SkySun, winter, 20, 78, 0, 0, 0, AltitudeCrossingOptions{}, nil) + if above.State != AltitudeAlwaysAbove || below.State != AltitudeAlwaysBelow { + t.Errorf("polar states %v %v", above.State, below.State) + } + peak := 90 - 39.9 + 23.4366 + tangent, _ := SkyAltitudeCrossingWithInstance(SkySun, summer, 116.4, 39.9, 8, 0, peak, AltitudeCrossingOptions{Limb: AltitudeLimbCenter, NoRefraction: true}, nil) + if tangent.State == AltitudeCrosses && !math.IsNaN(tangent.Rise) && !math.IsNaN(tangent.Set) && (tangent.Set-tangent.Rise)*1440 > 30 { + t.Errorf("near-peak altitude should graze, got %+v", tangent) + } + if _, err := SkyAltitudeCrossingWithInstance(SkySun, summer, 0, 95, 0, 0, 0, AltitudeCrossingOptions{}, nil); err == nil { + t.Error("invalid latitude must fail") + } +} + +func TestStarAndPlanetAltitudeCrossing(t *testing.T) { + jd := Date2JD(beijingDate(2025, 3, 20)) + star, err := StarAltitudeCrossing(jd, 101.287, -16.716, 116.4, 39.9, 8, 10, AltitudeCrossingOptions{}) + if err != nil || star.State != AltitudeCrosses || math.IsNaN(star.Rise) || math.IsNaN(star.Set) { + t.Fatalf("Sirius at 10°: %+v %v", star, err) + } + mars, err := SkyAltitudeCrossingWithInstance(SkyMars, jd, 116.4, 39.9, 8, 0, 0, AltitudeCrossingOptions{}, nil) + if err != nil || mars.State != AltitudeCrosses { + t.Fatalf("mars: %+v %v", mars, err) + } +} + +func TestAltitudeCrossingFindsNearPeakCrossings(t *testing.T) { + jd := Date2JD(beijingDate(2025, 3, 20)) + options := AltitudeCrossingOptions{NoRefraction: true} + for ra := 0.0; ra < 360; ra += 15 { + got, err := StarAltitudeCrossing(jd, ra, 19.9, 116.4, 39.9, 8, 69.999, options) + if err != nil || got.State != AltitudeCrosses || got.MaxResidual <= 0 { + t.Fatalf("ra %v: %+v %v", ra, got, err) + } + if !math.IsNaN(got.Rise) && !math.IsNaN(got.Set) && !(got.Rise < got.Set && (got.Set-got.Rise)*1440 < 10) { + t.Errorf("ra %v: rise %v set %v", ra, got.Rise, got.Set) + } + if math.IsNaN(got.Rise) && math.IsNaN(got.Set) { + t.Errorf("ra %v: no crossing", ra) + } + } + missed, _ := StarAltitudeCrossing(jd, 90, 19.9, 116.4, 39.9, 8, 70.01, options) + if missed.State != AltitudeAlwaysBelow { + t.Errorf("above peak: %+v", missed) + } +} diff --git a/basic/rise_set_curve.go b/basic/rise_set_curve.go index a417446..7eb1f6b 100644 --- a/basic/rise_set_curve.go +++ b/basic/rise_set_curve.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) // RiseSetPhase 标识升落边界对应的局部事件阶段。 @@ -199,7 +201,7 @@ func riseSetRefineGeographicRoot( return 0, 0, false } if math.Abs(first) <= 1e-11 && math.Abs(second) <= 1e-11 { - return normalizeLongitude(longitude), latitude, true + return geodata.NormalizeLongitude(longitude), latitude, true } firstLon, secondLon, lonOK := residualAt(longitude+finiteDifferenceDegrees, latitude) firstLat, secondLat, latOK := residualAt(longitude, latitude+finiteDifferenceDegrees) @@ -221,14 +223,14 @@ func riseSetRefineGeographicRoot( deltaLongitude *= 5 / scale deltaLatitude *= 5 / scale } - longitude = normalizeLongitude(longitude + deltaLongitude) + longitude = geodata.NormalizeLongitude(longitude + deltaLongitude) latitude += deltaLatitude if latitude <= -89.999999 || latitude >= 89.999999 || !finite(latitude) { return 0, 0, false } } first, second, ok := residualAt(longitude, latitude) - return normalizeLongitude(longitude), latitude, + return geodata.NormalizeLongitude(longitude), latitude, ok && finite(first) && finite(second) && math.Abs(first) <= 1e-8 && math.Abs(second) <= 1e-8 } @@ -251,7 +253,7 @@ func riseSetHorizonPoint(centerLongitude, centerLatitude, angle float64) (float6 first[1]*math.Cos(angle) + second[1]*math.Sin(angle), first[2]*math.Cos(angle) + second[2]*math.Sin(angle), } - return normalizeLongitude(math.Atan2(point[1], point[0]) * 180 / math.Pi), + return geodata.NormalizeLongitude(math.Atan2(point[1], point[0]) * 180 / math.Pi), math.Asin(math.Max(-1, math.Min(1, point[2]))) * 180 / math.Pi } diff --git a/basic/rise_set_curve_test.go b/basic/rise_set_curve_test.go index 47f3934..6b201ad 100644 --- a/basic/rise_set_curve_test.go +++ b/basic/rise_set_curve_test.go @@ -565,7 +565,7 @@ func assertOccultationRiseSetPolarProjectionSpacing( deltaLongitude := math.Remainder((second.Longitude-first.Longitude)*rad, 2*math.Pi) firstY := math.Log(math.Tan(math.Pi/4 + math.Min(85, math.Max(-85, first.Latitude))*rad/2)) secondY := math.Log(math.Tan(math.Pi/4 + math.Min(85, math.Max(-85, second.Latitude))*rad/2)) - distance := occultationTopocentricEarthRadiusKM * math.Hypot(deltaLongitude, secondY-firstY) + distance := topocentricEarthRadiusKM * math.Hypot(deltaLongitude, secondY-firstY) if distance > maximumMercatorKM { t.Fatalf("%s/%s segment %d edge %d has %.1f km Web Mercator length, want at most %.1f km", curve.Phase, curve.Direction, segmentIndex, pointIndex-1, distance, maximumMercatorKM) diff --git a/basic/rise_set_dynamic_test.go b/basic/rise_set_dynamic_test.go index 7f23fdf..1d370de 100644 --- a/basic/rise_set_dynamic_test.go +++ b/basic/rise_set_dynamic_test.go @@ -46,7 +46,7 @@ func TestSunRiseSetDynamicResidual(t *testing.T) { func TestSunApparentStateReusesDistanceWithoutChangingCoordinates(t *testing.T) { const jd = 2460827.5 ra, dec, distanceAU := hSunApparentRaDecDistanceN(jd, -1) - wantRA, wantDec := LoBoToRaDec(jd, HSunApparentLoN(jd, -1), HSunTrueBoN(jd, -1)) + wantRA, wantDec := HSunApparentRaDec(jd) assertClose(t, "sun state RA", ra, wantRA, 1e-12) assertClose(t, "sun state Dec", dec, wantDec, 1e-12) assertClose(t, "sun state distance", distanceAU, EarthAwayN(jd, -1), 1e-15) diff --git a/basic/rise_set_weather.go b/basic/rise_set_weather.go new file mode 100644 index 0000000..88eded5 --- /dev/null +++ b/basic/rise_set_weather.go @@ -0,0 +1,66 @@ +package basic + +import "math" + +const ( + riseSetWeatherMaxPressureHPa = 1200.0 + riseSetWeatherMinTemperatureC = -100.0 + riseSetWeatherMaxTemperatureC = 80.0 +) + +// validRiseSetWeather 气压与气温须落在地表可能出现的量程内。 +func validRiseSetWeather(pressureHPa, temperatureC float64) bool { + return pressureHPa > 0 && pressureHPa <= riseSetWeatherMaxPressureHPa && + temperatureC >= riseSetWeatherMinTemperatureC && temperatureC <= riseSetWeatherMaxTemperatureC +} + +type riseSetWeatherResidual func(jd, longitude, latitude, timeZone, zenithShift, height, pressureHPa, temperatureC float64, n int, inst *Instance) float64 + +// riseSetWithWeather 以标准气象结果为种子,在给定气压气温下重新求解同一民用日的升落;种子失效时按分钟扫描。 +func riseSetWithWeather(seed float64, seedErr error, julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, + isRise bool, n int, inst *Instance, residualAt riseSetWeatherResidual) (float64, error) { + if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || + !isFiniteFloat(height) || !validRiseSetWeather(pressureHPa, temperatureC) { + return 0, ErrInvalidObservationInput + } + civilDayStart := math.Floor(julianDay) + 0.5 + residual := func(jd float64) float64 { + return residualAt(jd, longitude, latitude, timeZone, 1, height, pressureHPa, temperatureC, n, inst) + } + if seedErr == nil && isFiniteFloat(seed) { + const derivativeStep = 0.000005 + slope := math.NaN() + refined, ok := eventNewtonRefine(seed, 1e-7, func(jd float64) float64 { + slope = (residual(jd+derivativeStep) - residual(jd-derivativeStep)) / (2 * derivativeStep) + return residual(jd) / slope + }) + if ok && math.Abs(refined-seed) < 0.02 && eventRiseSetCandidateValid(refined, civilDayStart, slope, isRise) { + return refined, nil + } + } + return eventDirectionalRiseSetSearch(civilDayStart, isRise, nil, residual) +} + +// GetSunRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算日出,口径同 GetSunRiseTimeWithInstance 开启大气修正 / sunrise with explicit pressure and temperature. +func GetSunRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := GetSunRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return riseSetWithWeather(seed, err, julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC, true, -1, inst, sunRiseSetResidualWeather) +} + +// GetSunSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算日落 / sunset with explicit pressure and temperature. +func GetSunSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := GetSunSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return riseSetWithWeather(seed, err, julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC, false, -1, inst, sunRiseSetResidualWeather) +} + +// GetMoonRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算月出 / moonrise with explicit pressure and temperature. +func GetMoonRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := GetMoonRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return riseSetWithWeather(seed, err, julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC, true, -1, inst, moonRiseSetResidualWeather) +} + +// GetMoonSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算月落 / moonset with explicit pressure and temperature. +func GetMoonSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := GetMoonSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return riseSetWithWeather(seed, err, julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC, false, -1, inst, moonRiseSetResidualWeather) +} diff --git a/basic/rise_set_weather_points.go b/basic/rise_set_weather_points.go new file mode 100644 index 0000000..f81f7d1 --- /dev/null +++ b/basic/rise_set_weather_points.go @@ -0,0 +1,143 @@ +package basic + +import "math" + +type riseSetHeightFunc func(localJD float64) float64 + +// pointRiseSetWithWeather 点光源(行星、恒星)带气象升落:沿用标准结果所在的事件窗口,残差为几何高度 + 地平俯角 + 指定气象折射。 +// 行星与恒星的标准升落按最近中天取事件,可能落在输入日期的前一天,因此这里不做民用日校验。 +func pointRiseSetWithWeather(seed float64, seedErr error, latitude, height, pressureHPa, temperatureC float64, + isRise bool, heightAt riseSetHeightFunc) (float64, error) { + if seedErr != nil { + return 0, seedErr + } + if !isFiniteFloat(seed) || !isFiniteFloat(latitude) || !isFiniteFloat(height) || !validRiseSetWeather(pressureHPa, temperatureC) { + return 0, ErrInvalidObservationInput + } + residual := func(jd float64) float64 { + altitude := heightAt(jd) + return altitude + HeightDegreeByLat(height, latitude) + RefractionFromTrueAltitude(altitude, pressureHPa, temperatureC) + } + const derivativeStep = 0.000005 + slope := math.NaN() + refined, ok := eventNewtonRefine(seed, 1e-7, func(jd float64) float64 { + slope = (residual(jd+derivativeStep) - residual(jd-derivativeStep)) / (2 * derivativeStep) + return residual(jd) / slope + }) + if !ok || math.Abs(refined-seed) > 0.02 || !isFiniteFloat(slope) || (isRise && slope <= 0) || (!isRise && slope >= 0) { + return 0, ErrInvalidObservationInput + } + return refined, nil +} + +// MercuryRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算水星升起 / mercury rise with explicit pressure and temperature. +func MercuryRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := MercuryRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return MercuryHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// MercurySetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算水星落下 / mercury set with explicit pressure and temperature. +func MercurySetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := MercurySetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return MercuryHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// VenusRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算金星升起 / venus rise with explicit pressure and temperature. +func VenusRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := VenusRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return VenusHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// VenusSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算金星落下 / venus set with explicit pressure and temperature. +func VenusSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := VenusSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return VenusHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// MarsRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算火星升起 / mars rise with explicit pressure and temperature. +func MarsRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := MarsRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return MarsHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// MarsSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算火星落下 / mars set with explicit pressure and temperature. +func MarsSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := MarsSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return MarsHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// JupiterRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算木星升起 / jupiter rise with explicit pressure and temperature. +func JupiterRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := JupiterRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return JupiterHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// JupiterSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算木星落下 / jupiter set with explicit pressure and temperature. +func JupiterSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := JupiterSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return JupiterHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// SaturnRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算土星升起 / saturn rise with explicit pressure and temperature. +func SaturnRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := SaturnRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return SaturnHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// SaturnSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算土星落下 / saturn set with explicit pressure and temperature. +func SaturnSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := SaturnSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return SaturnHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// UranusRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算天王星升起 / uranus rise with explicit pressure and temperature. +func UranusRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := UranusRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return UranusHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// UranusSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算天王星落下 / uranus set with explicit pressure and temperature. +func UranusSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := UranusSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return UranusHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// NeptuneRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算海王星升起 / neptune rise with explicit pressure and temperature. +func NeptuneRiseTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := NeptuneRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return NeptuneHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// NeptuneSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算海王星落下 / neptune set with explicit pressure and temperature. +func NeptuneSetTimeWithAtmosphere(julianDay, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64, inst *Instance) (float64, error) { + seed, err := NeptuneSetTimeWithInstance(julianDay, longitude, latitude, timeZone, 1, height, inst) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return NeptuneHeightWithInstance(jd, longitude, latitude, timeZone, inst) }) +} + +// StarRiseTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算恒星升起;ra/dec 为瞬时视赤经赤纬(度) / stellar rise with explicit pressure and temperature. +func StarRiseTimeWithAtmosphere(julianDay, ra, dec, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64) (float64, error) { + seed, err := StarRiseTime(julianDay, ra, dec, longitude, latitude, height, timeZone, true) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, true, + func(jd float64) float64 { return StarHeight(jd, ra, dec, longitude, latitude, timeZone) }) +} + +// StarSetTimeWithAtmosphere 按给定气压(hPa)与气温(°C)计算恒星落下;ra/dec 为瞬时视赤经赤纬(度) / stellar set with explicit pressure and temperature. +func StarSetTimeWithAtmosphere(julianDay, ra, dec, longitude, latitude, timeZone, height, pressureHPa, temperatureC float64) (float64, error) { + seed, err := StarSetTime(julianDay, ra, dec, longitude, latitude, height, timeZone, true) + return pointRiseSetWithWeather(seed, err, latitude, height, pressureHPa, temperatureC, false, + func(jd float64) float64 { return StarHeight(jd, ra, dec, longitude, latitude, timeZone) }) +} diff --git a/basic/rise_set_weather_test.go b/basic/rise_set_weather_test.go new file mode 100644 index 0000000..9a73e45 --- /dev/null +++ b/basic/rise_set_weather_test.go @@ -0,0 +1,129 @@ +// 被测契约:标准气象下与原升落一致;高压低温让日出提前、日落推迟;非法气象报错。 +package basic + +import ( + "math" + "testing" +) + +func TestRiseSetWithAtmosphere(t *testing.T) { + jd, lon, lat, tz := 2460676.5, 116.4, 39.9, 8.0 + cases := []struct { + name string + standard func() (float64, error) + weather func(p, c float64) (float64, error) + rise bool + }{ + {"sunrise", func() (float64, error) { return GetSunRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) }, + func(p, c float64) (float64, error) { + return GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil) + }, true}, + {"sunset", func() (float64, error) { return GetSunSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) }, + func(p, c float64) (float64, error) { + return GetSunSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil) + }, false}, + {"moonrise", func() (float64, error) { return GetMoonRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) }, + func(p, c float64) (float64, error) { + return GetMoonRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil) + }, true}, + {"moonset", func() (float64, error) { return GetMoonSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) }, + func(p, c float64) (float64, error) { + return GetMoonSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil) + }, false}, + } + for _, c := range cases { + base, err := c.standard() + if err != nil { + t.Fatalf("%s: %v", c.name, err) + } + same, err := c.weather(refractionStandardPressureHPa, refractionStandardTemperatureC) + if err != nil || math.Abs(same-base)*86400 > 0.01 { + t.Errorf("%s: standard weather %.6f vs %.6f (%v)", c.name, same, base, err) + } + dense, err := c.weather(1040, -30) + if err != nil { + t.Fatalf("%s dense: %v", c.name, err) + } + shift := (dense - base) * 86400 + t.Logf("%s: 1040 hPa / -30 °C shifts %+.1f s", c.name, shift) + if c.rise && shift >= 0 || !c.rise && shift <= 0 || math.Abs(shift) > 120 { + t.Errorf("%s: implausible shift %.1f s", c.name, shift) + } + } + if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, -1, 10, nil); err != ErrInvalidObservationInput { + t.Errorf("negative pressure: %v", err) + } + if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, 1010, -300, nil); err != ErrInvalidObservationInput { + t.Errorf("below absolute zero: %v", err) + } +} + +func TestPointRiseSetWithAtmosphere(t *testing.T) { + jd, lon, lat, tz := 2460676.5, 116.4, 39.9, 8.0 + type pair struct { + name string + plain, atmos func(p, temp float64) (float64, error) + } + cases := []pair{ + {"MarsRise", func(float64, float64) (float64, error) { return MarsRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) }, + func(p, temp float64) (float64, error) { + return MarsRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, temp, nil) + }}, + {"JupiterSet", func(float64, float64) (float64, error) { + return JupiterSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) + }, + func(p, temp float64) (float64, error) { + return JupiterSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, temp, nil) + }}, + {"SiriusRise", func(float64, float64) (float64, error) { + return StarRiseTime(jd, 101.29, -16.72, lon, lat, 0, tz, true) + }, + func(p, temp float64) (float64, error) { + return StarRiseTimeWithAtmosphere(jd, 101.29, -16.72, lon, lat, tz, 0, p, temp) + }}, + } + for _, c := range cases { + plain, err := c.plain(0, 0) + if err != nil { + t.Fatalf("%s plain: %v", c.name, err) + } + std, err := c.atmos(refractionStandardPressureHPa, refractionStandardTemperatureC) + if err != nil { + t.Fatalf("%s standard: %v", c.name, err) + } + // 原接口用固定 34′ 地平折射,这里按公式在实际高度求折射,标准气象下差约 2–3 s。 + if d := math.Abs(std-plain) * 86400; d > 5 { + t.Errorf("%s: standard weather differs from plain by %.2f s", c.name, d) + } + dense, err := c.atmos(1040, -30) + if err != nil { + t.Fatalf("%s dense: %v", c.name, err) + } + shift := (dense - std) * 86400 + t.Logf("%s: 1040 hPa / -30 °C shifts %+.1f s", c.name, shift) + rising := c.name != "JupiterSet" + if (rising && shift >= 0) || (!rising && shift <= 0) || math.Abs(shift) > 300 { + t.Errorf("%s: implausible shift %.1f s", c.name, shift) + } + } + if _, err := MarsRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, -1, 10, nil); err == nil { + t.Error("invalid pressure must fail") + } +} + +func TestRiseSetWeatherRejectsOutOfRange(t *testing.T) { + jd, lon, lat, tz := 2460831.0, 116.4, 39.9, 8.0 + for _, c := range [][2]float64{{1.01e9, 10}, {1e6, 10}, {1201, 10}, {1010, -273.14}, {1010, -101}, {1010, 81}, {math.NaN(), 10}, {1010, math.Inf(1)}} { + if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != ErrInvalidObservationInput { + t.Errorf("sun p=%g T=%g: err=%v", c[0], c[1], err) + } + if _, err := MercuryRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != ErrInvalidObservationInput { + t.Errorf("mercury p=%g T=%g: err=%v", c[0], c[1], err) + } + } + for _, c := range [][2]float64{{1200, 10}, {1010, -100}, {1010, 80}, {300, 10}} { + if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != nil { + t.Errorf("sun p=%g T=%g must be accepted: %v", c[0], c[1], err) + } + } +} diff --git a/basic/saturn.go b/basic/saturn.go index 59d0ea4..4938378 100644 --- a/basic/saturn.go +++ b/basic/saturn.go @@ -109,59 +109,34 @@ func SaturnMag(jde float64) float64 { return SaturnMagN(jde, -1) } -func SaturnHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := SaturnApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// SaturnHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(5, localJD, lon, lat, timezone, -1, inst) } -func SaturnAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := SaturnApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// SaturnAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(5, localJD, lon, lat, timezone, -1, inst) } -func SaturnHourAngle(jd, lon, timezone float64) float64 { +// SaturnHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - SaturnApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - SaturnApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func SaturnCulminationTime(localJD, lon, timezone float64) float64 { +// SaturnCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-SaturnHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-SaturnHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := SaturnHourAngle(localJD, lon, timezone) + currentHourAngle := SaturnHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -179,14 +154,22 @@ func SaturnCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func SaturnRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// SaturnRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func SaturnSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// SaturnSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SaturnSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, SaturnCulminationTime, SaturnHeight, SaturnApparentDec) +func saturnRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return SaturnCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return SaturnHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return SaturnApparentDecWithInstance(jde, inst) }) } diff --git a/basic/saturn_events.go b/basic/saturn_events.go index c37daa7..e60aef6 100644 --- a/basic/saturn_events.go +++ b/basic/saturn_events.go @@ -14,8 +14,10 @@ const ( saturnPhaseCoarseTolerance = 30.0 / 86400.0 ) -func saturnSunLongitudeDelta(jde, degree float64, filter bool) float64 { - sub := Limit360(Limit360(SaturnApparentLo(jde)-HSunApparentLo(jde)) - degree) +func saturnSunLongitudeDelta(jde, degree float64, filter bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(SaturnApparentLoWithInstance(jde, inst)-HSunApparentLoWithInstance(jde, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -27,8 +29,10 @@ func saturnSunLongitudeDelta(jde, degree float64, filter bool) float64 { return sub } -func saturnSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { - sub := Limit360(Limit360(SaturnApparentLoN(jde, n)-HSunApparentLoN(jde, n)) - degree) +func saturnSunLongitudeDeltaN(jde, degree float64, filter bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(SaturnApparentLoNWithInstance(jde, n, inst)-HSunApparentLoNWithInstance(jde, n, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -40,8 +44,10 @@ func saturnSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { return sub } -func saturnRADerivative(jde, delta float64) float64 { - sub := SaturnApparentRa(jde+delta) - SaturnApparentRa(jde-delta) +func saturnRADerivative(jde, delta float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := SaturnApparentRaWithInstance(jde+delta, inst) - SaturnApparentRaWithInstance(jde-delta, inst) if sub > 180 { sub -= 360 } @@ -51,8 +57,10 @@ func saturnRADerivative(jde, delta float64) float64 { return sub / (2 * delta) } -func saturnRADerivativeN(jde, delta float64, n int) float64 { - sub := SaturnApparentRaN(jde+delta, n) - SaturnApparentRaN(jde-delta, n) +func saturnRADerivativeN(jde, delta float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := SaturnApparentRaNWithInstance(jde+delta, n, inst) - SaturnApparentRaNWithInstance(jde-delta, n, inst) if sub > 180 { sub -= 360 } @@ -62,13 +70,15 @@ func saturnRADerivativeN(jde, delta float64, n int) float64 { return sub / (2 * delta) } -func saturnConjunctionFull(jde, degree float64, next uint8) float64 { +func saturnConjunctionFull(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := SATURN_S_PERIOD / 360 - currentDelta := saturnSunLongitudeDelta(jde, degree, false) + currentDelta := saturnSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -78,8 +88,8 @@ func saturnConjunctionFull(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := saturnSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (saturnSunLongitudeDelta(prevJDE+0.000005, degree, true) - saturnSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := saturnSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (saturnSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - saturnSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -93,13 +103,15 @@ func saturnConjunctionFull(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func saturnConjunction(jde, degree float64, next uint8) float64 { +func saturnConjunction(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := SATURN_S_PERIOD / 360 - currentDelta := saturnSunLongitudeDelta(jde, degree, false) + currentDelta := saturnSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -109,8 +121,8 @@ func saturnConjunction(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := saturnSunLongitudeDeltaN(prevJDE, degree, true, saturnEventSearchN) - longitudeSlope := (saturnSunLongitudeDeltaN(prevJDE+0.000005, degree, true, saturnEventSearchN) - saturnSunLongitudeDeltaN(prevJDE-0.000005, degree, true, saturnEventSearchN)) / 0.00001 + longitudeDelta := saturnSunLongitudeDeltaN(prevJDE, degree, true, saturnEventSearchN, inst) + longitudeSlope := (saturnSunLongitudeDeltaN(prevJDE+0.000005, degree, true, saturnEventSearchN, inst) - saturnSunLongitudeDeltaN(prevJDE-0.000005, degree, true, saturnEventSearchN, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= saturnPhaseCoarseTolerance { @@ -124,8 +136,8 @@ func saturnConjunction(jde, degree float64, next uint8) float64 { converged = false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := saturnSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (saturnSunLongitudeDelta(prevJDE+0.000005, degree, true) - saturnSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := saturnSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (saturnSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - saturnSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -139,39 +151,65 @@ func saturnConjunction(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func LastSaturnConjunction(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 0, saturnConjunction) +// LastSaturnConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func NextSaturnConjunction(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 0, saturnConjunction) +// NextSaturnConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func LastSaturnOpposition(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 180, saturnConjunction) +// LastSaturnOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func NextSaturnOpposition(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 180, saturnConjunction) +// NextSaturnOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func NextSaturnEasternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 90, saturnConjunction) +// NextSaturnEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func LastSaturnEasternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 90, saturnConjunction) +// LastSaturnEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func NextSaturnWesternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 270, saturnConjunction) +// NextSaturnWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func LastSaturnWesternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 270, saturnConjunction) +// LastSaturnWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return saturnConjunction(jde, degree, next, inst) + }) } -func saturnRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool) float64 { +func saturnRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(oppositionJD) { return math.NaN() } @@ -179,103 +217,107 @@ func saturnRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOppositi startTT := oppositionTT endTT := oppositionTT if searchBeforeOpposition { - easternQuadratureUT := saturnConjunction(oppositionTT, 90, 0) + easternQuadratureUT := saturnConjunction(oppositionTT, 90, 0, inst) startTT = UTC2TT(easternQuadratureUT) } else { - westernQuadratureUT := saturnConjunction(oppositionTT, 270, 1) + westernQuadratureUT := saturnConjunction(oppositionTT, 270, 1, inst) endTT = UTC2TT(westernQuadratureUT) } bestJDE := zeroEventInWindow(startTT, endTT, 2.0, 2.0, 30.0/86400.0, func(jd float64) float64 { - return saturnRADerivativeN(jd, stationDerivativeStepDay, saturnEventSearchN) + return saturnRADerivativeN(jd, stationDerivativeStepDay, saturnEventSearchN, inst) }, func(jd float64) float64 { - return saturnRADerivative(jd, stationDerivativeStepDay) + return saturnRADerivative(jd, stationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func NextSaturnRetrogradeToPrograde(jde float64) float64 { +// NextSaturnRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := saturnConjunctionFull(jde, 180, 0) - date := saturnRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := saturnConjunctionFull(jde, 180, 0, inst) + date := saturnRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - nextOppositionJD := saturnConjunctionFull(jde, 180, 1) + nextOppositionJD := saturnConjunctionFull(jde, 180, 1, inst) if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - date = saturnRetrogradeAroundOpposition(nextOppositionJD, false) + date = saturnRetrogradeAroundOpposition(nextOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastSaturnRetrogradeToPrograde(jde float64) float64 { +// LastSaturnRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := saturnConjunctionFull(jde, 180, 0) - date := saturnRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := saturnConjunctionFull(jde, 180, 0, inst) + date := saturnRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - previousOppositionJD := saturnConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0) + previousOppositionJD := saturnConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0, inst) if !isFiniteFloat(previousOppositionJD) { return math.NaN() } - date = saturnRetrogradeAroundOpposition(previousOppositionJD, false) + date = saturnRetrogradeAroundOpposition(previousOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } return date } -func NextSaturnProgradeToRetrograde(jde float64) float64 { +// NextSaturnProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextSaturnProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := saturnConjunctionFull(jde, 180, 1) - date := saturnRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := saturnConjunctionFull(jde, 180, 1, inst) + date := saturnRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - followingOppositionJD := saturnConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1) + followingOppositionJD := saturnConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1, inst) if !isFiniteFloat(followingOppositionJD) { return math.NaN() } - date = saturnRetrogradeAroundOpposition(followingOppositionJD, true) + date = saturnRetrogradeAroundOpposition(followingOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastSaturnProgradeToRetrograde(jde float64) float64 { +// LastSaturnProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastSaturnProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := saturnConjunctionFull(jde, 180, 1) - date := saturnRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := saturnConjunctionFull(jde, 180, 1, inst) + date := saturnRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - lastOppositionJD := saturnConjunctionFull(jde, 180, 0) + lastOppositionJD := saturnConjunctionFull(jde, 180, 0, inst) if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - date = saturnRetrogradeAroundOpposition(lastOppositionJD, true) + date = saturnRetrogradeAroundOpposition(lastOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } diff --git a/basic/saturn_ring.go b/basic/saturn_ring.go index 24de300..2ee0597 100644 --- a/basic/saturn_ring.go +++ b/basic/saturn_ring.go @@ -12,7 +12,7 @@ func SaturnRingParameters(jde float64) (earthLatitude, sunLatitude, positionAngl return SaturnRingParametersN(jde, -1) } -// SaturnRingParametersN 土星环参数(截断版) / truncated Saturn ring parameters. +// SaturnRingParametersN 土星环参数(n 变体) / n-parameterised Saturn ring parameters. func SaturnRingParametersN(jde float64, n int) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) { inclination, node := saturnRingPlane(jde) earthLon, earthLat := SaturnApparentLoBoN(jde, n) @@ -36,7 +36,7 @@ func SaturnRingB(jde float64) float64 { return SaturnRingBN(jde, -1) } -// SaturnRingBN 土星环张角 B(截断版) / truncated Saturn ring opening angle B. +// SaturnRingBN 土星环张角 B(n 变体) / n-parameterised Saturn ring opening angle B. func SaturnRingBN(jde float64, n int) float64 { earthLatitude, _, _, _, _, _ := SaturnRingParametersN(jde, n) return earthLatitude @@ -47,7 +47,7 @@ func SaturnRingSunB(jde float64) float64 { return SaturnRingSunBN(jde, -1) } -// SaturnRingSunBN 土星环太阳侧张角 B'(截断版) / truncated Sun-side Saturn ring opening angle B'. +// SaturnRingSunBN 土星环太阳侧张角 B'(n 变体) / n-parameterised Sun-side Saturn ring opening angle B'. func SaturnRingSunBN(jde float64, n int) float64 { _, sunLatitude, _, _, _, _ := SaturnRingParametersN(jde, n) return sunLatitude @@ -58,7 +58,7 @@ func SaturnRingPositionAngle(jde float64) float64 { return SaturnRingPositionAngleN(jde, -1) } -// SaturnRingPositionAngleN 土星环北半短轴位置角(截断版) / truncated position angle of Saturn ring northern semiminor axis. +// SaturnRingPositionAngleN 土星环北半短轴位置角(n 变体) / n-parameterised position angle of Saturn ring northern semiminor axis. func SaturnRingPositionAngleN(jde float64, n int) float64 { _, _, positionAngle, _, _, _ := SaturnRingParametersN(jde, n) return positionAngle @@ -69,7 +69,7 @@ func SaturnRingDeltaU(jde float64) float64 { return SaturnRingDeltaUN(jde, -1) } -// SaturnRingDeltaUN 太阳和地球在环面内的土星心黄经差(截断版) / truncated Saturnicentric ring longitude difference. +// SaturnRingDeltaUN 太阳和地球在环面内的土星心黄经差(n 变体) / n-parameterised Saturnicentric ring longitude difference. func SaturnRingDeltaUN(jde float64, n int) float64 { _, _, _, deltaU, _, _ := SaturnRingParametersN(jde, n) return deltaU @@ -80,7 +80,7 @@ func SaturnRingAxis(jde float64) (majorAxis, minorAxis float64) { return SaturnRingAxisN(jde, -1) } -// SaturnRingAxisN 土星环外缘长短轴(截断版),单位角秒 / truncated outer ring axes in arcseconds. +// SaturnRingAxisN 土星环外缘长短轴(n 变体),单位角秒 / n-parameterised outer ring axes in arcseconds. func SaturnRingAxisN(jde float64, n int) (majorAxis, minorAxis float64) { _, _, _, _, majorAxis, minorAxis = SaturnRingParametersN(jde, n) return diff --git a/basic/saturn_ring_ephemeris_instance.go b/basic/saturn_ring_ephemeris_instance.go new file mode 100644 index 0000000..594064e --- /dev/null +++ b/basic/saturn_ring_ephemeris_instance.go @@ -0,0 +1,110 @@ +package basic + +import ( + "math" + + . "b612.me/astro/tools" +) + +func saturnRingSunLoBoWithInstance(jde float64, n int, node float64, inst *Instance) (lon, lat float64) { + lon = PlanetCoordinateNWithInstance(5, 0, jde, n, inst) + lat = PlanetCoordinateNWithInstance(5, 1, jde, n, inst) + distance := PlanetCoordinateNWithInstance(5, 2, jde, n, inst) + lat -= 0.000764 * Cos(lon-node) / distance + lon -= 0.01759 / distance + return lon, lat +} + +func saturnRingParametersNWithInstance(jde float64, n int, inst *Instance) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) { + inclination, node := saturnRingPlane(jde) + earthLon, earthLat := SaturnApparentLoBoNWithInstance(jde, n, inst) + sunLon, sunLat := saturnRingSunLoBoWithInstance(jde, n, node, inst) + earthLatitude = saturnRingLatitude(inclination, node, earthLon, earthLat) + sunLatitude = saturnRingLatitude(inclination, node, sunLon, sunLat) + positionAngle = saturnRingPositionAngle(jde, inclination, node, earthLon, earthLat) + earthU := saturnRingLongitude(inclination, node, earthLon, earthLat) + sunU := saturnRingLongitude(inclination, node, sunLon, sunLat) + deltaU = saturnRingLongitudeDelta(earthU, sunU) + earthDistance := EarthSaturnAwayNWithInstance(jde, n, inst) + majorAxis = 375.35 / earthDistance + minorAxis = majorAxis * math.Abs(Sin(earthLatitude)) + return +} + +func saturnRingParametersWithInstance(jde float64, inst *Instance) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) { + return saturnRingParametersNWithInstance(jde, -1, inst) +} + +// SaturnRingParametersWithInstance 土星环参数 / Saturn ring parameters with an explicit ephemeris instance. +func SaturnRingParametersWithInstance(jde float64, inst *Instance) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) { + if inst == nil { + return SaturnRingParameters(jde) + } + return saturnRingParametersWithInstance(jde, inst) +} + +// SaturnRingParametersNWithInstance 土星环参数(n 变体) / n-parameterised Saturn ring parameters with an explicit ephemeris instance. +func SaturnRingParametersNWithInstance(jde float64, n int, inst *Instance) (earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis float64) { + if inst == nil { + return SaturnRingParametersN(jde, n) + } + return saturnRingParametersNWithInstance(jde, n, inst) +} + +// SaturnRingBWithInstance 土星环张角 / Saturn ring opening angle with an explicit ephemeris instance. +func SaturnRingBWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return SaturnRingB(jde) + } + earthLatitude, _, _, _, _, _ := saturnRingParametersWithInstance(jde, inst) + return earthLatitude +} + +// SaturnRingSunBWithInstance 土星环太阳侧张角 / Sun-side Saturn ring opening angle with an explicit ephemeris instance. +func SaturnRingSunBWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return SaturnRingSunB(jde) + } + _, sunLatitude, _, _, _, _ := saturnRingParametersWithInstance(jde, inst) + return sunLatitude +} + +// SaturnRingPositionAngleWithInstance 土星环位置角 / Saturn ring position angle with an explicit ephemeris instance. +func SaturnRingPositionAngleWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return SaturnRingPositionAngle(jde) + } + _, _, positionAngle, _, _, _ := saturnRingParametersWithInstance(jde, inst) + return positionAngle +} + +// SaturnRingDeltaUWithInstance 土星环面经度差 / Saturn ring-plane longitude difference with an explicit ephemeris instance. +func SaturnRingDeltaUWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return SaturnRingDeltaU(jde) + } + _, _, _, deltaU, _, _ := saturnRingParametersWithInstance(jde, inst) + return deltaU +} + +// SaturnRingAxisWithInstance 土星环外缘轴 / outer Saturn ring axes with an explicit ephemeris instance. +func SaturnRingAxisWithInstance(jde float64, inst *Instance) (float64, float64) { + if inst == nil { + return SaturnRingAxis(jde) + } + _, _, _, _, majorAxis, minorAxis := saturnRingParametersWithInstance(jde, inst) + return majorAxis, minorAxis +} + +// SaturnMagWithInstance 土星视星等 / apparent magnitude of Saturn with an explicit ephemeris instance. +func SaturnMagWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return SaturnMag(jde) + } + awaySun := SaturnRWithInstance(jde, inst) + awayEarth := EarthSaturnAwayWithInstance(jde, inst) + ringB, _, _, deltaU, _, _ := saturnRingParametersWithInstance(jde, inst) + ringB = math.Abs(ringB) + mag := -8.68 + 5*math.Log10(awaySun*awayEarth) + 0.044*deltaU - 2.6*Sin(ringB) + 1.25*Sin(ringB)*Sin(ringB) + return FloatRound(mag, 2) +} diff --git a/basic/saturn_ring_test.go b/basic/saturn_ring_test.go index b0a6a0f..1a98466 100644 --- a/basic/saturn_ring_test.go +++ b/basic/saturn_ring_test.go @@ -1,8 +1,11 @@ package basic import ( + "fmt" "math" "testing" + + "b612.me/astro/internal/ephem" ) func TestSaturnRingParametersMeeusExample(t *testing.T) { @@ -34,6 +37,53 @@ func TestSaturnRingNFullMatchesDefault(t *testing.T) { assertSameFloat(t, "SaturnRingDeltaU", SaturnRingDeltaU(jd), SaturnRingDeltaUN(jd, -1)) } +func TestSaturnRingParametersNWithInstanceForwardsN(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + jd := 2458850.5 + names := []string{"EarthLatitude", "SunLatitude", "PositionAngle", "DeltaU", "MajorAxis", "MinorAxis"} + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + ephem.SetActive(model) + inst := ephem.NewModelInstance(model) + for _, n := range []int{-1, 0} { + expectedEarthLatitude, expectedSunLatitude, expectedPositionAngle, expectedDeltaU, expectedMajorAxis, expectedMinorAxis := SaturnRingParametersN(jd, n) + gotEarthLatitude, gotSunLatitude, gotPositionAngle, gotDeltaU, gotMajorAxis, gotMinorAxis := SaturnRingParametersNWithInstance(jd, n, inst) + got := []float64{gotEarthLatitude, gotSunLatitude, gotPositionAngle, gotDeltaU, gotMajorAxis, gotMinorAxis} + expected := []float64{expectedEarthLatitude, expectedSunLatitude, expectedPositionAngle, expectedDeltaU, expectedMajorAxis, expectedMinorAxis} + for index := range names { + assertClose(t, fmt.Sprintf("model=%d n=%d %s", model, n, names[index]), got[index], expected[index], 1e-10) + } + } + } +} + +func TestSaturnRingParametersNWithInstanceIsolatedFromGlobalModel(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + jd := 2458850.5 + names := []string{"EarthLatitude", "SunLatitude", "PositionAngle", "DeltaU", "MajorAxis", "MinorAxis"} + for _, testCase := range []struct { + globalModel ephem.Model + instanceModel ephem.Model + }{ + {globalModel: ephem.Legacy, instanceModel: ephem.Semianalytic}, + {globalModel: ephem.Semianalytic, instanceModel: ephem.Legacy}, + } { + inst := ephem.NewModelInstance(testCase.instanceModel) + for _, n := range []int{-1, 0} { + ephem.SetActive(testCase.instanceModel) + expectedEarthLatitude, expectedSunLatitude, expectedPositionAngle, expectedDeltaU, expectedMajorAxis, expectedMinorAxis := SaturnRingParametersN(jd, n) + ephem.SetActive(testCase.globalModel) + gotEarthLatitude, gotSunLatitude, gotPositionAngle, gotDeltaU, gotMajorAxis, gotMinorAxis := SaturnRingParametersNWithInstance(jd, n, inst) + got := []float64{gotEarthLatitude, gotSunLatitude, gotPositionAngle, gotDeltaU, gotMajorAxis, gotMinorAxis} + expected := []float64{expectedEarthLatitude, expectedSunLatitude, expectedPositionAngle, expectedDeltaU, expectedMajorAxis, expectedMinorAxis} + for index := range names { + assertClose(t, fmt.Sprintf("global=%d instance=%d n=%d %s", testCase.globalModel, testCase.instanceModel, n, names[index]), got[index], expected[index], 1e-10) + } + } + } +} + func assertClose(t *testing.T, name string, got, want, tolerance float64) { t.Helper() if math.Abs(got-want) > tolerance { diff --git a/basic/sky_events.go b/basic/sky_events.go new file mode 100644 index 0000000..a5d6c79 --- /dev/null +++ b/basic/sky_events.go @@ -0,0 +1,269 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/geodata" +) + +// SkyBody 通用天象搜索支持的天体 / body for generic sky event searches. +type SkyBody int + +const ( + // SkySun 太阳 / the Sun. + SkySun SkyBody = iota + // SkyMoon 月球 / the Moon. + SkyMoon + // SkyMercury 水星 / Mercury. + SkyMercury + // SkyVenus 金星 / Venus. + SkyVenus + // SkyMars 火星 / Mars. + SkyMars + // SkyJupiter 木星 / Jupiter. + SkyJupiter + // SkySaturn 土星 / Saturn. + SkySaturn + // SkyUranus 天王星 / Uranus. + SkyUranus + // SkyNeptune 海王星 / Neptune. + SkyNeptune +) + +// SkyEvent 通用天象事件:TT 儒略日与事件时的角量(度)/ generic sky event at TT JDE with its angle in degrees. +type SkyEvent struct { + JDE float64 + Value float64 +} + +func (body SkyBody) valid() bool { return body >= SkySun && body <= SkyNeptune } + +// skyScanStep 保证相邻采样间视黄经变化远小于 180°,逆行回转也不会漏根。 +func skyScanStep(bodies ...SkyBody) float64 { + step := 2.0 + for _, b := range bodies { + switch b { + case SkyMoon: + step = math.Min(step, 0.25) + case SkyMercury, SkyVenus, SkySun: + step = math.Min(step, 1.0) + } + } + return step +} + +// SkyBodyApparentLoBoWithInstance 地心视黄经、视黄纬(度)/ geocentric apparent ecliptic longitude and latitude. +func SkyBodyApparentLoBoWithInstance(body SkyBody, jde float64, inst *Instance) (float64, float64) { + switch body { + case SkySun: + return sunApparentLoBoN(jde, -1, inst) + case SkyMoon: + return moonApparentLoBoN(jde, -1, inst) + case SkyMercury, SkyVenus, SkyMars, SkyJupiter, SkySaturn, SkyUranus, SkyNeptune: + return planetApparentLoBoWithInstance(int(body-SkyMercury)+1, jde, inst) + } + return math.NaN(), math.NaN() +} + +// skyGridMaxIntervals 网格段数上限;须能放进 32 位 int,超出时扫描本身也不可能完成。 +const skyGridMaxIntervals = 1 << 30 + +// skyGrid 把 [start, end] 均分为至少 minIntervals 段、每段不超过 step;按下标取点,浮点下必然前进且末点恰为 end。 +func skyGrid(start, end, step float64, minIntervals int) (count int, at func(int) float64, ok bool) { + span := end - start + if !finite(start) || !finite(end) || !(span > 0) || !(step > 0) { + return 0, nil, false + } + ratio := math.Ceil(span / step) + if !(ratio < skyGridMaxIntervals) { + return 0, nil, false + } + count = minIntervals + if ratio > float64(count) { + count = int(ratio) + } + return count, func(index int) float64 { + if index == count { + return end + } + return start + span*float64(index)/float64(count) + }, true +} + +// skyRoots 在闭区间 [start, end] 内按 step 扫描 f 的零点并二分到约 0.1 秒;f 须连续(已处理 360° 回绕)。 +func skyRoots(start, end, step float64, f func(float64) float64) []float64 { + count, at, ok := skyGrid(start, end, step, 1) + if !ok { + return nil + } + var roots []float64 + prevT, prev := start, f(start) + if prev == 0 { + roots = append(roots, start) + } + for index := 1; index <= count; index++ { + t := at(index) + cur := f(t) + switch { + case cur == 0: + roots = append(roots, t) + // 回绕跳变时 |差| 接近 360,不是真根;prev 为 0 的根已在上一点记过。 + case prev != 0 && !math.IsNaN(prev) && !math.IsNaN(cur) && (prev < 0) != (cur < 0) && math.Abs(cur-prev) < 180: + lo, hi, flo := prevT, t, prev + // 迭代上限防止大 JDE 下浮点间隔大于容差时不收敛。 + for i := 0; i < 100 && hi-lo > 1e-6; i++ { + mid := (lo + hi) / 2 + fm := f(mid) + if fm == 0 { + lo, hi = mid, mid + break + } + if (fm < 0) == (flo < 0) { + lo, flo = mid, fm + } else { + hi = mid + } + } + roots = append(roots, (lo+hi)/2) + } + prevT, prev = t, cur + } + return roots +} + +// SkyLongitudeCrossingsWithInstance body 视黄经经过 longitude 的全部时刻 / times when the apparent longitude equals longitude. +func SkyLongitudeCrossingsWithInstance(body SkyBody, longitude, startJDE, endJDE float64, inst *Instance) []SkyEvent { + if !body.valid() || !(endJDE > startJDE) { + return nil + } + f := func(t float64) float64 { + lo, _ := SkyBodyApparentLoBoWithInstance(body, t, inst) + return geodata.NormalizeLongitude(lo - longitude) + } + var out []SkyEvent + for _, t := range skyRoots(startJDE, endJDE, skyScanStep(body), f) { + out = append(out, SkyEvent{JDE: t, Value: longitude}) + } + return out +} + +// SkyStationsWithInstance 视黄经留(速度过零)的时刻,Value 为留时视黄经 / longitude stations; Value is the longitude. +func SkyStationsWithInstance(body SkyBody, startJDE, endJDE float64, inst *Instance) []SkyEvent { + if !body.valid() || body == SkySun || body == SkyMoon || !(endJDE > startJDE) { + return nil + } + const h = 0.05 + rate := func(t float64) float64 { + a, _ := SkyBodyApparentLoBoWithInstance(body, t-h, inst) + b, _ := SkyBodyApparentLoBoWithInstance(body, t+h, inst) + return geodata.NormalizeLongitude(b - a) + } + var out []SkyEvent + for _, t := range skyRoots(startJDE, endJDE, skyScanStep(body), rate) { + lo, _ := SkyBodyApparentLoBoWithInstance(body, t, inst) + out = append(out, SkyEvent{JDE: t, Value: lo}) + } + return out +} + +// SkyAspectCrossingsWithInstance a 与 b 视黄经差(a − b)等于 angle 的时刻;angle 0 为黄经合 / times when lon(a) − lon(b) equals angle. +func SkyAspectCrossingsWithInstance(a, b SkyBody, angle, startJDE, endJDE float64, inst *Instance) []SkyEvent { + if !a.valid() || !b.valid() || a == b || !(endJDE > startJDE) { + return nil + } + f := func(t float64) float64 { + la, _ := SkyBodyApparentLoBoWithInstance(a, t, inst) + lb, _ := SkyBodyApparentLoBoWithInstance(b, t, inst) + return geodata.NormalizeLongitude(la - lb - angle) + } + var out []SkyEvent + for _, t := range skyRoots(startJDE, endJDE, skyScanStep(a, b), f) { + out = append(out, SkyEvent{JDE: t, Value: angle}) + } + return out +} + +func skyAngularDistance(lon1, lat1, lon2, lat2 float64) float64 { + v1, v2 := sphericalUnit(lon1, lat1), sphericalUnit(lon2, lat2) + cross := [3]float64{v1[1]*v2[2] - v1[2]*v2[1], v1[2]*v2[0] - v1[0]*v2[2], v1[0]*v2[1] - v1[1]*v2[0]} + return math.Atan2(math.Sqrt(cross[0]*cross[0]+cross[1]*cross[1]+cross[2]*cross[2]), v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) / rad +} + +// skyGoldenMin 黄金分割求 [lo, hi] 内单峰 f 的极小点。 +func skyGoldenMin(lo, hi float64, f func(float64) float64) (float64, float64) { + const g = 0.6180339887498949 + x1, x2 := hi-g*(hi-lo), lo+g*(hi-lo) + fx1, fx2 := f(x1), f(x2) + for i := 0; i < 200 && hi-lo > 1e-6; i++ { + if fx1 < fx2 { + hi, x2, fx2 = x2, x1, fx1 + x1 = hi - g*(hi-lo) + fx1 = f(x1) + } else { + lo, x1, fx1 = x1, x2, fx2 + x2 = lo + g*(hi-lo) + fx2 = f(x2) + } + } + t := (lo + hi) / 2 + return t, f(t) +} + +// skyMinima 扫描 f 在 [start, end] 内的局部极小并用黄金分割细化;只保留 ≤ maxValue 的极小。 +func skyMinima(start, end, step, maxValue float64, f func(float64) float64) []SkyEvent { + count, at, ok := skyGrid(start, end, step, 2) + if !ok { + return nil + } + var out []SkyEvent + add := func(lo, hi, boundary float64) { + // 端点段细化值须低于边界采样值,否则极小在窗口外,不是窗口内的局部极小。 + if t, v := skyGoldenMin(lo, hi, f); v <= maxValue && v < boundary { + out = append(out, SkyEvent{JDE: t, Value: v}) + } + } + t0, t1 := at(0), at(1) + f0, f1 := f(t0), f(t1) + if f0 < f1 { + add(t0, t1, f0) + } + for index := 2; index <= count; index++ { + t2 := at(index) + f2 := f(t2) + if f1 <= f0 && f1 < f2 { + add(t0, t2, math.Inf(1)) + } + t0, t1, f0, f1 = t1, t2, f1, f2 + } + if f1 < f0 { + add(t0, t1, f1) + } + return out +} + +// SkySeparationMinimaWithInstance a、b 地心视角距的局部极小(度),只返回 ≤ maxSeparation 的 / closest approaches within maxSeparation degrees. +func SkySeparationMinimaWithInstance(a, b SkyBody, maxSeparation, startJDE, endJDE float64, inst *Instance) []SkyEvent { + if !a.valid() || !b.valid() || a == b || !(endJDE > startJDE) { + return nil + } + f := func(t float64) float64 { + la, ba := SkyBodyApparentLoBoWithInstance(a, t, inst) + lb, bb := SkyBodyApparentLoBoWithInstance(b, t, inst) + return skyAngularDistance(la, ba, lb, bb) + } + return skyMinima(startJDE, endJDE, skyScanStep(a, b)/2, maxSeparation, f) +} + +// SkyStarSeparationMinimaWithInstance 天体与恒星地心视角距的局部极小(度)/ closest approaches of a body to a star within maxSeparation degrees. +func SkyStarSeparationMinimaWithInstance(body SkyBody, star StarCoordinate, maxSeparation, startJDE, endJDE float64, inst *Instance) []SkyEvent { + if !body.valid() || !(endJDE > startJDE) { + return nil + } + f := func(t float64) float64 { + ra, dec := starApparentRaDecGeocentric(t, star, inst) + sl, sb := RaDecToLoBo(t, ra, dec) + lo, bo := SkyBodyApparentLoBoWithInstance(body, t, inst) + return skyAngularDistance(lo, bo, sl, sb) + } + return skyMinima(startJDE, endJDE, skyScanStep(body)/2, maxSeparation, f) +} diff --git a/basic/sky_events_test.go b/basic/sky_events_test.go new file mode 100644 index 0000000..3e8fecb --- /dev/null +++ b/basic/sky_events_test.go @@ -0,0 +1,140 @@ +// 被测契约:通用天象搜索与各行星专用事件一致,并能找到大合与行星近星。 +package basic + +import ( + "math" + "testing" + "time" + + "b612.me/astro/internal/geodata" +) + +func TestSkyAspectMatchesMarsOpposition(t *testing.T) { + start := 2460000.5 + // 专用函数返回 UT。 + want := UTC2TT(NextMarsOppositionWithInstance(start, nil)) + got := SkyAspectCrossingsWithInstance(SkyMars, SkySun, 180, start, start+800, nil) + if len(got) == 0 || math.Abs(got[0].JDE-want)*86400 > 1 { + t.Fatalf("opposition %v, want %.6f", got, want) + } +} + +// 专用函数按赤经留且返回 UT,通用搜索按黄经留,二者相差数小时属口径不同;这里只核对黄经速度确实变号。 +func TestSkyStationsFlipLongitudeRate(t *testing.T) { + got := SkyStationsWithInstance(SkyMars, 2460000.5, 2460800.5, nil) + if len(got) != 2 { + t.Fatalf("stations %v", got) + } + for _, e := range got { + before, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE-1, nil) + at, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE, nil) + after, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE+1, nil) + if geodata.NormalizeLongitude(at-before)*geodata.NormalizeLongitude(after-at) >= 0 { + t.Errorf("no rate flip at %.5f", e.JDE) + } + ut := UTC2TT(NextMarsProgradeToRetrogradeWithInstance(e.JDE-30, nil)) + if math.Abs(ut-e.JDE) > 5 && math.Abs(UTC2TT(NextMarsRetrogradeToProgradeWithInstance(e.JDE-30, nil))-e.JDE) > 5 { + t.Errorf("station %.5f far from RA stations", e.JDE) + } + } +} + +func TestSkyGreatConjunction2020(t *testing.T) { + got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459100.5, 2459300.5, nil) + if len(got) != 1 { + t.Fatalf("minima %v", got) + } + when := JD2Date(TT2UTC(got[0].JDE)) + if when.Year() != 2020 || when.Month() != time.December || when.Day() < 21 || when.Day() > 22 || got[0].Value > 0.11 || got[0].Value < 0.09 { + t.Errorf("great conjunction at %v separation %.4f°", when, got[0].Value) + } +} + +func TestSkyStarApproachRegulus(t *testing.T) { + regulus := StarCoordinate{ID: "Regulus", RA: 152.09296, Dec: 11.96721, Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000} + got := SkyStarSeparationMinimaWithInstance(SkyVenus, regulus, 1.5, 2460400.5, 2460600.5, nil) + if len(got) == 0 { + t.Fatal("no Venus–Regulus approach") + } + for _, e := range got { + if e.Value > 1.5 || e.Value < 0 { + t.Errorf("approach %.4f° at %.5f", e.Value, e.JDE) + } + } +} + +func TestSkySeparationMinimaFindsNarrowWindow(t *testing.T) { + wide := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459190, 2459220, nil) + if len(wide) == 0 { + t.Fatal("wide window returned no minimum") + } + center := wide[0].JDE + for _, halfWidth := range []float64{0.1, 0.25, 0.49, 0.99, 1.5, 3} { + got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, center-halfWidth, center+halfWidth, nil) + if len(got) == 0 { + t.Errorf("halfWidth %v: no minimum", halfWidth) + continue + } + if math.Abs(got[0].JDE-center) > 1e-4 { + t.Errorf("halfWidth %v: JDE = %.6f, want %.6f", halfWidth, got[0].JDE, center) + } + } +} + +func TestSkySeparationMinimaFindsEdgeOfWindow(t *testing.T) { + wide := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459190, 2459220, nil) + if len(wide) == 0 { + t.Fatal("wide window returned no minimum") + } + center := wide[0].JDE + windows := [][2]float64{ + {center - 1.0/1440, center + 3.0/24}, + {center - 3.0/24, center + 1.0/1440}, + {center - 1.0/24, center + 1.0/24}, + } + for _, window := range windows { + got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, window[0], window[1], nil) + if len(got) != 1 || math.Abs(got[0].JDE-center) > 1e-4 { + t.Errorf("window [%+.4f, %+.4f]: got %+v, want one minimum at %.6f", window[0]-center, window[1]-center, got, center) + } + } + if got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, center+0.5, center+3, nil); len(got) != 0 { + t.Errorf("window after the minimum: got %+v, want none", got) + } +} + +func TestSkyScansTerminateOnDegenerateWindows(t *testing.T) { + start := 2459205.26539 + windows := [][2]float64{ + {start, math.Nextafter(start, math.Inf(1))}, + {start, start + 26e-6/86400}, + {start, math.Inf(1)}, + {math.Inf(-1), start}, + {math.NaN(), start}, + } + for _, window := range windows { + SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, window[0], window[1], nil) + SkyLongitudeCrossingsWithInstance(SkySun, 280, window[0], window[1], nil) + SkyAspectCrossingsWithInstance(SkyJupiter, SkySaturn, 0, window[0], window[1], nil) + } +} + +func TestSkyGridRejectsLimitInclusive(t *testing.T) { + if _, _, ok := skyGrid(0, float64(skyGridMaxIntervals), 1, 1); ok { + t.Error("grid at limit accepted") + } + if count, _, ok := skyGrid(0, float64(skyGridMaxIntervals-1), 1, 1); !ok || count != skyGridMaxIntervals-1 { + t.Errorf("grid below limit: count=%d ok=%v", count, ok) + } +} + +func TestSkyLongitudeCrossingsIncludesClosedEndpoints(t *testing.T) { + const jde = 2451545.0 + lo, _ := SkyBodyApparentLoBoWithInstance(SkySun, jde, nil) + for _, window := range [][2]float64{{jde - 2, jde}, {jde, jde + 2}} { + got := SkyLongitudeCrossingsWithInstance(SkySun, lo, window[0], window[1], nil) + if len(got) != 1 || math.Abs(got[0].JDE-jde) > 1e-5 { + t.Errorf("window [%v, %v]: got %+v, want one crossing at %v", window[0], window[1], got, jde) + } + } +} diff --git a/basic/solar_eclipse.go b/basic/solar_eclipse.go index f3d688b..a9ca965 100644 --- a/basic/solar_eclipse.go +++ b/basic/solar_eclipse.go @@ -1,6 +1,11 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/geodata" +) // SolarEclipseRadiusModel 表示日食计算中月亮平均半径 k 的取法。 type SolarEclipseRadiusModel string @@ -28,6 +33,8 @@ type SolarEclipseOptions struct { RadiusModel SolarEclipseRadiusModel // SunRadiusModel 太阳半径口径,零值为标准档 / solar radius convention. SunRadiusModel SolarEclipseSunRadiusModel + // LunarLimb 月缘剖面,nil 为圆月面;修正站心接触、食甚时的站心食分与遮蔽率,以及全球偏食始末与食甚点中心食时长 / lunar limb profile for local contacts, magnitude and obscuration, global P1/P4 and central duration. + LunarLimb LunarLimb } // SolarEclipseType 整场日食的全局食型。 @@ -67,8 +74,14 @@ type SolarEclipseResult struct { // The fields below are the conventions that fix the numbers above. Model SolarEclipseRadiusModel SunRadiusModel SolarEclipseSunRadiusModel - Type SolarEclipseType - Centrality SolarEclipseCentrality + // LunarLimbApplied 表示接触或食甚点中心食时长已按月缘剖面修正 / reports whether lunar-limb refinement changed the result. + LunarLimbApplied bool + // PartialBeginContact / PartialEndContact 是月缘修正后的偏食始末接触点位置,未修正时 Defined 为 false。 + // The limb-corrected locations of the global partial contacts; Defined is false without a correction. + PartialBeginContact SolarEclipseLimbContact + PartialEndContact SolarEclipseLimbContact + Type SolarEclipseType + Centrality SolarEclipseCentrality // GreatestEclipse 是全局“影轴最接近地心”的时刻。 GreatestEclipse float64 @@ -116,6 +129,8 @@ type solarEclipseModelParameters struct { penumbralK float64 umbralK float64 sunRadiusRatio float64 + // lunarLimb 非 nil 时站心接触改用月缘剖面的方向半径。 + lunarLimb LunarLimb } type solarEclipseShadowRadii struct { @@ -132,6 +147,8 @@ type solarEclipseAxis struct { } type solarEclipseSolver struct { + inst *Instance + newMoonJDE float64 model SolarEclipseRadiusModel sunRadiusModel SolarEclipseSunRadiusModel @@ -139,6 +156,8 @@ type solarEclipseSolver struct { localStateContextCache map[uint64]localSolarEclipseStateContext localEphemeris *solarEclipseLocalEphemeris + // limbAxes 与 localEphemeris 同网格的月球本体坐标架,仅在设置月缘时构造。 + limbAxes *solarEclipseLimbAxes // deltaTSeconds 是调用方显式给出的 ΔT(秒);NaN 表示未覆盖,用进程级模型。 // 只影响地球自转相位(轴的 gst),不改变任何 TT 时刻。 deltaTSeconds float64 @@ -194,7 +213,6 @@ type solarEclipseLineIntersection struct { } const ( - solarEclipseEarthEquatorialRadiusKM = 6378.1366 // 赤道自转线速度,用于把 ΔT 误差换算成地面横移(见 DeltaTGroundShiftKM)。 // Equatorial rotation speed, used to convert a ΔT error into ground displacement. solarEclipseEarthEquatorialRotationKMPerSecond = 0.4651 @@ -255,9 +273,9 @@ func solarEclipseSunRadiusRatio(model SolarEclipseSunRadiusModel) float64 { return solarEclipseSunRadiusRatioStandard } -// SolarEclipseSunSemidiameter 指定太阳半径口径下的视半径,单位角秒 / apparent solar semidiameter in arcseconds under a given eclipse sun radius convention. -func SolarEclipseSunSemidiameter(jde float64, model SolarEclipseSunRadiusModel) float64 { - return angularSemidiameterFromAU(solarEclipseSunRadiusRatio(model)*solarEclipseEarthEquatorialRadiusKM, EarthAwayN(jde, -1)) +// SolarEclipseSunSemidiameterWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseSunSemidiameterWithInstance(jde float64, model SolarEclipseSunRadiusModel, inst *Instance) float64 { + return angularSemidiameterFromAU(solarEclipseSunRadiusRatio(model)*geodata.EarthEquatorialRadiusKM, EarthAwayNWithInstance(jde, -1, inst)) } // SolarEclipse 计算给定近朔时刻附近的一次全局日食,默认使用 NASABulletin Split-K 模型与标准太阳半径。 @@ -265,32 +283,42 @@ func SolarEclipse(seedJDE float64) SolarEclipseResult { return SolarEclipseNASABulletinSplitK(seedJDE) } -// SolarEclipseWithOptions 计算给定近朔时刻附近的一次全局日食,半径口径由 options 指定 / computes one global solar eclipse with the given radius conventions. -func SolarEclipseWithOptions(seedJDE float64, options SolarEclipseOptions) SolarEclipseResult { - return solarEclipseWithDeltaT(seedJDE, options, 0) +// SolarEclipseWithOptionsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseWithOptionsWithInstance(seedJDE float64, options SolarEclipseOptions, inst *Instance) SolarEclipseResult { + if options.LunarLimb == nil { + return solarEclipseWithDeltaT(seedJDE, options, 0, inst) + } + limb := options.LunarLimb + options.LunarLimb = nil + newMoonJDE := solarEclipseAnchorNewMoon(seedJDE, inst) + solver := newSolarEclipseSolverWithOptions(newMoonJDE, options, inst) + return solver.withLunarLimb(solver.eclipseResult(), limb) } -// SolarEclipseIAUSingleK 计算给定近朔时刻附近的一次全局日食,使用 IAU Single-K 模型。 -func SolarEclipseIAUSingleK(seedJDE float64) SolarEclipseResult { - return solarEclipse(seedJDE, SolarEclipseModelIAUSingleK) +// SolarEclipseIAUSingleKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseIAUSingleKWithInstance(seedJDE float64, inst *Instance) SolarEclipseResult { + return solarEclipse(seedJDE, SolarEclipseModelIAUSingleK, inst) } -// SolarEclipseNASABulletinSplitK 计算给定近朔时刻附近的一次全局日食,使用 NASA bulletin Split-K 模型。 -func SolarEclipseNASABulletinSplitK(seedJDE float64) SolarEclipseResult { - return solarEclipse(seedJDE, SolarEclipseModelNASABulletinSplitK) +// SolarEclipseNASABulletinSplitKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseNASABulletinSplitKWithInstance(seedJDE float64, inst *Instance) SolarEclipseResult { + return solarEclipse(seedJDE, SolarEclipseModelNASABulletinSplitK, inst) } -func solarEclipse(seedJDE float64, model SolarEclipseRadiusModel) SolarEclipseResult { - return solarEclipseWithDeltaT(seedJDE, SolarEclipseOptions{RadiusModel: model}, 0) +func solarEclipse(seedJDE float64, model SolarEclipseRadiusModel, instances ...*Instance) SolarEclipseResult { + inst := instanceValue(instances) + + return solarEclipseWithDeltaT(seedJDE, SolarEclipseOptions{RadiusModel: model}, 0, inst) } func solarEclipseWithDeltaT( seedJDE float64, options SolarEclipseOptions, - deltaTSeconds float64, -) SolarEclipseResult { - newMoonJDE := CalcMoonSHByJDE(seedJDE, 0) - solver := newSolarEclipseSolverWithOptions(newMoonJDE, options).withDeltaTSeconds(deltaTSeconds) + deltaTSeconds float64, instances ...*Instance) SolarEclipseResult { + inst := instanceValue(instances) + + newMoonJDE := solarEclipseAnchorNewMoon(seedJDE, inst) + solver := newSolarEclipseSolverWithOptions(newMoonJDE, options, inst).withDeltaTSeconds(deltaTSeconds) return solver.eclipseResult() } @@ -372,19 +400,21 @@ func newSolarEclipseSolver(newMoonJDE float64, model SolarEclipseRadiusModel) so return newSolarEclipseSolverWithOptions(newMoonJDE, SolarEclipseOptions{RadiusModel: model}) } -func newSolarEclipseSolverWithOptions(newMoonJDE float64, options SolarEclipseOptions) solarEclipseSolver { +func newSolarEclipseSolverWithOptions(newMoonJDE float64, options SolarEclipseOptions, instances ...*Instance) solarEclipseSolver { + inst := instanceValue(instances) + options.RadiusModel = normalizeSolarEclipseRadiusModel(options.RadiusModel) options.SunRadiusModel = normalizeSolarEclipseSunRadiusModel(options.SunRadiusModel) params := solarEclipseModelParams(options.RadiusModel, options.SunRadiusModel) firstNodeJDE := newMoonJDE + (0-float64(solarEclipseNodeCount)/2+0.5)*solarEclipseNodeStepDays lastNodeJDE := newMoonJDE + (float64(solarEclipseNodeCount-1)-float64(solarEclipseNodeCount)/2+0.5)*solarEclipseNodeStepDays - firstSun, firstMoon := solarEclipseSunMoonEquatorial(firstNodeJDE) - lastSun, lastMoon := solarEclipseSunMoonEquatorial(lastNodeJDE) + firstSun, firstMoon := solarEclipseSunMoonEquatorial(firstNodeJDE, inst) + lastSun, lastMoon := solarEclipseSunMoonEquatorial(lastNodeJDE, inst) - meanSunMoonDistance := ((firstSun[2] + lastSun[2]) - (firstMoon[2] + lastMoon[2])) / 2 / solarEclipseEarthEquatorialRadiusKM + meanSunMoonDistance := ((firstSun[2] + lastSun[2]) - (firstMoon[2] + lastMoon[2])) / 2 / geodata.EarthEquatorialRadiusKM - return solarEclipseSolver{ + return solarEclipseSolver{inst: inst, newMoonJDE: newMoonJDE, model: options.RadiusModel, sunRadiusModel: options.SunRadiusModel, @@ -436,7 +466,10 @@ func (solver solarEclipseSolver) siderealTimeAt(jde float64) float64 { // bounded window and is used by all contact and topology refinements. func (solver solarEclipseSolver) withLocalEphemeris() solarEclipseSolver { if solver.localEphemeris == nil { - solver.localEphemeris = newSolarEclipseLocalEphemeris(solver.newMoonJDE) + solver.localEphemeris = newSolarEclipseLocalEphemeris(solver.newMoonJDE, solver.inst) + } + if solver.limbAxes == nil && solver.params.lunarLimb != nil { + solver.limbAxes = newSolarEclipseLimbAxes(solver.localEphemeris, solver.params.lunarLimb, solver.inst) } return solver } @@ -561,9 +594,9 @@ func (solver solarEclipseSolver) feature() solarEclipseFeature { // 单侧极限(A1/T1)只有一侧限界,非中心中心食(A0/T0)连限界都没有:解析式 2r/|sin h| // 在 h→0 时发散,两类事件该栏都无定义,与中心线逐点宽度一起置 0。 if solarEclipseTwoLimitsTypeCode(typeCode) { - sunAltitude := solarEclipseSunAltitudeAtGreatest(greatestEclipseJDE, greatestLongitude, greatestLatitude, axis.gst) + sunAltitude := solarEclipseSunAltitudeAtGreatest(greatestEclipseJDE, greatestLongitude, greatestLatitude, axis.gst, solver.inst) if math.Abs(math.Sin(sunAltitude)) > 1e-12 { - pathWidthKM = math.Abs(2*greatestRadii.umbraRadius*solarEclipseEarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitude)) + pathWidthKM = math.Abs(2*greatestRadii.umbraRadius*geodata.EarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitude)) } } } @@ -693,7 +726,7 @@ func (solver solarEclipseSolver) centralAxisContactPointAt(jde float64) (SolarEc JDE: jde, Longitude: longitude, Latitude: latitude, - SunAltitude: solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst) / rad, + SunAltitude: solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst) / rad, }, true } @@ -740,7 +773,7 @@ func (solver solarEclipseSolver) shadowRadiiAt(moonBesselZ float64) solarEclipse } func (solver solarEclipseSolver) besselAxisAt(jde float64) solarEclipseAxis { - sun, moon := solarEclipseSunMoonEquatorial(jde) + sun, moon := solarEclipseSunMoonEquatorial(jde, solver.inst) return solarEclipseBesselAxisFromEquatorialWithDeltaT( jde, sun, moon, solver.effectiveDeltaTSeconds(jde), ) @@ -766,7 +799,7 @@ func solarEclipseBesselAxisFromEquatorialWithDeltaT( return solarEclipseAxis{ rightAscension: solarEclipseNormalizeRadians(math.Pi/2 + axis[0]), tilt: math.Pi/2 - axis[1], - gst: solarEclipseNormalizeSignedRadians(ApparentSiderealTime(utJDE) * 15 * rad), + gst: normalizeSignedRadians(ApparentSiderealTime(utJDE) * 15 * rad), } } @@ -791,7 +824,7 @@ func (solver solarEclipseSolver) besselGeometryAt(jde float64) ([3]float64, sola if entry, ok := solver.besselGeometryCache[key]; ok && entry.generation == deltaTGenerationValue() { return entry.moon, entry.axis, entry.sun } - sun, moon := solarEclipseSunMoonEquatorial(jde) + sun, moon := solarEclipseSunMoonEquatorial(jde, solver.inst) axis := solarEclipseBesselAxisFromEquatorialWithDeltaT( jde, sun, moon, solver.effectiveDeltaTSeconds(jde), ) @@ -849,7 +882,7 @@ func storeSolarEclipseBesselGeometry( func solarEclipseBesselMoonFromEquatorial(moon [3]float64, axis solarEclipseAxis) [3]float64 { rotated := solarEclipseRotateLLR( - solarEclipseNormalizeSignedRadians(moon[0]-axis.rightAscension), + normalizeSignedRadians(moon[0]-axis.rightAscension), moon[1], moon[2], -axis.tilt, @@ -857,26 +890,35 @@ func solarEclipseBesselMoonFromEquatorial(moon [3]float64, axis solarEclipseAxis rectangular := solarEclipseLLRToXYZ(rotated[0], rotated[1], rotated[2]) return [3]float64{ - rectangular[0] / solarEclipseEarthEquatorialRadiusKM, - rectangular[1] / solarEclipseEarthEquatorialRadiusKM, - rectangular[2] / solarEclipseEarthEquatorialRadiusKM, + rectangular[0] / geodata.EarthEquatorialRadiusKM, + rectangular[1] / geodata.EarthEquatorialRadiusKM, + rectangular[2] / geodata.EarthEquatorialRadiusKM, } } -func solarEclipseSunMoonEquatorial(jde float64) ([3]float64, [3]float64) { +func solarEclipseSunMoonEquatorial(jde float64, instances ...*Instance) ([3]float64, [3]float64) { + inst := instanceValue(instances) + julianCentury := (jde - 2451545.0) / 36525.0 nutationLongitude, nutationObliquity := Nutation2000B(jde) obliquity := (Obliquity1980(jde) + nutationObliquity) * rad // Share the full-series distance and nutation for this single TT. - sunDistanceAU := EarthAway(jde) - sunLongitude := (HSunTrueLoN(jde, -1) + nutationLongitude - 20.49552/sunDistanceAU/3600) * rad - sunLatitude := HSunTrueBo(jde) * rad + sunDistanceAU := EarthAwayWithInstance(jde, inst) + // 新模型全精度时 HSunApparentLoN 已含光行差,避免重复计入常数项。 + sunLongitude := HSunApparentLoNWithInstance(jde, -1, inst) * rad + sunLatitude := HSunTrueBoWithInstance(jde, inst) * rad sunDistance := sunDistanceAU * solarEclipseAstronomicalUnitKM - moonLongitude := solarEclipseNormalizeRadians((HMoonTrueLoN(jde, -1)+nutationLongitude)*rad + solarEclipseMoonLonAberrRad) - moonLatitude := HMoonTrueBo(jde)*rad + moonLatitudeAberrationRad(julianCentury) - moonDistance := HMoonAway(jde) + // 新模型全精度时 HMoonApparentLoN 已含光行差,避免重复计入常数项。 + moonLongitude := solarEclipseNormalizeRadians(HMoonApparentLoNWithInstance(jde, -1, inst) * rad) + moonLatitude := HMoonTrueBoWithInstance(jde, inst) * rad + if instanceModel(inst) == ephem.Legacy { + sunLongitude = (HSunTrueLoNWithInstance(jde, -1, inst) + nutationLongitude - 20.49552/sunDistanceAU/3600) * rad + moonLongitude = solarEclipseNormalizeRadians((HMoonTrueLoNWithInstance(jde, -1, inst)+nutationLongitude)*rad + solarEclipseMoonLonAberrRad) + moonLatitude += moonLatitudeAberrationRad(julianCentury) + } + moonDistance := HMoonAwayWithInstance(jde, inst) sunEquatorial := solarEclipseRotateLLR(sunLongitude, sunLatitude, sunDistance, obliquity) moonEquatorial := solarEclipseRotateLLR(moonLongitude, moonLatitude, moonDistance, obliquity) @@ -885,8 +927,10 @@ func solarEclipseSunMoonEquatorial(jde float64) ([3]float64, [3]float64) { [3]float64{moonEquatorial[0], moonEquatorial[1], moonEquatorial[2]} } -func solarEclipseSunAltitudeAtGreatest(jde, lonDeg, latDeg, gst float64) float64 { - sun, _ := solarEclipseSunMoonEquatorial(jde) +func solarEclipseSunAltitudeAtGreatest(jde, lonDeg, latDeg, gst float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sun, _ := solarEclipseSunMoonEquatorial(jde, inst) return solarEclipseSunAltitudeFromEquatorial(sun, lonDeg, latDeg, gst) } @@ -1001,7 +1045,7 @@ func axisIntersectionLatitude(intersection solarEclipseLineIntersection, axis so } func solarEclipseIntersectionGeodetic(intersection solarEclipseLineIntersection, axis solarEclipseAxis) (float64, float64) { - longitude := solarEclipseNormalizeSignedRadians(math.Atan2(intersection.y, intersection.x) + axis.rightAscension - axis.gst) + longitude := normalizeSignedRadians(math.Atan2(intersection.y, intersection.x) + axis.rightAscension - axis.gst) latitude := math.Atan(intersection.z / solarEclipseEarthPolarRatioSquared / math.Sqrt(intersection.x*intersection.x+intersection.y*intersection.y)) return longitude * deg, latitude * deg } @@ -1009,7 +1053,7 @@ func solarEclipseIntersectionGeodetic(intersection solarEclipseLineIntersection, func solarEclipseBesselPointToGeodetic(x, y, z float64, axis solarEclipseAxis, ellipsoidal bool) (float64, float64) { point := solarEclipseXYZToLLR(x, y, z) rotated := solarEclipseRotateLLR(point[0], point[1], point[2], axis.tilt) - longitude := solarEclipseNormalizeSignedRadians(rotated[0] + axis.rightAscension - axis.gst) + longitude := normalizeSignedRadians(rotated[0] + axis.rightAscension - axis.gst) latitude := rotated[1] if ellipsoidal { latitude = math.Atan(math.Tan(latitude) / solarEclipseEarthPolarRatioSquared) @@ -1038,7 +1082,7 @@ func solarEclipseNormalizeRadians(angle float64) float64 { return angle } -func solarEclipseNormalizeSignedRadians(angle float64) float64 { +func normalizeSignedRadians(angle float64) float64 { angle = math.Mod(angle, 2*math.Pi) if angle <= -math.Pi { angle += 2 * math.Pi diff --git a/basic/solar_eclipse_band.go b/basic/solar_eclipse_band.go index f4114be..e26165c 100644 --- a/basic/solar_eclipse_band.go +++ b/basic/solar_eclipse_band.go @@ -383,7 +383,7 @@ func closestSolarEclipseBandApproach( } return SolarEclipsePathPoint{ JDE: horizonFirst.JDE + bestFraction*(horizonSecond.JDE-horizonFirst.JDE), - Longitude: normalizeLongitude(horizonFirst.Longitude + bestFraction*math.Remainder(horizonSecond.Longitude-horizonFirst.Longitude, 360)), + Longitude: geodata.NormalizeLongitude(horizonFirst.Longitude + bestFraction*math.Remainder(horizonSecond.Longitude-horizonFirst.Longitude, 360)), Latitude: horizonFirst.Latitude + bestFraction*(horizonSecond.Latitude-horizonFirst.Latitude), }, true } @@ -644,7 +644,7 @@ func solveSolarEclipseCentralLimitHorizonRoot( if evaluation.centralContactSecondDerivative(coordinates[0], coordinates[1]) <= 0 { return SolarEclipsePathPoint{}, false } - return SolarEclipsePathPoint{JDE: coordinates[2], Longitude: normalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, true + return SolarEclipsePathPoint{JDE: coordinates[2], Longitude: geodata.NormalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, true } delta, ok := solveSolarEclipse3x3(jacobian, [3]float64{-residual[0], -residual[1], -residual[2]}) if !ok { @@ -653,7 +653,7 @@ func solveSolarEclipseCentralLimitHorizonRoot( for index := range coordinates { coordinates[index] += delta[index] } - coordinates[0] = normalizeLongitude(coordinates[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0]) } return SolarEclipsePathPoint{}, false } @@ -814,7 +814,7 @@ func solarEclipseRingCenter(ring []geodata.GeoPoint) geodata.GeoPoint { } total := float64(len(ring)) return geodata.GeoPoint{ - Longitude: normalizeLongitude(longitude / total), + Longitude: geodata.NormalizeLongitude(longitude / total), Latitude: latitude / total, } } @@ -1024,7 +1024,7 @@ func dedupeSolarEclipseClosedRing( return points, times } open, openTimes := points, times - if pointDistanceKM(points[len(points)-1], points[0]) < minimumKM { + if geodata.EquatorialDistanceKM(points[len(points)-1], points[0]) < minimumKM { open, openTimes = points[:len(points)-1], times[:len(times)-1] } if len(open) < 3 { @@ -1035,7 +1035,7 @@ func dedupeSolarEclipseClosedRing( result = append(result, open[0]) resultTimes = append(resultTimes, openTimes[0]) for index, point := range open[1:] { - if pointDistanceKM(result[len(result)-1], point) >= minimumKM { + if geodata.EquatorialDistanceKM(result[len(result)-1], point) >= minimumKM { result = append(result, point) resultTimes = append(resultTimes, openTimes[index+1]) } @@ -1085,7 +1085,7 @@ func solarEclipseRingVertexTimes( backIndex, backDistance := offset, 0.0 for steps := 0; steps < count; steps++ { previous := (backIndex - 1 + count) % count - backDistance += pointDistanceKM(ring[previous], ring[backIndex]) + backDistance += geodata.EquatorialDistanceKM(ring[previous], ring[backIndex]) backIndex = previous if known[backIndex] { break @@ -1094,7 +1094,7 @@ func solarEclipseRingVertexTimes( forwardIndex, forwardDistance := offset, 0.0 for steps := 0; steps < count; steps++ { next := (forwardIndex + 1) % count - forwardDistance += pointDistanceKM(ring[forwardIndex], ring[next]) + forwardDistance += geodata.EquatorialDistanceKM(ring[forwardIndex], ring[next]) forwardIndex = next if known[forwardIndex] { break @@ -1167,7 +1167,7 @@ func smoothSolarEclipseClosedRing( ringTimes := make([]float64, 0, count+1) for index := 0; index < count; index++ { ring = append(ring, geodata.GeoPoint{ - Longitude: normalizeLongitude(longitudes[index]), + Longitude: geodata.NormalizeLongitude(longitudes[index]), Latitude: latitudes[index], }) ringTimes = append(ringTimes, vertexTimes[index]) @@ -1177,17 +1177,6 @@ func smoothSolarEclipseClosedRing( return ring, ringTimes } -// pointDistanceKM is the great-circle distance between two geographic points. -func pointDistanceKM(first, second geodata.GeoPoint) float64 { - firstLatitude := first.Latitude * rad - secondLatitude := second.Latitude * rad - deltaLatitude := secondLatitude - firstLatitude - deltaLongitude := math.Remainder((second.Longitude-first.Longitude)*rad, 2*math.Pi) - haversine := math.Sin(deltaLatitude/2)*math.Sin(deltaLatitude/2) + - math.Cos(firstLatitude)*math.Cos(secondLatitude)*math.Sin(deltaLongitude/2)*math.Sin(deltaLongitude/2) - return 2 * 6378.1366 * math.Asin(math.Sqrt(math.Min(1, haversine))) -} - // decimateSolarEclipseClosedRing thins one closed ring to the requested // spacing while keeping its first vertex and its closure. func decimateSolarEclipseClosedRing( diff --git a/basic/solar_eclipse_band_closure_scan.go b/basic/solar_eclipse_band_closure_scan.go index 90f57dc..1e03c9b 100644 --- a/basic/solar_eclipse_band_closure_scan.go +++ b/basic/solar_eclipse_band_closure_scan.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) // 地平闭包根的扫描式枚举。闭包根的定义是三个条件同时成立:站点落在 central limit 上 @@ -96,7 +98,7 @@ func (solver solarEclipseSolver) closureScanCandidatesAt(jde float64) []solarEcl sun := solarEclipseXYZToLLR( evaluation.center.sunXYZ[0], evaluation.center.sunXYZ[1], evaluation.center.sunXYZ[2], ) - centerLongitude := normalizeLongitude((sun[0] - evaluation.center.gst) / rad) + centerLongitude := geodata.NormalizeLongitude((sun[0] - evaluation.center.gst) / rad) centerLatitude := sun[1] / rad valueAt := func(angle float64) (float64, bool) { longitude, latitude := riseSetHorizonPoint(centerLongitude, centerLatitude, angle) diff --git a/basic/solar_eclipse_bessel.go b/basic/solar_eclipse_bessel.go index 2dd30b3..0b05315 100644 --- a/basic/solar_eclipse_bessel.go +++ b/basic/solar_eclipse_bessel.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) //下游研究需要,改成直接导出 @@ -93,11 +95,9 @@ func SolarEclipseBesselianMuForPublishedTable( return SolarEclipseBesselianPolynomial{mu[0] + shift, mu[1], mu[2], mu[3]} } -// SolarEclipseBesselianElements 计算给定近朔时刻附近一次日食的多项式贝塞尔根数,窗口内无日食时返回 false。 -// Polynomial Besselian elements for the solar eclipse near the given new-moon instant; false when there is none. -func SolarEclipseBesselianElements( - seedJDE float64, options SolarEclipseBesselianElementsOptions, -) (SolarEclipseBesselianElementsResult, bool) { +// SolarEclipseBesselianElementsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseBesselianElementsWithInstance( + seedJDE float64, options SolarEclipseBesselianElementsOptions, inst *Instance) (SolarEclipseBesselianElementsResult, bool) { options.Model = normalizeSolarEclipseRadiusModel(options.Model) options.SunRadiusModel = normalizeSolarEclipseSunRadiusModel(options.SunRadiusModel) validHours := options.ValidHours @@ -105,10 +105,10 @@ func SolarEclipseBesselianElements( validHours = solarEclipseBesselianDefaultValidHours } - solver := newSolarEclipseSolverWithOptions(CalcMoonSHByJDE(seedJDE, 0), SolarEclipseOptions{ + solver := newSolarEclipseSolverWithOptions(solarEclipseAnchorNewMoon(seedJDE, inst), SolarEclipseOptions{ RadiusModel: options.Model, SunRadiusModel: options.SunRadiusModel, - }). + }, inst). withDeltaTSeconds(options.DeltaTSeconds) feature := solver.feature() if feature.typeCode == "N" { diff --git a/basic/solar_eclipse_diagram.go b/basic/solar_eclipse_diagram.go index 845cf76..cc4fc25 100644 --- a/basic/solar_eclipse_diagram.go +++ b/basic/solar_eclipse_diagram.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) const ( @@ -20,6 +22,8 @@ type LocalSolarEclipseDiagramOptions struct { StepDays float64 // SunRadiusModel 太阳半径口径,零值为标准档 / solar radius convention, standard when zero. SunRadiusModel SolarEclipseSunRadiusModel + // LunarLimb 月缘剖面,nil 为圆月面;修正接触时刻、站心食分与遮蔽率,食甚时刻不变,月盘仍按圆面采样 / lunar limb profile for contacts, magnitude and obscuration. + LunarLimb LunarLimb } // LocalSolarEclipseDiagramFrame 表示一个时刻的站心日月视圆几何。 @@ -80,28 +84,28 @@ func LocalSolarEclipseDiagram(seedJDE, lon, lat, height float64, options LocalSo return LocalSolarEclipseDiagramNASABulletinSplitK(seedJDE, lon, lat, height, options) } -// LocalSolarEclipseDiagramIAUSingleK 计算站心日食视圆图几何数据,使用 IAU Single-K 模型。 -// LocalSolarEclipseDiagramIAUSingleK computes local solar eclipse disk diagram geometry with the IAU Single-K model. -func LocalSolarEclipseDiagramIAUSingleK(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions) LocalSolarEclipseDiagramResult { - return localSolarEclipseDiagram(seedJDE, lon, lat, height, SolarEclipseModelIAUSingleK, options) +// LocalSolarEclipseDiagramIAUSingleKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LocalSolarEclipseDiagramIAUSingleKWithInstance(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions, inst *Instance) LocalSolarEclipseDiagramResult { + return localSolarEclipseDiagram(seedJDE, lon, lat, height, SolarEclipseModelIAUSingleK, options, inst) } -// LocalSolarEclipseDiagramNASABulletinSplitK 计算站心日食视圆图几何数据,使用 NASA bulletin Split-K 模型。 -// LocalSolarEclipseDiagramNASABulletinSplitK computes local solar eclipse disk diagram geometry with the NASA bulletin Split-K model. -func LocalSolarEclipseDiagramNASABulletinSplitK(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions) LocalSolarEclipseDiagramResult { - return localSolarEclipseDiagram(seedJDE, lon, lat, height, SolarEclipseModelNASABulletinSplitK, options) +// LocalSolarEclipseDiagramNASABulletinSplitKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LocalSolarEclipseDiagramNASABulletinSplitKWithInstance(seedJDE, lon, lat, height float64, options LocalSolarEclipseDiagramOptions, inst *Instance) LocalSolarEclipseDiagramResult { + return localSolarEclipseDiagram(seedJDE, lon, lat, height, SolarEclipseModelNASABulletinSplitK, options, inst) } func localSolarEclipseDiagram( seedJDE, lonDeg, latDeg, heightMeters float64, model SolarEclipseRadiusModel, - options LocalSolarEclipseDiagramOptions, -) LocalSolarEclipseDiagramResult { + options LocalSolarEclipseDiagramOptions, instances ...*Instance) LocalSolarEclipseDiagramResult { + inst := instanceValue(instances) + options = normalizeLocalSolarEclipseDiagramOptions(options) eclipse := localSolarEclipse(seedJDE, lonDeg, latDeg, heightMeters, SolarEclipseOptions{ RadiusModel: model, SunRadiusModel: options.SunRadiusModel, - }) + LunarLimb: options.LunarLimb, + }, inst) result := LocalSolarEclipseDiagramResult{ Eclipse: eclipse, StepDays: options.StepDays, @@ -118,7 +122,7 @@ func localSolarEclipseDiagram( result.StepDays = stepDays result.Frames = make([]LocalSolarEclipseDiagramFrame, 0, len(times)) for _, item := range times { - frame := localSolarEclipseDiagramFrameAt(item.jde, lonRad, latRad, heightKM, params) + frame := localSolarEclipseDiagramFrameAt(item.jde, lonRad, latRad, heightKM, params, inst) frame.Label = localSolarEclipseDiagramPrimaryLabel(item.labels) frame.Labels = append([]string(nil), item.labels...) result.Frames = append(result.Frames, frame) @@ -250,9 +254,10 @@ func localSolarEclipseDiagramLabelPriority(labels []string) int { func localSolarEclipseDiagramFrameAt( jdTT, lonRad, latRad, heightKM float64, - params solarEclipseModelParameters, -) LocalSolarEclipseDiagramFrame { - sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT) + params solarEclipseModelParameters, instances ...*Instance) LocalSolarEclipseDiagramFrame { + inst := instanceValue(instances) + + sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT, inst) sunXYZ := solarEclipseLLRToXYZ(sunEquatorial[0], sunEquatorial[1], sunEquatorial[2]) moonXYZ := solarEclipseLLRToXYZ(moonEquatorial[0], moonEquatorial[1], moonEquatorial[2]) @@ -278,14 +283,14 @@ func localSolarEclipseDiagramFrameAt( ) sunRadiusRad := math.Asin(localSolarEclipseClampUnit( - solarEclipseEarthEquatorialRadiusKM * params.sunRadiusRatio / sunTopocentric[2], + geodata.EarthEquatorialRadiusKM * params.sunRadiusRatio / sunTopocentric[2], )) moonRadiusRad := math.Asin(localSolarEclipseClampUnit( solarEclipsePenumbralRadiusNumerator(params) / moonTopocentric[2], )) if params.umbralK > params.penumbralK { moonRadiusRad = math.Asin(localSolarEclipseClampUnit( - solarEclipseEarthEquatorialRadiusKM * params.umbralK * localSolarMoonRadiusScale / moonTopocentric[2], + geodata.EarthEquatorialRadiusKM * params.umbralK * localSolarMoonRadiusScale / moonTopocentric[2], )) } diff --git a/basic/solar_eclipse_hybrid_envelope.go b/basic/solar_eclipse_hybrid_envelope.go index 4aae51b..d8c6d92 100644 --- a/basic/solar_eclipse_hybrid_envelope.go +++ b/basic/solar_eclipse_hybrid_envelope.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) // Absolute Julian dates quantize a moving sky position at about 1e-10 radians. const solarEclipseCentralVectorTolerance = 2e-10 @@ -20,8 +24,8 @@ func solarCentralBandSkyOffset(context localSolarEclipseStateContext, longitude, equatorial := math.Hypot(sun[0], sun[1]) east := [3]float64{-sun[1] / equatorial, sun[0] / equatorial, 0} north := [3]float64{-sun[2] * east[1], sun[2] * east[0], equatorial} - radius := math.Asin(solarEclipseEarthEquatorialRadiusKM*context.params.umbralK*localSolarMoonRadiusScale/moonDistance) - - math.Asin(solarEclipseEarthEquatorialRadiusKM*context.params.sunRadiusRatio/sunDistance) + radius := math.Asin(geodata.EarthEquatorialRadiusKM*context.params.umbralK*localSolarMoonRadiusScale/moonDistance) - + math.Asin(geodata.EarthEquatorialRadiusKM*context.params.sunRadiusRatio/sunDistance) return [3]float64{dotSolarEclipse3(moon, east), dotSolarEclipse3(moon, north), math.Sin(radius)} } @@ -96,7 +100,7 @@ func (solver solarEclipseSolver) correctCentralBandVectorBoundary(predictor, tan state := evaluation.center.stateAt(coordinates[0]*rad, coordinates[1]*rad, 0) return solarEclipseNonCentralBandState{ coordinates: coordinates, tangent: nextTangent, - point: SolarEclipsePathPoint{JDE: jde, Longitude: normalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, + point: SolarEclipsePathPoint{JDE: jde, Longitude: geodata.NormalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, }, valid } delta, valid := solveSolarEclipse3x3([3][3]float64{jacobian[0], jacobian[1], tangent}, [3]float64{-residual[0], -residual[1], -plane}) @@ -257,7 +261,7 @@ func (solver solarEclipseSolver) hybridCentralBandTransition(seed SolarEclipsePa residual := solarCentralBandSkyOffset(context, coordinates[0], coordinates[1]) if math.Hypot(residual[0], residual[1]) < solarEclipseCentralVectorTolerance/2 && math.Abs(residual[2]) < 1e-12 { state := context.stateAt(coordinates[0]*rad, coordinates[1]*rad, 0) - return SolarEclipsePathPoint{JDE: jde, Longitude: normalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, true + return SolarEclipsePathPoint{JDE: jde, Longitude: geodata.NormalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad}, true } var jacobian [3][3]float64 for column := range coordinates { @@ -305,7 +309,7 @@ func (solver solarEclipseSolver) centralBandVectorHorizonRoots(axisContactJDE, d context := solver.localStateContextAt(jde) state := context.stateAt(coordinates[0]*rad, coordinates[1]*rad, 0) if math.Hypot(residual[0], residual[1]) <= solarEclipseCentralVectorTolerance && math.Abs(state.sunAltitudeRad) < 1e-8 { - roots[i] = SolarEclipsePathPoint{JDE: jde, Longitude: normalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad} + roots[i] = SolarEclipsePathPoint{JDE: jde, Longitude: geodata.NormalizeLongitude(coordinates[0]), Latitude: coordinates[1], SunAltitude: state.sunAltitudeRad / rad} // Grazing horizon roots can be many minutes from axis contact. // Bound them by the shadow's limb-crossing interval, not a fixed // window around the seed. diff --git a/basic/solar_eclipse_isochrone.go b/basic/solar_eclipse_isochrone.go index af8e207..bdb0e50 100644 --- a/basic/solar_eclipse_isochrone.go +++ b/basic/solar_eclipse_isochrone.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) const ( solarEclipseGreatestTimeContourSeedLatitudeStepDegrees = 5.0 @@ -61,7 +65,7 @@ func (arc solarEclipseGreatestTimeArc) residual(longitude, latitude float64) flo func (arc solarEclipseGreatestTimeArc) point(longitude, latitude float64, state localSolarEclipseState) SolarEclipsePathPoint { return SolarEclipsePathPoint{ JDE: arc.evaluation.jd, - Longitude: normalizeLongitude(longitude), + Longitude: geodata.NormalizeLongitude(longitude), Latitude: latitude, SunAltitude: state.sunAltitudeRad / rad, } @@ -187,7 +191,7 @@ func (solver solarEclipseSolver) traceGreatestTimeArc( next := arc.point(nextLongitude, nextLatitude, nextState) distance := solarEclipsePathDistanceKM(points[len(points)-1], next) // 校正回到原点说明该方向已经走到支路端点,继续只会原地打转。 - if distance < 1e-9 || distance > 4*step*greatestTimeContourKMPerDegree { + if distance < 1e-9 || distance > 4*step*geodata.EarthKMPerDegree { break } points = append(points, next) diff --git a/basic/solar_eclipse_isochrone_test.go b/basic/solar_eclipse_isochrone_test.go index 5ffb147..b4cb103 100644 --- a/basic/solar_eclipse_isochrone_test.go +++ b/basic/solar_eclipse_isochrone_test.go @@ -110,8 +110,7 @@ func TestSolarEclipseGreatestTimeContoursRejectOutOfWindowLevels(t *testing.T) { } } -// 同一时刻取值的等时线必须是一条连通曲线:曾经因为"覆盖判据用点到顶点距离"而把同一条线 -// 用多个种子各画一遍(四条重复支路、点数翻三倍)。 +// 同一时刻取值的等时线必须是一条连通曲线,不得由多个种子重复描出支路。 func TestSolarEclipseGreatestTimeContoursHaveSingleBranchPerLevel(t *testing.T) { for _, tc := range []struct { name string diff --git a/basic/solar_eclipse_limb.go b/basic/solar_eclipse_limb.go new file mode 100644 index 0000000..655d73a --- /dev/null +++ b/basic/solar_eclipse_limb.go @@ -0,0 +1,155 @@ +package basic + +import "math" + +// solarEclipseLimbMaxShiftDays 是全球偏食始末月缘修正的上限(2 分钟),超出视为不可信并保留圆月面结果。 +const solarEclipseLimbMaxShiftDays = 120.0 / 86400.0 + +// solarEclipseLimbDerivativeStepDays 是求外切间隙时间导数的差分半步长(10 秒)。 +const solarEclipseLimbDerivativeStepDays = 10.0 / 86400.0 + +// SolarEclipseLimbContact 月缘修正后的全球偏食始末接触点,与修正后的时刻同源 / limb-corrected global contact location. +type SolarEclipseLimbContact struct { + // Defined 为真时经纬度有效;月缘未生效时保持 false 并且经纬度为零值 / the location is valid. + Defined bool + // Longitude 经度,东正西负,单位度 / longitude in degrees, east positive. + Longitude float64 + // Latitude 纬度,北正南负,单位度 / latitude in degrees, north positive. + Latitude float64 +} + +// withLunarLimb 在圆月面全球结果上按月缘修正偏食始末(P1/P4)与食甚点中心食时长;中心线始末、食甚、食分与食型不变, +// 本函数不改带宽,月缘带宽增量由路径层在成对横截面上叠加。 +func (solver solarEclipseSolver) withLunarLimb(result SolarEclipseResult, limb LunarLimb) SolarEclipseResult { + if limb == nil || result.Type == SolarEclipseNone { + return result + } + limbSolver := solver.withLimbParams(limb) + + original := result + if result.HasPartial { + result.PartialBeginOnEarth, result.PartialBeginContact = solver.limbPartialContactPoint(limbSolver, result.PartialBeginOnEarth) + result.PartialEndOnEarth, result.PartialEndContact = solver.limbPartialContactPoint(limbSolver, result.PartialEndOnEarth) + } + if result.HasCentral && result.GreatestEclipse > 0 { + // 剖面在任一求解状态上无值时,半径已退回圆月面,保留圆月面时长而不是采纳离散差。 + if duration, limbOK := limbSolver.centralPhaseDurationDaysWithLimbAt( + result.GreatestEclipse, result.GreatestLongitude, result.GreatestLatitude, + ); limbOK && duration > 0 { + result.CentralDurationDays = duration + } + } + result.LunarLimbApplied = solarEclipseLimbChanged(result, original) + // 时刻未真正改变时位置也不该被声明,避免出图标记用上噪声级的平移。 + if !result.LunarLimbApplied { + result.PartialBeginContact, result.PartialEndContact = SolarEclipseLimbContact{}, SolarEclipseLimbContact{} + } + return result +} + +// limbPartialContactPoint 在修正后的时刻重解半影与地球的外切点,使接触点位置与新时刻同源。 +func (solver solarEclipseSolver) limbPartialContactPoint( + limbSolver solarEclipseSolver, + contactJDE float64, +) (float64, SolarEclipseLimbContact) { + jde, ok := solver.limbPartialContact(limbSolver, contactJDE) + if !ok { + return jde, SolarEclipseLimbContact{} + } + point, pointOK := solver.shadowContactPointAt(jde, solarEclipsePenumbralShadow, false) + if !pointOK { + return jde, SolarEclipseLimbContact{} + } + return jde, SolarEclipseLimbContact{Defined: true, Longitude: point.Longitude, Latitude: point.Latitude} +} + +func solarEclipseLimbChanged(result, original SolarEclipseResult) bool { + for _, pair := range [][2]float64{ + {result.PartialBeginOnEarth, original.PartialBeginOnEarth}, + {result.PartialEndOnEarth, original.PartialEndOnEarth}, + {result.CentralDurationDays, original.CentralDurationDays}, + } { + if limbTimeChanged(pair[0], pair[1]) { + return true + } + } + return false +} + +// solarEclipseLimbSiteSpanRad 与 solarEclipseLimbSiteStepRad 是切点两侧沿地球边缘扫描的半宽与步长: +// 月缘起伏约 ±5 km,而偏离切点 δ 时影锥外距约 R⊕·δ²/2,δ 超过约 3° 后不可能更早接触。 +const ( + solarEclipseLimbSiteSpanRad = 3.0 * math.Pi / 180 + solarEclipseLimbSiteStepRad = 0.1 * math.Pi / 180 +) + +// limbPartialContact 修正一次全球偏食始末,P1 取最早、P4 取最晚;第二个返回值报告是否取到月缘解。 +func (solver solarEclipseSolver) limbPartialContact(limbSolver solarEclipseSolver, contactJDE float64) (float64, bool) { + if !finite(contactJDE) || contactJDE <= 0 { + return contactJDE, false + } + moon := solver.besselMoonAt(contactJDE) + norm := math.Hypot(moon[0], moon[1]) + if !finite(norm) || norm == 0 { + return contactJDE, false + } + axis := solver.besselAxisAt(contactJDE) + center := math.Atan2(moon[1], moon[0]) + siteAt := func(angle float64) (float64, float64, bool) { + x, y := math.Cos(angle), math.Sin(angle) + // 与 quickContactAt 同口径的椭球有效半径,再稍向内收保证视线与椭球相交。 + effective := 1 - (1/solarEclipseEarthPolarRatioSquared-1)*y*y/2 + lonDeg, latDeg, ok := solarEclipseBesselXYToGeodetic(x*effective*0.999, y*effective*0.999, axis, true) + return lonDeg * rad, latDeg * rad, ok + } + gapAt := func(s solarEclipseSolver, lonRad, latRad, jde float64) float64 { + return s.localStateContextAt(jde).stateAt(lonRad, latRad, 0).movingDiskContactState().externalContactGap() + } + // 剖面在该点无值时半径退回圆月面,这样的点不能参与修正,否则圆月面离散误差会冒充月缘修正。 + limbGapAt := func(lonRad, latRad, jde float64) (float64, bool) { + state := limbSolver.localStateContextAt(jde).stateAt(lonRad, latRad, 0) + return state.movingDiskContactState().externalContactGap(), state.outerLimbApplied + } + lon0, lat0, ok := siteAt(center) + if !ok { + return contactJDE, false + } + reference := gapAt(solver, lon0, lat0, contactJDE) + after, okAfter := limbGapAt(lon0, lat0, contactJDE+solarEclipseLimbDerivativeStepDays) + before, okBefore := limbGapAt(lon0, lat0, contactJDE-solarEclipseLimbDerivativeStepDays) + derivative := (after - before) / (2 * solarEclipseLimbDerivativeStepDays) + if !okAfter || !okBefore || !finite(reference) || !finite(derivative) || derivative == 0 { + return contactJDE, false + } + // derivative < 0 表示间隙随时间缩小(P1),此时取最早接触;否则取最晚(P4)。 + earliest := derivative < 0 + best, found := 0.0, false + steps := int(math.Round(solarEclipseLimbSiteSpanRad / solarEclipseLimbSiteStepRad)) + for i := -steps; i <= steps; i++ { + lonRad, latRad, ok := siteAt(center + float64(i)*solarEclipseLimbSiteStepRad) + if !ok { + continue + } + gap, limbOK := limbGapAt(lonRad, latRad, contactJDE) + if !limbOK || !finite(gap) { + continue + } + shift := -(gap - reference) / derivative + if !found || (earliest && shift < best) || (!earliest && shift > best) { + best, found = shift, true + } + } + if !found || !finite(best) || math.Abs(best) > solarEclipseLimbMaxShiftDays { + return contactJDE, false + } + return contactJDE + best, true +} + +// withLimbParams 返回带月缘、独立状态缓存的求解器副本;原求解器的圆月面缓存不受影响。 +func (solver solarEclipseSolver) withLimbParams(limb LunarLimb) solarEclipseSolver { + limbSolver := solver + limbSolver.params.lunarLimb = limb + limbSolver.localStateContextCache = make(map[uint64]localSolarEclipseStateContext) + limbSolver.limbAxes = nil + return limbSolver +} diff --git a/basic/solar_eclipse_local.go b/basic/solar_eclipse_local.go index 6c0a24c..7646d86 100644 --- a/basic/solar_eclipse_local.go +++ b/basic/solar_eclipse_local.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) // LocalSolarEclipseResult 表示某个站点的一次站心日食几何结果。 // @@ -11,7 +15,9 @@ type LocalSolarEclipseResult struct { // The fields below are the conventions that fix the numbers above. Model SolarEclipseRadiusModel SunRadiusModel SolarEclipseSunRadiusModel - Type SolarEclipseType + // LunarLimbApplied 表示接触时刻已按月缘剖面修正 / reports whether lunar-limb refinement changed the contacts. + LunarLimbApplied bool + Type SolarEclipseType // GreatestEclipse 是站心盘面中心角距最小的时刻。 GreatestEclipse float64 @@ -52,8 +58,13 @@ type localSolarEclipseState struct { sunRadiusRad float64 moonOuterRadiusRad float64 moonInnerRadiusRad float64 - sunAltitudeRad float64 - sunAzimuthRad float64 + // innerTotal 为真表示内切按全食求、食型为全食;有月缘时取全食、环食两支中内切间隙较小者,不能由被钳过的等效内半径反推。 + innerTotal bool + sunAltitudeRad float64 + sunAzimuthRad float64 + // limbApplied 表示内外半径都取自月缘剖面;outerLimbApplied 只看外切半径,偏食界线与全球始末只需要它。 + limbApplied bool + outerLimbApplied bool } func (state localSolarEclipseState) movingDiskContactState() movingDiskContactState { @@ -76,6 +87,8 @@ type localSolarEclipseStateContext struct { moonXYZ [3]float64 gst float64 params solarEclipseModelParameters + // 仅设置月缘时非 nil;用指针让圆月面路径的缓存条目保持免分配。 + limbFrame *lunarLimbFrame // generation 记录写入缓存时的 ΔT 世代(gst 依赖 ΔT,覆盖 ΔT 后条目必须失效)。 // generation is the ΔT generation the cached context was built under; gst depends on // ΔT, so an override must invalidate the entry. @@ -91,23 +104,15 @@ const ( localSolarMoonRadiusScale = 1.0000036 ) -// LocalSolarEclipse 计算给定近朔时刻附近的一次站心日食,默认使用 NASA bulletin Split-K 模型。 -// -// seedJDE 为力学时儒略日(TT),只需落在目标朔月附近,允许相差数天。 -// LocalSolarEclipse 计算给定近朔时刻附近的一次站心日食,使用 NASA bulletin Split-K 模型与标准太阳半径。 -// -// lon 为经度,东正西负;lat 为纬度,北正南负;height 为观测点高度,单位米,按椭球高(大地高)解读: -// 只有正高 H 时须由调用方先加上大地水准面差距 N,即 height = H + N;本库不建模 N。 -// LocalSolarEclipse computes one local solar eclipse with the NASA bulletin Split-K model and -// the standard solar radius. height is the ellipsoidal height in metres; convert an orthometric -// height H with height = H + N, since the geoid undulation N is not modelled here. +// LocalSolarEclipse 计算给定近朔时刻附近的一次站心日食,默认使用 NASA bulletin Split-K 模型与标准太阳半径 / one local solar eclipse near a new moon. +// seedJDE 为力学时儒略日(TT),落在目标朔月附近即可;height 按椭球高(大地高)解读,只有正高 H 时须先加 N。 func LocalSolarEclipse(seedJDE, lon, lat, height float64) LocalSolarEclipseResult { return LocalSolarEclipseNASABulletinSplitK(seedJDE, lon, lat, height) } -// LocalSolarEclipseWithOptions 计算给定近朔时刻附近的一次站心日食,半径口径由 options 指定 / computes one local solar eclipse with the given radius conventions. -func LocalSolarEclipseWithOptions(seedJDE, lon, lat, height float64, options SolarEclipseOptions) LocalSolarEclipseResult { - return localSolarEclipse(seedJDE, lon, lat, height, options) +// LocalSolarEclipseWithOptionsWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LocalSolarEclipseWithOptionsWithInstance(seedJDE, lon, lat, height float64, options SolarEclipseOptions, inst *Instance) LocalSolarEclipseResult { + return localSolarEclipse(seedJDE, lon, lat, height, options, inst) } // LocalSolarEclipseIAUSingleK 计算给定近朔时刻附近的一次站心日食,使用 IAU Single-K 模型。 @@ -120,18 +125,63 @@ func LocalSolarEclipseNASABulletinSplitK(seedJDE, lon, lat, height float64) Loca return localSolarEclipse(seedJDE, lon, lat, height, SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK}) } -func localSolarEclipse(seedJDE, lonDeg, latDeg, heightMeters float64, options SolarEclipseOptions) LocalSolarEclipseResult { - newMoonJDE := CalcMoonSHByJDE(seedJDE, 0) +func localSolarEclipse(seedJDE, lonDeg, latDeg, heightMeters float64, options SolarEclipseOptions, instances ...*Instance) LocalSolarEclipseResult { + if options.LunarLimb != nil { + circularOptions := options + circularOptions.LunarLimb = nil + circular := localSolarEclipseCore(seedJDE, lonDeg, latDeg, heightMeters, circularOptions, instances...) + result := localSolarEclipseCore(seedJDE, lonDeg, latDeg, heightMeters, options, instances...) + result.LunarLimbApplied = localSolarEclipseLimbChanged(result, circular) + return result + } + return localSolarEclipseCore(seedJDE, lonDeg, latDeg, heightMeters, options, instances...) +} + +// localSolarEclipseLimbChanged 报告月缘是否改变了站心结果中任何对外可见的数值: +// 四个接触时刻、食甚时的食分与遮蔽率,以及站心食型。食甚时刻本身按圆月面最近距离求,不参与比较。 +func localSolarEclipseLimbChanged(result, circular LocalSolarEclipseResult) bool { + if result.Type == SolarEclipseNone || circular.Type == SolarEclipseNone { + return result.Type != circular.Type + } + if result.Type != circular.Type { + return true + } + for _, pair := range [][2]float64{ + {result.PartialStart, circular.PartialStart}, + {result.PartialEnd, circular.PartialEnd}, + {result.CentralStart, circular.CentralStart}, + {result.CentralEnd, circular.CentralEnd}, + } { + if limbTimeChanged(pair[0], pair[1]) { + return true + } + } + for _, pair := range [][2]float64{ + {result.Magnitude, circular.Magnitude}, + {result.Obscuration, circular.Obscuration}, + } { + if limbMagnitudeChanged(pair[0], pair[1]) { + return true + } + } + return false +} + +func localSolarEclipseCore(seedJDE, lonDeg, latDeg, heightMeters float64, options SolarEclipseOptions, instances ...*Instance) LocalSolarEclipseResult { + inst := instanceValue(instances) + + newMoonJDE := solarEclipseAnchorNewMoon(seedJDE, inst) lonRad := lonDeg * rad latRad := latDeg * rad heightKM := heightMeters / 1000.0 options.RadiusModel = normalizeSolarEclipseRadiusModel(options.RadiusModel) options.SunRadiusModel = normalizeSolarEclipseSunRadiusModel(options.SunRadiusModel) params := solarEclipseModelParams(options.RadiusModel, options.SunRadiusModel) + params.lunarLimb = options.LunarLimb - greatestEclipseJDE := localSolarEclipseGreatest(newMoonJDE, lonRad, latRad, heightKM, params) - state := localSolarEclipseStateAt(greatestEclipseJDE, lonRad, latRad, heightKM, params) - contactEvaluator := newLocalSolarEclipseContactEvaluator(lonRad, latRad, heightKM, params) + greatestEclipseJDE := localSolarEclipseGreatest(newMoonJDE, lonRad, latRad, heightKM, params, inst) + state := localSolarEclipseStateAt(greatestEclipseJDE, lonRad, latRad, heightKM, params, inst) + contactEvaluator := newLocalSolarEclipseContactEvaluator(lonRad, latRad, heightKM, params, inst) visibleThresholdRad := 0.0 if heightMeters > 0 { visibleThresholdRad = -HeightDegreeByLat(heightMeters, latDeg) * rad @@ -179,7 +229,7 @@ func localSolarEclipse(seedJDE, lonDeg, latDeg, heightMeters float64, options So } result.HasCentral = true - if state.moonInnerRadiusRad >= state.sunRadiusRad { + if state.innerTotal { result.Type = SolarEclipseTotal result.HasTotal = true } else { @@ -202,9 +252,9 @@ func localSolarEclipse(seedJDE, lonDeg, latDeg, heightMeters float64, options So // Split-K 的 k1 是编译期常量,这里保持常量折叠后的末位,使该模型的既有输出逐字节不变;IAU Single-K 只能走运行时值。 func solarEclipsePenumbralRadiusNumerator(params solarEclipseModelParameters) float64 { if params.penumbralK == solarEclipsePenumbralK { - return solarEclipseEarthEquatorialRadiusKM * solarEclipsePenumbralK * localSolarMoonRadiusScale + return geodata.EarthEquatorialRadiusKM * solarEclipsePenumbralK * localSolarMoonRadiusScale } - return solarEclipseEarthEquatorialRadiusKM * params.penumbralK * localSolarMoonRadiusScale + return geodata.EarthEquatorialRadiusKM * params.penumbralK * localSolarMoonRadiusScale } func solarEclipseModelParams(model SolarEclipseRadiusModel, sunRadiusModel SolarEclipseSunRadiusModel) solarEclipseModelParameters { @@ -222,10 +272,13 @@ func solarEclipseModelParams(model SolarEclipseRadiusModel, sunRadiusModel Solar func localSolarEclipseGreatest( newMoonJDE, lonRad, latRad, heightKM float64, - params solarEclipseModelParameters, -) float64 { + params solarEclipseModelParameters, instances ...*Instance) float64 { + inst := instanceValue(instances) + + // 食甚只看日月中心角距,与月缘无关,免去每步的全方向月缘扫描。 + params.lunarLimb = nil return localSolarEclipseGreatestWith(newMoonJDE, func(jd float64) localSolarEclipseState { - return localSolarEclipseStateAt(jd, lonRad, latRad, heightKM, params) + return localSolarEclipseStateAt(jd, lonRad, latRad, heightKM, params, inst) }) } @@ -247,15 +300,30 @@ func localSolarEclipseGreatestWith( // centralPhaseDurationDaysAt 用事件局部插值星历求解某点的中心相时长(日):先求局部食甚, // 再解本影/反本影的内切接触。没有中心相或接触退化时返回 0。 func (solver solarEclipseSolver) centralPhaseDurationDaysAt(jde, lonDeg, latDeg float64) float64 { + duration, _ := solver.centralPhaseDurationDaysWithLimbAt(jde, lonDeg, latDeg) + return duration +} + +// centralPhaseDurationDaysWithLimbAt 同 centralPhaseDurationDaysAt,并报告求解用到的每个状态是否都取到了月缘半径。 +func (solver solarEclipseSolver) centralPhaseDurationDaysWithLimbAt(jde, lonDeg, latDeg float64) (float64, bool) { solver = solver.withLocalEphemeris() lonRad, latRad := lonDeg*rad, latDeg*rad + limbOK := solver.params.lunarLimb != nil stateAt := func(jde float64) localSolarEclipseState { - return solver.localStateContextCandidateAt(jde).stateAt(lonRad, latRad, 0) + state := solver.localStateContextCandidateAt(jde).stateAt(lonRad, latRad, 0) + if !state.limbApplied { + limbOK = false + } + return state } - greatestJDE := localSolarEclipseGreatestWith(solver.newMoonJDE, stateAt) + greatestJDE := localSolarEclipseGreatestWith(solver.newMoonJDE, func(jde float64) localSolarEclipseState { + context := solver.localStateContextCandidateAt(jde) + context.limbFrame = nil + return context.stateAt(lonRad, latRad, 0) + }) contactState := stateAt(greatestJDE).movingDiskContactState() if !contactState.valid || contactState.internalContactGap() > 0 { - return 0 + return 0, false } evaluator := newMovingDiskContactEvaluator(func(jde float64) (movingDiskContactState, bool) { contact := stateAt(jde).movingDiskContactState() @@ -264,21 +332,22 @@ func (solver solarEclipseSolver) centralPhaseDurationDaysAt(jde, lonDeg, latDeg evaluator.prime(greatestJDE, contactState, true) start, ok := localSolarEclipseContactWithEvaluator(greatestJDE, evaluator, true, true) if !ok { - return 0 + return 0, false } end, ok := localSolarEclipseContactWithEvaluator(greatestJDE, evaluator, true, false) if !ok || end <= start { - return 0 + return 0, false } - return end - start + return end - start, limbOK } func newLocalSolarEclipseContactEvaluator( lonRad, latRad, heightKM float64, - params solarEclipseModelParameters, -) *movingDiskContactEvaluator { + params solarEclipseModelParameters, instances ...*Instance) *movingDiskContactEvaluator { + inst := instanceValue(instances) + return newMovingDiskContactEvaluator(func(jdTT float64) (movingDiskContactState, bool) { - state := localSolarEclipseStateAt(jdTT, lonRad, latRad, heightKM, params) + state := localSolarEclipseStateAt(jdTT, lonRad, latRad, heightKM, params, inst) contactState := state.movingDiskContactState() return contactState, contactState.valid }) @@ -320,35 +389,44 @@ func localSolarEclipseContactWithEvaluator( func localSolarEclipseStateAt( jdTT, lonRad, latRad, heightKM float64, - params solarEclipseModelParameters, -) localSolarEclipseState { - return newLocalSolarEclipseStateContext(jdTT, params).stateAt(lonRad, latRad, heightKM) + params solarEclipseModelParameters, instances ...*Instance) localSolarEclipseState { + inst := instanceValue(instances) + + return newLocalSolarEclipseStateContext(jdTT, params, inst).stateAt(lonRad, latRad, heightKM) } func newLocalSolarEclipseStateContext( jdTT float64, - params solarEclipseModelParameters, -) localSolarEclipseStateContext { - return newLocalSolarEclipseStateContextWithOverride(jdTT, math.NaN(), params) + params solarEclipseModelParameters, instances ...*Instance) localSolarEclipseStateContext { + inst := instanceValue(instances) + + return newLocalSolarEclipseStateContextWithOverride(jdTT, math.NaN(), params, inst) } // newLocalSolarEclipseStateContextWithOverride 用显式 ΔT 构造站心状态上下文:TT 时刻不变, // ΔT 只决定恒星时相位;deltaTSeconds 为 NaN 时走进程级模型。 func newLocalSolarEclipseStateContextWithOverride( jdTT, deltaTSeconds float64, - params solarEclipseModelParameters, -) localSolarEclipseStateContext { - sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT) + params solarEclipseModelParameters, instances ...*Instance) localSolarEclipseStateContext { + inst := instanceValue(instances) + + sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT, inst) ut1JDE := TT2UT1(jdTT) if !math.IsNaN(deltaTSeconds) { ut1JDE = jdTT - deltaTSeconds/86400 } - return localSolarEclipseStateContext{ + context := localSolarEclipseStateContext{ sunXYZ: solarEclipseLLRToXYZ(sunEquatorial[0], sunEquatorial[1], sunEquatorial[2]), moonXYZ: solarEclipseLLRToXYZ(moonEquatorial[0], moonEquatorial[1], moonEquatorial[2]), gst: ApparentSiderealTime(ut1JDE) * 15 * rad, params: params, } + if params.lunarLimb != nil { + if frame := newLunarLimbFrame(params.lunarLimb, jdTT, context.moonXYZ, inst); frame.valid { + context.limbFrame = &frame + } + } + return context } func (context localSolarEclipseStateContext) stateAt(lonRad, latRad, heightKM float64) localSolarEclipseState { @@ -376,14 +454,29 @@ func (context localSolarEclipseStateContext) stateAt(lonRad, latRad, heightKM fl } sunRadiusRad := math.Asin(localSolarEclipseClampUnit( - solarEclipseEarthEquatorialRadiusKM * context.params.sunRadiusRatio / sunTopocentric[2], + geodata.EarthEquatorialRadiusKM * context.params.sunRadiusRatio / sunTopocentric[2], )) moonOuterRadiusRad := math.Asin(localSolarEclipseClampUnit( solarEclipsePenumbralRadiusNumerator(context.params) / moonTopocentric[2], )) moonInnerRadiusRad := math.Asin(localSolarEclipseClampUnit( - solarEclipseEarthEquatorialRadiusKM * context.params.umbralK * localSolarMoonRadiusScale / moonTopocentric[2], + geodata.EarthEquatorialRadiusKM * context.params.umbralK * localSolarMoonRadiusScale / moonTopocentric[2], )) + innerTotal := moonInnerRadiusRad >= sunRadiusRad + limbApplied, outerLimbApplied := false, false + if context.limbFrame != nil { + outer, inner, total, outerOK, innerOK := context.limbRadii( + observerXYZ, math.Acos(dot), sunRadiusRad, moonTopocentric[2], moonInnerRadiusRad, + ) + // 剖面在某侧无值时该侧保持圆月面。 + if outerOK { + moonOuterRadiusRad, outerLimbApplied = outer, true + } + if innerOK { + moonInnerRadiusRad, innerTotal = inner, total + } + limbApplied = outerOK && innerOK + } sunHorizontal := solarEclipseEquatorialToHorizontal( sunTopocentric[0], @@ -402,12 +495,57 @@ func (context localSolarEclipseStateContext) stateAt(lonRad, latRad, heightKM fl moonInnerRadiusRad: moonInnerRadiusRad, sunAltitudeRad: sunHorizontal[1], sunAzimuthRad: solarEclipseNormalizeRadians(sunHorizontal[0] + math.Pi), + innerTotal: innerTotal, + limbApplied: limbApplied, + outerLimbApplied: outerLimbApplied, } } +// limbRadii 用月缘剖面求外、内接触的等效月半径(弧度):月缘在全部位置角上的最不利余量折算成圆月面半径, +// 使圆月面接触间隙恰为月缘余量的相反数。圆月面内半径离日面半径不到一个起伏幅度时全食、环食两支都算, +// 取内切间隙较小的一支并据此报告 total;内外两侧分别报告是否取到剖面值。 +func (context localSolarEclipseStateContext) limbRadii( + observerXYZ [3]float64, + separationRad, sunRadiusRad, moonDistanceKM, circularInnerRad float64, +) (outer, inner float64, total, outerOK, innerOK bool) { + moonTopocentric := [3]float64{ + context.moonXYZ[0] - observerXYZ[0], + context.moonXYZ[1] - observerXYZ[1], + context.moonXYZ[2] - observerXYZ[2], + } + sunTopocentric := [3]float64{ + context.sunXYZ[0] - observerXYZ[0], + context.sunXYZ[1] - observerXYZ[1], + context.sunXYZ[2] - observerXYZ[2], + } + total = circularInnerRad >= sunRadiusRad + view, ok := context.limbFrame.viewToward(moonTopocentric, sunTopocentric) + if !ok { + return 0, 0, total, false, false + } + contact := lunarLimbContact{ + frame: context.limbFrame, + view: view, + distanceKM: moonDistanceKM, + separation: separationRad, + targetRadius: sunRadiusRad, + } + outer, outerOK = contact.outerRadius() + inner, innerOK = contact.innerRadius(total) + low, high, ranged := contact.bounds() + // 混合食附近月缘可让食型翻转:剖面不报告范围时无从判断,同样两支都算。 + if innerOK && (!ranged || math.Abs(circularInnerRad-sunRadiusRad) < high-low) { + if other, otherOK := contact.innerRadius(!total); otherOK && + separationRad-math.Abs(other-sunRadiusRad) < separationRad-math.Abs(inner-sunRadiusRad) { + inner, total = other, !total + } + } + return outer, inner, total, outerOK, innerOK +} + func localSolarEclipseObserverXYZ(gst, lonRad, latRad, heightKM float64) [3]float64 { - equatorialRadius := solarEclipseEarthEquatorialRadiusKM - polarRadius := solarEclipseEarthEquatorialRadiusKM * solarEclipseEarthPolarRatio + equatorialRadius := geodata.EarthEquatorialRadiusKM + polarRadius := geodata.EarthEquatorialRadiusKM * solarEclipseEarthPolarRatio u := math.Atan((polarRadius / equatorialRadius) * math.Tan(latRad)) radiusXY := equatorialRadius*math.Cos(u) + heightKM*math.Cos(latRad) diff --git a/basic/solar_eclipse_magnitude.go b/basic/solar_eclipse_magnitude.go index 523bcac..f7cceaa 100644 --- a/basic/solar_eclipse_magnitude.go +++ b/basic/solar_eclipse_magnitude.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) func (solver solarEclipseSolver) magnitudeContourSegments( @@ -24,6 +26,8 @@ func (solver solarEclipseSolver) magnitudeContourSegmentsWithSpacing( if targetSpacingKM <= 0 || math.IsNaN(targetSpacingKM) || math.IsInf(targetSpacingKM, 0) { targetSpacingKM = solarEclipseMagnitudeContourTargetSpacingKM } + // 追踪用事件局部插值星历求残差与雅可比,输出点仍由 validMagnitudeArcState 精确确认。 + solver = solver.withLocalEphemeris() var transitions []SolarEclipsePathPoint if hybrid && math.Abs(magnitude-1) <= 1e-12 { transitions = solver.centralMagnitudeOneTransitionsInInterval( @@ -527,7 +531,7 @@ func (solver solarEclipseSolver) validMagnitudeArcState( iterations int, ) (solarEclipseMagnitudeArcState, int, bool) { jde := referenceJDE + coordinates[2]/solarEclipseMagnitudeContourTimeScale - longitude := normalizeLongitude(coordinates[0]) + longitude := geodata.NormalizeLongitude(coordinates[0]) latitude := coordinates[1] evaluation := solver.magnitudeEvaluationAt(jde) state := evaluation.center.stateAt(longitude*rad, latitude*rad, 0) @@ -553,9 +557,9 @@ func (solver solarEclipseSolver) magnitudeEnvelopeJacobian( magnitude, referenceJDE float64, ) ([2]float64, [2][3]float64, bool) { jde := referenceJDE + coordinates[2]/solarEclipseMagnitudeContourTimeScale - longitude := normalizeLongitude(coordinates[0]) + longitude := geodata.NormalizeLongitude(coordinates[0]) latitude := coordinates[1] - evaluation := solver.magnitudeEvaluationAt(jde) + evaluation := solver.magnitudeCandidateEvaluationAt(jde) residual, ok := solarEclipseMagnitudeEnvelopeResidualAt(evaluation, longitude, latitude, magnitude) if !ok { return [2]float64{}, [2][3]float64{}, false @@ -574,14 +578,14 @@ func (solver solarEclipseSolver) magnitudeEnvelopeJacobian( jacobian[row][column] = (shiftedResidual[row] - residual[row]) / steps[column] } } - timeEvaluation := solver.magnitudeEvaluationAt(jde + steps[2]/solarEclipseMagnitudeContourTimeScale) + timeEvaluation := solver.magnitudeCandidateEvaluationAt(jde + steps[2]/solarEclipseMagnitudeContourTimeScale) timeResidual, timeOK := solarEclipseMagnitudeEnvelopeResidualAt( timeEvaluation, longitude, latitude, magnitude, ) if !timeOK { return [2]float64{}, [2][3]float64{}, false } - beforeEvaluation := solver.magnitudeEvaluationAt(jde - steps[2]/solarEclipseMagnitudeContourTimeScale) + beforeEvaluation := solver.magnitudeCandidateEvaluationAt(jde - steps[2]/solarEclipseMagnitudeContourTimeScale) beforeResidual, beforeOK := solarEclipseMagnitudeEnvelopeResidualAt( beforeEvaluation, longitude, latitude, magnitude, ) @@ -638,7 +642,7 @@ func (solver solarEclipseSolver) refineMagnitudeHorizonCrossing( deltaLongitude := math.Remainder(hidden.Longitude-visible.Longitude, 360) return solver.refineMagnitudeHorizonPoint( visible.JDE+fraction*(hidden.JDE-visible.JDE), - normalizeLongitude(visible.Longitude+fraction*deltaLongitude), + geodata.NormalizeLongitude(visible.Longitude+fraction*deltaLongitude), visible.Latitude+fraction*(hidden.Latitude-visible.Latitude), magnitude, ) @@ -688,7 +692,7 @@ func (solver solarEclipseSolver) refineMagnitudeHorizonPoint( if math.Abs(delta[2]) > 10.0/1440.0 { delta[2] = math.Copysign(10.0/1440.0, delta[2]) } - longitude = normalizeLongitude(longitude + delta[0]) + longitude = geodata.NormalizeLongitude(longitude + delta[0]) latitude += delta[1] jde += delta[2] } @@ -725,14 +729,14 @@ func (solver solarEclipseSolver) magnitudeCandidateEvaluationAt(jde float64) sol func (solver solarEclipseSolver) localStateContextAt(jde float64) localSolarEclipseStateContext { if solver.localStateContextCache == nil { - return newLocalSolarEclipseStateContextWithOverride(jde, solver.deltaTSeconds, solver.params) + return newLocalSolarEclipseStateContextWithOverride(jde, solver.deltaTSeconds, solver.params, solver.inst) } key := math.Float64bits(jde) if context, ok := solver.localStateContextCache[key]; ok && context.generation == deltaTGenerationValue() { return context } - context := newLocalSolarEclipseStateContextWithOverride(jde, solver.deltaTSeconds, solver.params) + context := newLocalSolarEclipseStateContextWithOverride(jde, solver.deltaTSeconds, solver.params, solver.inst) return storeLocalSolarEclipseStateContext(solver.localStateContextCache, key, context) } @@ -756,19 +760,28 @@ func storeLocalSolarEclipseStateContext( } func (solver solarEclipseSolver) localStateContextCandidateAt(jde float64) localSolarEclipseStateContext { - if solver.localEphemeris == nil { + // 有月缘时坐标架也取插值表;插值表缺失或越界时退回精确上下文。 + if solver.localEphemeris == nil || (solver.params.lunarLimb != nil && solver.limbAxes == nil) { return solver.localStateContextAt(jde) } sun, moon, ok := solver.localEphemeris.equatorialAt(jde) if !ok { return solver.localStateContextAt(jde) } - return localSolarEclipseStateContext{ + context := localSolarEclipseStateContext{ sunXYZ: solarEclipseLLRToXYZ(sun[0], sun[1], sun[2]), moonXYZ: solarEclipseLLRToXYZ(moon[0], moon[1], moon[2]), gst: solver.siderealTimeAt(jde), params: solver.params, } + if solver.params.lunarLimb != nil { + frame, ok := solver.limbAxes.frameAt(jde) + if !ok { + return solver.localStateContextAt(jde) + } + context.limbFrame = &frame + } + return context } func (solver solarEclipseSolver) magnitudeHorizonResidual( @@ -924,7 +937,7 @@ func (solver solarEclipseSolver) magnitudeContourGeographicSeedsAt(jde, magnitud // Reuse the global greatest-eclipse coordinates as the bounded seed for // the local envelope solver instead of treating the missing central axis // as evidence that every magnitude contour is absent. - result := solarEclipse(solver.newMoonJDE, solver.model) + result := solarEclipse(solver.newMoonJDE, solver.model, solver.inst) if !result.HasPartial || result.GreatestEclipse == 0 { return nil } @@ -972,7 +985,7 @@ func (solver solarEclipseSolver) magnitudeContourGeographicSeedsAt(jde, magnitud centerVector[1]*math.Cos(angle) + direction[1]*math.Sin(angle), centerVector[2]*math.Cos(angle) + direction[2]*math.Sin(angle), } - longitude := normalizeLongitude(math.Atan2(pointVector[1], pointVector[0]) / rad) + longitude := geodata.NormalizeLongitude(math.Atan2(pointVector[1], pointVector[0]) / rad) latitude := math.Asin(math.Max(-1, math.Min(1, pointVector[2]))) / rad state := evaluation.center.stateAt(longitude*rad, latitude*rad, 0) value := solarEclipseMagnitudeAtTarget(state, magnitude) - magnitude @@ -992,7 +1005,7 @@ func (solver solarEclipseSolver) magnitudeContourGeographicSeedsAt(jde, magnitud centerVector[1]*math.Cos(middleAngle) + direction[1]*math.Sin(middleAngle), centerVector[2]*math.Cos(middleAngle) + direction[2]*math.Sin(middleAngle), } - middleLongitude := normalizeLongitude(math.Atan2(middleVector[1], middleVector[0]) / rad) + middleLongitude := geodata.NormalizeLongitude(math.Atan2(middleVector[1], middleVector[0]) / rad) middleLatitude := math.Asin(math.Max(-1, math.Min(1, middleVector[2]))) / rad middleState := evaluation.center.stateAt(middleLongitude*rad, middleLatitude*rad, 0) middleValue := solarEclipseMagnitudeAtTarget(middleState, magnitude) - magnitude @@ -1012,7 +1025,7 @@ func (solver solarEclipseSolver) magnitudeContourGeographicSeedsAt(jde, magnitud centerVector[1]*math.Cos(seedAngle) + direction[1]*math.Sin(seedAngle), centerVector[2]*math.Cos(seedAngle) + direction[2]*math.Sin(seedAngle), } - seedLongitude := normalizeLongitude(math.Atan2(seedVector[1], seedVector[0]) / rad) + seedLongitude := geodata.NormalizeLongitude(math.Atan2(seedVector[1], seedVector[0]) / rad) seedLatitude := math.Asin(math.Max(-1, math.Min(1, seedVector[2]))) / rad seedLongitude, seedLatitude, refined := solver.refineMagnitudeEnvelopePoint( magnitude, seedLongitude, seedLatitude, evaluation, @@ -1160,7 +1173,7 @@ func (solver solarEclipseSolver) magnitudeContourPointAt( return SolarEclipsePathPoint{}, false } longitude, latitude := solarEclipseIntersectionGeodetic(intersection, axis) - sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst) + sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst) return SolarEclipsePathPoint{ JDE: jde, Longitude: longitude, diff --git a/basic/solar_eclipse_magnitude_interp_test.go b/basic/solar_eclipse_magnitude_interp_test.go new file mode 100644 index 0000000..5a18053 --- /dev/null +++ b/basic/solar_eclipse_magnitude_interp_test.go @@ -0,0 +1,84 @@ +package basic + +import ( + "math" + "testing" +) + +// 等值线追踪走插值星历,但每个输出点都必须满足精确星历下的食分 / contour tracing uses the +// interpolator, yet every output point must satisfy the target magnitude under exact ephemerides. +func TestSolarEclipseMagnitudeContoursExactAtOutputPoints(t *testing.T) { + cases := []struct { + name string + year, month int + day float64 + deltaTSeconds float64 + }{ + {"2023-04-20 hybrid", 2023, 4, 20, 0}, + {"2024-04-08 total", 2024, 4, 8, 0}, + {"2022-10-25 partial", 2022, 10, 25, 0}, + {"2027-08-02 total explicit ΔT", 2027, 8, 2, 75}, + } + magnitudes := []float64{0.2, 0.5, 0.8} + for _, tc := range cases { + seed := JDCalc(tc.year, tc.month, tc.day) + result := SolarEclipsePartialFootprints(seed, SolarEclipsePartialFootprintOptions{ + StepDays: 5.0 / 1440, BoundaryPoints: 48, MagnitudeValues: magnitudes, + DeltaTSeconds: tc.deltaTSeconds, + }) + solver := newSolarEclipseSolverWithOptions(solarEclipseAnchorNewMoon(seed), SolarEclipseOptions{ + RadiusModel: SolarEclipseModelNASABulletinSplitK, + }).withDeltaTSeconds(tc.deltaTSeconds) + if len(result.MagnitudeContours) != len(magnitudes) { + t.Fatalf("%s: %d contours, want %d", tc.name, len(result.MagnitudeContours), len(magnitudes)) + } + for _, contour := range result.MagnitudeContours { + points := 0 + for _, segment := range contour.Segments { + for _, point := range segment { + // 端点可能由地平线或混合食分支精修得到,只检查追踪内点 / endpoints may come + // from horizon or hybrid refinement; only traced interior points are checked. + if point.SunAltitude < 0.05 { + continue + } + state := solver.localStateContextAt(point.JDE).stateAt(point.Longitude*rad, point.Latitude*rad, 0) + got := solarEclipseMagnitudeAtTarget(state, contour.Magnitude) + if math.IsNaN(got) || math.Abs(got-contour.Magnitude) > 1e-6 { + t.Fatalf("%s m=%.1f point (%.6f, %.6f) at %.8f: exact magnitude %.9f", + tc.name, contour.Magnitude, point.Longitude, point.Latitude, point.JDE, got) + } + points++ + } + } + if points < 20 { + t.Fatalf("%s m=%.1f: only %d interior points checked", tc.name, contour.Magnitude, points) + } + } + } +} + +// 追踪依赖的插值误差在事件 ±4 小时内远低于等值线容差 / interpolation error over ±4 h stays far below contour tolerance. +func TestSolarEclipseLocalEphemerisAngularErrorForContours(t *testing.T) { + center := CalcMoonSHByJDE(JDCalc(2024, 4, 8), 0) + ephemeris := newSolarEclipseLocalEphemeris(center) + maxArcsec := 0.0 + for step := -1440; step <= 1440; step++ { + jde := center + float64(step)*10.0/86400 + sun, moon, ok := ephemeris.equatorialAt(jde) + if !ok { + t.Fatalf("interpolator rejected %+.4f d", jde-center) + } + wantSun, wantMoon := solarEclipseSunMoonEquatorial(jde) + for _, pair := range [][2][3]float64{{sun, wantSun}, {moon, wantMoon}} { + got := solarEclipseLLRToXYZ(pair[0][0], pair[0][1], pair[0][2]) + want := solarEclipseLLRToXYZ(pair[1][0], pair[1][1], pair[1][2]) + cross := [3]float64{got[1]*want[2] - got[2]*want[1], got[2]*want[0] - got[0]*want[2], got[0]*want[1] - got[1]*want[0]} + dot := got[0]*want[0] + got[1]*want[1] + got[2]*want[2] + arcsec := math.Atan2(math.Sqrt(cross[0]*cross[0]+cross[1]*cross[1]+cross[2]*cross[2]), dot) / rad * 3600 + maxArcsec = math.Max(maxArcsec, arcsec) + } + } + if maxArcsec > 1e-3 { + t.Fatalf("max interpolation error %.3e arcsec, want <= 1e-3", maxArcsec) + } +} diff --git a/basic/solar_eclipse_noncentral_band.go b/basic/solar_eclipse_noncentral_band.go index bc18803..948e716 100644 --- a/basic/solar_eclipse_noncentral_band.go +++ b/basic/solar_eclipse_noncentral_band.go @@ -316,7 +316,7 @@ func appendNonCentralBandInterpolatedSegment( fraction := float64(step) / float64(steps) points = append(points, SolarEclipsePathPoint{ JDE: start.JDE + fraction*(end.JDE-start.JDE), - Longitude: normalizeLongitude(start.Longitude + fraction*deltaLongitude), + Longitude: geodata.NormalizeLongitude(start.Longitude + fraction*deltaLongitude), Latitude: start.Latitude + fraction*(end.Latitude-start.Latitude), SunAltitude: start.SunAltitude + fraction*(end.SunAltitude-start.SunAltitude), }) @@ -442,7 +442,7 @@ func (solver solarEclipseSolver) appendRefinedSolarEclipseCentralHorizonSegment( return append(points, end) } jde := (start.JDE + end.JDE) / 2 - longitude := normalizeLongitude( + longitude := geodata.NormalizeLongitude( start.Longitude + math.Remainder(end.Longitude-start.Longitude, 360)/2, ) latitude := (start.Latitude + end.Latitude) / 2 @@ -547,7 +547,7 @@ func normalizeSolarEclipseSphericalCoordinates(longitude, latitude float64) (flo longitude += 180 } if crossedPole { - longitude = normalizeLongitude(longitude) + longitude = geodata.NormalizeLongitude(longitude) } return longitude, latitude } @@ -559,7 +559,7 @@ func (solver solarEclipseSolver) validNonCentralBandState( iterations int, ) (solarEclipseNonCentralBandState, int, bool) { jde := referenceJDE + coordinates[2]/solarEclipseNonCentralBandTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := solver.magnitudeEvaluationAt(jde) state := evaluation.center.stateAt(longitude*rad, latitude*rad, 0) if math.Abs(solarEclipseCentralContactGap(state)) > 1e-6 || @@ -586,7 +586,7 @@ func (solver solarEclipseSolver) nonCentralBandBoundaryJacobian( referenceJDE float64, ) ([2]float64, [2][3]float64, bool) { jde := referenceJDE + coordinates[2]/solarEclipseNonCentralBandTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := solver.magnitudeEvaluationAt(jde) residual, ok := solarEclipseNonCentralBandBoundaryResidualAt(evaluation, longitude, latitude) if !ok { @@ -670,7 +670,7 @@ func (solver solarEclipseSolver) refineNonCentralBandHorizonGapCrossing( for iteration := 0; iteration < 64; iteration++ { jde := (first.JDE + second.JDE) / 2 fraction := (jde - first.JDE) / (second.JDE - first.JDE) - longitude := normalizeLongitude( + longitude := geodata.NormalizeLongitude( first.Longitude + fraction*math.Remainder(second.Longitude-first.Longitude, 360), ) latitude := first.Latitude + fraction*(second.Latitude-first.Latitude) diff --git a/basic/solar_eclipse_partial_limb.go b/basic/solar_eclipse_partial_limb.go new file mode 100644 index 0000000..60916a4 --- /dev/null +++ b/basic/solar_eclipse_partial_limb.go @@ -0,0 +1,165 @@ +package basic + +import "math" + +// solarEclipsePartialLimbHorizonDeg 以下的零食分包络点视为地平线端点,改用日出日落约束求解,与升落曲线在同一点相接。 +const solarEclipsePartialLimbHorizonDeg = 0.05 + +// limbCorrectPartialBoundary 把偏食可见域外边界(分包络与初亏、复圆的升落曲线)平移到月缘外切界线,食甚曲线保持原样。 +func (solver solarEclipseSolver) limbCorrectPartialBoundary( + result *SolarEclipsePartialFootprintsResult, + limb LunarLimb, +) bool { + if limb == nil { + return false + } + applied := false + contours := make([][]SolarEclipsePathPoint, len(result.PartialBandContours)) + for index, contour := range result.PartialBandContours { + corrected, changed := solver.limbCorrectPartialPoints(contour, limb, false) + contours[index] = corrected + applied = applied || changed + } + result.PartialBandContours = contours + + curves := make([]SolarEclipseRiseSetCurve, len(result.RiseSetCurves)) + for index, curve := range result.RiseSetCurves { + curves[index] = curve + if curve.Phase == RiseSetPhaseGreatest { + continue + } + curves[index].Segments = make([][]SolarEclipsePathPoint, len(curve.Segments)) + for segmentIndex, segment := range curve.Segments { + corrected, changed := solver.limbCorrectPartialPoints(segment, limb, true) + curves[index].Segments[segmentIndex] = corrected + applied = applied || changed + } + } + result.RiseSetCurves = curves + return applied +} + +// limbCorrectPartialPoints 逐点平移;求解失败的点保留圆月面位置。horizon 为真表示整条线都在地平线上。 +func (solver solarEclipseSolver) limbCorrectPartialPoints( + points []SolarEclipsePathPoint, + limb LunarLimb, + horizon bool, +) ([]SolarEclipsePathPoint, bool) { + corrected := append([]SolarEclipsePathPoint(nil), points...) + applied := false + for index, point := range points { + if !finite(point.JDE) || point.JDE <= 0 { + continue + } + onHorizon := horizon || math.Abs(point.SunAltitude) < solarEclipsePartialLimbHorizonDeg + // 介于端点与 0.5° 之间的低空点地面梯度过小,平移不可信,保留圆月面点。 + if !onHorizon && math.Abs(point.SunAltitude) < limbLimitHorizonDeg { + continue + } + context := solver.localStateContextAt(point.JDE) + evaluate, ok := solver.partialLimbGap(context, point.JDE, limb) + if !ok { + continue + } + var longitude, latitude float64 + if onHorizon { + longitude, latitude, ok = partialLimbHorizonSolve(point.Longitude, point.Latitude, evaluate, context) + } else { + longitude, latitude, ok = limbLimitSolve(point.Longitude, point.Latitude, evaluate) + } + if !ok { + continue + } + if !limbGroundMoved(point.Longitude, point.Latitude, longitude, latitude) { + continue + } + corrected[index].Longitude, corrected[index].Latitude = longitude, latitude + corrected[index].SunAltitude = context.stateAt(longitude*rad, latitude*rad, 0).sunAltitudeRad / rad + applied = true + } + return corrected, applied +} + +// partialLimbGap 返回某时刻站点处的圆月面外切间隙与月缘要求的间隙(弧度),月缘间隙与站心同一判据。 +func (solver solarEclipseSolver) partialLimbGap( + context localSolarEclipseStateContext, + jde float64, + limb LunarLimb, +) (limbLimitGap, bool) { + limbContext := solver.withLimbParams(limb).localStateContextAt(jde) + if limbContext.limbFrame == nil { + return nil, false + } + return func(longitude, latitude float64) (float64, float64, bool) { + lonRad, latRad := longitude*rad, latitude*rad + state := context.stateAt(lonRad, latRad, 0) + limbState := limbContext.stateAt(lonRad, latRad, 0) + if !limbState.outerLimbApplied { + return 0, 0, false + } + gap := state.movingDiskContactState().externalContactGap() + return gap, gap - limbState.movingDiskContactState().externalContactGap(), true + }, true +} + +// partialLimbHorizonSolve 在固定时刻同时满足月缘外切与太阳高度为零:沿该时刻的晨昏线移动地平线上的界线点。 +func partialLimbHorizonSolve( + longitude, latitude float64, + evaluate limbLimitGap, + context localSolarEclipseStateContext, +) (float64, float64, bool) { + kmPerDegLat := math.Pi / 180 * topocentricEarthRadiusKM + altitudeAt := func(lon, lat float64) float64 { + return context.stateAt(lon*rad, lat*rad, 0).sunAltitudeRad + } + gap0, _, ok := evaluate(longitude, latitude) + if !ok { + return longitude, latitude, false + } + altitude0 := altitudeAt(longitude, latitude) + lon, lat := longitude, latitude + converged := false + for iteration := 0; iteration < limbLimitIterations+2; iteration++ { + kmPerDegLon := kmPerDegLat * math.Cos(lat*rad) + if kmPerDegLon < 1 { + return longitude, latitude, false + } + dLon := limbLimitProbeKM / kmPerDegLon + dLat := limbLimitProbeKM / kmPerDegLat + gap, target, ok := evaluate(lon, lat) + east, _, okEast := evaluate(lon+dLon, lat) + north, _, okNorth := evaluate(lon, lat+dLat) + if !ok || !okEast || !okNorth { + return longitude, latitude, false + } + // 高度残差以原点为零点,与 gap0 同理吸收圆月面采样点本身的地平线求解误差。 + altitude := altitudeAt(lon, lat) - altitude0 + altitudeEast := altitudeAt(lon+dLon, lat) - altitude0 + altitudeNorth := altitudeAt(lon, lat+dLat) - altitude0 + gx, gy := (east-gap)/limbLimitProbeKM, (north-gap)/limbLimitProbeKM + hx, hy := (altitudeEast-altitude)/limbLimitProbeKM, (altitudeNorth-altitude)/limbLimitProbeKM + determinant := gx*hy - gy*hx + if !finite(determinant) || determinant == 0 { + return longitude, latitude, false + } + residualGap, residualAltitude := -(gap - gap0 - target), -altitude + stepX := (residualGap*hy - gy*residualAltitude) / determinant + stepY := (gx*residualAltitude - residualGap*hx) / determinant + lon += stepX / kmPerDegLon + lat += stepY / kmPerDegLat + if math.Abs(stepX) < 1e-4 && math.Abs(stepY) < 1e-4 { + converged = true + break + } + } + shiftKM := math.Hypot(signedAngleDifference(lon, longitude)*kmPerDegLat*math.Cos(latitude*rad), (lat-latitude)*kmPerDegLat) + if !converged || !finite(shiftKM) || shiftKM > limbLimitMaxShiftKM || lat > 90 || lat < -90 { + return longitude, latitude, false + } + if lon > 180 { + lon -= 360 + } else if lon < -180 { + lon += 360 + } + return lon, lat, true +} diff --git a/basic/solar_eclipse_path.go b/basic/solar_eclipse_path.go index 3661ec5..154a02c 100644 --- a/basic/solar_eclipse_path.go +++ b/basic/solar_eclipse_path.go @@ -4,6 +4,8 @@ import ( "math" "sort" "time" + + "b612.me/astro/internal/geodata" ) //哦~我是一颗小地球~~ @@ -19,7 +21,7 @@ const ( solarEclipsePathDurationHorizonToleranceDegrees = 0.1 // 地球表面上的影锥足迹不可能比地球本身更宽。 // No shadow footprint on the Earth can be wider than the Earth itself. - solarEclipsePathMaxPossibleWidthKM = 2 * 6371.0088 + solarEclipsePathMaxPossibleWidthKM = 2 * geodata.EarthMeanRadiusKM solarEclipsePathDuplicateTimeDays = 1e-10 // A short segment can still hide a large directional change at high // latitude. Use a small relative sagitta tolerance so refinement follows @@ -120,6 +122,8 @@ type SolarEclipsePathOptions struct { // most expensive stage of the pipeline and would otherwise be computed twice // by callers that ask for both products. SkipCentralBand bool + // LunarLimb 月缘剖面,nil 为圆月面;修正南北界、带宽、中心带足迹、最长中心食时长与全球始末,中心线不变 / lunar limb profile for limits, widths, band, longest duration and P1/P4. + LunarLimb LunarLimb } // SolarEclipsePathPoint 表示日食路径上的一个地理点。 @@ -173,6 +177,8 @@ type SolarEclipsePathResult struct { // the sampled instantaneous footprints because no analytic envelope // described this event. CentralBandSampled bool + // CentralLimitsLimbApplied 南北界至少一点按月缘剖面平移,带宽随之修正 / the central limits were moved by the lunar limb. + CentralLimitsLimbApplied bool // StepDays 是实际采用的基础时间采样步长,单位为日。 // StepDays is the effective base time step in days. StepDays float64 @@ -224,6 +230,9 @@ type SolarEclipsePartialFootprintOptions struct { // SunRadiusModel 太阳半径口径,零值为标准档。 // SunRadiusModel is the solar radius convention; the zero value is the standard one. SunRadiusModel SolarEclipseSunRadiusModel + // LunarLimb 月缘剖面,nil 为圆月面;修正偏食可见域外边界(零食分包络与初亏/复圆升落曲线), + // 内嵌全球结果与 P1/P4 也与全球日食接口同源地修正;食分线与等时线仍按圆月面 / lunar limb profile for the outer partial boundary, embedded global result and P1/P4. + LunarLimb LunarLimb } // SolarEclipsePartialAreaOptions 是 SolarEclipsePartialFootprintOptions 的兼容别名。 @@ -295,6 +304,8 @@ type SolarEclipsePartialFootprintsResult struct { // topology audit (the 16-segment per-curve and 32-segment total caps, shared // endpoints and monotonic times), so RiseSetCurves is a truncated result. RiseSetTopologyDegraded bool + // PartialBoundaryLimbApplied 表示偏食可见域外边界至少有一点按月缘剖面平移 / the outer partial boundary was moved by the lunar limb. + PartialBoundaryLimbApplied bool // P1-P4 是半影与地球的外切/内切接触点;不存在的内切点保持零值。 // P1-P4 are external/internal penumbral contacts; absent internal contacts remain zero. P1 SolarEclipsePathPoint @@ -371,16 +382,14 @@ func SolarEclipseCentralPath(seedJDE float64, options SolarEclipsePathOptions) S return SolarEclipseCentralPathNASABulletinSplitK(seedJDE, options) } -// SolarEclipseCentralPathIAUSingleK 计算日食中心路径,使用 IAU Single-K 模型。 -// SolarEclipseCentralPathIAUSingleK computes the central path with the IAU Single-K model. -func SolarEclipseCentralPathIAUSingleK(seedJDE float64, options SolarEclipsePathOptions) SolarEclipsePathResult { - return solarEclipseCentralPath(seedJDE, SolarEclipseModelIAUSingleK, options) +// SolarEclipseCentralPathIAUSingleKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseCentralPathIAUSingleKWithInstance(seedJDE float64, options SolarEclipsePathOptions, inst *Instance) SolarEclipsePathResult { + return solarEclipseCentralPath(seedJDE, SolarEclipseModelIAUSingleK, options, inst) } -// SolarEclipseCentralPathNASABulletinSplitK 计算日食中心路径,使用 NASA bulletin Split-K 模型。 -// SolarEclipseCentralPathNASABulletinSplitK computes the central path with the NASA bulletin Split-K model. -func SolarEclipseCentralPathNASABulletinSplitK(seedJDE float64, options SolarEclipsePathOptions) SolarEclipsePathResult { - return solarEclipseCentralPath(seedJDE, SolarEclipseModelNASABulletinSplitK, options) +// SolarEclipseCentralPathNASABulletinSplitKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipseCentralPathNASABulletinSplitKWithInstance(seedJDE float64, options SolarEclipsePathOptions, inst *Instance) SolarEclipsePathResult { + return solarEclipseCentralPath(seedJDE, SolarEclipseModelNASABulletinSplitK, options, inst) } // SolarEclipsePartialFootprints 计算给定近朔时刻附近的日食偏食半影足迹序列,默认使用 NASA bulletin Split-K 模型。 @@ -389,16 +398,14 @@ func SolarEclipsePartialFootprints(seedJDE float64, options SolarEclipsePartialF return SolarEclipsePartialFootprintsNASABulletinSplitK(seedJDE, options) } -// SolarEclipsePartialFootprintsIAUSingleK 计算日食偏食半影足迹序列,使用 IAU Single-K 模型。 -// SolarEclipsePartialFootprintsIAUSingleK computes penumbral footprint samples with the IAU Single-K model. -func SolarEclipsePartialFootprintsIAUSingleK(seedJDE float64, options SolarEclipsePartialFootprintOptions) SolarEclipsePartialFootprintsResult { - return solarEclipsePartialFootprints(seedJDE, SolarEclipseModelIAUSingleK, options) +// SolarEclipsePartialFootprintsIAUSingleKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipsePartialFootprintsIAUSingleKWithInstance(seedJDE float64, options SolarEclipsePartialFootprintOptions, inst *Instance) SolarEclipsePartialFootprintsResult { + return solarEclipsePartialFootprints(seedJDE, SolarEclipseModelIAUSingleK, options, inst) } -// SolarEclipsePartialFootprintsNASABulletinSplitK 计算日食偏食半影足迹序列,使用 NASA bulletin Split-K 模型。 -// SolarEclipsePartialFootprintsNASABulletinSplitK computes penumbral footprint samples with the NASA bulletin Split-K model. -func SolarEclipsePartialFootprintsNASABulletinSplitK(seedJDE float64, options SolarEclipsePartialFootprintOptions) SolarEclipsePartialFootprintsResult { - return solarEclipsePartialFootprints(seedJDE, SolarEclipseModelNASABulletinSplitK, options) +// SolarEclipsePartialFootprintsNASABulletinSplitKWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SolarEclipsePartialFootprintsNASABulletinSplitKWithInstance(seedJDE float64, options SolarEclipsePartialFootprintOptions, inst *Instance) SolarEclipsePartialFootprintsResult { + return solarEclipsePartialFootprints(seedJDE, SolarEclipseModelNASABulletinSplitK, options, inst) } // SolarEclipsePartialArea 计算日食偏食半影足迹序列,是 SolarEclipsePartialFootprints 的兼容包装。 @@ -419,13 +426,15 @@ func SolarEclipsePartialAreaNASABulletinSplitK(seedJDE float64, options SolarEcl return SolarEclipsePartialFootprintsNASABulletinSplitK(seedJDE, options) } -func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, options SolarEclipsePathOptions) SolarEclipsePathResult { +func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, options SolarEclipsePathOptions, instances ...*Instance) SolarEclipsePathResult { + inst := instanceValue(instances) + options = normalizeSolarEclipsePathOptions(options) - newMoonJDE := CalcMoonSHByJDE(seedJDE, 0) + newMoonJDE := solarEclipseAnchorNewMoon(seedJDE, inst) solver := newSolarEclipseSolverWithOptions(newMoonJDE, SolarEclipseOptions{ RadiusModel: model, SunRadiusModel: options.SunRadiusModel, - }).withDeltaTSeconds(options.DeltaTSeconds) + }, inst).withDeltaTSeconds(options.DeltaTSeconds) result := solver.eclipseResult() path := SolarEclipsePathResult{ Eclipse: result, @@ -447,7 +456,7 @@ func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, opt result.GreatestEclipse, result.GreatestLongitude, result.GreatestLatitude, - solver.besselAxisAt(result.GreatestEclipse).gst, + solver.besselAxisAt(result.GreatestEclipse).gst, inst, ) / rad, } } @@ -486,14 +495,33 @@ func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, opt solver.setCenterLinePairWidths( centerLine, result.Centrality, northernLimit, southernLimit, pairedLimits, ) + limb := options.LunarLimb + if limb != nil { + path.Eclipse = solver.withLunarLimb(path.Eclipse, limb) + greatestIndex, circularWidth := solarEclipseNearestCenterWidth(centerLine, result.GreatestEclipse) + northernLimb, northWidthApplied := solver.limbCorrectCentralLimits(northernLimit, limb) + southernLimb, southWidthApplied := solver.limbCorrectCentralLimits(southernLimit, limb) + path.CentralLimitsLimbApplied = northWidthApplied || southWidthApplied + solver.setCenterLinePairWidths(centerLine, result.Centrality, northernLimb, southernLimb, pairedLimits) + // 目录带宽沿用解析口径,只叠加食甚附近成对横截面上的月缘增量。 + if greatestIndex >= 0 && circularWidth > 0 && centerLine[greatestIndex].WidthKM > 0 { + delta := centerLine[greatestIndex].WidthKM - circularWidth + path.Eclipse.PathWidthKM += delta + path.Greatest.WidthKM += delta + } + } if result.Centrality == SolarEclipseCentralTwoLimits { // A one-limit event has no paired cross-section at all, so every sample // keeps the 0 that the catalogues also print for it. setSolarEclipseCentralContactWidths(centerLine, path.NorthernLimit, path.SouthernLimit) } path.CenterLine = centerLine + durationSolver := solver + if limb != nil { + durationSolver = solver.withLimbParams(limb) + } path.MaxCentralDurationDays, path.MaxCentralDurationLongitude, path.MaxCentralDurationLatitude = - solver.centralPathMaxCentralDuration(centerLine) + durationSolver.centralPathMaxCentralDuration(centerLine) // A two-limit central eclipse starts and ends at the external umbral // contacts, where the northern and southern limits meet. The center line // begins later because its Earth-axis intersection is undefined before the @@ -523,6 +551,10 @@ func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, opt result.CentralEndOnEarth, solarEclipseCentralLimitTargetSpacingKM, ) + var northApplied, southApplied bool + path.NorthernLimit, northApplied = solver.limbCorrectCentralLimits(path.NorthernLimit, limb) + path.SouthernLimit, southApplied = solver.limbCorrectCentralLimits(path.SouthernLimit, limb) + path.CentralLimitsLimbApplied = path.CentralLimitsLimbApplied || northApplied || southApplied path.PathWidthDefined = result.Centrality == SolarEclipseCentralTwoLimits && len(path.NorthernLimit) > 0 && len(path.SouthernLimit) > 0 if !path.PathWidthDefined { @@ -542,10 +574,29 @@ func solarEclipseCentralPath(seedJDE float64, model SolarEclipseRadiusModel, opt StepDays: options.StepDays, BoundaryPoints: 96, DisableRiseSetCurves: true, })) path.CentralBandSegments = band.CentralBandSegments + if limb != nil { + for index, segment := range path.CentralBandSegments { + path.CentralBandSegments[index], _ = solver.limbCorrectCentralLimits(segment, limb) + } + } path.CentralBandSampled = band.CentralBandSampled return path } +// solarEclipseNearestCenterWidth 返回最接近 jde 的中心线样本序号与其带宽,没有样本时序号为 −1。 +func solarEclipseNearestCenterWidth(centerLine []SolarEclipsePathPoint, jde float64) (int, float64) { + best, bestDelta := -1, math.Inf(1) + for index, point := range centerLine { + if delta := math.Abs(point.JDE - jde); delta < bestDelta { + best, bestDelta = index, delta + } + } + if best < 0 { + return -1, 0 + } + return best, centerLine[best].WidthKM +} + func (solver solarEclipseSolver) attachCentralMagnitudeOneTransitions( points []SolarEclipsePathPoint, ) []SolarEclipsePathPoint { @@ -581,13 +632,14 @@ func normalizeSolarEclipsePathOptions(options SolarEclipsePathOptions) SolarEcli func solarEclipsePartialFootprints( seedJDE float64, model SolarEclipseRadiusModel, - options SolarEclipsePartialFootprintOptions, -) SolarEclipsePartialFootprintsResult { + options SolarEclipsePartialFootprintOptions, instances ...*Instance) SolarEclipsePartialFootprintsResult { + inst := instanceValue(instances) + return solarEclipsePartialFootprintsWithResult( seedJDE, model, options, solarEclipseWithDeltaT(seedJDE, SolarEclipseOptions{ RadiusModel: model, SunRadiusModel: options.SunRadiusModel, - }, options.DeltaTSeconds), + }, options.DeltaTSeconds, inst), inst, ) } @@ -595,8 +647,9 @@ func solarEclipsePartialFootprintsWithResult( seedJDE float64, model SolarEclipseRadiusModel, options SolarEclipsePartialFootprintOptions, - result SolarEclipseResult, -) SolarEclipsePartialFootprintsResult { + result SolarEclipseResult, instances ...*Instance) SolarEclipsePartialFootprintsResult { + inst := instanceValue(instances) + options = normalizeSolarEclipsePartialFootprintOptions(options) footprintsResult := SolarEclipsePartialFootprintsResult{ Eclipse: result, @@ -608,20 +661,28 @@ func solarEclipsePartialFootprintsWithResult( return footprintsResult } - newMoonJDE := CalcMoonSHByJDE(seedJDE, 0) + newMoonJDE := solarEclipseAnchorNewMoon(seedJDE, inst) solver := newSolarEclipseSolverWithOptions(newMoonJDE, SolarEclipseOptions{ RadiusModel: model, SunRadiusModel: options.SunRadiusModel, - }).withDeltaTSeconds(options.DeltaTSeconds) + }, inst).withDeltaTSeconds(options.DeltaTSeconds) footprintsResult, totalMagnitudeOneSegments := solver.centralBandWithResult(result, options) + // 有月缘时先求全球月缘结果:偏食足迹与食甚等时线必须落在最终报告的 P1–P4 之内。 + global, window := result, result + if options.LunarLimb != nil { + global = solver.withLunarLimb(result, options.LunarLimb) + if global.PartialBeginOnEarth < result.GreatestEclipse && result.GreatestEclipse < global.PartialEndOnEarth { + window = global + } + } // Partial and central-shadow sweeps used to enforce the point budget // independently. A high-resolution request could therefore allocate // nearly two budgets for each sweep before the caller saw the result. // Reserve the already-built central-band samples and share the remaining // budget between the main time series before either sweep is generated. partialTimes, _ := solarEclipseMovingDiskEngine().sampleTimes( - result.PartialBeginOnEarth, - result.PartialEndOnEarth, + window.PartialBeginOnEarth, + window.PartialEndOnEarth, result.GreatestEclipse, options.StepDays, ) @@ -654,9 +715,12 @@ func solarEclipsePartialFootprintsWithResult( false, ) } + if options.LunarLimb != nil && !options.DisableRiseSetCurves { + footprintsResult.PartialBoundaryLimbApplied = solver.limbCorrectPartialBoundary(&footprintsResult, options.LunarLimb) + } footprints, stepDays, effectiveBoundaryPoints := solver.partialFootprints( - result.PartialBeginOnEarth, - result.PartialEndOnEarth, + window.PartialBeginOnEarth, + window.PartialEndOnEarth, result.GreatestEclipse, options, ) @@ -694,7 +758,7 @@ func solarEclipsePartialFootprintsWithResult( greatestTimeOptions := options if len(greatestTimeOptions.GreatestTimeValues) == 0 && greatestTimeOptions.GreatestTimeStep > 0 { levels := greatestTimeContourAlignedLevels( - result.PartialBeginOnEarth, result.PartialEndOnEarth, + window.PartialBeginOnEarth, window.PartialEndOnEarth, greatestTimeOptions.GreatestTimeStep, greatestTimeContourMaxLevels, ) greatestTimeOptions.GreatestTimeValues = make([]float64, 0, len(levels)) @@ -703,13 +767,36 @@ func solarEclipsePartialFootprintsWithResult( } } footprintsResult.GreatestTimeContours = solver.greatestTimeContours( - result.PartialBeginOnEarth, - result.PartialEndOnEarth, + window.PartialBeginOnEarth, + window.PartialEndOnEarth, greatestTimeOptions, ) + if options.LunarLimb != nil { + applyFootprintsGlobalLimb(&footprintsResult, global) + } return footprintsResult } +// applyFootprintsGlobalLimb 让足迹里的全球结果与 P1/P4 和全球日食接口同源:换上月缘修正后的偏食始末、 +// 食甚点中心食时长与接触点位置;偏食足迹与食甚等时线须已按同一时间窗采样。 +func applyFootprintsGlobalLimb(footprintsResult *SolarEclipsePartialFootprintsResult, global SolarEclipseResult) { + footprintsResult.Eclipse = global + if !global.LunarLimbApplied { + return + } + // 时刻与位置必须同源:月缘解给出接触点时一并替换,否则保留整套圆月面接触点。 + if footprintsResult.P1.JDE != 0 && global.PartialBeginContact.Defined { + footprintsResult.P1.JDE = global.PartialBeginOnEarth + footprintsResult.P1.Longitude = global.PartialBeginContact.Longitude + footprintsResult.P1.Latitude = global.PartialBeginContact.Latitude + } + if footprintsResult.P4.JDE != 0 && global.PartialEndContact.Defined { + footprintsResult.P4.JDE = global.PartialEndOnEarth + footprintsResult.P4.Longitude = global.PartialEndContact.Longitude + footprintsResult.P4.Latitude = global.PartialEndContact.Latitude + } +} + func (solver solarEclipseSolver) centralBandWithResult( result SolarEclipseResult, options SolarEclipsePartialFootprintOptions, @@ -1337,6 +1424,8 @@ func (solver solarEclipseSolver) centralPathMaxCentralDuration( centerLine []SolarEclipsePathPoint, ) (float64, float64, float64) { bestDays, bestLongitude, bestLatitude := 0.0, 0.0, 0.0 + // 局部星历只建一次;逐点各建一次会让整段扫描的星历开销翻上百倍。 + solver = solver.withLocalEphemeris() for _, point := range centerLine { if point.SunAltitude < -solarEclipsePathDurationHorizonToleranceDegrees { continue diff --git a/basic/solar_eclipse_path_geometry.go b/basic/solar_eclipse_path_geometry.go index ef4b7a0..3a437f9 100644 --- a/basic/solar_eclipse_path_geometry.go +++ b/basic/solar_eclipse_path_geometry.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) func uniqueSolarEclipsePathTimes(times []float64) []float64 { return movingDiskUniqueTimes(times) @@ -229,12 +233,12 @@ func (solver solarEclipseSolver) centralPathPointAt(jde float64) (SolarEclipsePa } longitude, latitude := solarEclipseIntersectionGeodetic(intersection, axis) - sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst) + sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst) radii := solver.shadowRadiiAt(moon[2] - intersection.r2) widthKM := 0.0 if math.Abs(math.Sin(sunAltitudeRad)) > 1e-12 { - widthKM = math.Abs(2*radii.umbraRadius*solarEclipseEarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitudeRad)) + widthKM = math.Abs(2*radii.umbraRadius*geodata.EarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitudeRad)) } return SolarEclipsePathPoint{ @@ -364,13 +368,13 @@ func (solver solarEclipseSolver) centralPathLimitsAtAlong( if fraction <= 0 || fraction >= 1 { return } - longitude := normalizeLongitude(first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)*fraction) + longitude := geodata.NormalizeLongitude(first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)*fraction) latitudeValue := first.Latitude + (second.Latitude-first.Latitude)*fraction candidates = append(candidates, SolarEclipsePathPoint{ JDE: center.JDE, Longitude: longitude, Latitude: latitudeValue, - SunAltitude: solarEclipseSunAltitudeAtGreatest(center.JDE, longitude, latitudeValue, axis.gst) / rad, + SunAltitude: solarEclipseSunAltitudeAtGreatest(center.JDE, longitude, latitudeValue, axis.gst, solver.inst) / rad, WidthKM: center.WidthKM, }) } @@ -726,7 +730,7 @@ func (solver solarEclipseSolver) shadowContactPointAt( return SolarEclipsePathPoint{}, false } longitude, latitude := solarEclipseIntersectionGeodetic(intersection, axis) - sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst) + sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst) return SolarEclipsePathPoint{ JDE: jde, Longitude: longitude, @@ -1317,7 +1321,7 @@ func solarEclipsePathSphericalInterpolate( firstWeight*firstVector[2] + secondWeight*secondVector[2], } coordinates := solarEclipseXYZToLLR(vector[0], vector[1], vector[2]) - longitude := solarEclipseNormalizeSignedRadians(coordinates[0]) / rad + longitude := normalizeSignedRadians(coordinates[0]) / rad return SolarEclipsePathPoint{ JDE: first.JDE + fraction*(second.JDE-first.JDE), Longitude: longitude, @@ -1328,14 +1332,5 @@ func solarEclipsePathSphericalInterpolate( } func solarEclipsePathDistanceKM(a, b SolarEclipsePathPoint) float64 { - lat1 := a.Latitude * rad - lat2 := b.Latitude * rad - dlat := lat2 - lat1 - dlon := solarEclipseNormalizeSignedRadians((b.Longitude - a.Longitude) * rad) - h := math.Sin(dlat/2)*math.Sin(dlat/2) + - math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlon/2)*math.Sin(dlon/2) - if h > 1 { - h = 1 - } - return 2 * solarEclipseEarthEquatorialRadiusKM * math.Asin(math.Sqrt(h)) + return geodata.EarthEquatorialRadiusKM * geodata.CentralAngle(a.Longitude, a.Latitude, b.Longitude, b.Latitude) } diff --git a/basic/solar_eclipse_path_test.go b/basic/solar_eclipse_path_test.go index b54d425..42003c9 100644 --- a/basic/solar_eclipse_path_test.go +++ b/basic/solar_eclipse_path_test.go @@ -5,6 +5,8 @@ import ( "math" "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestSolarEclipseCentralPathMatchesGlobalGreatest(t *testing.T) { @@ -172,6 +174,10 @@ func TestSolarEclipseNonCentralGreatestHorizonFoldsRemainTimedSegments(t *testin } { date := date t.Run(fmt.Sprintf("%04d-%02d-%02d", date[0], date[1], date[2]), func(t *testing.T) { + if date[0] == 1967 { + // 该日的折返只在旧模型几何下出现,用来覆盖拆段分支。 + ephemtest.PinLegacy(t) + } result := SolarEclipsePartialFootprints(JDCalc(date[0], date[1], float64(date[2])), SolarEclipsePartialFootprintOptions{ StepDays: 2.0 / 1440.0, BoundaryPoints: 24, RiseSetStepDays: 2.0 / 1440.0, }) diff --git a/basic/solar_eclipse_path_width_contract_test.go b/basic/solar_eclipse_path_width_contract_test.go index d01afbc..ff1d1ae 100644 --- a/basic/solar_eclipse_path_width_contract_test.go +++ b/basic/solar_eclipse_path_width_contract_test.go @@ -3,6 +3,8 @@ package basic import ( "math" "testing" + + "b612.me/astro/internal/ephem" ) // 带宽契约:解析式 2r/|sin(太阳高度)| 只在南北两限都存在时有定义,单侧极限事件置 0 并标记未定义。 @@ -13,13 +15,14 @@ func TestSolarEclipsePathWidthRequiresBothLimits(t *testing.T) { date [3]int defined bool widthKM float64 + semiKM float64 }{ {name: "-1404-01-07 single-sided limit without limits", date: [3]int{-1404, 1, 7}}, {name: "2003-05-31 single-sided limit with limit lines", date: [3]int{2003, 5, 31}}, {name: "1874-10-10 single-sided limit with limit lines", date: [3]int{1874, 10, 10}}, - {name: "2024-04-08 total", date: [3]int{2024, 4, 8}, defined: true, widthKM: 198.6161}, - {name: "2010-01-15 annular", date: [3]int{2010, 1, 15}, defined: true, widthKM: 335.0298}, - {name: "4862-09-28 annular", date: [3]int{4862, 9, 28}, defined: true, widthKM: 259.4316}, + {name: "2024-04-08 total", date: [3]int{2024, 4, 8}, defined: true, widthKM: 198.6161, semiKM: 198.6214}, + {name: "2010-01-15 annular", date: [3]int{2010, 1, 15}, defined: true, widthKM: 335.0298, semiKM: 335.0272}, + {name: "4862-09-28 annular", date: [3]int{4862, 9, 28}, defined: true, widthKM: 259.4316, semiKM: 193.8401}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -43,8 +46,14 @@ func TestSolarEclipsePathWidthRequiresBothLimits(t *testing.T) { t.Fatalf("undefined width leaks: eclipse=%.4f greatest=%.4f info=%.4f", path.Eclipse.PathWidthKM, path.Greatest.WidthKM, info.PathWidthKM) } - } else if math.Abs(path.Eclipse.PathWidthKM-tc.widthKM) > 0.01 { - t.Fatalf("path width %.4f want %.4f", path.Eclipse.PathWidthKM, tc.widthKM) + } else { + want := tc.widthKM + if ephem.Active() == ephem.Semianalytic { + want = tc.semiKM + } + if math.Abs(path.Eclipse.PathWidthKM-want) > 0.01 { + t.Fatalf("path width %.4f want %.4f", path.Eclipse.PathWidthKM, want) + } } if path.Eclipse.PathWidthKM > 0 && (len(path.NorthernLimit) == 0 || len(path.SouthernLimit) == 0) { t.Fatalf("width %.4f km with limits %d/%d", path.Eclipse.PathWidthKM, diff --git a/basic/solar_eclipse_review_fixes_test.go b/basic/solar_eclipse_review_fixes_test.go index 3a5baaf..44a316b 100644 --- a/basic/solar_eclipse_review_fixes_test.go +++ b/basic/solar_eclipse_review_fixes_test.go @@ -3,6 +3,8 @@ package basic import ( "math" "testing" + + "b612.me/astro/internal/geodata" ) // 采样并集的顶点必须保留自己的时间:平滑把顶点移了位置,时间场必须跟着同一个滤波器走, @@ -218,7 +220,7 @@ func solarEclipseRingCenterDistanceKM( return math.Inf(1) } return solarEclipsePathDistanceKM(SolarEclipsePathPoint{ - Longitude: normalizeLongitude(longitude / float64(points)), + Longitude: geodata.NormalizeLongitude(longitude / float64(points)), Latitude: latitude / float64(points), }, reference) } diff --git a/basic/solar_eclipse_rise_set.go b/basic/solar_eclipse_rise_set.go index e8013a2..d5bd49b 100644 --- a/basic/solar_eclipse_rise_set.go +++ b/basic/solar_eclipse_rise_set.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) func solarEclipsePathSampleTimes(startJDE, endJDE, greatestJDE, requestedStepDays float64) ([]float64, float64) { @@ -446,7 +448,7 @@ func (solver solarEclipseSolver) riseSetPointsAtEvaluation( sun := solarEclipseXYZToLLR( evaluation.center.sunXYZ[0], evaluation.center.sunXYZ[1], evaluation.center.sunXYZ[2], ) - centerLongitude := normalizeLongitude((sun[0] - evaluation.center.gst) / rad) + centerLongitude := geodata.NormalizeLongitude((sun[0] - evaluation.center.gst) / rad) centerLatitude := sun[1] / rad appendRoots := func(greatest bool) { diff --git a/basic/solar_eclipse_rise_set_arc.go b/basic/solar_eclipse_rise_set_arc.go index 5fca1b5..41dc9ca 100644 --- a/basic/solar_eclipse_rise_set_arc.go +++ b/basic/solar_eclipse_rise_set_arc.go @@ -3,6 +3,8 @@ package basic import ( "math" "sort" + + "b612.me/astro/internal/geodata" ) const ( @@ -261,7 +263,12 @@ func (solver solarEclipseSolver) closeSolarEclipseRiseSetArcFolds( if len(segment) < 2 { continue } - for _, pointIndex := range []int{0, len(segment) - 1} { + for _, last := range []bool{false, true} { + // 加密首边会改变段长,末点下标必须取当前段。 + pointIndex := 0 + if last { + pointIndex = len(curve.Segments[segmentIndex]) - 1 + } endpoint := curve.Segments[segmentIndex][pointIndex] if solarEclipseRiseSetArcEndpointShared(curves, curveIndex, segmentIndex, pointIndex) { continue @@ -286,7 +293,9 @@ func (solver solarEclipseSolver) closeSolarEclipseRiseSetArcFolds( solarEclipsePathDistanceKM(fold, endpoint) > maximumFoldDistance { continue } - curve.Segments[segmentIndex][pointIndex] = fold + curve.Segments[segmentIndex] = solver.replaceRiseSetArcEndpoint( + curve.Segments[segmentIndex], last, fold, curve.Phase, curve.Direction, + ) bestJunction := SolarEclipsePathPoint{} bestMetric := math.Inf(1) for _, junction := range junctions { @@ -327,6 +336,30 @@ func (solver solarEclipseSolver) closeSolarEclipseRiseSetArcFolds( } } +// replaceRiseSetArcEndpoint 把段端点移到细化后的折点,新边照常加密而不留长弦。 +func (solver solarEclipseSolver) replaceRiseSetArcEndpoint( + segment []SolarEclipsePathPoint, + last bool, + fold SolarEclipsePathPoint, + phase RiseSetPhase, + direction RiseSetDirection, +) []SolarEclipsePathPoint { + if len(segment) < 2 { + return segment + } + if !last { + edge := solver.appendRefinedRiseSetSegment( + []SolarEclipsePathPoint{fold}, fold, segment[1], phase, direction, 0, + ) + return append(edge, segment[2:]...) + } + neighbor := segment[len(segment)-2] + edge := solver.appendRefinedRiseSetSegment( + []SolarEclipsePathPoint{neighbor}, neighbor, fold, phase, direction, 0, + ) + return append(segment[:len(segment)-2:len(segment)-2], edge...) +} + func solarEclipseRiseSetArcEndpointShared( curves []SolarEclipseRiseSetCurve, curveIndex, segmentIndex, pointIndex int, @@ -724,7 +757,7 @@ func (solver solarEclipseSolver) correctRiseSetArc( for index := range coordinates { coordinates[index] += delta[index] } - coordinates[0] = normalizeLongitude(coordinates[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0]) if coordinates[1] <= -89.999999 || coordinates[1] >= 89.999999 { return solarEclipseRiseSetArcState{}, iteration, false } @@ -744,7 +777,7 @@ func (solver solarEclipseSolver) validRiseSetArcState( iterations int, ) (solarEclipseRiseSetArcState, int, bool) { jde := referenceJDE + coordinates[2]/solarEclipseRiseSetArcTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := solver.magnitudeEvaluationAt(jde) state := evaluation.center.stateAt(longitude*rad, latitude*rad, 0) tangent, ok := solarEclipseMagnitudeArcTangent(jacobian) @@ -766,7 +799,7 @@ func (solver solarEclipseSolver) riseSetArcJacobian( referenceJDE float64, ) ([2]float64, [2][3]float64, bool) { jde := referenceJDE + coordinates[2]/solarEclipseRiseSetArcTimeScale - longitude, latitude := normalizeLongitude(coordinates[0]), coordinates[1] + longitude, latitude := geodata.NormalizeLongitude(coordinates[0]), coordinates[1] evaluation := solver.magnitudeEvaluationAt(jde) residual, ok := solarEclipseRiseSetArcResidualAt(evaluation, longitude, latitude, greatest) if !ok { @@ -933,7 +966,7 @@ func (solver solarEclipseSolver) riseSetArcTransition( if firstAltitudeDerivative*secondAltitudeDerivative <= 0 { if point, ok := solver.refineRiseSetDirectionJunction( (first.point.JDE+second.point.JDE)/2, - normalizeLongitude(first.point.Longitude+math.Remainder(second.point.Longitude-first.point.Longitude, 360)/2), + geodata.NormalizeLongitude(first.point.Longitude+math.Remainder(second.point.Longitude-first.point.Longitude, 360)/2), (first.point.Latitude+second.point.Latitude)/2, greatest, ); ok { @@ -1023,7 +1056,7 @@ func solarEclipseRiseSetArcPointAtTime( fraction := (jd - before.JDE) / (after.JDE - before.JDE) return SolarEclipsePathPoint{ JDE: jd, - Longitude: normalizeLongitude( + Longitude: geodata.NormalizeLongitude( before.Longitude + fraction*math.Remainder(after.Longitude-before.Longitude, 360), ), Latitude: before.Latitude + fraction*(after.Latitude-before.Latitude), diff --git a/basic/solar_eclipse_rise_set_refine.go b/basic/solar_eclipse_rise_set_refine.go index 09f94bd..7de9693 100644 --- a/basic/solar_eclipse_rise_set_refine.go +++ b/basic/solar_eclipse_rise_set_refine.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) func (solver solarEclipseSolver) refineRiseSetPhaseJunction( jde, longitude, latitude float64, @@ -46,7 +50,7 @@ func (solver solarEclipseSolver) refineRiseSetPhaseJunction( if math.Abs(delta[2]) > 5.0/1440.0 { delta[2] = math.Copysign(5.0/1440.0, delta[2]) } - longitude = normalizeLongitude(longitude + delta[0]) + longitude = geodata.NormalizeLongitude(longitude + delta[0]) latitude += delta[1] jde += delta[2] if latitude <= -89.999999 || latitude >= 89.999999 { @@ -407,7 +411,7 @@ func solarEclipseRiseSetHorizonCenter(evaluation solarEclipseRiseSetEvaluation) sun := solarEclipseXYZToLLR( evaluation.center.sunXYZ[0], evaluation.center.sunXYZ[1], evaluation.center.sunXYZ[2], ) - return normalizeLongitude((sun[0] - evaluation.center.gst) / rad), sun[1] / rad + return geodata.NormalizeLongitude((sun[0] - evaluation.center.gst) / rad), sun[1] / rad } func (solver solarEclipseSolver) refineRiseSetFoldPoint( @@ -465,7 +469,7 @@ func (solver solarEclipseSolver) refineRiseSetFoldPoint( for index := range coordinates { coordinates[index] += delta[index] } - coordinates[0] = normalizeLongitude(coordinates[0]) + coordinates[0] = geodata.NormalizeLongitude(coordinates[0]) if coordinates[1] <= -89.999999 || coordinates[1] >= 89.999999 { return SolarEclipsePathPoint{}, false } @@ -565,7 +569,7 @@ func (solver solarEclipseSolver) appendRefinedRiseSetSegment( return append(points, end) } jde := (start.JDE + end.JDE) / 2 - longitude := normalizeLongitude(start.Longitude + math.Remainder(end.Longitude-start.Longitude, 360)/2) + longitude := geodata.NormalizeLongitude(start.Longitude + math.Remainder(end.Longitude-start.Longitude, 360)/2) latitude := (start.Latitude + end.Latitude) / 2 // A comfortably straight candidate needs no new output vertex. Reserve // half the chord budget for prediction error; every inserted vertex still @@ -620,7 +624,7 @@ func solarEclipseRiseSetChordDeviationKM(point, start, end SolarEclipsePathPoint dotSolarEclipse3(solarEclipseRiseSetCross(middle, last), normal) < 0 { return math.Min(solarEclipsePathDistanceKM(point, start), solarEclipsePathDistanceKM(point, end)) } - return solarEclipseEarthEquatorialRadiusKM * math.Asin(math.Min(1, math.Abs(dotSolarEclipse3(middle, normal))/norm)) + return geodata.EarthEquatorialRadiusKM * math.Asin(math.Min(1, math.Abs(dotSolarEclipse3(middle, normal))/norm)) } func solarEclipseRiseSetCross(first, second [3]float64) [3]float64 { diff --git a/basic/solar_eclipse_rise_set_scan_test.go b/basic/solar_eclipse_rise_set_scan_test.go index 13cd724..87cc81b 100644 --- a/basic/solar_eclipse_rise_set_scan_test.go +++ b/basic/solar_eclipse_rise_set_scan_test.go @@ -9,6 +9,9 @@ import ( ) func TestSolarEclipsePathTopologyAcrossSarosAnchors(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } for _, year := range []int{1526, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2526} { events := solarEclipseScanEvents(JDCalc(year, 1, 1), JDCalc(year+1, 1, 1)) if len(events) == 0 { diff --git a/basic/solar_eclipse_rise_set_topology.go b/basic/solar_eclipse_rise_set_topology.go index 61ca084..0a43aa2 100644 --- a/basic/solar_eclipse_rise_set_topology.go +++ b/basic/solar_eclipse_rise_set_topology.go @@ -1,6 +1,10 @@ package basic -import "math" +import ( + "math" + + "b612.me/astro/internal/geodata" +) func (solver solarEclipseSolver) completeRiseSetCurveEndpoints( curves []SolarEclipseRiseSetCurve, @@ -320,7 +324,7 @@ type solarEclipseRiseSetPhaseAttachment struct { func solarEclipseRiseSetMidpoint(first, second SolarEclipsePathPoint) SolarEclipsePathPoint { return SolarEclipsePathPoint{ JDE: (first.JDE + second.JDE) / 2, - Longitude: normalizeLongitude( + Longitude: geodata.NormalizeLongitude( first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)/2, ), Latitude: (first.Latitude + second.Latitude) / 2, @@ -472,7 +476,7 @@ func (solver solarEclipseSolver) completeRiseSetDirectionJunctions( continue } setEndpoint := setEndpoints[bestSetIndex] - longitude := normalizeLongitude( + longitude := geodata.NormalizeLongitude( riseEndpoint.point.Longitude + math.Remainder(setEndpoint.point.Longitude-riseEndpoint.point.Longitude, 360)/2, ) latitude := (riseEndpoint.point.Latitude + setEndpoint.point.Latitude) / 2 @@ -577,7 +581,7 @@ func (solver solarEclipseSolver) refineRiseSetDirectionJunction( if math.Abs(delta[2]) > 5.0/1440.0 { delta[2] = math.Copysign(5.0/1440.0, delta[2]) } - longitude = normalizeLongitude(longitude + delta[0]) + longitude = geodata.NormalizeLongitude(longitude + delta[0]) latitude += delta[1] jde += delta[2] if latitude <= -89.999999 || latitude >= 89.999999 { diff --git a/basic/solar_eclipse_shadow.go b/basic/solar_eclipse_shadow.go index 7cfbb00..ab0692d 100644 --- a/basic/solar_eclipse_shadow.go +++ b/basic/solar_eclipse_shadow.go @@ -1,6 +1,8 @@ package basic -import "math" +import ( + "math" +) // SolarEclipseShadowKind 阴影类型 / shadow kind. type SolarEclipseShadowKind int @@ -125,6 +127,8 @@ func (instant SolarEclipseShadowInstant) Empty() bool { // SolarEclipseShadowSolver 可复用的单时刻求解器;非并发安全,宿主每条 lane 各持一个 / reusable solver, one per lane. type SolarEclipseShadowSolver struct { + inst *Instance + options SolarEclipseShadowSolverOptions anchorSet bool @@ -132,8 +136,8 @@ type SolarEclipseShadowSolver struct { anchorSolver solarEclipseSolver } -// NewSolarEclipseShadowSolver 构造单时刻求解器 / builds a single-instant solver. -func NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver { +// NewSolarEclipseShadowSolverWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NewSolarEclipseShadowSolverWithInstance(options SolarEclipseShadowSolverOptions, inst *Instance) *SolarEclipseShadowSolver { options.Model = normalizeSolarEclipseRadiusModel(options.Model) options.SunRadiusModel = normalizeSolarEclipseSunRadiusModel(options.SunRadiusModel) if options.BoundaryPoints <= 0 { @@ -156,7 +160,7 @@ func NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *Solar if options.TargetSpacingKM < 0 { options.TargetSpacingKM = 0 } - return &SolarEclipseShadowSolver{options: options} + return &SolarEclipseShadowSolver{inst: inst, options: options} } // ShadowAtJDE 给定 TT 时刻的阴影足迹;不在地球上时返回 (零值, false) / footprint at one TT instant. @@ -210,13 +214,13 @@ func (solver *SolarEclipseShadowSolver) solverFor(jdeTT float64) solarEclipseSol if solver.anchorSet && math.Abs(jdeTT-solver.anchorJDE) <= solarEclipseShadowAnchorHalfWindowDays { return solver.anchorSolver } - anchor := CalcMoonSHByJDE(jdeTT, 0) + anchor := solarEclipseAnchorNewMoon(jdeTT, solver.inst) solver.anchorSet = true solver.anchorJDE = anchor solver.anchorSolver = newSolarEclipseSolverWithOptions(anchor, SolarEclipseOptions{ RadiusModel: solver.options.Model, SunRadiusModel: solver.options.SunRadiusModel, - }) + }, solver.inst) return solver.anchorSolver } @@ -228,7 +232,7 @@ func (solver *SolarEclipseShadowSolver) besselGeometryAt( if solver.options.DeltaTSeconds <= 0 { return inner.besselGeometryAt(jdeTT) } - sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdeTT) + sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdeTT, solver.inst) axis := solarEclipseBesselAxisFromEquatorialWithDeltaT( jdeTT, sunEquatorial, moonEquatorial, deltaTSeconds, ) @@ -311,7 +315,7 @@ func (solver *SolarEclipseShadowSolver) StationStateAtJDE( heightKM := heightMeters / 1000 state := localSolarEclipseStateAtWithDeltaT( jdeTT, deltaT, lonDeg*rad, latDeg*rad, heightKM, - solarEclipseModelParams(solver.options.Model, solver.options.SunRadiusModel), + solarEclipseModelParams(solver.options.Model, solver.options.SunRadiusModel), solver.inst, ) contact := state.movingDiskContactState() central := contact.internalContactGap() <= 0 @@ -361,17 +365,19 @@ func SolarEclipseStationStateAtJDE( func localSolarEclipseStateAtWithDeltaT( jdTT, deltaTSeconds, lonRad, latRad, heightKM float64, - params solarEclipseModelParameters, -) localSolarEclipseState { - context := newLocalSolarEclipseStateContextWithDeltaT(jdTT, deltaTSeconds, params) + params solarEclipseModelParameters, instances ...*Instance) localSolarEclipseState { + inst := instanceValue(instances) + + context := newLocalSolarEclipseStateContextWithDeltaT(jdTT, deltaTSeconds, params, inst) return context.stateAt(lonRad, latRad, heightKM) } func newLocalSolarEclipseStateContextWithDeltaT( jdTT, deltaTSeconds float64, - params solarEclipseModelParameters, -) localSolarEclipseStateContext { - sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT) + params solarEclipseModelParameters, instances ...*Instance) localSolarEclipseStateContext { + inst := instanceValue(instances) + + sunEquatorial, moonEquatorial := solarEclipseSunMoonEquatorial(jdTT, inst) utJDE := jdTT - deltaTSeconds/86400 return localSolarEclipseStateContext{ sunXYZ: solarEclipseLLRToXYZ(sunEquatorial[0], sunEquatorial[1], sunEquatorial[2]), diff --git a/basic/solar_terms.go b/basic/solar_terms.go index ee0362f..84f6c45 100644 --- a/basic/solar_terms.go +++ b/basic/solar_terms.go @@ -5,6 +5,9 @@ import "math" func GetMoonLoops(year float64, loop int) []float64 { var start float64 var newMoon, lastNewMoon float64 + if loop <= 0 { + return nil + } moonLoops := make([]float64, loop) if year < 6000 { start = year + 11.00/12.00 + 5.00/30.00/12.00 @@ -12,7 +15,10 @@ func GetMoonLoops(year float64, loop int) []float64 { start = year + 9.00/12.00 + 5.00/30.00/12.00 } i := 1 - for j := 0; j < loop; j++ { + // 重复朔只跳过、不计入,最多容忍与 loop 同量级的重试。 + maxAttempts := 2*loop + 4 + j := 0 + for ; j < loop && i <= maxAttempts; j++ { if year > 3000 { newMoon = TT2UTC(CalcMoonSH(start+float64(i-1)/12.5, 0) + 8.0/24.0) } else { @@ -29,7 +35,7 @@ func GetMoonLoops(year float64, loop int) []float64 { lastNewMoon = moonLoops[j] i++ } - return moonLoops + return moonLoops[:j] } // GetJieqiLoops 返回从该年冬至起连续 loop 个节气时刻(北京时间,按 15° 一步): @@ -55,55 +61,55 @@ func GetJieqiLoops(year, loop int) []float64 { } func GetJQTime(year, angle int) float64 { - // Calculate initial day based on angle parity - var initialDay float64 - if angle%2 == 0 { - initialDay = 18 - } else { - initialDay = 3 - } - - // Calculate temporary factor for month offset - var tempFactor float64 - if angle%10 != 0 { - tempFactor = float64(angle+15) / 30.0 - } else { - tempFactor = float64(angle) / 30.0 - } - - // Calculate initial month, adjusting if超过 12 - initialMonth := 3.0 + tempFactor - if initialMonth > 12.0 { - initialMonth -= 12.0 - } - - // Calculate initial Julian date - initialJD := JDCalc(year, int(initialMonth), initialDay) - - // Set target angle for iteration; if angle is 0, use 360 - targetAngle := float64(angle) - if angle == 0 { - targetAngle = 360.0 - } - - // Newton-Raphson iteration to find precise Julian date - currentJDE := initialJD - var ok bool - currentJDE, ok = eventNewtonRefine(currentJDE, 0.00001, func(previousJD float64) float64 { - errorValue := JQLospec(previousJD, targetAngle) - targetAngle - derivative := (JQLospec(previousJD+0.000005, targetAngle) - JQLospec(previousJD-0.000005, targetAngle)) / 0.00001 - return errorValue / derivative + target := solarTermTarget(angle) + tt, ok := solveSolarTermTT(solarTermSeed(year, angle), target, func(jde float64) float64 { + return JQLospec(jde, target) }) if !ok { return math.NaN() } - - // Convert to UT and return - return TT2UTC(currentJDE) + return TT2UTC(tt) } func JQLospec(jde float64, target float64) float64 { - sunLo := HSunApparentLo(jde) + return wrapSolarLongitude(HSunApparentLo(jde), target) +} + +// 太阳视黄经平均日变化(度/天),只用于迭代步长。 +const solarTermMeanRate = 360 / 365.2422 + +const solarTermToleranceDay = 0.00001 + +// 粗解收敛到该步长(天)即转精确求解。 +const solarTermCoarseToleranceDay = 0.001 + +// solarTermSeed 按节气角度给出所在月份的迭代初值。 +func solarTermSeed(year, angle int) float64 { + initialDay := 18.0 + if angle%2 != 0 { + initialDay = 3 + } + tempFactor := float64(angle) / 30 + if angle%10 != 0 { + tempFactor = float64(angle+15) / 30 + } + initialMonth := 3 + tempFactor + if initialMonth > 12 { + initialMonth -= 12 + } + return JDCalc(year, int(initialMonth), initialDay) +} + +// solarTermTarget 0° 按 360° 求解,与春分前的视黄经展开方向一致。 +func solarTermTarget(angle int) float64 { + if angle == 0 { + return 360 + } + return float64(angle) +} + +// wrapSolarLongitude 把视黄经展开到目标附近,避免 0/360° 跳变。 +func wrapSolarLongitude(sunLo, target float64) float64 { if target >= 345 { if sunLo <= 12 { sunLo += 360 @@ -115,3 +121,73 @@ func JQLospec(jde float64, target float64) float64 { } return sunLo } + +// 割线解离初值超过该天数时改走原牛顿迭代,以保持原选根结果。 +const solarTermSameRootDays = 19 + +// solveSolarTermTT 先用低精度视黄经逼近,再以精确视黄经割线法收敛,返回 TT。 +func solveSolarTermTT(seed, target float64, precise func(float64) float64) (float64, bool) { + if !isFiniteFloat(seed) { + return math.NaN(), false + } + if coarse, ok := solarTermCoarse(seed, target); ok { + if tt, ok := solarTermSecant(coarse, target, precise); ok && math.Abs(tt-seed) <= solarTermSameRootDays { + return tt, true + } + } + // 古代儒略历初值偏离较大时,原牛顿法可能收敛到相邻年份的根,须保持一致。 + return solarTermNewton(seed, target, precise) +} + +func solarTermNewton(seed, target float64, precise func(float64) float64) (float64, bool) { + const h = solarTermToleranceDay / 2 + return eventNewtonRefine(seed, solarTermToleranceDay, func(x float64) float64 { + residual := precise(x) - target + derivative := (precise(x+h) - precise(x-h)) / solarTermToleranceDay + return residual / derivative + }) +} + +func solarTermCoarse(seed, target float64) (float64, bool) { + x := seed + for i := 0; i < eventNewtonMaxIterations; i++ { + step := (wrapSolarLongitude(SunApparentLo(x), target) - target) / solarTermMeanRate + if !isFiniteFloat(step) { + return math.NaN(), false + } + x -= step + if math.Abs(step) <= solarTermCoarseToleranceDay { + return x, true + } + } + return math.NaN(), false +} + +// solarTermSecant 导数取相邻两次残差,每步只求一次精确视黄经。 +func solarTermSecant(seed, target float64, precise func(float64) float64) (float64, bool) { + x0 := seed + f0 := precise(x0) - target + if !isFiniteFloat(f0) { + return math.NaN(), false + } + x1 := x0 - f0/solarTermMeanRate + for i := 0; i < eventNewtonMaxIterations; i++ { + f1 := precise(x1) - target + if !isFiniteFloat(f1) { + return math.NaN(), false + } + if f1 == f0 { + // 残差不再变化说明已到浮点分辨率;残差仍大则是落在跳变两侧。 + return x1, math.Abs(f1) <= solarTermMeanRate*solarTermToleranceDay + } + x2 := x1 - f1*(x1-x0)/(f1-f0) + if !isFiniteFloat(x2) { + return math.NaN(), false + } + if math.Abs(x2-x1) <= solarTermToleranceDay { + return x2, true + } + x0, f0, x1 = x1, f1, x2 + } + return math.NaN(), false +} diff --git a/basic/solar_terms_bounds_test.go b/basic/solar_terms_bounds_test.go new file mode 100644 index 0000000..f38a0c4 --- /dev/null +++ b/basic/solar_terms_bounds_test.go @@ -0,0 +1,14 @@ +package basic + +import "testing" + +func TestGetMoonLoopsNonPositive(t *testing.T) { + for _, loop := range []int{0, -1} { + if got := GetMoonLoops(2000, loop); len(got) != 0 { + t.Fatalf("GetMoonLoops(2000, %d) = %v, want empty", loop, got) + } + } + if got := GetMoonLoops(2000, 15); len(got) != 15 { + t.Fatalf("GetMoonLoops(2000, 15) len = %d, want 15", len(got)) + } +} diff --git a/basic/solar_terms_ephemeris_instance.go b/basic/solar_terms_ephemeris_instance.go new file mode 100644 index 0000000..17b061a --- /dev/null +++ b/basic/solar_terms_ephemeris_instance.go @@ -0,0 +1,159 @@ +package basic + +import ( + "math" +) + +func solarLongitudeAtTargetWithInstance(jde, target float64, inst *Instance) float64 { + return wrapSolarLongitude(HSunApparentLoWithInstance(jde, inst), target) +} + +func getJQTimeWithInstance(year, angle int, inst *Instance) float64 { + target := solarTermTarget(angle) + tt, ok := solveSolarTermTT(solarTermSeed(year, angle), target, func(jde float64) float64 { + return solarLongitudeAtTargetWithInstance(jde, target, inst) + }) + if !ok { + return math.NaN() + } + return TT2UTC(tt) +} + +// JQLospecWithInstance 指定实例的节气黄经残差 / solar longitude residual with an explicit ephemeris instance. +func JQLospecWithInstance(jde, target float64, inst *Instance) float64 { + return solarLongitudeAtTargetWithInstance(jde, target, inst) +} + +// GetJQTimeWithInstance 指定实例的节气时刻 / solar-term instant with an explicit ephemeris instance. +func GetJQTimeWithInstance(year, angle int, inst *Instance) float64 { + if inst == nil { + return GetJQTime(year, angle) + } + return getJQTimeWithInstance(year, angle, inst) +} + +// GetJieqiLoopsWithInstance 指定实例的连续节气时刻 / consecutive solar-term instants with an explicit ephemeris instance. +func GetJieqiLoopsWithInstance(year, loop int, inst *Instance) []float64 { + if inst == nil { + return GetJieqiLoops(year, loop) + } + if loop <= 0 { + return nil + } + result := make([]float64, loop) + for i := 1; i <= loop; i++ { + angle := 270 + 15*(i-1) + for angle > 360 { + angle -= 360 + } + result[i-1] = getJQTimeWithInstance(year+int(math.Ceil(float64(i-1)/24)), angle, inst) + 8.0/24 + } + return result +} + +// GetMoonLoopsWithInstance 指定实例的连续朔时刻 / consecutive new-moon instants with an explicit ephemeris instance. +func GetMoonLoopsWithInstance(year float64, loop int, inst *Instance) []float64 { + if inst == nil { + return GetMoonLoops(year, loop) + } + if loop <= 0 { + return nil + } + var start float64 + if year < 6000 { + start = year + 11.00/12.00 + 5.00/30.00/12.00 + } else { + start = year + 9.00/12.00 + 5.00/30.00/12.00 + } + result := make([]float64, loop) + var last float64 + for i, j := 1, 0; j < loop; i++ { + var newMoon float64 + if year > 3000 { + newMoon = TT2UTC(CalcMoonSHWithInstance(start+float64(i-1)/12.5, 0, inst) + 8.0/24) + } else { + newMoon = TT2UTC(CalcMoonSWithInstance(start+float64(i-1)/12.5, 0, inst) + 8.0/24) + } + if i != 1 && newMoon == last { + continue + } + result[j] = newMoon + last = newMoon + j++ + } + return result +} + +// GetShiErCiWithInstance 指定实例的十二次 / twelve ecliptic divisions with an explicit ephemeris instance. +func GetShiErCiWithInstance(jde float64, inst *Instance) string { + if inst == nil { + return GetShiErCi(jde) + } + tlo := HSunApparentLoWithInstance(jde, inst) + switch { + case tlo >= 255 && tlo < 285: + return "星纪" + case tlo >= 285 && tlo < 315: + return "玄枵" + case tlo >= 315 && tlo < 345: + return "娵訾" + case tlo >= 345 || tlo < 15: + return "降娄" + case tlo >= 15 && tlo < 45: + return "大梁" + case tlo >= 45 && tlo < 75: + return "实沈" + case tlo >= 75 && tlo < 105: + return "鹑首" + case tlo >= 105 && tlo < 135: + return "鹑火" + case tlo >= 135 && tlo < 165: + return "鹑尾" + case tlo >= 165 && tlo < 195: + return "寿星" + case tlo >= 195 && tlo < 225: + return "大火" + case tlo >= 225 && tlo < 255: + return "析木" + default: + return "" + } +} + +// GetWuHouTimeWithInstance 指定实例的七十二候时刻 / pentad marker instant with an explicit ephemeris instance. +func GetWuHouTimeWithInstance(year, angle int, inst *Instance) float64 { + if inst == nil { + return GetWuHouTime(year, angle) + } + tmp := angle + angle = angle / 15 * 15 + day := 3 + if angle%2 == 0 { + day = 18 + } + timePoint := float64(angle+15) / 30 + if angle%10 == 0 { + timePoint = float64(angle) / 30 + } + month := int(3 + timePoint) + if month > 12 { + month -= 12 + } + jde := JDCalc(year, month, float64(day)) + float64(tmp-angle) + if angle <= 5 { + angle += 360 + } + return TT2UTC(refineSolarTermWithInstance(jde, float64(angle), inst)) +} + +func refineSolarTermWithInstance(jde, angle float64, inst *Instance) float64 { + result, ok := eventNewtonRefine(jde, 0.00001, func(previousJD float64) float64 { + value := solarLongitudeAtTargetWithInstance(previousJD, angle, inst) - angle + derivative := (solarLongitudeAtTargetWithInstance(previousJD+0.000005, angle, inst) - solarLongitudeAtTargetWithInstance(previousJD-0.000005, angle, inst)) / 0.00001 + return value / derivative + }) + if !ok { + return math.NaN() + } + return result +} diff --git a/basic/star_catalog.dat b/basic/star_catalog.dat index a5560f7f79932e41b8319bbc5acdcdd46d8492b4..096aef870da554704d5502931144923629bbe58b 100644 GIT binary patch literal 165083 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 z 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 GFa!YAxIGR4 literal 608381 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

;F-{ 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 u3+7Ao@j~wH4e53D|LeCCmSWZ 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 G+s!KxdC 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

w0hM zPTz$W^i21uPsYKLG+68RVwhpi*Z++^I?L(1f{gZB>K>CTW<3#?CB%-0i(`XPCwZRAa@(7HMV z8~--#5q`6{L=pom&Z>zJ3tlPyaI z(J?YJqm%=KWG{6$9CmxrEcA}On(H~&e*nzF9@JEiXeTd&C4Y<>`lDolcHtrJrYLXh*Yod`9b~q|ayk`! zhSVqGnHLUFiRkQINeonz9tXQv0-iLYFqoF&iAgHJmqJ_KKK9AftM=o{q&V1)pp96B zu4EBfiDzR{slFUzS~k^hh!>xQyVH3( z<4o&>3DKILyI>4G!+D*7o}ZOUOXN#s%3ds`_cxMjt%^5WK2^73OjSF)s|v)9?cr}_ zZ?Q`HNqxEo*-Piw-;n+G_xO%Z$C+TfU4h^Xw>dV=Flx@rJfifqLXxG(iXfF>>{*B6 zZE;MpubYi(6dEqAseSke#q9~Y?kL&UTfpeg(QA|S&InnO_seg|*RC@KWk=7wR%Cu^ z=&7cIIx~K`20gw$X5hV6gnObjER(sg{I!A+u`Tv^=THqCVz#Wu2~<&>1tWJ4e53=z zZ}qwEpq={Z@4JW;H6YK^MSH>bvR{ie%O8`i`Wvm}lGp&Xw7T50If$T3_SH{e1rAr0VtDqOYxCpqaWyd+OBG&z5hIX!VA@!D|PSbvQ1w{X{RGq zaBk)GVYK|OE93dGm-hXwy6a2P9`y>)U_)JDutsh%v4$+v^6aPeft|D?EzHu@mPcEI?NYp-ebus7pl^*@ws&+fExtrg z;o8ty@k7om>oN{T5ut}ZGm+~5t!VkVa^j=A1j|+32*9o+D)CQDjz=5+?y%iuoFI>Gkw+uw048B%l$QzAJm%ojeN+aaezFg zQ}DE2K@=iSC-(f96|Yr)9Y?#gzt-OjtwnR}w5DDl2hEh@;3};i(49Q%h3GyOfOPN- zP1kehfGRbHvP4wfV5 z)`6y)j8uwYu35GsA(|cb$#M_ z_R2EB-PZzJljK7yn^L^U#1Bi?XjccBok_b|5bdmof8{$kLs%(QMd;|@2JG?!M5HQG zPvF{zs;D3(!hEfjmtr-%%G0uuDu4|&4mhbnm<}82X}Ha+>FL|fj&*<5w~c!xQ{ch7;X6)vbxT|Q19Bs zYGeqLHHb&md%x(c&x5IT9jpaIG_RM+1~B`=WwOf;!8h(`|Ao5V8JH6ff#-7#i6gYz zW3~8J$y4f)kHX?$28>I>a*g9(tpyz3OKc2ZXtgxdym~~|-%qlccfwSy?FM4VR*iy$DVuBMBFfyITb(#|jkT?Q!ka=|3OVqxt&YDzJ@a zn_Q|>f4_X>y4u5DAi8j$_Od>~)f`rYo6lK0jf~|2SSiL*-?$ME&-@2%!xTKa1+q4F z>1sxKna$K}#WiKc?+Ux+r`gUush<9w=2E_TV7|s+9u~=b)C!Hx?um_KjiUDK1>aEF z`ILSqYKG6_sh-y}e52npv6HO%hr%qGa`w{AhuaBcez88=j{3d@vc4~ZFX;l!OY^%v zr)lZxTph=ssa<1?uI(za zhMJ8FG)K(i;X1yVC@g#cN5dkG{%D?WGI3wqEd^x1?1i^dpIGJnA~YHrspr?~O54#M zXe^JYE0z6SgVhuUXl&~7UNEx|CPhfvfHPEL7ojxbuoN@ zi{V%K8GXVsVOkg!epJubNKDl@jK){m2fn^sSON2kV>UH{y_Ja4PK{taUo(=9q?&22 z_8C`~uBE=yIM`1)Up=qqpkO~Uz8M|S9!rI44vag0)Sc54?Go>4m$2_(O-9Kj(eDrnc*xy*)e}?uG9y zPc5$vzjWPTeE{=Tnp@_mR?uE{&vR7D9ZPAn?*{X=6+}WY9)&B=D!>q8Zjd`P4}I2H zo7&Y#$*ACM;Q{a>#(busXlu;KTF?Z0*+aIVXklc*o)k;FtHie8H9BK|7k0~TUa<;l z<4}VPu8|A2pialDW${=Qek1HMZNRcl#csk+U?SH)<@s zH(OipQ1lt>Sjo0%eY>*Mz2}jYs7ge$H5t)5Jo9mKh6JHe;AKJYlK+U4;UeL5b6OV!#M=H@wxI%X!UAg9>je6IKu zF#lFI-gFT$6<5j@pCE7L^$hX%R(OKOuT*1(na8v#cGVf3u|@3p z8^Ow$$+t={n|GB^8GMy%i*m7co53AlH<+WjDNGERprD(87;O%Q%y_g~;h^@%Z?x`4 zQcY}3&Nl6p&g&Q*;Hbr3IEs_Zre;x1`a?<9f&};LplkX0e~PP9Xr)px?H#4U@Kas? zU0I_4kVX3pD&=3s;j$`ii&(v^v#ypCvO$(LK;6wuEFD0zlj+{DqI)S3jU2QaK~6(!+AC59=pnSs)3I6D0%9y=sL^djT-U6dg3qTrTj_H z))Py+bueFURqX;}b@dUN4GrOYd{V!;|AUn77(R`c#C|a&ZYbGPGQ8w(C1qmW_=M(C zdHET?#Mji`SK`a+U-Mjarynj>D_qA~2maAMR4A_2XS)ZpTwBMXMpy*DdXCQE=ineH zg&%(o&fGS-&Y<9(;3Kqii%(J9*j5XDur67t?_jSYfVnj z85Cp{T4|>{{3+Hl?54HQjd|i)!NFPui(-BCh1Dd@qEk;RrWfq#UD0|sk4{zUpc zy+>ExNes9-eyXpW8H_l4D*Q%c={ioUBh+Cwb|+8Pf!YHzDIH;dc|BUeZ-nOEVibBh ztH)Y_sdlZB>qD1ny{y;n)mW=D!m>6|R{pKTZ9dRU_*i{DL8J4FzB^j$bFl2tz1lA> zKqb`Kl?GJ*Hi6%$spiEKjMZxG_J_!sz_v+?JC&@4TTa&`m>FWH`p&rtvo?*2&cS`BzT5<6cph~;d*H=0b8C66k!|rX6`nV1 zX6>V1*?5$Hz!hp#SXunOcR+|t(k`|sy6!78m}AWG)7e@zBja7LmAayLrgrIKI6Kcp z$4AfVKBYKkjXW_X{R|a&My1vz#^8$iOi(MX`%4F>lH>|aYR*UOklYkgW+~05cJf~i zN8?>L##!W5v%`yef`(cpmxT!8rE;*9HcrpemG7R=$}Xd|(1%)f^Y302jQLBDMK`|3 z@#nH2g-&sPT7PPP3TJ8ydwX=orxgra=Akbg_QP?tLf`8bjQBfD{fbc|_hAR68~d*j zKCOM$wYR_1Ofj}BO(S9p*?E5TJQmE z@4IwOV?V6b^PcQ$Ya%*Fm>U*E^AQ{aQ*^K9^P`%LXJTKqqKEq65?#HKywBbs_%p%^ ztjhJW;H|;Am^!lt>WLx2h)l26W6^a|%fV}v4ZG3OSgmt-HMJA7qt)#fMWan!W7<(O zXjM3&oq4ssw_M}1Qgfv?`R_B)m}O@}2g?fH65dtYuh4!|H#jS2FVCmnL2`7X@E2!j zmTl1fY<9vY!5K`al505GqUrbt3-vo2?(I3K@+?QQVmTN>vn-C4*Juu#1>~1zMXQs} zLRDiP-rxvOuZ5Ze=7~LyrlT1o=b;TU16<+)SWApyOlmqz)3^6&)lbl_Fizi}rrmWz z{9f01B>XJzZhBZBehpV95{YJsjp2Z7=Bq>BM9;(L#qXcSHDI zV>mZF8?M*bj0sPNUWvN;+>}J^#2o4ZN1+?J2HfaaeDLQ{?VgJc-c0R|OSH?3)$5;Y z{7!4-IeIz@U&El>Ge2UePtp!xEX4XXQ}s=49&K6 zF##&gxa+*I4MxMu!(+O`Lwbs@%8ouh#U@ig{~ktx z+)5hR`jitz?!+>TaTxzUy1do_adQEjlF;#k5USrgambSxwby$Ft_AK?sL%Pd|@ zOvO3?o`DJVrmgg)+F?3`H#Is>Ywh2!|6i^BW-zU(sOR6u>|GAGmvxlw^`;XCO7g}S zYq*iKIUA&G=@ciiHT_o0=8ja(O-zO`-e>R094`|+xG!xMUtjNNH0<03d;96fY?&?>RJbP8;S zpUcsqE&8b*%r4U$ttWGz&5SKjhN=!btXY!WOH1OVTD8@*V#Y@^C>bZ?40a$RcN7gn zv!VSrHtKhb{`C16dD28G7W!Y!rHGGPf>uRYvVf6%;D0lP|Gi4`^&&8;XJQ3T-qNW$ z!_(;No{347_ax)b8U3CF&3jT$ewOOn5R!_Q#sV81Q--QYM~BTuQvvK=1oM+e40RhU zW7cl}P%CMp{=W?JLy8r+{}iQe=&0RVB^*&*hzb4yW2& zsxGVx6sSIQm1dUCmO(`3hmgnX1e)5}l`Q?+b@#bg!<}UPnAfn8dZ7VX74yg1A8rZC zYq|RCknWj|W{>^p-NcyJfY=zVb8C!R@oD_Gem;wL$avK7Ck z3LKzWXfifK3AnL(td;I{8QiLU$hCEa1JYS2=Qz58J-qleU2M zO5Ns5z&Dt^zgRwj8CRW;yOiAH<(iW}sR!4`ht;kRh;nQBM&n(p}w4!cXu&4v6uy!S$@sr(Y>s9)5h}wFU+CDJM z*6Qv6Bh7{SMi-5NvrO$ZH~VT088y^Td%`yChh5+c%%yOUy4jxK1hd0U-1|Ioe$ENz z5yS2Ylgl9e`?oq9FJ^vPWwI5@e{EoJ=}9f(d3u7@;UcOm&cpgns2AJCmYQKZG~-&u z8=~u?FO_xkk9f2GeXT~%-m_H+2QU*{f6-Cj@5zkst5IJfpP?82rWOCrX?3Vs())4qy(m*0CkC%ku z(V9SJgm6A+5E;noXcDz%Nd5rB&lKDfww=o6ijtoE1@0SCrk>X@zd<@odwNX4o&!KDV6yKFawJ;%s7UUM0HNo6CiYs>6BbUHHhm@P7`D8&px~xe90G@$3^h~NRuh? zaX3xwBkscM+YA5P0k!3hcyGzm^2x7~z4uHorsfUV(X+uOZ4R9hV>Bnas-JGt9`(;~ zOJZU$BfKLcJ_$Ff7pBC{@s*Nii6mAJjv^Oe`_XELj!BwVXqG-P8*8RY*o2zk1g%WR2)y4O1TB?`O5!3|DZ2$L z{LRh^er{FIp5zR5TzifB&P)|0>VsU}$=N@%_?HXs>oY6CqAiAtWxT#M zRew)_8jae87n9>jettgpn1#wh0sN-($l6S$5@{|KU&~-DHW$YNs`$IeV|hU9_Il0g zWCpQVW9ZD@BG^_R4mXCI!|iw%)q^<{T#vPo8pWA-b~9kyn5i{tg#)A3ro)P|K;Fs@ zjmAtp@pu#vXK4>SfvwX~r_eRwgRny$hSiT>(x}u3<`S~z^UwI0^HK1#!eR^gB7agl zuhagX15?&Y@{Z2qPXW7XJklFrpR5=061o}FUY(1}|uyc1rQH_}btTc%k%Sbx7?v#zK1gT1=XVm*Chl&;2- zC0!Q(31-~2MB{WiE>tT%Q>(|LSztxUnbaa$^ZEyvevICofw%TE6%#+8>t`;WE$X51 z=+T!WGHsSuSJEAi4~Dcvmqe7m`zN)3eV7t{&%XCVn5L)k`E+G?Uu$MZSSLSteOMbt zgkR)4{j8_ER-ek%2xjOk%!G~1cbG<2a1!X?`8v}lqtr1vy58Am%QAG=4{CmY4nvJC zd1oQ6)?E2GxWefw?c!sx1tw|~=E5Se6wI*^=YQ5}a4q1ovA6uWO<{Hzub#}-$v9h9 z!tFY>vti`xO(k{*5F*YdCAE=U1?38y%Ig36y5rw9dS-0CM=hF4*4*)ugZkTdWaQ1w zm_@F_If`=Hi=NVWysBsaQg{6k7VBNISZq(uO!4kKtv0V9XJ}k=Qa8etz`ManTMfds zw=4B*o5_98Wxei0pK7p9r5(}Mpb@0?gB1qyKyg&Wo^HzN&_*-&nlLub zl&@XY6ze;+7r?iqX!fgEK{n1sL<}DvO^P)JtXfcRA3jMfkgwi7`93R~kK}5?bjPfSGyB5^7f1y)nYJTMdM2 zS*kfQsaZQqtoIz>C)(oAKz4S4t|>x1utp zDH+~8w3>6#=xYoLtU-$Fs5zMq^OH705w?x~Rz+6ux|rmW$bqlgJo<~ZvyRtV9w&>k zFt!El<({k-tbyQ6b(YS^U$s9OS^u*3_+Mg8Dsqx)*zKt6O6J;HvZhXwd$+}HJG>*G z%tiaInbxeckoF?1*WsGmBrelE?@H4~XjZq~%9a9gcm;QRM479W^Sa zwL^a*i}CwtyuEwVs(2mc8FZqq&kT{)8QX$#l=1luVL|#NJ{Q}Q>FcZUyOK!E-uR=g z^tf!8E2%{)M>h6;tg`{=qnoF+557hhGK~GmH5q4auVbOk)+DFIt?;v42*=GO`oI0d z%Rr*91-0_~c$LmfM;gZJ?EY7LCiaWh#W&>z-mCjRto_@p{@wAa1`t)hLU$NMTyd!G zcR2{ZI{03t^fyPT&BgPVc)dpHocg^uzM^ZqCcigNy=3gxL-8hgz^}^J98cYN$Kd*? z)*8Vx@UI(}`yP3BN6^-zm$Nv&t9kpbUURMa!q_+7rB%@zbdR%e7Y1i=u2vg|!OJ%Q zom)qi0#;WZ_1f2<+TgsW$8fi7li_;G_hNb~cV3!RjT8#dE#6TG>oHeH)li>xY}@(R}>9 zNm^HPiKCs71@V^Voo%s4O?mtA`5-EM=w0Q4&E^J<+ez(b|Ze@PX^3>fsx+9~k9qFVZ>sW%``?emaBYY=RDbYt8l(8qYC0 zLCg-Y544r@5#~;vtF>tj-8u1()EfO;=W<2OIb%6mr&vT=vYIofVp*qe)kkx$39O7O zwOct3vtR9g3`Lu!Xx!)0zIxFrW0y2yw$JB(ytJ#zJ5gO=OgNaw(4IaQ8-EH8Z*eI7|DMj=YpBk8){~~ zrSDtIsjtgv~_H)hORQU3ouRP_MFb9dH5%nW;B{8$Cc-cB^oZAUf;&Vz5{{I%9{TdlfVWHIKW+T1(r z(a5cbzj8AwD?Sz4P<8BB&#!Qt^+P#!51QBZ`aXd(Zx8I;36xXr3!AALo}$-sWJ@O* z!#AK1TwA+EN42iDdcvy4y|q);m)}&EoZTOSRj-ZRYOL0})_;xmzjJ!>%Y(Ci3pCfP z(q11F>6_|h;||WE4`plAb@)5sd09(U#a{L3CmNGw;T!e$g<(C&qkOf;T+&Gl|9tG* zTjgE-D_C9eb6M=I;O=w9>agHyROhQ^${%sgW)=CZU*cu*iY|e3aTT7O?>Yk}<6p_+ zEhB$s7TBL;DK3L4$Gjo);9^{wVvJjlk30`+V7~mrr3WAlJn9)F}Qz-c4uqQU>Vh3G#gIku_rNjET&r#TvN>c(x5No?5SQIN9AX z^4y(0NqSX|VIPGlXqr~%>l)c1(F$PJXuUrCGrXv$F4X!uPp5E@U29KfzCO21E#8FX zZ;i7tplWC6bh8zb8O+SNn(CKv=rcM)_o>EioStzr8N4NE7|ljUaRO*wYs>#TR89=j zxOsH1(!J+uZjA~{<%5>iIp2uk1>FbD9p!uaHe=NwSLwdvC2R*{rK7w z-~n~cG(BucWmadvaAYt4Fi6UC@SB_(t9iFbJ+lbT(M2%fEYdrR!N@L*dr_-ghyvzv_>*S9Lo**<`&sJU`EcXT;}a`b zLrch1&Snm8Lx*IEPNFCaZ8JKXuH7_`sUv=8Vm&z3FOw~Dt)AHtrlZk0i5)Q)_OrGi z;L5|um!bDF!a>ke8>x(Qe7Fe;>o@3guJ!Jf(q-zb^T&Ar*K3@hsyqbycBA#Z^;7^? z)EQxh3u9&4!6tXHtfPy_|7_J5ZXhycU#mI1LtTPZ@Ep6W$XqjSr!pR>^Li&x5GsaW z>m#fd)e=9gKl-<}?dxdvIu>W#Nog29E0Tfp9=`=OjXkJ)q!Hsc z_9Gvz-(sD<88DMLw`T?Sic|vHS8S&JK6zK`?K&pi4P_5=KKRTWqHiX>r84CH&XLb^ zWn?XI&c!jA11ytb2mS9)GV@m|^=Ik-SR z>IBoM98815HQ@0D`1i|D)tkWQGjC0@+R!+jvZ+eNAX6I}6jD>~yAlcz^aIGZZ z63swScvG}@WCYLw&2nd7)4;Hnf}z^VDdp9_In>A0m5s1nYkocFpfTE&s4VF#d(Qot zOH{o+UTZdS_zpzq8^cGQ1#_O!>UHoy>*`Z!XrTl!M{Cv39a0PqnPk0fQy8C{fyI^2 z70PL5*P*tu27C_9`LjK>__he!!J^VbYor`Z5XOwQ#4}3-V=qR>Z>Z;E97#lq=ek3`>5#Vxk5=YF72uiuq5>;I3|?>x*q?w1Yu1 z$@hO1KIUnSfvd06;r;e5)Ck3i*1A_)e7s&-Q}&Vipp)1V>}fZ=jUFhmxfad(6m9j& zP(7V1!vo`i9&8hFU zR&z~VDT)41g#B=Bo58UIo=tyh76*Wt{Ee=BK1`GCVf87Jbw66q-<|B1eS_{GExVyi ze?EMSuFC76`DJb>t7AClX=TTuy4qztPgii^P4Eu-Q13T5I2zXs&a;{!$xCX*duJW3 zDA7mz-saeuyrBJA`!nb1-F~pb^g$Q>BE5IMPUViWn7#p>vzMJc4_){3)q_Lz)Q%aR zrxrN^;CkF|HB0(}W9+9@;<{k#st;B__UH2#kg*$}J9;g3Kv8gr?8Oe6nf=l4Fb95P z*p%wPK{^2Dnt^x~ePO431OL}ql{;gLczL`v{x)71KZyNldoQX;Cu?T*CTHtsvJOU) z^R@%g&MxR8I__VYyR-$jY<-0R@K3t_tr09aT|wt|pxSJ+W^Ehw+?CoXuhyrt^;#3H zrdGPTeS|jTv&?S$yYOQChkW}tg?H{ae>x1`=4Ge^&s?NJB7>aiBeLF} zmsK)awo3Jo)DNyA@3yrp!>2x%rNun+8&Eo|LY zt9n!kkH-ZilS-`mTNLlqNE@Tn4;9IM`rFSs#z%yk)Q2s#gSHRV5;GH%zmWRu0qf29-E4|?YlaSnx#}W$v{!3ayg2yp&&h-OGQ1X^4{wDj;jfwz+wgLx=@}=( z=Drv|cY?g`@39lMYPTLm2F&Qiv%0f6pk|{;c&*x9Lq5mDdaBv_%tq{rshR~NG{1kV zz193;h5BwO&{WxpW7Aw!=;rrUb>TBP0tak#4*AGKM7_ObuwX_#$ z<<^rOQlL*7^+JT|C%FM5i;TwIn zBqq^}_MPX!|KhCek9Y&WGD=Id+H1?#+YG~Xll@l3o`J+)qY`$JS@EKH5?7FinhHC(Z0uHi4Kr5UfYv8nv(z4~_@ z?d6wfmyUV@=bemV`B{B+8qa zi05mk{82lT_W&bu5}?z}YH$gEw$KyatsUy+U^NvZRWA*nqLucYTJWISW9;F4_)N~h zvFbf@9T@c`Ama|ePgGhxFqX`k**IK#l!cvf5S~b9o9e-s)C6pfEwgT#-w$d|-LKyh zI+63?f2^vW-9x@^Sxjc>j}Z;q6*qwNJixB*$cSS+m#dGNi0y+ZrCc-Ip6!hj6{N^-`+$PP-6QCoDP}VC? z_Aw)vcdjCgLH5ZH#uHdsw#IT(`cRcoMXrmkRfhK3+OoULqg|I&&??rMT9)igvkYmQU!@*7vaLVs$VDb#&z} zL>%ffH;hDg{N*V8v-ZrllCvF&dgCUo;+-(Knf=e|6vv{uaFcwxx$3)hs6H%$n>vTp zTIuGR>7=K126m(@+Rx(yvX@^~kD3{?F@B`0Ry)B@^=^D9-W@NHhtV-!8n1~D%V+yc z>&2MN!RXtip>|-TjUw|hEY9oBzDoaYoVEUxn3Khwhu)w zqMx3*xxRTiI_~~gc{UfvTC(=1!>PAj*7qIi@7-`RmkAT}&SSE?jUCyh-PR}OeR`Vv z#`VR{@8TgHo`b6l>+`<7a8Yrgcvk8xF-*UY!zkqS}I zHHPz;Ua1-$)QK@$bLd3u1GiRpSTu{(+wJ2Yv>R-RCrX;g#(P|IlEcsN3L z3mS(Wue4xZi4i(i^N6x_)a-4hcfFsrrjju?B$;sA(-*Ez{f$0%TqF9STDl|N6K~fZ zzECrvfGpS&wfG-;#}R;wW4G8_C*X(i8F`(rs_)Iy=-kBNxF~!QUr}GRR4;X;IJz-Bg*I_WW zbRr*Kthqgg*lkUekn&KM@2F2#)VM#Qz79m~`smziNxrT-Xy^vSL37kMU14Cca;UZa zTnE}$-|dA0N=sI)=WlIr^5#|;Pj0D*T=6vGnJx8s*Ma_`UV1`4WVWWP>bLyB+tz!5nUIW(LpO~yMeowoXdF0-gjo?W3 zdN8zKYh|aYXRW_y9jAKQ#XixetP*lXIE`wLE2@kU*&P2S>-5WLrm9-9BM;yUZ;Q7? zs{+;3lkXzBU6b>rG`pekM@gPhV@REMGsjpudPB|7kgZ5$u_~-*=FKsGLt2V2CdnsL zBHuGX4B9!zT-~Km_N(p8-PAzlhu>=TWNQB0lsa3@XR=E>=uw^ETlDQ+FsvBc)k0UF z2HNRDwZ_~YW$?m_z{ePgY7RLoEp3M_-PwU%D6pK=w;StThd9F)XpD?huBskB1Wsr_ zJS0;!wnH^{kE7UN^uQsVh1T{vUn{SN?o^EkLw(H`hRf^h)LMyn1i~wPVxNx+dX$%^CBSbkh8O5RT7+_zB)xkvt5mNZhK~y@^XEuF!s=CKP8&gX#sVx>ENUtWBeILmCD+6 z(zWm2D$nRCwecpMr91Up&O8;OnLbbBv<+`(9K7rc$Oz6PQ#p(J#u2)=+w`mYW)3`j z)|q!5M@FzdpczhG&*;jy1KN>h!iup4Z0{aWV$<~vD^GOSns_zb6HdZ?@rn9vq4u(A zaH+bs@F)59wZeF6y?>;-&kCaDVEUg5QrMAz!W6!Dqt1+@y3#XYYSUeWz^lXZoTtOi zVwZt{!99q!k2Y{8E*=D>XRTVYDbLDkrKvqC*9?a+zY<>3b?A87Q)$9UbO6laDbxv&P_uTl;rzAALOsxvC&`FbfeG8%Gf5Q=`*f={m-bRy z>DqrQqnEH_mrx~m64sV#!PN)l!)dZ|F_uax1t$Mh!jDg-ljH`I^$;cT|M%bj&8YUj z|Np=LOZ-pXFnN{ab&M7WDEuYJF_xh!&(VS+ss;nTd?7o6(KhFZr0uy$=k_j@Umek!LYotMx~{?i%kcRDPcV&+NEsHKMU6v}X2erdmgI zA}i90IFsOB-9<#ejEhG8=TT2%{P!Bh?kCw*`)I4@L$R(|1HK}2V40Ua3k5H$j9SZg zR|?5nlNy)e*bq-Ni|n_(-d1?0&MxMWm&^u}l!11&QHW1adu`O{Wcd)*qWDZdg|dI@ zQZ>|=YNiW`$D1qPdg)dTG;4ZW{l6vERLxNUZ9tO0E9d|_Vo&%YDuJ2qfU4p_X6kyB z?aQD%(VmQtHAm*@oqe$ZHGwD=t*$Oq%sKDY7Tz3lX1Ka= z7^psTz+6K>|0j| z&X`({-7$sU)JxPwX{96nRbNyhJL#P!>J!%@b|zCeQ1*&Fui85Oc11I^-6=ox8u`BM zVty3;h)L%x$w^!xah&Wv~Bi`sjC)JPr%5$-C{DjNM~<5jVr ze88@;sXW$|>JdNPKJD5G*$q8l3mt^dJd`>>XEN*QN&3@27lf;r30;D?WY5llC$O1LBdZ~_)Uy=Py7J^@$7y71bN?S?#k~r_Dtj04}t;xkM^{u;pTOPLB$mQG8=ZZZ1_&A ztAA?hYIX3*oMEk$(iW-@rZ$~QhH_{@9A@3sBJyXxAbZOn$NMx+Bh};AX|+3>VkC{D zi#KX+R0`&@CD_Kw4kW=+M@R0EnP z?x*OJptO3$*~4S{9RMoQ=icw-&78yQ+O5^UUDq-TfGZr0Ca4$2#z*7{T%r;CAXbf! zl>9)<+WExYsM#NtU203EJk>}aYOPnKy5+U-_r#3ELG7jGQET0)KHRK3yjYSK%W!%g z1bgnfpaZbKts(bOGwj!_cI)`g}&ur$)+t z?@qmLmgqe&y|0ctVJ@t%*SCSGD~v`wxth={-?m20L%JU&CUaDor*sz)`EBgcj>Xi+ z4w)an*G%{x`!vb7VXR%6*7rd;ER4Tg1JZsX_5M@gdRfb#W@MQILuVmYt&w}j;YhUA z?pplCtZ(0QF7<{v+Ut6Sx5DqjqnfYRhkfCBt<6@{J{N)S zYesJT1h~b`aHve67H(yHPJcHqhbw>z^y&t^zE1aEp?BBHBAts3ZsocS*hN!OT5PJl z`~jVmPq=0@d=)n6_c^s;y*@F8io2zn2R>t3YhRf`6loH^$UOYOAK?s`sw;k?`%ML3 zI2QEhZt#nx)#7a$&yi$ZCxN~!(02;p+Z+K`<5{)hF0E&C^z1>I_cyvqEuFr{OxiGADFP913H@i14m_+u`y-`@n*9 zD4HX8N~oF`9nQkPQeOM2`4@Mp*QRS{xJ+&6rqjx}{0=$|N`-HA7H-psj@GjdSG zXNH?O`n>r|zk%^*BYxs!7w;nnrQ=qdecvOYZ=m5#5;gihBv z;0#&=-Q|4Ez}hIqWy`0S3p#cgk+_0rj-7AFz;DytbHecGNQ5hd@?aCZKm0@cVf8Qv zZ`o_WRl&xpACKj*ODmv}c#Q1AdiXC_Xf5~9^Y#eVt=uB(xNdk*uPsDZ^n_;BL7vE! zLK{&Wck7>uR%thi<`JEYH>$7hBLd|fcsMw^*PlB0!Md6`*K5`Zv0Bx7PUizKyJT}N zH3?R!a@Oo(_0C#6;!^r_KGldm^XJB@WM<031=t~&g>zZ7*4O3Qg+GCdXg?NthCZF( zj7-xiHKyr4J&Eh9lNy7$;7T(%7f<7%mIWg?jB4Y3#2U`&=?>`kC@O#D$a<3+P77wu;x<)>m&P3bwTh$M{@V!n_OIQJ|h_v8uR?_H~*C&j8aTP&@;F^Ox%{tc| zR-(2)fn~K9)m&qzPiU@|(;nx~*4Jz>PpJLZ!&KRyO5N!Y+Ja^}+k+m5`9`yeo*n~F zTZ~uVB%Hx_Gw*ACn3@tWs2e?XR4vH?PgO&+vL-n`zrRt}PUjq|imjGKy>)#m#7e>J z=~^jQAMA&R=oex>K09n-|DbtcHtE{>WDS^AT>Il%Kck0gr7R~~sP(~}nm>K7TE8B~ z?Ha)u&TNpQM~NR;rEDfu|7T&KUVy5JvB$+=R1Twn*-WjPtsTc%A;-txjxX!~-)kLz zAzxsGJj2=8e;>-Ge=`1qh>A|*Xq`gmkei{&kpo&a$=+WFEppd48GBj@_RH#Er5$fs zsXfL~$$I!_-xAboOK)%x3>fMjx3$lw7@_Af?{kVGOGVL2<^eOw=`S7$Dd1gF;ak^%Ga8&$Y z%x1^AP3 zX=ORzWUS*}SwzQSu-qyC)j7DD;dNOBJ@GgPQNz`Tyk xVvc@y)^^*6ww>hhsiV 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℞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%0DXVgyHB6(;+mZrY3 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 L*n;r?8Y7I*iahaF literal 0 HcmV?d00001 diff --git a/moon/limb/limb.go b/moon/limb/limb.go new file mode 100644 index 0000000..0ccf9ad --- /dev/null +++ b/moon/limb/limb.go @@ -0,0 +1,37 @@ +// Package limb provides the embedded Kaguya LALT lunar limb profile. +package limb + +import ( + _ "embed" + "sync" + + "b612.me/astro/moon/limbtable" +) + +//go:embed kaguya_lalt.bin +var kaguyaTable []byte + +// Profile is the decoded Kaguya lunar limb profile. +type Profile = limbtable.Profile + +var ( + defaultOnce sync.Once + defaultProfile *Profile +) + +// Default returns the embedded Kaguya LALT profile, decoded on first use. +func Default() *Profile { + defaultOnce.Do(func() { + profile, err := Parse(kaguyaTable) + if err != nil { + panic(err) + } + defaultProfile = profile + }) + return defaultProfile +} + +// Parse decodes an LLB1 profile table. +func Parse(data []byte) (*Profile, error) { return limbtable.Parse(data) } + +const headerSize = limbtable.HeaderSize diff --git a/moon/limb/limb_test.go b/moon/limb/limb_test.go new file mode 100644 index 0000000..2efe724 --- /dev/null +++ b/moon/limb/limb_test.go @@ -0,0 +1,110 @@ +package limb + +import ( + "bytes" + "compress/zlib" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "math" + "testing" +) + +// 内置表的 sha256;换表时须同步更新。 +const kaguyaTableSHA256 = "7d49dc75497c404b78b5b259129d0f5ab17b56522128478591ea33c8cb97d072" + +func TestEmbeddedTableChecksum(t *testing.T) { + sum := sha256.Sum256(kaguyaTable) + if got := hex.EncodeToString(sum[:]); got != kaguyaTableSHA256 { + t.Fatalf("embedded table sha256 = %s, want %s", got, kaguyaTableSHA256) + } +} + +func TestLimbRadiusMatchesGenerator(t *testing.T) { + cases := []struct{ lon, lat, angle, radiusKM float64 }{ + {0, 0, 0, 1737.432000000}, + {3.3, -2.7, 123.45, 1735.688760000}, + {-8.6, 7.9, 359.97, 1738.466256000}, + {5.5, 1.25, 270.05, 1738.604000000}, + {-1.0, -6.0, 45.0, 1737.796000000}, + } + profile := Default() + for _, c := range cases { + got, ok := profile.LimbRadiusKM(c.lon, c.lat, c.angle) + if !ok { + t.Fatalf("LimbRadiusKM(%v, %v, %v) not ok", c.lon, c.lat, c.angle) + } + if math.Abs(got-c.radiusKM) > 1e-6 { + t.Errorf("LimbRadiusKM(%v, %v, %v) = %.9f, want %.9f", c.lon, c.lat, c.angle, got, c.radiusKM) + } + } +} + +func TestLimbRadiusWrapsAndClamps(t *testing.T) { + profile := Default() + base, _ := profile.LimbRadiusKM(2, 3, 10) + for _, angle := range []float64{370, -350} { + if got, _ := profile.LimbRadiusKM(2, 3, angle); math.Abs(got-base) > 1e-9 { + t.Errorf("angle %v = %.9f, want %.9f", angle, got, base) + } + } + edge, _ := profile.LimbRadiusKM(9, -8, 10) + if got, _ := profile.LimbRadiusKM(12, -11, 10); got != edge { + t.Errorf("out-of-range libration = %.9f, want clamped %.9f", got, edge) + } + if _, ok := profile.LimbRadiusKM(math.NaN(), 0, 0); ok { + t.Error("NaN libration must fail") + } +} + +func TestParseRejectsBadInput(t *testing.T) { + if _, err := Parse([]byte("nope")); err == nil { + t.Error("short input must fail") + } + broken := append([]byte(nil), kaguyaTable[:headerSize+16]...) + if _, err := Parse(broken); err == nil { + t.Error("truncated payload must fail") + } + huge := append([]byte(nil), kaguyaTable...) + binary.LittleEndian.PutUint32(huge[4:], 1<<30) + binary.LittleEndian.PutUint32(huge[8:], 1<<30) + if _, err := Parse(huge); err == nil { + t.Error("oversized header must fail before allocating") + } +} + +func TestParseRejectsNonFiniteHeader(t *testing.T) { + cases := []struct { + name string + angleStep, ref float64 + }{ + {name: "NaN angle step", angleStep: math.NaN(), ref: 1737.4}, + {name: "Inf angle step", angleStep: math.Inf(1), ref: 1737.4}, + {name: "NaN reference radius", angleStep: 180, ref: math.NaN()}, + {name: "zero reference radius", angleStep: 180, ref: 0}, + } + for _, c := range cases { + if _, err := Parse(syntheticTable(c.angleStep, c.ref)); err == nil { + t.Errorf("%s: Parse err = nil, want an error", c.name) + } + } + if _, err := Parse(syntheticTable(180, 1737.4)); err != nil { + t.Fatalf("valid header: Parse err = %v", err) + } +} + +// syntheticTable 最小合法 2×2×2 LLB1 表,仅表头两项可变。 +func syntheticTable(angleStep, referenceRadiusKM float64) []byte { + var buf bytes.Buffer + buf.WriteString("LLB1") + for _, count := range []uint32{2, 2, 2} { + binary.Write(&buf, binary.LittleEndian, count) + } + for _, value := range []float64{0, 1, 0, 1, angleStep, referenceRadiusKM, 1} { + binary.Write(&buf, binary.LittleEndian, math.Float64bits(value)) + } + writer := zlib.NewWriter(&buf) + writer.Write(make([]byte, 2*8)) + writer.Close() + return buf.Bytes() +} diff --git a/moon/limbtable/limbtable.go b/moon/limbtable/limbtable.go new file mode 100644 index 0000000..803a9cd --- /dev/null +++ b/moon/limbtable/limbtable.go @@ -0,0 +1,153 @@ +// Package limbtable decodes lunar limb profile tables without embedding a dataset. +package limbtable + +import ( + "bytes" + "compress/zlib" + "encoding/binary" + "errors" + "io" + "math" +) + +const HeaderSize = 4 + 3*4 + 7*8 +const maxCells = 1 << 24 + +// Profile is a lunar limb profile table. +type Profile struct { + lonCount, latCount, angleCount int + lonStart, lonStep float64 + latStart, latStep float64 + angleStep float64 + referenceRadiusKM float64 + minHeight, maxHeight float64 + heights []float32 +} + +// Parse decodes an LLB1 profile table. +func Parse(data []byte) (*Profile, error) { + if len(data) < HeaderSize || string(data[:4]) != "LLB1" { + return nil, errors.New("limbtable: not an LLB1 table") + } + le := binary.LittleEndian + counts := [3]int{} + for index := range counts { + counts[index] = int(le.Uint32(data[4+4*index:])) + } + values := [7]float64{} + for index := range values { + values[index] = math.Float64frombits(le.Uint64(data[16+8*index:])) + } + profile := &Profile{ + lonCount: counts[0], latCount: counts[1], angleCount: counts[2], + lonStart: values[0], lonStep: values[1], + latStart: values[2], latStep: values[3], + angleStep: values[4], referenceRadiusKM: values[5], + } + quantum := values[6] + finite := true + for _, value := range values { + finite = finite && isFinite(value) + } + if !finite || profile.lonCount < 2 || profile.latCount < 2 || profile.angleCount < 2 || + !(profile.lonStep > 0) || !(profile.latStep > 0) || !(quantum > 0) || !(profile.referenceRadiusKM > 0) || + math.Abs(profile.angleStep*float64(profile.angleCount)-360) > 1e-9 || + profile.lonCount > maxCells/profile.latCount/profile.angleCount { + return nil, errors.New("limbtable: invalid LLB1 header") + } + + reader, err := zlib.NewReader(bytes.NewReader(data[HeaderSize:])) + if err != nil { + return nil, err + } + defer reader.Close() + total := profile.lonCount * profile.latCount * profile.angleCount + raw := make([]byte, 2*total) + if _, err := io.ReadFull(reader, raw); err != nil { + return nil, err + } + row := profile.lonCount * profile.angleCount + levels := make([]int32, total) + for index := range levels { + levels[index] = int32(int16(le.Uint16(raw[2*index:]))) + } + for index := row; index < total; index++ { + levels[index] += levels[index-row] + } + for lat := 0; lat < profile.latCount; lat++ { + base := lat * row + for index := base + profile.angleCount; index < base+row; index++ { + levels[index] += levels[index-profile.angleCount] + } + } + profile.heights = make([]float32, total) + profile.minHeight, profile.maxHeight = math.Inf(1), math.Inf(-1) + for index, level := range levels { + height := float32(float64(level) * quantum) + profile.heights[index] = height + profile.minHeight = math.Min(profile.minHeight, float64(height)) + profile.maxHeight = math.Max(profile.maxHeight, float64(height)) + } + return profile, nil +} + +// LimbRadiusKM returns the Moon-centre to limb-point distance in kilometres. +func (profile *Profile) LimbRadiusKM(librationLongitude, librationLatitude, positionAngle float64) (float64, bool) { + if profile == nil || len(profile.heights) == 0 || + !isFinite(librationLongitude) || !isFinite(librationLatitude) || !isFinite(positionAngle) { + return 0, false + } + lon, lonWeight := profile.cell(librationLongitude, profile.lonStart, profile.lonStep, profile.lonCount) + lat, latWeight := profile.cell(librationLatitude, profile.latStart, profile.latStep, profile.latCount) + angle := math.Mod(positionAngle/profile.angleStep, float64(profile.angleCount)) + if angle < 0 { + angle += float64(profile.angleCount) + } + angle0 := int(angle) + if angle0 >= profile.angleCount { + angle0 = profile.angleCount - 1 + } + angleWeight := angle - float64(angle0) + angle1 := (angle0 + 1) % profile.angleCount + height := 0.0 + for dLat, wLat := range [2]float64{1 - latWeight, latWeight} { + for dLon, wLon := range [2]float64{1 - lonWeight, lonWeight} { + base := ((lat+dLat)*profile.lonCount + lon + dLon) * profile.angleCount + height += wLat * wLon * ((1-angleWeight)*float64(profile.heights[base+angle0]) + + angleWeight*float64(profile.heights[base+angle1])) + } + } + return profile.referenceRadiusKM + height/1000, true +} + +// AngleStepDeg 位置角网格步长(度) / position-angle grid step in degrees. +func (profile *Profile) AngleStepDeg() float64 { + if profile == nil { + return 0 + } + return profile.angleStep +} + +// RadiusRangeKM 表内可能取到的最小、最大月缘半径(km) / smallest and largest limb radius. +func (profile *Profile) RadiusRangeKM() (float64, float64) { + if profile == nil || len(profile.heights) == 0 { + return 0, 0 + } + return profile.referenceRadiusKM + profile.minHeight/1000, profile.referenceRadiusKM + profile.maxHeight/1000 +} + +func (profile *Profile) cell(value, start, step float64, count int) (int, float64) { + position := (value - start) / step + if position <= 0 { + return 0, 0 + } + if position >= float64(count-1) { + return count - 2, 1 + } + index := int(position) + return index, position - float64(index) +} + +func isFinite(value float64) bool { + return !math.IsNaN(value) && !math.IsInf(value, 0) +} diff --git a/moon/max_declination.go b/moon/max_declination.go index 97c1014..8726d24 100644 --- a/moon/max_declination.go +++ b/moon/max_declination.go @@ -15,43 +15,43 @@ type MaximumDeclinationInfo struct { } // MaximumNorthDeclinationsInMonth 指定年月内的所有月球最大北赤纬事件 / all maximum northern lunar declination events in the given Gregorian month. -func MaximumNorthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { - return convertMaximumDeclinationInfos(basic.MoonMaximumNorthDeclinations(year, month)) +func (module *Moon) MaximumNorthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { + return convertMaximumDeclinationInfos(basic.MoonMaximumNorthDeclinationsWithInstance(year, month, module.inst)) } // MaximumSouthDeclinationsInMonth 指定年月内的所有月球最大南赤纬事件 / all maximum southern lunar declination events in the given Gregorian month. -func MaximumSouthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { - return convertMaximumDeclinationInfos(basic.MoonMaximumSouthDeclinations(year, month)) +func (module *Moon) MaximumSouthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { + return convertMaximumDeclinationInfos(basic.MoonMaximumSouthDeclinationsWithInstance(year, month, module.inst)) } // LastMaximumNorthDeclination 指定时刻之前最近一次月球最大北赤纬 / last maximum northern lunar declination at or before date. -func LastMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.LastMoonMaximumNorthDeclination(timeToUTJDE(date))) +func (module *Moon) LastMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.LastMoonMaximumNorthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } // NextMaximumNorthDeclination 指定时刻之后最近一次月球最大北赤纬 / next maximum northern lunar declination after date. -func NextMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.NextMoonMaximumNorthDeclination(timeToUTJDE(date))) +func (module *Moon) NextMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.NextMoonMaximumNorthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } // ClosestMaximumNorthDeclination 离指定时刻最近一次月球最大北赤纬 / closest maximum northern lunar declination to date. -func ClosestMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.ClosestMoonMaximumNorthDeclination(timeToUTJDE(date))) +func (module *Moon) ClosestMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.ClosestMoonMaximumNorthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } // LastMaximumSouthDeclination 指定时刻之前最近一次月球最大南赤纬 / last maximum southern lunar declination at or before date. -func LastMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.LastMoonMaximumSouthDeclination(timeToUTJDE(date))) +func (module *Moon) LastMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.LastMoonMaximumSouthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } // NextMaximumSouthDeclination 指定时刻之后最近一次月球最大南赤纬 / next maximum southern lunar declination after date. -func NextMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.NextMoonMaximumSouthDeclination(timeToUTJDE(date))) +func (module *Moon) NextMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.NextMoonMaximumSouthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } // ClosestMaximumSouthDeclination 离指定时刻最近一次月球最大南赤纬 / closest maximum southern lunar declination to date. -func ClosestMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { - return convertMaximumDeclinationInfo(date, basic.ClosestMoonMaximumSouthDeclination(timeToUTJDE(date))) +func (module *Moon) ClosestMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return convertMaximumDeclinationInfo(date, basic.ClosestMoonMaximumSouthDeclinationWithInstance(timeToUTJDE(date), module.inst)) } func convertMaximumDeclinationInfos(events []basic.DeclinationEvent) []MaximumDeclinationInfo { diff --git a/moon/moon.go b/moon/moon.go index 49d788b..367f277 100644 --- a/moon/moon.go +++ b/moon/moon.go @@ -1,13 +1,13 @@ package moon import ( - "b612.me/astro/internal/civiltime" - "b612.me/astro/tools" "errors" "math" "time" "b612.me/astro/basic" + "b612.me/astro/internal/civiltime" + "b612.me/astro/tools" ) var ( @@ -36,194 +36,131 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // TrueLo 月亮真黄经 / true ecliptic longitude. -// -// 返回月亮在 date 对应绝对时刻的地心真黄经,单位度。 -// Returns the Moon's geocentric true ecliptic longitude at the instant represented by date, in degrees. -func TrueLo(date time.Time) float64 { +func (module *Moon) TrueLo(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonTrueLo(basic.UTC2TT(jd)) + return basic.HMoonTrueLoWithInstance(basic.UTC2TT(jd), module.inst) } -// TrueLoN 截断项月亮真黄经 / truncated true ecliptic longitude. -// -// 参数与 TrueLo 相同;n<0 使用当前仓库内嵌的全部 ELP 项,其余值用于截断月球级数。 -// Uses the same inputs as TrueLo. n<0 keeps all embedded ELP terms in this repository; other values truncate the lunar series. -func TrueLoN(date time.Time, n int) float64 { +// TrueLoN 月亮真黄经(n 变体) / n-parameterised true ecliptic longitude. +func (module *Moon) TrueLoN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonTrueLoN(basic.UTC2TT(jd), n) + return basic.HMoonTrueLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } // TrueBo 月亮真黄纬 / true ecliptic latitude. -// -// 返回月亮在 date 对应绝对时刻的地心真黄纬,单位度。 -// Returns the Moon's geocentric true ecliptic latitude at the instant represented by date, in degrees. -func TrueBo(date time.Time) float64 { +func (module *Moon) TrueBo(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonTrueBo(basic.UTC2TT(jd)) + return basic.HMoonTrueBoWithInstance(basic.UTC2TT(jd), module.inst) } -// TrueBoN 截断项月亮真黄纬 / truncated true ecliptic latitude. -// -// 参数与 TrueBo 相同;n<0 使用当前仓库内嵌的全部 ELP 项,其余值用于截断月球级数。 -// Uses the same inputs as TrueBo. n<0 keeps all embedded ELP terms in this repository; other values truncate the lunar series. -func TrueBoN(date time.Time, n int) float64 { +// TrueBoN 月亮真黄纬(n 变体) / n-parameterised true ecliptic latitude. +func (module *Moon) TrueBoN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonTrueBoN(basic.UTC2TT(jd), n) + return basic.HMoonTrueBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } // ApparentLo 月亮地心视黄经 / apparent geocentric ecliptic longitude. -// -// 返回月亮在 date 对应绝对时刻的地心视黄经,单位度。 -// Returns the Moon's apparent geocentric ecliptic longitude at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Moon) ApparentLo(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonApparentLo(basic.UTC2TT(jd)) + return basic.HMoonApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // TrueRa 月亮地心真赤经 / true geocentric right ascension. -// -// 返回月亮在 date 对应绝对时刻的地心真赤经,单位度。 -// Returns the Moon's geocentric true right ascension at the instant represented by date, in degrees. -func TrueRa(date time.Time) float64 { +func (module *Moon) TrueRa(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricTrueRa(basic.UTC2TT(jd)) + return basic.HMoonGeocentricTrueRaWithInstance(basic.UTC2TT(jd), module.inst) } // TrueDec 月亮地心真赤纬 / true geocentric declination. -// -// 返回月亮在 date 对应绝对时刻的地心真赤纬,单位度。 -// Returns the Moon's geocentric true declination at the instant represented by date, in degrees. -func TrueDec(date time.Time) float64 { +func (module *Moon) TrueDec(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricTrueDec(basic.UTC2TT(jd)) + return basic.HMoonGeocentricTrueDecWithInstance(basic.UTC2TT(jd), module.inst) } // TrueRaDec 月亮地心真赤经、真赤纬 / true geocentric right ascension and declination. -// -// 返回月亮在 date 对应绝对时刻的地心真赤经与真赤纬,单位度。 -// Returns the Moon's geocentric true right ascension and declination at the instant represented by date, in degrees. -func TrueRaDec(date time.Time) (float64, float64) { +func (module *Moon) TrueRaDec(date time.Time) (float64, float64) { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricTrueRaDec(basic.UTC2TT(jd)) + return basic.HMoonGeocentricTrueRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // GeocentricApparentRa 月亮地心视赤经 / apparent geocentric right ascension. -// -// 返回月亮在 date 对应绝对时刻的地心视赤经,单位度。 -// Returns the Moon's apparent geocentric right ascension at the instant represented by date, in degrees. -func GeocentricApparentRa(date time.Time) float64 { +func (module *Moon) GeocentricApparentRa(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricApparentRa(basic.UTC2TT(jd)) + return basic.HMoonGeocentricApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // GeocentricApparentDec 月亮地心视赤纬 / apparent geocentric declination. -// -// 返回月亮在 date 对应绝对时刻的地心视赤纬,单位度。 -// Returns the Moon's apparent geocentric declination at the instant represented by date, in degrees. -func GeocentricApparentDec(date time.Time) float64 { +func (module *Moon) GeocentricApparentDec(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricApparentDec(basic.UTC2TT(jd)) + return basic.HMoonGeocentricApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // GeocentricApparentRaDec 月亮地心视赤经、视赤纬 / apparent geocentric right ascension and declination. -// -// 返回月亮在 date 对应绝对时刻的地心视赤经与视赤纬,单位度。 -// Returns the Moon's apparent geocentric right ascension and declination at the instant represented by date, in degrees. -func GeocentricApparentRaDec(date time.Time) (float64, float64) { +func (module *Moon) GeocentricApparentRaDec(date time.Time) (float64, float64) { jd := basic.Date2JD(date.UTC()) - return basic.HMoonGeocentricApparentRaDec(basic.UTC2TT(jd)) + return basic.HMoonGeocentricApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 月亮站心视赤经 / apparent topocentric right ascension. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; the result is in degrees. -func ApparentRa(date time.Time, lon, lat float64) float64 { +func (module *Moon) ApparentRa(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonApparentRa(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonApparentRaWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // ApparentDec 月亮站心视赤纬 / apparent topocentric declination. -// -// 参数与 ApparentRa 相同,返回月亮站心视赤纬,单位度。 -// Uses the same inputs as ApparentRa and returns the Moon's apparent topocentric declination in degrees. -func ApparentDec(date time.Time, lon, lat float64) float64 { +func (module *Moon) ApparentDec(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonApparentDec(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonApparentDecWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // ApparentRaDec 月亮站心视赤经、视赤纬 / apparent topocentric right ascension and declination. -// -// 参数与 ApparentRa 相同,返回月亮站心视赤经与视赤纬,单位度。 -// Uses the same inputs as ApparentRa and returns the Moon's apparent topocentric right ascension and declination in degrees. -func ApparentRaDec(date time.Time, lon, lat float64) (float64, float64) { +func (module *Moon) ApparentRaDec(date time.Time, lon, lat float64) (float64, float64) { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonApparentRaDec(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonApparentRaDecWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // HourAngle 月亮时角 / hour angle. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; the result is in degrees. -func HourAngle(date time.Time, lon, lat float64) float64 { +func (module *Moon) HourAngle(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.MoonTimeAngle(localJD, lon, lat, float64(loc)/3600.0) + return basic.MoonTimeAngleWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // Azimuth 月亮方位角 / azimuth. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +func (module *Moon) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonAzimuth(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonAzimuthWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // Altitude 月亮高度角 / lunar altitude. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +func (module *Moon) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonHeight(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonHeightWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } // Zenith 月亮天顶距 / lunar zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Moon) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } // CulminationTime 月亮中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive. -func CulminationTime(date time.Time, lon, lat float64) time.Time { +func (module *Moon) CulminationTime(date time.Time, lon, lat float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() - calcJD := basic.MoonCulminationTime(localJD, lon, lat, float64(loc)/3600.0) - float64(loc)/86400 + calcJD := basic.MoonCulminationTimeWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) - float64(loc)/86400 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 月出时刻 / moonrise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负; -// height 为椭球高(大地高),单位米;aero 为 true 时按动态标准大气折射和实时月球视半径计算上缘过地平线。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive; -// height 为观测者椭球高(大地高),单位米;aero 使用动态标准折射和实时月球视半径计算上缘过地平线。 -// height is observer elevation in meters; aero uses dynamic standard refraction and the instantaneous lunar semidiameter for an upper-limb crossing. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Moon) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { if result, err, handled := civiltime.Event(date, ERR_NOT_TODAY, func(d time.Time) (time.Time, error) { - return RiseTime(d, lon, lat, height, aero) + return module.RiseTime(d, lon, lat, height, aero) }); handled { return result, err } @@ -235,27 +172,19 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e if aero { aeroFloat = 1 } - riseJD, err := basic.GetMoonRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.GetMoonRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 月落时刻别名 / deprecated moonset alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Moon) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 月落时刻 / moonset time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的月落时刻。 -// Uses the same inputs as RiseTime and returns the moonset time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Moon) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { if result, err, handled := civiltime.Event(date, ERR_NOT_TODAY, func(d time.Time) (time.Time, error) { - return SetTime(d, lon, lat, height, aero) + return module.SetTime(d, lon, lat, height, aero) }); handled { return result, err } @@ -267,27 +196,21 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er if aero { aeroFloat = 1 } - downJD, err := basic.GetMoonSetTime(localJD, lon, lat, timezone, aeroFloat, height) + downJD, err := basic.GetMoonSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, downJD, err) } // SunMoonLoDiff 日月黄经差 / Moon-Sun longitude difference. -// -// 返回月亮视黄经减去太阳视黄经的结果,单位度,取值范围 [0, 360);新月附近接近 0°,满月附近接近 180°。 -// Returns apparent lunar longitude minus apparent solar longitude in degrees, normalized to [0, 360). It is near 0° at new moon and near 180° at full moon. -func SunMoonLoDiff(date time.Time) float64 { +func (module *Moon) SunMoonLoDiff(date time.Time) float64 { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - sunLo := basic.HSunApparentLo(jde) - moonLo := basic.HMoonApparentLo(jde) + sunLo := basic.HSunApparentLoWithInstance(jde, module.inst) + moonLo := basic.HMoonApparentLoWithInstance(jde, module.inst) return tools.Limit360(moonLo - sunLo) } // PhaseDesc 月相文字描述 / textual lunar phase description. -// -// 基于 SunMoonLoDiff 的分段结果返回中文月相名称。 -// Returns a Chinese phase name derived from the segmented Moon-Sun longitude difference. -func PhaseDesc(date time.Time) string { - moonSunLoDiff := SunMoonLoDiff(date) +func (module *Moon) PhaseDesc(date time.Time) string { + moonSunLoDiff := module.SunMoonLoDiff(date) if moonSunLoDiff >= 0 && moonSunLoDiff <= 30 { return "新月" } else if moonSunLoDiff > 30 && moonSunLoDiff <= 75 { @@ -310,77 +233,72 @@ func PhaseDesc(date time.Time) string { } // Phase 月面受照比例 / illuminated fraction. -// -// 返回月亮在 date 对应绝对时刻的受照比例,范围 [0, 1]。 -// Returns the Moon's illuminated fraction at the instant represented by date, in the range [0, 1]. -func Phase(date time.Time) float64 { +func (module *Moon) Phase(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) - return basic.MoonPhase(basic.UTC2TT(jd)) + return basic.MoonPhaseWithInstance(basic.UTC2TT(jd), module.inst) } // ShuoYue 朔月锚点解 / new-moon solution near a decimal year anchor. -// -// year 为公历小数年锚点,例如 2025.0 或 2025.5;返回以该锚点求得的一次朔月时刻,结果为 UTC。 -// year is a decimal Gregorian-year anchor such as 2025.0 or 2025.5. The returned time is one new moon solved near that anchor, in UTC. -func ShuoYue(year float64) time.Time { - jd := basic.TT2UTC(basic.CalcMoonSH(year, 0)) +func (module *Moon) ShuoYue(year float64) time.Time { + jd := basic.TT2UTC(basic.CalcMoonSHWithInstance(year, 0, module.inst)) return basic.JD2DateByZone(jd, time.UTC, false) } // NextShuoYue 下一次朔月 / next new moon. -// -// 返回 date 之后最近一次朔月时刻,结果保持 date 的时区。 -// Returns the next new moon after date, keeping date's time zone. -func NextShuoYue(date time.Time) time.Time { - return nextMoonPhase(date, 0) +func (module *Moon) NextShuoYue(date time.Time) time.Time { + return nextMoonPhase(date, 0, module) } // LastShuoYue 上一次朔月 / previous new moon. -// -// 返回 date 之前最近一次朔月时刻,结果保持 date 的时区。 -// Returns the previous new moon before date, keeping date's time zone. -func LastShuoYue(date time.Time) time.Time { - return lastMoonPhase(date, 0) +func (module *Moon) LastShuoYue(date time.Time) time.Time { + return lastMoonPhase(date, 0, module) } // ClosestShuoYue 最近朔月 / closest new moon. -// -// 返回离 date 最近的朔月时刻,结果保持 date 的时区。 -// Returns the new moon nearest to date, keeping date's time zone. -func ClosestShuoYue(date time.Time) time.Time { - return closestMoonPhase(date, 0) +func (module *Moon) ClosestShuoYue(date time.Time) time.Time { + return closestMoonPhase(date, 0, module) } // NewMoon 朔月英文别名 / English alias for ShuoYue. -func NewMoon(year float64) time.Time { - return ShuoYue(year) +func (module *Moon) NewMoon(year float64) time.Time { + return module.ShuoYue(year) } // NextNewMoon 下一次朔月英文别名 / English alias for NextShuoYue. -func NextNewMoon(date time.Time) time.Time { - return NextShuoYue(date) +func (module *Moon) NextNewMoon(date time.Time) time.Time { + return module.NextShuoYue(date) } // LastNewMoon 上一次朔月英文别名 / English alias for LastShuoYue. -func LastNewMoon(date time.Time) time.Time { - return LastShuoYue(date) +func (module *Moon) LastNewMoon(date time.Time) time.Time { + return module.LastShuoYue(date) } // ClosestNewMoon 最近朔月英文别名 / English alias for ClosestShuoYue. -func ClosestNewMoon(date time.Time) time.Time { - return ClosestShuoYue(date) +func (module *Moon) ClosestNewMoon(date time.Time) time.Time { + return module.ClosestShuoYue(date) } -func closestMoonPhase(date time.Time, typed int) time.Time { +func closestMoonPhase(date time.Time, typed int, modules ...*Moon) time.Time { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + //0=shuo 1=wang 2=shangxian 3=xiaxian jde := basic.UTC2TT(basic.Date2JD(date.UTC())) if typed < 2 { - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDE(jde, typed)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDEWithInstance(jde, typed, module.inst)), date.Location(), false) } - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDE(jde, typed-2)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDEWithInstance(jde, typed-2, module.inst)), date.Location(), false) } -func nextMoonPhase(date time.Time, typed int) time.Time { +func nextMoonPhase(date time.Time, typed int, modules ...*Moon) time.Time { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + //0=shuo 1=wang 2=shangxian 3=xiaxian diffCode := 0.00 switch typed { @@ -392,7 +310,7 @@ func nextMoonPhase(date time.Time, typed int) time.Time { diffCode = 270 } jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - cost := basic.HMoonApparentLo(jde) - basic.HSunApparentLo(jde) - float64(diffCode) + cost := basic.HMoonApparentLoWithInstance(jde, module.inst) - basic.HSunApparentLoWithInstance(jde, module.inst) - float64(diffCode) for cost < 0 { cost += 360 } @@ -403,12 +321,17 @@ func nextMoonPhase(date time.Time, typed int) time.Time { jde += (240 - cost) / 11.19 } if typed < 2 { - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDE(jde, typed)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDEWithInstance(jde, typed, module.inst)), date.Location(), false) } - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDE(jde, typed-2)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDEWithInstance(jde, typed-2, module.inst)), date.Location(), false) } -func lastMoonPhase(date time.Time, typed int) time.Time { +func lastMoonPhase(date time.Time, typed int, modules ...*Moon) time.Time { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + //0=shuo 1=wang 2=shangxian 3=xiaxian diffCode := 0.00 switch typed { @@ -420,7 +343,7 @@ func lastMoonPhase(date time.Time, typed int) time.Time { diffCode = 270 } jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - cost := basic.HMoonApparentLo(jde) - basic.HSunApparentLo(jde) - float64(diffCode) + cost := basic.HMoonApparentLoWithInstance(jde, module.inst) - basic.HSunApparentLoWithInstance(jde, module.inst) - float64(diffCode) for cost < 0 { cost += 360 } @@ -431,176 +354,158 @@ func lastMoonPhase(date time.Time, typed int) time.Time { jde -= (cost - 120) / 11.19 } if typed < 2 { - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDE(jde, typed)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonSHByJDEWithInstance(jde, typed, module.inst)), date.Location(), false) } - return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDE(jde, typed-2)), date.Location(), false) + return basic.JD2DateByZone(basic.TT2UTC(basic.CalcMoonXHByJDEWithInstance(jde, typed-2, module.inst)), date.Location(), false) } // WangYue 望月锚点解 / full-moon solution near a decimal year anchor. -// -// year 为公历小数年锚点,例如 2025.0 或 2025.5;返回以该锚点求得的一次望月时刻,结果为 UTC。 -// year is a decimal Gregorian-year anchor such as 2025.0 or 2025.5. The returned time is one full moon solved near that anchor, in UTC. -func WangYue(year float64) time.Time { - jd := basic.TT2UTC(basic.CalcMoonSH(year, 1)) +func (module *Moon) WangYue(year float64) time.Time { + jd := basic.TT2UTC(basic.CalcMoonSHWithInstance(year, 1, module.inst)) return basic.JD2DateByZone(jd, time.UTC, false) } // NextWangYue 下一次望月 / next full moon. -// -// 返回 date 之后最近一次望月时刻,结果保持 date 的时区。 -// Returns the next full moon after date, keeping date's time zone. -func NextWangYue(date time.Time) time.Time { - return nextMoonPhase(date, 1) +func (module *Moon) NextWangYue(date time.Time) time.Time { + return nextMoonPhase(date, 1, module) } // LastWangYue 上一次望月 / previous full moon. -// -// 返回 date 之前最近一次望月时刻,结果保持 date 的时区。 -// Returns the previous full moon before date, keeping date's time zone. -func LastWangYue(date time.Time) time.Time { - return lastMoonPhase(date, 1) +func (module *Moon) LastWangYue(date time.Time) time.Time { + return lastMoonPhase(date, 1, module) } // ClosestWangYue 最近望月 / closest full moon. -// -// 返回离 date 最近的望月时刻,结果保持 date 的时区。 -// Returns the full moon nearest to date, keeping date's time zone. -func ClosestWangYue(date time.Time) time.Time { - return closestMoonPhase(date, 1) +func (module *Moon) ClosestWangYue(date time.Time) time.Time { + return closestMoonPhase(date, 1, module) } // FullMoon 望月英文别名 / English alias for WangYue. -func FullMoon(year float64) time.Time { - return WangYue(year) +func (module *Moon) FullMoon(year float64) time.Time { + return module.WangYue(year) } // NextFullMoon 下一次望月英文别名 / English alias for NextWangYue. -func NextFullMoon(date time.Time) time.Time { - return NextWangYue(date) +func (module *Moon) NextFullMoon(date time.Time) time.Time { + return module.NextWangYue(date) } // LastFullMoon 上一次望月英文别名 / English alias for LastWangYue. -func LastFullMoon(date time.Time) time.Time { - return LastWangYue(date) +func (module *Moon) LastFullMoon(date time.Time) time.Time { + return module.LastWangYue(date) } // ClosestFullMoon 最近望月英文别名 / English alias for ClosestWangYue. -func ClosestFullMoon(date time.Time) time.Time { - return ClosestWangYue(date) +func (module *Moon) ClosestFullMoon(date time.Time) time.Time { + return module.ClosestWangYue(date) } // ShangXianYue 上弦锚点解 / first-quarter solution near a decimal year anchor. -// -// year 为公历小数年锚点,例如 2025.0 或 2025.5;返回以该锚点求得的一次上弦时刻,结果为 UTC。 -// year is a decimal Gregorian-year anchor such as 2025.0 or 2025.5. The returned time is one first-quarter solution near that anchor, in UTC. -func ShangXianYue(year float64) time.Time { - jd := basic.TT2UTC(basic.CalcMoonXH(year, 0)) +func (module *Moon) ShangXianYue(year float64) time.Time { + jd := basic.TT2UTC(basic.CalcMoonXHWithInstance(year, 0, module.inst)) return basic.JD2DateByZone(jd, time.UTC, false) } // NextShangXianYue 下一次上弦 / next first quarter. -// -// 返回 date 之后最近一次上弦时刻,结果保持 date 的时区。 -// Returns the next first quarter after date, keeping date's time zone. -func NextShangXianYue(date time.Time) time.Time { - return nextMoonPhase(date, 2) +func (module *Moon) NextShangXianYue(date time.Time) time.Time { + return nextMoonPhase(date, 2, module) } // LastShangXianYue 上一次上弦 / previous first quarter. -// -// 返回 date 之前最近一次上弦时刻,结果保持 date 的时区。 -// Returns the previous first quarter before date, keeping date's time zone. -func LastShangXianYue(date time.Time) time.Time { - return lastMoonPhase(date, 2) +func (module *Moon) LastShangXianYue(date time.Time) time.Time { + return lastMoonPhase(date, 2, module) } // ClosestShangXianYue 最近上弦 / closest first quarter. -// -// 返回离 date 最近的上弦时刻,结果保持 date 的时区。 -// Returns the first quarter nearest to date, keeping date's time zone. -func ClosestShangXianYue(date time.Time) time.Time { - return closestMoonPhase(date, 2) +func (module *Moon) ClosestShangXianYue(date time.Time) time.Time { + return closestMoonPhase(date, 2, module) } // FirstQuarter 上弦英文别名 / English alias for ShangXianYue. -func FirstQuarter(year float64) time.Time { - return ShangXianYue(year) +func (module *Moon) FirstQuarter(year float64) time.Time { + return module.ShangXianYue(year) } // NextFirstQuarter 下一次上弦英文别名 / English alias for NextShangXianYue. -func NextFirstQuarter(date time.Time) time.Time { - return NextShangXianYue(date) +func (module *Moon) NextFirstQuarter(date time.Time) time.Time { + return module.NextShangXianYue(date) } // LastFirstQuarter 上一次上弦英文别名 / English alias for LastShangXianYue. -func LastFirstQuarter(date time.Time) time.Time { - return LastShangXianYue(date) +func (module *Moon) LastFirstQuarter(date time.Time) time.Time { + return module.LastShangXianYue(date) } // ClosestFirstQuarter 最近上弦英文别名 / English alias for ClosestShangXianYue. -func ClosestFirstQuarter(date time.Time) time.Time { - return ClosestShangXianYue(date) +func (module *Moon) ClosestFirstQuarter(date time.Time) time.Time { + return module.ClosestShangXianYue(date) } // XiaXianYue 下弦锚点解 / last-quarter solution near a decimal year anchor. -// -// year 为公历小数年锚点,例如 2025.0 或 2025.5;返回以该锚点求得的一次下弦时刻,结果为 UTC。 -// year is a decimal Gregorian-year anchor such as 2025.0 or 2025.5. The returned time is one last-quarter solution near that anchor, in UTC. -func XiaXianYue(year float64) time.Time { - jd := basic.TT2UTC(basic.CalcMoonXH(year, 1)) +func (module *Moon) XiaXianYue(year float64) time.Time { + jd := basic.TT2UTC(basic.CalcMoonXHWithInstance(year, 1, module.inst)) return basic.JD2DateByZone(jd, time.UTC, false) } // NextXiaXianYue 下一次下弦 / next last quarter. -// -// 返回 date 之后最近一次下弦时刻,结果保持 date 的时区。 -// Returns the next last quarter after date, keeping date's time zone. -func NextXiaXianYue(date time.Time) time.Time { - return nextMoonPhase(date, 3) +func (module *Moon) NextXiaXianYue(date time.Time) time.Time { + return nextMoonPhase(date, 3, module) } // LastXiaXianYue 上一次下弦 / previous last quarter. -// -// 返回 date 之前最近一次下弦时刻,结果保持 date 的时区。 -// Returns the previous last quarter before date, keeping date's time zone. -func LastXiaXianYue(date time.Time) time.Time { - return lastMoonPhase(date, 3) +func (module *Moon) LastXiaXianYue(date time.Time) time.Time { + return lastMoonPhase(date, 3, module) } // ClosestXiaXianYue 最近下弦 / closest last quarter. -// -// 返回离 date 最近的下弦时刻,结果保持 date 的时区。 -// Returns the last quarter nearest to date, keeping date's time zone. -func ClosestXiaXianYue(date time.Time) time.Time { - return closestMoonPhase(date, 3) +func (module *Moon) ClosestXiaXianYue(date time.Time) time.Time { + return closestMoonPhase(date, 3, module) } // LastQuarter 下弦英文别名 / English alias for XiaXianYue. -func LastQuarter(year float64) time.Time { - return XiaXianYue(year) +func (module *Moon) LastQuarter(year float64) time.Time { + return module.XiaXianYue(year) } // NextLastQuarter 下一次下弦英文别名 / English alias for NextXiaXianYue. -func NextLastQuarter(date time.Time) time.Time { - return NextXiaXianYue(date) +func (module *Moon) NextLastQuarter(date time.Time) time.Time { + return module.NextXiaXianYue(date) } // LastLastQuarter 上一次下弦英文别名 / English alias for LastXiaXianYue. -func LastLastQuarter(date time.Time) time.Time { - return LastXiaXianYue(date) +func (module *Moon) LastLastQuarter(date time.Time) time.Time { + return module.LastXiaXianYue(date) } // ClosestLastQuarter 最近下弦英文别名 / English alias for ClosestXiaXianYue. -func ClosestLastQuarter(date time.Time) time.Time { - return ClosestXiaXianYue(date) +func (module *Moon) ClosestLastQuarter(date time.Time) time.Time { + return module.ClosestXiaXianYue(date) } -// EarthDistance 地月距离 / Earth-Moon distance. -// -// 返回月亮在 date 对应绝对时刻到地球质心的距离,单位千米。 -// Returns the distance from the Moon to Earth's center at the instant represented by date, in kilometers. -func EarthDistance(date time.Time) float64 { - jd := basic.Date2JD(date) - jde := basic.UTC2TT(jd) - return basic.MoonAway(jde) +// RiseTimeWithAtmosphere 月出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / moonrise time with explicit pressure and temperature. +func (module *Moon) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + if result, err, handled := civiltime.Event(date, ERR_NOT_TODAY, func(d time.Time) (time.Time, error) { + return module.RiseTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC) + }); handled { + return result, err + } + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + localJD := basic.Date2JD(date) + _, loc := date.Zone() + jd, err := basic.GetMoonRiseTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 月落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / moonset time with explicit pressure and temperature. +func (module *Moon) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + if result, err, handled := civiltime.Event(date, ERR_NOT_TODAY, func(d time.Time) (time.Time, error) { + return module.SetTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC) + }); handled { + return result, err + } + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + localJD := basic.Date2JD(date) + _, loc := date.Zone() + jd, err := basic.GetMoonSetTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) } diff --git a/moon/nodes.go b/moon/nodes.go index 74eaa05..c7683fe 100644 --- a/moon/nodes.go +++ b/moon/nodes.go @@ -7,23 +7,23 @@ import ( ) // AscendingNode 月球升交点黄经 / ascending node longitude of the Moon. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Moon) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 月球升交点黄经(截断版) / truncated ascending node longitude of the Moon. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 月球升交点黄经(n 变体) / n-parameterised ascending node longitude of the Moon. +func (module *Moon) AscendingNodeN(date time.Time, n int) float64 { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.MoonAscendingNodeN(jde, n) + return basic.MoonAscendingNodeNWithInstance(jde, n, module.inst) } // DescendingNode 月球降交点黄经 / descending node longitude of the Moon. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Moon) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 月球降交点黄经(截断版) / truncated descending node longitude of the Moon. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 月球降交点黄经(n 变体) / n-parameterised descending node longitude of the Moon. +func (module *Moon) DescendingNodeN(date time.Time, n int) float64 { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.MoonDescendingNodeN(jde, n) + return basic.MoonDescendingNodeNWithInstance(jde, n, module.inst) } diff --git a/moon/occultation.go b/moon/occultation.go index 076d346..01b2750 100644 --- a/moon/occultation.go +++ b/moon/occultation.go @@ -41,9 +41,12 @@ const ( // OccultationPartial 表示掩甚时有限目标盘面只有部分被覆盖。 // OccultationPartial means only part of a finite target disk is covered at greatest occultation. OccultationPartial = basic.OccultationPartial - // OccultationGrazing 表示两边缘相切,且没有正持续时间的重叠。 - // OccultationGrazing means the limbs are tangent without a positive-duration overlap. + // OccultationGrazing 表示掠掩:圆月面下两边缘相切;按月缘剖面求解时星迹始终在月缘起伏带内,可有多段遮挡。 + // OccultationGrazing is a graze: tangent limbs with a circular Moon, or a track inside the limb relief band with a limb profile. OccultationGrazing = basic.OccultationGrazing + // OccultationNone 表示月缘剖面下恒星未被遮挡,只出现在精密剖面结果中。 + // OccultationNone means the star is never hidden by the limb profile; it only appears in precise profile results. + OccultationNone = basic.OccultationNone ) // OccultationPlanet 标识有限盘面行星目标。 @@ -171,14 +174,12 @@ type StarOccultationInstant = basic.StarOccultationInstant // PlanetOccultationInstant contains the visible partial and total planetary footprints at one requested instant. type PlanetOccultationInstant = basic.PlanetOccultationInstant -// StarOccultationFootprintAt 计算指定时刻的精确恒星月掩可见足迹。 -// StarOccultationFootprintAt calculates the exact visible stellar-occultation footprint at one instant. -func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) { - return basic.StarOccultationFootprintAt(at, star) +// StarOccultationFootprintAt 计算指定时刻的精确恒星月掩可见足迹 / exact stellar occultation footprint at an instant. +func (module *Moon) StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) { + return basic.StarOccultationFootprintAtWithInstance(at, star, module.inst) } -// PlanetOccultationFootprintsAt 计算指定时刻的精确行星偏掩和全掩可见足迹。 -// PlanetOccultationFootprintsAt calculates the exact visible partial and total planetary footprints at one instant. -func PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) { - return basic.PlanetOccultationFootprintsAt(at, planet) +// PlanetOccultationFootprintsAt 计算指定时刻的精确行星偏掩和全掩可见足迹 / exact planetary occultation footprints at an instant. +func (module *Moon) PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) { + return basic.PlanetOccultationFootprintsAtWithInstance(at, planet, module.inst) } diff --git a/moon/occultation_limb_graze_test.go b/moon/occultation_limb_graze_test.go new file mode 100644 index 0000000..187dc86 --- /dev/null +++ b/moon/occultation_limb_graze_test.go @@ -0,0 +1,46 @@ +package moon + +// 外部基准:2017-09-18 轩辕十四掠掩(IOTA 实测 5 段)。内置剖面不保证段数,只钉住主遮挡段与实测最后一段相差不超过 1 s。 + +import ( + "math" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +func TestRegulusGraze20170918MatchesMainObservedInterval(t *testing.T) { + regulus := StarCoordinate{ + ID: "HIP 49669", RA: 152.09358042, Dec: 11.96719519, + Epoch: time.Date(1991, time.April, 2, 13, 30, 0, 0, time.UTC), Frame: CoordinateFrameICRS, + ProperMotionRACosDecMasPerYear: -248.73, ProperMotionDecMasPerYear: 5.59, + ParallaxMas: 41.13, RadialVelocityKmPerSecond: 6, + } + const lon, lat, height = 22.966666666666667, 38.98208333333333, 85 + start := time.Date(2017, time.September, 18, 3, 0, 0, 0, time.UTC) + events, err := FindStarOccultations(start, start.Add(time.Hour), regulus, lon, lat, height, + OccultationSearchOptions{LunarLimb: limb.Default()}) + if err != nil || len(events) != 1 { + t.Fatalf("FindStarOccultations() = %d events, %v; want one", len(events), err) + } + profile, err := StarOccultationLimbProfile(events[0], regulus, limb.Default(), StarOccultationLimbProfileOptions{}) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) == 0 { + t.Fatal("graze reports no hidden interval") + } + main := profile.Hidden[len(profile.Hidden)-1] + for _, pair := range []struct { + name string + got, observed time.Time + }{ + {"disappearance", main.Start, time.Date(2017, time.September, 18, 3, 29, 48, 0, time.UTC)}, + {"reappearance", main.End, time.Date(2017, time.September, 18, 3, 30, 2, 950_000_000, time.UTC)}, + } { + if d := pair.got.Sub(pair.observed).Seconds(); math.Abs(d) > 1 { + t.Errorf("%s %v is %.2f s from the observed %v", pair.name, pair.got, d, pair.observed) + } + } +} diff --git a/moon/occultation_limb_test.go b/moon/occultation_limb_test.go new file mode 100644 index 0000000..6e85226 --- /dev/null +++ b/moon/occultation_limb_test.go @@ -0,0 +1,117 @@ +package moon + +import ( + "math" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +// 月缘只应让接触时刻移动几秒:既要确实移动,又不能超过 15 s;掩甚与食型不变,模块副本不影响原模块。 +func TestOccultationLunarLimbShiftsContacts(t *testing.T) { + location := time.FixedZone("CST", 8*3600) + start := time.Date(2024, time.August, 21, 9, 30, 0, 0, location) + end := time.Date(2024, time.August, 21, 12, 0, 0, 0, location) + plain := NewWithInstance(nil) + limbed := plain.WithLunarLimb(limb.Default()) + a, err := plain.FindPlanetOccultations(start, end, OccultationSaturn, -30.072, 16.21, 0, OccultationSearchOptions{}) + if err != nil || len(a) != 1 { + t.Fatalf("circular: %v, %d events", err, len(a)) + } + b, err := limbed.FindPlanetOccultations(start, end, OccultationSaturn, -30.072, 16.21, 0, OccultationSearchOptions{}) + if err != nil || len(b) != 1 { + t.Fatalf("limb: %v, %d events", err, len(b)) + } + if !a[0].Greatest.Equal(b[0].Greatest) || a[0].Type != b[0].Type { + t.Fatal("greatest or type changed") + } + moved := false + for i, pair := range [][2]time.Time{ + {a[0].ExternalImmersion, b[0].ExternalImmersion}, {a[0].InternalImmersion, b[0].InternalImmersion}, + {a[0].InternalEmersion, b[0].InternalEmersion}, {a[0].ExternalEmersion, b[0].ExternalEmersion}, + } { + shift := pair[1].Sub(pair[0]).Seconds() + t.Logf("Saturn contact %d shift %+.3f s", i, shift) + if math.Abs(shift) > 15 { + t.Errorf("contact %d shift %.3f s too large", i, shift) + } + if math.Abs(shift) > 0.05 { + moved = true + } + } + if !moved { + t.Error("lunar limb did not move any contact") + } + viaOptions, err := plain.FindPlanetOccultations(start, end, OccultationSaturn, -30.072, 16.21, 0, + OccultationSearchOptions{LunarLimb: limb.Default()}) + if err != nil || len(viaOptions) != 1 || viaOptions[0] != b[0] { + t.Error("options limb differs from module limb") + } + again, _ := plain.FindPlanetOccultations(start, end, OccultationSaturn, -30.072, 16.21, 0, OccultationSearchOptions{}) + if len(again) != 1 || again[0] != a[0] { + t.Error("WithLunarLimb changed the original module") + } +} + +func TestStarOccultationLunarLimbShiftsContacts(t *testing.T) { + observer := Observer{Longitude: 121.4737, Latitude: 31.2304, Height: 4} + eventTime := time.Date(2026, 8, 2, 12, 0, 0, 0, time.UTC) + ra, dec := occultationTestMoonTopocentricRaDec(occultationTestTimeToTT(eventTime), observer, -1) + star := StarCoordinate{ID: "synthetic", RA: ra + 0.1, Dec: dec + 0.1, Epoch: eventTime, Frame: CoordinateFrameApparentOfDate} + start, end := eventTime.Add(-3*time.Hour), eventTime.Add(3*time.Hour) + plain := NewWithInstance(nil) + a, err := plain.FindStarOccultations(start, end, star, observer.Longitude, observer.Latitude, observer.Height, OccultationSearchOptions{MaxEvents: 1}) + if err != nil || len(a) != 1 { + t.Fatalf("circular: %v, %d events", err, len(a)) + } + b, err := plain.WithLunarLimb(limb.Default()).FindStarOccultations(start, end, star, observer.Longitude, observer.Latitude, observer.Height, OccultationSearchOptions{MaxEvents: 1}) + if err != nil || len(b) != 1 { + t.Fatalf("limb: %v, %d events", err, len(b)) + } + if !a[0].Greatest.Equal(b[0].Greatest) { + t.Fatal("greatest changed") + } + for i, pair := range [][2]time.Time{{a[0].Immersion, b[0].Immersion}, {a[0].Emersion, b[0].Emersion}} { + shift := pair[1].Sub(pair[0]).Seconds() + t.Logf("star contact %d shift %+.3f s", i, shift) + if math.Abs(shift) > 15 { + t.Errorf("contact %d shift %.3f s too large", i, shift) + } + } +} + +func TestStarOccultationPathLunarLimbShiftsGrazeLines(t *testing.T) { + star := StarCoordinate{ + ID: "HR 4799", RA: 189.1975, Dec: -5.831944444444, + Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -28, ProperMotionDecMasPerYear: -18, + } + start := time.Date(2025, 6, 4, 16, 0, 0, 0, time.UTC) + end := time.Date(2025, 6, 5, 16, 0, 0, 0, time.UTC) + options := OccultationPathOptions{Step: 5 * time.Minute, TargetSpacingKM: 200, DisableFootprints: true, DisableRiseSet: true} + plain := NewWithInstance(nil) + circular, err := plain.FindStarOccultationPaths(start, end, star, options) + if err != nil || len(circular) != 1 { + t.Fatalf("circular: %v %d", err, len(circular)) + } + limbed, err := plain.WithLunarLimb(limb.Default()).FindStarOccultationPaths(start, end, star, options) + if err != nil || len(limbed) != 1 { + t.Fatalf("limbed: %v %d", err, len(limbed)) + } + for side, pair := range [][2][]OccultationPathPoint{{circular[0].NorthernLimit, limbed[0].NorthernLimit}, {circular[0].SouthernLimit, limbed[0].SouthernLimit}} { + worst, moved := 0.0, 0 + for i := range pair[0] { + a, b := pair[0][i], pair[1][i] + d := math.Hypot((a.Longitude-b.Longitude)*math.Cos(a.Latitude*math.Pi/180), a.Latitude-b.Latitude) * 111.2 + if d > 0 { + moved++ + } + worst = math.Max(worst, d) + } + t.Logf("side %d: %d/%d moved, max %.3f km", side, moved, len(pair[0]), worst) + if worst > 15 || moved == 0 { + t.Errorf("side %d: implausible graze shift (max %.3f km, moved %d)", side, worst, moved) + } + } +} diff --git a/moon/occultation_path.go b/moon/occultation_path.go index 0d848ec..77ce14b 100644 --- a/moon/occultation_path.go +++ b/moon/occultation_path.go @@ -6,10 +6,10 @@ import ( "b612.me/astro/basic" ) -// FindStarOccultationPaths 搜索单颗点源恒星月掩的全球掩带。 -// 调用者显式提供坐标;函数不会加载内嵌星表。查询端点使用核心求解器的 10 ms 数值选择容差。 -// FindStarOccultationPaths searches the global lunar-occultation footprint of one point-source star. -// Callers provide the coordinate explicitly; this function does not load the embedded star catalog. Query endpoints use the core solver's 10 ms numerical selection tolerance. -func FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) { - return basic.FindStarOccultationPaths(start, end, star, options) +// FindStarOccultationPaths 搜索单颗点源恒星月掩的全球掩带 / global paths of a point-source stellar occultation. +func (module *Moon) FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) { + if options.LunarLimb == nil { + options.LunarLimb = module.lunarLimb + } + return basic.FindStarOccultationPathsWithInstance(start, end, star, options, module.inst) } diff --git a/moon/occultation_planet.go b/moon/occultation_planet.go index 200667b..d3d3151 100644 --- a/moon/occultation_planet.go +++ b/moon/occultation_planet.go @@ -6,20 +6,14 @@ import ( "b612.me/astro/basic" ) -// FindPlanetOccultations 搜索固定地点的有限盘面行星月掩。 -// 经度东为正、纬度北为正,单位为度;高度为平均海平面以上米数。圆形目标使用行星赤道半径,忽略行星环。 -// FindPlanetOccultations searches finite-disk lunar occultations of one planet at a fixed observing site. -// Longitude is east-positive in degrees, latitude is north-positive in degrees, and height is above mean sea level in meters. The circular target uses the planet's equatorial body radius; rings are excluded. -func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, +// FindPlanetOccultations 搜索固定地点的有限盘面行星月掩 / finite-disk planetary occultations at a fixed site. +func (module *Moon) FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { - return basic.FindPlanetOccultations(start, end, planet, longitude, latitude, height, options) + return basic.FindPlanetOccultationsWithInstance(start, end, planet, longitude, latitude, height, module.occultationOptions(options), module.inst) } -// FindBestPlanetOccultations 返回窗口内每次有限盘面行星月掩的全球海平面几何掩甚点。 -// 地平线可见性只报告、不参与点选择;查询端点使用核心求解器的 10 ms 数值选择容差。 -// FindBestPlanetOccultations returns the global geometric greatest sea-level point for every finite-disk occultation of one planet in the window. -// Horizon visibility is reported but does not affect point selection. Query endpoints use the core solver's 10 ms numerical selection tolerance. -func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, +// FindBestPlanetOccultations 返回窗口内每次有限盘面行星月掩的全球海平面几何掩甚点 / global greatest points of planetary occultations in a window. +func (module *Moon) FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { - return basic.FindBestPlanetOccultations(start, end, planet, options) + return basic.FindBestPlanetOccultationsWithInstance(start, end, planet, module.occultationOptions(options), module.inst) } diff --git a/moon/occultation_planet_diagram.go b/moon/occultation_planet_diagram.go index 8eb8a99..6ad1381 100644 --- a/moon/occultation_planet_diagram.go +++ b/moon/occultation_planet_diagram.go @@ -1,6 +1,8 @@ package moon -import "b612.me/astro/basic" +import ( + "b612.me/astro/basic" +) // PlanetOccultationDiagramOptions 控制本地行星月掩轨迹采样。 // PlanetOccultationDiagramOptions controls local planetary-occultation track sampling. @@ -14,11 +16,10 @@ type PlanetOccultationDiagramFrame = basic.PlanetOccultationDiagramFrame // PlanetOccultationDiagramResult contains a fixed-site Moon-centered planetary track. type PlanetOccultationDiagramResult = basic.PlanetOccultationDiagramResult -// PlanetOccultationDiagram 为已求解的固定地点行星月掩计算图形几何数据。 -// PlanetOccultationDiagram computes diagram geometry for an already solved fixed-site planetary occultation. -func PlanetOccultationDiagram( +// PlanetOccultationDiagram 为已求解的固定地点行星月掩计算图形几何数据 / diagram geometry for a solved local planetary occultation. +func (module *Moon) PlanetOccultationDiagram( info PlanetOccultationInfo, options PlanetOccultationDiagramOptions, ) PlanetOccultationDiagramResult { - return basic.PlanetOccultationDiagram(info, options) + return basic.PlanetOccultationDiagramWithInstance(info, options, module.inst) } diff --git a/moon/occultation_planet_path.go b/moon/occultation_planet_path.go index 3aee907..0db5984 100644 --- a/moon/occultation_planet_path.go +++ b/moon/occultation_planet_path.go @@ -6,11 +6,11 @@ import ( "b612.me/astro/basic" ) -// FindPlanetOccultationPaths 搜索窗口内每次有限盘面行星月掩的完整全球外接触掩带。 -// 查询端点使用核心求解器的 10 ms 数值选择容差。 -// FindPlanetOccultationPaths searches the complete global outer-contact footprint of every finite-disk planetary occultation selected by the window. -// Query endpoints use the core solver's 10 ms numerical selection tolerance. -func FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, +// FindPlanetOccultationPaths 搜索窗口内每次有限盘面行星月掩的完整全球外接触掩带 / global paths of finite-disk planetary occultations in a window. +func (module *Moon) FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, options OccultationPathOptions) ([]PlanetOccultationPath, error) { - return basic.FindPlanetOccultationPaths(start, end, planet, options) + if options.LunarLimb == nil { + options.LunarLimb = module.lunarLimb + } + return basic.FindPlanetOccultationPathsWithInstance(start, end, planet, options, module.inst) } diff --git a/moon/occultation_star.go b/moon/occultation_star.go index 6deac1a..78fc9a9 100644 --- a/moon/occultation_star.go +++ b/moon/occultation_star.go @@ -6,20 +6,13 @@ import ( "b612.me/astro/basic" ) -// FindStarOccultations 搜索单颗点源恒星的月掩星。 -// -// 输入坐标从历元传播并转换到当日视坐标系。此函数不会加载内嵌星表;需要星表搜索时,调用者必须显式加载并选择恒星。经度东为正、纬度北为正,单位为度;高度为平均海平面以上米数。 -// FindStarOccultations searches for lunar occultations of one point-source star. -// The input coordinate is propagated from its epoch and converted to the apparent frame of date. This function never loads the embedded star catalog; callers that need catalog searches must load and select stars explicitly. Longitude is east-positive in degrees, latitude is north-positive in degrees, and height is the observer elevation above mean sea level in meters. -func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, +// FindStarOccultations 搜索单颗点源恒星的月掩星 / occultations of a point-source star at a fixed site. +func (module *Moon) FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, options OccultationSearchOptions) ([]StarOccultationInfo, error) { - return basic.FindStarOccultations(start, end, star, longitude, latitude, height, options) + return basic.FindStarOccultationsWithInstance(start, end, star, longitude, latitude, height, module.occultationOptions(options), module.inst) } -// FindBestStarOccultations 返回窗口内每次恒星月掩的全球海平面几何掩甚点。 -// 点选择不使用地平线或可见性评分;查询端点使用核心求解器的 10 ms 数值选择容差。 -// FindBestStarOccultations returns the global geometric greatest sea-level point for every stellar occultation whose greatest instant is in the window. -// The point is selected without a horizon or visibility score. Query endpoints use the core solver's 10 ms numerical selection tolerance. -func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) { - return basic.FindBestStarOccultations(start, end, star, options) +// FindBestStarOccultations 返回窗口内每次恒星月掩的全球海平面几何掩甚点 / global greatest points of stellar occultations in a window. +func (module *Moon) FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) { + return basic.FindBestStarOccultationsWithInstance(start, end, star, module.occultationOptions(options), module.inst) } diff --git a/moon/occultation_star_diagram.go b/moon/occultation_star_diagram.go index 738e6a6..bb1e2f9 100644 --- a/moon/occultation_star_diagram.go +++ b/moon/occultation_star_diagram.go @@ -1,6 +1,8 @@ package moon -import "b612.me/astro/basic" +import ( + "b612.me/astro/basic" +) // StarOccultationDiagramOptions 控制本地恒星月掩轨迹采样。 // StarOccultationDiagramOptions controls local stellar-occultation track sampling. @@ -14,12 +16,39 @@ type StarOccultationDiagramFrame = basic.StarOccultationDiagramFrame // StarOccultationDiagramResult contains a fixed-site Moon-centered stellar track. type StarOccultationDiagramResult = basic.StarOccultationDiagramResult -// StarOccultationDiagram 为已求解的固定地点恒星月掩计算图形几何数据。 -// StarOccultationDiagram computes diagram geometry for an already solved fixed-site stellar occultation. -func StarOccultationDiagram( +// StarOccultationDiagram 为已求解的固定地点恒星月掩计算图形几何数据 / diagram geometry for a solved local stellar occultation. +func (module *Moon) StarOccultationDiagram( info StarOccultationInfo, star StarCoordinate, options StarOccultationDiagramOptions, ) StarOccultationDiagramResult { - return basic.StarOccultationDiagram(info, star, options) + return basic.StarOccultationDiagramWithInstance(info, star, options, module.inst) +} + +// LunarLimb 月缘剖面,nil 为圆月面,内置数据表在 moon/limb / lunar limb profile; nil means a circular limb. +type LunarLimb = basic.LunarLimb + +// StarOccultationLimbProfileOptions 控制精密恒星月掩的星迹采样 / sampling of a precise stellar occultation. +type StarOccultationLimbProfileOptions = basic.StarOccultationLimbProfileOptions + +// StarOccultationLimbSample 星迹上一个时刻的月缘几何 / limb geometry at one instant on the star track. +type StarOccultationLimbSample = basic.StarOccultationLimbSample + +// OccultationInterval 一段连续遮挡 / one continuous hidden interval. +type OccultationInterval = basic.OccultationInterval + +// StarOccultationLimbProfileResult 一次恒星月掩沿星迹的月缘剖面与全部遮挡区间 / limb profile and every hidden interval of a stellar occultation. +type StarOccultationLimbProfileResult = basic.StarOccultationLimbProfileResult + +// StarOccultationLimbProfile 精密恒星月掩;limb 为 nil 时用模块月缘 / precise stellar occultation; nil limb uses the module profile. +func (module *Moon) StarOccultationLimbProfile(info StarOccultationInfo, star StarCoordinate, limb LunarLimb, options StarOccultationLimbProfileOptions) (StarOccultationLimbProfileResult, error) { + if limb == nil { + limb = module.lunarLimb + } + return basic.StarOccultationLimbProfileWithInstance(info, star, limb, options, module.inst) +} + +// StarOccultationLimbProfileInUT1 返回精密恒星月掩的 UT1 时刻副本 / copy in UT1 of a precise stellar occultation. +func (module *Moon) StarOccultationLimbProfileInUT1(profile StarOccultationLimbProfileResult) StarOccultationLimbProfileResult { + return StarOccultationLimbProfileInUT1(profile) } diff --git a/moon/occultation_ut1.go b/moon/occultation_ut1.go index 98c8539..523fb86 100644 --- a/moon/occultation_ut1.go +++ b/moon/occultation_ut1.go @@ -3,7 +3,6 @@ package moon import ( "time" - "b612.me/astro" "b612.me/astro/basic" ) @@ -14,7 +13,7 @@ func occultationUT1Label(t time.Time) time.Time { if t.IsZero() { return t } - return astro.LabelIn(astro.TimeScaleUT1, t) + return basic.LabelIn(basic.TimeScaleUT1, t) } func occultationUT1PathPoint(p basic.OccultationPathPoint) basic.OccultationPathPoint { @@ -129,3 +128,23 @@ func PlanetOccultationInfoInUT1(info basic.PlanetOccultationInfo) basic.PlanetOc info.ExternalEmersion = occultationUT1Label(info.ExternalEmersion) return info } + +// StarOccultationLimbProfileInUT1 返回精密恒星月掩的 UT1 时刻副本 / copy in UT1 of a precise stellar occultation. +func StarOccultationLimbProfileInUT1(profile basic.StarOccultationLimbProfileResult) basic.StarOccultationLimbProfileResult { + profile.Occultation = StarOccultationInfoInUT1(profile.Occultation) + segments := make([][]basic.StarOccultationLimbSample, len(profile.Segments)) + for i, segment := range profile.Segments { + segments[i] = append([]basic.StarOccultationLimbSample(nil), segment...) + for j := range segments[i] { + segments[i][j].Time = occultationUT1Label(segments[i][j].Time) + } + } + profile.Segments = segments + hidden := append([]basic.OccultationInterval(nil), profile.Hidden...) + for i := range hidden { + hidden[i].Start = occultationUT1Label(hidden[i].Start) + hidden[i].End = occultationUT1Label(hidden[i].End) + } + profile.Hidden = hidden + return profile +} diff --git a/moon/parallactic.go b/moon/parallactic.go index fe4c0fc..ea18e2d 100644 --- a/moon/parallactic.go +++ b/moon/parallactic.go @@ -7,8 +7,6 @@ import ( ) // ParallacticAngle 月亮视差角(天顶方向角) / lunar parallactic angle. -// -// 对月亮使用现有站心视时角和站心视赤纬链路,因此会显式依赖观测者经纬度。 -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon, lat), ApparentDec(date, lon, lat), lat) +func (module *Moon) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon, lat), module.ApparentDec(date, lon, lat), lat) } diff --git a/moon/phase.go b/moon/phase.go index 2ff93d0..22210ae 100644 --- a/moon/phase.go +++ b/moon/phase.go @@ -7,26 +7,23 @@ import ( ) // BrightLimbPositionAngle 月亮明亮边缘位置角,单位度 / position angle of the Moon's bright limb in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Moon) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 月亮明亮边缘位置角(截断版),单位度 / truncated position angle of the Moon's bright limb in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.MoonBrightLimbPositionAngleN(observationTT(date), n) +// BrightLimbPositionAngleN 月亮明亮边缘位置角(n 变体),单位度 / n-parameterised position angle of the Moon's bright limb in degrees. +func (module *Moon) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.MoonBrightLimbPositionAngleNWithInstance(observationTT(date), n, module.inst) } // TopocentricBrightLimbPositionAngle 月亮站心明亮边缘位置角,单位度 / topocentric position angle of the Moon's bright limb in degrees. -// -// date 为观测时刻;observerLon/observerLat 为观测者经纬度,东正西负、北正南负;height 为椭球高(大地高),单位米。 -// date is the observing instant; observerLon/observerLat are east-positive and north-positive; height is observer elevation in meters. -func TopocentricBrightLimbPositionAngle(date time.Time, observerLon, observerLat, height float64) float64 { - return TopocentricBrightLimbPositionAngleN(date, observerLon, observerLat, height, -1) +func (module *Moon) TopocentricBrightLimbPositionAngle(date time.Time, observerLon, observerLat, height float64) float64 { + return module.TopocentricBrightLimbPositionAngleN(date, observerLon, observerLat, height, -1) } -// TopocentricBrightLimbPositionAngleN 月亮站心明亮边缘位置角(截断版),单位度 / truncated topocentric position angle of the Moon's bright limb in degrees. -func TopocentricBrightLimbPositionAngleN(date time.Time, observerLon, observerLat, height float64, n int) float64 { - return basic.MoonTopocentricBrightLimbPositionAngleN(observationTT(date), observerLon, observerLat, height, n) +// TopocentricBrightLimbPositionAngleN 月亮站心明亮边缘位置角(n 变体),单位度 / n-parameterised topocentric position angle of the Moon's bright limb in degrees. +func (module *Moon) TopocentricBrightLimbPositionAngleN(date time.Time, observerLon, observerLat, height float64, n int) float64 { + return basic.MoonTopocentricBrightLimbPositionAngleNWithInstance(observationTT(date), observerLon, observerLat, height, n, module.inst) } func observationTT(date time.Time) float64 { diff --git a/moon/physical.go b/moon/physical.go index 0f4b9e0..febfe42 100644 --- a/moon/physical.go +++ b/moon/physical.go @@ -25,14 +25,14 @@ type PhysicalInfo struct { } // Physical 月球物理观测参数 / physical observing parameters of the Moon. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Moon) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 月球物理观测参数(截断版) / truncated physical observing parameters of the Moon. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 月球物理观测参数(n 变体) / n-parameterised physical observing parameters of the Moon. +func (module *Moon) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.MoonPhysicalN(basic.UTC2TT(jd), n) + info := basic.MoonPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ OpticalLongitude: info.OpticalLongitude, OpticalLatitude: info.OpticalLatitude, diff --git a/moon/physical_topocentric.go b/moon/physical_topocentric.go index 79de323..5c64861 100644 --- a/moon/physical_topocentric.go +++ b/moon/physical_topocentric.go @@ -7,16 +7,13 @@ import ( ) // TopocentricPhysical 月球站心物理观测参数 / topocentric physical observing parameters of the Moon. -// -// date 为观测时刻;observerLon/observerLat 为观测者经纬度,东正西负、北正南负;height 为椭球高(大地高),单位米。 -// date is the observing instant; observerLon/observerLat are east-positive and north-positive; height is observer elevation in meters. -func TopocentricPhysical(date time.Time, observerLon, observerLat, height float64) PhysicalInfo { - return TopocentricPhysicalN(date, observerLon, observerLat, height, -1) +func (module *Moon) TopocentricPhysical(date time.Time, observerLon, observerLat, height float64) PhysicalInfo { + return module.TopocentricPhysicalN(date, observerLon, observerLat, height, -1) } -// TopocentricPhysicalN 月球站心物理观测参数(截断版) / truncated topocentric physical observing parameters of the Moon. -func TopocentricPhysicalN(date time.Time, observerLon, observerLat, height float64, n int) PhysicalInfo { - info := basic.MoonTopocentricPhysicalN(observationTT(date), observerLon, observerLat, height, n) +// TopocentricPhysicalN 月球站心物理观测参数(n 变体) / n-parameterised topocentric physical observing parameters of the Moon. +func (module *Moon) TopocentricPhysicalN(date time.Time, observerLon, observerLat, height float64, n int) PhysicalInfo { + info := basic.MoonTopocentricPhysicalNWithInstance(observationTT(date), observerLon, observerLat, height, n, module.inst) return PhysicalInfo{ OpticalLongitude: info.OpticalLongitude, OpticalLatitude: info.OpticalLatitude, diff --git a/moon/refraction.go b/moon/refraction.go index 536c7b3..acea781 100644 --- a/moon/refraction.go +++ b/moon/refraction.go @@ -7,11 +7,11 @@ import ( ) // ApparentAltitude 月亮视高度角 / apparent lunar altitude. -func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { - return basic.ApparentAltitude(Altitude(date, lon, lat), pressureHPa, temperatureC) +func (module *Moon) ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return basic.ApparentAltitude(module.Altitude(date, lon, lat), pressureHPa, temperatureC) } // ApparentZenith 月亮视天顶距 / apparent lunar zenith distance. -func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { - return 90 - ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) +func (module *Moon) ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return 90 - module.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) } diff --git a/moon/rise_set_altitude.go b/moon/rise_set_altitude.go new file mode 100644 index 0000000..00b825b --- /dev/null +++ b/moon/rise_set_altitude.go @@ -0,0 +1,42 @@ +package moon + +import ( + "time" + + "b612.me/astro/basic" +) + +// AltitudeLimb 取高度的位置(上缘、中心、下缘)/ limb used for the altitude. +type AltitudeLimb = basic.AltitudeLimb + +const ( + LimbUpper = basic.AltitudeLimbUpper + LimbCenter = basic.AltitudeLimbCenter + LimbLower = basic.AltitudeLimbLower +) + +// AltitudeOptions 升落选项;零值为上缘、标准气象、计入折射 / altitude crossing options. +type AltitudeOptions = basic.AltitudeCrossingOptions + +// AltitudeState 一个民用日内的穿越状态 / crossing state over one civil day. +type AltitudeState = basic.AltitudeCrossingState + +const ( + AltitudeCrosses = basic.AltitudeCrosses + AltitudeAlwaysAbove = basic.AltitudeAlwaysAbove + AltitudeAlwaysBelow = basic.AltitudeAlwaysBelow + AltitudeTangent = basic.AltitudeTangent +) + +// AltitudeCrossingResult date 所在民用日内首次升、落与残差范围(度)/ first rise and set on the civil day. +type AltitudeCrossingResult = basic.AltitudeCrossingResult + +// RiseSet 月球在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制月缘与大气 / lunar rise and set on the civil day. +func (module *Moon) RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return basic.SkyAltitudeCrossingOnDate(basic.SkyMoon, date, lon, lat, height, -basic.HeightDegreeByLat(height, lat), options, module.inst) +} + +// RiseSet 月球在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制月缘与大气 / lunar rise and set on the civil day. +func RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return defaultModule.RiseSet(date, lon, lat, height, options) +} diff --git a/moon/rise_set_altitude_test.go b/moon/rise_set_altitude_test.go new file mode 100644 index 0000000..bac06dc --- /dev/null +++ b/moon/rise_set_altitude_test.go @@ -0,0 +1,75 @@ +// 被测契约:默认选项与 RiseTime/SetTime 一致;夏令时切换日按真实日长取当天事件;实例方法使用绑定实例。 +package moon + +import ( + "testing" + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +func TestRiseSetMatchesRiseTimeSetTime(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + for i := 0; i < 30; i++ { + date := time.Date(2025, 1, 1, 12, 0, 0, 0, loc).AddDate(0, 0, i*12) + got, err := RiseSet(date, 116.4, 39.9, 50, basic.AltitudeCrossingOptions{}) + if err != nil { + t.Fatal(err) + } + checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, 116.4, 39.9, 50, true) }) + checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, 116.4, 39.9, 50, true) }) + } +} + +func TestRiseSetDaylightSavingDays(t *testing.T) { + loc, err := time.LoadLocation("America/New_York") + if err != nil { + t.Skip(err) + } + for _, date := range []time.Time{ + time.Date(2023, 3, 12, 12, 0, 0, 0, loc), + time.Date(2023, 11, 5, 12, 0, 0, 0, loc), + } { + got, err := RiseSet(date, -74, 40.7, 0, basic.AltitudeCrossingOptions{}) + if err != nil { + t.Fatal(err) + } + checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, -74, 40.7, 0, true) }) + checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, -74, 40.7, 0, true) }) + for _, ev := range []time.Time{got.Rise, got.Set} { + if !ev.IsZero() && (ev.Location() != loc || ev.YearDay() != date.YearDay()) { + t.Errorf("%s: event %v outside civil day", date.Format("2006-01-02"), ev) + } + } + } +} + +func TestRiseSetUsesModuleInstance(t *testing.T) { + date := time.Date(2025, 3, 20, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + got, err := NewWithInstance(ephem.NewModelInstance(ephem.Legacy)).RiseSet(date, 116.4, 39.9, 0, basic.AltitudeCrossingOptions{}) + if err != nil { + t.Fatal(err) + } + want, err := basic.SkyAltitudeCrossingOnDate(basic.SkyMoon, date, 116.4, 39.9, 0, 0, basic.AltitudeCrossingOptions{}, ephem.NewModelInstance(ephem.Legacy)) + if err != nil { + t.Fatal(err) + } + if !got.Rise.Equal(want.Rise) || !got.Set.Equal(want.Set) { + t.Errorf("instance result %+v, want %+v", got, want) + } +} + +func checkEvent(t *testing.T, date time.Time, kind string, got time.Time, has bool, ref func() (time.Time, error)) { + t.Helper() + want, err := ref() + if has != (err == nil) { + t.Errorf("%s %s: has=%v, reference err=%v", date.Format("2006-01-02"), kind, has, err) + return + } + if has { + if d := got.Sub(want); d > 100*time.Millisecond || d < -100*time.Millisecond { + t.Errorf("%s %s: %v, reference %v", date.Format("2006-01-02"), kind, got, want) + } + } +} diff --git a/moon/svg/occultation.go b/moon/svg/occultation.go index 2b648d8..166c71c 100644 --- a/moon/svg/occultation.go +++ b/moon/svg/occultation.go @@ -3,6 +3,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "errors" "fmt" @@ -253,7 +254,8 @@ func renderOccultationPathSVG( } writePlanetOccultationEventsPanel(&b, *planetPath, layout, options) } - writeStarOccultationFooter(&b, layout, options, flags, occultationScaleInstant(path, planetPath)) + writeStarOccultationFooter(&b, layout, options, flags, occultationScaleInstant(path, planetPath), + occultationLimbApplied(path, planetPath)) b.WriteString(``) return b.String(), nil } @@ -757,16 +759,26 @@ func occultationScaleInstant(path moon.StarOccultationPath, planetPath *moon.Pla return path.Greatest.Time } +// occultationLimbApplied 报告图中界线是否按月缘剖面修正。 +func occultationLimbApplied(path moon.StarOccultationPath, planetPath *moon.PlanetOccultationPath) bool { + if planetPath != nil { + return planetPath.LunarLimbApplied + } + return path.LunarLimbApplied +} + func writeStarOccultationFooter( b *strings.Builder, layout starOccultationSVGLayout, options StarOccultationSVGOptions, flags occultationSVGTextFlags, instant time.Time, + limbApplied bool, ) { maxWidth := layout.width - 80 lineHeight := svgchart.FooterLineHeight(11) - scaleNote := timenote.Scale(options.TimeScale, instant, options.Location, options.Language) + scaleNote := limbnote.Join(timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + limbnote.Limits(limbApplied, options.Language), options.Language) // 页脚默认排在画布底边之内(与既有版面一致),说明行放得下时声明单独占最后一行; // 自定义说明太长时改用按字号留白的行数上限:截断说明也要保住时标声明和底边留白。 lines := occultationWrappedTextLines(starOccultationSVGFooter(options), flags.footerNote, maxWidth, 11) diff --git a/moon/svg/occultation_detailed.go b/moon/svg/occultation_detailed.go index 1066f9d..0b6d0f1 100644 --- a/moon/svg/occultation_detailed.go +++ b/moon/svg/occultation_detailed.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "errors" "fmt" @@ -11,6 +12,7 @@ import ( "b612.me/astro" "b612.me/astro/basic" + "b612.me/astro/internal/geodata" "b612.me/astro/internal/svgchart" "b612.me/astro/internal/svgmap" "b612.me/astro/moon" @@ -217,7 +219,7 @@ func occultationDetailedMoonRows(at time.Time, language string) []svgchart.Panel jde := basic.UTC2TT(basic.Date2JD(at.UTC())) ra, dec := basic.HMoonTrueRaDec(jde) sd := basic.MoonSemidiameter(jde) - hp := math.Asin(math.Sin(sd/3600*math.Pi/180)*6378.137/1737.4) * 180 / math.Pi * 3600 + hp := math.Asin(math.Sin(sd/3600*math.Pi/180)*geodata.EarthWGS84RadiusKM/1737.4) * 180 / math.Pi * 3600 rows := []svgchart.PanelRow{ {Label: "赤经 R.A.", Value: occultationDetailedRA(ra)}, {Label: "赤纬 Dec.", Value: occultationDetailedDec(dec)}, @@ -363,7 +365,8 @@ func renderOccultationDetailedSVG( return "", err } writeOccultationDetailedGrid(&b, view, blocks) - writeOccultationDetailedFooter(&b, view, options, occultationScaleInstant(path, planetPath)) + writeOccultationDetailedFooter(&b, view, options, occultationScaleInstant(path, planetPath), + occultationLimbApplied(path, planetPath)) b.WriteString(``) return b.String(), nil } @@ -433,7 +436,7 @@ func writeOccultationDetailedSummary( } } -func writeOccultationDetailedFooter(b *strings.Builder, view occultationDetailedView, options OccultationDetailedSVGOptions, instant time.Time) { +func writeOccultationDetailedFooter(b *strings.Builder, view occultationDetailedView, options OccultationDetailedSVGOptions, instant time.Time, limbApplied bool) { text := options.FooterNote if text == "" { if options.Language == starOccultationSVGLanguageEnglish { @@ -448,7 +451,8 @@ func writeOccultationDetailedFooter(b *strings.Builder, view occultationDetailed if options.FooterNote != "" { lines = svgchart.WrapText(text, maxWidth, 11) } - lines = svgchart.FooterLinesWithScale(lines, timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + lines = svgchart.FooterLinesWithScale(lines, limbnote.Join(timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + limbnote.Limits(limbApplied, options.Language), options.Language), maxWidth, 11, svgchart.BaselineLineLimit(11, svgchart.FooterLineHeight(11), view.footerY, view.height-svgchart.FooterBottomPadding(11))) diff --git a/moon/svg/occultation_detailed_test.go b/moon/svg/occultation_detailed_test.go index f7a2a6e..734e455 100644 --- a/moon/svg/occultation_detailed_test.go +++ b/moon/svg/occultation_detailed_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "b612.me/astro/internal/ephemtest" "b612.me/astro/moon" ) @@ -155,6 +156,7 @@ func TestOccultationDetailedSVGPanelValuesGolden(t *testing.T) { t.Fatalf("StarOccultationDetailedSVG() error = %v", err) } star := occultationSVGDetailedPanelRows(t, starDiagram) + semiValues := ephemtest.NewValues(t, "occultation_detailed_panel") for _, expected := range []struct { title, label, value string }{ @@ -172,7 +174,12 @@ func TestOccultationDetailedSVGPanelValuesGolden(t *testing.T) { {"天平动", "纬天平动 b", "-2.79°"}, {"天平动", "自转轴位置角 c", "-21.92°"}, } { - if got := occultationSVGPanelValue(t, star, expected.title, expected.label); got != expected.value { + got := occultationSVGPanelValue(t, star, expected.title, expected.label) + // 月心地心坐标随模型变化,与行星坐标行一样按模型取期望值。 + if expected.title == "月亮(地心坐标)" && strings.HasPrefix(expected.label, "赤") { + expected.value = semiValues.WantString("moon."+expected.label, expected.value, got) + } + if got != expected.value { t.Fatalf("stellar panel %q row %q = %q, want %q", expected.title, expected.label, got, expected.value) } } @@ -195,7 +202,12 @@ func TestOccultationDetailedSVGPanelValuesGolden(t *testing.T) { {"接触时刻", "全掩始", "10:30:00"}, {"接触时刻", "全掩终", "12:30:00"}, } { - if got := occultationSVGPanelValue(t, planet, expected.title, expected.label); got != expected.value { + got := occultationSVGPanelValue(t, planet, expected.title, expected.label) + // 新模型视位置更准,坐标行按模型取期望值。 + if strings.HasPrefix(expected.label, "赤") { + expected.value = semiValues.WantString("planet."+expected.label, expected.value, got) + } + if got != expected.value { t.Fatalf("planetary panel %q row %q = %q, want %q", expected.title, expected.label, got, expected.value) } } diff --git a/moon/svg/occultation_limb_custom_test.go b/moon/svg/occultation_limb_custom_test.go new file mode 100644 index 0000000..c10d2b5 --- /dev/null +++ b/moon/svg/occultation_limb_custom_test.go @@ -0,0 +1,76 @@ +package svg + +import ( + "strings" + "testing" + "time" + + "b612.me/astro/basic" + "b612.me/astro/moon" +) + +type constantLimb float64 + +func (r constantLimb) LimbRadiusKM(_, _, _ float64) (float64, bool) { return float64(r), true } + +type failingLimb struct{} + +func (failingLimb) LimbRadiusKM(_, _, _ float64) (float64, bool) { return 0, false } + +func TestLocalOccultationSVGAcceptsCustomLimbAndRejectsFailedLimbFlag(t *testing.T) { + start := time.Date(2034, time.November, 21, 0, 0, 0, 0, time.UTC) + end := time.Date(2034, time.November, 23, 0, 0, 0, 0, time.UTC) + find := func(profile basic.LunarLimb) moon.PlanetOccultationInfo { + t.Helper() + events, err := moon.FindPlanetOccultations(start, end, moon.OccultationJupiter, + -42.36358358336173, 21.762064240049245, 0, moon.OccultationSearchOptions{LunarLimb: profile}) + if err != nil || len(events) != 1 { + t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err) + } + return events[0] + } + circular := find(nil) + + custom := find(constantLimb(1750)) + if !custom.LunarLimbApplied { + t.Fatal("constant 1750 km limb: LunarLimbApplied = false, want true") + } + if custom.ExternalImmersion.Equal(circular.ExternalImmersion) { + t.Fatal("constant 1750 km limb did not move the contact") + } + diagram, err := LocalPlanetOccultationSVG(custom, LocalPlanetOccultationSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatalf("LocalPlanetOccultationSVG(custom limb) error = %v", err) + } + if !strings.Contains(diagram, "月缘剖面") { + t.Fatal("custom limb chart does not declare the limb profile") + } + + failed := find(failingLimb{}) + if failed.LunarLimbApplied { + t.Fatal("failing limb: LunarLimbApplied = true, want false") + } + if !failed.ExternalImmersion.Equal(circular.ExternalImmersion) || !failed.InternalEmersion.Equal(circular.InternalEmersion) { + t.Fatal("failing limb changed the circular contacts") + } + diagram, err = LocalPlanetOccultationSVG(failed, LocalPlanetOccultationSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatalf("LocalPlanetOccultationSVG(failed limb) error = %v", err) + } + if strings.Contains(diagram, "月缘剖面") || !strings.Contains(diagram, "圆月面") { + t.Fatal("failed limb chart must declare the circular limb") + } +} + +func TestOccultationPathLimbFlagRequiresCorrectedLimit(t *testing.T) { + start := time.Date(2034, time.November, 21, 0, 0, 0, 0, time.UTC) + end := time.Date(2034, time.November, 23, 0, 0, 0, 0, time.UTC) + paths, err := moon.FindPlanetOccultationPaths(start, end, moon.OccultationJupiter, + moon.OccultationPathOptions{Step: 10 * time.Minute, TargetSpacingKM: 400, LunarLimb: failingLimb{}}) + if err != nil || len(paths) != 1 { + t.Fatalf("FindPlanetOccultationPaths() = %d paths, %v; want one", len(paths), err) + } + if paths[0].LunarLimbApplied { + t.Fatal("failing limb: path LunarLimbApplied = true, want false") + } +} diff --git a/moon/svg/occultation_limb_diagram_test.go b/moon/svg/occultation_limb_diagram_test.go new file mode 100644 index 0000000..71dd113 --- /dev/null +++ b/moon/svg/occultation_limb_diagram_test.go @@ -0,0 +1,116 @@ +package svg + +// 月缘剖面精修过的接触时刻不落在圆月面上,站心图仍须能出图。 + +import ( + "strings" + "testing" + "time" + + "b612.me/astro/moon" + "b612.me/astro/moon/limb" +) + +func TestLocalPlanetOccultationSVGAcceptsLunarLimbContacts(t *testing.T) { + events, err := moon.FindPlanetOccultations( + time.Date(2034, time.November, 21, 0, 0, 0, 0, time.UTC), + time.Date(2034, time.November, 23, 0, 0, 0, 0, time.UTC), + moon.OccultationJupiter, -42.36358358336173, 21.762064240049245, 0, + moon.OccultationSearchOptions{LunarLimb: limb.Default()}, + ) + if err != nil || len(events) != 1 { + t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err) + } + if !events[0].LunarLimbApplied { + t.Fatal("LunarLimbApplied = false, want true") + } + if _, err := LocalPlanetOccultationSVG(events[0], LocalPlanetOccultationSVGOptions{}); err != nil { + t.Fatalf("LocalPlanetOccultationSVG() error = %v", err) + } +} + +func TestLocalStarOccultationSVGAcceptsLunarLimbContacts(t *testing.T) { + star := moon.StarCoordinate{ + ID: "Antares", RA: 247.3516666666667, Dec: -26.431944444444444, + Epoch: time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC), Frame: moon.CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -10, ProperMotionDecMasPerYear: -20, ParallaxMas: 24, + } + date := time.Date(2026, time.February, 11, 0, 0, 0, 0, time.UTC) + events, err := moon.FindStarOccultations(date, date.Add(24*time.Hour), star, + 24.493397847811025, -74.18160307712625, 0, + moon.OccultationSearchOptions{LunarLimb: limb.Default()}) + if err != nil || len(events) != 1 { + t.Fatalf("FindStarOccultations() = %d events, %v; want one", len(events), err) + } + if _, err := LocalStarOccultationSVG(events[0], star, LocalStarOccultationSVGOptions{}); err != nil { + t.Fatalf("LocalStarOccultationSVG() error = %v", err) + } +} + +func TestOccultationSVGsDeclareLunarLimbConvention(t *testing.T) { + observer := [2]float64{-42.36358358336173, 21.762064240049245} + start := time.Date(2034, time.November, 21, 0, 0, 0, 0, time.UTC) + end := time.Date(2034, time.November, 23, 0, 0, 0, 0, time.UTC) + draw := func(applied bool) string { + t.Helper() + options := moon.OccultationSearchOptions{} + if applied { + options.LunarLimb = limb.Default() + } + events, err := moon.FindPlanetOccultations(start, end, moon.OccultationJupiter, + observer[0], observer[1], 0, options) + if err != nil || len(events) != 1 { + t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err) + } + if events[0].LunarLimbApplied != applied { + t.Fatalf("LunarLimbApplied = %v, want %v", events[0].LunarLimbApplied, applied) + } + diagram, err := LocalPlanetOccultationSVG(events[0], LocalPlanetOccultationSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatalf("LocalPlanetOccultationSVG() error = %v", err) + } + return diagram + } + if circular := draw(false); !strings.Contains(circular, "圆月面") { + t.Fatal("circular chart does not declare the circular limb") + } + if corrected := draw(true); !strings.Contains(corrected, "月缘剖面") { + t.Fatal("limb chart does not declare the limb profile") + } +} + +func TestOccultationPathSVGsDeclareLunarLimbConvention(t *testing.T) { + start := time.Date(2034, time.November, 21, 0, 0, 0, 0, time.UTC) + end := time.Date(2034, time.November, 23, 0, 0, 0, 0, time.UTC) + for _, applied := range []bool{false, true} { + options := moon.OccultationPathOptions{Step: 10 * time.Minute, TargetSpacingKM: 400} + if applied { + options.LunarLimb = limb.Default() + } + paths, err := moon.FindPlanetOccultationPaths(start, end, moon.OccultationJupiter, options) + if err != nil || len(paths) != 1 { + t.Fatalf("FindPlanetOccultationPaths() = %d paths, %v; want one", len(paths), err) + } + if paths[0].LunarLimbApplied != applied { + t.Fatalf("LunarLimbApplied = %v, want %v", paths[0].LunarLimbApplied, applied) + } + want := "月面按圆月面计算。" + if applied { + want = "界线按月缘剖面修正" + } + global, err := PlanetOccultationPathSVG(paths[0], PlanetOccultationSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatalf("PlanetOccultationPathSVG() error = %v", err) + } + if !strings.Contains(global, want) { + t.Fatalf("global path chart (limb=%v) missing %q", applied, want) + } + detailed, err := PlanetOccultationDetailedSVG(paths[0], OccultationDetailedSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatalf("PlanetOccultationDetailedSVG() error = %v", err) + } + if !strings.Contains(detailed, want) { + t.Fatalf("detailed chart (limb=%v) missing %q", applied, want) + } + } +} diff --git a/moon/svg/occultation_limb_profile.go b/moon/svg/occultation_limb_profile.go new file mode 100644 index 0000000..353c5dd --- /dev/null +++ b/moon/svg/occultation_limb_profile.go @@ -0,0 +1,629 @@ +package svg + +import ( + "errors" + "fmt" + "html" + "math" + "strings" + "time" + + "b612.me/astro" + "b612.me/astro/internal/svgasset" + "b612.me/astro/internal/svgchart" + "b612.me/astro/internal/timenote" + "b612.me/astro/moon" +) + +const ( + limbProfileSVGDefaultWidth = 920 + limbProfileSVGDefaultHeight = 620 + limbProfileSVGMinimumWidth = 640 + limbProfileSVGMinimumHeight = 480 + limbProfileSVGMaxRows = 12 + // limbProfilePanelGap 栏间距须容下上栏的时刻、位置角两行刻度与下栏上沿的两行接触编号。 + limbProfilePanelGap = 56.0 + // limbProfileInsetMinHeight 缩略图(含标题)的最小高度,接触表按它限行。 + limbProfileInsetMinHeight = 120.0 +) + +// ErrInvalidStarOccultationLimbProfileSVGOptions 表示精密月掩剖面图选项无效 / invalid precise occultation profile chart options. +var ErrInvalidStarOccultationLimbProfileSVGOptions = errors.New("invalid stellar occultation limb profile SVG options") + +// ErrEmptyStarOccultationLimbProfile 表示剖面没有采样,恒星全程在月缘起伏带外 / the profile has no samples; the star stays outside the relief band. +var ErrEmptyStarOccultationLimbProfile = errors.New("stellar occultation limb profile has no samples") + +// ErrInvalidStarOccultationLimbProfile 表示剖面数据不是 StarOccultationLimbProfile 能给出的形态 / malformed limb profile data. +var ErrInvalidStarOccultationLimbProfile = errors.New("invalid stellar occultation limb profile") + +// StarOccultationLimbProfileSVGOptions 控制精密恒星月掩的月缘剖面图 / options for the precise stellar occultation profile chart. +type StarOccultationLimbProfileSVGOptions struct { + // Width、Height 为画布尺寸,非正值用默认 / canvas size; non-positive uses defaults. + Width int + Height int + // Title 为空时自动生成 / empty uses an automatic title. + Title string + // Language 为 "en" 时英文,否则中文 / "en" for English, otherwise Chinese. + Language string + // Location 控制显示时刻,nil 为 UTC+8 / display zone; nil uses UTC+8. + Location *time.Location + // TimeScale 为 TimeScaleUT1 时改用 UT1 时刻,此时 Location 须为 nil 或 UTC / label time scale. + TimeScale astro.TimeScale +} + +// StarOccultationLimbProfileSVG 渲染精密恒星月掩剖面:横轴时刻、纵轴相对参考圆月面高度(km),给出遮挡区间与全部掩入复现 / precise stellar occultation limb profile chart. +func StarOccultationLimbProfileSVG(profile moon.StarOccultationLimbProfileResult, options StarOccultationLimbProfileSVGOptions) (string, error) { + if options.Width > 0 && options.Width < limbProfileSVGMinimumWidth { + return "", fmt.Errorf("%w: width must be zero or at least %d", ErrInvalidStarOccultationLimbProfileSVGOptions, limbProfileSVGMinimumWidth) + } + if options.Height > 0 && options.Height < limbProfileSVGMinimumHeight { + return "", fmt.Errorf("%w: height must be zero or at least %d", ErrInvalidStarOccultationLimbProfileSVGOptions, limbProfileSVGMinimumHeight) + } + if options.TimeScale == astro.TimeScaleUT1 && options.Location != nil && options.Location != time.UTC { + return "", fmt.Errorf("occultation SVG: UT1 labels do not take a non-UTC location") + } + samples := 0 + for _, segment := range profile.Segments { + samples += len(segment) + } + if samples < 2 || profile.Occultation.Greatest.IsZero() { + return "", ErrEmptyStarOccultationLimbProfile + } + if err := validateStarOccultationLimbProfile(profile); err != nil { + return "", err + } + if options.Width <= 0 { + options.Width = limbProfileSVGDefaultWidth + } + if options.Height <= 0 { + options.Height = limbProfileSVGDefaultHeight + } + if options.TimeScale == astro.TimeScaleUT1 { + options.Location = time.UTC + profile = moon.StarOccultationLimbProfileInUT1(profile) + } + if options.Location == nil { + options.Location = time.FixedZone("UTC+8", starOccultationSVGDefaultZone) + } + if strings.EqualFold(options.Language, starOccultationSVGLanguageEnglish) { + options.Language = starOccultationSVGLanguageEnglish + } else { + options.Language = starOccultationSVGLanguageChinese + } + return renderStarOccultationLimbProfileSVG(profile, options), nil +} + +type limbProfilePoint struct { + t time.Time + limbKM, starKM float64 + positionAngle float64 + // skyX、skyY 为恒星相对月心的天空平面坐标(km,向东、向北为正)。 + skyX, skyY float64 +} + +// limbProfileHeights 把角量换算成月面上相对参考球的高度(km),恒星与月缘用同一比例。 +func limbProfileHeights(segment []moon.StarOccultationLimbSample, referenceKM float64) []limbProfilePoint { + points := make([]limbProfilePoint, 0, len(segment)) + for _, sample := range segment { + scale := referenceKM / sample.MeanRadiusArcsec + points = append(points, limbProfilePoint{ + t: sample.Time, + limbKM: (sample.LimbRadiusArcsec - sample.MeanRadiusArcsec) * scale, + starKM: (sample.SeparationArcsec - sample.MeanRadiusArcsec) * scale, + positionAngle: sample.PositionAngleDeg, + skyX: sample.SeparationArcsec * scale * math.Sin(sample.PositionAngleDeg*math.Pi/180), + skyY: sample.SeparationArcsec * scale * math.Cos(sample.PositionAngleDeg*math.Pi/180), + }) + } + return points +} + +func limbProfileText(language, zh, en string) string { + if language == starOccultationSVGLanguageEnglish { + return en + } + return zh +} + +func renderStarOccultationLimbProfileSVG(profile moon.StarOccultationLimbProfileResult, options StarOccultationLimbProfileSVGOptions) string { + info := profile.Occultation + width, height := float64(options.Width), float64(options.Height) + language := options.Language + segments := make([][]limbProfilePoint, 0, len(profile.Segments)) + minKM, maxKM := math.Inf(1), math.Inf(-1) + for _, segment := range profile.Segments { + points := limbProfileHeights(segment, limbProfileReferenceKM(profile)) + if len(points) < 2 { + continue + } + for _, point := range points { + minKM = math.Min(minKM, math.Min(point.limbKM, point.starKM)) + maxKM = math.Max(maxKM, math.Max(point.limbKM, point.starKM)) + } + segments = append(segments, points) + } + if !(maxKM > minKM) { + minKM, maxKM = -1, 1 + } + padKM := (maxKM - minKM) * 0.08 + minKM, maxKM = minKM-padKM, maxKM+padKM + + title := options.Title + if title == "" { + target := info.TargetID + if target == "" { + target = limbProfileText(language, "恒星", "star") + } + date := info.Greatest.In(options.Location).Format("2006-01-02") + title = date + limbProfileText(language, " 精密月掩月缘剖面 · "+target, " Limb Profile · Occultation of "+target) + } + site := starOccultationFormatCoordinates(info.Observer.Longitude, info.Observer.Latitude) + summary := fmt.Sprintf(limbProfileText(language, "观测点 %s | 椭球高 %.0f 米 | 遮挡 %d 段 | 采样 %s", "Site %s | elevation %.0f m | %d hidden interval(s) | step %s"), + site, info.Observer.Height, len(profile.Hidden), profile.Step) + direction := limbProfileDirectionText(segments, profile.Hidden, language) + + const top = 136.0 + tableWidth := 260.0 + left, right := 70.0, width-tableWidth-48 + chartTop, chartBottom := top, height-122 + panels := len(segments) + if panels < 1 { + panels = 1 + } + panelHeight := (chartBottom - chartTop - limbProfilePanelGap*float64(panels-1)) / float64(panels) + + var b strings.Builder + fmt.Fprintf(&b, ``, + options.Width, options.Height, options.Width, options.Height, html.EscapeString(title)) + b.WriteString(``) + b.WriteString(svgasset.MoonFaceSymbol()) + b.WriteString(``) + b.WriteString(``) + b.WriteString(``) + fmt.Fprintf(&b, ``, width-44, height-36) + fmt.Fprintf(&b, `%s`, + width/2, starOccultationTitleFontSize(title, width), html.EscapeString(svgchart.EllipsizeText(title, width-80, float64(starOccultationTitleFontSize(title, width))))) + fmt.Fprintf(&b, `%s`, + width/2, html.EscapeString(svgchart.EllipsizeText(summary, width-80, 13))) + fmt.Fprintf(&b, `%s`, + width/2, html.EscapeString(svgchart.EllipsizeText(direction, width-80, 12))) + + for index, points := range segments { + y0 := chartTop + float64(index)*(panelHeight+limbProfilePanelGap) + writeLimbProfilePanel(&b, points, profile.Hidden, left, right, y0, y0+panelHeight, minKM, maxKM, options) + } + // 接触表与图例让出缩略图的最小高度:图例占 84 px,表头 28 px,每行 19 px。 + maxRows := int((chartBottom + 30 - limbProfileInsetMinHeight - 18 - top - 28 - 84) / 19) + if maxRows < 2 { + maxRows = 2 + } + if maxRows > limbProfileSVGMaxRows { + maxRows = limbProfileSVGMaxRows + } + legendBottom := writeLimbProfileContacts(&b, profile, right+28, top, width-48, maxRows, options) + writeLimbProfileInset(&b, segments, info, limbProfileReferenceKM(profile), right+28, legendBottom+18, width-48, chartBottom+30, language) + writeLimbProfileFooter(&b, info, limbProfileReferenceKM(profile), left, height-66, width-left-40, options) + b.WriteString(``) + return b.String() +} + +func writeLimbProfilePanel(b *strings.Builder, points []limbProfilePoint, hidden []moon.OccultationInterval, + left, right, top, bottom, minKM, maxKM float64, options StarOccultationLimbProfileSVGOptions) { + start, end := points[0].t, points[len(points)-1].t + span := end.Sub(start).Seconds() + if span <= 0 { + span = 1 + } + x := func(t time.Time) float64 { return left + (right-left)*t.Sub(start).Seconds()/span } + y := func(km float64) float64 { return bottom - (bottom-top)*(km-minKM)/(maxKM-minKM) } + fmt.Fprintf(b, ``, left, top, right-left, bottom-top) + for _, interval := range hidden { + if interval.End.Before(start) || interval.Start.After(end) { + continue + } + x0, x1 := x(laterTime(interval.Start, start)), x(earlierTime(interval.End, end)) + fmt.Fprintf(b, ``, x0, top, math.Max(x1-x0, 0.6), bottom-top) + } + if minKM < 0 && maxKM > 0 { + fmt.Fprintf(b, ``, left, y(0), right, y(0)) + } + var limb, star strings.Builder + for index, point := range points { + command := "L" + if index == 0 { + command = "M" + } + fmt.Fprintf(&limb, "%s%.2f %.2f ", command, x(point.t), y(point.limbKM)) + fmt.Fprintf(&star, "%s%.2f %.2f ", command, x(point.t), y(point.starKM)) + } + fmt.Fprintf(b, ``, strings.TrimSpace(limb.String())) + fmt.Fprintf(b, ``, strings.TrimSpace(star.String())) + font := `font-family="Georgia, 'Times New Roman', serif"` + // 横轴按整刻度标时刻,下方配该时刻的位置角;刻度线贯穿剖面框作网格。 + step := limbProfileTickStep(span, right-left) + majors := limbProfileTicks(start, end, step, options.Location) + for _, tick := range majors { + tx := x(tick) + fmt.Fprintf(b, ``, tx, top, tx, bottom) + fmt.Fprintf(b, ``, tx, bottom, tx, bottom+3) + // 标签夹进剖面框,靠近两端时不越出框线,也不压住框外的 PA 轴标题。 + label := tick.In(options.Location).Format(limbProfileTickLayout(step)) + labelX := limbProfileClampLabel(tx, svgchart.EstimatedTextWidth(label, 10), left, right) + fmt.Fprintf(b, `%s`, labelX, bottom+14, font, label) + angle := limbProfileFormatAngle(limbProfilePositionAngleAt(points, tick)) + angleX := limbProfileClampLabel(tx, svgchart.EstimatedTextWidth(angle, 9), left, right) + fmt.Fprintf(b, `%s`, angleX, bottom+26, font, angle) + } + fmt.Fprintf(b, `PA`, left-4, bottom+26, font) + minor := step / 6 + if step%time.Minute != 0 { + minor = step / 5 + } + major := make(map[int64]bool, len(majors)) + for _, tick := range majors { + major[tick.UnixNano()] = true + } + for _, tick := range limbProfileTicks(start, end, minor, options.Location) { + if major[tick.UnixNano()] { + continue + } + fmt.Fprintf(b, ``, x(tick), bottom, x(tick), bottom+2) + } + for _, label := range limbProfileContactLabels(hidden, start, end, x, left, right) { + labelY := top - 4 - float64(label.row)*11 + fmt.Fprintf(b, ``, label.x, labelY+2, label.contactX, top+6) + fmt.Fprintf(b, `%d`, label.x, labelY, font, label.number) + } + if ratio := limbProfileExaggeration(points, right-left, bottom-top, maxKM-minKM); ratio > 1.5 { + fmt.Fprintf(b, `%s`, right-6, top+14, font, + html.EscapeString(fmt.Sprintf(limbProfileText(options.Language, "纵向放大约 %.0f 倍", "vertical ×%.0f"), ratio))) + } else if ratio > 0 && ratio < 1/1.5 { + fmt.Fprintf(b, `%s`, right-6, top+14, font, + html.EscapeString(fmt.Sprintf(limbProfileText(options.Language, "横向放大约 %.0f 倍", "horizontal ×%.0f"), 1/ratio))) + } + fmt.Fprintf(b, `%+.1f`, left-4, top+9, font, maxKM) + fmt.Fprintf(b, `%+.1f`, left-4, bottom, font, minKM) +} + +const limbProfileContactLabelGap = 14.0 + +type limbProfileContactLabel struct { + number int + row int + x, contactX float64 +} + +// limbProfileContactLabels 接触编号两行排位后逐行自右向左回推,使其落在 [left, right] 内且同行间距不小于 14 px。 +func limbProfileContactLabels(hidden []moon.OccultationInterval, start, end time.Time, x func(time.Time) float64, left, right float64) []limbProfileContactLabel { + var labels []limbProfileContactLabel + rows := [2][]int{} + rowEnd := [2]float64{math.Inf(-1), math.Inf(-1)} + number := 0 + for _, interval := range hidden { + for _, contact := range []time.Time{interval.Start, interval.End} { + number++ + if contact.Before(start) || contact.After(end) { + continue + } + cx := x(contact) + row := 0 + if rowEnd[1] < rowEnd[0] { + row = 1 + } + if cx-rowEnd[0] >= limbProfileContactLabelGap { + row = 0 + } + labelX := math.Max(cx, rowEnd[row]+limbProfileContactLabelGap) + rowEnd[row] = labelX + rows[row] = append(rows[row], len(labels)) + labels = append(labels, limbProfileContactLabel{number: number, row: row, x: labelX, contactX: cx}) + } + } + halfWidth := func(label limbProfileContactLabel) float64 { + return svgchart.EstimatedTextWidth(fmt.Sprint(label.number), 10) / 2 + } + for _, indices := range rows { + if len(indices) == 0 { + continue + } + limit := math.Inf(1) + for k := len(indices) - 1; k >= 0; k-- { + label := &labels[indices[k]] + label.x = math.Min(label.x, math.Min(limit, right-halfWidth(*label))) + limit = label.x - limbProfileContactLabelGap + } + // 一行塞不下时均分整行,间距小于 14 px 也不越框。 + first, last := labels[indices[0]], labels[indices[len(indices)-1]] + if low, high := left+halfWidth(first), right-halfWidth(last); first.x < low && len(indices) > 1 { + for k, index := range indices { + labels[index].x = low + (high-low)*float64(k)/float64(len(indices)-1) + } + } else if first.x < low { + labels[indices[0]].x = low + } + } + return labels +} + +func laterTime(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} + +func earlierTime(a, b time.Time) time.Time { + if a.Before(b) { + return a + } + return b +} + +// writeLimbProfileContacts 返回图例底边,供缩略图接在下方。 +func writeLimbProfileContacts(b *strings.Builder, profile moon.StarOccultationLimbProfileResult, left, top, right float64, maxRows int, options StarOccultationLimbProfileSVGOptions) float64 { + language := options.Language + font := `font-family="Georgia, 'Times New Roman', serif"` + fmt.Fprintf(b, `%s`, left, top+4, font, + html.EscapeString(limbProfileText(language, "掩入 / 复现", "Disappearances / reappearances"))) + rows := make([][2]string, 0, 2*len(profile.Hidden)) + for _, interval := range profile.Hidden { + rows = append(rows, + [2]string{fmt.Sprintf("%d %s", len(rows)+1, limbProfileText(language, "掩入", "Disappears")), interval.Start.In(options.Location).Format("15:04:05.00")}, + [2]string{fmt.Sprintf("%d %s", len(rows)+2, limbProfileText(language, "复现", "Reappears")), interval.End.In(options.Location).Format("15:04:05.00")}) + } + if len(rows) == 0 { + rows = append(rows, [2]string{limbProfileText(language, "未发生遮挡", "Not occulted"), ""}) + } + overflow := 0 + if len(rows) > maxRows { + overflow = len(rows) - (maxRows - 1) + rows = rows[:maxRows-1] + } + for index, row := range rows { + baseline := top + 28 + float64(index)*19 + fmt.Fprintf(b, `%s`, left, baseline, font, html.EscapeString(row[0])) + fmt.Fprintf(b, `%s`, right, baseline, font, row[1]) + } + if overflow > 0 { + fmt.Fprintf(b, `%s`, left, top+28+float64(len(rows))*19, font, + html.EscapeString(fmt.Sprintf(limbProfileText(language, "另有 %d 个接触未列出", "%d more contacts not listed"), overflow))) + } + listed := len(rows) + if overflow > 0 { + listed++ + } + legendTop := top + 40 + float64(listed)*19 + fmt.Fprintf(b, ``, left, legendTop, left+22, legendTop) + fmt.Fprintf(b, `%s`, left+28, legendTop+4, font, + html.EscapeString(limbProfileText(language, "恒星方向月缘高度", "Limb height toward the star"))) + fmt.Fprintf(b, ``, left, legendTop+18, left+22, legendTop+18) + fmt.Fprintf(b, `%s`, left+28, legendTop+22, font, + html.EscapeString(limbProfileText(language, "恒星距参考圆月面(km)", "Star above the mean limb (km)"))) + fmt.Fprintf(b, ``, left, legendTop+31) + fmt.Fprintf(b, `%s`, left+28, legendTop+40, font, + html.EscapeString(limbProfileText(language, "恒星被遮挡", "Star hidden"))) + return legendTop + 44 +} + +func writeLimbProfileFooter(b *strings.Builder, info moon.StarOccultationInfo, referenceKM, left, baseline, maxWidth float64, options StarOccultationLimbProfileSVGOptions) { + note := limbProfileText(options.Language, + "纵轴为月面上相对参考球 %.1f km 的高度;月缘按剖面表插值,短于采样步长的遮挡可能漏检。", + "Heights are relative to the %.1f km reference sphere; the limb is interpolated from the profile table and events shorter than the step may be missed.") + note = fmt.Sprintf(note, referenceKM) + lines := svgchart.TruncateTextLines(svgchart.WrapTextBalanced(note, maxWidth, 10), maxWidth, 10, 2) + lines = append(lines, svgchart.EllipsizeText(timenote.Scale(options.TimeScale, info.Greatest, options.Location, options.Language), maxWidth, 10)) + font := `font-family="Georgia, 'Times New Roman', serif"` + for index, line := range lines { + fmt.Fprintf(b, `%s`, left, baseline+float64(index)*14, font, html.EscapeString(line)) + } +} + +// limbProfileTrackKM 返回恒星在天空平面上走过的弦长(km,按月面尺度)。 +func limbProfileTrackKM(points []limbProfilePoint) float64 { + first, last := points[0], points[len(points)-1] + return math.Hypot(last.skyX-first.skyX, last.skyY-first.skyY) +} + +// limbProfileExaggeration 纵向放大倍数:横向每像素代表的月面距离与纵向之比。 +func limbProfileExaggeration(points []limbProfilePoint, widthPX, heightPX, spanKM float64) float64 { + if widthPX <= 0 || heightPX <= 0 || spanKM <= 0 { + return 0 + } + return (limbProfileTrackKM(points) / widthPX) / (spanKM / heightPX) +} + +// limbProfileCompass 把位置角换成八方位名。 +func limbProfileCompass(positionAngle float64, language string) string { + zh := []string{"北", "东北", "东", "东南", "南", "西南", "西", "西北"} + en := []string{"N", "NE", "E", "SE", "S", "SW", "W", "NW"} + index := int(math.Floor(math.Mod(positionAngle+22.5+720, 360) / 45)) + if language == starOccultationSVGLanguageEnglish { + return en[index] + } + return zh[index] +} + +func limbProfileDirectionText(segments [][]limbProfilePoint, hidden []moon.OccultationInterval, language string) string { + if len(segments) == 0 { + return "" + } + first := segments[0][0] + lastSegment := segments[len(segments)-1] + last := lastSegment[len(lastSegment)-1] + if len(segments) == 1 { + return fmt.Sprintf(limbProfileText(language, + "恒星沿月缘自 PA %s(%s)移向 PA %s(%s),横向约 %.0f km;PA 自天北向东量", + "The star runs along the limb from PA %s (%s) to PA %s (%s), about %.0f km; PA measured from north through east"), + limbProfileFormatAngle(first.positionAngle), limbProfileCompass(first.positionAngle, language), + limbProfileFormatAngle(last.positionAngle), limbProfileCompass(last.positionAngle, language), limbProfileTrackKM(segments[0])) + } + // 深掩时取首次掩入与最后复现时刻的位置角,而非两栏端点。 + immersion, emersion := first.positionAngle, last.positionAngle + if len(hidden) > 0 { + immersion = limbProfilePositionAngleAt(segments[0], hidden[0].Start) + emersion = limbProfilePositionAngleAt(lastSegment, hidden[len(hidden)-1].End) + } + return fmt.Sprintf(limbProfileText(language, + "恒星于 PA %s(%s)掩入、PA %s(%s)复现;上下两栏分别为掩入侧与复现侧,PA 自天北向东量", + "The star disappears at PA %s (%s) and reappears at PA %s (%s); panels show the two sides; PA measured from north through east"), + limbProfileFormatAngle(immersion), limbProfileCompass(immersion, language), + limbProfileFormatAngle(emersion), limbProfileCompass(emersion, language)) +} + +// limbProfileFormatAngle 按一位小数格式化位置角,舍入到 360.0° 时归一为 0.0°。 +func limbProfileFormatAngle(positionAngle float64) string { + rounded := math.Round(positionAngle*10) / 10 + if rounded >= 360 { + rounded -= 360 + } + return fmt.Sprintf("%.1f°", rounded) +} + +// writeLimbProfileInset 画掩甚时刻的站心月相缩略图(上北左东),加粗放大的月缘段并用箭头标出恒星运动方向。 +func writeLimbProfileInset(b *strings.Builder, segments [][]limbProfilePoint, info moon.StarOccultationInfo, referenceKM, left, top, right, bottom float64, language string) { + size := math.Min(right-left, bottom-top-26) + if size < 60 || len(segments) == 0 { + return + } + font := `font-family="Georgia, 'Times New Roman', serif"` + fmt.Fprintf(b, `%s`, left, top+10, font, + html.EscapeString(svgchart.EllipsizeText(limbProfileText(language, "月缘位置(上北左东)", "On the limb (N up, E left)"), right-left, 12))) + radius := size/2 - 16 + cx, cy := left+(right-left)/2, top+26+size/2 + writeLocalOccultationMoon(b, cx, cy, radius, "limb-profile-moon", + localOccultationMoonAppearanceAt(info.Greatest, info.Observer), "limb-profile-moon-phase") + fmt.Fprintf(b, `%s`, cx, cy-radius-4, font, limbProfileText(language, "北", "N")) + fmt.Fprintf(b, `%s`, cx-radius-4, cy+4, font, limbProfileText(language, "东", "E")) + limbPoint := func(positionAngle, scale float64) (float64, float64) { + angle := positionAngle * math.Pi / 180 + return cx - scale*math.Sin(angle), cy - scale*math.Cos(angle) + } + for _, points := range segments { + first, last := points[0], points[len(points)-1] + x0, y0 := limbPoint(first.positionAngle, radius) + x1, y1 := limbPoint(last.positionAngle, radius) + sweep := 0 + if signedPositionAngleStep(first.positionAngle, last.positionAngle) < 0 { + sweep = 1 + } + fmt.Fprintf(b, ``, x0, y0, radius, radius, sweep, x1, y1) + } + // 恒星轨迹按真实比例画在月盘坐标里,深掩时两段之间连成穿过月面的弦。 + kmToPX := radius / referenceKM + firstPoint := segments[0][0] + lastSegment := segments[len(segments)-1] + lastPoint := lastSegment[len(lastSegment)-1] + fmt.Fprintf(b, ``, + cx-firstPoint.skyX*kmToPX, cy-firstPoint.skyY*kmToPX, cx-lastPoint.skyX*kmToPX, cy-lastPoint.skyY*kmToPX) +} + +// signedPositionAngleStep 返回自 from 到 to 的位置角变化(度),取 (−180, 180];负值为顺时针(向西)。 +func signedPositionAngleStep(from, to float64) float64 { + delta := math.Mod(to-from+540, 360) - 180 + return delta +} + +// limbProfileTickStep 选使相邻时刻标签不重叠的最小整刻度步长。 +func limbProfileTickStep(spanSeconds, widthPX float64) time.Duration { + for _, step := range []time.Duration{5 * time.Second, 10 * time.Second, 15 * time.Second, 20 * time.Second, 30 * time.Second, + time.Minute, 2 * time.Minute, 5 * time.Minute, 10 * time.Minute, 15 * time.Minute, 30 * time.Minute, time.Hour} { + if widthPX*step.Seconds()/spanSeconds >= 56 { + return step + } + } + return 2 * time.Hour +} + +func limbProfileTickLayout(step time.Duration) string { + if step%time.Minute == 0 { + return "15:04" + } + return "15:04:05" +} + +// limbProfilePositionAngleAt 在相邻样本间线性插值位置角,跨 0° 时按最短方向。 +func limbProfilePositionAngleAt(points []limbProfilePoint, at time.Time) float64 { + for index := 1; index < len(points); index++ { + if points[index].t.Before(at) { + continue + } + previous, next := points[index-1], points[index] + span := next.t.Sub(previous.t).Seconds() + if span <= 0 { + return next.positionAngle + } + fraction := at.Sub(previous.t).Seconds() / span + return math.Mod(previous.positionAngle+fraction*signedPositionAngleStep(previous.positionAngle, next.positionAngle)+360, 360) + } + return points[len(points)-1].positionAngle +} + +// limbProfileTicks 返回 [start, end] 内按显示时区本地时钟对齐 step 的全部刻度;每个刻度按它自身所在时刻的时区偏移判断,跨夏令时切换也落在当地整刻度上。 +func limbProfileTicks(start, end time.Time, step time.Duration, location *time.Location) []time.Time { + // ceil 返回不早于 at、在当地时钟上是 step 整数倍的第一个时刻;结果若落到另一偏移下,按结果的偏移重算一次。 + ceil := func(at time.Time) time.Time { + withOffset := func(at time.Time, offset int) time.Time { + shift := time.Duration(offset) * time.Second + tick := at.Add(shift).Truncate(step).Add(-shift) + if tick.Before(at) { + tick = tick.Add(step) + } + return tick + } + _, offset := at.In(location).Zone() + tick := withOffset(at, offset) + if _, actual := tick.In(location).Zone(); actual != offset { + tick = withOffset(at, actual) + } + return tick + } + var ticks []time.Time + for tick := ceil(start); !tick.After(end) && len(ticks) < 1000; tick = ceil(tick.Add(step)) { + ticks = append(ticks, tick) + } + return ticks +} + +// limbProfileClampLabel 返回中锚标签的 x,使宽 width 的标签整体落在 [left, right] 内。 +func limbProfileClampLabel(x, width, left, right float64) float64 { + return math.Max(left+width/2, math.Min(right-width/2, x)) +} + +// limbProfileReferenceKM 剖面结果声明的参考月球半径,旧结果未填时用 1737.4 km。 +func limbProfileReferenceKM(profile moon.StarOccultationLimbProfileResult) float64 { + if profile.ReferenceRadiusKM > 0 && !math.IsInf(profile.ReferenceRadiusKM, 0) { + return profile.ReferenceRadiusKM + } + return 1737.4 +} + +// validateStarOccultationLimbProfile 拒绝 StarOccultationLimbProfile 给不出的数据:至多两栏、每栏至少两个样本且时间递增、角量有限、遮挡区间有序。 +func validateStarOccultationLimbProfile(profile moon.StarOccultationLimbProfileResult) error { + if len(profile.Segments) > 2 { + return fmt.Errorf("%w: at most two segments, got %d", ErrInvalidStarOccultationLimbProfile, len(profile.Segments)) + } + finiteValue := func(value float64) bool { return !math.IsNaN(value) && !math.IsInf(value, 0) } + if !finiteValue(profile.ReferenceRadiusKM) || profile.ReferenceRadiusKM < 0 { + return fmt.Errorf("%w: reference radius must be finite and non-negative", ErrInvalidStarOccultationLimbProfile) + } + for index, segment := range profile.Segments { + if len(segment) < 2 { + return fmt.Errorf("%w: segment %d has fewer than two samples", ErrInvalidStarOccultationLimbProfile, index) + } + for sampleIndex, sample := range segment { + if !finiteValue(sample.SeparationArcsec) || !finiteValue(sample.LimbRadiusArcsec) || + !finiteValue(sample.MeanRadiusArcsec) || sample.MeanRadiusArcsec <= 0 || !finiteValue(sample.PositionAngleDeg) { + return fmt.Errorf("%w: segment %d sample %d has non-finite geometry", ErrInvalidStarOccultationLimbProfile, index, sampleIndex) + } + if sampleIndex > 0 && !sample.Time.After(segment[sampleIndex-1].Time) { + return fmt.Errorf("%w: segment %d samples must increase in time", ErrInvalidStarOccultationLimbProfile, index) + } + } + } + for index, interval := range profile.Hidden { + if !interval.End.After(interval.Start) || (index > 0 && interval.Start.Before(profile.Hidden[index-1].End)) { + return fmt.Errorf("%w: hidden intervals must be ordered", ErrInvalidStarOccultationLimbProfile) + } + } + return nil +} diff --git a/moon/svg/occultation_limb_profile_test.go b/moon/svg/occultation_limb_profile_test.go new file mode 100644 index 0000000..8dced39 --- /dev/null +++ b/moon/svg/occultation_limb_profile_test.go @@ -0,0 +1,373 @@ +package svg + +// 精密月掩剖面图:每段遮挡都要出现在接触表里,无样本或非法选项时返回错误。 + +import ( + "errors" + "fmt" + "math" + "regexp" + "strconv" + "strings" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/internal/svgchart" + "b612.me/astro/moon" + "b612.me/astro/moon/limb" +) + +var limbProfileTestStar = moon.StarCoordinate{ + ID: "Antares", RA: 247.3516666666667, Dec: -26.431944444444444, + Epoch: time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC), Frame: moon.CoordinateFrameJ2000, + ProperMotionRACosDecMasPerYear: -10, ProperMotionDecMasPerYear: -20, ParallaxMas: 24, +} + +func limbProfileTestGraze(t *testing.T) moon.StarOccultationLimbProfileResult { + t.Helper() + start := time.Date(2026, time.February, 10, 12, 0, 0, 0, time.UTC) + events, err := moon.FindStarOccultations(start, start.Add(36*time.Hour), limbProfileTestStar, + 41.5907, -55.07036, 0, moon.OccultationSearchOptions{LunarLimb: limb.Default()}) + if err != nil || len(events) != 1 { + t.Fatalf("FindStarOccultations() = %d events, %v; want one", len(events), err) + } + profile, err := moon.StarOccultationLimbProfile(events[0], limbProfileTestStar, limb.Default(), moon.StarOccultationLimbProfileOptions{}) + if err != nil { + t.Fatal(err) + } + if len(profile.Hidden) < 2 { + t.Fatalf("hidden intervals = %d, want a multi-contact graze", len(profile.Hidden)) + } + return profile +} + +func TestStarOccultationLimbProfileSVGListsEveryContact(t *testing.T) { + profile := limbProfileTestGraze(t) + for _, language := range []string{"", "en"} { + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Language: language, Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(diagram, "") { + t.Fatal("output is not a complete SVG document") + } + for _, interval := range profile.Hidden { + for _, instant := range []time.Time{interval.Start, interval.End} { + if label := instant.UTC().Format("15:04:05.00"); !strings.Contains(diagram, label) { + t.Errorf("language %q: contact %s missing", language, label) + } + } + } + if got := strings.Count(diagram, `fill-opacity="0.16"`); got < len(profile.Hidden) { + t.Errorf("language %q: %d shaded rects, want at least %d", language, got, len(profile.Hidden)) + } + } +} + +func TestStarOccultationLimbProfileSVGDeclaresUT1(t *testing.T) { + profile := limbProfileTestGraze(t) + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{TimeScale: astro.TimeScaleUT1}) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(diagram, "UT1") { + t.Error("UT1 chart does not declare the time scale") + } + if _, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{TimeScale: astro.TimeScaleUT1, Location: time.FixedZone("x", 3600)}); err == nil { + t.Error("UT1 with a non-UTC location accepted") + } +} + +func TestStarOccultationLimbProfileSVGRejectsBadInput(t *testing.T) { + if _, err := StarOccultationLimbProfileSVG(moon.StarOccultationLimbProfileResult{}, StarOccultationLimbProfileSVGOptions{}); err == nil { + t.Error("empty profile accepted") + } + profile := limbProfileTestGraze(t) + if _, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Width: 100}); err == nil { + t.Error("tiny width accepted") + } +} + +func TestStarOccultationLimbProfileSVGTextStaysInsideFrame(t *testing.T) { + profile := limbProfileTestGraze(t) + text := regexp.MustCompile(`]*font-size="([0-9]+)"([^>]*)>([^<]*)`) + for _, language := range []string{"zh", "en"} { + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Language: language, Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + for _, match := range text.FindAllStringSubmatch(diagram, -1) { + x, _ := strconv.ParseFloat(match[1], 64) + size, _ := strconv.ParseFloat(match[2], 64) + width := svgchart.EstimatedTextWidth(match[4], size) + low, high := x, x+width + if strings.Contains(match[3], "middle") { + low, high = x-width/2, x+width/2 + } else if strings.Contains(match[3], `"end"`) { + low, high = x-width, x + } + if low < 22 || high > limbProfileSVGDefaultWidth-22 { + t.Errorf("language %q: text [%.0f, %.0f] leaves the frame: %s", language, low, high, match[4]) + } + } + } +} + +func TestStarOccultationLimbProfileSVGDrawsPhasedMoonAndNumberedContacts(t *testing.T) { + profile := limbProfileTestGraze(t) + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(diagram, `href="#le-moon"`) || !strings.Contains(diagram, `class="limb-profile-moon"`) { + t.Fatal("inset does not use the textured, phased Moon") + } + if strings.Contains(diagram, `data-illumination="1.000000"`) { + t.Error("inset ignores the lunar phase") + } + for number := 1; number <= 2*len(profile.Hidden); number++ { + if !strings.Contains(diagram, `font-weight="700" text-anchor="middle">`+strconv.Itoa(number)+``) { + t.Errorf("contact %d is not numbered on the profile", number) + } + } +} + +func limbProfileTestAt(t *testing.T, latitude float64) moon.StarOccultationLimbProfileResult { + t.Helper() + info := moon.StarOccultationInfo{Greatest: time.Date(2026, time.February, 11, 3, 47, 15, 0, time.UTC), + Observer: moon.Observer{Longitude: 41.5907, Latitude: latitude}} + if latitude < -55.5 { + start := time.Date(2026, time.February, 10, 12, 0, 0, 0, time.UTC) + events, err := moon.FindStarOccultations(start, start.Add(36*time.Hour), limbProfileTestStar, 41.5907, latitude, 0, + moon.OccultationSearchOptions{LunarLimb: limb.Default()}) + if err != nil || len(events) != 1 { + t.Fatalf("FindStarOccultations() = %d events, %v", len(events), err) + } + info = events[0] + } + profile, err := moon.StarOccultationLimbProfile(info, limbProfileTestStar, limb.Default(), moon.StarOccultationLimbProfileOptions{}) + if err != nil { + t.Fatal(err) + } + return profile +} + +type limbProfileTestText struct { + x, y, width float64 + text string +} + +func limbProfileTestTexts(diagram string) []limbProfileTestText { + pattern := regexp.MustCompile(`]*font-size="([0-9]+)"([^>]*)>([^<]*)`) + var texts []limbProfileTestText + for _, match := range pattern.FindAllStringSubmatch(diagram, -1) { + x, _ := strconv.ParseFloat(match[1], 64) + y, _ := strconv.ParseFloat(match[2], 64) + size, _ := strconv.ParseFloat(match[3], 64) + width := svgchart.EstimatedTextWidth(match[5], size) + if strings.Contains(match[4], "middle") { + x -= width / 2 + } else if strings.Contains(match[4], `"end"`) { + x -= width + } + texts = append(texts, limbProfileTestText{x, y, width, match[5]}) + } + return texts +} + +func TestStarOccultationLimbProfileSVGDeepCase(t *testing.T) { + profile := limbProfileTestAt(t, -57.0) + if len(profile.Segments) != 2 || len(profile.Hidden) != 1 || profile.Occultation.Type != moon.OccultationTotal { + t.Fatalf("deep profile: %d segments, %d intervals, type %v", len(profile.Segments), len(profile.Hidden), profile.Occultation.Type) + } + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + texts := limbProfileTestTexts(diagram) + for i, a := range texts { + for _, b := range texts[i+1:] { + if math.Abs(a.y-b.y) < 9 && a.x < b.x+b.width && b.x < a.x+a.width { + t.Errorf("texts overlap: %q at (%.0f,%.0f) and %q at (%.0f,%.0f)", a.text, a.x, a.y, b.text, b.x, b.y) + } + } + } + immersion := profile.Hidden[0].Start + want := "" + for _, sample := range profile.Segments[0] { + if !sample.Time.Before(immersion) { + want = fmt.Sprintf("PA %.1f°", sample.PositionAngleDeg) + break + } + } + if !strings.Contains(diagram, want) { + t.Errorf("direction text does not use the immersion position angle %s", want) + } + if !strings.Contains(diagram, "横向放大约") { + t.Error("compressed vertical scale is not labelled") + } +} + +func TestStarOccultationLimbProfileSVGNotOcculted(t *testing.T) { + profile := limbProfileTestAt(t, -55.07036+0.8/111.2) + if len(profile.Hidden) != 0 || profile.Occultation.Type != moon.OccultationNone { + t.Fatalf("near-miss profile: %d intervals, type %v", len(profile.Hidden), profile.Occultation.Type) + } + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(diagram, "未发生遮挡") { + t.Error("not-occulted chart does not say so") + } + if _, err := StarOccultationLimbProfileSVG(moon.StarOccultationLimbProfileResult{}, StarOccultationLimbProfileSVGOptions{}); !errors.Is(err, ErrEmptyStarOccultationLimbProfile) { + t.Errorf("empty profile error = %v, want ErrEmptyStarOccultationLimbProfile", err) + } +} + +func TestLimbProfileHelpers(t *testing.T) { + kathmandu := time.FixedZone("UTC+5:45", 5*3600+45*60) + start := time.Date(2026, 2, 11, 3, 3, 7, 0, time.UTC) + for _, step := range []time.Duration{10 * time.Minute, time.Hour} { + ticks := limbProfileTicks(start, start.Add(3*time.Hour), step, kathmandu) + if len(ticks) == 0 || ticks[0].Before(start) { + t.Fatalf("step %v: ticks %v", step, ticks) + } + for _, tick := range ticks { + local := tick.In(kathmandu) + if local.Sub(time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, kathmandu))%step != 0 { + t.Errorf("step %v: tick %v not aligned in the display zone", step, local) + } + } + } + // 豪勋爵岛夏令时只拨 30 分钟:切换后 20 分钟刻度仍须落在当地整 20 分上。 + lordHowe, err := time.LoadLocation("Australia/Lord_Howe") + if err == nil { + switchStart := time.Date(2026, 4, 4, 14, 0, 0, 0, time.UTC) + for _, tick := range limbProfileTicks(switchStart, switchStart.Add(2*time.Hour), 20*time.Minute, lordHowe) { + if local := tick.In(lordHowe); local.Minute()%20 != 0 || local.Second() != 0 { + t.Errorf("Lord Howe tick %v is not on a local 20-minute mark", local) + } + } + } + if got := limbProfileFormatAngle(359.96); got != "0.0°" { + t.Errorf("359.96 formats as %s", got) + } +} + +func TestStarOccultationLimbProfileSVGRejectsMalformedProfiles(t *testing.T) { + good := limbProfileTestGraze(t) + clone := func() moon.StarOccultationLimbProfileResult { + profile := good + profile.Segments = make([][]moon.StarOccultationLimbSample, len(good.Segments)) + for index, segment := range good.Segments { + profile.Segments[index] = append([]moon.StarOccultationLimbSample(nil), segment...) + } + profile.Hidden = append([]moon.OccultationInterval(nil), good.Hidden...) + return profile + } + cases := map[string]func(*moon.StarOccultationLimbProfileResult){ + "NaN position angle": func(p *moon.StarOccultationLimbProfileResult) { p.Segments[0][3].PositionAngleDeg = math.NaN() }, + "NaN mean radius": func(p *moon.StarOccultationLimbProfileResult) { p.Segments[0][3].MeanRadiusArcsec = math.NaN() }, + "Inf separation": func(p *moon.StarOccultationLimbProfileResult) { p.Segments[0][3].SeparationArcsec = math.Inf(1) }, + "five segments": func(p *moon.StarOccultationLimbProfileResult) { + for len(p.Segments) < 5 { + p.Segments = append(p.Segments, p.Segments[0]) + } + }, + "time going back": func(p *moon.StarOccultationLimbProfileResult) { + p.Segments[0][4].Time = p.Segments[0][2].Time + }, + "reversed interval": func(p *moon.StarOccultationLimbProfileResult) { + p.Hidden[0].Start, p.Hidden[0].End = p.Hidden[0].End, p.Hidden[0].Start + }, + } + for name, mutate := range cases { + profile := clone() + mutate(&profile) + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if !errors.Is(err, ErrInvalidStarOccultationLimbProfile) { + t.Errorf("%s: err = %v, want ErrInvalidStarOccultationLimbProfile", name, err) + } + if diagram != "" { + t.Errorf("%s: produced output", name) + } + } +} + +func TestStarOccultationLimbProfileSVGLabelsNeverOverlap(t *testing.T) { + base := limbProfileTestGraze(t) + start := base.Segments[0][0].Time + end := base.Segments[0][len(base.Segments[0])-1].Time + // 8 段各 8 s 的密集遮挡,加上不同起点相位,覆盖编号与首刻度的碰撞。 + for phase := 0; phase < 40; phase++ { + profile := base + shift := time.Duration(phase) * 1700 * time.Millisecond + profile.Segments = [][]moon.StarOccultationLimbSample{append([]moon.StarOccultationLimbSample(nil), base.Segments[0]...)} + for index := range profile.Segments[0] { + profile.Segments[0][index].Time = profile.Segments[0][index].Time.Add(shift) + } + profile.Hidden = nil + for k := 0; k < 8; k++ { + at := start.Add(shift).Add(end.Sub(start)/3 + time.Duration(k)*12*time.Second) + profile.Hidden = append(profile.Hidden, moon.OccultationInterval{Start: at, End: at.Add(8 * time.Second)}) + } + profile.Occultation.Greatest = profile.Occultation.Greatest.Add(shift) + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + texts := limbProfileTestTexts(diagram) + for i, a := range texts { + for _, b := range texts[i+1:] { + if math.Abs(a.y-b.y) < 8 && a.x < b.x+b.width-0.5 && b.x < a.x+a.width-0.5 { + t.Fatalf("phase %d: %q at (%.1f,%.0f) overlaps %q at (%.1f,%.0f)", phase, a.text, a.x, a.y, b.text, b.x, b.y) + } + } + } + } +} + +func TestStarOccultationLimbProfileSVGContactNumbersStayInsideFrame(t *testing.T) { + base := limbProfileTestGraze(t) + start := base.Segments[0][0].Time + end := base.Segments[0][len(base.Segments[0])-1].Time + const frameLeft, frameRight = 70.0, limbProfileSVGDefaultWidth - 260.0 - 48 + for _, count := range []int{3, 6, 8, 12} { + for _, gap := range []time.Duration{2 * time.Second, 8 * time.Second} { + profile := base + profile.Hidden = nil + for k := count - 1; k >= 0; k-- { + at := end.Add(-time.Duration(k+1) * 2 * gap) + profile.Hidden = append(profile.Hidden, moon.OccultationInterval{Start: at, End: at.Add(gap)}) + } + if profile.Hidden[0].Start.Before(start) { + t.Fatalf("%d intervals do not fit the profile span", count) + } + diagram, err := StarOccultationLimbProfileSVG(profile, StarOccultationLimbProfileSVGOptions{Location: time.UTC}) + if err != nil { + t.Fatal(err) + } + var numbers []limbProfileTestText + for _, text := range limbProfileTestTexts(diagram) { + if _, err := strconv.Atoi(text.text); err == nil && text.y > 110 && text.y < 136 { + numbers = append(numbers, text) + } + } + if len(numbers) != 2*count { + t.Fatalf("%d intervals, gap %v: %d contact numbers, want %d", count, gap, len(numbers), 2*count) + } + for i, a := range numbers { + if a.x < frameLeft-0.5 || a.x+a.width > frameRight+0.5 { + t.Errorf("%d intervals, gap %v: number %s spans %.1f–%.1f, frame %.0f–%.0f", count, gap, a.text, a.x, a.x+a.width, frameLeft, frameRight) + } + for _, b := range numbers[i+1:] { + if a.y == b.y && a.x < b.x+b.width-0.5 && b.x < a.x+a.width-0.5 { + t.Errorf("%d intervals, gap %v: numbers %s and %s overlap", count, gap, a.text, b.text) + } + } + } + } + } +} diff --git a/moon/svg/occultation_local.go b/moon/svg/occultation_local.go index b83652a..b243322 100644 --- a/moon/svg/occultation_local.go +++ b/moon/svg/occultation_local.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "errors" "fmt" @@ -254,7 +255,7 @@ func renderLocalStarOccultationSVG( writeLocalStarOccultationOverview(&b, diagram, events, layout, options) writeLocalStarOccultationContacts(&b, events, layout, options) writeLocalStarOccultationStages(&b, events, layout, options) - writeLocalStarOccultationFooter(&b, layout, options, info.Greatest) + writeLocalStarOccultationFooter(&b, layout, options, info.Greatest, info.LunarLimbApplied) b.WriteString(``) return b.String() } @@ -374,6 +375,8 @@ func writeLocalStarOccultationTrack( } fmt.Fprintf(&path, "%s %.3f %.3f ", command, mapX(frame.StarXArcsec), mapY(frame.StarYArcsec)) } + // 白色衬底保证轨迹叠在暗月面与亮月面上都可见。 + fmt.Fprintf(b, ``, strings.TrimSpace(path.String())) fmt.Fprintf(b, ``, strings.TrimSpace(path.String())) } @@ -469,12 +472,22 @@ func writeLocalStarOccultationAxes(b *strings.Builder, cx, cy, radius float64, l } } +// 被掩目标轮廓用浅金色,配暗色衬底,暗月面与亮月面上都能分辨。 +const localOccultationHiddenOutlineColor = "#f3d48e" + +func writeLocalOccultationHiddenOutlineHalo(b *strings.Builder, x, y, radius float64) { + fmt.Fprintf(b, ``, x, y, radius) +} + func writeLocalStarOccultationStar(b *strings.Builder, x, y float64, hidden bool, class, label string) { fill, stroke, dash := "#fff7d6", "#9b3028", "" if hidden { - fill, stroke, dash = "none", "#7d3430", ` stroke-dasharray="2 2"` + fill, stroke, dash = "none", localOccultationHiddenOutlineColor, ` stroke-dasharray="3 2"` } fmt.Fprintf(b, ``, html.EscapeString(class), html.EscapeString(label)) + if hidden { + writeLocalOccultationHiddenOutlineHalo(b, x, y, 4.2) + } fmt.Fprintf(b, ``, x, y, fill, stroke, dash) fmt.Fprintf(b, ``, x-6, y, x+6, y, stroke) fmt.Fprintf(b, ``, x, y-6, x, y+6, stroke) @@ -558,11 +571,13 @@ func writeLocalStarOccultationFooter( layout localStarOccultationSVGLayout, options LocalStarOccultationSVGOptions, instant time.Time, + limbApplied bool, ) { directionLines, footerLines := localOccultationFooterLines( localStarOccultationSVGDirectionText(options), localStarOccultationSVGFooterNote(options), - timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + limbnote.Join(timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + limbnote.Contacts(limbApplied, options.Language), options.Language), layout.footerY, layout.width, layout.height) for index, line := range directionLines { fmt.Fprintf(b, `%s`, diff --git a/moon/svg/occultation_planet_local.go b/moon/svg/occultation_planet_local.go index b666be1..1befec3 100644 --- a/moon/svg/occultation_planet_local.go +++ b/moon/svg/occultation_planet_local.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "errors" "fmt" @@ -223,7 +224,7 @@ func renderLocalPlanetOccultationSVG( options.Width, options.Height, options.Width, options.Height, html.EscapeString(title)) b.WriteString(``) b.WriteString(svgasset.MoonFaceSymbol()) - b.WriteString(``) + b.WriteString(``) b.WriteString(``) b.WriteString(``) fmt.Fprintf(&b, ``, layout.width-44, layout.height-36) @@ -319,8 +320,6 @@ func writeLocalPlanetOccultationOverview( table.Reserve(mapX(event.frame.PlanetXArcsec)-radius, mapY(event.frame.PlanetYArcsec)-radius, 2*radius, 2*radius) } writeLocalStarOccultationAxes(b, cx, cy, moonRadius, options.Language, table) - writeLocalPlanetOccultationLunarPath(b, diagram.Frames, mapX, mapY, extent, options.Language) - writeLocalPlanetOccultationTrack(b, diagram.Frames, mapX, mapY) for _, event := range events { writeLocalPlanetOccultationDisk(b, mapX(event.frame.PlanetXArcsec), mapY(event.frame.PlanetYArcsec), @@ -330,6 +329,8 @@ func writeLocalPlanetOccultationOverview( writeLocalOccultationMoon(b, cx, cy, moonRadius, "overview-moon", localOccultationMoonAppearanceAt(diagram.Occultation.Greatest, diagram.Occultation.Observer), "local-planet-occultation-moon-overview") + writeLocalPlanetOccultationLunarPath(b, diagram.Frames, mapX, mapY, extent, options.Language) + writeLocalPlanetOccultationTrack(b, diagram.Frames, mapX, mapY) overviewLabels := make([]localOccultationOverviewLabel, 0, len(events)) for _, event := range events { x := mapX(event.frame.PlanetXArcsec) @@ -378,7 +379,9 @@ func writeLocalPlanetOccultationTrack( } fmt.Fprintf(&path, "%s %.3f %.3f ", command, mapX(frame.PlanetXArcsec), mapY(frame.PlanetYArcsec)) } - fmt.Fprintf(b, ``, strings.TrimSpace(path.String())) + // 白色衬底保证轨迹叠在暗月面与亮月面上都可见。 + fmt.Fprintf(b, ``, strings.TrimSpace(path.String())) + fmt.Fprintf(b, ``, strings.TrimSpace(path.String())) } func writeLocalPlanetOccultationLunarPath( @@ -447,7 +450,8 @@ func writeLocalPlanetOccultationDisk( ) { fill, stroke, dash := "#d6ad69", "#704b2d", "" if hidden { - fill, stroke, dash = "none", "#7d3430", ` stroke-dasharray="2 2"` + writeLocalOccultationHiddenOutlineHalo(b, x, y, math.Max(radius, 0.15)) + fill, stroke, dash = "none", localOccultationHiddenOutlineColor, ` stroke-dasharray="3 2"` } fmt.Fprintf(b, ``, html.EscapeString(class), html.EscapeString(label), x, y, math.Max(radius, 0.15), fill, stroke, dash) @@ -516,8 +520,9 @@ func writeLocalPlanetOccultationStages( writeLocalOccultationMoon(b, cx, cy, event.frame.MoonRadiusArcsec*scale, "stage-moon", event.appearance, fmt.Sprintf("local-planet-occultation-moon-stage-%d", index)) if event.hidden { - fmt.Fprintf(b, ``, - x, y, displayRadius) + writeLocalOccultationHiddenOutlineHalo(b, x, y, displayRadius) + fmt.Fprintf(b, ``, + x, y, displayRadius, localOccultationHiddenOutlineColor) } fmt.Fprintf(b, `%s`, cx, cy+radius+18, html.EscapeString(event.name)) @@ -560,7 +565,8 @@ func writeLocalPlanetOccultationFooter( directionLines, footerLines := localOccultationFooterLines( localPlanetOccultationSVGDirectionText(options), localPlanetOccultationSVGFooterNote(info, options), - timenote.Scale(options.TimeScale, info.Greatest, options.Location, options.Language), + limbnote.Join(timenote.Scale(options.TimeScale, info.Greatest, options.Location, options.Language), + limbnote.Contacts(info.LunarLimbApplied, options.Language), options.Language), layout.footerY, layout.width, layout.height) for index, line := range directionLines { fmt.Fprintf(b, `%s`, diff --git a/moon/svg/occultation_timescale_test.go b/moon/svg/occultation_timescale_test.go index 3500d1b..a3b4d1f 100644 --- a/moon/svg/occultation_timescale_test.go +++ b/moon/svg/occultation_timescale_test.go @@ -63,7 +63,7 @@ func TestOccultationSVGTimeScaleUT1(t *testing.T) { } } -// assertOccultationUT1Declaration 钉住 UT1 声明是可解析的 元素并写出 DUT1:曾经的裸文本写入不会渲染。 +// assertOccultationUT1Declaration UT1 声明须是可解析的 元素并写出 DUT1。 func assertOccultationUT1Declaration(t *testing.T, name, diagram string) { t.Helper() for _, text := range occultationSVGTexts(t, diagram) { diff --git a/moon/svg/testdata/semianalytic/occultation_detailed_panel.json b/moon/svg/testdata/semianalytic/occultation_detailed_panel.json new file mode 100644 index 0000000..36d6256 --- /dev/null +++ b/moon/svg/testdata/semianalytic/occultation_detailed_panel.json @@ -0,0 +1,6 @@ +{ + "moon.赤纬 Dec.": "-00°01'34.4\"", + "moon.赤经 R.A.": "23h37m22.4s", + "planet.赤纬 Dec.": "+03°27'41.3\"", + "planet.赤经 R.A.": "00h58m06.2s" +} diff --git a/neptune/api_default.go b/neptune/api_default.go new file mode 100644 index 0000000..8241ac8 --- /dev/null +++ b/neptune/api_default.go @@ -0,0 +1,258 @@ +package neptune + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 海王星升交点黄经 / ascending node longitude of Neptune. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 海王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Neptune. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 海王星降交点黄经 / descending node longitude of Neptune. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 海王星降交点黄经(n 变体) / n-parameterised descending node longitude of Neptune. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 海王星视直径,单位角秒 / apparent Neptune diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 海王星视直径(n 变体),单位角秒 / n-parameterised apparent Neptune diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastEasternQuadrature 上一次东方照,取 date 所在当地民用日 / previous eastern quadrature, on the local civil day of date. +func LastEasternQuadrature(date time.Time) time.Time { + return defaultModule.LastEasternQuadrature(date) +} + +// LastOpposition 上一次冲日,取 date 所在当地民用日 / previous opposition, on the local civil day of date. +func LastOpposition(date time.Time) time.Time { return defaultModule.LastOpposition(date) } + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastWesternQuadrature 上一次西方照,取 date 所在当地民用日 / previous western quadrature, on the local civil day of date. +func LastWesternQuadrature(date time.Time) time.Time { + return defaultModule.LastWesternQuadrature(date) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextEasternQuadrature 下一次东方照,取 date 所在当地民用日 / next eastern quadrature, on the local civil day of date. +func NextEasternQuadrature(date time.Time) time.Time { + return defaultModule.NextEasternQuadrature(date) +} + +// NextOpposition 下一次冲日,取 date 所在当地民用日 / next opposition, on the local civil day of date. +func NextOpposition(date time.Time) time.Time { return defaultModule.NextOpposition(date) } + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextWesternQuadrature 下一次西方照,取 date 所在当地民用日 / next western quadrature, on the local civil day of date. +func NextWesternQuadrature(date time.Time) time.Time { + return defaultModule.NextWesternQuadrature(date) +} + +// ParallacticAngle 海王星视差角(天顶方向角) / Neptune parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 海王星视差角(天顶方向角)(n 变体) / n-parameterised Neptune parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 海王星物理观测参数 / physical observing parameters of Neptune. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 海王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Neptune. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 海王星视半径,单位角秒 / apparent Neptune semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 海王星视半径(n 变体),单位角秒 / n-parameterised apparent Neptune semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/neptune/context.go b/neptune/context.go new file mode 100644 index 0000000..5687c10 --- /dev/null +++ b/neptune/context.go @@ -0,0 +1,13 @@ +package neptune + +import ( + "b612.me/astro/internal/ephem" +) + +// Neptune 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Neptune struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Neptune { return &Neptune{inst: inst} } + +var defaultModule Neptune diff --git a/neptune/context_test.go b/neptune/context_test.go new file mode 100644 index 0000000..425ffca --- /dev/null +++ b/neptune/context_test.go @@ -0,0 +1,92 @@ +package neptune + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastEasternQuadrature(date time.Time) time.Time + LastOpposition(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastWesternQuadrature(date time.Time) time.Time + NextConjunction(date time.Time) time.Time + NextEasternQuadrature(date time.Time) time.Time + NextOpposition(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextWesternQuadrature(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Neptune)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/neptune/diameter.go b/neptune/diameter.go index 9c683ce..4856894 100644 --- a/neptune/diameter.go +++ b/neptune/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 海王星视半径,单位角秒 / apparent Neptune semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Neptune) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 海王星视半径(截断版),单位角秒 / truncated apparent Neptune semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 海王星视半径(n 变体),单位角秒 / n-parameterised apparent Neptune semidiameter in arcseconds. +func (module *Neptune) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneSemidiameterN(basic.UTC2TT(jd), n) + return basic.NeptuneSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 海王星视直径,单位角秒 / apparent Neptune diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Neptune) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 海王星视直径(截断版),单位角秒 / truncated apparent Neptune diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 海王星视直径(n 变体),单位角秒 / n-parameterised apparent Neptune diameter in arcseconds. +func (module *Neptune) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneDiameterN(basic.UTC2TT(jd), n) + return basic.NeptuneDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/neptune/doc.go b/neptune/doc.go index 13a0695..bc4decb 100644 --- a/neptune/doc.go +++ b/neptune/doc.go @@ -1,3 +1,7 @@ // Package neptune 海王星位置、升落、合冲、留、方照、相位、视星等、视直径与物理星历,角度单位为度。 // Package neptune covers Neptune position, rise/set, conjunction, opposition, station, quadrature, phase, magnitude, apparent diameter and physical ephemeris; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package neptune diff --git a/neptune/neptune.go b/neptune/neptune.go index 1df34a4..a10062f 100644 --- a/neptune/neptune.go +++ b/neptune/neptune.go @@ -1,11 +1,11 @@ package neptune import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回海王星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Neptune at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Neptune) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentLo(basic.UTC2TT(jd)) + return basic.NeptuneApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回海王星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Neptune at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Neptune) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentBo(basic.UTC2TT(jd)) + return basic.NeptuneApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回海王星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Neptune at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Neptune) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentRa(basic.UTC2TT(jd)) + return basic.NeptuneApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回海王星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Neptune at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Neptune) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentDec(basic.UTC2TT(jd)) + return basic.NeptuneApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回海王星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Neptune at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Neptune) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentRaDec(basic.UTC2TT(jd)) + return basic.NeptuneApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回海王星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Neptune at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Neptune) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneMag(basic.UTC2TT(jd)) + return basic.NeptuneMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回海王星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Neptune to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Neptune) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthNeptuneAway(basic.UTC2TT(jd)) + return basic.EarthNeptuneAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回海王星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Neptune to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Neptune) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(7, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(7, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Neptune) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneHeight(localJD, lon, lat, timezone) + return basic.NeptuneHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Neptune) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Neptune) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneAzimuth(localJD, lon, lat, timezone) + return basic.NeptuneAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Neptune) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneHourAngle(localJD, lon, timezone) + return basic.NeptuneHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Neptune) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.NeptuneCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.NeptuneCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Neptune) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.NeptuneRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.NeptuneRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Neptune) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Neptune) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,114 +146,84 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.NeptuneSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.NeptuneSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Neptune) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Neptune) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastOpposition 上一次冲日 / previous opposition. -// -// 返回 date 当前或之前最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or before date, keeping date's time zone. -func LastOpposition(date time.Time) time.Time { +func (module *Neptune) LastOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneOppositionWithInstance(jde, module.inst), date.Location(), false) } // NextOpposition 下一次冲日 / next opposition. -// -// 返回 date 当前或之后最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or after date, keeping date's time zone. -func NextOpposition(date time.Time) time.Time { +func (module *Neptune) NextOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneOppositionWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Neptune) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Neptune) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Neptune) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Neptune) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // LastEasternQuadrature 上一次东方照 / previous eastern quadrature. -// -// 返回 date 当前或之前最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or before date, keeping date's time zone. -func LastEasternQuadrature(date time.Time) time.Time { +func (module *Neptune) LastEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextEasternQuadrature 下一次东方照 / next eastern quadrature. -// -// 返回 date 当前或之后最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or after date, keeping date's time zone. -func NextEasternQuadrature(date time.Time) time.Time { +func (module *Neptune) NextEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // LastWesternQuadrature 上一次西方照 / previous western quadrature. -// -// 返回 date 当前或之前最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or before date, keeping date's time zone. -func LastWesternQuadrature(date time.Time) time.Time { +func (module *Neptune) LastWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastNeptuneWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastNeptuneWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextWesternQuadrature 下一次西方照 / next western quadrature. -// -// 返回 date 当前或之后最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or after date, keeping date's time zone. -func NextWesternQuadrature(date time.Time) time.Time { +func (module *Neptune) NextWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextNeptuneWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextNeptuneWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Neptune) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.NeptuneMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/neptune/neptune_test.go b/neptune/neptune_test.go index f3c67ff..8126918 100644 --- a/neptune/neptune_test.go +++ b/neptune/neptune_test.go @@ -4,6 +4,8 @@ import ( "math" "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func sameUnixSecond(got time.Time, want int64) bool { @@ -13,17 +15,23 @@ func sameUnixSecond(got time.Time, want int64) bool { func TestNeptune(t *testing.T) { tz := time.FixedZone("CST", 8*3600) date := time.Date(2022, 01, 20, 00, 00, 00, 00, tz) - if !sameUnixSecond(NextConjunction(date), 1647171800) { - t.Fatal(NextConjunction(date).Unix()) + values := ephemtest.NewValues(t, "neptune") + want := func(key string, legacy int64, got time.Time) int64 { + return int64(values.Want(key, float64(legacy), float64(got.Unix()))) } - if CulminationTime(date, 115).Unix() != 1642665021 { - t.Fatal(CulminationTime(date, 115).Unix()) + conjunction := NextConjunction(date) + if !sameUnixSecond(conjunction, want("NextConjunction", 1647171800, conjunction)) { + t.Fatal(conjunction.Unix()) + } + culmination := CulminationTime(date, 115) + if culmination.Unix() != want("CulminationTime", 1642665021, culmination) { + t.Fatal(culmination.Unix()) } date, err := (RiseTime(date, 115, 40, 0, false)) if err != nil { t.Fatal(err) } - if date.Unix() != 1642644398 { + if date.Unix() != want("RiseTime", 1642644399, date) { t.Fatal(date.Unix()) } } diff --git a/neptune/nodes.go b/neptune/nodes.go index 92aa3f2..dbbecf1 100644 --- a/neptune/nodes.go +++ b/neptune/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 海王星升交点黄经 / ascending node longitude of Neptune. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Neptune) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 海王星升交点黄经(截断版) / truncated ascending node longitude of Neptune. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 海王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Neptune. +func (module *Neptune) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneAscendingNodeN(basic.UTC2TT(jd), n) + return basic.NeptuneAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 海王星降交点黄经 / descending node longitude of Neptune. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Neptune) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 海王星降交点黄经(截断版) / truncated descending node longitude of Neptune. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 海王星降交点黄经(n 变体) / n-parameterised descending node longitude of Neptune. +func (module *Neptune) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneDescendingNodeN(basic.UTC2TT(jd), n) + return basic.NeptuneDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/neptune/parallactic.go b/neptune/parallactic.go index a38a743..042a0bb 100644 --- a/neptune/parallactic.go +++ b/neptune/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 海王星视差角(天顶方向角) / Neptune parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Neptune) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项海王星视差角(天顶方向角) / truncated Neptune parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 海王星视差角(天顶方向角)(n 变体) / n-parameterised Neptune parallactic angle. +func (module *Neptune) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/neptune/phase.go b/neptune/phase.go index 3af0de4..40ffd0d 100644 --- a/neptune/phase.go +++ b/neptune/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Neptune) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.NeptunePhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Neptune) PhaseAngleN(date time.Time, n int) float64 { + return basic.NeptunePhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Neptune) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.NeptuneIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Neptune) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.NeptuneIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Neptune) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Neptune) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Neptune) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.NeptuneBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Neptune) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.NeptuneBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/neptune/physical.go b/neptune/physical.go index 422839e..9d0f0d3 100644 --- a/neptune/physical.go +++ b/neptune/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 海王星物理观测参数 / physical observing parameters of Neptune. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Neptune) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 海王星物理观测参数(截断版) / truncated physical observing parameters of Neptune. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 海王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Neptune. +func (module *Neptune) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.NeptunePhysicalN(basic.UTC2TT(jd), n) + info := basic.NeptunePhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, diff --git a/neptune/rise_set_atmosphere.go b/neptune/rise_set_atmosphere.go new file mode 100644 index 0000000..4f87c81 --- /dev/null +++ b/neptune/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package neptune + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 海王星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Neptune) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.NeptuneRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 海王星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Neptune) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.NeptuneSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 海王星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 海王星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/neptune/testdata/semianalytic/neptune.json b/neptune/testdata/semianalytic/neptune.json new file mode 100644 index 0000000..49b2bd5 --- /dev/null +++ b/neptune/testdata/semianalytic/neptune.json @@ -0,0 +1,5 @@ +{ + "CulminationTime": 1642665021, + "NextConjunction": 1647171785, + "RiseTime": 1642644398 +} diff --git a/neptune/truncated.go b/neptune/truncated.go index a3b0873..07ba3c6 100644 --- a/neptune/truncated.go +++ b/neptune/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Neptune) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentLoN(basic.UTC2TT(jd), n) + return basic.NeptuneApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Neptune) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentBoN(basic.UTC2TT(jd), n) + return basic.NeptuneApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Neptune) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentRaN(basic.UTC2TT(jd), n) + return basic.NeptuneApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Neptune) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentDecN(basic.UTC2TT(jd), n) + return basic.NeptuneApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Neptune) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneApparentRaDecN(basic.UTC2TT(jd), n) + return basic.NeptuneApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Neptune) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.NeptuneMagN(basic.UTC2TT(jd), n) + return basic.NeptuneMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Neptune) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthNeptuneAwayN(basic.UTC2TT(jd), n) + return basic.EarthNeptuneAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Neptune) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(7, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(7, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Neptune) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneHeightN(localJD, lon, lat, timezone, n) + return basic.NeptuneHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Neptune) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Neptune) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneAzimuthN(localJD, lon, lat, timezone, n) + return basic.NeptuneAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Neptune) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.NeptuneHourAngleN(localJD, lon, timezone, n) + return basic.NeptuneHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Neptune) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.NeptuneCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.NeptuneCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Neptune) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.NeptuneRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.NeptuneRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Neptune) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Neptune) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.NeptuneSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.NeptuneSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/orbit/doc.go b/orbit/doc.go index 5a92bab..18fecc4 100644 --- a/orbit/doc.go +++ b/orbit/doc.go @@ -8,9 +8,10 @@ // All input angles are in degrees. EpochJD and TpJD are TT/TDB Julian days. // Returned geometric positions do not include perturbations beyond the supplied // elements. Astrometric results include down-leg light-time correction. -// Apparent results follow this repository's existing planet semantics: -// astrometric position plus nutation/of-date coordinate conversion, without a -// full external aberration model. The package also provides observer-facing +// Optional planetary perturbations are available through PropagationOptions and +// StateWithOptions; a zero-value options value preserves the two-body path. +// Apparent results add annual aberration, nutation, and of-date coordinate +// conversion to the astrometric position. The package also provides observer-facing // altitude/azimuth/hour-angle and rise/transit/set helpers built on top of the // apparent topocentric coordinates. Rise/set helpers return package-local // sentinel errors ERR_ORBIT_NEVER_RISE / ERR_ORBIT_NEVER_SET, matching the diff --git a/orbit/orbit.go b/orbit/orbit.go index 1381011..4a3a737 100644 --- a/orbit/orbit.go +++ b/orbit/orbit.go @@ -15,8 +15,8 @@ var ( // Elements 日心二体圆锥曲线根数 / heliocentric two-body conic elements. // 参考系为 J2000 平黄道/平春分点。 // The reference frame is the J2000 mean ecliptic and mean equinox. -// EpochJD 与 TpJD 使用 TT/TDB 对应的儒略日。 -// EpochJD and TpJD are Julian days on the TT/TDB scale. +// EpochJD 与 TpJD 使用 TT/TDB 对应的儒略日;摄动传播时经典根数必须给出非零 EpochJD。 +// EpochJD and TpJD are Julian days on the TT/TDB scale; perturbed propagation of classical elements requires a non-zero EpochJD. // // 经典椭圆根数:A/E/I/Omega/W/M0 // 近日点形式:Q/E/I/Omega/W/TpJD @@ -151,8 +151,8 @@ func AstrometricGeocentricEquatorialJ2000(date time.Time, elements Elements) Equ // ApparentGeocentricEcliptic 地心视黄道坐标 / apparent geocentric ecliptic coordinates. // -// 返回加入光行时与章动修正后的地心视黄经、黄纬和距离;角度单位度,距离单位 AU。 -// Returns apparent geocentric ecliptic longitude, latitude, and distance after light-time and nutation corrections. Angles are in degrees and distance is in AU. +// 返回加入光行时、周年光行差与章动修正后的地心视黄经、黄纬和距离;角度单位度,距离单位 AU。 +// Returns apparent geocentric ecliptic longitude, latitude, and distance after light-time, annual aberration, and nutation corrections. Angles are in degrees and distance is in AU. func ApparentGeocentricEcliptic(date time.Time, elements Elements) EclipticPosition { lon, lat, distance := basic.OrbitApparentGeocentricEcliptic(ttJulianDay(date), toBasicElements(elements)) return EclipticPosition{Lon: lon, Lat: lat, Distance: distance} @@ -160,8 +160,8 @@ func ApparentGeocentricEcliptic(date time.Time, elements Elements) EclipticPosit // ApparentGeocentricEquatorial 地心视赤道坐标 / apparent geocentric equatorial coordinates. // -// 返回加入光行时与章动修正后的地心视赤经、赤纬和距离;角度单位度,距离单位 AU。 -// Returns apparent geocentric right ascension, declination, and distance after light-time and nutation corrections. Angles are in degrees and distance is in AU. +// 返回加入光行时、周年光行差与章动修正后的地心视赤经、赤纬和距离;角度单位度,距离单位 AU。 +// Returns apparent geocentric right ascension, declination, and distance after light-time, annual aberration, and nutation corrections. Angles are in degrees and distance is in AU. func ApparentGeocentricEquatorial(date time.Time, elements Elements) EquatorialPosition { ra, dec, distance := basic.OrbitApparentGeocentricEquatorial(ttJulianDay(date), toBasicElements(elements)) return EquatorialPosition{RA: ra, Dec: dec, Distance: distance} @@ -169,9 +169,9 @@ func ApparentGeocentricEquatorial(date time.Time, elements Elements) EquatorialP // ApparentTopocentricEquatorial 站心视赤道坐标 / apparent topocentric equatorial coordinates. // -// 返回加入光行时、章动和站心修正后的视赤经、赤纬和距离; +// 返回加入光行时、周年光行差、章动和站心修正后的视赤经、赤纬和距离; // `observerLon` 东经为正,`observerLat` 北纬为正,`observerHeight` 单位米。 -// Returns apparent topocentric right ascension, declination, and distance after light-time, nutation, and topocentric corrections. +// Returns apparent topocentric right ascension, declination, and distance after light-time, annual aberration, nutation, and topocentric corrections. func ApparentTopocentricEquatorial(date time.Time, elements Elements, observerLon, observerLat, observerHeight float64) EquatorialPosition { ra, dec, distance := basic.OrbitApparentTopocentricEquatorial(ttJulianDay(date), observerLon, observerLat, observerHeight, toBasicElements(elements)) return EquatorialPosition{RA: ra, Dec: dec, Distance: distance} diff --git a/orbit/orbit_test.go b/orbit/orbit_test.go index 71a6b4a..c761d55 100644 --- a/orbit/orbit_test.go +++ b/orbit/orbit_test.go @@ -275,12 +275,12 @@ func TestSecularRatesReduceMarsDriftAgainstVSOP(t *testing.T) { dynamicError := angleDiffAbs(dynamic.RA, wantRA) + math.Abs(dynamic.Dec-wantDec) staticError := angleDiffAbs(stale.RA, wantRA) + math.Abs(stale.Dec-wantDec) - if dynamicError > staticError+1e-9 { - t.Fatalf("%s dynamic elements should improve Mars drift: dynamic=%.9f static=%.9f", date.Format("2006-01-02"), dynamicError, staticError) - } if date.Equal(time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC)) { continue } + if dynamicError > staticError+1e-9 { + t.Fatalf("%s dynamic elements should improve Mars drift: dynamic=%.9f static=%.9f", date.Format("2006-01-02"), dynamicError, staticError) + } if staticError/dynamicError < 4 { t.Fatalf("%s Mars drift improvement too small: dynamic=%.9f static=%.9f", date.Format("2006-01-02"), dynamicError, staticError) } @@ -542,3 +542,185 @@ func sphericalAzimuthFromHourAngle(hourAngle, dec, lat float64) float64 { } return azimuth } + +func TestStateWithOptionsZeroMatchesState(t *testing.T) { + elements := Elements{ + EpochJD: 2451545, A: 2.7, E: 0.08, I: 10, Omega: 80, W: 73, M0: 231, + } + for _, jd := range []float64{2451545, 2452000, 2449000} { + wantPos, wantVel, wantErr := State(elements, jd) + gotPos, gotVel, gotErr := StateWithOptions(elements, jd, PropagationOptions{}) + if gotPos != wantPos || gotVel != wantVel || gotErr != wantErr { + t.Fatalf("jd %.1f: got %v %v %v, want %v %v %v", jd, gotPos, gotVel, gotErr, wantPos, wantVel, wantErr) + } + } +} + +type staticPerturbationEngine struct{} + +func (staticPerturbationEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if target == center { + return pos, vel, nil + } + if center != 0 { + return pos, vel, ErrPerturbationEngine + } + switch target { + case 10: + return pos, vel, nil + case 5: + pos[0] = 778000000 + return pos, vel, nil + default: + return pos, vel, ErrPerturbationEngine + } +} + +func (staticPerturbationEngine) Coverage(target Body) (start, end float64, ok bool) { + return 0, 3000000, true +} + +func TestStateWithOptionsPerturbationChangesState(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + plain, _, err := State(elements, elements.EpochJD+30) + if err != nil { + t.Fatal(err) + } + perturbed, perturbedVelocity, err := StateWithOptions(elements, elements.EpochJD+30, PropagationOptions{ + Perturbers: []Body{5}, + Engine: staticPerturbationEngine{}, + Step: 2, + Tolerance: 1e-10, + }) + if err != nil { + t.Fatal(err) + } + if perturbed == plain { + t.Fatal("perturbation did not change the state") + } + if !finite(vectorNorm(perturbed)) || !finite(vectorNorm(perturbedVelocity)) || vectorNorm(perturbedVelocity) > 1e7 { + t.Fatalf("perturbed state is unstable: position=%v velocity=%v", perturbed, perturbedVelocity) + } +} + +type countingPerturbationEngine struct { + calls map[Body]int +} + +func (engine *countingPerturbationEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if target == center { + return pos, vel, nil + } + engine.calls[target]++ + if center != 0 { + return pos, vel, ErrPerturbationEngine + } + switch target { + case 10: + return pos, vel, nil + case 5: + pos[0] = 778000000 + return pos, vel, nil + default: + return pos, vel, ErrPerturbationEngine + } +} + +func (*countingPerturbationEngine) Coverage(target Body) (start, end float64, ok bool) { + return 0, 3000000, true +} + +func TestStateWithOptionsCachesRepeatedSamples(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + provider := &countingPerturbationEngine{calls: make(map[Body]int)} + _, _, err := StateWithOptions(elements, elements.EpochJD+1, PropagationOptions{ + Perturbers: []Body{5}, + Engine: provider, + Step: 1, + Tolerance: 1, + }) + if err != nil { + t.Fatal(err) + } + for _, body := range []Body{5, 10} { + if got, want := provider.calls[body], 5; got != want { + t.Fatalf("body %d provider calls = %d, want %d", body, got, want) + } + } +} + +func TestStateWithOptionsDeduplicatesPerturbers(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + provider := &countingPerturbationEngine{calls: make(map[Body]int)} + singlePos, singleVel, err := StateWithOptions(elements, elements.EpochJD+10, PropagationOptions{ + Perturbers: []Body{5}, + Engine: provider, + Step: 2, + Tolerance: 1e-8, + }) + if err != nil { + t.Fatal(err) + } + duplicatePos, duplicateVel, err := StateWithOptions(elements, elements.EpochJD+10, PropagationOptions{ + Perturbers: []Body{5, 5, 10, 0}, + Engine: provider, + Step: 2, + Tolerance: 1e-8, + }) + if err != nil { + t.Fatal(err) + } + if duplicatePos != singlePos || duplicateVel != singleVel { + t.Fatalf("duplicate perturbers changed state: %v %v versus %v %v", duplicatePos, duplicateVel, singlePos, singleVel) + } +} + +func TestStateWithOptionsRejectsNonFiniteTarget(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + if _, _, err := StateWithOptions(elements, math.NaN(), PropagationOptions{}); err != ErrInvalidPropagationElements { + t.Fatalf("NaN target error = %v", err) + } +} + +func TestEphemerisEngineSunPerturberKeepsTwoBodyPath(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + engine := NewEphemerisEngine(Body(12345), elements, PropagationOptions{Perturbers: []Body{10}}) + gotPos, gotVel, err := engine.State(Body(12345), 10, elements.EpochJD+10) + if err != nil { + t.Fatal(err) + } + wantPos, wantVel, err := State(elements, elements.EpochJD+10) + if err != nil { + t.Fatal(err) + } + if gotPos != wantPos || gotVel != wantVel { + t.Fatalf("Sun-only perturbation changed two-body state: %v %v versus %v %v", gotPos, gotVel, wantPos, wantVel) + } +} + +func TestStateWithOptionsZeroMatchesExistingPositionMath(t *testing.T) { + cases := []Elements{ + {EpochJD: 2451545, A: 2.7, E: 0.08, I: 10, Omega: 80, W: 73, M0: 231}, + {Q: 0.9, E: 1, I: 30, Omega: 40, W: 50, TpJD: 2451545}, + {Q: 1.2, E: 1.05, I: 30, Omega: 40, W: 50, TpJD: 2451545}, + } + for _, elements := range cases { + jd := 2451545.0 + 20 + got, _, err := StateWithOptions(elements, jd, PropagationOptions{}) + if err != nil { + t.Fatal(err) + } + basicVector := basic.OrbitHeliocentricXYZJ2000(jd, toBasicElements(elements)) + epsilon := basic.EclipticObliquity(2451545, false) * math.Pi / 180 + want := [3]float64{ + basicVector[0] * 149597870.7, + (basicVector[1]*math.Cos(epsilon) - basicVector[2]*math.Sin(epsilon)) * 149597870.7, + (basicVector[1]*math.Sin(epsilon) + basicVector[2]*math.Cos(epsilon)) * 149597870.7, + } + for index := range got { + if math.Abs(got[index]-want[index]) > 1e-6 { + t.Fatalf("elements %+v component %d = %.12f, want %.12f", elements, index, got[index], want[index]) + } + } + } +} diff --git a/orbit/propagation.go b/orbit/propagation.go new file mode 100644 index 0000000..d7253c8 --- /dev/null +++ b/orbit/propagation.go @@ -0,0 +1,565 @@ +package orbit + +import ( + "errors" + "fmt" + "math" + "sync" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +const ( + propagationAU = 149597870.7 + propagationGaussianK = 0.01720209895 + propagationSunGM = propagationGaussianK * propagationGaussianK * propagationAU * propagationAU * propagationAU +) + +var ( + // ErrInvalidPropagationElements 根数不能用于传播 / orbital elements cannot be propagated. + ErrInvalidPropagationElements = errors.New("orbit: invalid propagation elements") + // ErrPerturbationEngine 摄动传播缺少天体星历 / perturbation propagation requires an ephemeris. + ErrPerturbationEngine = errors.New("orbit: perturbation ephemeris is not configured") + // ErrUnsupportedPerturber 摄动天体未知或重复计入 / perturbing body is unknown or counted twice. + ErrUnsupportedPerturber = errors.New("orbit: unsupported perturbing body") +) + +// maxPropagationNodes 单次传播允许的网格步数上限 / upper bound on grid steps of one propagation. +const maxPropagationNodes = 1 << 30 + +// Body NAIF 天体编号 / NAIF body identifier. +type Body = ephem.Body + +// StateProvider 提供摄动天体状态 / supplies perturbing-body states. +type StateProvider = ephem.Engine + +// PropagationOptions 可选摄动传播参数;零值保持纯二体 / optional perturbation propagation parameters; zero preserves two-body propagation. +type PropagationOptions struct { + Perturbers []Body // 摄动天体 / perturbing bodies. + Engine StateProvider // 摄动星历 / perturbation ephemeris. + Context StateProvider // 摄动星历别名 / alternate perturbation ephemeris. + Step float64 // 最大步长,单位日 / maximum step in days. + Tolerance float64 // 步长加倍的相对误差阈值 / relative step-doubling error threshold. +} + +// State 计算日心 J2000 平黄道根数的二体状态,输出 ICRF km 与 km/日 / computes a two-body state from heliocentric J2000 ecliptic elements, returning ICRF km and km/day. +func State(elements Elements, jdTDB float64) (pos, vel [3]float64, err error) { + return twoBodyState(elements, jdTDB) +} + +// StateWithOptions 按选项传播根数;零值与 State 完全一致 / propagates elements with optional perturbations; zero options exactly match State. +func StateWithOptions(elements Elements, jdTDB float64, options PropagationOptions) (pos, vel [3]float64, err error) { + if !finite(jdTDB) { + return pos, vel, ErrInvalidPropagationElements + } + perturbers, err := normalizePerturbers(options.Perturbers) + if err != nil { + return pos, vel, err + } + if len(perturbers) == 0 { + return twoBodyState(elements, jdTDB) + } + run, err := newPropagationRun(elements, perturbers, options) + if err != nil { + return pos, vel, err + } + return run.state(jdTDB, nil) +} + +// propagationRun 固定一次传播的根数、天体与步长网格。 +type propagationRun struct { + elements Elements + perturbers []Body + provider StateProvider + step float64 + tolerance float64 + epoch float64 + start propagationCheckpoint +} + +type propagationCheckpoint struct { + pos [3]float64 + vel [3]float64 +} + +// propagationCheckpointStride 每隔多少个网格点缓存一次状态。 +const propagationCheckpointStride = 32 + +// propagationCheckpoints 引擎缓存的网格状态,下标 j 对应 epoch±j·stride·step。 +type propagationCheckpoints struct { + forward []propagationCheckpoint + backward []propagationCheckpoint +} + +func newPropagationRun(elements Elements, perturbers []Body, options PropagationOptions) (*propagationRun, error) { + provider := options.Engine + if provider == nil { + provider = options.Context + } + if provider == nil { + return nil, ErrPerturbationEngine + } + epoch := propagationEpoch(elements) + // 经典根数漏填 EpochJD 时为 0,会从 JD 0 起积分数千年,按非法根数拒绝。 + if !finite(epoch) || epoch == 0 { + return nil, ErrInvalidPropagationElements + } + pos, vel, err := twoBodyState(elements, epoch) + if err != nil { + return nil, err + } + step := options.Step + if !(step > 0) || !finite(step) { + step = 0.5 + } + tolerance := options.Tolerance + if !(tolerance > 0) || !finite(tolerance) { + tolerance = 1e-6 + } + return &propagationRun{ + elements: elements, + perturbers: perturbers, + provider: provider, + step: step, + tolerance: tolerance, + epoch: epoch, + start: propagationCheckpoint{pos: pos, vel: vel}, + }, nil +} + +// propagationEpoch 积分起点:近日点形式给出 EpochJD 时取其为密切历元,否则用 TpJD / integration start: the osculating EpochJD when a perihelion-form set provides one, otherwise TpJD. +func propagationEpoch(elements Elements) float64 { + if usesPerihelionForm(elements) && (elements.EpochJD == 0 || !finite(elements.EpochJD)) { + return elements.TpJD + } + return elements.EpochJD +} + +// state 先沿固定网格积分到不越过目标的最后一个格点,再积分余量;结果只取决于目标历元,与 cache 内容无关 / integrates along a fixed grid to the last node short of the target, then the remainder; the result depends only on the target, never on cache contents. +func (run *propagationRun) state(target float64, cache *propagationCheckpoints) (pos, vel [3]float64, err error) { + if target == run.epoch { + return run.start.pos, run.start.vel, nil + } + direction := 1.0 + if target < run.epoch { + direction = -1 + } + span := math.Abs(target - run.epoch) + count := math.Floor(span / run.step) + if count > maxPropagationNodes { + return pos, vel, ErrInvalidPropagationElements + } + index := int(count) + for index > 0 && math.Abs(run.node(index, direction)-run.epoch) > span { + index-- + } + current, known := run.start, 0 + var stored *[]propagationCheckpoint + if cache != nil { + stored = &cache.forward + if direction < 0 { + stored = &cache.backward + } + if len(*stored) == 0 { + *stored = append(*stored, run.start) + } + slot := index / propagationCheckpointStride + if slot > len(*stored)-1 { + slot = len(*stored) - 1 + } + known = slot * propagationCheckpointStride + current = (*stored)[slot] + } + session := newPropagationSession(run.provider) + for k := known; k < index; k++ { + current.pos, current.vel, err = run.integrate(current.pos, current.vel, run.node(k, direction), run.node(k+1, direction), session) + if err != nil { + return current.pos, current.vel, err + } + if stored != nil && (k+1)%propagationCheckpointStride == 0 && len(*stored) == (k+1)/propagationCheckpointStride { + *stored = append(*stored, current) + } + } + from := run.node(index, direction) + if from == target { + return current.pos, current.vel, nil + } + return run.integrate(current.pos, current.vel, from, target, session) +} + +func (run *propagationRun) node(index int, direction float64) float64 { + return run.epoch + direction*float64(index)*run.step +} + +// integrate 自适应子步积分 [from, to],终点精确落在 to / adaptively integrates [from, to], landing exactly on to. +func (run *propagationRun) integrate(pos, vel [3]float64, from, to float64, session *propagationSession) (outPos, outVel [3]float64, err error) { + outPos, outVel = pos, vel + current := from + for current != to { + dt := to - current + var used float64 + outPos, outVel, used, err = propagationAdaptiveStep(outPos, outVel, current, dt, run.elements, run.perturbers, session, run.tolerance) + if err != nil { + return outPos, outVel, err + } + if used == dt { + current = to + } else { + current += used + } + } + return outPos, outVel, nil +} + +func propagationAdaptiveStep(pos, vel [3]float64, jd, dt float64, elements Elements, perturbers []Body, session *propagationSession, tolerance float64) (outPos, outVel [3]float64, used float64, err error) { + trial := dt + lastError := math.Inf(1) + for attempts := 0; attempts < 20; attempts++ { + fullPos, fullVel, fullErr := propagationRK4(pos, vel, jd, trial, elements, perturbers, session) + if fullErr != nil { + return pos, vel, trial, fullErr + } + halfPos, halfVel, halfErr := propagationRK4(pos, vel, jd, trial/2, elements, perturbers, session) + if halfErr != nil { + return pos, vel, trial, halfErr + } + halfPos, halfVel, halfErr = propagationRK4(halfPos, halfVel, jd+trial/2, trial/2, elements, perturbers, session) + if halfErr != nil { + return pos, vel, trial, halfErr + } + error := 0.0 + for index := range fullPos { + positionScale := math.Max(1, math.Max(math.Abs(fullPos[index]), math.Abs(halfPos[index]))) + velocityScale := math.Max(1, math.Max(math.Abs(fullVel[index]), math.Abs(halfVel[index]))) + error = math.Max(error, math.Abs(fullPos[index]-halfPos[index])/(15*positionScale)) + error = math.Max(error, math.Abs(fullVel[index]-halfVel[index])/(15*velocityScale)) + } + lastError = error + if error <= tolerance || math.Abs(trial) <= 1e-8 { + return halfPos, halfVel, trial, nil + } + trial /= 2 + } + return pos, vel, trial, fmt.Errorf("orbit: propagation step did not converge (dt=%g error=%g tolerance=%g)", trial, lastError, tolerance) +} + +type propagationSession struct { + provider StateProvider + states map[propagationStateKey]propagationSample +} + +type propagationStateKey struct { + body Body + jd uint64 +} + +type propagationSample struct { + pos [3]float64 + vel [3]float64 + err error +} + +func newPropagationSession(provider StateProvider) *propagationSession { + return &propagationSession{ + provider: provider, + states: make(map[propagationStateKey]propagationSample), + } +} + +func (session *propagationSession) state(body Body, jd float64) (pos, vel [3]float64, err error) { + key := propagationStateKey{body: body, jd: math.Float64bits(jd)} + if sample, ok := session.states[key]; ok { + return sample.pos, sample.vel, sample.err + } + pos, vel, err = session.provider.State(body, ephem.SSB, jd) + session.states[key] = propagationSample{pos: pos, vel: vel, err: err} + return pos, vel, err +} + +// EphemerisEngine 根数传播的只读星历引擎,结果与 StateWithOptions 逐位一致且不受查询顺序影响 / read-only engine over propagated elements. +type EphemerisEngine struct { + target Body + elements Elements + options PropagationOptions + setupErr error + run *propagationRun + mu sync.Mutex + cache propagationCheckpoints +} + +// NewEphemerisEngine 创建根数传播星历引擎;选项无效时 State 返回对应错误 / creates an ephemeris engine backed by propagated elements; invalid options surface from State. +func NewEphemerisEngine(target Body, elements Elements, options PropagationOptions) *EphemerisEngine { + engine := &EphemerisEngine{target: target, elements: elements} + options.Perturbers, engine.setupErr = normalizePerturbers(options.Perturbers) + engine.options = options + if engine.setupErr == nil && len(options.Perturbers) != 0 { + engine.run, engine.setupErr = newPropagationRun(elements, options.Perturbers, options) + } + return engine +} + +// Target 返回传播目标 / returns the propagated target. +func (engine *EphemerisEngine) Target() Body { return engine.target } + +// Elements 返回传播根数 / returns the propagation elements. +func (engine *EphemerisEngine) Elements() Elements { return engine.elements } + +// State 返回传播目标相对指定中心的状态 / returns propagated target state relative to a center. +func (engine *EphemerisEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if engine == nil || target != engine.target { + if target == center { + return pos, vel, nil + } + return pos, vel, ephem.ErrUnsupportedBody + } + if target == center { + return pos, vel, nil + } + if center != ephem.Sun && center != ephem.SSB { + return pos, vel, ephem.ErrUnsupportedBody + } + if engine.setupErr != nil { + return pos, vel, engine.setupErr + } + if !finite(jdTDB) { + return pos, vel, ErrInvalidPropagationElements + } + if engine.run == nil { + pos, vel, err = twoBodyState(engine.elements, jdTDB) + } else { + engine.mu.Lock() + pos, vel, err = engine.run.state(jdTDB, &engine.cache) + engine.mu.Unlock() + } + if err != nil { + return pos, vel, err + } + if center == ephem.Sun { + return pos, vel, nil + } + provider := engine.options.Engine + if provider == nil { + provider = engine.options.Context + } + if provider == nil { + return pos, vel, ErrPerturbationEngine + } + sunPos, sunVel, sunErr := provider.State(ephem.Sun, ephem.SSB, jdTDB) + if sunErr != nil { + return pos, vel, sunErr + } + for index := range pos { + pos[index] += sunPos[index] + vel[index] += sunVel[index] + } + return pos, vel, nil +} + +// Coverage 返回传播引擎的数学覆盖范围 / returns the mathematical coverage of the propagator. +func (engine *EphemerisEngine) Coverage(target Body) (start, end float64, ok bool) { + if engine == nil || target != engine.target { + return 0, 0, false + } + return math.Inf(-1), math.Inf(1), true +} + +func propagationRK4(pos, vel [3]float64, jd, dt float64, elements Elements, perturbers []Body, session *propagationSession) ([3]float64, [3]float64, error) { + p1, v1, err := propagationDerivative(pos, vel, jd, elements, perturbers, session) + if err != nil { + return pos, vel, err + } + p2 := vectorAdd(pos, vectorScale(p1, dt/2)) + v2 := vectorAdd(vel, vectorScale(v1, dt/2)) + p2d, v2d, err := propagationDerivative(p2, v2, jd+dt/2, elements, perturbers, session) + if err != nil { + return pos, vel, err + } + p3 := vectorAdd(pos, vectorScale(p2d, dt/2)) + v3 := vectorAdd(vel, vectorScale(v2d, dt/2)) + p3d, v3d, err := propagationDerivative(p3, v3, jd+dt/2, elements, perturbers, session) + if err != nil { + return pos, vel, err + } + p4 := vectorAdd(pos, vectorScale(p3d, dt)) + v4 := vectorAdd(vel, vectorScale(v3d, dt)) + p4d, v4d, err := propagationDerivative(p4, v4, jd+dt, elements, perturbers, session) + if err != nil { + return pos, vel, err + } + return vectorAdd(pos, vectorScale(vectorAdd(vectorAdd(p1, vectorScale(p2d, 2)), vectorAdd(vectorScale(p3d, 2), p4d)), dt/6)), vectorAdd(vel, vectorScale(vectorAdd(vectorAdd(v1, vectorScale(v2d, 2)), vectorAdd(vectorScale(v3d, 2), v4d)), dt/6)), nil +} + +func propagationDerivative(pos, vel [3]float64, jd float64, elements Elements, perturbers []Body, session *propagationSession) ([3]float64, [3]float64, error) { + radius := vectorNorm(pos) + if !(radius > 0) || !finite(radius) { + return vel, [3]float64{}, ErrInvalidPropagationElements + } + acc := vectorScale(pos, -propagationSunGM/math.Pow(radius, 3)) + sunPos, _, err := session.state(ephem.Sun, jd) + if err != nil { + return vel, acc, err + } + for _, body := range perturbers { + if body == ephem.Sun || body == ephem.SSB { + continue + } + bodyPos, _, bodyErr := session.state(body, jd) + if bodyErr != nil { + return vel, acc, bodyErr + } + relBody := vectorSub(bodyPos, sunPos) + toBody := vectorSub(relBody, pos) + mu := perturbingGM(body) + if vectorNorm(relBody) == 0 || vectorNorm(toBody) == 0 { + return vel, acc, ErrInvalidPropagationElements + } + acc = vectorAdd(acc, vectorScale(toBody, mu/math.Pow(vectorNorm(toBody), 3))) + acc = vectorAdd(acc, vectorScale(relBody, -mu/math.Pow(vectorNorm(relBody), 3))) + } + return vel, acc, nil +} + +func perturbingGM(body Body) float64 { + const secondPerDay = 86400.0 + var gm float64 + switch body { + case ephem.Mercury: + gm = 22031.86855 + case ephem.Venus: + gm = 324858.592 + case ephem.Earth: + gm = 398600.435436 + case ephem.Moon: + gm = 4902.800066 + case ephem.EMB: + gm = 398600.435436 + 4902.800066 + case ephem.Mars: + gm = 42828.375214 + case ephem.Jupiter: + gm = 126686534.911 + case ephem.Saturn: + gm = 37931207.8 + case ephem.Uranus: + gm = 5793966.0 + case ephem.Neptune: + gm = 6835100.0 + } + return gm * secondPerDay * secondPerDay +} + +// normalizePerturbers 去重并跳过太阳与 SSB;未知天体或 EMB 与地球/月球同时出现时报错 / dedups and skips Sun and SSB; rejects unknown bodies and EMB combined with Earth or Moon. +func normalizePerturbers(perturbers []Body) ([]Body, error) { + if len(perturbers) == 0 { + return nil, nil + } + seen := make(map[Body]struct{}, len(perturbers)) + result := make([]Body, 0, len(perturbers)) + for _, body := range perturbers { + if body == ephem.Sun || body == ephem.SSB { + continue + } + if perturbingGM(body) == 0 { + return nil, fmt.Errorf("%w: %d", ErrUnsupportedPerturber, body) + } + if _, ok := seen[body]; ok { + continue + } + seen[body] = struct{}{} + result = append(result, body) + } + _, hasEMB := seen[ephem.EMB] + _, hasEarth := seen[ephem.Earth] + _, hasMoon := seen[ephem.Moon] + if hasEMB && (hasEarth || hasMoon) { + return nil, fmt.Errorf("%w: EMB already includes Earth and Moon", ErrUnsupportedPerturber) + } + return result, nil +} + +func twoBodyState(elements Elements, jd float64) (pos, vel [3]float64, err error) { + if !finite(jd) { + return pos, vel, ErrInvalidPropagationElements + } + basicElements := toBasicElements(elements) + trueAnomaly := basic.OrbitTrueAnomaly(jd, basicElements) + if math.IsNaN(trueAnomaly) { + return pos, vel, ErrInvalidPropagationElements + } + resolved := elementsAt(elements, jd) + eccentricity := resolved.E + semiLatus := resolved.A * (1 - eccentricity*eccentricity) + if usesPerihelionForm(elements) { + semiLatus = resolved.Q * (1 + eccentricity) + } + if !(semiLatus > 0) || !finite(semiLatus) { + return pos, vel, ErrInvalidPropagationElements + } + nu := trueAnomaly * math.Pi / 180 + mu := propagationGaussianK * propagationGaussianK + radius := semiLatus / (1 + eccentricity*math.Cos(nu)) + if !(radius > 0) || !finite(radius) { + return pos, vel, ErrInvalidPropagationElements + } + x := radius * math.Cos(nu) + y := radius * math.Sin(nu) + speedScale := math.Sqrt(mu / semiLatus) + vx := -speedScale * math.Sin(nu) + vy := speedScale * (eccentricity + math.Cos(nu)) + posAU, velAU := rotatePerifocal(x, y, vx, vy, resolved) + return rotateEquatorial(posAU, propagationAU), rotateEquatorial(velAU, propagationAU), nil +} + +func elementsAt(elements Elements, jd float64) Elements { + if usesPerihelionForm(elements) || !finite(elements.EpochJD) { + return elements + } + delta := jd - elements.EpochJD + elements.A += elements.ADot * delta + elements.E += elements.EDot * delta + elements.I += elements.IDot * delta + elements.Omega += elements.OmegaDot * delta + elements.W += elements.WDot * delta + return elements +} + +func usesPerihelionForm(elements Elements) bool { return elements.Q > 0 && finite(elements.TpJD) } + +func rotatePerifocal(x, y, vx, vy float64, elements Elements) ([3]float64, [3]float64) { + omega := elements.Omega * math.Pi / 180 + argument := elements.W * math.Pi / 180 + inclination := elements.I * math.Pi / 180 + sinOmega, cosOmega := math.Sincos(omega) + sinArgument, cosArgument := math.Sincos(argument) + sinInclination, cosInclination := math.Sincos(inclination) + basisX := [3]float64{cosOmega*cosArgument - sinOmega*sinArgument*cosInclination, sinOmega*cosArgument + cosOmega*sinArgument*cosInclination, sinArgument * sinInclination} + basisY := [3]float64{-cosOmega*sinArgument - sinOmega*cosArgument*cosInclination, -sinOmega*sinArgument + cosOmega*cosArgument*cosInclination, cosArgument * sinInclination} + return vectorAdd(vectorScale(basisX, x), vectorScale(basisY, y)), vectorAdd(vectorScale(basisX, vx), vectorScale(basisY, vy)) +} + +var sinJ2000Obliquity, cosJ2000Obliquity = math.Sincos(basic.EclipticObliquity(2451545.0, false) * math.Pi / 180) + +func rotateEquatorial(value [3]float64, scale float64) [3]float64 { + sinEps, cosEps := sinJ2000Obliquity, cosJ2000Obliquity + return [3]float64{value[0] * scale, (value[1]*cosEps - value[2]*sinEps) * scale, (value[1]*sinEps + value[2]*cosEps) * scale} +} + +func vectorAdd(left, right [3]float64) [3]float64 { + return [3]float64{left[0] + right[0], left[1] + right[1], left[2] + right[2]} +} + +func vectorSub(left, right [3]float64) [3]float64 { + return [3]float64{left[0] - right[0], left[1] - right[1], left[2] - right[2]} +} + +func vectorScale(value [3]float64, scale float64) [3]float64 { + return [3]float64{value[0] * scale, value[1] * scale, value[2] * scale} +} + +func vectorNorm(value [3]float64) float64 { + return math.Sqrt(value[0]*value[0] + value[1]*value[1] + value[2]*value[2]) +} + +func finite(value float64) bool { return !math.IsNaN(value) && !math.IsInf(value, 0) } + +var _ StateProvider = (*EphemerisEngine)(nil) diff --git a/orbit/propagation_fix_test.go b/orbit/propagation_fix_test.go new file mode 100644 index 0000000..fd6b29c --- /dev/null +++ b/orbit/propagation_fix_test.go @@ -0,0 +1,147 @@ +package orbit + +import ( + "errors" + "math" + "testing" +) + +// movingPerturbationEngine 提供随时间变化的太阳与行星位置 / supplies time-varying Sun and planet positions. +type movingPerturbationEngine struct{} + +func (movingPerturbationEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if target == center { + return pos, vel, nil + } + if center != 0 { + return pos, vel, ErrPerturbationEngine + } + angle := func(period float64) float64 { return 2 * math.Pi * (jdTDB - 2451545) / period } + switch target { + case 10: + phase := angle(4332.6) + pos = [3]float64{-742000 * math.Cos(phase), -742000 * math.Sin(phase), 0} + case 3: + phase := angle(365.25) + pos = [3]float64{149597870 * math.Cos(phase), 149597870 * math.Sin(phase), 0} + case 5: + phase := angle(4332.6) + pos = [3]float64{778000000 * math.Cos(phase), 778000000 * math.Sin(phase), 0} + default: + return pos, vel, ErrPerturbationEngine + } + return pos, vel, nil +} + +func (movingPerturbationEngine) Coverage(target Body) (start, end float64, ok bool) { + return 0, 3000000, true +} + +func TestEphemerisEngineMatchesFreshPropagationInAnyOrder(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + options := PropagationOptions{Perturbers: []Body{5, 3}, Engine: movingPerturbationEngine{}, Step: 0.7, Tolerance: 1e-9} + queries := []float64{2451545 + 400.3, 2451545 + 12.25, 2451545 - 90.1, 2451545 + 400.3, 2451545 + 33.6, 2451545 - 3.5, 2451545, 2451545 + 1000} + engine := NewEphemerisEngine(Body(12345), elements, options) + for _, jd := range queries { + gotPos, gotVel, err := engine.State(Body(12345), 10, jd) + if err != nil { + t.Fatal(err) + } + wantPos, wantVel, err := StateWithOptions(elements, jd, options) + if err != nil { + t.Fatal(err) + } + if gotPos != wantPos || gotVel != wantVel { + t.Fatalf("jd %.2f: engine %v %v, fresh %v %v", jd, gotPos, gotVel, wantPos, wantVel) + } + } + // 逆序查询同样逐位一致 / reverse order also matches bit for bit. + reverse := NewEphemerisEngine(Body(12345), elements, options) + for index := len(queries) - 1; index >= 0; index-- { + gotPos, _, err := reverse.State(Body(12345), 10, queries[index]) + if err != nil { + t.Fatal(err) + } + wantPos, _, _ := StateWithOptions(elements, queries[index], options) + if gotPos != wantPos { + t.Fatalf("reverse jd %.2f: engine %v, fresh %v", queries[index], gotPos, wantPos) + } + } +} + +func TestEphemerisEngineKeepsConstructionElements(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + options := PropagationOptions{Perturbers: []Body{5}, Engine: movingPerturbationEngine{}, Step: 1} + engine := NewEphemerisEngine(Body(12345), elements, options) + if _, _, err := engine.State(Body(12345), 10, 2451545+50); err != nil { + t.Fatal(err) + } + options.Perturbers[0] = 3 + got := engine.Elements() + got.A = 9 + gotPos, _, err := engine.State(Body(12345), 10, 2451545+80) + if err != nil { + t.Fatal(err) + } + wantPos, _, _ := StateWithOptions(elements, 2451545+80, PropagationOptions{Perturbers: []Body{5}, Engine: movingPerturbationEngine{}, Step: 1}) + if gotPos != wantPos || engine.Elements() != elements || engine.Target() != Body(12345) { + t.Fatalf("engine state changed after caller mutation: %v versus %v", gotPos, wantPos) + } +} + +func TestStateWithOptionsPerihelionFormStartsAtOsculatingEpoch(t *testing.T) { + elements := Elements{EpochJD: 2451545 + 120, Q: 0.6, E: 0.9, I: 30, Omega: 40, W: 50, TpJD: 2451545} + options := PropagationOptions{Perturbers: []Body{5}, Engine: movingPerturbationEngine{}, Step: 1, Tolerance: 1e-10} + gotPos, gotVel, err := StateWithOptions(elements, elements.EpochJD, options) + if err != nil { + t.Fatal(err) + } + wantPos, wantVel, err := State(elements, elements.EpochJD) + if err != nil { + t.Fatal(err) + } + if gotPos != wantPos || gotVel != wantVel { + t.Fatalf("osculating epoch state %v %v, want two-body %v %v", gotPos, gotVel, wantPos, wantVel) + } + // 未给 EpochJD 时仍以 TpJD 为起点 / without EpochJD the integration still starts at TpJD. + noEpoch := elements + noEpoch.EpochJD = 0 + gotPos, _, err = StateWithOptions(noEpoch, noEpoch.TpJD, options) + if err != nil { + t.Fatal(err) + } + wantPos, _, _ = State(noEpoch, noEpoch.TpJD) + if gotPos != wantPos { + t.Fatalf("TpJD start state %v, want %v", gotPos, wantPos) + } +} + +func TestStateWithOptionsRejectsUnsupportedPerturbers(t *testing.T) { + elements := Elements{EpochJD: 2451545, A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + for _, perturbers := range [][]Body{{5, 9}, {2000001}, {3, 399}, {301, 3}} { + _, _, err := StateWithOptions(elements, elements.EpochJD+5, PropagationOptions{Perturbers: perturbers, Engine: movingPerturbationEngine{}}) + if !errors.Is(err, ErrUnsupportedPerturber) { + t.Fatalf("perturbers %v error = %v, want ErrUnsupportedPerturber", perturbers, err) + } + engine := NewEphemerisEngine(Body(12345), elements, PropagationOptions{Perturbers: perturbers, Engine: movingPerturbationEngine{}}) + if _, _, err := engine.State(Body(12345), 10, elements.EpochJD+5); !errors.Is(err, ErrUnsupportedPerturber) { + t.Fatalf("engine perturbers %v error = %v, want ErrUnsupportedPerturber", perturbers, err) + } + } + plain, _, _ := State(elements, elements.EpochJD+30) + withEMB, _, err := StateWithOptions(elements, elements.EpochJD+30, PropagationOptions{Perturbers: []Body{3}, Engine: movingPerturbationEngine{}, Tolerance: 1e-10}) + if err != nil { + t.Fatal(err) + } + if withEMB == plain { + t.Fatal("EMB perturbation did not change the state") + } +} + +func TestStateWithOptionsRejectsMissingClassicalEpoch(t *testing.T) { + elements := Elements{A: 1.5, E: 0.02, I: 3, Omega: 20, W: 30, M0: 10} + _, _, err := StateWithOptions(elements, 2451545, PropagationOptions{Perturbers: []Body{3}, Engine: movingPerturbationEngine{}}) + if !errors.Is(err, ErrInvalidPropagationElements) { + t.Fatalf("error = %v, want ErrInvalidPropagationElements", err) + } +} diff --git a/phenomena/altitude.go b/phenomena/altitude.go new file mode 100644 index 0000000..b6d9cc3 --- /dev/null +++ b/phenomena/altitude.go @@ -0,0 +1,43 @@ +package phenomena + +import ( + "time" + + "b612.me/astro/basic" +) + +// AltitudeLimb 日月取高度的位置 / limb used for Sun and Moon altitude. +type AltitudeLimb = basic.AltitudeLimb + +const ( + LimbUpper = basic.AltitudeLimbUpper + LimbCenter = basic.AltitudeLimbCenter + LimbLower = basic.AltitudeLimbLower +) + +// AltitudeOptions 任意高度升落选项;零值为上缘、标准气象、计入折射 / altitude crossing options. +type AltitudeOptions = basic.AltitudeCrossingOptions + +// AltitudeState 一个民用日内的穿越状态 / crossing state over one civil day. +type AltitudeState = basic.AltitudeCrossingState + +const ( + AltitudeCrosses = basic.AltitudeCrosses + AltitudeAlwaysAbove = basic.AltitudeAlwaysAbove + AltitudeAlwaysBelow = basic.AltitudeAlwaysBelow + AltitudeTangent = basic.AltitudeTangent +) + +// AltitudeCrossingResult date 所在民用日内首次升、落(无则为零值,对应 HasRise/HasSet 为 false)与残差范围(度)/ first rise and set on the civil day. +type AltitudeCrossingResult = basic.AltitudeCrossingResult + +// AltitudeCrossing 日、月或行星在 date 所在民用日内穿过视高度 altitude(度,相对天文地平)的时刻;height 只影响月球视差 / +// Sun, Moon or planet crossings of an apparent altitude. +func (module *Phenomena) AltitudeCrossing(body Body, date time.Time, lon, lat, height, altitude float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return basic.SkyAltitudeCrossingOnDate(body, date, lon, lat, height, altitude, options, module.inst) +} + +// StarAltitudeCrossing 恒星(视赤经、视赤纬,度)穿过视高度 altitude 的时刻 / star crossings of an apparent altitude. +func StarAltitudeCrossing(date time.Time, ra, dec, lon, lat, altitude float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return basic.StarAltitudeCrossingOnDate(date, ra, dec, lon, lat, altitude, options) +} diff --git a/phenomena/api_default.go b/phenomena/api_default.go new file mode 100644 index 0000000..b266fc2 --- /dev/null +++ b/phenomena/api_default.go @@ -0,0 +1,53 @@ +package phenomena + +import "time" + +// LongitudeCrossings 视黄经经过 longitude 的全部时刻 / times when the apparent longitude equals longitude. +func LongitudeCrossings(body Body, longitude float64, start, end time.Time) []Event { + return defaultModule.LongitudeCrossings(body, longitude, start, end) +} + +// Stations 视黄经留 / longitude stations. +func Stations(body Body, start, end time.Time) []Event { + return defaultModule.Stations(body, start, end) +} + +// AspectCrossings a 与 b 视黄经差(a − b)等于 angle 的时刻 / times when lon(a) − lon(b) equals angle. +func AspectCrossings(a, b Body, angle float64, start, end time.Time) []Event { + return defaultModule.AspectCrossings(a, b, angle, start, end) +} + +// ClosestApproaches 两天体最近角距 / closest approaches of two bodies. +func ClosestApproaches(a, b Body, maxSeparation float64, start, end time.Time) []Event { + return defaultModule.ClosestApproaches(a, b, maxSeparation, start, end) +} + +// StarApproaches 天体与恒星最近角距 / closest approaches of a body to a star. +func StarApproaches(body Body, star StarCoordinate, maxSeparation float64, start, end time.Time) []Event { + return defaultModule.StarApproaches(body, star, maxSeparation, start, end) +} + +// PlanetHeliacalEvents 行星偕日事件 / planet heliacal events. +func PlanetHeliacalEvents(body Body, start, end time.Time, lon, lat float64, options HeliacalOptions) []HeliacalEvent { + return defaultModule.PlanetHeliacalEvents(body, start, end, lon, lat, options) +} + +// StarHeliacalEvents 恒星偕日事件 / star heliacal events. +func StarHeliacalEvents(star StarCoordinate, magnitude float64, start, end time.Time, lon, lat float64, options HeliacalOptions) []HeliacalEvent { + return defaultModule.StarHeliacalEvents(star, magnitude, start, end, lon, lat, options) +} + +// Apsides 行星或地球的近日点与远日点 / perihelia and aphelia. +func Apsides(body Body, start, end time.Time) []OrbitalEvent { + return defaultModule.Apsides(body, start, end) +} + +// Nodes 行星的升交点与降交点 / node passages. +func Nodes(body Body, start, end time.Time) []OrbitalEvent { + return defaultModule.Nodes(body, start, end) +} + +// AltitudeCrossing 日、月或行星穿过任意视高度的时刻 / altitude crossings with the default ephemeris. +func AltitudeCrossing(body Body, date time.Time, lon, lat, height, altitude float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return defaultModule.AltitudeCrossing(body, date, lon, lat, height, altitude, options) +} diff --git a/phenomena/orbital_zone_test.go b/phenomena/orbital_zone_test.go new file mode 100644 index 0000000..1a40339 --- /dev/null +++ b/phenomena/orbital_zone_test.go @@ -0,0 +1,30 @@ +package phenomena + +import ( + "testing" + "time" +) + +func TestApsidesIndependentOfHostZone(t *testing.T) { + start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC) + saved := time.Local + defer func() { time.Local = saved }() + + time.Local = time.UTC + want := Apsides(Earth, start, end) + newYork, err := time.LoadLocation("America/New_York") + if err != nil { + newYork = time.FixedZone("EST", -5*3600) + } + time.Local = newYork + got := Apsides(Earth, start, end) + if len(got) != len(want) || len(got) == 0 { + t.Fatalf("len got %d, want %d", len(got), len(want)) + } + for i := range got { + if !got[i].Time.Equal(want[i].Time) || got[i].Time.Location() != time.UTC { + t.Fatalf("event %d: got %v, want %v", i, got[i].Time, want[i].Time) + } + } +} diff --git a/phenomena/phenomena.go b/phenomena/phenomena.go new file mode 100644 index 0000000..4d35596 --- /dev/null +++ b/phenomena/phenomena.go @@ -0,0 +1,176 @@ +// Package phenomena 提供任意两天体的黄经穿越、留、相位、最近角距与偕日可见搜索 / generic sky event and heliacal visibility searches. +package phenomena + +import ( + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +// Body 可搜索的天体 / searchable body. +type Body = basic.SkyBody + +// Earth 地球,只用于 Apsides / Earth, valid only for Apsides. +const Earth = basic.SkyEarth + +const ( + // Sun 太阳 / the Sun. + Sun = basic.SkySun + // Moon 月球 / the Moon. + Moon = basic.SkyMoon + // Mercury 水星 / Mercury. + Mercury = basic.SkyMercury + // Venus 金星 / Venus. + Venus = basic.SkyVenus + // Mars 火星 / Mars. + Mars = basic.SkyMars + // Jupiter 木星 / Jupiter. + Jupiter = basic.SkyJupiter + // Saturn 土星 / Saturn. + Saturn = basic.SkySaturn + // Uranus 天王星 / Uranus. + Uranus = basic.SkyUranus + // Neptune 海王星 / Neptune. + Neptune = basic.SkyNeptune +) + +// StarCoordinate 恒星星表坐标 / catalog star coordinate. +type StarCoordinate = basic.StarCoordinate + +// HeliacalOptions 偕日可见参数 / heliacal visibility options. +type HeliacalOptions = basic.HeliacalOptions + +// HeliacalEventKind 偕日事件类型 / heliacal event kind. +type HeliacalEventKind = basic.HeliacalEventKind + +const ( + // HeliacalMorningFirst 晨始见(偕日升) / morning first visibility. + HeliacalMorningFirst = basic.HeliacalMorningFirst + // HeliacalMorningLast 晨末见 / morning last visibility. + HeliacalMorningLast = basic.HeliacalMorningLast + // HeliacalEveningFirst 昏始见 / evening first visibility. + HeliacalEveningFirst = basic.HeliacalEveningFirst + // HeliacalEveningLast 昏末见(偕日落) / evening last visibility. + HeliacalEveningLast = basic.HeliacalEveningLast +) + +// Event 天象事件;Value 为事件对应的角量(度)/ sky event; Value is the event angle in degrees. +type Event struct { + Time time.Time + Value float64 +} + +// HeliacalEvent 偕日事件,Time 为当次晨昏最佳可见时刻 / heliacal event at the best moment of that twilight. +type HeliacalEvent struct { + Kind HeliacalEventKind + Time time.Time + Margin float64 + TargetAltitude float64 + SunAltitude float64 +} + +// Phenomena 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Phenomena struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Phenomena { return &Phenomena{inst: inst} } + +var defaultModule Phenomena + +func toTT(date time.Time) float64 { return basic.UTC2TT(basic.Date2JD(date.UTC())) } + +func events(list []basic.SkyEvent, loc *time.Location) []Event { + out := make([]Event, 0, len(list)) + for _, e := range list { + out = append(out, Event{Time: basic.JD2DateByZone(basic.TT2UTC(e.JDE), loc, false), Value: e.Value}) + } + return out +} + +// LongitudeCrossings 视黄经经过 longitude 的全部时刻 / times when the apparent longitude equals longitude. +func (module *Phenomena) LongitudeCrossings(body Body, longitude float64, start, end time.Time) []Event { + return events(basic.SkyLongitudeCrossingsWithInstance(body, longitude, toTT(start), toTT(end), module.inst), start.Location()) +} + +// Stations 视黄经留,Value 为留时视黄经 / longitude stations; Value is the longitude. +func (module *Phenomena) Stations(body Body, start, end time.Time) []Event { + return events(basic.SkyStationsWithInstance(body, toTT(start), toTT(end), module.inst), start.Location()) +} + +// AspectCrossings a 与 b 视黄经差(a − b)等于 angle 的时刻 / times when lon(a) − lon(b) equals angle. +func (module *Phenomena) AspectCrossings(a, b Body, angle float64, start, end time.Time) []Event { + return events(basic.SkyAspectCrossingsWithInstance(a, b, angle, toTT(start), toTT(end), module.inst), start.Location()) +} + +// ClosestApproaches 两天体地心视角距的局部极小,Value 为角距(度)/ closest approaches within maxSeparation degrees. +func (module *Phenomena) ClosestApproaches(a, b Body, maxSeparation float64, start, end time.Time) []Event { + return events(basic.SkySeparationMinimaWithInstance(a, b, maxSeparation, toTT(start), toTT(end), module.inst), start.Location()) +} + +// StarApproaches 天体与恒星地心视角距的局部极小 / closest approaches of a body to a star. +func (module *Phenomena) StarApproaches(body Body, star StarCoordinate, maxSeparation float64, start, end time.Time) []Event { + return events(basic.SkyStarSeparationMinimaWithInstance(body, star, maxSeparation, toTT(start), toTT(end), module.inst), start.Location()) +} + +func heliacal(list []basic.HeliacalEvent, loc *time.Location) []HeliacalEvent { + out := make([]HeliacalEvent, 0, len(list)) + for _, e := range list { + out = append(out, HeliacalEvent{e.Kind, basic.JD2DateByZone(e.JD, loc, false), e.Margin, e.TargetAltitude, e.SunAltitude}) + } + return out +} + +// PlanetHeliacalEvents 行星晨昏始见、末见(经度东正)/ planet heliacal events; longitude east positive. +func (module *Phenomena) PlanetHeliacalEvents(body Body, start, end time.Time, lon, lat float64, options HeliacalOptions) []HeliacalEvent { + return heliacal(basic.FindPlanetHeliacalEventsWithInstance(body, basic.Date2JD(start.UTC()), basic.Date2JD(end.UTC()), lon, lat, options, module.inst), start.Location()) +} + +// StarHeliacalEvents 恒星晨昏始见、末见;magnitude 为 V 星等 / star heliacal events with V magnitude. +func (module *Phenomena) StarHeliacalEvents(star StarCoordinate, magnitude float64, start, end time.Time, lon, lat float64, options HeliacalOptions) []HeliacalEvent { + return heliacal(basic.FindStarHeliacalEventsWithInstance(star, magnitude, basic.Date2JD(start.UTC()), basic.Date2JD(end.UTC()), lon, lat, options, module.inst), start.Location()) +} + +// LimitingMagnitude 无月夜肉眼极限星等(Schaefer 模型),消光系数非正时为 NaN / naked-eye limiting magnitude without moonlight. +func LimitingMagnitude(targetAltitude, sunAltitude, separation, extinction float64) float64 { + return basic.LimitingMagnitude(targetAltitude, sunAltitude, separation, extinction) +} + +// OrbitalEventKind 轨道事件类型 / orbital event kind. +type OrbitalEventKind = basic.OrbitalEventKind + +const ( + // Perihelion 近日点 / perihelion. + Perihelion = basic.OrbitalPerihelion + // Aphelion 远日点 / aphelion. + Aphelion = basic.OrbitalAphelion + // AscendingNode 升交点 / ascending node. + AscendingNode = basic.OrbitalAscendingNode + // DescendingNode 降交点 / descending node. + DescendingNode = basic.OrbitalDescendingNode +) + +// OrbitalEvent 轨道事件,DistanceAU 为日心距 / orbital event with heliocentric distance in AU. +type OrbitalEvent struct { + Time time.Time + Kind OrbitalEventKind + DistanceAU float64 +} + +func orbital(list []basic.OrbitalEvent, loc *time.Location) []OrbitalEvent { + out := make([]OrbitalEvent, 0, len(list)) + for _, e := range list { + out = append(out, OrbitalEvent{Time: basic.JD2DateByZone(basic.TT2UTC(e.JDE), loc, false), Kind: e.Kind, DistanceAU: e.DistanceAU}) + } + return out +} + +// Apsides 行星或地球在 [start, end] 内的近日点与远日点 / perihelia and aphelia in the window. +func (module *Phenomena) Apsides(body Body, start, end time.Time) []OrbitalEvent { + return orbital(basic.PlanetApsidesWithInstance(body, toTT(start), toTT(end), module.inst), start.Location()) +} + +// Nodes 行星在 [start, end] 内穿过历元平黄道的升交点与降交点 / ascending and descending node passages. +func (module *Phenomena) Nodes(body Body, start, end time.Time) []OrbitalEvent { + return orbital(basic.PlanetNodesWithInstance(body, toTT(start), toTT(end), module.inst), start.Location()) +} diff --git a/phenomena/phenomena_test.go b/phenomena/phenomena_test.go new file mode 100644 index 0000000..37cf888 --- /dev/null +++ b/phenomena/phenomena_test.go @@ -0,0 +1,41 @@ +// 被测契约:公开包装与 basic 一致,并保留输入时区。 +package phenomena + +import ( + "testing" + "time" +) + +func TestGreatConjunctionPublic(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + got := ClosestApproaches(Jupiter, Saturn, 1, time.Date(2020, 10, 1, 0, 0, 0, 0, loc), time.Date(2021, 3, 1, 0, 0, 0, 0, loc)) + if len(got) != 1 || got[0].Time.Location() != loc || got[0].Time.Month() != time.December || got[0].Value > 0.11 { + t.Fatalf("%+v", got) + } +} + +func TestVenusHeliacalPublic(t *testing.T) { + got := PlanetHeliacalEvents(Venus, time.Date(2025, 2, 15, 0, 0, 0, 0, time.UTC), time.Date(2025, 4, 30, 0, 0, 0, 0, time.UTC), 116.4, 39.9, HeliacalOptions{}) + if len(got) < 2 { + t.Fatalf("%+v", got) + } +} + +func TestAltitudeCrossingPublic(t *testing.T) { + date := time.Date(2025, 3, 20, 0, 0, 0, 0, time.FixedZone("CST", 8*3600)) + got, err := AltitudeCrossing(Sun, date, 116.4, 39.9, 0, -18, AltitudeOptions{Limb: LimbCenter, NoRefraction: true}) + if err != nil || !got.HasRise || !got.HasSet || got.State != AltitudeCrosses { + t.Fatalf("astronomical twilight: %+v %v", got, err) + } + if got.Rise.Hour() != 4 || got.Set.Hour() != 19 { + t.Errorf("unexpected twilight times %v %v", got.Rise, got.Set) + } + star, err := StarAltitudeCrossing(date, 101.287, -16.716, 116.4, 39.9, 0, AltitudeOptions{}) + if err != nil || !star.HasRise { + t.Fatalf("star: %+v %v", star, err) + } + ev := Apsides(Earth, time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)) + if len(ev) != 2 || ev[0].Kind != Perihelion || ev[0].Time.Month() != time.January { + t.Errorf("earth apsides 2026: %+v", ev) + } +} diff --git a/phenomena/star_coordinate.go b/phenomena/star_coordinate.go new file mode 100644 index 0000000..1952d3a --- /dev/null +++ b/phenomena/star_coordinate.go @@ -0,0 +1,29 @@ +package phenomena + +import "b612.me/astro/basic" + +// StarData 内嵌 9100 星表中的一条记录 / one entry from the embedded 9100-star catalog. +type StarData = basic.StarData + +// StarCoordinateFromStarData 将星表记录转换为恒星坐标 / converts a catalog entry to a star coordinate. +func StarCoordinateFromStarData(star StarData) (StarCoordinate, error) { + return basic.StarCoordinateFromStarData(star) +} + +// StarCoordinateByHR 按 HR 编号从内嵌星表得到恒星坐标 / returns a star coordinate by HR number. +func StarCoordinateByHR(hr int) (StarCoordinate, error) { + star, err := basic.StarDataByHR(hr) + if err != nil { + return StarCoordinate{}, err + } + return StarCoordinateFromStarData(star) +} + +// StarCoordinateByName 按内嵌星表中文名得到恒星坐标 / returns a star coordinate by Chinese catalog name. +func StarCoordinateByName(name string) (StarCoordinate, error) { + star, err := basic.StarDataByChinese(name) + if err != nil { + return StarCoordinate{}, err + } + return StarCoordinateFromStarData(star) +} diff --git a/phenomena/star_coordinate_test.go b/phenomena/star_coordinate_test.go new file mode 100644 index 0000000..632cdc4 --- /dev/null +++ b/phenomena/star_coordinate_test.go @@ -0,0 +1,45 @@ +package phenomena + +import ( + "testing" + + "b612.me/astro/basic" +) + +func TestStarCoordinateLookup(t *testing.T) { + byHR, err := StarCoordinateByHR(2491) + if err != nil { + t.Fatalf("StarCoordinateByHR() error = %v", err) + } + byName, err := StarCoordinateByName("天狼") + if err != nil { + t.Fatalf("StarCoordinateByName() error = %v", err) + } + if byHR != byName { + t.Fatalf("lookup mismatch: HR=%+v name=%+v", byHR, byName) + } + + data, err := basic.StarDataByHR(2491) + if err != nil { + t.Fatalf("basic star lookup error = %v", err) + } + converted, err := StarCoordinateFromStarData(data) + if err != nil { + t.Fatalf("StarCoordinateFromStarData() error = %v", err) + } + if byHR != converted { + t.Fatalf("wrapper mismatch: lookup=%+v converted=%+v", byHR, converted) + } +} + +func TestStarCoordinateLookupInvalid(t *testing.T) { + if _, err := StarCoordinateByHR(0); err == nil { + t.Fatal("StarCoordinateByHR(0) expected an error") + } + if _, err := StarCoordinateByName("不存在的恒星"); err == nil { + t.Fatal("StarCoordinateByName() expected an error") + } + if _, err := StarCoordinateFromStarData(StarData{}); err == nil { + t.Fatal("StarCoordinateFromStarData() expected an error") + } +} diff --git a/planet/doc.go b/planet/doc.go index 1f7e6a3..60da5bc 100644 --- a/planet/doc.go +++ b/planet/doc.go @@ -1,3 +1,5 @@ -// Package planet 用内置 VSOP87 截断级数求行星黄经、黄纬与日心距离;jde 为 TT 儒略日,角度单位为度,距离单位为 AU。 -// Package planet evaluates the built-in truncated VSOP87 series for planetary longitude, latitude and heliocentric distance; jde is a TT Julian day, angles are degrees and distances are AU. +// Package planet 提供内置星历模型的低层入口:半解析表(默认)与 VSOP87/ELP2000-82 旧级数;jde 为 TT 儒略日,角度单位为度,距离单位为 AU。 +// Package planet provides the low-level entry points of the built-in ephemeris models: the semianalytic tables (default) and the VSOP87/ELP2000-82 legacy series; jde is a TT Julian day, angles are degrees and distances are AU. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. package planet diff --git a/planet/internal/semianalytic/apparent.go b/planet/internal/semianalytic/apparent.go new file mode 100644 index 0000000..f5f9cda --- /dev/null +++ b/planet/internal/semianalytic/apparent.go @@ -0,0 +1,247 @@ +package semianalytic + +import ( + "errors" + "math" + "sync" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet/internal/semianalytic/lunar" + "b612.me/astro/planet/internal/semianalytic/planets" +) + +const ( + cKMPerDay = 299792.458 * 86400 + // 太阳史瓦西半径 2GM☉/c²,AU。 + srsAU = 1.97412574336e-8 +) + +var errInvalidObserver = errors.New("semianalytic: non-finite observer state") + +type observer struct { + jd float64 + p, v [3]float64 // 地球日心位置 km、相对质心速度 km/日 + sunVel [3]float64 // 太阳相对质心速度 km/日 +} + +// 同一时刻常连续求多个天体,观测者状态缓存最近一次。 +var ( + observerMu sync.RWMutex + observerCache observer + observerSet bool +) + +func observerAt(jd float64) (observer, error) { + observerMu.RLock() + c, set := observerCache, observerSet + observerMu.RUnlock() + if set && c.jd == jd { + return c, nil + } + e, err := helio(ephem.Earth, jd) + if err != nil { + return observer{}, err + } + // 太阳质心速度只进光行差与光行时修正:快速档、只计四颗巨行星,内行星贡献 < 0.1 mas。 + var sv [3]float64 + _, total := planets.GM(1) + for id := 1; id <= 4; id++ { + gm, _ := planets.GM(id) + total += gm + } + for id := 5; id <= 8; id++ { + gm, _ := planets.GM(id) + _, v, err := planets.Fast().HeliocentricState(id, jd) + if err != nil { + return observer{}, err + } + for i := range sv { + sv[i] -= gm * v[i] + } + total += gm + } + o := observer{jd: jd, p: e.p} + for i := range sv { + sv[i] /= total + o.sunVel[i] = sv[i] + o.v[i] = e.v[i] + sv[i] + // 坏表读出的非有限速度不得进缓存,否则同一时刻会一直返回 NaN 方向而 err 为 nil。 + if !finite(o.v[i]) || !finite(o.p[i]) { + return observer{}, errInvalidObserver + } + } + observerMu.Lock() + observerCache, observerSet = o, true + observerMu.Unlock() + return o, nil +} + +// targetHelio 目标日心位置;fast 用快速档,只作光行时初值与几何距离。 +func targetHelio(b ephem.Body, jd float64, fast bool) ([3]float64, error) { + switch { + case b == ephem.Sun: + return [3]float64{}, nil + case b == ephem.Moon: + s, err := helio(ephem.Moon, jd) + return s.p, err + case b >= ephem.Mercury && b <= ephem.Neptune && b != ephem.EMB: + if fast { + return planets.Fast().Heliocentric(int(b), jd) + } + return planets.Default().Heliocentric(int(b), jd) + } + return [3]float64{}, ephem.ErrUnsupportedBody +} + +type apparentEntry struct { + jd float64 + dir [3]float64 + dist, tau float64 + set bool +} + +var ( + apparentMu sync.RWMutex + apparentCache [10]apparentEntry +) + +func apparentStore(slot int, e apparentEntry) { + e.set = true + apparentMu.Lock() + apparentCache[slot] = e + apparentMu.Unlock() +} + +// cacheSlot 天体对应的缓存槽;-1 表示不支持该天体。 +func cacheSlot(b ephem.Body) int { + switch { + case b == ephem.Sun: + return 0 + case b == ephem.Moon: + return 9 + case b >= ephem.Mercury && b <= ephem.Neptune && b != ephem.EMB: + return int(b) + } + return -1 +} + +// Apparent 地心视方向(ICRF 单位矢量)、几何地心距 km、光行时 日 / geocentric apparent direction. +func Apparent(target ephem.Body, jd float64) (dir [3]float64, distKM, tauDays float64, err error) { + slot := cacheSlot(target) + if slot < 0 { + return dir, 0, 0, ephem.ErrUnsupportedBody + } + if !finite(jd) { + return dir, 0, 0, ephem.ErrOutOfRange + } + apparentMu.RLock() + c := apparentCache[slot] + apparentMu.RUnlock() + if c.set && c.jd == jd { + return c.dir, c.dist, c.tau, nil + } + o, err := observerAt(jd) + if err != nil { + return dir, 0, 0, err + } + if target == ephem.Moon { + e, err := apparentMoon(jd, o) + if err != nil { + return dir, 0, 0, err + } + apparentStore(slot, e) + return e.dir, e.dist, e.tau, nil + } + now, err := targetHelio(target, jd, true) + if err != nil { + return dir, 0, 0, err + } + g := vsub(now, o.p) + dist := vlen(g) + tau := dist / cKMPerDay + // 迭代光行时,每次误差缩小约 v/c 倍:先用快速档收敛 τ,末次用全表;太阳在 τ 内相对质心的位移按线性计入。 + var r [3]float64 + for k := 0; k < 2; k++ { + if r, err = targetHelio(target, jd-tau, k == 0); err != nil { + return dir, 0, 0, err + } + for i := range g { + g[i] = r[i] - o.p[i] - o.sunVel[i]*tau + } + tau = vlen(g) / cKMPerDay + } + q := vscale(g, 1/vlen(g)) + if target != ephem.Sun && target != ephem.Moon { + q = deflect(q, r, o.p) + } + q = aberrate(q, o.v) + e := apparentEntry{jd: jd, dir: toICRF(q), dist: dist, tau: tau} + apparentStore(slot, e) + return e.dir, e.dist, e.tau, nil +} + +// apparentMoon 月球地心视方向。光行时仅约 1.3 s,若像行星那样在 jd 与 jd−τ 各求一次地月质心 +// 日心位置(~1.5e8 km)再与地球相减,远历元表求值的舍入噪声(2981 年约 2e-4 km)会原样进入 +// 地心矢量,超过下游牛顿迭代的残差容差。这里全程用地心量: +// 月球日心(jd−τ) − 地球日心(jd) = EMB(jd−τ) − EMB(jd) + f·m(jd−τ) + m(jd)/(1+μ), +// 其中 EMB 在 τ 内的位移按 jd 时刻速度线性外推,二阶项 ½aτ² < 1e-5 km。 +// 月球地心位置同样按 jd 时刻解析速度外推(½aτ² ≈ 2e-6 km):直接求 m(jd−τ) 时,jd−τ 的舍入 +// (~4.7e-10 日)乘月球地心速度(~8.8e4 km/日)给出 ~1e-5 km 的逐点抖动,有限差分导数会把它放大。 +func apparentMoon(jd float64, o observer) (apparentEntry, error) { + emb, err := planetHelio(int(ephem.EMB), jd) + if err != nil { + return apparentEntry{}, err + } + now, vel := lunar.Default().PositionVelocityJ2000(jd) + dist := vlen(now) + tau := dist / cKMPerDay + f := emrat / (1 + emrat) + g := now + for k := 0; k < 2; k++ { + for i := range g { + g[i] = f*(now[i]-vel[i]*tau) + now[i]/(1+emrat) - (emb.v[i]+o.sunVel[i])*tau + } + tau = vlen(g) / cKMPerDay + } + q := aberrate(vscale(g, 1/vlen(g)), o.v) + return apparentEntry{jd: jd, dir: toICRF(q), dist: dist, tau: tau}, nil +} + +// deflect 太阳引力偏折;p 目标方向,t 目标日心位置,e 地球日心位置。 +func deflect(p, t, e [3]float64) [3]float64 { + em := vlen(e) / auKM + eu := vscale(e, 1/vlen(e)) + q := vscale(t, 1/vlen(t)) + qpe := vdot(q, eu) + 1 + if qpe < 1e-9 { + return p + } + w := srsAU / em / qpe + pe, pq := vdot(p, eu), vdot(p, q) + // p × (e × q) = e(p·q) − q(p·e) + for i := range p { + p[i] += w * (eu[i]*pq - q[i]*pe) + } + return p +} + +// aberrate 相对论光行差;v 观测者相对质心速度 km/日。 +func aberrate(p, v [3]float64) [3]float64 { + b := vscale(v, 1/cKMPerDay) + bm := 1 / math.Sqrt(1-vdot(b, b)) + pdv := vdot(p, b) + w1 := 1 + pdv/(1+1/bm) + w2 := 1 / (1 + pdv) + var r [3]float64 + for i := range p { + r[i] = (p[i]/bm + w1*b[i]) * w2 + } + return vscale(r, 1/vlen(r)) +} + +func vdot(a, b [3]float64) float64 { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] } +func vlen(a [3]float64) float64 { return math.Sqrt(vdot(a, a)) } +func vsub(a, b [3]float64) [3]float64 { + return [3]float64{a[0] - b[0], a[1] - b[1], a[2] - b[2]} +} +func vscale(a [3]float64, k float64) [3]float64 { return [3]float64{a[0] * k, a[1] * k, a[2] * k} } diff --git a/planet/internal/semianalytic/apparent_generic_test.go b/planet/internal/semianalytic/apparent_generic_test.go new file mode 100644 index 0000000..91f91f0 --- /dev/null +++ b/planet/internal/semianalytic/apparent_generic_test.go @@ -0,0 +1,45 @@ +package semianalytic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +// stateOnly 只暴露几何状态,迫使实例走通用矢量链。 +type stateOnly struct{ Ephemeris } + +func (e stateOnly) State(t, c ephem.Body, jd float64) ([3]float64, [3]float64, error) { + return e.Ephemeris.State(t, c, jd) +} +func (e stateOnly) Coverage(t ephem.Body) (float64, float64, bool) { return e.Ephemeris.Coverage(t) } + +func TestGenericApparentMatchesNative(t *testing.T) { + inst := ephem.NewInstance(stateOnly{}) + worst := 0.0 + for _, jd := range []float64{2451545.0, 2460409.25, 2433282.5, 2488069.5} { + for _, b := range []ephem.Body{ephem.Sun, ephem.Moon, ephem.Mercury, ephem.Venus, ephem.Mars, ephem.Jupiter, ephem.Saturn, ephem.Uranus, ephem.Neptune} { + want, wd, _, err := Ephemeris{}.Apparent(b, jd) + if err != nil { + t.Fatal(err) + } + got, gd, _, err := inst.Apparent(b, jd) + if err != nil { + t.Fatalf("body %d: %v", b, err) + } + cx := got[1]*want[2] - got[2]*want[1] + cy := got[2]*want[0] - got[0]*want[2] + cz := got[0]*want[1] - got[1]*want[0] + c := got[0]*want[0] + got[1]*want[1] + got[2]*want[2] + // acos 在 1 附近每 ulp 约 3 mas,用 atan2 避开这一噪声底。 + arcsec := math.Atan2(math.Sqrt(cx*cx+cy*cy+cz*cz), c) * 206264.806 + worst = math.Max(worst, arcsec) + // 原生链的太阳质心速度只计四颗巨行星,与通用链差到数 mas。 + if arcsec > 0.005 || math.Abs(gd-wd)/wd > 1e-9 { + t.Errorf("jd %.1f body %d: direction %.5f\" dist %.3f km", jd, b, arcsec, gd-wd) + } + } + } + t.Logf("worst %.6f\"", worst) +} diff --git a/planet/internal/semianalytic/apparent_moon_test.go b/planet/internal/semianalytic/apparent_moon_test.go new file mode 100644 index 0000000..92025c5 --- /dev/null +++ b/planet/internal/semianalytic/apparent_moon_test.go @@ -0,0 +1,76 @@ +package semianalytic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +// 视月球方向在远历元也必须是光滑函数:下游中心带牛顿迭代的残差容差约 2e-10, +// 求值噪声需留出一个量级。二阶差分 RMS 按月地距折算成 km,h=1e-7 日时真实曲率项可忽略。 +func TestApparentMoonSmoothAtDistantEpochs(t *testing.T) { + const h, n, limitKM = 1e-7, 200, 6e-5 + for _, jd0 := range []float64{2451545.0, 2460680.5, 2288000.5, 2810139.6097} { + var prev [2][3]float64 + var sum float64 + for i := 0; i < n; i++ { + d, _, _, err := Apparent(ephem.Moon, jd0+float64(i)*h) + if err != nil { + t.Fatal(err) + } + p := [3]float64{d[0] * 3.844e5, d[1] * 3.844e5, d[2] * 3.844e5} + if i >= 2 { + for k := range p { + dd := p[k] - 2*prev[1][k] + prev[0][k] + sum += dd * dd + } + } + prev[0], prev[1] = prev[1], p + } + if rms := math.Sqrt(sum / (n - 2)); rms > limitKM { + t.Errorf("jd %.1f: apparent Moon second-difference rms %.2e km, want <= %.0e", jd0, rms, limitKM) + } + } +} + +// 地心化的光行时链与"两次日心相减"的旧链在数学上等价,只差 EMB 的二阶外推项(< 0.01 mas)。 +// 旧链自身带有 jd−τ 的舍入(~1e-10 日)乘地月质心速度(2.6e6 km/日)的误差,最大约 0.35 mas, +// 因此容差取 0.5 mas;两者差值反映的是旧链误差,不是新链误差。 +func TestApparentMoonMatchesHeliocentricChain(t *testing.T) { + for _, jd := range []float64{2451545.0, 2460409.25, 2433282.5, 2810139.6097, 2086000.5} { + got, gd, gt, err := Apparent(ephem.Moon, jd) + if err != nil { + t.Fatal(err) + } + o, err := observerAt(jd) + if err != nil { + t.Fatal(err) + } + tau := gt + r, err := targetHelio(ephem.Moon, jd-tau, false) + if err != nil { + t.Fatal(err) + } + var g [3]float64 + for i := range g { + g[i] = r[i] - o.p[i] - o.sunVel[i]*tau + } + want := toICRF(aberrate(vscale(g, 1/vlen(g)), o.v)) + cx := got[1]*want[2] - got[2]*want[1] + cy := got[2]*want[0] - got[0]*want[2] + cz := got[0]*want[1] - got[1]*want[0] + c := got[0]*want[0] + got[1]*want[1] + got[2]*want[2] + mas := math.Atan2(math.Sqrt(cx*cx+cy*cy+cz*cz), c) * 206264806.2 + if mas > 0.5 { + t.Errorf("jd %.1f: geocentric chain differs by %.4f mas", jd, mas) + } + now, err := targetHelio(ephem.Moon, jd, true) + if err != nil { + t.Fatal(err) + } + if wd := vlen(vsub(now, o.p)); math.Abs(gd-wd) > 1 { + t.Errorf("jd %.1f: geometric distance %.3f km, heliocentric chain %.3f km", jd, gd, wd) + } + } +} diff --git a/planet/internal/semianalytic/guard_test.go b/planet/internal/semianalytic/guard_test.go new file mode 100644 index 0000000..2a7f90d --- /dev/null +++ b/planet/internal/semianalytic/guard_test.go @@ -0,0 +1,45 @@ +// 被测契约:非法 Body 与非有限历元必须报错而不是 panic 或静默返回 NaN;缓存槽不得跨天体串用。 +package semianalytic + +import ( + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +func TestApparentRejectsUnsupportedBody(t *testing.T) { + for _, body := range []ephem.Body{ephem.SSB, ephem.EMB, ephem.Earth, ephem.Body(-1), ephem.Body(2000001)} { + if _, _, _, err := Apparent(body, 2451545); err != ephem.ErrUnsupportedBody { + t.Errorf("Apparent(%d) err = %v, want %v", body, err, ephem.ErrUnsupportedBody) + } + } +} + +func TestApparentCacheSlotsAreDistinct(t *testing.T) { + const jd = 2451545.0 + sunDir, sunDist, _, err := Apparent(ephem.Sun, jd) + if err != nil { + t.Fatalf("Apparent(Sun) err = %v", err) + } + for _, body := range []ephem.Body{ephem.Mercury, ephem.Mars, ephem.Neptune, ephem.Moon} { + dir, dist, _, err := Apparent(body, jd) + if err != nil { + t.Fatalf("Apparent(%d) err = %v", body, err) + } + if dir == sunDir && dist == sunDist { + t.Errorf("Apparent(%d) returned the Sun entry", body) + } + } +} + +func TestNonFiniteEpochIsOutOfRange(t *testing.T) { + for _, jd := range []float64{math.NaN(), math.Inf(1), math.Inf(-1)} { + if _, _, err := (Ephemeris{}).State(ephem.Mars, ephem.Sun, jd); err != ephem.ErrOutOfRange { + t.Errorf("State(%v) err = %v, want %v", jd, err, ephem.ErrOutOfRange) + } + if _, _, _, err := (Ephemeris{}).Apparent(ephem.Mars, jd); err != ephem.ErrOutOfRange { + t.Errorf("Apparent(%v) err = %v, want %v", jd, err, ephem.ErrOutOfRange) + } + } +} diff --git a/planet/internal/semianalytic/lunar/default.go b/planet/internal/semianalytic/lunar/default.go new file mode 100644 index 0000000..95791e2 --- /dev/null +++ b/planet/internal/semianalytic/lunar/default.go @@ -0,0 +1,46 @@ +package lunar + +import ( + _ "embed" + "sync" +) + +//go:embed lunar.bin +var defaultTable []byte + +var ( + defaultOnce sync.Once + defaultModel *Model +) + +// Default 内置表,首次调用时解析 / the built-in table, parsed on first use. +func Default() *Model { + defaultOnce.Do(func() { + m, err := Parse(defaultTable) + if err != nil { + panic(err) + } + defaultModel = m + }) + return defaultModel +} + +//go:embed fast.bin +var fastTable []byte + +var ( + fastOnce sync.Once + fastModel *Model +) + +// Fast 快速档表(n ≥ 0 用),首次调用时解析 / the fast-tier table used for n ≥ 0, parsed on first use. +func Fast() *Model { + fastOnce.Do(func() { + m, err := Parse(fastTable) + if err != nil { + panic(err) + } + fastModel = m + }) + return fastModel +} diff --git a/planet/internal/semianalytic/lunar/fast.bin b/planet/internal/semianalytic/lunar/fast.bin new file mode 100644 index 0000000000000000000000000000000000000000..981815510a2c6a5eaca9c0a167a61cad12abc610 GIT binary patch literal 4653 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 literal 0 HcmV?d00001 diff --git a/planet/internal/semianalytic/lunar/lunar.go b/planet/internal/semianalytic/lunar/lunar.go new file mode 100644 index 0000000..5374ec8 --- /dev/null +++ b/planet/internal/semianalytic/lunar/lunar.go @@ -0,0 +1,513 @@ +// Package lunar 月球半解析级数运行时(地心,J2000 平黄道)。 +package lunar + +import ( + "encoding/binary" + "errors" + "math" + "sync" +) + +const ( + j2000 = 2451545.0 + century = 36525.0 + arcsec = math.Pi / 648000.0 + fundCount = 13 + maxEnvelope = 8 +) + +type coordinate struct { + args []uint16 + nodes []int32 + // 包络阶数为 0 的项单独成段:flat0 为 [cos,sin] 交错 + nodes0 []int32 + flat0 []float32 + // 其余项(阶数 ≥1) + nodesH []int32 + envH []uint8 + coefH []float32 + env []uint8 + secular []float64 + coef []float32 +} + +// Model 已解析的月球级数表 / parsed lunar series table. +type Model struct { + s2 [5][]float64 + args [fundCount][5]float64 + lonPoly [5]float64 + p, q [5]float64 + maxMult [fundCount]int + powOff [fundCount]int + coords [3]coordinate + maxEnv int + // 前缀树:节点 i 的复相位 = 节点 parent[i] × pow[factor[i]],parent 恒小于 i(-1 为根) + nodeParent []int32 + nodeFactor []int32 + argNode []int32 + // pow 槽位 → (基本幅角, 乘数),用于速度计算中的相位变化率 + slotFund []int8 + slotMult []float64 + powLen int + legLen int + // Parse 之后 Model 只读;每次求值的缓冲从池中取,可并发调用 + pool sync.Pool +} + +// scratch 单次求值的工作区。 +type scratch struct { + pow []complex128 + node []complex128 + nodeRate []float64 + leg []float64 + dleg []float64 + env [maxEnvelope + 1]float64 + denv [maxEnvelope + 1]float64 +} + +func (m *Model) getScratch() *scratch { + if s, ok := m.pool.Get().(*scratch); ok { + return s + } + return &scratch{ + pow: make([]complex128, m.powLen), + node: make([]complex128, len(m.nodeParent)), + nodeRate: make([]float64, len(m.nodeParent)), + leg: make([]float64, m.legLen), + dleg: make([]float64, m.legLen), + } +} + +type reader struct { + b []byte + off int + err error +} + +func (r *reader) take(n int) []byte { + if r.err != nil || r.off+n > len(r.b) { + r.err = errors.New("lunar: truncated table") + return make([]byte, n) + } + s := r.b[r.off : r.off+n] + r.off += n + return s +} + +func (r *reader) u8() uint8 { return r.take(1)[0] } +func (r *reader) u16() uint16 { return binary.LittleEndian.Uint16(r.take(2)) } +func (r *reader) f64() float64 { return math.Float64frombits(binary.LittleEndian.Uint64(r.take(8))) } +func (r *reader) f32() float32 { return math.Float32frombits(binary.LittleEndian.Uint32(r.take(4))) } + +// Parse 解析 LM7 v2 二进制表 / parse an LM7 v2 table. +func Parse(b []byte) (*Model, error) { + r := &reader{b: b} + if string(r.take(4)) != "LM7\x02" { + return nil, errors.New("lunar: bad magic") + } + m := &Model{} + nargs := int(r.u16()) + s2n := int(r.u16()) + secDeg := int(r.u8()) + for j := 0; j < 5; j++ { + m.s2[j] = make([]float64, s2n) + for k := range m.s2[j] { + m.s2[j][k] = r.f64() + } + } + for i := 0; i < fundCount; i++ { + for k := 0; k < 5; k++ { + m.args[i][k] = r.f64() + } + } + for k := range m.lonPoly { + m.lonPoly[k] = r.f64() + } + for k := range m.p { + m.p[k] = r.f64() + } + for k := range m.q { + m.q[k] = r.f64() + } + r.f64() // 距离比例已折入拟合系数 + mults := make([][fundCount]int8, nargs) + for a := range mults { + mask := r.u16() + for i := 0; i < 16; i++ { + if mask&(1<= fundCount { + return nil, errors.New("lunar: multiplier index out of range") + } + k := int8(r.u8()) + mults[a][i] = k + if v := int(math.Abs(float64(k))); v > m.maxMult[i] { + m.maxMult[i] = v + } + } + } + m.buildTrie(mults) + for c := 0; c < 3; c++ { + n := int(r.u16()) + co := &m.coords[c] + co.args = make([]uint16, n) + for j := range co.args { + co.args[j] = r.u16() + if int(co.args[j]) >= nargs { + return nil, errors.New("lunar: argument index out of range") + } + } + co.env = r.take(n) + ncoef := 0 + for _, e := range co.env { + if int(e) > maxEnvelope { + return nil, errors.New("lunar: envelope degree too high") + } + if int(e) > m.maxEnv { + m.maxEnv = int(e) + } + ncoef += 2 * (int(e) + 1) + } + co.secular = make([]float64, secDeg+1) + for k := range co.secular { + co.secular[k] = r.f64() + } + raw := make([]float32, ncoef) + for k := range raw { + raw[k] = r.f32() + } + co.coef = interleave(co.env, raw) + } + if r.err != nil { + return nil, r.err + } + if r.off != len(b) { + return nil, errors.New("lunar: trailing bytes") + } + for c := range m.coords { + co := &m.coords[c] + co.nodes = make([]int32, len(co.args)) + pos := 0 + for j, a := range co.args { + co.nodes[j] = m.argNode[a] + e := int(co.env[j]) + if e == 0 { + co.nodes0 = append(co.nodes0, co.nodes[j]) + co.flat0 = append(co.flat0, co.coef[pos], co.coef[pos+1]) + } else { + co.nodesH = append(co.nodesH, co.nodes[j]) + co.envH = append(co.envH, co.env[j]) + co.coefH = append(co.coefH, co.coef[pos:pos+2*(e+1)]...) + } + pos += 2 * (e + 1) + } + } + m.legLen = max3(s2n, secDeg+1, m.maxEnv+1) + return m, nil +} + +func (m *Model) buildTrie(mults [][fundCount]int8) { + total := 0 + for i := 0; i < fundCount; i++ { + m.powOff[i] = total + m.maxMult[i] + total += 2*m.maxMult[i] + 1 + } + m.powLen = total + m.slotFund = make([]int8, total) + m.slotMult = make([]float64, total) + for i := 0; i < fundCount; i++ { + for k := -m.maxMult[i]; k <= m.maxMult[i]; k++ { + m.slotFund[m.powOff[i]+k] = int8(i) + m.slotMult[m.powOff[i]+k] = float64(k) + } + } + type edge struct{ parent, factor int32 } + index := map[edge]int32{} + m.argNode = make([]int32, len(mults)) + for a, mv := range mults { + cur := int32(-1) + for i := 0; i < fundCount; i++ { + if mv[i] == 0 { + continue + } + e := edge{cur, int32(m.powOff[i] + int(mv[i]))} + n, ok := index[e] + if !ok { + n = int32(len(m.nodeParent)) + index[e] = n + m.nodeParent = append(m.nodeParent, e.parent) + m.nodeFactor = append(m.nodeFactor, e.factor) + } + cur = n + } + m.argNode[a] = cur + } +} + +// cmul 绕开内建复数乘法的 Inf/NaN 分支。 +func cmul(a, b complex128) complex128 { + ar, ai, br, bi := real(a), imag(a), real(b), imag(b) + return complex(ar*br-ai*bi, ar*bi+ai*br) +} + +func max3(a, b, c int) int { + if b > a { + a = b + } + if c > a { + a = c + } + return a +} + +func poly(c *[5]float64, t float64) float64 { + return c[0] + t*(c[1]+t*(c[2]+t*(c[3]+t*c[4]))) +} + +// phase 与 poly 同值,但线性项单独做补偿归约:c1·t 在 ±1 万年达 1e6 rad,直接相加会丢掉约 1e-10 rad。 +func phase(c *[5]float64, t, tLow float64) float64 { + const twoPiLow = 2.4492935982947064e-16 + product := c[1] * t + roundoff := math.FMA(c[1], t, -product) + c[1]*tLow + turns := math.Round(product / (2 * math.Pi)) + lin := math.FMA(-turns, 2*math.Pi, product) + (roundoff - turns*twoPiLow) + return lin + (c[0] + t*t*(c[2]+t*(c[3]+t*c[4]))) +} + +// dpoly 返回 poly 的一阶导数。 +func dpoly(c *[5]float64, t float64) float64 { + return c[1] + t*(2*c[2]+t*(3*c[3]+t*4*c[4])) +} + +// legendreD 写入 P0..P(n-1)(x);withD 时同时写入导数 P'k(x) 到 d。 +func legendreD(x float64, out, d []float64, withD bool) { + out[0] = 1 + if len(out) > 1 { + out[1] = x + } + for k := 2; k < len(out); k++ { + out[k] = ((2*float64(k)-1)*x*out[k-1] - (float64(k)-1)*out[k-2]) / float64(k) + } + if !withD { + return + } + d[0] = 0 + if len(d) > 1 { + d[1] = 1 + } + // P'k = P'(k-2) + (2k-1)·P(k-1) + for k := 2; k < len(d); k++ { + d[k] = d[k-2] + (2*float64(k)-1)*out[k-1] + } +} + +func dot(c []float64, l []float64) float64 { + s := 0.0 + for k, v := range c { + s += v * l[k] + } + return s +} + +// PositionJ2000 地心月球 J2000 平黄道几何位置(km)/ geocentric Moon, J2000 mean ecliptic, km. +// 可并发调用。 +func (m *Model) PositionJ2000(jdTT float64) [3]float64 { + p, _ := m.eval(jdTT, false) + return p +} + +// PositionVelocityJ2000 位置(km)与解析速度(km/日,对 TT)/ position (km) and analytic velocity (km/day). +// 可并发调用。 +func (m *Model) PositionVelocityJ2000(jdTT float64) (pos, vel [3]float64) { + return m.eval(jdTT, true) +} + +// eval 所有量先对 t(儒略世纪)求导,最后换算到每日。 +func (m *Model) eval(jdTT float64, wantVel bool) (pos, vel [3]float64) { + s := m.getScratch() + defer m.pool.Put(s) + days := jdTT - j2000 + origin := days - jdTT + daysLow := (jdTT - (days - origin)) - (j2000 + origin) + t := days / century + // tLow 保留历元相减与世纪除法的舍入,远离 J2000 时线性相位才不丢精度。 + tLow := (math.FMA(-t, century, days) + daysLow) / century + x := (t + 20) / 100 + const dxdt = 0.01 + leg, dleg := s.leg, s.dleg + // 递推前缀稳定,一次算到最长,S2 修正与各坐标长期项共用。 + legendreD(x, leg[:m.legLen], dleg[:m.legLen], wantVel) + var dc, ddc [5]float64 + for j := range dc { + dc[j] = dot(m.s2[j], leg) + if wantVel { + ddc[j] = dot(m.s2[j], dleg) * dxdt + } + } + // ζ 随 W1 修正,D/F/l/l′ 各自修正 + var corr, dcorr, rate [fundCount]float64 + corr[0], corr[1], corr[2], corr[3], corr[12] = dc[1], dc[2], dc[3], dc[4], dc[0] + dcorr[0], dcorr[1], dcorr[2], dcorr[3], dcorr[12] = ddc[1], ddc[2], ddc[3], ddc[4], ddc[0] + for i := 0; i < fundCount; i++ { + k := m.maxMult[i] + if k == 0 { + continue + } + rate[i] = dpoly(&m.args[i], t) + dcorr[i] + sn, cs := math.Sincos(phase(&m.args[i], t, tLow) + corr[i]) + e := complex(cs, sn) + p := s.pow[m.powOff[i]-k : m.powOff[i]+k+1] + p[k] = 1 + for n := 1; n <= k; n++ { + p[k+n] = cmul(p[k+n-1], e) + p[k-n] = complex(real(p[k+n]), -imag(p[k+n])) + } + } + // 热循环按 wantVel 拆开,纯位置路径不写 nodeRate + node, factor := s.node, m.nodeFactor + if wantVel { + nr := s.nodeRate + for n, parent := range m.nodeParent { + fac := factor[n] + f := s.pow[fac] + r := m.slotMult[fac] * rate[m.slotFund[fac]] + if parent >= 0 { + f = cmul(node[parent], f) + r += nr[parent] + } + node[n] = f + nr[n] = r + } + } else { + for n, parent := range m.nodeParent { + f := s.pow[factor[n]] + if parent >= 0 { + f = cmul(node[parent], f) + } + node[n] = f + } + } + env, denv := s.env[:m.maxEnv+1], s.denv[:m.maxEnv+1] + legendreD(x, env, denv, wantVel) + var v, dv [3]float64 + for c := 0; c < 3; c++ { + co := &m.coords[c] + sum := dot(co.secular, leg) + dsum := 0.0 + if wantVel { + dsum = dot(co.secular, dleg) * dxdt + } + f0 := co.flat0 + if wantVel { + nr := s.nodeRate + for j, n := range co.nodes0 { + z := node[n] + a, b := float64(f0[2*j]), float64(f0[2*j+1]) + sum += a*real(z) + b*imag(z) + dsum += nr[n] * (b*real(z) - a*imag(z)) + } + pos := 0 + for j, e := range co.envH { + n := co.nodesH[j] + z := node[n] + var ac, as, dac, das float64 + for d := 0; d <= int(e); d++ { + cc, cn := float64(co.coefH[pos]), float64(co.coefH[pos+1]) + ac += env[d] * cc + as += env[d] * cn + dac += denv[d] * cc + das += denv[d] * cn + pos += 2 + } + sum += ac*real(z) + as*imag(z) + dsum += dxdt*(dac*real(z)+das*imag(z)) + nr[n]*(as*real(z)-ac*imag(z)) + } + } else { + for j, n := range co.nodes0 { + z := node[n] + sum += float64(f0[2*j])*real(z) + float64(f0[2*j+1])*imag(z) + } + pos := 0 + for j, e := range co.envH { + z := node[co.nodesH[j]] + var ac, as float64 + for d := 0; d <= int(e); d++ { + ac += env[d] * float64(co.coefH[pos]) + as += env[d] * float64(co.coefH[pos+1]) + pos += 2 + } + sum += ac*real(z) + as*imag(z) + } + } + v[c], dv[c] = sum, dsum + } + lon := v[0]*arcsec + phase(&m.lonPoly, t, tLow) + dc[0] + lat := v[1] * arcsec + rad := v[2] + sl, cl := math.Sincos(lon) + sb, cb := math.Sincos(lat) + x0, y0, z0 := rad*cb*cl, rad*cb*sl, rad*sb + p := poly(&m.p, t) * t + q := poly(&m.q, t) * t + ra := 2 * math.Sqrt(1-p*p-q*q) + pos = [3]float64{ + (1-2*p*p)*x0 + 2*p*q*y0 + p*ra*z0, + 2*p*q*x0 + (1-2*q*q)*y0 - q*ra*z0, + -p*ra*x0 + q*ra*y0 + (1-2*p*p-2*q*q)*z0, + } + if !wantVel { + return pos, vel + } + dlon := dv[0]*arcsec + dpoly(&m.lonPoly, t) + ddc[0] + dlat := dv[1] * arcsec + drad := dv[2] + // 球坐标 → 直角坐标的时间导数 + dx0 := drad*cb*cl - rad*sb*cl*dlat - rad*cb*sl*dlon + dy0 := drad*cb*sl - rad*sb*sl*dlat + rad*cb*cl*dlon + dz0 := drad*sb + rad*cb*dlat + // 历元黄道 → J2000 旋转矩阵本身随 P、Q 变化 + dp := dpoly(&m.p, t)*t + poly(&m.p, t) + dq := dpoly(&m.q, t)*t + poly(&m.q, t) + dra := -4 * (p*dp + q*dq) / ra + dpq := 2 * (dp*q + p*dq) + vel = [3]float64{ + (1-2*p*p)*dx0 + 2*p*q*dy0 + p*ra*dz0 + + -4*p*dp*x0 + dpq*y0 + (dp*ra+p*dra)*z0, + 2*p*q*dx0 + (1-2*q*q)*dy0 - q*ra*dz0 + + dpq*x0 - 4*q*dq*y0 - (dq*ra+q*dra)*z0, + -p*ra*dx0 + q*ra*dy0 + (1-2*p*p-2*q*q)*dz0 + + -(dp*ra+p*dra)*x0 + (dq*ra+q*dra)*y0 - 4*(p*dp+q*dq)*z0, + } + for i := range vel { + vel[i] /= century + } + return pos, vel +} + +// interleave 把按阶数分块(每阶先 cos 块后 sin 块)的系数重排为逐项 [d0cos,d0sin,d1cos,…]。 +func interleave(env []uint8, raw []float32) []float32 { + out := make([]float32, len(raw)) + start := make([]int, len(env)) + for j := 1; j < len(env); j++ { + start[j] = start[j-1] + 2*(int(env[j-1])+1) + } + pos := 0 + for d := 0; ; d++ { + var idx []int + for j, e := range env { + if int(e) >= d { + idx = append(idx, j) + } + } + if len(idx) == 0 { + break + } + for n, j := range idx { + out[start[j]+2*d] = raw[pos+n] + out[start[j]+2*d+1] = raw[pos+len(idx)+n] + } + pos += 2 * len(idx) + } + return out +} diff --git a/planet/internal/semianalytic/lunar/lunar_test.go b/planet/internal/semianalytic/lunar/lunar_test.go new file mode 100644 index 0000000..6d6510f --- /dev/null +++ b/planet/internal/semianalytic/lunar/lunar_test.go @@ -0,0 +1,115 @@ +// 被测契约:内置表与参考向量一致(< 0.1 m),解析速度与差分一致,可并发求值。 +package lunar + +import ( + "bufio" + "math" + "os" + "strconv" + "strings" + "sync" + "testing" +) + +func sq(x float64) float64 { return x * x } + +func TestMatchesReferenceVectors(t *testing.T) { checkVectors(t, Default(), "testdata/vectors.csv") } + +func TestFastMatchesReferenceVectors(t *testing.T) { checkVectors(t, Fast(), "testdata/fast.csv") } + +func checkVectors(t *testing.T, m *Model, path string) { + f, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + defer f.Close() + sc := bufio.NewScanner(f) + n, worst := 0, 0.0 + for sc.Scan() { + parts := strings.Split(sc.Text(), ",") + var v [4]float64 + for i := range v { + if v[i], err = strconv.ParseFloat(parts[i], 64); err != nil { + t.Fatal(err) + } + } + got := m.PositionJ2000(v[0]) + if d := math.Sqrt(sq(got[0]-v[1]) + sq(got[1]-v[2]) + sq(got[2]-v[3])); d > worst { + worst = d + } + n++ + } + if n < 40 { + t.Fatalf("only %d vectors", n) + } + if worst > 1e-4 { + t.Fatalf("max diff %.3e km over %d vectors", worst, n) + } +} + +func TestVelocityMatchesFiniteDifference(t *testing.T) { + m := Default() + const h = 0.05 + worst := 0.0 + for _, jd := range []float64{990545.5, 1500000.25, 2299160.5, 2451545.0, 2460676.5, 2816787.5, 3912545.0} { + p, v := m.PositionVelocityJ2000(jd) + if q := m.PositionJ2000(jd); q != p { + t.Fatalf("jd=%.1f position mismatch", jd) + } + a, b := m.PositionJ2000(jd+h), m.PositionJ2000(jd-h) + a2, b2 := m.PositionJ2000(jd+2*h), m.PositionJ2000(jd-2*h) + for i := range v { + fd := (8*(a[i]-b[i]) - (a2[i] - b2[i])) / (12 * h) + if d := math.Abs(fd - v[i]); d > worst { + worst = d + } + } + } + if worst > 2e-3 { + t.Fatalf("max velocity diff %.3e km/day", worst) + } +} + +func TestParseRejectsTruncated(t *testing.T) { + for _, n := range []int{0, 3, 5, 40, len(defaultTable) / 2, len(defaultTable) - 1} { + if _, err := Parse(defaultTable[:n]); err == nil { + t.Errorf("len %d: expected error", n) + } + } +} + +func TestConcurrentEvaluation(t *testing.T) { + m := Default() + const n = 64 + var want [n][3]float64 + for i := range want { + want[i] = m.PositionJ2000(2451545.0 + float64(i)*13.7) + } + var wg sync.WaitGroup + var bad [8]bool + for g := 0; g < 8; g++ { + wg.Add(1) + go func(g int) { + defer wg.Done() + for k := 0; k < n; k++ { + i := (k + g*7) % n + if m.PositionJ2000(2451545.0+float64(i)*13.7) != want[i] { + bad[g] = true + } + } + }(g) + } + wg.Wait() + for g, b := range bad { + if b { + t.Fatalf("goroutine %d saw a mismatch", g) + } + } +} + +func BenchmarkPositionJ2000(b *testing.B) { + m := Default() + for i := 0; i < b.N; i++ { + _ = m.PositionJ2000(2460000.5 + float64(i%10000)*0.37) + } +} diff --git a/planet/internal/semianalytic/lunar/testdata/fast.csv b/planet/internal/semianalytic/lunar/testdata/fast.csv new file mode 100644 index 0000000..6ae38ad --- /dev/null +++ b/planet/internal/semianalytic/lunar/testdata/fast.csv @@ -0,0 +1,51 @@ +-1931455,-263046.57528520969,297684.18718319025,-24345.864066160168 +-1785355,24378.534686367391,-400825.36392118747,-44672.568525917093 +-1639255,221175.99144970218,325206.96587989992,-14472.844556519216 +-1493155,-319978.52720147866,-179819.4818149425,-7729.6582925465245 +-1347055,362622.46972198499,20286.814075592443,4137.3575503146285 +-1200955,-346235.18691354321,185914.73322113504,30174.27456151383 +-1054855,171052.68131930399,-366289.65810158779,26232.886379638556 +-908755,98623.637690714822,376297.77810421737,40802.587272379009 +-762655,-253665.11515733672,-272838.23852057324,19911.166048921259 +-616555,335485.16883375292,146456.59956479297,14870.504114287725 +-470455,-383645.40573639271,65997.21790989369,-826.92611041679697 +-324355,276927.4702533714,-295829.19680496032,-30322.013752343068 +-178255,-26435.785915690412,386079.73800098576,-28015.326875332928 +-32155,-173562.98376470321,-327340.8539868113,-37468.408479997677 +113945,284098.82846766489,239256.46597111417,-25343.748850755001 +260045,-385157.83176014025,-49369.527013191553,-15281.761574469898 +406145,345160.97807879257,-214636.8383361497,3414.5771813550259 +552245,-134108.10483846199,361980.9990905014,29968.389469456466 +698345,-83984.239959958926,-355799.14247223095,28817.078112816904 +844445,223226.00566205085,299354.17357796693,36909.979945451472 +990545,-362607.45994305814,-147502.75304507115,28081.953043893249 +1136645,383606.60880971845,-132531.90493783797,8832.002235690501 +1282745,-216360.67463345666,321769.59784516064,-8957.0037364521304 +1428845,7433.8731166889338,-359577.9208704764,-27286.722728085369 +1574945,157042.40898349817,338967.54339773324,-30844.406074648126 +1721045,-328371.10510008538,-221529.93877308263,-36739.865212875557 +1867145,400234.08615918033,-57984.149689135251,-23513.254374541601 +2013245,-274021.09564645251,273929.27390877635,-2133.8352285152291 +2159345,92791.766991493409,-345234.67832922813,12342.071461675338 +2305445,88338.286030037401,363169.93015197461,26462.650120618968 +2451545,-291607.24183591013,-274978.62186731066,36267.30787189916 +2597645,401391.47803711233,3266.8554273079903,31443.354743021413 +2743745,-312245.28483573761,223268.80530781401,14449.195688692611 +2889845,167261.68852763134,-317547.01628260425,854.67113282198795 +3035945,22710.051466743793,377536.27831278293,-18254.778814458063 +3182045,-258568.79979545044,-310953.08002426871,-31511.794215090176 +3328145,391597.73715416581,50492.738155701489,-35663.250569429547 +3474245,-336640.78667349386,169381.01759883287,-24138.799220522917 +3620345,229894.62683911348,-283245.2596621954,-10124.478719948 +3766445,-30725.076385326527,386554.31412064168,4630.937622600225 +3912545,-232347.00044598628,-331529.30967142241,31692.736681196671 +4058645,373153.27734119527,88709.654120304229,33189.966274715975 +4204745,-352665.5780016224,110675.94346091514,29168.298060724752 +4350845,279862.12418001605,-250761.84026383076,22491.093918623097 +4496945,-63300.801294757643,393827.49900787073,-4549.380602734378 +4643045,-211639.87281130059,-339806.65624159103,-18216.154066961921 +4789145,345869.06789046078,125142.00856319141,-35847.90651925862 +4935245,-365917.02341126435,47072.64876266574,-31135.795917215175 +5081345,313235.01489341311,-233557.38203767358,-25005.730334995031 +5227445,-71563.036314018507,398823.35837659024,-9633.2749911986139 +5373545,-186164.91514019907,-338423.50248531549,24637.566759055353 diff --git a/planet/internal/semianalytic/lunar/testdata/vectors.csv b/planet/internal/semianalytic/lunar/testdata/vectors.csv new file mode 100644 index 0000000..a0fe65f --- /dev/null +++ b/planet/internal/semianalytic/lunar/testdata/vectors.csv @@ -0,0 +1,51 @@ +-1931455,-263012.14372776932,297721.55430688168,-24324.987254599062 +-1785355,24414.613429926769,-400822.30277713871,-44670.197726442522 +-1639255,221200.37674641001,325184.92725227826,-14474.943560636351 +-1493155,-319973.35559277143,-179836.67922932078,-7722.0931910639329 +-1347055,362620.6275818554,20261.340424064758,4158.7475902214901 +-1200955,-346245.11238489667,185898.25815803016,30163.905893696552 +-1054855,171029.90424524632,-366296.60906562442,26231.502494924491 +-908755,98585.018472548079,376301.83738717507,40804.120474682692 +-762655,-253673.35964539216,-272828.69776402361,19905.706493336733 +-616555,335481.25258198346,146459.68939852307,14858.106694331284 +-470455,-383647.05975822004,66013.008896738858,-829.53397079068804 +-324355,276926.62049089983,-295829.60577838111,-30320.482347503283 +-178255,-26445.54739251201,386080.83489992673,-28013.881581822534 +-32155,-173564.14468521808,-327344.00872558804,-37468.46077107366 +113945,284090.35252503335,239265.58618684916,-25343.650846110519 +260045,-385159.51631046517,-49348.816915762458,-15277.391684530896 +406145,345165.46504725312,-214639.96530993938,3409.4271382105871 +552245,-134100.72888107621,361985.48449367564,29978.850367708317 +698345,-83982.342926013938,-355811.46501246258,28808.215852912752 +844445,223235.08660139333,299351.08166803332,36915.189665281585 +990545,-362602.7882248871,-147511.51703076772,28082.07356267815 +1136645,383604.91929247783,-132547.95810837013,8838.4387429826493 +1282745,-216364.57090490102,321764.38059364131,-8961.5757619206961 +1428845,7411.8790805348717,-359585.32555476134,-27293.192427568905 +1574945,157039.98810843128,338969.64958912093,-30843.917066599242 +1721045,-328370.6008870838,-221528.04782516821,-36744.16639076647 +1867145,400234.16277750907,-57976.122899650822,-23521.520092170747 +2013245,-274016.4330496171,273934.67297238286,-2129.3720209763578 +2159345,92794.113822126208,-345233.42375816189,12336.461166121979 +2305445,88335.023380869083,363166.75449031434,26461.650624866372 +2451545,-291608.37627978966,-274979.69324404577,36271.128877822775 +2597645,401391.20135964605,3263.1459841862388,31451.825037924391 +2743745,-312251.31017224718,223260.77557797215,14444.457253400202 +2889845,167249.48724887962,-317556.36523747904,861.57897404768664 +3035945,22695.663788870559,377539.04920572782,-18246.340165569305 +3182045,-258575.29689052334,-310947.48117673211,-31511.022415741383 +3328145,391596.12233842851,50493.68541584225,-35666.970403817082 +3474245,-336640.9610806547,169391.52518719292,-24128.503619066782 +3620345,229900.52348247898,-283241.08259236073,-10118.936107852864 +3766445,-30703.555025148959,386554.4143357625,4624.3092540068592 +3912545,-232340.39127966101,-331532.73305540072,31692.97586258821 +4058645,373158.78561370028,88690.555206671619,33189.3675445954 +4204745,-352676.95412928553,110654.5265917302,29168.430885959289 +4350845,279850.74386479775,-250759.61582796593,22465.457479309545 +4496945,-63304.248176228684,393828.06630098209,-4545.7108401102751 +4643045,-211635.86742416851,-339798.65198398969,-18217.631416908665 +4789145,345881.5840039468,125117.11976231023,-35854.923632790691 +4935245,-365925.61155116902,47053.327116662622,-31140.397995070532 +5081345,313238.78055125696,-233551.4275320005,-25002.984985012321 +5227445,-71571.816487501972,398827.95987496921,-9638.4140099458273 +5373545,-186177.32558991387,-338413.93355647428,24655.523094058852 diff --git a/planet/internal/semianalytic/ofdate.go b/planet/internal/semianalytic/ofdate.go new file mode 100644 index 0000000..7a46230 --- /dev/null +++ b/planet/internal/semianalytic/ofdate.go @@ -0,0 +1,154 @@ +package semianalytic + +import ( + "math" + "sync" + + "b612.me/astro/internal/ltp" + "b612.me/astro/planet/internal/semianalytic/lunar" + "b612.me/astro/planet/internal/semianalytic/planets" + "b612.me/astro/tools" +) + +const ( + auKM = 149597870.7 + earthSlot = 0 + moonSlot = 8 + fastBase = 9 +) + +var sinEps0, cosEps0 = math.Sincos(eps0) + +// xt 1..7 对应的表内天体号。 +var bodyID = [8]int{0, planets.Mercury, planets.Venus, planets.Mars, planets.Jupiter, planets.Saturn, planets.Uranus, planets.Neptune} + +type lbr struct{ jd, l, b, r float64 } + +type lbrSlot struct { + v lbr + set bool +} + +// L、B、R 通常在同一时刻连续取用,每个天体每档缓存最近一次的结果;0..8 全表,9..17 快速档。 +var ( + cacheMu sync.RWMutex + cache [18]lbrSlot +) + +// tier n < 0 用全表,n ≥ 0 用快速档;新模型的快速档不随 n 细分。 +func tier(n int) (*planets.Model, *lunar.Model, int) { + if n < 0 { + return planets.Default(), lunar.Default(), 0 + } + return planets.Fast(), lunar.Fast(), fastBase +} + +func cached(slot int, jd float64) (lbr, bool) { + cacheMu.RLock() + c := cache[slot] + cacheMu.RUnlock() + if c.set && c.v.jd == jd { + return c.v, true + } + return lbr{}, false +} + +func store(slot int, jd float64, v [3]float64) lbr { + l, b, r := ofDate(v, jd) + c := lbr{jd, l, b, r} + cacheMu.Lock() + cache[slot] = lbrSlot{v: c, set: true} + cacheMu.Unlock() + return c +} + +type moonVec struct { + jd float64 + p [3]float64 + set bool +} + +// 地球合成与月球本身在同一时刻都要月球向量,按档缓存最近一次。 +var ( + moonVecMu sync.RWMutex + moonVecCache [2]moonVec +) + +func moonJ2000(lm *lunar.Model, slot int, jde float64) [3]float64 { + moonVecMu.RLock() + c := moonVecCache[slot] + moonVecMu.RUnlock() + if c.set && c.jd == jde { + return c.p + } + p := lm.PositionJ2000(jde) + moonVecMu.Lock() + moonVecCache[slot] = moonVec{jd: jde, p: p, set: true} + moonVecMu.Unlock() + return p +} + +// ofDate J2000 平黄道向量 → 历元平黄道经纬(弧度,经度在 [0, 2π))与模长。 +func ofDate(v [3]float64, jde float64) (l, b, r float64) { + q0, q1, q2 := v[0], v[1]*cosEps0-v[2]*sinEps0, v[1]*sinEps0+v[2]*cosEps0 + x, y, z := ltp.EclipticBasis(2000 + (jde-2451545)/365.25) + ex := x[0]*q0 + x[1]*q1 + x[2]*q2 + ey := y[0]*q0 + y[1]*q1 + y[2]*q2 + ez := z[0]*q0 + z[1]*q1 + z[2]*q2 + l = math.Atan2(ey, ex) + if l < 0 { + l += 2 * math.Pi + } + return l, math.Atan2(ez, math.Hypot(ex, ey)), math.Sqrt(ex*ex + ey*ey + ez*ez) +} + +// Planet 与 planet.WherePlanetN 同语义(xt 取 -1..7,zn 取 0..2),调用方负责越界检查 / same contract as planet.WherePlanetN. +func Planet(xt, zn int, jde float64, n int) float64 { + pm, lm, base := tier(n) + slot := base + earthSlot + if xt > 0 { + slot = base + xt + } + c, ok := cached(slot, jde) + if !ok { + var p [3]float64 + if xt <= 0 { + emb, _ := pm.Heliocentric(planets.EMB, jde) + p = planets.EarthFromEMB(emb, moonJ2000(lm, base/fastBase, jde)) + } else { + p, _ = pm.Heliocentric(bodyID[xt], jde) + } + c = store(slot, jde, p) + } + switch zn { + case 0: + l := c.l * 180 / math.Pi + if xt == 0 { + l += 180 + } + return tools.Limit360(l) + case 1: + if xt == 0 { + return -c.b * 180 / math.Pi + } + return c.b * 180 / math.Pi + } + return c.r / auKM +} + +// Moon 地心月球历元平黄道:coord 0 黄经、1 黄纬(弧度)、2 距离(km),n ≥ 0 用快速档,调用方负责越界检查 / geocentric Moon of date. +func Moon(coord int, jde float64, n int) float64 { + _, lm, base := tier(n) + slot := base + moonSlot + c, ok := cached(slot, jde) + if !ok { + c = store(slot, jde, moonJ2000(lm, base/fastBase, jde)) + } + switch coord { + case 0: + return c.l + case 1: + return c.b + } + return c.r +} diff --git a/planet/internal/semianalytic/planets/default.go b/planet/internal/semianalytic/planets/default.go new file mode 100644 index 0000000..bc7ffca --- /dev/null +++ b/planet/internal/semianalytic/planets/default.go @@ -0,0 +1,67 @@ +package planets + +import ( + _ "embed" + "sync" +) + +// 现代段为 J2000 ± 1000 儒略年。 +const ( + ModernStart = 2086295.0 + ModernEnd = 2816795.0 +) + +var ( + //go:embed long.bin + longTable []byte + //go:embed modern.bin + modernTable []byte +) + +var ( + defaultOnce sync.Once + defaultModel *Model +) + +// Default 内置长时段表叠加现代段,首次调用时解析 / built-in long-range plus modern tables, parsed on first use. +func Default() *Model { + defaultOnce.Do(func() { + m, err := Parse(longTable) + if err == nil { + err = m.WithModernSegment(modernTable, ModernStart, ModernEnd) + } + if err != nil { + panic(err) + } + defaultModel = m + }) + return defaultModel +} + +// GM 天体号 1..8 的 GM 与太阳 GM,au³/日²;EMB 为地月合计,越界返回 0 / GM of body id and of the Sun, au³/day². +func GM(id int) (body, sun float64) { + if id < 1 || id > 8 { + return 0, gmSol + } + return gmp[id-1], gmSol +} + +//go:embed fast.bin +var fastTable []byte + +var ( + fastOnce sync.Once + fastModel *Model +) + +// Fast 快速档表(n ≥ 0 用),首次调用时解析 / the fast-tier table used for n ≥ 0, parsed on first use. +func Fast() *Model { + fastOnce.Do(func() { + m, err := Parse(fastTable) + if err != nil { + panic(err) + } + fastModel = m + }) + return fastModel +} diff --git a/planet/internal/semianalytic/planets/fast.bin b/planet/internal/semianalytic/planets/fast.bin new file mode 100644 index 0000000000000000000000000000000000000000..27027c8b8f27b73322e6877a6bca585a379b80cb GIT binary patch literal 42150 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≻|_4kW;VBP4)O1Q3beH0tiI_ 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

yd#^z3WE)^Yw)AXGgCaWQN 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 z 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%0Dwj))R(Hs8$9PVkklzdLp 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 IfBlddfDiJQj{pDw literal 0 HcmV?d00001 diff --git a/planet/internal/semianalytic/planets/planets.go b/planet/internal/semianalytic/planets/planets.go new file mode 100644 index 0000000..9e74795 --- /dev/null +++ b/planet/internal/semianalytic/planets/planets.go @@ -0,0 +1,610 @@ +// Package planets 行星半解析级数运行时(PLN1/PLN2/PLN3 表,日心,J2000 平黄道)。 +package planets + +import ( + "encoding/binary" + "errors" + "math" + "sync" +) + +const ( + j2000 = 2451545.0 + millennium = 365250.0 + blendDays = 36525.0 + auKM = 149597870.691 + tMid = -2.0 + tHalf = 10.0 + emrat = 81.3005682214972 + gmSol = 2.9591220836841438269e-04 + maxDeg = 24 +) + +// Body 编号 / body id;3 为地月质心。 +const ( + Mercury = 1 + Venus = 2 + EMB = 3 + Mars = 4 + Jupiter = 5 + Saturn = 6 + Uranus = 7 + Neptune = 8 +) + +var ci0 = [17]float64{4.402608631669, 3.176134461576, 1.753470369433, 6.203500014141, 4.091360003050, + 1.713740719173, 5.598641292287, 2.805136360408, 2.326989734620, 0.5995461070350, + 0.8740185101070, 5.481225395663, 5.311897933164, 0.0, 5.19846640063, + 1.62790513602, 2.35555563875} + +var ci1 = [17]float64{26087.90314068555, 10213.28554743445, 6283.075850353215, 3340.612434145457, + 1731.170452721855, 1704.450855027201, 1428.948917844273, 1364.756513629990, + 1361.923207632842, 529.6909615623250, 213.2990861084880, 74.78165903077800, + 38.13297222612500, 0.3595362285049309, 77713.7714481804, 84334.6615717837, + 83286.9142477147} + +var gmp = [8]float64{4.9125474514508118699e-11, 7.2434524861627027000e-10, 8.9970116036316091182e-10, + 9.5495351057792580598e-11, 2.8253458420837780000e-07, 8.4597151856806587398e-08, + 1.2920249167819693900e-08, 1.5243589007842762800e-08} + +// topDMu 必须逐步按 float64 舍入(与 Python 参考求值一致),不能写成常量表达式。 +var topDMu = func() float64 { + f0, f1, d := 529.6909622785881, 213.2990811942489, 880.0 + return (f0 - f1) / d +}() + +// skyToVSOP skyfield ecliptic_J2000 → VSOP2013 动力学黄道;求值时用其转置。 +var skyToVSOP = func() [3][3]float64 { + as := 180 / math.Pi * 3600 + return mul(transpose(mul(rz(-0.05188/as), rx(84381.41136/as))), rx(84381.448/as)) +}() + +// element 的项按包络阶降序排列,第 d 阶只覆盖前 cnt[d] 项。 +type element struct { + secular []float64 + arg []int32 + cnt []int + c, s [][]float64 +} + +const ( + maxKeys = 8192 + maxTab = 512 + topBits = 6 + // 超出此值的幅角乘数必然撑破幂表,先在 int64 下拒绝,保证转 int 不溢出。 + maxKeyAbs = maxTab << (2 * topBits) +) + +var errBadKey = errors.New("planets: argument multiplier out of range") + +// factor 幂表槽位;neg 表示取共轭。 +type factor struct { + slot int32 + neg bool +} + +type body struct { + top bool + f, l0 float64 + rgm float64 + // 幂表:VSOP 每个基本幅角一段,TOP 每个 6 位数字层一段 + tabMax [17]int + tabOff [17]int + keyOff []int32 + factors []factor + keyNeg []bool + el [6]element + // legDeg 长期项与包络用到的最高 Legendre 阶。 + legDeg int +} + +// Model 已解析的 PLN1 表,只读、可并发求值 / parsed PLN1 table, safe for concurrent use. +type Model struct { + bodies [9]*body + modernBodies [9]*body + modernStart float64 + modernEnd float64 +} + +type reader struct { + b []byte + p int + err error +} + +func (r *reader) take(n int) []byte { + if r.err != nil || r.p+n > len(r.b) { + r.err = errors.New("planets: truncated table") + return make([]byte, n) + } + s := r.b[r.p : r.p+n] + r.p += n + return s +} + +func (r *reader) u8() uint8 { return r.take(1)[0] } +func (r *reader) f64() float64 { return math.Float64frombits(binary.LittleEndian.Uint64(r.take(8))) } +func (r *reader) f32() float32 { return math.Float32frombits(binary.LittleEndian.Uint32(r.take(4))) } + +func (r *reader) uvar() uint64 { + n, shift := uint64(0), uint(0) + for { + c := r.u8() + n |= uint64(c&0x7F) << shift + shift += 7 + if c < 0x80 || r.err != nil || shift > 56 { + return n + } + } +} + +func (r *reader) zvar() int64 { + z := r.uvar() + if z%2 == 0 { + return int64(z >> 1) + } + return -int64(z>>1) - 1 +} + +// Parse 解析 PLN1 表 / parse a PLN1 table. +func Parse(b []byte) (*Model, error) { + bodies, err := parseBodies(b) + if err != nil { + return nil, err + } + return &Model{bodies: bodies}, nil +} + +// WithModernSegment 为长时段表叠加现代段表,[jdStart, jdEnd] 内优先使用 / add a modern-era segment used inside [jdStart, jdEnd]. +func (m *Model) WithModernSegment(b []byte, jdStart, jdEnd float64) error { + bodies, err := parseBodies(b) + if err != nil { + return err + } + m.modernBodies, m.modernStart, m.modernEnd = bodies, jdStart, jdEnd + return nil +} + +func parseBodies(b []byte) (bodies [9]*body, err error) { + r := &reader{b: b} + magic := string(r.take(4)) + if magic != "PLN1" && magic != "PLN2" && magic != "PLN3" { + return bodies, errors.New("planets: bad magic") + } + quant, packed := magic == "PLN2", magic == "PLN3" + for n := int(r.u8()); n > 0 && r.err == nil; n-- { + id := int(r.u8()) + if id < 1 || id > 8 { + return bodies, errors.New("planets: bad body id") + } + bd := &body{top: r.u8() != 0} + secDeg := int(r.u8()) + bd.f, bd.l0 = r.f64(), r.f64() + bd.rgm = math.Sqrt(gmp[id-1] + gmSol) + nk := r.uvar() + if nk > maxKeys { + return bodies, errors.New("planets: too many arguments") + } + if err := readKeys(r, bd, int(nk)); err != nil { + return bodies, err + } + bd.legDeg = secDeg + sortedArgs := packed && r.u8() != 0 + for v := range bd.el { + var err error + if packed { + err = readElement3(r, &bd.el[v], secDeg, int(nk), sortedArgs) + } else { + err = readElement(r, &bd.el[v], secDeg, int(nk), quant) + } + if err != nil { + return bodies, err + } + if d := len(bd.el[v].cnt) - 1; d > bd.legDeg { + bd.legDeg = d + } + } + bodies[id] = bd + } + if r.err != nil { + return bodies, r.err + } + if r.p != len(b) { + return bodies, errors.New("planets: trailing bytes") + } + return bodies, nil +} + +// readKeys 把整数幅角拆成幂表槽位:VSOP 按基本幅角升序,TOP 按 6 位数字层由低到高。 +func readKeys(r *reader, bd *body, nk int) error { + type raw struct{ fund, k int } + keys := make([][]raw, nk) + bd.keyNeg = make([]bool, nk) + for j := 0; j < nk && r.err == nil; j++ { + if bd.top { + k := r.zvar() + if k > maxKeyAbs || k < -maxKeyAbs { + return errBadKey + } + a := int(k) + if a < 0 { + a, bd.keyNeg[j] = -a, true + } + for lvl := 0; lvl < 3; lvl++ { + d := a >> uint(topBits*lvl) + if lvl < 2 { + d &= 1<>uint(i)&1 != 0 { + k := r.zvar() + if k > maxKeyAbs || k < -maxKeyAbs { + return errBadKey + } + keys[j] = append(keys[j], raw{i, int(k)}) + } + } + } + if r.err != nil { + return r.err + } + for _, ks := range keys { + for _, f := range ks { + a := f.k + if a < 0 { + a = -a + } + if a > bd.tabMax[f.fund] { + bd.tabMax[f.fund] = a + } + } + } + n := 0 + for i := range bd.tabMax { + bd.tabOff[i] = n + if bd.tabMax[i] > 0 || (bd.top && i < 3) { + n += bd.tabMax[i] + 1 + } + } + if n > maxTab { + return errors.New("planets: power table too large") + } + bd.keyOff = make([]int32, nk+1) + for j, ks := range keys { + for _, f := range ks { + a, neg := f.k, false + if a < 0 { + a, neg = -a, true + } + if !bd.top && a == 0 { + continue + } + bd.factors = append(bd.factors, factor{int32(bd.tabOff[f.fund] + a), neg}) + } + bd.keyOff[j+1] = int32(len(bd.factors)) + } + return nil +} + +// readElement 把文件里按包络阶分块的列序重排为逐项连续;quant 为 PLN2 整数量化系数。 +func readElement(r *reader, e *element, secDeg, nk int, quant bool) error { + if secDeg > maxDeg { + return errors.New("planets: secular degree too high") + } + n64 := r.uvar() + if n64 > uint64(nk) { + return errors.New("planets: too many terms") + } + nint := int(n64) + e.secular = make([]float64, secDeg+1) + for i := range e.secular { + e.secular[i] = r.f64() + } + args, env := make([]int32, nint), make([]int, nint) + top := 0 + for j := 0; j < nint; j++ { + a := r.uvar() + if a >= uint64(nk) { + return errors.New("planets: argument index out of range") + } + args[j], env[j] = int32(a), int(r.u8()) + if env[j] > maxDeg { + return errors.New("planets: envelope degree too high") + } + if env[j] > top { + top = env[j] + } + } + // 按包络阶降序的稳定排序:pos[j] 为文件第 j 项的新位置 + pos := make([]int, nint) + e.arg = make([]int32, nint) + k := 0 + for d := top; d >= 0; d-- { + for j := 0; j < nint; j++ { + if env[j] == d { + pos[j], e.arg[k] = k, args[j] + k++ + } + } + } + coef := func() float64 { return float64(r.f32()) } + if quant { + step := r.f64() + coef = func() float64 { return float64(r.zvar()) * step } + } + if nint == 0 { + return r.err + } + e.cnt = make([]int, top+1) + e.c, e.s = make([][]float64, top+1), make([][]float64, top+1) + for d := 0; d <= top; d++ { + for j := 0; j < nint; j++ { + if env[j] >= d { + e.cnt[d]++ + } + } + e.c[d], e.s[d] = make([]float64, e.cnt[d]), make([]float64, e.cnt[d]) + for _, dst := range [][]float64{e.c[d], e.s[d]} { + for j := 0; j < nint; j++ { + if env[j] >= d { + dst[pos[j]] = coef() + } + } + } + } + return r.err +} + +// Heliocentric 日心位置,km,J2000 平黄道 / heliocentric position, km, mean ecliptic J2000. +func (m *Model) Heliocentric(id int, jdTDB float64) ([3]float64, error) { + pos, _, err := m.state(id, jdTDB, false) + return pos, err +} + +// HeliocentricState 日心位置(km)与密切速度(km/日),J2000 平黄道 / heliocentric position and osculating velocity. +func (m *Model) HeliocentricState(id int, jdTDB float64) (pos, vel [3]float64, err error) { + return m.state(id, jdTDB, true) +} + +// state wantVel 为 false 时 vel 不计算、恒为零。 +func (m *Model) state(id int, jdTDB float64, wantVel bool) (pos, vel [3]float64, err error) { + if id < 1 || id > 8 { + return pos, vel, errors.New("planets: body not in table") + } + bd := m.bodies[id] + if bd == nil { + return pos, vel, errors.New("planets: body not in table") + } + t := (jdTDB - j2000) / millennium + md := m.modernBodies[id] + sky := func(p, v [3]float64) ([3]float64, [3]float64, error) { + if !wantVel { + return toSky(p), v, nil + } + return toSky(p), toSky(v), nil + } + if md == nil || jdTDB <= m.modernStart || jdTDB >= m.modernEnd { + p, v := bd.ellxyz(bd.elements(t), wantVel) + return sky(p, v) + } + w, dw := m.modernWeight(jdTDB) + pm, vm := md.ellxyz(md.elements(t), wantVel) + if w == 1 { + return sky(pm, vm) + } + pl, vl := bd.ellxyz(bd.elements(t), wantVel) + for i := range pos { + pos[i] = pl[i] + w*(pm[i]-pl[i]) + if wantVel { + vel[i] = vl[i] + w*(vm[i]-vl[i]) + dw*(pm[i]-pl[i]) + } + } + return sky(pos, vel) +} + +// modernWeight 现代段权重及其对 JD 的导数;两端各 blendDays 内用 smoothstep 过渡,位置与速度均连续。 +func (m *Model) modernWeight(jd float64) (w, dw float64) { + u, sign := (jd-m.modernStart)/blendDays, 1.0 + if e := (m.modernEnd - jd) / blendDays; e < u { + u, sign = e, -1 + } + if u >= 1 { + return 1, 0 + } + return u * u * (3 - 2*u), sign * 6 * u * (1 - u) / blendDays +} + +// toSky 动力学黄道 au → skyfield ecliptic_J2000 km。 +func toSky(p [3]float64) (out [3]float64) { + for i := range out { + out[i] = (skyToVSOP[0][i]*p[0] + skyToVSOP[1][i]*p[1] + skyToVSOP[2][i]*p[2]) * auKM + } + return out +} + +// cmul 显式转换阻止编译器融合乘加,保证与 Python 参考求值逐位一致。 +func cmul(ar, ai, br, bi float64) (float64, float64) { + return float64(ar*br) - float64(ai*bi), float64(ar*bi) + float64(ai*br) +} + +// phasors 填充每个幅角的 (cos, sin)。 +// tc、ts 为倍角表工作区,只读取本次写入的槽位,复用时不必清零。 +func (bd *body) phasors(t float64, pc, ps, tc, ts []float64) { + for i := range bd.tabMax { + if bd.tabMax[i] == 0 && !(bd.top && i < 3) { + continue + } + var s, c float64 + if bd.top { + s, c = math.Sincos(topDMu * t * float64(int(1)< 0 { + r, im = tc[fs[0].slot], ts[fs[0].slot] + if fs[0].neg { + im = -im + } + fs = fs[1:] + } + for _, f := range fs { + br, bi := tc[f.slot], ts[f.slot] + if f.neg { + bi = -bi + } + r, im = cmul(r, im, br, bi) + } + if bd.keyNeg[j] { + im = -im + } + pc[j], ps[j] = r, im + } +} + +type scratch struct{ buf []float64 } + +var scratchPool = sync.Pool{New: func() interface{} { return new(scratch) }} + +// elements t 为 TDB 千年(J2000 起算)。 +func (bd *body) elements(t float64) [6]float64 { + x := (t - tMid) / tHalf + var leg [maxDeg + 1]float64 + leg[0], leg[1] = 1, x + for k := 2; k <= bd.legDeg; k++ { + leg[k] = (leg[k-1]*x*float64(2*k-1) - leg[k-2]*float64(k-1)) / float64(k) + } + nk := len(bd.keyNeg) + sc := scratchPool.Get().(*scratch) + defer scratchPool.Put(sc) + if cap(sc.buf) < 2*nk+2*maxTab { + sc.buf = make([]float64, 2*nk+2*maxTab) + } + // 各段在读取前都会被完整写入,复用时不必清零。 + buf := sc.buf[:2*nk+2*maxTab] + pc, ps := buf[:nk], buf[nk:2*nk] + bd.phasors(t, pc, ps, buf[2*nk:2*nk+maxTab], buf[2*nk+maxTab:]) + var el [6]float64 + for v := range bd.el { + e := &bd.el[v] + sum := 0.0 + for i, c := range e.secular { + sum += c * leg[i] + } + // 逐阶做点积再乘 Legendre 值,避免逐项累加包络系数的读改写。 + for d := range e.cnt { + cd, sd := e.c[d], e.s[d] + arg := e.arg[:len(cd)] + s0, s1 := 0.0, 0.0 + for i, a := range arg { + s0 += cd[i] * pc[a] + s1 += sd[i] * ps[a] + } + sum += leg[d] * (s0 + s1) + } + el[v] = sum + } + // 先归约平经度再加周期和,与 Python 参考求值一致。 + lm := math.Mod(bd.l0+bd.f*t, 2*math.Pi) + if lm < 0 { + lm += 2 * math.Pi + } + el[1] += lm + return el +} + +// EarthFromEMB 由地月质心与地心月球合成物理地心 / Earth centre from EMB and geocentric Moon. +func EarthFromEMB(emb, moon [3]float64) [3]float64 { + k := 1 / (1 + emrat) + return [3]float64{emb[0] - moon[0]*k, emb[1] - moon[1]*k, emb[2] - moon[2]*k} +} + +// ellxyz VSOP2013 椭圆元件 (a,l,k,h,q,p) → 动力学黄道位置(au)与速度(au/日)。 +func (bd *body) ellxyz(el [6]float64, wantVel bool) (pos, vel [3]float64) { + a, l, k, h, q, p := el[0], el[1], el[2], el[3], el[4], el[5] + fi := math.Sqrt(1 - k*k - h*h) + ki := math.Sqrt(1 - q*q - p*p) + u := 1 / (1 + fi) + ex := math.Hypot(k, h) + gl := math.Mod(l, 2*math.Pi) + if gl < 0 { + gl += 2 * math.Pi + } + gm := gl - math.Atan2(h, k) + e := gl + (ex-0.125*ex*ex*ex)*math.Sin(gm) + 0.5*ex*ex*math.Sin(2*gm) + 0.375*ex*ex*ex*math.Sin(3*gm) + var zs, zc float64 + for it := 0; it < 20; it++ { + zs, zc = math.Sincos(e) + z3r, z3i := k*zc+h*zs, k*zs-h*zc + dl := gl - e + z3i + e += dl / (1 - z3r) + if math.Abs(dl) < 1e-15 { + break + } + } + zs, zc = math.Sincos(e) + z3r, z3i := k*zc+h*zs, k*zs-h*zc + rsa := 1 - z3r + z1r, z1i := u*k*z3i, u*h*z3i + z2r, z2i := z1i, -z1r + xcw := (-k + zc + z2r) / rsa + xsw := (-h + zs + z2i) / rsa + xm := p*xcw - q*xsw + xr := a * rsa + pos = [3]float64{xr * (xcw - 2*p*xm), xr * (xsw + 2*q*xm), -2 * xr * ki * xm} + if !wantVel { + return pos, vel + } + xms := a * (h + xsw) / fi + xmc := a * (k + xcw) / fi + xn := bd.rgm / math.Pow(a, 1.5) + vel = [3]float64{xn * ((2*p*p-1)*xms + 2*p*q*xmc), xn * ((1-2*q*q)*xmc - 2*p*q*xms), 2 * xn * ki * (p*xms + q*xmc)} + return pos, vel +} + +func rx(a float64) [3][3]float64 { + s, c := math.Sincos(a) + return [3][3]float64{{1, 0, 0}, {0, c, -s}, {0, s, c}} +} + +func rz(a float64) [3][3]float64 { + s, c := math.Sincos(a) + return [3][3]float64{{c, -s, 0}, {s, c, 0}, {0, 0, 1}} +} + +func mul(a, b [3][3]float64) (out [3][3]float64) { + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + for k := 0; k < 3; k++ { + out[i][j] += a[i][k] * b[k][j] + } + } + } + return out +} + +func transpose(a [3][3]float64) (out [3][3]float64) { + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + out[i][j] = a[j][i] + } + } + return out +} diff --git a/planet/internal/semianalytic/planets/planets_test.go b/planet/internal/semianalytic/planets/planets_test.go new file mode 100644 index 0000000..1e75885 --- /dev/null +++ b/planet/internal/semianalytic/planets/planets_test.go @@ -0,0 +1,166 @@ +// 被测契约:内置表与参考向量一致(< 10 cm),现代段区间外回落长时段表,接缝连续,可并发求值。 +package planets + +import ( + "bufio" + "math" + "os" + "strconv" + "strings" + "sync" + "testing" +) + +func sq(x float64) float64 { return x * x } + +func longOnly(t *testing.T) *Model { + m, err := Parse(longTable) + if err != nil { + t.Fatal(err) + } + return m +} + +func checkVectors(t *testing.T, m *Model, path string) { + f, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + defer f.Close() + n := 0 + sc := bufio.NewScanner(f) + for sc.Scan() { + parts := strings.Split(sc.Text(), ",") + var v [8]float64 + for i := range v { + if v[i], err = strconv.ParseFloat(parts[i], 64); err != nil { + t.Fatal(err) + } + } + id := int(v[0]) + p, vel, err := m.HeliocentricState(id, v[1]) + if err != nil { + t.Fatal(err) + } + d := math.Sqrt(sq(p[0]-v[2]) + sq(p[1]-v[3]) + sq(p[2]-v[4])) + dv := math.Sqrt(sq(vel[0]-v[5]) + sq(vel[1]-v[6]) + sq(vel[2]-v[7])) + if !(d <= 1e-4 && dv <= 1e-6) { + t.Errorf("body %d jd %.1f: %.3e km, %.3e km/day", id, v[1], d, dv) + } + n++ + } + if n < 100 { + t.Fatalf("only %d vectors", n) + } +} + +func TestLongTableMatchesReference(t *testing.T) { + checkVectors(t, longOnly(t), "testdata/long.csv") +} + +func TestModernSegmentMatchesReference(t *testing.T) { + checkVectors(t, Default(), "testdata/modern.csv") +} + +func TestFastTableMatchesReference(t *testing.T) { + checkVectors(t, Fast(), "testdata/fast.csv") +} + +func TestFallsBackOutsideModern(t *testing.T) { + long, dual := longOnly(t), Default() + for _, jd := range []float64{ModernStart, ModernEnd, ModernStart - 1, ModernEnd + 1, 625000.5, 5000000.5} { + for id := 1; id <= 8; id++ { + a, _ := long.Heliocentric(id, jd) + b, _ := dual.Heliocentric(id, jd) + if a != b { + t.Errorf("body %d jd %.1f: did not fall back to long table", id, jd) + } + } + } +} + +func TestSeamContinuous(t *testing.T) { + m := Default() + const h = 0.5 + for _, jd := range []float64{ModernStart, ModernEnd, ModernStart + blendDays/2, ModernEnd - blendDays/2} { + for id := 1; id <= 8; id++ { + a, _ := m.Heliocentric(id, jd-h) + b, _ := m.Heliocentric(id, jd+h) + _, v, _ := m.HeliocentricState(id, jd) + var d, vn float64 + for i := range v { + d += sq((b[i]-a[i])/(2*h) - v[i]) + vn += sq(v[i]) + } + if d, vn = math.Sqrt(d), math.Sqrt(vn); d > 1e-3*vn { + t.Errorf("body %d jd %.1f: velocity differs from finite difference by %.3f of %.0f km/day", id, jd, d, vn) + } + } + } +} + +func TestParseRejectsTruncated(t *testing.T) { + for _, b := range [][]byte{longTable, modernTable} { + for _, n := range []int{0, 3, 5, 40, len(b) / 2, len(b) - 1} { + if _, err := Parse(b[:n]); err == nil { + t.Errorf("len %d: expected error", n) + } + } + } +} + +func TestConcurrentMatchesSerial(t *testing.T) { + m := Default() + const n = 64 + var want [n][3]float64 + for i := range want { + want[i], _ = m.Heliocentric(1+i%8, 2451545.0+float64(i)*977.3) + } + var wg sync.WaitGroup + var bad [8]bool + for g := 0; g < 8; g++ { + wg.Add(1) + go func(g int) { + defer wg.Done() + for i := 0; i < n; i++ { + if got, _ := m.Heliocentric(1+i%8, 2451545.0+float64(i)*977.3); got != want[i] { + bad[g] = true + } + } + }(g) + } + wg.Wait() + for g, b := range bad { + if b { + t.Fatalf("goroutine %d saw a mismatch", g) + } + } +} + +func BenchmarkHeliocentricState(b *testing.B) { + m := Default() + for id := 1; id <= 8; id++ { + id := id + b.Run(strconv.Itoa(id), func(b *testing.B) { + for i := 0; i < b.N; i++ { + m.HeliocentricState(id, 2378496.0+float64(i%14610)*10) + } + }) + } +} + +func TestZigzagDecodeBeyond32Bits(t *testing.T) { + for _, want := range []int64{0, -1, 1, 1<<31 - 1, -1 << 31, 1 << 40, -(1 << 40) - 7, 1<<55 + 3} { + z := uint64(want<<1) ^ uint64(want>>63) + var b []byte + for z >= 0x80 { + b = append(b, byte(z)|0x80) + z >>= 7 + } + b = append(b, byte(z)) + r := &reader{b: b} + if got := r.zvar(); got != want || r.err != nil || r.p != len(b) { + t.Errorf("zvar(%d) = %d, err %v, read %d/%d", want, got, r.err, r.p, len(b)) + } + } +} diff --git a/planet/internal/semianalytic/planets/pln3.go b/planet/internal/semianalytic/planets/pln3.go new file mode 100644 index 0000000..aee9e7e --- /dev/null +++ b/planet/internal/semianalytic/planets/pln3.go @@ -0,0 +1,193 @@ +package planets + +import ( + "encoding/binary" + "errors" + "math/bits" +) + +// bitReader 按位读取,字节内高位在前。 +type bitReader struct { + b []byte + p int // 位位置 + err error +} + +// window 返回从当前位开始的 64 位窗口(高位对齐,越界补零),有效位至少 57 位。 +func (br *bitReader) window() uint64 { + i := br.p >> 3 + var w uint64 + if i+8 <= len(br.b) { + w = binary.BigEndian.Uint64(br.b[i:]) + } else { + for k := 0; k < 8; k++ { + w <<= 8 + if i+k < len(br.b) { + w |= uint64(br.b[i+k]) + } + } + } + return w << uint(br.p&7) +} + +func (br *bitReader) skip(n int) { + br.p += n + if br.p > 8*len(br.b) { + br.err = errors.New("planets: truncated bit stream") + } +} + +func (br *bitReader) get(n int) uint64 { + if n == 0 || br.err != nil { + return 0 + } + if n > 56 { + hi := br.get(n - 32) + return hi<<32 | br.get(32) + } + v := br.window() >> uint(64-n) + br.skip(n) + return v +} + +// gamma Elias-gamma,编码值为 n+1。 +func (br *bitReader) gamma() int { + if br.err != nil { + return 0 + } + z := bits.LeadingZeros64(br.window()) + if z > 28 { + br.err = errors.New("planets: bad gamma code") + return 0 + } + v := br.get(2*z + 1) + return int(v) - 1 +} + +func unzigzag(z int) int { + if z&1 == 0 { + return z >> 1 + } + return -(z >> 1) - 1 +} + +// readElement3 读取 PLN3 元件位流:包络游程、幅角号(定长或同阶升序间隔)、成对共享位宽的量化系数。 +func readElement3(r *reader, e *element, secDeg, nk int, sortedArgs bool) error { + if secDeg > maxDeg { + return errors.New("planets: secular degree too high") + } + n64 := r.uvar() + if n64 > uint64(nk) { + return errors.New("planets: too many terms") + } + nint := int(n64) + e.secular = make([]float64, secDeg+1) + for i := range e.secular { + e.secular[i] = r.f64() + } + step := r.f64() + size := r.uvar() + if r.err != nil { + return r.err + } + if size > uint64(len(r.b)-r.p) { + return errors.New("planets: truncated table") + } + br := &bitReader{b: r.take(int(size))} + if r.err != nil { + return r.err + } + env := make([]int, 0, nint) + for runs := br.gamma(); runs > 0 && br.err == nil; runs-- { + d := int(br.get(4)) + cnt := br.gamma() + 1 + if d > maxDeg || len(env)+cnt > nint { + return errors.New("planets: bad envelope runs") + } + for ; cnt > 0; cnt-- { + env = append(env, d) + } + } + if br.err != nil { + return br.err + } + if len(env) != nint { + return errors.New("planets: envelope count mismatch") + } + args := make([]int32, nint) + nbits := bits.Len(uint(nk - 1)) + if nbits == 0 { + nbits = 1 + } + prev, cur := -1, -1 + for j := 0; j < nint; j++ { + a := 0 + if sortedArgs { + if env[j] != cur { + prev, cur = -1, env[j] + } + a = prev + 1 + br.gamma() + prev = a + } else { + a = int(br.get(nbits)) + } + if a >= nk { + return errors.New("planets: argument index out of range") + } + args[j] = int32(a) + } + top := 0 + for _, d := range env { + if d > top { + top = d + } + } + // 按包络阶降序的稳定排序:pos[j] 为位流第 j 项的新位置 + pos := make([]int, nint) + e.arg = make([]int32, nint) + k := 0 + for d := top; d >= 0; d-- { + for j := 0; j < nint; j++ { + if env[j] == d { + pos[j], e.arg[k] = k, args[j] + k++ + } + } + } + if nint == 0 { + return br.err + } + e.cnt = make([]int, top+1) + e.c, e.s = make([][]float64, top+1), make([][]float64, top+1) + for d := 0; d <= top; d++ { + for j := 0; j < nint; j++ { + if env[j] >= d { + e.cnt[d]++ + } + } + e.c[d], e.s[d] = make([]float64, e.cnt[d]), make([]float64, e.cnt[d]) + } + width := 0 + coef := func() float64 { + neg := br.get(1) != 0 + v := float64(br.get(width)) + if neg { + v = -v + } + return v * step + } + for d := 0; d <= top; d++ { + for j := 0; j < nint; j++ { + if env[j] < d { + continue + } + width += unzigzag(br.gamma()) + if width < 0 || width > 63 { + return errors.New("planets: bad coefficient width") + } + e.c[d][pos[j]] = coef() + e.s[d][pos[j]] = coef() + } + } + return br.err +} diff --git a/planet/internal/semianalytic/planets/testdata/fast.csv b/planet/internal/semianalytic/planets/testdata/fast.csv new file mode 100644 index 0000000..7da7d1e --- /dev/null +++ b/planet/internal/semianalytic/planets/testdata/fast.csv @@ -0,0 +1,108 @@ +1,-1931455,-62833473.893474385,-11738779.389872823,6761826.5210533086,14582.743821870408,-3696554.6520415167,-230752.33664395515 +1,-982346.61040627258,-54594365.924901746,-40249361.98021353,3524624.2650957904,1711348.2495792741,-2996593.1799513367,-395982.00376442779 +1,-664723.78605570132,19568697.79645298,41744274.883307591,670482.25088474201,-4562923.694390011,2147128.8468987569,659053.82913683238 +1,421526.7353404928,-8560427.7447433379,46317092.227732658,4314797.4566545757,-4930648.2616276676,-487278.75317214674,484118.99164901557 +1,613717.16879534395,50633858.869895719,-23956414.024757061,-7062273.6544543374,1013728.6378109402,4113246.5162761854,200754.98254079727 +1,1204502.931725678,12176372.939927423,-66239425.365220524,-6319067.7063551536,3316934.4824261386,1057323.5527012271,-251499.67586286186 +1,2332037.5255191391,21884339.865066517,40755036.895314112,1284676.9193076652,-4545596.9765684316,2165482.7567381081,597004.52219926508 +1,2469806.8315679757,-23900066.995238971,41442929.584906727,5578718.794936697,-4493684.7198801376,-1942325.6042346412,253081.92401840785 +1,3655932.6525249016,-15625619.077793298,-67848691.682821631,-4521720.6778781908,3243436.5201193294,-809742.86308659229,-339015.81050683191 +1,3836523.2060893951,-44660371.648988307,-49451083.941693358,-618447.01226177043,2254980.1946469825,-2714128.8424649457,-418801.59788438171 +1,4285288.7909884127,52518688.948609084,-29805567.862637836,-6735289.4021467147,1232776.764282319,3732096.7001830083,230849.22404800789 +1,4496271.6919694338,-40970592.544536121,28277840.586867347,5648729.3139325604,-3280728.4638769156,-3416752.9628165388,-49932.553514436906 +1,4837980.1486025397,1645090.9344821104,-69537420.91412662,-6340653.6856198637,3343740.3474207832,158105.36204072251,-234192.53407119896 +1,5331864.0411642483,56436986.11134512,-10013566.797446055,-4887819.6523256069,-109367.85533025698,4144364.1988601065,384240.87884015433 +2,-1598084.2773930961,43003955.157424711,98305699.451780051,-3674993.2754645608,-2802072.3434968451,1196582.1047978092,127925.75419764339 +2,-1225915.9647452403,-105799331.6546907,17266220.875921905,5448318.3185304673,-515830.63036713895,-3005601.8309584637,65563.844435925843 +2,-692096.24148116377,57989042.114105493,90667249.141690865,-4073847.1424257746,-2573020.4274585145,1613889.7017710209,127850.96939677013 +2,187593.29539150605,88716670.998645633,61780084.21283368,-5352144.289310039,-1748825.6132587346,2466373.3962477297,92879.321422775989 +2,1095203.2899014666,-28055976.399595186,103606769.6181515,1995508.7866806835,-2935392.8241721937,-809364.24893465871,170514.82278035372 +2,1701846.1254665717,97535014.661751598,47044008.494557619,-5367299.3750670673,-1327226.8208148675,2710755.9973209505,99606.614997275887 +2,2308050.3265216323,88645800.762682974,-63055996.742153667,-5936287.7132207472,1734508.3510735633,2453166.5504519013,-69784.367334436451 +2,2618496.2093246514,-107562369.13024026,-341415.88202046097,6161142.2754273145,-7777.3418237289252,-3038598.744264584,-44914.178817536522 +2,2879637.9671252463,-48468367.056526691,-97016501.64084211,1106714.6549768529,2687368.2707796628,-1364202.5907063801,-175128.24677738719 +2,3346858.0548557155,106831470.41331172,-18993290.60020487,-6260502.5255921204,518381.71200490242,2967330.6535282987,31607.927781469803 +2,3973215.4673080323,-107553033.53270063,5512389.6949170651,5754080.8874512324,-170022.93112917306,-3030413.6719801705,-65506.191032127062 +2,4148003.3935663197,-68635460.407738149,82792845.939131647,5650823.9669022858,-2337871.1223638346,-1942368.6204800094,70190.903086091581 +2,5025748.652984336,-65871835.11454805,-86028739.428212389,391478.07920932205,2388128.1732157101,-1843921.3359892734,-167133.09745928633 +3,-1931455,-150473987.96749708,5883801.5462573338,1923538.0693601375,-147750.77739770696,-2551129.8230871521,-64692.224256650203 +3,-918186.82549542841,-110863312.67125413,-104059875.81935193,-1447641.1284470859,1713423.8837573638,-1862737.5626304727,-50191.130090461993 +3,-525169.90033902903,-108728624.19648589,-106174934.76442857,-1447046.7364528317,1749962.9826123023,-1830523.1413526826,-42808.60152032529 +3,-50015.837424891302,-149982079.27249333,-3735673.6772040366,427139.6470313305,15740.218431437454,-2565618.2023071856,-40691.97342836282 +3,214407.52228800766,-139371492.31200913,52274662.357543275,1085025.4423712757,-952112.96382766985,-2404320.8379542879,-31843.061662426342 +3,1092521.8990238593,-143790137.01210663,-43703238.619850412,-293384.56270973664,701894.38456182287,-2463479.4317906653,-21730.323819369802 +3,1416030.9360990282,4715646.8583583701,146871512.13379583,964593.70751670678,-2618153.9695822895,79500.610125824591,1050.1823758632122 +3,2378056.2365927286,132175681.75762367,68573450.673263684,36102.118903539522,-1227453.9501712061,2275388.4270697795,992.35470886810526 +3,2770178.8802035637,-96073210.551071092,-116446639.19599527,251893.53166848375,1944783.6443092676,-1649407.9808276175,2750.6892199300423 +3,3490087.2680593487,-116931603.97130293,-94309444.772382304,737564.25139874162,1579032.93677497,-2018306.6882843499,10434.31654207087 +3,3769866.3212560304,-127516758.11987072,-78677929.492055118,852483.08219713217,1316292.4725816047,-2206426.8100802186,14611.194917333378 +3,4284440.5978102293,-112416610.03425916,95623333.507418022,-638513.85279376747,-1699509.7377296686,-1978353.8742986633,26186.310126968638 +3,4957447.7597313328,142255916.9256832,-51303194.995899975,-64333.678592950368,845279.42989303696,2400859.4156803214,-37339.426391242741 +3,5263563.8233592883,147955932.18912891,27452790.244904704,-1372245.362749584,-495436.32893261855,2509475.863939683,-34341.816687568724 +4,-1664596.7865721234,-119841287.66127659,214629928.90642872,7344645.1552384049,-1655802.7048651963,-982284.96186421195,60183.350695118723 +4,-599478.89874812448,19090267.437296763,-208769610.16352469,-3666610.8099194989,2240788.5261324784,274377.16941586416,-84098.028036331903 +4,-248716.27738850471,-154449764.37564087,193160125.03983238,8769981.5271898061,-1480039.323526321,-1211794.2744412571,37903.534240361609 +4,-112103.65643933322,17691024.283197854,242177683.63147894,3086072.6061628261,-1934726.3720257443,252551.67760584434,76282.778405779973 +4,378411.62997224135,-8672878.7757398747,242792687.49380925,4407000.5889353165,-1949667.1450878531,43212.109817951103,69667.886183071954 +4,1255789.973287046,-192079105.05521163,157457863.51673391,8991618.7145916373,-1208438.3833928432,-1471532.5260416819,9657.0171547220471 +4,2044206.5422547688,194353817.05443475,90208178.629202545,-3389383.5649577067,-786794.70067758078,2068074.8551034597,63532.552021373755 +4,2441004.9649596373,-174313155.28191385,-157340974.30781519,1001405.9012954868,1481922.0390659207,-1375297.4273268564,-65315.162482488347 +4,2610794.6258488195,31989205.642540239,-213194100.97962344,-5250336.265857337,2143934.4120386043,493820.56649056997,-40245.452173118872 +4,2952388.8482214063,197069112.07688695,73317863.285269588,-2687997.6104657804,-668614.76648096298,2150676.6897385409,60120.667137577468 +4,3868758.0927022072,192914598.65039673,-73336897.859198287,-4566452.8727918556,769453.22488924093,2158351.9605830782,32987.56225437281 +4,4106866.1883822149,-241022196.44414705,-61422556.733652458,2154472.241932245,531691.21472895541,-1826155.8443803398,-45117.620717894984 +4,4746544.2486086572,-117391484.90056048,-207574278.58181453,-2820662.3897265969,1811419.4700026382,-824291.99336024781,-33996.751894926485 +5,-1931455,-772690726.7694149,199332711.69978821,23295938.166143727,-289038.54449519527,-1061347.3055243213,12007.908902628149 +5,-1325996.6449592349,180480396.53800517,747681529.3589071,-7061549.837683348,-1101428.977350777,300005.9999717772,32090.033005237256 +5,-664681.44631013274,377609983.18746608,-667612150.77084684,-9468349.3027604315,978337.75683869782,594515.05479369068,-28858.505204890796 +5,-96501.675953416852,745409433.90775657,-90178932.856628656,-20083056.667049114,133001.91904592334,1161264.6514902934,-5676.8781348387647 +5,485342.37627524836,-188169392.61670572,758382147.73369586,3425868.8939713552,-1100205.1825472682,-229560.89454249665,28938.263893912637 +5,1173238.7993086451,706406117.20131779,240698978.76893193,-17876182.623419557,-370304.80656013114,1115951.9215193791,6376.9069425374564 +5,1615370.7769106091,587916367.11441386,461603151.21851331,-15229806.500849891,-705175.11843080819,938661.97823352518,13959.615618576123 +5,1826785.1037350758,561244956.97043788,-507505051.95698571,-11470186.947058314,746881.90311591025,887888.26927937986,-20231.729480225269 +5,2451545,598557823.64564335,439626178.58140647,-15226066.168285085,-683430.92717100971,963863.88426159602,11307.635576089893 +5,2753599.0637343237,226296031.21838596,-742092562.48249567,-1522612.6982041863,1065195.404953395,383426.92661061988,-25255.290575995921 +5,3114874.3738181461,182074976.44964525,732676104.37929857,-7896536.1257198798,-1115983.1714703941,327024.97186968755,22427.346278442008 +5,3449084.4638016652,-473694570.29021728,628465144.57373905,6330540.9853974404,-924301.80601250636,-625416.63053073618,23610.275622163819 +5,4335543.4811457656,668540603.99763763,-342758858.09950304,-11427062.794939062,487765.70515225077,1061641.1065687188,-18791.118632898186 +5,4811751.160826955,387658794.30004311,-672602161.38214934,-2153271.8565818518,946253.42872809921,620026.6109271819,-25724.810973033404 +6,-1641888.9886098416,-686492816.01118994,-1355795658.9303675,42952773.887906551,710557.33189186431,-321450.89553285151,-6888.9359527966481 +6,-1199890.7343151937,-6547011.5424645171,-1491167482.5340998,34200266.003707945,793980.82210951252,49998.421051204328,-18360.707512834677 +6,-495071.74549286813,-396127308.03415489,1302874076.7495625,-20658645.247012053,-849401.02253967361,-198232.35696117618,26315.801473504991 +6,203580.6581091024,274317216.33875406,1297789480.2521629,-38456880.780166082,-871830.11749293015,205223.45338959465,20776.149980213813 +6,657524.79896592442,-1220226661.9125969,701088106.64418411,22813430.848251615,-469486.88318913395,-701081.8470078907,31094.277480943074 +6,1334494.0976866423,-635557715.84245205,1197287757.297435,-3293179.9311753525,-791379.7125760701,-378763.81936424156,36086.596872546521 +6,1644883.4403174384,752681019.15505409,1112905920.4654369,-50536181.876766868,-743897.03621322254,476969.48294272821,17551.897493327084 +6,2439063.5577853415,1401748441.9140415,-348532673.8084535,-49588033.386088066,154486.24273349484,808464.44217149378,-20303.880735674633 +6,2603918.9317545565,-473505060.92402983,1266415358.6568511,-2142492.4030701751,-828138.15326644725,-295773.39530921279,38353.558952681233 +6,2998152.2305208556,1323691949.6476212,-637876853.9632442,-45218441.929919943,319045.09884086461,742504.9164734513,-24295.229203347066 +6,3545067.3353613606,273641525.40107274,-1466245783.7346866,6009256.3401860232,783834.96568117873,139774.31301671496,-34934.384793326637 +6,4787979.901907431,-360321053.2664209,1343615181.0427721,8587867.6068227608,-822927.48007010436,-234871.97090875759,36334.416353850575 +6,4968789.0632644892,1190224839.0095122,806484162.58310664,-53874195.601666808,-484756.00721770566,671940.6581197062,17819.928863205969 +7,-1931455,2894644070.4879403,756160167.78285444,-42722063.66376356,-158455.34607476872,539581.86262571253,5780.2971290324476 +7,-1299710.6774742757,-1652813497.4114439,-2284954817.3150005,13144349.41217255,467833.36724030983,-371963.94410855509,-9486.0812188854543 +7,-770462.97847182536,2256131299.248137,-1979021527.0557549,-45624834.443564534,378404.44925007265,413740.66348842659,-3537.7448013652229 +7,-92720.255235022865,2953685285.0036082,-591632835.3764168,-46921294.744743593,111064.71117343244,549236.76056757616,1184.5313449242369 +7,38233.718192380853,526087843.83821124,2813665258.0229359,6710576.5901976451,-584407.93390329578,81549.397549820918,9019.0293932408294 +7,915512.19167856034,1423541754.4921358,-2596205814.2575164,-32187370.544998161,509565.49289296364,253849.61985176097,-5967.853376906799 +7,1086819.7742420263,-2160795910.3294029,1686877290.1221597,37874651.336996913,-368816.29220835702,-491926.06640368479,2872.8911181288054 +7,2451545,2159068005.8505802,-2054593595.1767559,-35630066.061299823,400629.27618474205,399831.64672568179,-3705.4236690463977 +7,2788451.2690392667,1924974334.3014181,-2257900044.4903579,-32485162.263930943,445973.19522842293,353970.19111085538,-4468.9305090149783 +7,3196915.8307418544,1716461018.2789247,2383747287.6932254,-13912145.132387942,-479824.74531714933,317917.7165913487,7027.1173571583331 +7,3339211.5422046753,1100424600.2176342,-2706801493.959465,-22054736.893173464,543794.69902268471,196802.62984987439,-6159.8076093661357 +7,4077162.4576984812,1790044984.3184762,-2353626961.4547744,-27582447.996957697,464744.85598250676,328369.91984754609,-4747.3516259032485 +7,4563257.2600855278,-883688484.94580317,-2681132054.576282,4661750.7989627197,559138.3933246329,-212209.78296159173,-7060.3908146772455 +7,5255953.5429854253,176398133.55444014,2862858850.3950419,2672939.9215900172,-588777.1376254115,10089.319179438906,6855.505459606492 +8,-1788585.7039658355,-3513271192.4057598,2817751220.3355002,20844166.567134719,-297081.46535614121,-363966.00462519325,14160.039073059792 +8,-592717.14191114833,-419291993.66922736,4457175691.6954069,-82312642.387510329,-470508.53573387407,-41730.318686452192,11490.402501626359 +8,-494321.65565781738,3539963845.899384,-2764875081.0691595,-23190658.599192608,285124.46218677796,373313.13646270847,-14130.530094777387 +8,1127138.8410635418,2326042377.6315908,-3860427017.4484763,26348245.389132328,398611.37271773251,244595.72592458862,-14164.633121846622 +8,1858996.5446232599,4470376763.5941801,-87060745.380236328,-100867267.105951,4854.4957710794015,471779.90457555559,-9834.5936946464371 +8,2334649.7350391094,3667658235.331665,-2593945764.3535409,-31028660.995055448,267143.10877025634,386824.26512141677,-14096.69685815077 +8,2495643.2818019064,-3970906103.9656725,-2188493136.3610191,136607327.86990944,222674.08204176804,-408450.20825126721,3279.0431710929747 +8,3061785.314996372,4443405331.6073389,-532820754.7399025,-91837165.308714941,52736.851062547379,470233.70836222399,-10884.024327752826 +8,3773929.1366995303,1850118950.2099705,-4116742183.2299247,41801944.285420954,424157.75413573621,194772.46644325901,-13869.324883919659 +8,4086551.827056977,4453185592.2157459,342671517.68596846,-110749618.48873965,-39597.821519572375,470320.35091181187,-8777.7543097479811 +8,4328687.1229854897,4358180859.7030611,950957293.46743524,-121177021.29482579,-104310.08056429461,460950.24469642743,-7082.6688518946357 +8,4801490.3102042498,3432462840.3685045,-2908570515.0270596,-20426335.661570013,299816.18551788281,359628.5494703938,-14441.867965645717 +8,5242087.2074043704,951563814.29495192,4357254944.6240664,-112067795.75194977,-463065.41744182754,103285.93847407671,8732.5099581102131 diff --git a/planet/internal/semianalytic/planets/testdata/long.csv b/planet/internal/semianalytic/planets/testdata/long.csv new file mode 100644 index 0000000..b7bf559 --- /dev/null +++ b/planet/internal/semianalytic/planets/testdata/long.csv @@ -0,0 +1,976 @@ +1,-1684007.1141194815,-51012124.77325701,23569933.620385673,7506285.6034423243,-2507649.0608118493,-3398048.2214728654,80398.174800651148 +1,-1598084.2773930961,42632028.961591989,20651622.482445639,-3688263.8816285124,-2534064.6095627672,4224952.2056673029,567114.0809940215 +1,-1546567.3315786319,-18119276.02167467,-66632314.348705962,-2147405.2575914222,3310201.8809569767,-656021.86554344976,-429802.91059436055 +1,-1504240.5109060195,27565860.969403364,-55725923.388774455,-6806643.486033191,3022059.1740360889,2323456.0038026301,-203886.7494868486 +1,-1430035.1121999889,-59637368.680079915,6184531.7608202035,7348316.8777063228,-1202500.8528249483,-3773153.3189952066,-104061.97138637729 +1,-1417826.4385308512,8096109.7829212267,45835149.99319993,2026302.8611467092,-4872708.5202749399,1126161.5640872554,640314.89292053774 +1,-1335854.6077046522,50863359.87483605,-9968032.8585474566,-6549347.1365128877,89169.598155238331,4552857.989639163,286550.9168268597 +1,-1265381.6129784849,34036052.857999496,31666123.845183499,-1857607.6737119791,-3585810.8587316517,3493828.9752375693,643626.88865272584 +1,-1225915.9647452403,-6041925.9508440048,-67843698.221236452,-3766203.1474799407,3432245.3013470862,64450.195747170234,-391743.38653767819 +1,-1164062.7735359697,29771193.066041104,-54345294.537475049,-7017229.2485049507,2930734.1785560874,2458681.9592709574,-174556.91885153929 +1,-1011238.6947121838,35326270.676014699,30443188.417128101,-1996113.5266036501,-3474686.2365112682,3586464.2114522527,636571.04910826345 +1,-999292.02473752981,41230761.532087341,-41727720.140002415,-7498008.4544758629,2235160.6367370845,3376303.5052401461,-28929.941874138909 +1,-982346.61040627258,-54594383.709905662,-40249344.59847983,3524625.2735203109,1711346.6102330193,-2996593.8969291379,-395981.83738026948 +1,-952195.24691596127,-36654257.134895764,36722831.689112015,6639552.4971976383,-3733130.4612995158,-2602360.3000919353,250022.03956748586 +1,-918186.82549542841,35271996.800037473,-49423887.741078436,-7337260.3302234402,2660221.4482059786,2863521.8119761636,-109343.19034356911 +1,-861601.57841308578,30324169.717730086,35028011.9581559,-1065735.9335552456,-3919354.9792207261,3139272.7546379794,656418.50713188027 +1,-780588.03500993783,47801588.364891529,10647513.526298385,-4668431.2999201324,-1642191.727493291,4497791.1560364403,491394.86181506183 +1,-735829.94779943186,25689440.647644959,-57930429.758958243,-6847097.839911649,3073484.3650292833,2119197.6891593495,-201248.15330192621 +1,-692096.24148116377,51185646.069220312,-13485407.144146111,-6672019.6663890127,329092.06096941099,4455400.6779760262,268226.20322592737 +1,-664723.78605570132,19568699.203292768,41744284.836996086,670481.38451251481,-4562923.3391658943,2147127.4984989171,659054.21731221362 +1,-569021.28440544801,46795240.841573268,13479954.655198293,-4289672.2707228018,-1899066.4471639514,4421350.2397319889,517398.31879543577 +1,-525169.90033902903,-53189552.940174945,-42148133.457586318,2999302.846919884,1815484.2551217298,-2932536.1369386045,-405349.75345223112 +1,-336886.3062741172,34969986.211465769,-50517481.398822375,-7400108.8060029121,2678423.1096032141,2779542.9580878625,-99952.138485262767 +1,-266577.78305063839,32206280.538174246,33581322.137990318,-1165852.7763147419,-3794183.6383903925,3263254.0801000884,646642.98882098484 +1,-250271.28551590699,-59979409.09450873,-25135237.3300048,4801985.2967883451,813804.47020764626,-3535496.8324774508,-338925.00485428149 +1,-103117.7717652421,-28509547.509369422,40863189.380324759,6014367.8142349981,-4230880.5774205131,-2091664.0047385094,310975.62798368779 +1,-85315.127458434552,-31909376.805517651,-61955727.688259259,-956207.6920725829,2944389.657689536,-1569995.2701965973,-431979.594060309 +1,-50015.837424891302,41920166.378780939,-42280199.649565697,-7570609.317523418,2204122.840818244,3323694.4059832324,-1173.773732918384 +1,95991.210029273992,-26487961.349788629,-64484136.68877814,-1808856.613454873,3093019.4563473631,-1248795.7259877436,-422744.12273159879 +1,130372.27192090219,-59082923.754406542,-27523883.335750915,4347892.033720715,954896.06965055689,-3490826.9575761487,-355103.59167370299 +1,187593.29539150605,49403894.390312739,-26970458.630135607,-7242489.7798195295,1238192.524469652,4030813.0282472963,160338.02681400173 +1,421526.7353404928,-8560407.0332824886,46317102.660376631,4314794.588892635,-4930647.76198861,-487277.05941382621,484119.04881159833 +1,463545.89263325511,35634376.166677646,-50882709.024394386,-7506155.1717699459,2645428.1323148073,2748498.0670972005,-75319.994328690111 +1,536352.19204846583,-56585760.682257749,-33998456.398856618,3404736.1893621497,1337967.1715436263,-3305828.8875883054,-385298.282058126 +1,543741.34620087454,-56921645.927418627,-33133200.615573395,3500433.0376734538,1287022.9578168124,-3336311.7465958097,-382265.73911599122 +1,613717.16879534395,50633860.242248304,-23956438.700442232,-7062270.2978214072,1013729.9888191344,4113245.4270138661,200754.89946374297 +1,623708.62183470884,-51709048.344251439,-43309310.008054137,2158203.8311995356,1875760.6725848401,-2926313.8740746742,-413516.48107394623 +1,639349.3173756469,-50039791.307663187,21553933.929671112,6815490.0212750873,-2488471.4240311449,-3575555.1365216365,-7786.9901314475082 +1,714680.65252043353,-20245214.435159169,-66660907.547082469,-2897631.9795804084,3213744.3511557686,-905337.68865115894,-400707.84584853979 +1,777889.23808972351,-3204289.8041837467,46478221.502910003,3825406.0462649758,-5001430.9524592971,-32965.295439987814,513504.5918373412 +1,941418.85263170535,22492400.443529461,40314551.160144791,762584.40015047346,-4477522.9178090915,2314704.2959060166,632762.67449049314 +1,1019915.0357078202,-59913073.533217661,-19694870.839815136,4585193.8585917307,468438.86498618464,-3727897.0000111763,-331652.07884086779 +1,1088426.0524462555,-27941077.366109863,40348121.050110064,5911362.2029306609,-4275732.3934730617,-2149618.3983013863,267972.16400465631 +1,1095203.2899014666,-41690639.233005412,30703083.792125277,6563091.1938355919,-3322443.4157782034,-3131892.8421654226,96211.590215729462 +1,1149026.1837176164,23358918.824473675,39875560.181705758,706794.04392548813,-4439015.7863806039,2379161.5177563913,629648.52151784499 +1,1204502.931725678,12176356.612987993,-66239433.850870773,-6319064.302842116,3316934.2555145109,1057322.6331254833,-251499.6827967423 +1,1248358.0671537584,-42511373.743334077,29757550.378018871,6548737.0066136923,-3245327.4979154798,-3199498.5257338653,77954.147583345548 +1,1369226.4818641506,-37748006.075049937,33866350.719665915,6374818.1012697946,-3640972.4596880442,-2895195.0503653232,136665.5562476989 +1,1404375.425812115,32172140.599162024,-55286745.060954981,-7483897.551429539,2814168.778822551,2393716.7547520846,-92524.17126608883 +1,1416030.9360990282,-51803145.273587011,17629798.114448156,6496600.6340139909,-2203217.8286706987,-3740215.697072634,-73002.300782488324 +1,1487943.883013247,25409423.78922724,-60324587.760218203,-7211566.715021085,3051787.9002907211,1908832.9773840115,-151405.24887105159 +1,1701846.1254665717,-56903910.983065799,5692379.6366939479,5968462.6777765835,-1279085.5482471252,-3959861.2396464362,-188409.6813254327 +1,1804389.4595964327,48911275.438102879,11888022.1647074,-3767012.4074987066,-1794623.4418270646,4319726.5667903284,515362.64720967098 +1,1827322.0795898,2473115.7881354941,-68687802.73826164,-5690763.1201943485,3371914.2128269901,408153.31979944825,-291746.80014182668 +1,1848705.8859610055,26152543.393428288,-60186641.202501535,-7293251.5466976073,3026907.4414762221,1928707.030268393,-137265.79622535 +1,2096788.0793594532,48138094.945908196,-35262980.087061755,-7373071.0132816508,1663550.0556894052,3613990.039758746,133731.91427589039 +1,2138649.1521867919,15855286.688004168,-65568019.90871343,-6771688.2637125188,3251743.7972924472,1225123.4882660178,-206908.96902139692 +1,2207857.5232347613,-52401934.088251889,-40075353.369993113,1661008.7747062354,1698972.0053509288,-3140193.8488689107,-412590.44064081239 +1,2308050.3265216323,-57698492.75324361,-25900692.093096349,3251657.3089895528,856519.96703579824,-3647582.133357618,-375563.07194824459 +1,2323578.773018145,53761154.329231083,-11479207.716989405,-5915332.9743995862,65523.845874503691,4313408.2030456513,344299.98939277855 +1,2332037.5255191391,21884348.110274103,40755028.984698996,1284675.9996863885,-4545596.7894589156,2165483.674220094,597004.61549128441 +1,2378912.2879968146,53750005.371257141,-7811823.3866897887,-5597003.0330064278,-207664.08024399629,4358532.6863312451,374071.73139458202 +1,2409039.6972490782,-56976574.289655089,-28358934.396667201,2934426.6928431597,1008006.830438114,-3581523.2948661069,-384844.81287081429 +1,2451545,-19461723.790963124,-66913275.75261195,-3679853.7452132958,3196367.4030234357,-964605.37682434928,-372179.12417147082 +1,2460000.5,15223591.977972096,-66001764.24579408,-6790048.955488082,3257175.8208434386,1160934.7631422167,-203892.21660326232 +1,2469806.8315679757,-23900098.33181829,41442921.901772588,5578723.9520099871,-4493683.0973999603,-1942327.2032007454,253081.66402624111 +1,2610840.75890035,-57170715.860285446,-26978542.864071038,2963826.6091571278,925408.97812530503,-3632883.0833192775,-382718.21076947387 +1,2618496.2093246514,-54709579.030315094,-34450036.515314668,2122323.3593073655,1375938.0302895913,-3386243.4899780075,-403299.15926015616 +1,2751610.6835809182,-10643371.053717582,-68827805.942256019,-4741200.565036688,3311116.709119102,-448313.41076187103,-333960.8901152811 +1,2770178.8802035637,11721450.343743321,-67285319.589426875,-6621574.8476953525,3298094.1369316634,917589.39670395362,-219273.81942517389 +1,2785840.2756211869,21014952.96875833,-63805454.513076179,-7166459.7982789148,3149711.1761416262,1508520.5080836958,-156617.77855581112 +1,2808738.9916420998,38376512.027884677,28979995.771897566,-1021252.6737393495,-3370478.1870018616,3517633.8643700606,592600.04683777178 +1,2839784.2245940454,53786362.711473927,-3716897.4114786,-5097249.8765792698,-529775.15653471986,4362476.0784655586,409493.07469187322 +1,2879637.9671252463,49536657.516434133,12178977.34213477,-3382717.0155357867,-1827539.5213785446,4247721.3875682373,515559.05059807253 +1,3154668.7497814698,-54227557.866434239,-34480892.370373271,1804011.9300345229,1385693.0799109503,-3409893.4444795679,-407101.6372655601 +1,3249068.3727896679,-35824488.26812382,-58361476.40341486,-1844661.4639359054,2728205.1706273379,-2050887.6948675856,-408034.90533004497 +1,3270930.2648888035,36261807.527111694,31312336.288037382,-469104.93474224093,-3595808.4325500489,3312098.0968839303,588890.504396401 +1,3284072.2681582002,-52998903.770821504,-37041633.220045231,1416258.2781144264,1538775.2461185784,-3315121.8674476701,-412402.98717167269 +1,3346858.0548557155,-11712567.247772094,44880873.855854504,4804516.7806333536,-4933414.6298907939,-963395.7706290154,339489.39490661124 +1,3380769.0042219348,-2237481.8351637912,-69445787.20935905,-5704458.0737376399,3351480.2599263741,19974.884728532044,-283586.64229408046 +1,3419345.5441400679,-15601318.121973002,43952983.472794078,5060170.8453522697,-4829000.9649479249,-1311107.5423797716,298231.36190480215 +1,3490087.2680593487,-57420510.778123766,-3007246.5761490017,4585516.6543092448,-669176.34185869445,-4087724.6810523118,-292023.33114624821 +1,3518566.0884496719,27302821.694937944,38100854.696390152,954856.7534665867,-4277905.6961797755,2553472.8966393177,577739.15846806881 +1,3578895.9072680827,52127245.162259497,-28832584.789623614,-6827723.5135148056,1198533.7340707006,3801371.6425576569,224860.02578099541 +1,3585688.6648248574,18241355.206979357,42452086.357157424,2106597.3053908157,-4729197.9811720168,1751378.6209539894,545304.32323199755 +1,3655932.6525249016,-15625627.371391019,-67848693.045110986,-4521719.0868013613,3243436.3179070661,-809743.54064745735,-339015.80753365502 +1,3657029.3953580931,28815275.000462506,37210461.277210496,797634.18402081565,-4185606.1805020953,2671074.7858133009,576961.63923610293 +1,3678288.9220930533,24047845.725817524,-62906991.799947314,-7395478.3557633879,3074750.8581638872,1633976.317172796,-114745.02318152778 +1,3762039.2925404031,34748650.348231226,-55639849.807240359,-7652737.0552127464,2715437.152427427,2349279.0196980415,-21221.952286785068 +1,3819307.6395831443,35798573.036894716,-54772204.494705729,-7659195.7996393824,2668629.859803135,2417888.7731738156,-9892.3812688022899 +1,3832678.719234935,35155040.316770419,-55359720.04798951,-7656133.3424089961,2698189.29915189,2371025.1005775132,-15988.622912337181 +1,3836523.2060893951,-44660374.016383,-49451075.032304645,-618444.73137495015,2254980.1119936178,-2714129.1187646091,-418801.66995922546 +1,3973215.4673080323,-57539329.116311349,-7304332.1251934767,4013131.4528040756,-362535.68525314477,-4090774.4984259354,-325775.91605968855 +1,4092002.0398669122,4325981.9445304768,-69205127.645886585,-6377923.9060916444,3339834.097205339,370887.36752813158,-234463.55266571074 +1,4105959.1308997376,-56157403.996485852,702915.1369901431,4541382.6058647698,-947857.39287675463,-4132540.1040794337,-284783.523734032 +1,4109692.9677240904,32957699.548007112,-57459879.711581923,-7640039.1752746729,2793456.0508890417,2191530.4123684433,-31631.280062789592 +1,4146818.1703821719,38803171.042514324,-52235561.969492778,-7645887.635861001,2523394.1984617035,2601061.9473116831,26628.33686156297 +1,4148003.3935663197,-47224213.157576278,20957439.57392263,5583081.5821159026,-2598826.5841793558,-3775098.5352697424,-123206.64513607032 +1,4186230.5865203831,44025845.702987768,-46100768.156873047,-7517871.9636922069,2191516.0961017516,2991707.6076400247,88132.9706900561 +1,4271016.4964148831,-5330867.8174453052,-69434930.516478181,-5672207.1415076163,3334689.6539588049,-225763.95221670874,-281729.09061157174 +1,4285288.7909884127,52518663.74662073,-29805566.973988354,-6735288.2247669548,1232777.2456443885,3732097.9787366525,230849.21967714376 +1,4286016.0774189811,-1141079.8565556542,45795839.639231764,4108902.8943015449,-5080640.9629532909,-66732.310987445642,392636.19540915149 +1,4358794.8884052942,-47812476.659682401,-44557619.243368775,-196697.02402344521,1995391.9422450995,-3008455.9165469911,-420126.2375156302 +1,4363078.4222854916,-7448448.8374312771,45338227.6284642,4569778.6570161339,-5027252.5742964912,-647861.67152721318,334383.38096411811 +1,4401086.8749158718,-33602030.640825547,34807967.12541277,5673771.3688362958,-3911611.8361575105,-2874773.9099969706,50101.80499199628 +1,4484844.9876696859,-57195130.14610678,-8649051.6865183301,3636152.0775629883,-265687.18293903489,-4109730.8949280363,-342749.30433956638 +1,4496271.6919694338,-40970597.733659647,28277850.322892357,5648733.0984055493,-3280727.4999228846,-3416752.851214557,-49932.690006108511 +1,4514036.3835271653,-20415376.215103861,41997589.186017886,5281438.5796331447,-4663207.0935359001,-1808253.6228522919,197799.35088836486 +1,4558151.2511345278,5526729.2316197949,-69164521.548746392,-6549681.1183229871,3332400.1833709986,412171.40725677169,-218034.35704165543 +1,4570166.2625536146,-42150265.449820653,26946966.634787966,5604275.2722198637,-3158515.408974011,-3502956.7585195652,-69486.33514163697 +1,4621218.0733667631,-17962712.95438775,-66986853.303381369,-4583702.6529852897,3201500.9261228885,-1019139.3112533604,-333510.65190025646 +1,4804523.6742066173,-56116970.83857692,-2028592.6444170682,4000778.0431847502,-746261.11726771877,-4174990.873572398,-317329.66127698513 +1,4837980.1486025397,1645071.3491478879,-69537423.600297213,-6340648.1915222444,3343740.2316535972,158104.97259132119,-234192.75341552123 +1,4846629.3164225984,53451290.764945686,5695862.331063264,-3456675.4691495034,-1291047.4840084068,4217350.364757658,473048.066376189 +1,5025748.652984336,4847501.2554255566,45542575.750027962,3744613.4344709376,-5064863.3111551367,436761.0058742183,408205.924283856 +1,5038618.8071374437,-51490872.414948426,-36800094.263312414,432923.58656480198,1567218.2229479335,-3400819.4110823157,-420115.64192000427 +1,5048398.4929598905,-11693020.028017161,-68510116.041869968,-5319130.6491712471,3284496.5005318397,-664006.49219778704,-298448.92052066035 +1,5126817.1156069133,40780524.853493392,28035428.920540366,-408087.23872707138,-3248246.5635093609,3475293.9965171348,547791.5614069096 +1,5195872.3073759098,42982591.868764199,25597611.802354779,-758976.28182719916,-3015437.9428311982,3620187.9460700098,543215.37317973829 +1,5198267.6635453114,-44148565.798647493,23985799.389765471,5327393.837411535,-2906323.8548208047,-3693970.5150298704,-126364.01853472096 +1,5233556.4870860651,-54228694.005140468,-29049036.463190377,1232388.179129865,1102487.7599496469,-3702256.4174513514,-413875.33478072163 +1,5331864.0411642483,56436971.236163661,-10013629.568162572,-4887826.4485522565,-109364.12349267142,4144364.5094210561,384240.64591534162 +2,-1910625.4269706856,-9754645.2106898148,106413997.54650117,-1237487.789831307,-3045394.2147905012,-292891.85649677075,158284.7367929591 +2,-1816443.122843025,-91507221.014987707,55275410.818375662,3783085.4247284499,-1594905.2241177382,-2609278.9205799839,122344.91225704645 +2,-1664596.7865721234,45999261.529651433,96958128.850152612,-3830138.8884583223,-2764069.1456911708,1280856.5476522725,123407.72502563252 +2,-1633873.6231535422,92287063.544366375,-58678063.665058911,-3904077.7730063684,1584655.0462102608,2534811.101864581,-119672.01489473642 +2,-1490457.109678637,55263987.950432174,92140991.503968522,-4195628.9776114412,-2624220.8867149716,1540080.7913726093,115828.35541524332 +2,-1325996.6449592349,97214075.386822686,47392230.085723393,-5790377.6843734207,-1356523.8480711754,2701415.043932871,36443.079574279393 +2,-1305348.189373699,106529263.35746662,-22969807.227515291,-5369480.4891664647,602090.42789589311,2938837.6434466122,-70466.891488343521 +2,-1262026.8623748557,11328376.65151093,-108825843.91181129,791379.07467550552,2973426.4556739656,292459.47036126256,-162548.89623565241 +2,-1227450.9171129605,-67033382.983738683,-85389228.039797947,4668453.1805637451,2344412.7870447305,-1890234.9522581631,-101669.20510692682 +2,-1127198.4669872932,34424507.61933063,-103926726.07334788,-605486.62417843379,2836826.7955347309,931621.6099110418,-164305.81848843064 +2,-1033066.0951237342,-16556076.581378169,-107894422.74421887,2139344.2377189822,2955996.6421612524,-480473.12133332528,-155089.2925433963 +2,-876550.07506747963,-16557777.088164255,105758081.68589129,-205502.73280928697,-3014726.9547752002,-486260.36257326731,170704.83089589505 +2,-772078.22508312692,25898134.980987839,104157283.31354958,-2462407.7365044649,-2960925.7036552206,713453.47876759234,156683.56412914299 +2,-675378.02177428501,-98043308.399497792,-44915231.802088663,5871119.0449447837,1229459.1924035123,-2770750.1638993546,-42690.668445929936 +2,-599478.89874812448,-59133980.403707393,89129080.665662676,2520803.9494777368,-2544270.6054100641,-1692839.5666821522,157068.02681663173 +2,-545947.46776324348,-94175473.582788512,-52746104.34433724,5724575.764072082,1447642.1140779722,-2660070.1971710315,-58675.59447768129 +2,-495091.31634280551,90159459.581181034,-61667725.713971809,-4567534.750328185,1675389.5738505456,2479541.5305968621,-114298.51589107429 +2,-426729.67608257732,70390847.687771916,81648178.874224752,-4596986.8689948702,-2314311.0342898834,1959229.9903907084,115567.20251135399 +2,-396167.8497008069,64382161.32702262,86381740.750864029,-4282854.6084483536,-2448396.7320462842,1791951.658920747,125000.33455553671 +2,-354579.39496690687,11641886.36483933,106679180.48535842,-1425599.3656151956,-3030050.3939307476,311060.75405059854,169367.80861769975 +2,-334403.93271124619,107718454.93500558,13587776.296739971,-6202391.9310348565,-405447.78248114529,2983289.7259310181,1968.7800692887233 +2,-248716.27738850471,-70448564.782846212,80538262.690781847,3489469.0817090515,-2298318.0868241568,-2012239.719722871,143850.5679392718 +2,-241326.24223391432,-382810.52406527358,107260845.79927498,-666726.80668285897,-3047251.4917584066,-28458.050136686423,173709.24529932573 +2,-209764.07892694743,-21689898.347206768,-106790479.53990804,1899495.2719709002,2934274.8436527201,-622811.06693881948,-163483.46179437928 +2,-197403.55219547101,-15828145.405881096,-107860257.43417716,1563320.4313367831,2962929.476297711,-459812.72332608839,-166261.83632961448 +2,-121135.46653172863,67713687.355401337,83894029.426833957,-4346532.2053379007,-2375203.8055405598,1884293.9250689792,125459.94298036152 +2,-112103.65643933322,-57081945.978146844,90552668.883990929,2768589.7613487523,-2579917.4820273612,-1633432.0295897836,156816.63578605108 +2,-96501.675953416852,17614164.591126617,-107790521.52640909,-424679.44606125134,2955645.8825846803,469320.40516661631,-171955.20690921554 +2,-70854.688283364521,92631647.220100716,-57714531.428033456,-5012442.6380988508,1569678.3890163167,2550580.7155988868,-103452.00397276267 +2,-58803.431499841623,-102743841.74410823,-32194021.755083241,6064727.8681708248,877336.37542340765,-2906368.9576183422,-35363.962564708374 +2,-35874.079807724571,-90268516.650702104,-59287565.426840745,5483575.9005173817,1631726.3114479987,-2549224.495401131,-81009.742578393838 +2,36736.130432011094,-11552670.352026623,-108397474.66924031,1153276.5675159055,2979045.3899743236,-340445.63107895426,-170146.43723527674 +2,84992.291643073084,-107130936.81303066,6280154.2084000632,6158834.4712019395,-200552.71401300223,-3039206.8529060325,24206.89768464601 +2,109674.52296956419,-53120867.663472302,92980031.240037531,2699557.3287679348,-2646371.1234741746,-1520902.0897594972,158976.74656963968 +2,137839.11760925851,-47040565.504330419,-97951425.273485765,3091448.8414030094,2697877.529362685,-1330561.6832614879,-151017.64183654342 +2,214407.52228800766,-98138193.792036071,42939905.614950456,5552495.4028944448,-1232855.6210065635,-2791098.3964554956,80535.69940206148 +2,221397.23165164562,-103786345.70969887,-28490608.667619497,6111101.0171557628,774979.24650348094,-2936523.4905973626,-35600.378331410204 +2,337842.49135007011,10215950.291468121,-108620268.08235179,-339803.57476475311,2984129.8576483489,265280.88767196919,-174361.42169703144 +2,378411.62997224135,-39266330.196256295,99726947.681954086,2084007.3763790496,-2833320.2070914288,-1127844.1659348605,167455.41085797365 +2,485342.37627524836,40464976.216685064,99764631.362792835,-2492189.1197764063,-2821369.9634357681,1121319.9345893566,163487.80874200218 +2,494369.97426253278,-72022524.08926326,79269719.128805712,4115104.2987875799,-2256674.6007009516,-2054338.9776296956,134416.24337418319 +2,577993.6549429521,-106132382.1392847,-17238686.537217408,6227157.2634798931,461324.03380445053,-3004632.9743126491,-25239.624347144356 +2,655938.44117414718,-89082404.572037056,59495016.792423286,5225709.4849668816,-1697620.0229826227,-2535172.2586929356,99631.396511699742 +2,823252.73597173626,108239220.14957982,8461452.4009157661,-6358055.5243792795,-256076.70004755456,2999562.206975311,18759.077708673733 +2,877296.24198089819,-105591302.53640707,-20505966.552571904,6183332.899313258,553656.24239625549,-2987769.2186414613,-37491.194993516714 +2,924592.62089987099,108276625.13569634,7961189.7128660949,-6362678.0122843198,-241721.77228694764,3000754.5482891207,20223.707733055115 +2,1092521.8990238593,-68329675.024675891,82583360.982429311,4300456.3630227875,-2345956.8194549643,-1947866.418019623,132043.06478400898 +2,1166095.5485040057,27543951.641650897,-105488885.95418921,-2028727.3944306169,2902532.3412240394,749641.30516043294,-168424.34291538081 +2,1173238.7993086451,-93759834.074330658,-53410162.693905979,5325815.6247364013,1472869.449550054,-2646866.2392740585,-97184.615795266713 +2,1210411.445408382,108379781.31483646,6279363.2319046026,-6370658.566587517,-193340.04467343391,3004424.9929103707,23914.349835819467 +2,1219140.9107148079,69761673.170468539,-83792647.412285864,-4471875.3342428524,2300362.5071467063,1921955.1072865231,-127646.88840598711 +2,1255789.973287046,105453658.94323559,-26625252.781002585,-6344207.1757393302,719579.21730696526,2917620.8482378279,-29323.123785016709 +2,1286510.5901554311,-44600299.650955811,-98988059.663253143,2162842.117837206,2733805.104979293,-1260554.7818548647,-167341.94007989395 +2,1341117.2369882604,-32419601.373979732,-103730514.2833584,1378421.7710594411,2863449.1687533827,-919383.12122563052,-173747.54248465612 +2,1402307.25576506,108035084.59122032,10324596.935098516,-6316993.2450824538,-304715.91051353212,2996117.9909008108,34847.658074015737 +2,1615370.7769106091,16685679.334340867,106349129.71498905,-210318.63190395309,-3002139.6206510835,452807.30051291117,180165.2383606915 +2,1637496.3475555568,-36029764.035824046,-102470988.73518063,1360383.8278880112,2830991.713427546,-1019633.382899451,-174327.32728902873 +2,1756359.4320327346,-46148775.184480041,-98219385.729067922,1893963.8062313045,2715465.2446183804,-1302738.5599337348,-170559.18567860944 +2,1821188.4015711742,40899447.523382403,99760754.525550723,-1520182.7780562541,-2811603.1097705369,1133106.9322201691,175186.45614454304 +2,1826785.1037350758,89389671.170885131,60956892.771590054,-4708759.2869794546,-1716397.3820012894,2485788.4203071692,122910.68445984175 +2,1870677.9806007287,-99194446.283152074,40910201.319160685,6199309.3515092805,-1168524.5033765312,-2812931.7318489905,42629.28691994027 +2,1877735.7345504016,62029701.762946136,-89546317.977355376,-4471088.5530669298,2464766.6900306335,1710797.8594080121,-128760.4438697656 +2,2044206.5422547688,-31132466.966194928,-104061430.27756363,721238.75683852355,2877025.9294873965,-881522.576687427,-177523.16650252408 +2,2083410.1837759214,51952648.996972173,94589328.014956817,-2007399.7485320449,-2662870.0887064026,1442710.1359945731,171211.11390988683 +2,2095363.8025208651,-73456446.608175606,78211546.149966568,5145744.631880831,-2217226.9410058116,-2088566.6801177934,106599.39813167916 +2,2144562.8894250412,-47619421.474198818,96257316.354351968,3867345.9936540322,-2723019.6102086138,-1359156.5129460192,143389.48755916426 +2,2182921.9998152172,105972087.81759973,22917779.008632466,-5906317.8202504879,-651846.16357621748,2943167.2616585209,72180.579875499781 +2,2266773.6153919585,30716292.290016975,103333666.21998824,-517576.55589084851,-2910953.268266805,847503.25987414713,179517.50107268544 +2,2378056.2365927286,-103128753.33055475,29947295.543799806,6361565.7580629196,-858802.53124623501,-2919796.0090177013,11426.255336201706 +2,2397780.2731268918,12610116.397663662,106970789.42354229,688897.16780182929,-3015421.9328870457,338742.79574750166,178897.28441924992 +2,2441004.9649596373,-87100973.513380498,-63950033.143254392,4160828.7644302277,1769428.5532047462,-2453237.7365596737,-135514.02473727579 +2,2451545,-107456499.25639249,-4885009.1300608153,6135630.6963122943,119396.52470389186,-3036121.4261188689,-48387.615097017384 +2,2460000.5,72220849.67997171,80401291.3701884,-3063476.788842835,-2260354.7240211163,2009106.7065759217,158011.52715628446 +2,2535357.5503298556,-107536194.49492858,-2307729.999012473,6153351.9567425717,47082.499554856302,-3038299.4726908561,-46155.984090744649 +2,2593711.9383720476,34548300.344178908,102163344.05388612,-476962.04696100636,-2876148.3177498728,955145.92646327021,179126.99446869647 +2,2610794.6258488195,18591875.740513369,106147112.41530454,511595.72552996664,-2990297.6549822902,507037.75282265845,179019.29174198513 +2,2632432.4652583934,-106615726.27581996,-14911753.40739641,5883682.6705358168,400458.20966169034,-3009576.3799521662,-68175.272262359926 +2,2663593.9188041752,34437216.679443583,102208821.80680095,-410015.73280604981,-2876931.535610077,952150.74432661431,179123.33446595087 +2,2753599.0637343237,4616835.6901031174,-108631978.39373639,-1994100.2272040078,3003648.4062442421,118606.018864325,-169216.9472746609 +2,2851058.797517837,-107246186.72745512,8000170.9597123945,6212651.3939987794,-241519.46278764916,-3029553.4924527914,-36781.356727073908 +2,2894173.0374383992,-68722516.846594319,82541133.967008889,5288429.429034804,-2335118.7127313782,-1951287.2150307426,98942.869879548132 +2,2902172.8322488172,105420817.88853978,-25967595.968987841,-6402262.0948799271,710106.43735774397,2925446.9660799149,9704.2471463943966 +2,2923391.5965747973,-84490661.566540658,66278373.595369495,5910872.8450423228,-1878393.3062013646,-2394874.0328702615,64887.801677017262 +2,2944052.2326150993,-57998301.958915524,90437874.752543107,4840271.4258877905,-2556472.4405269059,-1649016.4049410925,115595.29600311004 +2,2952388.8482214063,-100633300.03328867,37626487.389612466,6328526.7763276855,-1073012.3272721379,-2846661.4680656386,10938.541397745954 +2,3071928.2882831879,-99074804.579072729,41557632.466298252,6307074.5003552213,-1183220.9202590061,-2802650.3973457189,15012.76219697634 +2,3094488.6810698071,57149272.626811907,-92500737.563239142,-4901986.4947628444,2556138.8709191442,1581813.3558776597,-113735.72118971757 +2,3148373.3684775028,-65731098.353688471,-86088840.390954494,2041316.6932840995,2385902.5165643035,-1847613.465167877,-167767.51122044012 +2,3186011.903311247,64783026.979666322,86481382.390868649,-1948601.5306299187,-2429442.4291376332,1802125.4315184255,169506.21463544224 +2,3385667.0888963919,-40596970.817331724,-100603622.78676195,160922.76016322177,2788224.0574109661,-1141675.9546511576,-176375.50025627337 +2,3396644.8951812498,-103675171.51154356,-29646028.42126539,5070753.3130940394,813727.93612169684,-2919954.9708546069,-104785.87512320228 +2,3449084.4638016652,94163158.18167986,-54092110.767682604,-6275528.5628944049,1492949.8564807873,2613516.8141726572,-26667.443024274246 +2,3465332.9111963827,15006216.409816435,106751911.33865874,1433837.4662790243,-3004522.6745983013,407490.83724488859,172432.29675520447 +2,3493903.4271124844,-77579200.518345371,74352634.056800529,5804792.9608557224,-2103308.2693772186,-2197329.5067934878,67646.634470825127 +2,3520848.3114578417,-28021520.675159372,103896883.09892805,3753779.4050467159,-2929513.4205258135,-802527.35079162812,141830.7018279332 +2,3579259.5810319055,4829768.9030195419,107652953.20322773,2095394.4987636399,-3030751.110258549,121610.49767866969,166479.69292198875 +2,3605342.5567358602,-47161965.522376359,96651112.889426857,4676922.1720518684,-2727438.9828790952,-1341053.8814722626,117520.86192688606 +2,3663524.8103983011,-2561170.3815131867,107696523.76404145,2557192.1890556333,-3032951.4163058554,-85925.883239218179,160911.89281751326 +2,3693156.3804768156,76668588.241898328,76252784.890353397,-2377590.7286467785,-2140422.5847882316,2134476.5136071844,163033.02328047663 +2,3769866.3212560304,-107294137.25858098,-9916553.4354768861,5479283.0198451346,262018.82487642585,-3022197.8855485702,-84033.516589461098 +2,3772581.733645685,-87662178.506290257,-63288876.398562759,3194434.4723323472,1753305.4869675878,-2462837.5670946245,-150440.50435634475 +2,3868758.0927022072,-79285444.70128946,-73641204.164524093,2425426.6223700293,2041824.0353325065,-2226164.6167943687,-160805.36772287489 +2,3935699.2530838838,-105747440.46325262,-21128188.425071325,5030873.5534741143,576027.91141590918,-2975932.6866827579,-102512.82846632124 +2,4069207.5081039593,-39121149.872889787,-101174717.56532851,-521529.39714056981,2806277.2942498089,-1097797.7091430374,-172890.00605631474 +2,4072793.0636102129,-64476504.488387898,-87056750.290532306,1143257.1144538203,2415119.3377165003,-1808703.4892260868,-170528.58387076581 +2,4085055.8753798483,20404547.634319838,105899876.47104582,1621790.9431459727,-2978717.6473624925,559994.64277190331,168094.36494369825 +2,4106866.1883822149,-22781626.988536261,105231803.59618002,3849261.590877152,-2964772.4734991542,-653397.83575962926,136290.12036006639 +2,4235859.9731076481,-65240181.455293782,85506470.168582216,5562844.0935690487,-2413835.4097807375,-1846749.3604527474,74400.375350810471 +2,4264349.4469904043,68405505.955074891,83679139.337612942,-1260935.1774805945,-2349014.4247965179,1905029.7263825089,169501.74865122046 +2,4284440.5978102293,-101183513.77537857,-37699710.735392891,4122609.9566717017,1039735.440821844,-2843162.953074798,-128094.25765990333 +2,4365530.6731302179,-99075423.935352206,41982631.044333808,6106837.1842966983,-1192564.536718809,-2794012.3592138295,-15691.230756817527 +2,4408910.9570551161,-107694187.30769964,3596104.3689597896,5476707.5579156997,-116029.33484618663,-3031423.5817148024,-76968.31316056136 +2,4455302.9423613138,103893327.59096944,-31043798.249258351,-6017571.4491894385,857232.13197082002,2890210.3299373481,37135.782291268464 +2,4478806.1677568033,-101839380.86707005,-35917947.333638012,4049478.6714440486,989926.96589004667,-2860720.4431654983,-128361.99898494528 +2,4618013.489915737,96667724.139380053,48676520.764594786,-3321092.5740772649,-1366100.3839607197,2693725.9902441571,143478.99923281805 +2,4746544.2486086572,93927581.284689128,53732562.359401114,-2931004.4838892943,-1507866.4804281897,2617721.5531648318,148978.97066379176 +2,4811751.160826955,-18630319.076771535,106089793.43254206,4031867.5537313833,-2987423.4316245262,-534896.0452064675,126203.43446848541 +2,4957447.7597313328,-44865495.952159554,-98722647.132455751,-914252.91275280819,2740717.6874760506,-1255654.2723538412,-166045.25770860357 +2,5052839.6295767939,27885206.048206154,104229835.53720392,1944481.8008703419,-2930017.1018669433,771588.13923849305,158705.32002947107 +2,5133790.0131960288,-105731142.46408191,20864460.852301128,5463503.7074231226,-599275.97796784772,-2973682.3072259743,-66071.109952868006 +2,5217727.9773081681,107324060.59394044,14455853.321649447,-4360527.815975287,-409275.53863115539,2990975.7459946815,113524.74434399932 +2,5263563.8233592883,108189021.15328068,5086118.7474926244,-4662646.6538420478,-147807.34971880922,3014837.5667961016,103192.04408677586 +3,-1807812.1053618162,148370070.47875455,-7502375.658282483,-1832177.5372982882,84020.656805492748,2588980.6719184034,65008.2127384704 +3,-1788585.7039658355,-98698324.341204897,-116079160.53068213,-1865064.0964211531,1914074.4002773941,-1647130.9608657141,-62769.554820718855 +3,-1725998.0823240243,147861391.03341153,-16297998.971105736,-1976792.734989485,235904.47882376885,2576167.3663499942,62116.968419624769 +3,-1709209.6563349424,140798926.43582067,-50007170.586095728,-2730100.6553717926,815150.96458209492,2443161.976854919,52516.080895850966 +3,-1641888.9886098416,-14168203.850360803,146234997.35207811,3752150.2964876886,-2608812.0604271381,-230605.00486959785,20767.917304210969 +3,-1576798.8049385049,-147910498.41000831,25417608.833645001,2067302.7188092344,-483817.2993711009,-2518203.7864576695,-56601.63022994349 +3,-1563625.955812044,-147190924.08225101,-34508155.774915338,592806.87037537491,539846.84934329498,-2487512.0559543059,-65681.921764479732 +3,-1539474.8702204335,-86546991.284923673,-125460370.6320388,-2202400.4154308308,2071301.6612433912,-1443168.3066246766,-54761.19727825997 +3,-1522003.9526984771,-150088682.00986171,9165540.8337305151,1647155.7800103058,-204849.1742447915,-2551137.5341731957,-59503.253405541494 +3,-1359448.2215805235,-147402273.14792889,-33148322.735159107,506064.80064147478,516834.27730027062,-2494214.7961530625,-62507.668679596813 +3,-1299710.6774742757,131333427.72512777,67124266.284893692,442202.17299961945,-1218790.4640412622,2306910.3907122226,63148.288579131615 +3,-1285572.1527371076,32958245.862527814,-148426888.46584439,-3669639.4645479308,2464994.2045349539,573949.19489368133,-7315.6808432110402 +3,-1199890.7343151937,-102959223.93267874,106509158.80198364,3199965.9325744933,-1898695.5353334192,-1773214.738926661,-24827.10865881764 +3,-1036426.3942562039,118587627.83247441,-92824901.961327344,-2843384.8760468722,1538896.6585047231,2040318.9858976987,33047.993601491529 +3,-961729.04699411965,-126880284.97549082,77616212.234162673,2498866.1136260876,-1391626.1870618558,-2181556.6916455585,-36723.357098198721 +3,-942747.86596195493,-107281187.91769528,102191375.69397545,2866507.9146837895,-1823681.441518463,-1849743.7632842632,-26746.980750657 +3,-791079.85613839678,-114552781.73297802,-99845659.475081787,-1327583.211227976,1643045.9318873149,-1927234.2365935943,-48799.879268134784 +3,-770462.97847182536,140651785.55430472,46083601.641668156,92155.887058350068,-849360.16775051504,2457679.7146911141,54386.158511992769 +3,-758022.83077099756,113082562.82429066,94296855.299478531,1222988.4881962216,-1696621.2948352515,1988204.3148906189,49741.805824936266 +3,-667166.31934148096,93949327.422821179,-118616088.44659513,-2828183.7805517693,1969706.5590084719,1609250.5368510974,20551.735503710381 +3,-664681.44631013274,-76444927.371865228,-131774086.21950896,-2148037.6599878138,2178072.0804995704,-1279462.9664472425,-36864.350015532124 +3,-495071.74549286813,147836819.67912278,15623716.339561522,-422269.80828236003,-318555.65062393935,2569451.6860920009,49067.497598880487 +3,-466550.27508213115,116584426.42223468,90167984.771321326,1105014.1365864594,-1623021.6211072772,2045676.5913938701,45134.371820210006 +3,-457424.73181510041,125476778.52505296,77713165.657197475,834986.67243356619,-1403565.669456807,2197692.6671263059,46719.853132078671 +3,-398391.7223886326,-34576107.589763701,-148465240.87979841,-2508702.4540489917,2458485.7333980929,-573774.4079499566,-21212.008543445096 +3,-395491.31715357001,-84382330.012976259,-126698822.65369867,-1895877.2643219503,2093877.58656299,-1416701.2685376354,-34666.095451210516 +3,-339731.97327987757,-59002584.187565081,134853831.83126637,2620867.2691996773,-2406435.1709108283,-1022428.745911832,-7807.0717614731393 +3,-315973.2507299108,-96787962.725383386,111656056.36462042,2348685.3680883488,-1993339.0619223095,-1676624.9410025952,-20990.602376749761 +3,-109697.22623653128,120593986.37954232,85096629.922619238,964465.04966919334,-1532171.8127467188,2110138.8128087055,39461.09241652154 +3,-92720.255235022865,-129628836.26480271,-78190898.074488208,-820614.48650411936,1281135.0691702685,-2196106.1346738506,-39691.297597624725 +3,-89386.738918630173,-38961587.752211973,-147337358.45576811,-2237874.4171885513,2440098.933663121,-650318.34562572604,-18684.225954926045 +3,-67038.171627650037,115775372.03614514,-96983551.288298041,-1928208.8250696759,1604698.03759268,1979913.7458370142,26281.785844471189 +3,-60881.297235909617,-370937.51241278835,-152381432.62077579,-2422795.5087862038,2525612.0534492056,1063.7772657645901,-8261.2981347840323 +3,38233.718192380853,117335618.60254842,89493771.097917497,1021000.0385639265,-1609125.1039446215,2052717.6496122743,36184.262633468177 +3,203580.6581091024,40654186.432666957,-146652207.20763412,-2195102.777855217,2432296.9011761425,692808.1110449282,3855.3507014336005 +3,233229.74423041334,145809474.57953554,-34789564.092546426,-840627.91388540913,549443.48550468567,2508439.1056086873,34040.55668233797 +3,297743.68281099852,-122709709.86992793,-88699543.184484914,-941659.52083569323,1459943.2843955527,-2081126.098663704,-31758.024431055102 +3,454705.29486214416,-69491895.273956582,129795753.59108461,1776813.9997726476,-2316806.5980687095,-1211134.5794518103,-11163.638606239861 +3,532662.51504404098,5762058.9739223169,-152237869.01574072,-1869627.4605716241,2524366.748738579,100362.99515196224,-2913.9415427229433 +3,541726.81441616919,-133266456.44216958,-71030611.021975383,-648152.10699268151,1163061.1501121479,-2268119.3532855278,-29471.169612463094 +3,574371.6326704151,140628701.54799932,-53294768.377136014,-854600.45045118197,864642.67321392172,2409169.6579052177,27471.040191956563 +3,657524.79896592442,-117028001.10656366,-96063112.068807468,-938259.42916032602,1585625.1311819018,-1987275.7713574506,-24883.195267953422 +3,915512.19167856034,131547375.94730209,-73884296.009108096,-837782.31380460714,1213462.3861545254,2244335.8621424194,20907.291320596218 +3,969383.4987256187,-128827816.21398157,73273805.814576894,792710.0368314177,-1319380.5436713311,-2237256.7567290124,-20094.047823448203 +3,1005653.0868851673,-35004412.995046042,-148151713.13294864,-1338891.8312237812,2458107.430911799,-592000.65509169456,-7223.0345879821625 +3,1076836.0565533908,-121498163.1545085,-89896154.897749498,-712329.59671377449,1484246.6836418489,-2069730.598918525,-19023.55405679359 +3,1086819.7742420263,134394872.70429567,-68482927.533373982,-675495.7590406494,1121979.495878198,2292203.5343797207,19242.750801038517 +3,1127138.8410635418,-60948110.767341562,133879837.13388725,1161386.9933243282,-2388951.0141304284,-1067608.7640871967,-7708.0878069950404 +3,1164024.0380486939,-47024214.256761298,139282403.17194793,1164398.9803829521,-2485002.3272267631,-824758.07778443058,-5537.8329091978239 +3,1171892.907888311,81884295.772000879,-127935887.6600374,-1080766.4023035171,2121470.9906555312,1385964.2946980626,10268.409895218396 +3,1181893.3188211354,26125381.786461797,144618798.91163874,1156160.2588865762,-2579104.2705090381,456027.39736346412,4889.6766594518831 +3,1253709.4806311391,80139196.1678617,-129067759.43920557,-1013284.1684096481,2140482.5681474027,1355569.1142826104,9517.5390360667698 +3,1334494.0976866423,149379048.81949618,7474325.4599672724,10967.87328662554,-174513.25267927843,2567960.2994168997,18272.706394823334 +3,1344472.4177958574,-74861214.823136196,126754873.62368083,912050.30709916505,-2262094.3770136153,-1311377.8309388557,-8605.774778215995 +3,1358288.4552726317,83300608.587566063,121686132.98354125,823612.69993067533,-2169756.7990126512,1451083.3686393527,10633.629868800794 +3,1478412.4381849142,146962275.19404721,26234214.785007272,140659.84503958913,-497851.49776471156,2530224.2438367582,15691.889425627274 +3,1507834.7553006513,-126705846.29909131,-81787875.632122844,-474320.26179427392,1350521.4146281832,-2166050.5845148754,-13118.790824803758 +3,1592688.0664429567,118927003.3841991,-93685804.156317398,-516003.38757514971,1547587.1011574245,2017407.5499117323,10894.892699613019 +3,1644883.4403174384,44700649.620833956,-145459446.63565829,-743522.54184352118,2415264.9107244117,751432.48286158137,3789.2571646634697 +3,1662044.8566168114,30929071.553489033,-149033699.13508731,-744671.3573192436,2475172.5758010834,518301.07765520253,2566.154079338211 +3,1763401.1851286227,-24875041.654088788,144867434.77914721,628366.64868708455,-2581305.1928058653,-440988.40813818149,-2030.33383934012 +3,1797417.5562174106,-125482247.96928266,78337819.542268604,316373.90144797799,-1407385.4790452856,-2188834.7910684352,-9118.1179574706439 +3,2090420.7047943072,-123615609.42326179,-86100695.105235741,-208529.46768295154,1427653.5328042044,-2119447.7150776759,-4648.5028452634333 +3,2103787.0252119754,60088426.993128978,134661154.60459965,300253.59522575059,-2393834.9558908856,1041149.313024592,2009.0587864298257 +3,2132447.7482447745,-83220267.037241668,-126510016.58794776,-261712.04441485525,2107059.5837834198,-1422166.2171744925,-2625.3236135701727 +3,2245931.6084092339,-88387893.158162788,117842600.92143407,143756.73402081116,-2101747.6376370187,-1552752.0812897396,-2172.2072051770851 +3,2251622.5967869186,135329972.80055961,-67244333.14695552,-72867.473137410198,1102581.2086173601,2296480.7754580821,2960.4580767059933 +3,2334649.7350391094,7867943.9330103891,146954191.33183149,107274.91982985199,-2612438.7850823319,128659.4198385738,-54.991216171083522 +3,2439063.5577853415,118787390.47292,89187301.264424041,7978.3513713111879,-1587299.2254689469,2048670.1725849975,147.28848134944457 +3,2451545,-26502519.306762833,144693955.78681269,-166.84745017527263,-2573548.793107627,-473313.5848235212,3.7662561913103434 +3,2460000.5,-135034585.02899519,60716736.045131363,-2429.2145759357181,-1097259.6319889387,-2357175.1218604138,128.57973922898069 +3,2485458.8781836675,105438531.94868514,104240492.11083095,-23954.121560785348,-1851223.9847656123,1820465.1189982824,-348.35025745684084 +3,2495643.2818019064,149919281.66998339,5901908.3280270612,-5372.1097643352905,-142924.32536148946,2561895.470125176,-694.80518553847753 +3,2547641.5393228149,-104392141.27517936,104154128.48285295,-55959.908454151489,-1859374.2923196878,-1832162.6791433641,1201.4513565889742 +3,2603918.9317545565,-141971980.38220748,42828189.184812576,-26278.630580079262,-784544.70598454156,-2474587.6971695074,2412.2136016441809 +3,2733245.0135987615,-147648268.85113809,-21859635.648664963,69409.926381497717,336442.45527669089,-2557216.2208588049,4367.3390216120188 +3,2746411.5647210125,-135377786.20309308,-64542894.572049275,147610.26197453562,1067144.8959084474,-2334569.4983976702,3999.0396844588922 +3,2788451.2690392667,-76270055.8804584,-130628042.53668256,290732.94416259712,2182275.2973084948,-1309249.3003465168,2125.7636931290422 +3,2789313.9323644927,148907045.60049099,17639720.225708649,-76786.33960329245,-343179.16359743278,2544355.8213499626,-5190.2944051854756 +3,2837948.6438788753,69104597.404827267,130693994.20238382,-331689.2745008042,-2315349.4544680449,1191254.8618832058,-2088.865516000867 +3,2905495.0170944203,120374250.12846458,87432003.555973873,-290421.26183475216,-1552366.2749359657,2070161.4658053461,-5137.0777924394833 +3,2998152.2305208556,30899681.28540856,-148806524.11861679,480078.30622897524,2480721.1378351799,510602.94757129595,-2967.7414838568707 +3,3008944.5139920544,-71051534.773353815,128958307.05215381,-400832.68850585248,-2293622.6185167106,-1254637.5017139039,5466.5498355831642 +3,3061785.314996372,150244316.42022538,-7433798.5239389138,-62276.164190680967,88245.717512187432,2557632.6053604353,-9545.3488248909089 +3,3114874.3738181461,-83269593.748825416,121507650.69207577,-433296.56816623139,-2161809.8579977429,-1468098.645281316,7366.8819610430064 +3,3146914.3863433609,139502788.92599514,53689374.030004524,-327043.4973188116,-963093.55468877393,2388614.7564683692,-9373.7803588302813 +3,3150144.1991835367,124285750.57262282,-86640118.912242815,276459.78536251478,1433281.6711703432,2097943.4050421328,-9920.0706109462244 +3,3196915.8307418544,144587675.20328248,-43446418.287351482,80538.036690979425,702302.67562633555,2451171.5128864669,-11606.69385953283 +3,3201574.1630870095,-34041047.372739129,-147699781.97089958,697223.2307572118,2469777.5850131456,-591634.55921323702,700.62013354483668 +3,3203550.4984710356,109996648.68402869,99994082.449980482,-543744.66038691031,-1769616.7689873271,1890740.862995035,-7168.9901546747215 +3,3232786.3573101312,79269262.858018368,125137372.85489759,-658114.44162170531,-2212323.7852268117,1363442.8711869507,-4539.3974198828519 +3,3306881.5332143093,149264702.63286835,16223303.397712806,-229694.26266593579,-315866.08134514594,2544478.4656246034,-12791.975144370754 +3,3325689.3187076421,-147947684.54124546,-17663746.830169681,242063.25036694063,267418.1663775295,-2569819.7815714357,13237.63471966175 +3,3339211.5422046753,-144560701.04392263,-37317702.903443389,348599.1519570697,605688.0662542379,-2506350.4114467604,12741.338597258862 +3,3437671.8906649854,123008686.19745123,84178841.172546238,-645671.02018317499,-1490637.404163599,2109139.5427640094,-10641.393311747848 +3,3545067.3353613606,106220529.81214897,104085606.71251056,-828036.02746962919,-1837681.45622397,1823056.8787557615,-9256.4947514745163 +3,3605229.621254039,81053694.083637238,-128408454.6669517,753614.76693401148,2140324.0352078751,1358194.6000267863,-12595.535928064681 +3,3671571.7178411912,-146677573.25501081,22268079.569568112,86190.709108891708,-421925.12077000953,-2560188.7498012632,19182.984508447029 +3,3774449.7754612248,103599206.57549891,106751286.95656808,-1028674.0413001495,-1882044.3131131972,1776166.6294597508,-10217.474176998237 +3,3975775.5296189105,-64457590.443318881,132545610.72983046,-1020603.4732512607,-2348397.5189324683,-1142564.2871936194,15754.720551250231 +3,4077162.4576984812,121213608.81513618,-90976450.023886353,533449.82235579053,1511708.9067598167,2040932.5729180025,-23199.34820865003 +3,4080380.4699281119,-34928415.284438215,-147132894.21781135,1478688.7624195346,2470837.6350639677,-611915.12992453505,-819.72132154885014 +3,4122988.5987655688,-96487082.819234326,111523021.25796898,-806754.5156695256,-1979410.7776263945,-1701434.372289137,21894.311126905031 +3,4155581.6111736093,-121066532.21217069,-88323433.32846795,1213129.6291519869,1484049.8216993744,-2096660.6442403372,16307.692789219946 +3,4201672.4632230625,32838313.016949091,-148065369.46169442,1389159.6774861175,2480013.1167629818,539193.50504267926,-12834.299135301029 +3,4335543.4811457656,-40995966.393005252,141710242.19937423,-1381168.3068033662,-2504000.1740589165,-733477.00998630887,16305.78871651368 +3,4407517.6630313201,-84714363.365873054,120735648.80488335,-1034062.6142048294,-2138027.5350664007,-1496488.5081555359,24245.729378910997 +3,4550384.2230565557,-148149908.64360163,8279682.7448617453,498246.94406428246,-173777.11186999021,-2588393.3485590592,31189.552381254565 +3,4563257.2600855278,-15086015.736443555,-150461979.82230121,1842762.5169491617,2530507.1967647355,-275765.17999450484,-7011.3605983337648 +3,4727566.9422645448,-129457883.94592711,-74640629.756509691,1536502.0818833432,1256461.7312885774,-2248776.1766818366,22606.28400362239 +3,4787979.901907431,149051048.57161927,-24028679.207550995,-404495.4036820149,380716.5490998,2520979.7040639399,-34334.66460469571 +3,4797263.9821976107,-117856227.52346838,88990166.587431177,-582764.76364190842,-1579551.7380540567,-2073182.2703396298,34444.853243937672 +3,4888502.817972417,58072229.42600213,136680995.74343258,-2123384.6922365944,-2396885.1475537629,986579.3250033044,-864.25478054305233 +3,4906853.0408161199,-134201344.24669653,62061183.558124714,-135747.96799931221,-1108250.3142035822,-2355585.8636102108,37408.947990988141 +3,4908491.6637900053,129598557.07277545,-78320470.242188171,378221.76023174636,1303140.2536042235,2182414.0278279088,-36122.633135225471 +3,4968789.0632644892,150701629.8751978,-5466005.7132380288,-741590.30909041304,65407.216299299995,2551695.2212103806,-35338.576654844088 +3,5039162.001057867,-73563479.566026822,-131138390.60974964,2255264.1355422321,2217501.098094515,-1279128.8552016104,5363.4492955006308 +3,5176060.928650463,-145469088.08833179,27932066.538858507,489792.55483316752,-511376.35145701043,-2547717.5639757803,40355.807948861584 +3,5210208.1565704886,144664720.56738016,-43879615.477958053,-263834.41801816871,720780.25512278208,2441952.8699616203,-40547.43767473811 +3,5216035.4154041745,126858460.57919021,-82677493.697710663,419698.50559006963,1379125.4142811683,2135191.8020489272,-40253.024065973746 +3,5255953.5429854253,51298962.160488047,139436511.0871008,-2413977.0613277666,-2440969.7018546457,868084.69568874629,2824.5809134347433 +3,5325082.7566090981,-143858353.05136642,35071685.163576409,434998.20041541418,-634679.3098669746,-2520754.4630484474,42632.015077610726 +4,-1931070.9484683401,-211738388.8655664,108185128.19590554,10187643.552340414,-781229.2065476866,-1835356.9787803483,16256.785354280935 +4,-1722220.8563856122,-218723708.39287463,92445839.032442227,10289440.486836215,-644954.16955395672,-1890468.337578818,8143.7429213234873 +4,-1624845.9708735053,54090759.269637667,238502482.23803151,230571.57212996174,-1869756.2488725362,502738.47373643838,84689.813069798722 +4,-1618826.2036244983,220209096.96770921,-14562209.921759006,-9497767.1027516779,307035.08437510883,2128182.3705627471,9564.4436693463558 +4,-1589915.7910634428,205781229.52217707,103781092.01855923,-7603271.9926808514,-771333.47941690811,1909996.9138242856,53127.99172535439 +4,-1563274.6505421316,106754008.45343758,-180305050.35131761,-6461385.1631606948,1966417.7302760417,1110530.1812307613,-71130.725706463403 +4,-1516451.3542829594,222097810.51478124,17013292.185816936,-9174670.776160907,9588.349449281568,2130831.1999232462,22955.530196895255 +4,-1413358.1385084488,201861117.86343461,110950911.4689752,-7193883.8044459419,-838248.09732840094,1883045.4511472473,56386.555702698672 +4,-1355432.1149842155,-158885370.64703193,186543048.21858427,8769278.6455395948,-1424101.2318228038,-1305689.885864743,44342.476744503059 +4,-1293456.3303186039,-208032734.92541209,-85297454.177318335,7635448.187030755,958157.22009760002,-1881228.2386964115,-61805.947280644323 +4,-1234093.812943904,220149733.47317326,35188907.908792056,-8678541.8644225113,-162627.47181680641,2122943.8834682801,32008.401272415096 +4,-1095864.2207655469,35974353.224030659,241628565.46932089,1517080.3417419149,-1902858.8990154052,369738.80026160734,82403.148594303508 +4,-1055927.994716112,-132249369.322054,208152236.47290266,7994871.0965782553,-1599916.7481770464,-1058899.1166669419,51997.807687241446 +4,-1047104.6200182812,65557435.105190076,233615522.21715349,256337.46746291555,-1848422.3547881979,629348.29531939479,83204.603542988145 +4,-924303.78151505254,210319784.55648705,-46799077.924466453,-9091427.7436621208,617776.29342032131,2111879.2838693587,2385.3466784798443 +4,-877481.17906051292,163271432.01911944,167154438.78569382,-4376070.2165729804,-1332477.5602968489,1533338.5123023784,73625.272130959638 +4,-860973.68683929415,134176567.55540699,195077884.69373018,-2822602.3104083943,-1559844.5358104713,1257854.8645711935,79142.663769975581 +4,-833468.43978301296,90343565.628691748,222766907.34944165,-671913.54166601179,-1775244.0517194916,858904.20279249991,82458.381032960562 +4,-762091.2296500362,189392105.04439136,129213433.12728393,-5800458.2591689304,-1015950.5406391077,1804575.91888553,64726.600737881294 +4,-730502.49436991126,200607208.54107949,106067367.3573961,-6526017.7939509945,-815580.08748831821,1926954.9516575469,58505.468430033558 +4,-666421.15642042435,-117726509.58293603,217460055.25274864,7640220.9199325321,-1678625.209233653,-917556.78989703744,53638.416596835763 +4,-639028.89346137433,44257215.339523129,238665579.80021682,1562234.1197807014,-1896559.1878559424,461447.29927745246,81080.74775539845 +4,-592717.14191114833,-189517803.77125895,-120481694.17059074,5745433.3216760624,1278980.122602449,-1683012.9040596543,-73716.259792094002 +4,-572098.15215920145,-176605474.8306269,-136060713.94250098,5000135.5443595592,1433554.8921221076,-1574067.8960799195,-78253.453416164353 +4,-540886.62030778546,191292671.76276994,123626131.95590007,-5715650.5877923621,-975633.58043708687,1842328.0660764189,64167.7346556185 +4,-527255.34522495489,184958397.44011676,-101332579.45544635,-8649545.6852692068,1162423.3504676362,1920756.4186749589,-18030.704066518105 +4,-494321.65565781738,121582818.90593003,-169242847.31991774,-7146647.5849570585,1855283.9894204307,1308146.7917578716,-53390.433851805574 +4,-438232.82740092021,-235118338.73172912,45004170.746943548,9741410.0455262344,-237467.15636265083,-1966457.1977880506,-19299.218766213282 +4,-399851.01195660583,-154524027.33135241,192844834.55939776,8767531.6826605424,-1475948.2819871942,-1219300.2639318171,39041.308464359317 +4,-352126.80209444487,101216293.66909704,-181960780.56636709,-6564027.3685690043,1981906.7595154284,1110512.2489205066,-59601.137540910073 +4,-345736.52423540223,127440417.90264587,197735668.62050906,-1962260.1445252034,-1602177.0992429813,1225793.8152602869,79485.239855449516 +4,-308992.04277046374,-187767589.51008856,-124703169.84254909,5312033.0561770769,1309720.9891765777,-1649433.6557300063,-74541.053245853152 +4,-162282.65303595481,73958045.088448986,227494625.73447922,704909.77285645506,-1837002.7994718936,745400.53836380632,80508.801671426452 +4,-161341.16243980057,-232364001.22040901,-33097387.057978805,8230165.4599960856,445127.88332442468,-1972358.1749134434,-46974.134175952706 +4,-123299.20634868275,195303839.48719901,-79587885.489992648,-8543626.9062284864,940797.3392805187,2038584.9979065557,-3754.4532617709597 +4,-91540.781198035926,103970969.85783973,212331918.75120968,-582121.96364279196,-1727515.3119964842,1021707.5809871838,80360.286630550429 +4,-35559.2392620272,-168088783.30896798,-148010976.65889251,3912039.8129616478,1530285.688859011,-1466626.727782785,-79812.521137085962 +4,8158.6950809895061,-61800652.28395509,238317343.20135981,6055232.6103028031,-1875626.0493038632,-420889.68846789753,62597.20676325653 +4,202162.97261313628,-122930140.70280984,-182688246.05417016,1445471.0720432356,1879090.1024124876,-1055142.9057797871,-85166.75472900692 +4,239617.69203267759,38988518.413535744,237150304.31090769,2511253.594147122,-1920056.6333494738,452761.90442204784,76514.676199417037 +4,270787.3655638881,-223079616.50280577,-69402372.996921226,6841193.2491190741,763042.46144176996,-1882880.958367514,-58567.048032875522 +4,561234.5694453516,-162629104.29830337,187167358.2210204,8869941.6186791137,-1442585.2749099385,-1248131.5322069994,28143.00644068982 +4,592877.97844298882,-213301918.71803233,124395014.74106802,9509846.6281557102,-918346.93319941417,-1681809.808270568,2222.7274048056438 +4,748179.12404572079,-239851462.01530817,43801322.113406688,8860189.5733564496,-245078.08253687745,-1927216.4485032037,-26170.369065248124 +4,758204.3348741713,173188547.48094204,142314090.73235261,-3279628.4429937475,-1195444.1896578211,1748606.4460863487,71482.131984370382 +4,996119.41169322701,-199606477.46841392,147347543.70506313,9206248.4793887455,-1117303.9590530968,-1544589.6228626883,7779.0263057143511 +4,1021164.8378990432,126208584.52106299,-164921186.39349279,-7140126.5923302108,1786197.453825776,1413355.0904477874,-31631.518875060494 +4,1255414.574490115,105183787.56633459,-179905797.17392647,-6708893.3925999114,1924231.2925225103,1205074.7309366385,-37062.799594997145 +4,1285934.7215993041,-59054478.362006381,236163199.23388195,6347750.1649159519,-1912411.5625893169,-360443.67320543027,52467.836470319649 +4,1333045.615201538,109043566.35051669,-177484270.50287038,-6768496.7546334649,1898748.1138225852,1246715.7374410352,-34516.464483653312 +4,1357749.6773919091,53480123.0103539,-204028922.34504053,-5571252.095019469,2139101.8385822251,682577.22428820829,-52449.973685025841 +4,1436352.402529336,534581.54034487519,238853785.53669214,4623651.2608445734,-1979700.7243212247,156328.64499714036,62939.13372099767 +4,1565777.5461319904,-193209791.01915368,-131403826.12014377,3128044.5137495692,1284867.4753293169,-1573946.0579631913,-68975.194518199802 +4,1769003.7600121726,-232018291.75828317,88154704.252040163,8373712.4176089223,-641505.13176462939,-1794511.7533947993,-17704.154675958456 +4,1795956.8823080864,-129520132.55379656,-186477078.95376354,-76753.345523766213,1820306.5371998276,-1030494.5117031734,-72263.198703853646 +4,1858996.5446232599,-231380261.67767501,90374945.491704047,8297572.3997528953,-662564.90754883166,-1785093.0737089992,-17506.193039309655 +4,1910128.6727151419,140697311.03299507,-152659992.14966318,-6991334.3672346566,1637124.2778829783,1584877.0528332375,-13215.813416117629 +4,1921825.2573348898,167283808.74759382,-121780196.53223294,-7092255.7183375917,1330012.9478909795,1858804.8692924972,967.02861393276555 +4,1933220.1043110229,-244527658.58833551,34656873.218848124,7443919.36309864,-197556.083460097,-1906032.1547361074,-33262.397364809323 +4,2008035.1489841179,-199027435.82240194,148983488.77356815,8444658.6246469878,-1160029.1760144257,-1507675.1073270806,707.58347723423833 +4,2145109.6278560166,184848709.57733524,-92479412.1695963,-6778590.2283530366,1027546.0160879821,2044180.0978659,15032.737457352518 +4,2240103.331075517,27349774.330721557,231680284.67806891,4095010.0190459476,-1991813.2863950201,418199.03846381704,60087.691186072429 +4,2269749.9807109777,-168422218.14042926,181114368.33604094,8078905.6839130167,-1447198.776831964,-1251407.6790502898,11137.502429530943 +4,2326398.4397361521,62176114.654211849,-203207736.36495066,-5804816.5634062197,2085633.1776071724,789343.13441156258,-36364.32015188456 +4,2439374.0622833427,-66319713.051445641,231006592.10003453,6470894.989906556,-1932200.1804417146,-400329.77132095478,39248.831863987507 +4,2451545,208048078.42127582,-2007244.4472496153,-5156305.5956591172,100455.76008866659,2271980.8102510222,45126.399826216199 +4,2460000.5,-98564071.848701105,221732403.22500572,7064801.5036379993,-1833711.570115159,-672400.84202191024,30887.790410253005 +4,2557666.3751890045,-240496730.7643497,64381844.269567214,7114372.965350586,-466704.18058366311,-1841268.9413497695,-27570.781171132265 +4,2606665.1170293968,15592888.290926484,-216336732.2774989,-4928920.9890018888,2161756.5604991298,333670.03026143875,-44100.603430082017 +4,2619507.220755944,-245323120.28657204,40330287.129102468,6634067.9255712256,-267357.68066366285,-1883586.5210862635,-33416.597489345237 +4,2688874.7699650759,-238526762.68578681,72116258.796344861,7047682.445782789,-536034.25556678895,-1820408.7917058158,-26060.640346502041 +4,2727265.2162081916,-150676509.35689628,193802873.44658124,7554862.5016626595,-1583823.7973228742,-1101146.0247081241,13013.074696313173 +4,2789075.9653556263,-121891465.55829018,210637681.59707567,7213143.7270578388,-1745004.002549578,-863848.56230226334,21039.248070943446 +4,3067331.9864320979,-170252480.79880112,177667894.17060637,7327098.8152405797,-1455553.636966289,-1258350.5916119174,3543.9441820254451 +4,3073813.8175774403,18546458.581311017,-217515954.48926196,-5014153.4279024564,2142442.4083032375,369260.09795311815,-36692.093373121876 +4,3080166.4188349219,197427708.26131457,70998806.988956735,-2587048.1992779113,-651830.41268492874,2160656.6285826052,59508.526935201175 +4,3083588.6219172869,203927995.95478776,43002655.35942816,-3312481.1960353069,-375197.7613862849,2239589.5248804591,55444.87247716335 +4,3133271.3294226751,-154401881.79075933,189998188.71105036,7197364.1191732436,-1571082.1597107768,-1129178.9268965526,8044.3986223221427 +4,3161301.256607159,105627276.18527241,194240628.91277102,1996074.0516700703,-1786337.4291827262,1191567.7149535683,61528.156767490014 +4,3177250.9576433105,-173991374.38358983,174042204.85706094,7210176.3045587149,-1428940.4309275448,-1288178.5067684613,1369.1735029256788 +4,3208473.1149532385,41795974.648254313,-212704063.31616586,-5360491.1148544662,2105693.1286452478,597369.52634886908,-29291.938617923512 +4,3280521.2034474742,-131724019.0614903,-192951628.17420617,-1537273.3666893907,1777396.1466605347,-985974.04496981169,-55626.312821915359 +4,3437818.024637864,-167593308.57549387,-169200114.05120161,-490197.79010967904,1529582.7179996276,-1277089.0820872127,-55400.635850889019 +4,3478048.6374966232,8115987.1561161149,229325473.10862294,4714987.9013506118,-2052193.0706402964,269918.78383661952,43162.336352575716 +4,3572999.510567978,-229301599.04069126,97375559.362511665,6102824.992138586,-782701.97615889797,-1729710.4235703917,-22788.254001066161 +4,3607022.6735171964,196388791.38634801,-62659190.374923974,-4743829.2095267558,672429.11267649091,2193050.7706047543,34626.366964322937 +4,3704275.0038312087,-248457705.36590779,-19008038.811728392,3771116.2990650469,190413.19589002826,-1888383.2859551229,-42954.269168022824 +4,3773929.1366995303,166565373.15997213,-124759467.97612466,-5340261.4658636004,1284639.6792667359,1876427.0343798408,18398.378275340121 +4,3894114.0626205932,108373464.17664041,-183664639.77730158,-5518868.0480814762,1827273.2022410231,1265974.8453687564,-2025.8814385663104 +4,3921416.1735839089,-213676243.98920044,-120066357.17180102,785861.27353925596,1048035.9906308226,-1623754.0775517104,-49948.759563697378 +4,3968634.8932288503,-151453526.9506112,188992987.52347896,6208851.9577132296,-1614127.8532289285,-1107614.7108196418,1331.0899806067864 +4,4052277.3809994301,163765680.20772663,133005535.88004608,367396.04837316606,-1303363.6726546281,1828490.3029075372,56724.972640585715 +4,4086551.827056977,203766751.58045053,-28470817.070660524,-3503155.7701395042,306071.94409760763,2277520.3233998707,42564.339579160354 +4,4109040.7027512658,-91828329.168722406,-214343787.67728952,-3108909.7237971867,1939807.6525719077,-621002.76740466314,-40256.810727165197 +4,4149639.9925093511,47397768.149022974,-214627314.20773169,-5061837.459084088,2058024.4886719643,655663.26233058062,-15160.480734501753 +4,4228945.0916803954,-49786845.499193393,226203305.73194665,5276433.0697981231,-2035908.1316343823,-246652.42697444936,22217.805062759526 +4,4240757.7177511035,-240218008.03604192,67206642.812057897,4565587.7284239223,-555689.64569266723,-1812741.0184406838,-29515.614932390388 +4,4253107.0509286588,-229163086.12664083,95993391.406672776,4983051.0108596357,-801254.47823111422,-1727542.4333721998,-24541.167075552425 +4,4328687.1229854897,-238062329.48582917,73650835.58599183,4518375.1565164467,-614203.03627123334,-1796018.0761602418,-28461.949201354942 +4,4558065.1688616257,-155761565.00345182,183401989.63957191,5365617.7249617381,-1601140.5712150102,-1150130.3731262079,-4690.9723865180013 +4,4564275.7312309472,-195647693.17926562,146789256.09194762,5082398.1126858732,-1262144.4147476114,-1469482.7672737692,-14821.896022544866 +4,4673454.3682588516,-115615556.76646952,205918658.50168696,5215037.782782387,-1835792.8883531445,-819598.43627948454,3309.3080079929223 +4,4693409.7711070431,-170914827.21919313,170912017.83234683,5084392.1393258022,-1491347.2466726312,-1274736.3289692672,-9274.6478493377399 +4,4693803.1919051223,173353556.17588347,-117054442.98368001,-4064118.0724652847,1162491.3069806744,1942220.1566946069,25616.527051451092 +4,4801490.3102042498,-139040748.45261738,-196581916.79696554,-2413630.1409350522,1695746.3983042846,-1002628.3320122008,-35520.97355999711 +4,4868308.2020280585,185648989.22746027,-94113488.663532436,-3491046.7868313277,930482.29450720758,2074922.117726079,30674.123909831684 +4,4881001.202090512,-225911960.81155404,100075239.06486316,3952268.5942599401,-866949.29458961089,-1708671.2778504067,-24338.594474356854 +4,4893365.0687342845,-224406401.41613144,102964779.31621125,3972232.668197812,-892548.64576674963,-1697152.5735995998,-23894.758066983341 +4,5014098.8058685018,82846129.292859972,198665533.24683085,2985480.6814731816,-1957286.7758963373,1012793.3370014146,34912.617717587193 +4,5043839.3714237846,-242540348.77329808,56079293.857993558,2991133.8987358431,-497085.8418987242,-1834329.3519190084,-29708.38022474806 +4,5064945.8377033202,61967837.357897952,207982070.32404724,3316684.6697251485,-2033505.2143373305,804189.09550206037,30886.388075820843 +4,5073893.9643627144,25842010.991480954,218886957.75555208,3799021.014450442,-2106467.6889360938,451709.46374686889,24875.68870328688 +4,5081856.7743789777,15020227.608504133,-226519459.58258319,-4253576.8964984454,2064202.622054063,345310.09669225296,-9873.8552434515404 +4,5095919.5884942086,-62978803.021326266,220203527.81035414,4499469.2222252898,-2041152.2738220629,-369616.66958913545,9076.6381779773765 +4,5206904.9168700986,51281029.880297564,-217944318.36209565,-4248995.7476576278,2007634.3069349073,686274.16328875092,-1884.8184700912113 +4,5242087.2074043704,200982699.49994895,41567630.857829213,-633009.05130108853,-457474.28034418903,2257566.7619875916,43107.455870725258 +4,5283235.3823237317,176008114.96242556,-115008936.44678837,-3172707.7410859959,1111201.8080244993,1959806.8799582438,27149.704883956867 +4,5284761.4890505355,97768561.470396757,189090524.28486249,2681609.7793860463,-1896071.9392995874,1168179.6083522923,32915.411764754936 +4,5302649.4996310193,44669608.255684927,212961865.55448472,3438547.4148120712,-2086499.5055291164,636005.73706616473,24531.136773175032 +4,5313691.8498331131,-62207448.707705207,219271195.94744432,4222169.806689851,-2052219.4969662167,-365752.03390596533,6656.8243937273965 +4,5350965.5230770763,-248486556.5688425,-33193758.293741483,944788.35003149719,239436.10105617842,-1870431.2292456739,-33854.974216671893 +4,5372224.0600400986,-246269405.58313504,37826013.498467259,2124815.5567590259,-356975.00636240671,-1864728.747637918,-30004.329694594195 +5,-1904201.9819930857,-64339276.971482791,-780686534.51544917,4208351.5555788465,1119263.5347208118,-61079.314629761378,-34359.146481495132 +5,-1898841.0148342673,751404532.51988864,-92634129.523711786,-22912814.951869972,132165.40707341296,1151775.9502748901,-7356.0711783350889 +5,-1751018.9153907483,585445444.15300381,481330802.21205556,-19216812.324838057,-721451.54168747354,906035.06711363548,19639.521568159234 +5,-1732093.7990164298,-790851126.15540767,122230258.14845693,23842830.481048014,-177490.92982140894,-1082343.8192493964,8323.6774134731186 +5,-1691548.3053922951,335522081.55716783,-694462319.84976494,-8397066.1354587972,1010601.958203367,524091.34847443039,-32195.089263691127 +5,-1606249.8621290897,-796062172.61890113,-85244951.19318518,24339258.021243166,114718.52422042993,-1090376.3200102744,-722.02243263368143 +5,-1583060.8169025772,490318374.14756674,-588971254.77905798,-13369927.568018081,861776.08230656025,754917.22674502898,-27966.359962414139 +5,-1569513.922424966,754725275.79895854,-30829626.189287242,-22748416.093460958,41337.279195872477,1161286.5549381475,-4115.1124328307915 +5,-1566750.021068665,-554218080.62869751,-571078692.29954028,18170053.185260646,805341.7335238622,-754033.20628089516,-22493.139665572988 +5,-1435621.7446177653,600056860.96676874,-467720628.61418015,-16895779.735247981,690335.21296288085,924665.04648542672,-22898.329435052961 +5,-1235318.0336190355,493286261.57893497,578646728.78505421,-15848337.002399117,-862766.53694504674,767232.10852864827,23867.690651500958 +5,-1155888.590707452,-784388524.19850802,158384188.43363023,22768227.457344994,-227765.79416905943,-1072951.6583633644,8975.2980357361557 +5,-1074445.7947135253,-119102959.42722873,769960022.02033985,1881588.1913620725,-1118562.0589360835,-137745.71694928023,33030.100742584917 +5,-1071924.0117630269,401825611.97365403,-653581445.71057427,-10399798.90607059,957855.03856044682,626275.33479997306,-29340.590991776087 +5,-1045852.0499942055,472115133.6409232,-601561599.17684746,-12546689.395300023,883924.5862959543,732682.31323958386,-27341.060918385137 +5,-813491.59623260843,-802664652.15517008,25623226.726280294,23018379.754214868,-38611.630778637344,-1092124.5981915232,3205.3130991672888 +5,-729888.82331964793,149793716.25280842,-759858772.98007035,-2827705.8691639509,1104391.7362984039,256531.35878706392,-31995.396439023138 +5,-716867.51020104205,178325333.44722351,-752967600.1187222,-3655426.1930012438,1095350.3528475219,296805.82769775274,-31787.515881298579 +5,-715647.59991219989,654368018.03416562,376467372.77056682,-19367592.897474606,-566111.32867014338,1015586.6382747648,14211.492749694266 +5,-673893.37115940941,-214837415.41676509,-756761345.65472269,7518887.2291366123,1082764.799349288,-271653.96264339436,-30241.264219958797 +5,-554805.63565502502,191004565.5391463,743960359.96024776,-6715750.291923332,-1098069.0020740614,318290.46381181531,30352.302788084748 +5,-495360.67746518832,651748592.06917143,-384503541.90768188,-17586200.26676609,570569.79682600801,1010011.5171660467,-17819.603954981612 +5,-440204.00122822216,-489481746.2115702,-627262421.90003085,14797044.262317555,887123.26509406266,-655965.13933037268,-23575.492556596721 +5,-388189.25410126615,-464051033.39817679,-645110531.98001671,14052890.795483669,913956.20058595634,-620781.06298334757,-24278.894727036899 +5,-385083.64839778002,-594387865.81725836,529999727.88156658,15562308.733975213,-753645.41643825173,-804532.42914278782,22383.13376571807 +5,-358083.90880132793,-641352707.27926147,-479096698.0578714,18637373.993938498,672220.8078025562,-866302.23031143274,-17078.85450919902 +5,-283286.1598915346,491681646.81882614,-582245272.44786334,-12524175.402104903,858098.62734534696,768950.90465376119,-24983.608176052381 +5,-245873.63921184954,-804492167.79022658,-30863752.461990997,22155809.284676466,38694.067163635445,-1089603.7326662387,834.06614731717218 +5,-209457.09095304389,621275004.83212531,-432221835.24432343,-16269813.147976426,641635.1721022639,966292.38174387685,-19275.741976068639 +5,-112018.44509654678,-669406230.76344049,436456987.76838666,17435353.1772856,-619927.30432737537,-906887.01813135319,18478.156089304539 +5,-102472.36373361992,-653169516.36235964,-464970824.81078398,18577840.891679544,652008.5646997263,-879349.97982828843,-16148.644414455242 +5,-99736.120707227383,11818293.137465144,775004939.32713544,-1698079.790396078,-1131430.6856078431,57582.602160711525,30627.194137067614 +5,83200.54146268894,-782958790.93365502,186009111.16679057,20618418.291010309,-264600.00035590632,-1058127.7943361795,8949.4637621349975 +5,95244.040576192318,-751311.73674593202,774480805.30228877,-1350551.1243100441,-1133189.7293812302,40718.094747011441,30223.713481683524 +5,123990.23325677938,499075351.90428776,-574130161.58498716,-12289465.28325198,848767.8336320488,782169.75923039613,-24034.669184797371 +5,136242.52100228099,-288356884.77759951,-736303151.25866282,8999263.1649599448,1046545.561759355,-371087.76796340884,-27245.722219043131 +5,181883.02858823352,14359447.361526906,773965045.6550436,-1791351.2348138571,-1132863.8763579931,62874.910349031423,29970.479268627929 +5,229450.68301810557,110440316.54650334,762675190.64699185,-4322423.3527618181,-1120930.9312308547,204826.85637088394,29258.182220001698 +5,265460.09844379639,-795636925.34724891,-133718642.17005754,21218904.442112062,183432.57857801716,-1072189.4831739685,-2852.0627759420149 +5,345586.57464146661,740668871.97880805,105677089.86548454,-19576838.785966877,-163120.0907993732,1161573.2785458581,2123.3720939474342 +5,462435.19693712564,746686430.25710475,-58723302.109363355,-19263798.800255157,84128.184741404286,1168653.5917149757,-4395.1316817460411 +5,481792.01034935005,-782319266.11377501,193812920.41916901,19897588.429023258,-275377.28626144928,-1053586.4039799443,9145.590725628992 +5,482875.55279528117,-261856345.41003299,-745102688.96131516,8207238.0998129398,1061202.9617314308,-331901.2575135642,-26857.550272049248 +5,519825.74351276457,8603841.7151749525,773901759.58867657,-1728464.933572402,-1133233.6497897904,56705.620680179505,29152.074164627997 +5,549075.74931576476,745312029.24800122,-68329725.051243931,-19061472.839494172,99361.32368875372,1168307.7404084129,-4846.9507797229726 +5,599396.63660645671,-659790245.62942505,-459771452.80971122,17830590.487262789,641096.53745544527,-883307.14225492848,-14676.09647737341 +5,601589.80602470925,560252316.56881392,503249615.387492,-15365874.604142649,-759228.80538609088,884393.52063656878,17697.923581686016 +5,661836.77107478539,743853328.63624394,72353531.889993101,-19103342.805950243,-113806.0275216954,1168614.2948695789,550.79207858885877 +5,693016.53723225137,124373629.2273455,757885618.33342838,-4686225.0028936639,-1120336.8860530125,228651.78334103143,28024.329668679973 +5,717468.42751627788,424061557.22699189,-634570541.44216263,-9484354.9063485637,932802.01385951473,673484.4556662509,-25039.829253229822 +5,796913.72088084603,227215379.68822926,730144569.37349367,-7258732.887218534,-1083516.4783907968,380187.76951170852,26560.132889336892 +5,830337.7069701124,599233186.22876632,-458776220.17006671,-14105475.733832138,681225.14392955555,942813.08994334692,-19155.56163675405 +5,850000.97752942704,-765203646.15865648,254779238.41448009,18673392.251310777,-362032.46856134071,-1026126.167847792,11302.792381368994 +5,889418.17472263752,-783456896.19307077,-201259834.69820121,20060772.399275213,275439.4871601616,-1049333.3581504463,-4607.764268791756 +5,961431.51358162053,343397061.32869971,676731303.4122417,-10041777.606854122,-1013083.9391263033,558693.63022203406,23948.525713068222 +5,1060125.9908807157,692547330.34750319,-287230686.88635457,-16439736.020390244,426636.2812703806,1090109.9365407142,-13023.22956761977 +5,1106175.5025935918,-736814134.52915084,-334037162.64121807,18914706.106358524,459990.16956345469,-982455.35419050185,-9032.1857615209119 +5,1231220.2172124833,-749692930.61172271,298434936.2630024,17513301.844537292,-424065.30595957371,-1001491.3212313921,12804.771310082913 +5,1325203.0652477555,495882476.10260111,565367599.50047672,-13423188.586846462,-855613.84938403976,793858.3124037591,18656.636274951317 +5,1338852.4821603564,-198123158.33272097,754456670.45432365,2854074.3031801819,-1100500.3129677826,-237809.80843760524,27168.721622369987 +5,1402922.8018730795,678590137.08114433,307484522.06712419,-17121336.440657284,-475039.69394000113,1077783.4743153332,8593.1029077111634 +5,1417327.405077422,-645249758.6368798,474952788.24899638,14241461.560368815,-676885.56937270542,-861644.76556649583,18531.277314992512 +5,1427449.1138293091,-186846837.44681019,-768580700.12759638,6524952.1435628552,1088492.8723718221,-218382.41242866134,-25553.224755150688 +5,1502838.3021728816,503854616.02550548,557228695.83230507,-13561363.624697113,-845105.21228954999,806692.27160815033,17934.28110420749 +5,1504854.7316032085,-697652501.19165611,-410336935.43087333,17780520.181407351,565043.411461727,-925404.54951629671,-10922.461900982888 +5,1807066.05181049,-491048373.19378716,622723515.22634947,9491044.3529208582,-897050.87022865401,-649083.65648260654,22977.273795670611 +5,1828688.3610051037,-452677053.907386,648781718.62161517,8472252.9852664471,-936494.93277720595,-596002.76495206472,23710.63818142486 +5,1853232.685172698,742786560.6907196,-21201380.469793908,-17197269.631125189,22317.005195885999,1180513.5798221,-4320.812591088039 +5,2015964.046858497,-760626107.69551194,-289683222.55776817,18477312.282969803,391907.39706823113,-1004138.3235384589,-5577.7848669330751 +5,2042215.1149636884,-616721709.20649278,-524416272.54064661,15966089.84560648,720976.00339054968,-809583.10881584068,-13750.005579674567 +5,2047885.2355123297,653131980.21134424,-369585069.74073565,-13706055.463620845,544002.84978624643,1035370.4676837435,-16074.105500258194 +5,2100710.6349458443,517628541.18142557,538669429.22167027,-13762070.448704878,-825951.72568549018,835352.01620298333,15916.851921664767 +5,2121300.338518301,479694793.29176909,-590071710.5437125,-8830208.1885396391,864808.7224804305,765092.42814903904,-22512.046312861879 +5,2203117.2804930164,-45429672.307900295,-785808461.41882598,3971179.7941134558,1115624.8867915631,-14246.031245263195,-25280.987524525986 +5,2227476.7784229773,-615121087.47825956,510572609.55132121,12007433.464931509,-733718.60292406788,-817308.69496112131,19704.776369205381 +5,2243525.9697822123,636032699.65757596,385092716.12350368,-15858956.716025896,-596777.43386097159,1018017.7615052902,9641.5392324780369 +5,2330739.3062638193,109701920.1690699,755352968.94923449,-5425639.9841751019,-1130183.6403220084,214960.52717611918,24611.302702177873 +5,2334270.0554389013,730277258.43783498,125662184.0473599,-16936710.584921613,-204190.76088895759,1166081.6530197605,33.988555410024333 +5,2358028.5104839718,-812008305.89386344,-73239751.70746173,18559600.885604732,87824.983855072554,-1072574.5346703373,2268.1870410817137 +5,2374866.544226788,-677066347.18746507,432048171.79515052,13497281.878111588,-620267.8946011638,-899949.01499818836,17527.162209895236 +5,2406481.9522091486,-327790343.92186385,-731056866.90672612,10318384.746013584,1016371.2784194491,-409363.81651449442,-21160.598696722573 +5,2437523.0593971862,429763322.67289937,-631231275.94788611,-7033482.9327899357,920665.84062352835,689227.13388509746,-23458.265820751923 +5,2451545,598567813.76204681,439604585.95339614,-15226839.64867932,-683409.35478732095,963890.0868179244,11306.410187360887 +5,2460000.5,707285134.77971029,219379856.3466478,-16735540.926316492,-347920.6405507329,1132391.6848131875,3080.4666150679236 +5,2486786.0300501743,31190544.34160918,765255141.84576511,-3909885.0409400463,-1142315.8323879293,98657.430811620463,25105.503395589465 +5,2495163.3148093577,348564136.92196006,665953858.17141879,-10599393.619298594,-1015558.1928091808,576925.71228521853,20227.73383875638 +5,2498064.921851078,313369129.43447959,-703716243.56231177,-4013210.8180206493,1018005.6372780193,513223.1423573466,-24883.101066027393 +5,2510926.3079226725,170569271.34094128,-759324992.71103263,-567033.6514566642,1088108.1312155661,299614.4291736579,-25536.144025766698 +5,2923786.3514651647,606583867.72662294,424393477.3500244,-15358125.453585537,-664095.02503654046,981421.49868536741,9594.9536889114206 +5,3027781.5188141204,594823673.09178257,439773751.29439569,-15221678.722191397,-690426.1406432119,964142.49321150186,10014.66799081864 +5,3118001.5442348439,627936722.20297873,-415654349.0416773,-11369171.06054634,603936.55740902608,997075.10352335509,-18468.167202365152 +5,3139597.5332193002,583563306.57252288,-481392549.73000956,-10015510.924463313,699302.44810106384,926203.33978524629,-20179.035784077583 +5,3223311.1541780243,49599565.221642062,759655093.61784136,-5320069.2641881201,-1146398.2175054448,127836.20544347606,23997.386234664937 +5,3267010.0717140138,-365867295.00671625,691591701.31015766,3955840.9384244783,-1017574.1373311263,-473998.26256381173,24591.072492326355 +5,3303495.1292049671,-158106015.93722278,-781418964.57087874,7888403.5880859783,1088366.6136474812,-168285.75239210614,-22449.531103880592 +5,3308225.8886620579,278017256.56654727,-723193931.61295307,-1821958.1320972047,1035834.1035550857,460735.47833917168,-24925.3671761757 +5,3344962.63423834,-337507968.03142864,703470938.97134364,3149194.9233510611,-1039140.3788673934,-432518.04908518208,24859.680564203562 +5,3416106.4454053668,-192949851.57145295,-776160967.72275126,8790121.8279623836,1076160.708068921,-216706.98012126292,-21788.330037834749 +5,3496702.75722194,-433132584.50148046,654315322.27301025,5218438.0663591661,-964277.99383829371,-568433.130197049,24126.902036987398 +5,3711828.8837918751,730822229.17358983,-131136382.09966142,-14701257.184563423,176694.93337357772,1168336.4687480058,-11480.387227443469 +5,3736885.8356647966,-10093895.731266193,-793514598.82638633,5525619.9788188096,1105758.7863239835,41013.644857821928,-23817.904236740484 +5,3780369.6095781131,163045290.65667036,-767677162.52222586,1750275.9259097984,1080921.6634246553,289792.92766879126,-24952.711340618087 +5,3820573.7092334693,643409127.57305777,358869020.4247601,-16132244.531108947,-574122.46907941485,1042878.7122507328,5039.5845067306145 +5,3836314.1055572927,-254644013.4935649,-763633150.04959321,10670295.160539089,1047885.9417159081,-301435.3856067867,-20165.240027081876 +5,3867169.9948296491,314810588.70014244,-709340787.69685972,-1735540.8528347895,1008771.2107020781,513198.7855522483,-24970.342931510913 +5,3876709.6657258002,722409265.44213629,144586737.80660152,-16332322.689101394,-246688.41034722794,1164629.9627487061,-2892.2310674086684 +5,3970788.1550345961,-79569649.921614617,-793311915.46849442,7376609.2875085836,1099810.4033548036,-56292.268128061856,-22917.070541095829 +5,4106093.2560905134,729648166.27189863,-138321607.14670557,-14421857.333484152,184273.87125445899,1167046.4932011713,-12650.673307985475 +5,4113061.3243086124,-702027712.64754188,-424556051.66321594,18066684.939094331,558089.69138685416,-911999.29079546116,-4964.3950608445448 +5,4239719.1714530457,256470482.90662622,-737410902.81673598,302325.1048632313,1040613.7209190927,427442.39836026839,-25337.357872498444 +5,4305182.7023277488,650398029.32829106,-378374062.43675911,-10776392.50327451,540578.61272245378,1033821.8185729812,-19623.255188450883 +5,4418854.537258558,323086485.2410236,668823113.53769445,-12319724.811019238,-1046303.132541917,548104.17261016706,17676.940810932265 +5,4505943.5836811168,-167883405.09820303,746058434.59825671,-2694787.1579090902,-1132112.5642111246,-192065.09320175223,25580.112423266746 +5,4730606.7309466451,511013631.09138918,529391123.28515691,-15557351.111027595,-844362.07610302919,841165.99302184803,10482.439051546717 +5,4812114.3121562004,660293782.44991851,-363707355.32110345,-10744376.690611646,512152.21308364219,1045856.3509869148,-20351.55610126051 +5,4847799.1757214339,312195422.20784265,672493623.62804604,-12771338.690886471,-1057837.3352243493,530228.56569992215,17656.892110978944 +5,4995518.4465233097,-155897737.33874846,745068970.18900084,-3697534.9127593818,-1140028.8471957017,-175660.43274927465,26007.580256876874 +5,5048080.5737637086,-684486022.45706999,407451361.80352765,10754547.019270863,-611979.3612400759,-916853.0990863099,21851.658416875685 +5,5051713.6093770387,-7780399.2422511913,752534130.30532908,-7041560.6356284199,-1165618.5120287908,43163.706035028394,24519.289033850597 +5,5165178.3518149359,-746475032.32346952,293373135.65350407,13128381.10676262,-449365.36426387052,-998040.73537183704,19415.292204516467 +5,5252654.7619642029,-626252046.38919687,-533328660.28336525,18795285.708003968,696486.65368292935,-806917.28365158336,-6937.7273175807468 +5,5262978.9095719485,739177925.99285686,-62120917.995255433,-15283815.798438206,58776.273438949094,1181259.2258242334,-13064.328583609642 +6,-1866390.8217137121,434957424.20288062,-1371239624.5248375,21087260.3848074,764508.59736696805,313834.80012566946,-20792.525678450758 +6,-1859478.6381289475,-1514717694.2359796,158949871.51638898,24608695.529594485,-120710.37298076559,-768902.59363829473,18596.400497651179 +6,-1844776.2508684718,497566082.00677389,-1345425517.7512081,19435108.627522182,750652.9014080998,351000.65846806124,-21405.613960176412 +6,-1760056.7064287546,-587253218.92753422,-1401198893.7557724,41176554.803894207,734887.43855875777,-263818.23023188673,-8192.9652267861547 +6,-1757063.7560016271,1303466319.2603393,-307288833.87544352,-18040616.476250134,156153.05175684579,872866.30971536785,-21714.316750928916 +6,-1726712.3684808342,251619195.27961913,-1442955107.4551268,26236739.130300041,788153.49791063997,199968.92787455959,-19347.416967708119 +6,-1716568.2971237851,-236357235.74780551,-1478464293.8435981,36388966.210866548,789342.00393309095,-74354.233697058735,-13463.423523327043 +6,-1690423.0185570649,488774862.98732996,1239678762.2682617,-36198127.832717523,-813836.13211234123,365188.09923748969,7796.6206889975028 +6,-1573085.9841105109,1123990253.4118748,675496227.93917143,-37199977.124720395,-468540.22376975749,774819.85596558906,-7746.8430397397478 +6,-1263449.0320235414,750226300.67542279,-1214522226.4924293,11459587.851506775,672998.74054133461,491942.45595003921,-25590.948008735886 +6,-1226707.497961025,-450054395.74093288,1308866853.0073655,-20061680.235176604,-832273.60204254847,-216547.87394546362,22903.033777445711 +6,-1162880.3080536649,203408451.44514182,1320312498.4671674,-34637849.892715797,-868930.60147166403,181513.20187041772,14850.243710799468 +6,-1148755.5098150419,-1495211698.3406126,-321284266.21870148,40120561.523381241,131575.4874189547,-763545.61343296897,14600.66637097979 +6,-1106835.4668482002,-1363949225.7343066,553176239.23858249,17536216.732444763,-358223.61735000234,-723323.18172488338,24492.786830562498 +6,-1082455.5208879597,-734376274.7558558,-1347809710.3301885,47260473.160918526,688791.59334001911,-350875.98217699805,-7326.8308943604243 +6,-927631.45386266767,1205048235.1590066,539725593.28678071,-40455651.042364277,-386920.41619347775,812132.01276242675,-9894.3198717975665 +6,-877347.41445744154,-180967825.76020846,-1499856630.5945344,39149185.171745375,785723.43701671145,-50811.64798277941,-17200.754022370147 +6,-840396.44537218194,467674914.53172177,1237943185.4546134,-39870812.233078025,-829359.02831024176,343984.50714218442,11524.955808850671 +6,-810329.23162478977,1305367703.4169533,-401541292.37679136,-21600647.542407986,197322.70425637264,847632.96929911117,-24450.990242126136 +6,-687751.14282282605,-1086755689.6407859,919967855.55532467,5085231.6764405444,-587084.52931785234,-595611.67356598459,28302.148859007859 +6,-643186.1244373396,-1491256282.4862952,-218434644.65554279,42024041.856647477,75930.883835267799,-783924.22756144451,16539.529505837396 +6,-630515.77936757822,-670066570.35070384,-1376040509.4105613,48941865.488606028,705937.29373142403,-322630.2360665258,-9926.5465361154638 +6,-617305.57807758474,1089264344.7811828,-899645414.08127499,-5929731.447102543,487192.98214833596,687725.13308354083,-28301.528714980377 +6,-577549.09580652136,-1160356594.2081389,-1004375321.0521135,52764498.584260248,501117.48175989941,-587184.66726492904,1322.5020724227506 +6,-213398.9064255436,-1459808407.4746544,208198906.95666957,34522074.425486192,-167472.22665997097,-787013.64008309459,23195.233451091925 +6,-170731.06577115227,-1367038875.4305193,489989868.19245678,25651884.223819438,-332497.96987691137,-747169.17442801001,26746.459050484493 +6,-108600.59201788786,408082667.02235222,1258247693.1157315,-40932650.756618097,-847940.70653183467,295558.08333156811,16436.998561988974 +6,-97966.832574080676,522450457.44672275,1210306852.0982268,-43044438.111184426,-819606.56811212143,367497.37868099514,14005.429096589938 +6,11429.791884967359,-945032766.02073908,1023488272.3577775,2616499.5398367099,-664501.03761099512,-533017.93256740412,31456.718855877065 +6,12083.255675736815,-1297733739.09149,607004391.81222391,22472969.761937942,-405149.5220203005,-723183.76977945596,28596.361266410895 +6,199547.48400193965,583916014.60497105,-1363749229.6501644,14902216.377976239,713468.88804659736,361004.93557045131,-29389.436235059864 +6,296654.12680528732,753217555.05407071,-1272028065.1229634,7174370.3297380647,665497.06408961362,453288.35140305525,-30521.760670603275 +6,334408.16288149683,-1055628624.9026648,916097035.37345409,10438413.892215077,-600236.77989334124,-604331.27429361874,32223.387985905305 +6,351481.08160984423,1275292681.2719545,-614024306.29541159,-24208500.267293982,308389.98343188572,779875.04290455743,-27565.408293908084 +6,469339.65942255035,1097610669.5470941,-932929348.22167981,-12096306.136011522,488702.42502181674,664591.13659005065,-30507.586882343388 +6,524018.30496501829,1367183486.856636,-263633075.20332259,-36413023.473059036,102031.81346161176,848232.30841002602,-22787.00096340814 +6,748293.01268799137,613418328.35237169,-1357700313.2418795,11178242.788916823,708728.43501523975,367423.51221004204,-31279.093718615146 +6,753367.40139273601,-594725177.47246313,1222500645.3547778,-8671288.0412603486,-804925.11138455966,-341863.71340711659,33842.418550225724 +6,799181.70743448986,-1315022637.0706756,-725003242.95157075,59256066.112000428,351974.45937166992,-709140.2286987938,4656.7769092520266 +6,860858.31913313549,-525494331.96835983,1245927181.4336874,-10838728.841812719,-823710.69517735217,-301948.3855116918,33930.450846536209 +6,892982.08181411261,-387228439.20245266,1289684617.8530827,-16153420.25863234,-855203.53654873138,-220143.42134719237,33246.366926551804 +6,978446.03205495141,197582210.19479626,1316069271.1115358,-36017225.70726712,-881076.68677259621,143829.52004684877,26367.198502448195 +6,993353.6699233898,-1106589675.4130239,-1027399156.8214276,60147617.112195738,514202.91317971656,-596376.04277119215,-3987.2948577442462 +6,1036655.3987520891,-957390730.62041867,-1172989985.5190609,58488063.701895252,594230.78037097945,-512987.61335634958,-8694.5016443633067 +6,1086561.2430617241,-279615658.87121904,1314352575.8271964,-19591698.548860159,-869842.14501007507,-155412.43992498834,33075.913421135971 +6,1180105.4794519646,1348369148.2602186,-447103804.99946755,-36741682.921105936,209191.27950607927,809853.76495262829,-24911.752558797238 +6,1223204.7609312409,1360483570.8167379,-408081866.54741997,-38406606.636803471,185112.18070771985,815457.18937023962,-24157.453355133741 +6,1284245.9915559893,-862418328.57962453,-1247297237.8892047,57173439.642272972,633073.06018273695,-462385.87314841035,-12219.310711667045 +6,1448504.9452536432,1156366430.7420824,-888348406.53175271,-22560491.338832751,457612.26239189989,673845.96254450222,-30639.025312149442 +6,1484069.4404338067,235453254.28050348,1313806449.4889727,-36122919.567917392,-872939.73488844547,159925.13857072929,28025.151825245477 +6,1495766.9474197924,-584300096.75111258,1221562418.7261858,-4574581.2693006638,-805430.22333086678,-349144.69981621607,36312.340488350659 +6,1620599.8946597972,1129995410.6466405,-939162511.04716396,-22041723.498274039,479240.96452991787,650111.36654955731,-30985.03455634541 +6,1641351.3685647794,669161847.48082078,-1342795660.8590951,3043468.9740430908,693919.97348435596,380581.17397613684,-33313.516616119297 +6,1643012.963145528,1398526168.7033346,-270569868.58880836,-45828696.764030345,104991.93425279662,826666.59654749825,-20877.781063368053 +6,1715069.2824420058,-540724950.90337586,-1409093785.5504858,48710383.792863339,728402.23626314395,-292952.87866469339,-20978.155404332625 +6,1763098.8232346056,841624320.97259414,1058884405.3816522,-52602120.337997682,-703936.40569557576,526616.15128045448,15550.515080506775 +6,1827400.1911035888,990593047.29083705,927934544.06014705,-55529289.177301541,-620555.24415178818,618421.1867037192,10945.20956667154 +6,1836513.1061023553,1384762561.0244267,-368582209.50372362,-44603110.975158989,162311.43110675539,814599.37520940613,-22270.15391172071 +6,1941102.2910224674,-448640358.60858202,-1444421094.1288261,45253694.199960105,744545.22962454008,-244844.02747172434,-23531.299856370326 +6,1966441.1271194094,1372050661.6243339,283638621.67560911,-57744708.741196029,-221405.42829274706,820782.80947883625,-7522.560229172057 +6,2170849.8782825698,1367379831.256634,300158292.14930373,-58656726.103295155,-228694.98907956216,817660.19969272206,-6351.5267442945633 +6,2180324.5865794327,1262280373.675102,-732204836.26991534,-35483383.713516422,369675.87869860564,724256.50811846182,-27809.377982761842 +6,2184050.7853907016,-289741250.28929716,1316385389.4521255,-13104746.618545424,-863910.52326579357,-177762.2715132375,36883.767466519341 +6,2217400.0708547826,-1076372850.0594034,852895146.00124168,26299672.723050032,-566961.83313453966,-652539.40522636415,34148.242291861854 +6,2287984.2296840474,1284876398.1909854,-700869465.56057429,-37772391.154221833,349469.6339276596,731644.05089212989,-27051.174815219427 +6,2307178.0159058841,-245698854.0827246,-1489197866.6936684,36604094.876084186,775681.13164569205,-137654.31517323942,-28026.897220019313 +6,2325669.8123510173,-1341125459.1465924,389012483.07776868,45834407.521699764,-279298.57627452479,-802866.8671454353,25487.440983541663 +6,2329284.2458710819,210080213.70624211,-1495064298.9974389,18645622.357850354,779097.7877641744,114706.90294943661,-32763.828189045656 +6,2448150.5838943552,610222141.4707402,-1366456694.2216163,-437638.78560847446,716182.83430855488,337863.86676851544,-34387.054852806265 +6,2451545,958385353.9396106,982855453.98026073,-55213047.494224891,-642126.59121650434,581983.5038886948,15400.757035783376 +6,2460000.5,1241392115.7833855,-785164747.02363932,-35750937.86114271,399544.17263938446,704825.93447314948,-28155.501489370134 +6,2475421.2658959888,-937222135.63248348,995432071.46371794,20149564.22072148,-652060.81283922726,-574666.01246698177,35942.350983566954 +6,2511943.9873975227,46073758.600251883,-1503571649.6350899,23918741.608388074,788478.90430193825,24289.968349956795,-31955.141382409038 +6,2562449.5705256015,-1369370336.227772,295920776.86919194,50033383.803713545,-221232.94999623016,-819731.42352738779,22789.61644662336 +6,2700729.6205745423,-416292123.16229296,1287960001.9587884,-4170380.4437568863,-836436.76127621334,-263337.60225568275,38221.598063273304 +6,2703494.4132060008,-1306402169.7459335,-615235233.54982758,63032887.179396331,311651.21988780366,-761652.41227427556,-223.20579713069745 +6,2704583.7827279167,-747717266.51374912,-1282154663.5458329,51243245.369908966,677209.7422859387,-428574.10804243473,-20478.459415843605 +6,2710372.6304322742,543516416.78940439,1247767542.1485598,-42420134.444875516,-807741.23399458081,327695.74948660313,27456.642403567032 +6,2802439.5499456478,42395797.156037614,-1497010685.905508,21902974.076864477,791794.9019849404,18416.391140398202,-32621.039056295733 +6,2890818.3357651895,1391989241.1966279,-417816834.69303548,-50843858.877376378,196022.10106996199,792501.9319376481,-20269.644433599191 +6,2969243.2904998856,-24081729.840557244,1355052187.1395087,-19411117.644522093,-875375.58112362155,-23846.74061644468,36501.3549026209 +6,3009057.306431775,1368876078.1503015,-525716917.9889589,-48790973.863728434,258621.84597134602,769137.28853588598,-22101.448953295847 +6,3015730.0175626939,-1220892907.3801255,-775939442.66903377,61999321.900297605,406120.69160341844,-714639.34136950609,-6234.4311114518177 +6,3036941.9426995283,-1326896851.7189121,-540901074.50562227,62901800.765995666,274279.88544427999,-782422.37467406527,121.88474645213387 +6,3045002.128133581,-385233980.43713462,1300493347.1544919,-3121145.390143584,-840721.53532025544,-245779.52175530308,38437.888307296031 +6,3074465.8207963472,1422579311.0437012,168234267.11511788,-61430282.661562517,-136098.57982679718,819644.65822574892,-6204.1818967315094 +6,3165393.0717011821,-1408619956.9685247,-23773979.54086047,59129058.358069956,-23988.985609702042,-842723.45938810101,12812.552402114365 +6,3344784.2432242995,837110652.10545683,1108216857.8092144,-49709361.691689789,-703245.88222348003,490048.5012143295,23121.035586207516 +6,3501949.98043379,198378388.14692733,-1476960971.1549318,9644811.0755550154,791334.72608331498,98208.245214774535,-34706.976057202824 +6,3507640.8887359872,-395711038.35826409,1304159884.0865397,845036.47681176965,-832059.73705399793,-253788.56475726073,38347.675862856035 +6,3644249.9755482934,1444580395.8105607,-170799241.47657332,-59451563.992932573,62222.598064641126,813578.33223971445,-12047.165823880137 +6,3696949.0124295177,1113892780.4127834,-979299691.42856503,-36418491.934878774,517668.99031350936,610515.19087700441,-28846.423303439926 +6,3707781.5093368925,1147222916.1951239,-939426117.67115235,-38320117.634960718,494653.00163421402,631601.61346879951,-28090.61582627823 +6,3714493.2065432733,-1378325479.1713588,-300761880.17620617,61950722.158808164,146853.60784577453,-830976.12075668608,2963.5989833152357 +6,3781197.2086071987,-199295391.7749148,-1460566764.5090077,24093031.224464327,795966.72193988331,-126714.17905631691,-32587.236751809218 +6,3895437.9284039391,-766285827.51497769,1135618857.3486922,21300022.582023509,-722370.98400119587,-481789.39581561135,35778.197211148487 +6,4100991.6683754195,-1322012580.4415276,413572352.66492325,52989243.907758683,-274670.96616548946,-812067.35249284818,19028.286753026805 +6,4175058.3375433059,-654231355.85097182,1207309720.0266035,17804503.521843571,-758552.48953159864,-412945.34364625235,36089.907813769983 +6,4230924.9921133053,-1372615403.7060406,-311334129.70973396,61484282.528562784,158035.81045020174,-830008.34559746773,73.830725022474041 +6,4265844.5237312308,222155610.05179983,-1461237931.4526742,2246224.7298459099,797619.93361752445,109430.48995085618,-35110.094296241063 +6,4268602.1343070818,1441109261.0439925,132187946.3773838,-62949496.517253287,-103529.51802005869,813367.03512996773,-2107.705576923106 +6,4287578.3774722591,391602008.66521305,-1427458401.8609684,-5463912.1506040618,777546.41523400741,204308.33635312156,-34978.866580214293 +6,4327766.6216624547,-1371458992.4066842,-297263265.34882122,61116873.940024383,150794.75948609575,-832631.14703269815,-82.883741764765716 +6,4392338.6874593496,-1371948737.0530758,-291726089.89255375,60970488.419127405,149784.27650280073,-832769.59060486895,-361.48640785435487 +6,4444699.9335893057,-1073067484.3123831,865009763.7796756,39971208.266288064,-546233.63648324972,-667075.99579799548,28065.746488824545 +6,4461724.9463671297,1429110468.0228941,-317559225.97796661,-59092810.14639464,157157.50729916806,797865.82595894393,-12238.364746964649 +6,4527934.6206054604,1051307236.5082774,955595076.69995463,-51357000.59278807,-583719.118846845,600119.78452144784,21098.967441013454 +6,4605847.7557078442,-957725649.28227901,992438293.6709218,34886867.234296359,-622734.24786651123,-597052.1324773212,30346.979028738893 +6,4668010.4919616645,926741321.73925221,1072542930.0469912,-45847281.077158809,-651139.62218722177,526338.83135938551,24813.388083339931 +6,4703284.846151432,-1245426175.3749177,600504586.33648169,49909957.542648137,-380577.81353297347,-769683.56349417358,20557.289757932438 +6,4727784.9500557585,-357848267.6305207,-1405959934.0301397,22884294.802985102,788278.02603201871,-225698.29607053305,-32465.431900336549 +6,4739576.6268608794,473229656.30014187,-1388133532.5860178,-12855439.019885821,769445.9341244977,250521.26942300409,-34231.964364934378 +6,4794976.3428191384,1371661687.1619895,-521719287.70887154,-55936362.927850291,277577.15063755837,762314.50472404563,-15687.423833910303 +6,4880783.7691027354,1291423652.4109712,-700045794.96778572,-51818821.739282504,379414.3454929383,715812.74193499971,-19456.138573985045 +6,5018985.5598869948,225822405.00280401,-1443235471.2758889,-4045994.0175215439,807778.06850961794,108979.61599843306,-34670.133026477932 +6,5063566.592538543,1239549531.9862299,-789175348.88027048,-49685177.986089624,431872.25583550363,685525.93612449302,-20735.274331460176 +6,5119177.495094033,1297465108.8223855,630588895.18199301,-56976520.053050995,-376706.81293589342,732780.09118314716,13596.546747428703 +6,5149579.5423255591,1211280513.4976327,-826465590.24527776,-48646699.886505887,456553.10228023137,671389.36014625558,-21388.909818224438 +6,5161516.4571719272,1449172283.1102784,89121792.405245185,-61585760.256967872,-64295.605719561921,814740.42000887159,255.69630788011159 +6,5220918.0492392303,-1349135358.7784386,-385190374.50747156,57963936.344748214,214333.87891707456,-820003.24577478506,-6786.0286876924083 +6,5268162.6090982864,1314782092.7775667,-642330860.53264666,-53674273.787196986,350669.74692549394,732391.43722500326,-16631.648197096267 +6,5299972.5642964449,1113389289.2914238,-953519159.41493797,-44534801.995792061,528591.93377435103,614005.42160891253,-23605.498125052061 +7,-1893910.4937293681,-157996955.13125345,2804608063.2553983,19058019.754973989,-598407.72795709968,-61460.98756484082,9380.4149902647732 +7,-1738250.9574883468,-1429003124.6766722,2360822358.3958855,36840037.486744367,-511900.8190334083,-332645.88438674051,6331.8145696369529 +7,-1658575.6817222033,2836258314.9067698,-1016475311.0189431,-51504105.54640644,189652.02666629612,525045.80957654561,5.5442863852229118 +7,-1653617.079335866,2461989240.7177048,1637370109.6230409,-30070231.5440301,-333626.06279161683,460807.3261920794,8041.4729615831193 +7,-1618620.6860795456,342656646.59246504,2818499613.4528909,10841242.313383969,-591491.59938130574,42287.95558678994,9726.9353141641004 +7,-1616544.1806287526,-879871867.45799232,2641597480.6807599,29400802.275008809,-567607.73734244832,-213549.49944839685,7871.9292490544476 +7,-1556875.0358978317,129663996.45094712,2829870873.7704701,14233556.653251121,-594879.23659642914,-539.55446382795412,9511.4482268025258 +7,-1498390.0988753627,1726492233.743479,2347783826.0281138,-14084795.38772526,-482264.36903485452,320650.36818676174,9542.8302959796893 +7,-1438375.5585838316,2310731210.3086138,1827452989.9363542,-26267756.070112392,-373914.442175465,434101.86708105414,8453.7749837357242 +7,-1374019.7327346923,881417511.32784116,2730970499.4128041,1629897.6941239913,-568528.42554714601,153866.53686436577,9909.4334390164186 +7,-1225320.9146935614,2826669692.4744844,937510676.57163739,-39169274.051340595,-193991.37230905821,530801.33390803332,6084.7312434714022 +7,-1178550.0444472285,-2297507592.8210187,-1569630000.2910295,27233438.164417047,322634.80780356086,-512680.56539903919,-7959.4279335446563 +7,-1122890.740732115,-2123097921.2522399,1737352344.6971536,43084950.566170938,-381107.71671115968,-481166.69956981263,3261.5452367532398 +7,-1071323.2147154252,2716542327.9233437,1199765053.9970927,-35743822.810051568,-245956.38219845048,512823.18129466224,6711.5391784467984 +7,-903527.21125794738,-2612398816.8936014,-883592568.96279132,35662606.874849118,180460.83083135812,-585019.5931267055,-6048.173834937088 +7,-676733.94679782959,2764266626.2399983,-1197585307.9314406,-48852348.318020061,224785.67122502057,512876.81219587295,-640.97888355062878 +7,-668975.92231195117,1388310105.897855,2536825161.1125689,-7440911.5931767859,-524308.20981799543,255243.60694345061,9394.0804136631596 +7,-618090.98153168475,1815530423.894527,-2370932866.0738964,-40567668.039086752,459054.20023936324,329077.07607795234,-5216.3441374352851 +7,-598015.50366721698,-2731129397.2518163,-70374985.587760523,41241026.683356486,5432.5106088374332,-616851.28667115676,-3427.7322897115087 +7,-584441.17993409303,2820662757.7798271,-1066517825.6270016,-48767663.598636135,198566.96127739703,521534.63954413956,-177.72152140242773 +7,-575268.42877262039,452673034.01014626,2805909102.3969774,8385917.2639193768,-589892.78373989696,64299.041501174368,9337.8411941802406 +7,-410226.97991740308,-1863799685.0168781,-2087954901.846122,16911696.269113839,431673.72653097357,-421647.96993640176,-8777.7768728182309 +7,-361607.77299356158,1085793408.7349117,2669766142.3406239,-2040248.4466942777,-551391.14241844311,192675.42461440165,9323.7811979689395 +7,-323507.48750560847,-2562554566.9489207,925292050.26845562,43504968.133656546,-206728.90535642442,-583871.55405451497,-2.9360066206366828 +7,-321273.52111283853,-2698499670.9169798,-435016085.89423966,38281984.128001727,85861.029255372137,-609780.05772118038,-4536.3514963470934 +7,-310109.97378281062,2320020325.9357743,-1902198124.8768632,-44985561.730629027,367118.40635072696,425390.95151592977,-3258.2213125519247 +7,-287196.24985790579,-1727099078.9100149,-2206532998.3548055,14254768.163195431,457205.39226784219,-391090.02924621012,-8944.4213892813514 +7,-50098.950495496858,-1581607387.564929,2272222154.9216013,35137021.164596006,-487294.60879596393,-364460.2516691293,5324.8584685946917 +7,8847.4638361188117,-245385546.70821998,2816691361.5900121,18088772.944195427,-592934.32089653495,-78737.456072043133,8348.2432588552856 +7,14223.416789966403,-2611842020.5438738,813464806.50078213,42692483.516464069,-180278.74312718573,-590515.10093525355,-374.81667592812795 +7,111098.89345094236,-2012426747.1872494,-1930993627.4522343,19732856.034518577,402767.2541044883,-451341.7212341947,-8223.6865000465932 +7,194397.8655472449,-1051688152.5268247,2588748168.2105637,28413647.501250874,-551453.30529591383,-247728.17965261044,6804.8803004718347 +7,397133.05108818179,2722780805.1021299,-1258838307.5997036,-45599350.291443743,241329.19840994623,508882.58931598533,-974.53347851541832 +7,449010.73673640657,-1907684693.8386331,-2051901315.0503507,17355661.569393601,425042.07034595084,-426172.30037602293,-8258.9171466546086 +7,483387.42268623831,46249044.11042197,-2899113659.2301121,-14959772.311402937,582600.40678270708,-16758.05559810995,-8489.0623742934185 +7,553102.40771040693,2998817642.6861749,93668200.459838599,-42585032.102055669,-23875.864087129048,562364.37287523516,3066.0055834012974 +7,566288.73689846927,-2520304234.5753832,1071966192.3383784,41363502.077980414,-236037.68545498856,-566785.39134212234,612.17587975356309 +7,577254.9844149733,1050487428.1552405,-2748066252.63378,-28363000.063539892,543263.66736608348,182926.6348779102,-6891.6464688336137 +7,667867.65945807146,227818196.46029738,-2899591032.2555642,-17239409.422551535,578974.79292429506,18429.500197007696,-8148.9135215793613 +7,672545.63914817153,2487108890.5460143,-1673152714.86252,-43528809.003030129,321638.92248752672,460446.42185201508,-2362.0941242058402 +7,740110.42515840614,2496121736.3059187,1598133982.8699198,-27894466.364063427,-324443.53100368223,469451.92717680067,6846.6972244412073 +7,820255.25501839956,-387584027.08889198,-2852903263.0203929,-7984104.1452788431,577075.58711247297,-107474.43741407238,-8655.5727989251427 +7,850814.66779403901,-455551700.16722822,-2839740296.0444775,-6982956.7495046621,574876.76571814879,-121453.09123694594,-8676.4190984554916 +7,891636.4681350207,2933457877.5412326,614800628.90390551,-38429002.343123481,-127995.16116203662,549214.73107450455,4386.7693159625887 +7,983797.01407324616,2918480221.4053473,678874666.5430572,-37699853.205187201,-139986.21240694297,544444.83754753426,4497.3037167039429 +7,1044574.0903337956,2980460096.7909923,359654702.26196861,-39904502.109739028,-77250.997726216461,555244.37707770662,3669.9635248625323 +7,1151323.0592143382,-2726602284.6540284,-120933938.38239075,37331670.632650577,20793.308645274126,-615996.50213216129,-3101.5130379847969 +7,1243371.5418850174,-2728760042.3438964,-123904060.74228607,37118891.109758697,21235.489721044149,-616390.79406713077,-3069.4050507536194 +7,1276865.1307086102,-2164947054.0213947,-1729303721.9641731,22080002.260660332,363012.00947965885,-487217.58102679491,-7211.0293798017556 +7,1292321.5537500111,2516244479.1065717,1582534921.0122952,-27583315.473176979,-317518.43147333141,468573.84944228706,6490.5820634244301 +7,1324006.7437984659,2152592548.4042935,2023064671.5163641,-20650937.00984662,-406532.37651742413,400731.93523080274,7376.6440121130881 +7,1459905.7019960317,-2496188234.8852615,-1161417243.8554237,28970122.847232848,243793.41532657263,-560748.16562788817,-5762.2562371714239 +7,1477866.4922103449,1954376814.828788,2199624405.2839355,-17060008.690485481,-443879.19803565455,362580.02790270798,7638.8725445227028 +7,1482226.0356866643,-438498618.35483038,2793521730.1301932,18107374.41657092,-585667.69845106022,-119371.41393117204,7470.3919721663024 +7,1725506.7675952152,865866788.53037548,2754620926.2672925,-62727.369015876051,-564381.6801435675,148897.49481218253,8224.81977444519 +7,1784883.4810830061,1904554028.0553534,2239592547.0416636,-16296855.923751734,-452701.72639185056,354982.23275471269,7555.3436754550648 +7,1822017.0328188022,-1627682216.9454625,2253304898.0083585,31078265.839943282,-479103.60881732928,-370557.81948447297,4869.5915507333484 +7,1897716.3550624503,1533476483.6660266,-2519374858.3373303,-30767135.854234885,497809.83017357887,279742.51232300763,-5482.4535206504479 +7,1915798.5790864606,-2320936297.9538732,1483105386.0395973,37016077.543447688,-321354.24287796527,-522816.20086393121,2163.096908151776 +7,1917219.1362859886,-2657858584.2098732,681708887.34761691,38238783.277691178,-149220.25869124869,-595997.48192822293,-453.23914496953705 +7,1968815.4807782993,2011531198.2719731,2144487653.6962287,-18119970.366455853,-432443.07889904792,375770.58742241422,7274.5777145642251 +7,2168762.7154202475,-1365545242.8106368,-2472073983.9236169,8368498.4996194011,511517.62844952516,-310457.53092472348,-7973.941425738376 +7,2224695.5196905467,-2714756107.1992378,366062721.13772857,37062655.934626676,-82257.736002327059,-609612.2909118461,-1281.9898645390463 +7,2373820.2608772237,-1275446968.0526686,2476314514.4337521,25973174.930483729,-528633.89399287035,-296068.3199012588,5774.4231195907032 +7,2374446.6126805879,-1594133977.9837651,2268771589.4762459,29348220.043097168,-487128.93422390911,-365780.73132706177,4979.4356075741653 +7,2394769.4425862394,2987395338.9666319,298233097.63935959,-37782930.012736753,-62818.821377945649,559420.68801652757,2900.098208936397 +7,2443812.1769471727,-1906727166.0196283,-2037759174.3213053,17182874.959802147,424349.10305861611,-430043.22383713559,-7085.6691450579929 +7,2451545,2158974221.9793773,-2054631591.6074328,-35625201.477211647,400639.44281359349,399828.592088882,-3705.9536905744944 +7,2460000.5,1975977485.7411468,2178666871.1638031,-17522263.042442236,-440521.38623186841,369049.79842025385,7064.5047924822238 +7,2482637.701129932,2315861676.7501888,-1884725691.7026496,-36964097.046712607,366059.44525111478,428977.7273227988,-3138.3938826754088 +7,2490188.003781531,2182975588.1899633,1986377081.9838452,-20880130.334525332,-400391.85136575287,408819.48769856343,6680.2587662436499 +7,2561521.1186625743,-2613888129.0429749,804447368.64602947,36717811.511300057,-177777.78298355496,-591596.20425688743,140.29389280894031 +7,2577169.5542735821,2940136710.2134643,-643343129.8956188,-40293019.753577352,121405.9091095886,547825.39698375307,426.46833622066129 +7,2578021.1390018361,3004047070.8862243,-170873860.71315527,-39392221.332060844,28645.633826681431,559297.20677119982,1679.5705250109427 +7,2646763.0079057133,586285644.2731111,2816673127.9191599,2568561.7065758123,-580697.35708106821,92109.601784236336,7798.6878621905962 +7,2725119.6324796379,921411796.8093704,-2772441087.8830366,-21655946.32491865,554440.33764683153,157316.65473690655,-6525.9797448115442 +7,2836400.686167337,-2290089978.8685932,1517575735.2124386,34392068.491888665,-326531.48098821437,-517660.65569114429,2357.1370370169848 +7,2844222.2036130754,-1147701149.1258931,-2575255025.7990584,5787202.6580620008,536565.92758505314,-268105.07371119602,-7762.8903364830321 +7,2868551.1873311438,-2646577032.7488432,691948404.98793566,36008878.449705526,-151546.88733670354,-597897.76413434569,-116.47171634090633 +7,2975558.236149407,2874013831.9094276,-867748214.53078306,-39299975.057081141,168363.91378502845,537034.10850242362,-345.53395009125063 +7,3090453.0004645661,-743134741.56988609,-2738382060.2974973,449168.14536042325,567149.22104927513,-181982.41201295392,-7706.3386286463992 +7,3125803.9637471046,1837715761.0146849,-2312839475.6190329,-30537768.743507076,460525.05484076834,339331.65937416774,-4689.7120391358849 +7,3132997.6694954531,2673237147.3861613,1321757392.8131633,-29244750.091850653,-262643.27849280514,499939.43060775241,4930.7589463224349 +7,3288608.0938383508,1895208166.220114,2253978832.165585,-16575756.621644134,-452487.64928124071,352619.34804520017,6735.8499675424937 +7,3365404.149100611,-1439932453.9461772,-2412318089.3210464,10436681.622285634,503071.13923362171,-326061.71842098591,-7262.7645258823604 +7,3437264.5591045385,2993646436.0563421,-137515873.83668977,-37499350.297483392,25843.1884011316,561204.91949157964,1365.2959351620802 +7,3465679.0313392272,2654949116.0650024,-1366236547.7572086,-36917575.957233183,266261.05570526142,497732.17176466697,-1794.3665609592811 +7,3466243.6318243416,2789243471.9987969,-1077929042.1671925,-37707698.045427009,209125.90822787469,522470.38812884298,-1006.9045304041881 +7,3637194.0420260653,-2742680845.6232095,-144436756.76942149,33353958.808449473,29043.192587383994,-613537.52764636197,-2119.4411969456751 +7,3785796.9180306084,-1443973074.1345229,2386533043.9421673,24239550.153182603,-506829.89383626904,-329616.92928970628,5276.3047569555547 +7,3872544.985832775,1552823747.4762778,2478054826.772397,-12270868.592177007,-502163.91295839538,286891.94334284769,6881.0825767127671 +7,3904902.8636076981,646616385.787027,2807792806.3137712,-365506.75302817579,-576067.78759210533,104548.27080082068,7282.996533854337 +7,3909575.1612539273,-1914915605.9402583,1999424480.1961358,28582470.081299141,-427048.33880885463,-434436.26307953522,4021.2240135234706 +7,3952825.9438704541,960897579.69524753,-2759735718.8417025,-18925872.246867638,553722.60483582132,165985.21480767746,-6270.6608156266038 +7,3976466.4584558215,-2509215024.9185429,-1136283815.1322851,27384553.017523587,239556.86140019723,-561802.87512289837,-4395.3880016646735 +7,3981650.9501856808,-107701261.24772511,-2870597417.8240438,-6253255.7534888163,583907.35259897762,-49102.68007746325,-7180.3707166876093 +7,4027290.641611686,855491419.62673306,2760616627.8154826,-3175589.5676715137,-563665.66765981144,146945.55365601022,7184.3904828687237 +7,4052024.7581967572,2978142625.3655734,368029191.68847656,-35042459.004731454,-74614.510968445902,555849.78096752695,2333.3778281808973 +7,4113372.0622575954,2979855260.6680155,359967795.88440454,-34931604.132814728,-71986.274527416972,555932.24046961323,2267.3011923493905 +7,4147685.9682209725,2036889540.3424654,2135950102.1164932,-19101104.776516125,-427184.21726941474,377698.74442527944,6080.8179292124987 +7,4221715.6858862033,-2554290697.5341606,-1010389002.0125787,28219615.136156205,215784.39302855061,-575825.99115112599,-4010.6250911606635 +7,4281686.8202227829,-2729573003.3559914,-171309458.59627625,32288955.503337331,35538.220661075677,-616404.5450413886,-1884.191497792867 +7,4283923.1730616502,-2328989811.1422877,-1472933931.302181,24375764.087794811,313061.26827415545,-524577.09009553771,-5010.5076238024512 +7,4318728.2981513478,-341698719.15937495,-2832270973.6297426,-2631508.4680438759,583003.30297832505,-97547.201927315036,-7210.352108372228 +7,4374010.5124558052,-2714540794.1204081,-342409192.4530009,31597816.314160187,73933.241480263838,-611214.55796373391,-2331.7464644436686 +7,4415785.3823228208,2278137237.1232929,-1907997391.162945,-31534361.137006227,377361.90015257412,422673.00305354677,-3521.7497790275675 +7,4444765.7729771314,1526267902.1991105,-2508622406.5077057,-23868739.873112742,501363.80015205563,278725.73299496592,-5334.6462359181824 +7,4478636.3275557943,2739359027.3327832,-1204249104.8983665,-35230310.150429763,237100.47040011801,512228.1652972016,-1695.6632345955284 +7,4545367.7508925581,2468745333.8577633,1666058789.8513701,-25604579.742527865,-329304.78471975948,460113.74480155559,4906.8614630220454 +7,4559330.7160395989,-2535287315.6329622,-1061054981.3384724,27700998.393498462,227891.99070006123,-570412.71853533806,-3931.1299388808343 +7,4601307.5784208523,2719249943.9369049,-1236230712.2798345,-34810433.706445739,242930.92452575944,508977.49901346111,-1773.5847235841391 +7,4612904.2376412479,-700980103.39080799,2746098753.5076833,14123949.23350727,-569478.80122223427,-171058.83790136818,6351.9411133724252 +7,4702674.2380924821,631267491.26871598,2825580822.6521502,-1655247.0573877911,-573083.32962914871,101421.48901073026,6954.9813115739335 +7,4721297.6960922908,1691921939.1666818,-2403547455.1567955,-24785057.876061201,481703.06225107569,313814.31176778895,-5035.3639067436588 +7,4826887.466176373,-218802599.00312349,2849203527.3455744,8181944.1564068543,-587157.89564920368,-71151.008928217329,6756.4720426516269 +7,5007248.1756073525,1865065581.2096,2278909951.0359454,-17544695.205134995,-455166.23464032583,348326.9223379083,5933.3955024283359 +7,5020617.6618434722,-2290855849.2542386,-1554129986.9789906,23861513.445759725,329477.8733153902,-510860.92489628907,-4794.4724766343252 +7,5021823.9062834885,-1821679300.1875298,-2112199252.9583108,17368340.883932654,444191.29382164963,-409751.51124981133,-5917.8830389390068 +7,5182825.1076644426,2167695154.8460479,-2018438816.9520857,-28605180.507169068,399989.31736275839,404601.76637634373,-3941.8473609531566 +7,5229945.4733923804,-2230609475.6262679,1639318016.851717,28633029.695979707,-350267.48287367157,-500355.64781004889,3246.2505075901668 +7,5288363.1755218264,-819922215.6965493,2701660372.7712665,13850983.19567281,-563061.51315759099,-195869.27928476047,6183.5104961617026 +7,5344069.1443945924,2223017571.1201086,1952967044.1953638,-22611840.714964431,-388557.41164559935,416049.24282817042,5131.1960191924527 +8,-1923299.6800556884,2558997023.2695389,3651527912.0082378,-132710122.86450072,-388066.94266187359,271911.40004989237,3100.6488871485699 +8,-1676477.8519173686,-111467134.410954,4468232788.0505524,-89524893.497460768,-473474.21968729532,-9284.3609884136149,10835.71642914427 +8,-1673991.6976694386,-1267831175.0088599,4293103492.6420369,-59949145.02908536,-452406.41408122901,-129317.38251801601,12843.074282209149 +8,-1592174.7404434106,-2611020650.8051176,-3708850345.7054467,135117955.8600862,381710.53477718646,-267732.6468416666,-3079.0827487050151 +8,-1570486.5735796981,4457697348.0871229,309831606.417485,-106680449.32500985,-34987.208146732555,471148.08707021421,-8923.976829411642 +8,-1488287.9835097014,-3310868732.250308,3041036234.1445436,11898759.27331778,-319426.58307719277,-342983.27150591614,14247.112360053667 +8,-1431082.0416018502,-2217184518.051456,3898158973.9401636,-30365933.208095819,-410960.4573718856,-228522.55785978315,13979.375859370335 +8,-1389689.4817755735,4445601723.3119421,445959429.39254278,-109334429.45459881,-50174.683724780029,468954.28719019296,-8550.2841103411683 +8,-1343251.6787880149,1054627760.3961194,-4392364599.3690557,66742547.160977557,452485.84102006973,111961.82391998128,-12491.461134655083 +8,-1257937.2707797578,1138933855.0994718,4314565285.4906816,-114583738.67138068,-456502.15891040221,123348.15308031437,7761.7206019090581 +8,-1219453.2519424818,2531451418.9093804,-3728626849.2173347,19746789.455626983,386695.10086195735,266691.47045150382,-14216.131748806034 +8,-1090195.3593005561,4469176585.5376148,-168946676.69805852,-97324607.336252749,15467.114002231627,471260.1766057146,-10079.091285303644 +8,-1054045.541972524,-3707335544.4703064,-2603705926.0371203,137259930.26585785,267189.62945364125,-380816.25481757161,1837.7127865282303 +8,-1042009.1233008691,1253761169.6297438,-4338458027.0024424,61081777.446016036,449319.26636155951,132568.2346095003,-12873.966739628218 +8,-1041936.2585759561,1286466185.8665977,-4328668337.5219011,60141859.053997606,448322.43491423543,136084.39817107006,-12921.076305390361 +8,-1007803.6105930222,-3036288033.888833,3312775223.5564218,248860.29420422355,-348035.94059731637,-315384.94343062036,14332.872348499977 +8,-851880.72580218525,4299308409.728075,-1260628901.7848942,-71140242.151766211,129793.99065725792,452679.15590860217,-12271.945800638805 +8,-816318.43809974124,-4315085082.8785858,-1371789351.8772931,125697683.79336536,140530.49510578968,-444594.29049499787,5986.5795921787003 +8,-780456.86688351515,2755731246.0663509,3508374166.0679975,-134505967.47931865,-372421.41447418783,293531.08846066444,2389.1498970826824 +8,-774981.89601031248,382376597.85173804,4454337644.2970772,-100355062.71928686,-471302.32826734416,42259.126575106304,9769.3397562633509 +8,-414936.7444188518,908947709.81708336,4371704240.548089,-110649488.66494881,-462300.80012478423,97847.775201033597,8435.5092563715098 +8,-329688.27939165942,-3079421187.6652384,-3333388884.1272721,138465402.47431591,341176.01509405056,-315098.51027330843,-1224.8686920755406 +8,-262115.97834237712,50435184.519241698,-4527856275.5636454,92099113.864805937,467593.70103611681,8269.3032289977382,-10776.583808188117 +8,-258906.13441053941,1516276041.626483,-4253638764.5976233,53238566.279044807,438883.58187689056,159570.69019174346,-13254.36560621255 +8,-238398.8240738227,2660071560.4831686,3576496592.6586008,-133940679.12866922,-381125.12432270154,283596.49487915338,2809.285382770739 +8,-222450.49953363347,-3888566126.4514489,2280044911.409193,41216935.275845245,-239734.59160248094,-401197.83922218514,13711.69143663003 +8,-185290.13198482478,4393801977.0346775,787765188.2444936,-115859418.51748267,-86304.323187971531,464333.51633129199,-7623.5810823516822 +8,-151032.93060163804,-3676633211.5921879,-2658944329.0749011,138160638.57972962,271246.61839460465,-378334.91095378943,1624.1483049021251 +8,-129103.66250778222,4342281248.9264727,-1091516722.4399507,-76042712.576720357,110636.79423164384,457186.16849260556,-11938.078711939668 +8,-80322.886333837407,614539332.4409219,-4483553021.6102076,78323951.71297963,463171.24351862015,66226.407439950533,-11888.479453425622 +8,-33430.993196095107,-4269705812.5531139,-1518914063.7275157,128225183.70097107,154408.45938780202,-440519.83406281902,5541.6139610594364 +8,-25256.984452868113,-1723901767.036902,-4194322129.1939335,125486691.02070187,430742.22173096373,-176610.95036112244,-6164.8928192848816 +8,-7606.9093315361533,4437285213.6586723,-569981581.88052869,-89045562.82375522,56502.579975123735,467240.68102771061,-10919.292492140468 +8,-6790.8460621091072,4467454449.2333775,-186857378.56629717,-97630419.731260419,17264.428817942429,471367.87565942964,-10105.545808597801 +8,-2182.8638609012123,4022534439.6264081,1924997666.6891458,-130995748.21750648,-205112.97312389335,426505.95546449337,-4124.5649874690671 +8,96386.836576555856,-1151244023.7056062,-4382570313.7303848,116384244.52083141,450767.29080543923,-117823.52818569857,-7839.2189797459223 +8,117728.28870040085,4107393744.0751486,1739011580.9081736,-129166159.43870695,-186958.98230680195,434205.26202106808,-4701.2745242545807 +8,123297.14034525864,2437588226.5994382,3729892085.9757438,-132201687.36841993,-394986.80671505479,259383.7327817801,3630.5814006232899 +8,133121.25105137401,-1992864309.4231126,4013754374.9666133,-37308512.054351225,-423292.96943746385,-205077.015302871,13865.397984667707 +8,135598.73058697814,-2964683592.5688701,3374133588.0114689,-2044789.0611118367,-355292.94449905615,-308032.2781773223,14398.209485255782 +8,207346.07311892975,-4252866601.7633595,-1555757633.7171841,128778379.21911508,157845.47998177019,-437790.14513394039,5441.9276998128325 +8,313217.70282690693,-1825349083.8459215,4092375027.7257862,-42732115.53199777,-431735.21398703539,-189600.37268000672,13710.785649829 +8,511962.15106822457,-3340953736.3477464,-3063441931.3684807,139155077.15288916,314277.77749718016,-344335.46993227227,-83.85950331727004 +8,638582.89819147578,-821146956.99110377,-4453649244.772274,110380408.6238963,459951.69048266712,-83446.405392475252,-8777.5401560286518 +8,668223.80978665454,936501515.14832914,4365250399.4738245,-111201856.68324177,-462066.12949958397,100218.64319643113,8449.9718403698971 +8,751746.61871354375,-3609605175.1316762,-2741094186.1010556,138704576.22174096,282104.88460903743,-371211.51086581557,1209.0470169137575 +8,915522.82451696647,-2079902999.4297545,3977673213.0850301,-34409439.311142407,-418462.89093223767,-215583.23449184772,13976.799944980468 +8,945083.80648518819,1730563181.2786005,-4166205970.5697236,46263444.299591742,430028.65651763778,182177.14762070792,-13583.008959958215 +8,1031908.8121870048,-270625371.33417082,4466021755.6030226,-85790771.461800754,-472005.68851868936,-25953.538737034884,11304.253519471416 +8,1097676.9260185962,-2703306082.0014315,3591893468.8089194,-12202785.654658582,-377511.93092624925,-279940.62273994921,14381.459935035282 +8,1184635.0365138007,1164403132.1227715,-4366466703.7745533,63302550.068096176,451410.03773249034,123297.63682125317,-12868.495984692689 +8,1240607.887358326,-793589664.60026062,-4463436668.9149189,110066903.05308686,459834.27183866821,-80221.102800164881,-8882.0567062996561 +8,1438736.9352281839,4452559863.5924854,358517739.08960265,-109335322.7796198,-42040.222127110661,470641.61191104871,-8732.0651357892675 +8,1473698.2508206,-3728577594.7591782,-2585157113.5265999,138629994.71244726,263511.40804129519,-383851.13233413268,1850.6937556888165 +8,1489867.9173414893,2903880996.3139067,-3444569072.4749022,4357348.1687278841,354976.94155331049,306078.12611297233,-14421.7675932698 +8,1501570.6993583576,4173806975.2579327,1572228242.1108892,-127999874.9869151,-167944.89812836959,442844.80511557742,-5259.7166162866133 +8,1510587.743066289,1110428287.6827548,4318325535.5534611,-114305511.57638152,-457253.42618277197,120732.52336017656,8007.7868032442493 +8,1630718.6853180029,1214634691.6357479,4289514613.51021,-116145384.39397043,-454088.76605888689,130313.58461530649,7718.522137136305 +8,1634747.6755013634,-672739411.04749954,4419280566.188983,-75517688.012616232,-467292.89169441035,-68580.648762858094,12117.215272804717 +8,1656135.8619036139,-3155184646.8495383,-3258536945.8972173,139458285.13852984,334010.08721115021,-324995.62855438888,-989.01016206078714 +8,1756351.9397676005,-1256888487.4482,4294035355.8743401,-59546381.372333854,-453073.17025858152,-129865.9093008748,13060.390184166285 +8,1787404.0341840032,1574558220.2954705,-4233525586.3559327,51006821.138939939,437814.92320003896,166287.88362940407,-13468.619735778406 +8,1878348.4469103264,-1990762668.3763566,4013339694.8367214,-36934705.311562628,-422583.74168653908,-205466.81584720124,13937.670518123467 +8,1932250.7486238298,889773497.12472832,4372287666.9736242,-110467876.93878141,-464183.42817459069,96641.57170339291,8670.9599020449532 +8,1950820.8069573049,-4461021914.3407145,-768012030.29995179,118294558.32220387,77081.015000981308,-461362.17729866999,7710.1679263262104 +8,2070865.2427328248,-4479619016.0989971,-638011901.69516206,116097118.88066736,62498.631085736823,-462199.98397037684,8079.4710701200202 +8,2224274.4242204106,4043053768.2645464,1883667232.9787235,-131799334.33862624,-200674.31104272936,427843.55560511822,-4203.1372074452929 +8,2302998.6477966486,-3305490691.247942,3049702019.6944366,13283718.492617529,-320400.53136101057,-342110.86956428323,14427.132202605202 +8,2311595.6093335822,-4477465303.3872452,-658719825.23595321,116597853.29866748,66445.097868053213,-461458.72915235348,7978.4013445431965 +8,2324694.527548709,-352457525.19135267,-4513520189.6343527,101022117.77970244,466003.99473296158,-33788.605499498932,-10021.386245050284 +8,2391359.1473642411,2516183612.3285098,-3737476989.6880403,19008568.569529943,385565.70903302793,264949.33414387732,-14334.95119399175 +8,2451545,2515047269.530158,-3738717715.8698902,19032286.034542102,385853.60451197112,265820.32188706176,-14347.848192073428 +8,2459295.1370014707,4413440090.819191,-745527701.86580503,-86369967.495873004,75976.119443139731,466821.42039467976,-11352.922046025826 +8,2460000.5,4454448646.5561705,-414300452.64405876,-94125009.541192219,40157.590281230528,471692.15242142248,-10622.525027513231 +8,2522318.7583222622,4426235119.4726105,585816477.81695092,-114106319.04343158,-64934.462667548556,467458.68557060097,-8150.7624229951161 +8,2553839.9016803317,-4333233750.723073,-1333186261.3388698,127366078.85236076,136454.09815483712,-446220.89671805192,6036.242658236114 +8,2623762.4987766221,-1230509821.2416782,-4363032081.8848448,118248036.74290413,449622.87455494725,-123918.95421865419,-7804.0928972629436 +8,2635955.737013571,3750010037.857553,-2468949822.8755875,-35648269.396785587,255591.56703340713,394580.48455516354,-14029.43648431837 +8,2675540.2571245916,-4120965756.4577494,-1897775599.3605249,134157084.28252815,194487.06281965849,-422868.28476855363,4229.6943281904232 +8,2716122.4345429298,173193016.26449859,4463866689.0259609,-95939612.084654301,-473003.39986740559,22069.401764848517,10463.623912290434 +8,2788739.5810845671,-4301684944.2606964,1379716396.8433645,70885609.077859089,-145941.50336174568,-443126.73166511278,12511.770398226023 +8,2824499.0467523281,4320411807.4851103,1112718091.1503465,-122656233.83704054,-121384.30460340116,458308.70925366133,-6634.3136670245167 +8,2851491.8594524395,-4522009415.2115688,213698716.60391113,100038877.26721053,-26037.229764447067,-466829.39546717796,10203.570843873615 +8,2859248.9859733144,-3324594349.1529365,-3091796643.3474922,140451024.24229088,315300.49437153019,-340184.41119689669,-270.52709349573746 +8,2976882.3515684241,-4051720514.6267347,-2040984026.0307658,135678177.99888885,207121.74149995149,-416813.14862204419,3780.6570631599302 +8,2981987.6770306583,-2492954075.3667479,-3795305209.0728345,135760271.43847397,389058.93350420782,-256124.7090183465,-3737.1754327797171 +8,3108937.6713916427,428661848.60537112,-4507899296.5606041,82856537.727221206,464622.31191801501,47455.11027092491,-11717.126840539097 +8,3165569.2779342923,-1207517453.9485662,-4371745709.3275652,117956881.21237428,449815.47301129211,-121923.54463378795,-7893.4289326923572 +8,3172449.7031237753,1920037534.230484,-4087206424.1296864,39670939.352392241,421221.76503970276,201361.26656725121,-13910.883850615828 +8,3174619.7673007874,2779322125.2711697,-3548808842.4377489,8698039.8235189207,366076.64233617223,293046.19561467506,-14490.366516076949 +8,3205181.8114790143,-3061084461.5723286,3286012807.1506877,3261411.0989665347,-347314.92271952535,-316243.82468116219,14576.09230551562 +8,3280210.3852755837,-3420812322.005188,-2977309169.3460088,140583442.73622617,304085.42003825906,-352150.39311445667,198.64811745887891 +8,3392247.2028327994,-4474364556.456563,621033632.87403607,90959889.670022964,-68516.806168687603,-462560.94093437423,11113.276518185103 +8,3435291.595060355,1588159410.6442223,4164928021.0186839,-122601853.39951801,-442870.03663604485,170551.02377943651,6759.6124469490014 +8,3566608.6043030256,-3211395443.7918339,3143100734.4602504,9754354.2440649699,-331674.26323075418,-333794.26527461433,14552.21387842408 +8,3618651.0781804733,262567527.78836057,4455881624.0824852,-97875587.169346407,-471918.36589172442,29399.193285437952,10326.281360121562 +8,3667462.3955610162,4255062110.6786256,1343529591.7251186,-126455383.31746221,-143929.16393998271,450439.96071997861,-5932.7405343460578 +8,3732323.7882751999,3089355069.9619479,3210877410.2577043,-137882887.8504689,-340769.75862944446,328100.72319115081,1158.1950052552058 +8,3763768.911270502,-2649681031.8493013,-3679729862.0355244,137314696.80978116,378010.80021728663,-270639.61149965686,-3185.0765674130516 +8,3782352.9043525672,4318827360.7226992,-1190506673.4800873,-75807037.468532369,120624.75616773564,455368.50997592637,-12186.386847007348 +8,3812724.6899585733,-4418444787.4722567,934346834.39421082,83423050.114741787,-100902.00053505009,-457039.47625016666,11748.115825417304 +8,3936736.4830415361,-4462838138.9647856,-759738865.46117675,119433012.7043937,76452.424500623718,-459196.78208952048,7698.2581852790199 +8,4111268.5797193972,-4051114471.9760971,1986711059.4242098,53412862.726742953,-210681.01409017801,-418215.06742730702,13528.967506935074 +8,4181639.4410130531,-3766997023.5371928,-2531904649.7272072,139923048.2074405,258475.04041283455,-387263.40942216432,1948.4214181555024 +8,4198094.1340487637,2954464304.8917828,-3396555548.3255939,1189214.2341797475,349894.47686182207,311068.01260366756,-14563.552750401574 +8,4265793.4883138156,4460148755.8503523,-310105627.41786873,-97559381.806133524,30103.634585623298,472040.07238469482,-10425.762224273682 +8,4395389.2281224253,2749023948.1538258,3501454097.0785966,-136246000.7075578,-371674.96085115679,293087.73073377984,2630.7835340133975 +8,4410176.6840111315,-3515842996.5920982,2810044765.317256,24143651.66648186,-297657.6227762482,-363031.46478146681,14430.843583957945 +8,4428430.0858939318,-1627716251.2827559,-4238140315.1724558,125257215.20985596,434156.71115402883,-166028.74493942302,-6720.8026674111934 +8,4463702.0632561212,-869304114.31812382,4384870149.8340845,-70001835.102113366,-464230.06400140678,-88945.869038701421,12658.432576440067 +8,4490693.3274819208,-699273455.57837319,-4483435290.8709307,108632790.73872453,461450.16227264405,-69732.52422689466,-9339.1867518567524 +8,4512886.3166226726,3688505343.4157062,2496159696.9272156,-137509263.15074274,-265891.64943700872,392495.29055040592,-1869.6232976953713 +8,4515815.7899172818,2742082992.0617876,3507067237.5710955,-136233741.77338904,-373210.12012757716,291544.81381914619,2701.9666297275876 +8,4629188.7503353469,4405773931.2518034,717464603.36999297,-117739087.27260599,-78336.020149713251,465748.41748619231,-7764.0177312972874 +8,4655623.4189050822,-4435017562.5501385,848523422.41980898,86204655.545299068,-90684.295255679535,-457862.29530289856,11556.580281446753 +8,4682167.1943705287,3719856975.3577681,-2524863911.8122268,-34991992.157929622,260280.57828901595,391981.19694212516,-14147.341143867223 +8,4702093.4076364925,250269815.76747519,4454082956.1809683,-97552772.829674229,-472353.60395432729,28329.521985698604,10455.573307360124 +8,4729931.4958646996,-1374475447.6186559,-4323729062.5472975,121176251.92378967,444219.36737478903,-138596.32956058288,-7523.6363627077653 +8,4818169.0854232013,2175955258.4880085,3886738193.6296062,-130975914.98444302,-414282.84496540844,231880.11978071087,4918.0483445313794 +8,5036912.8931462821,1444076492.3304369,-4282985639.0491982,54409502.569663219,442414.8526675452,152666.8309556102,-13506.764936185329 +8,5135360.7071849732,-4226768406.8657584,1572133132.1639738,66687887.339929305,-166586.97544520444,-438664.06624162925,12926.667347600562 +8,5143787.9241008088,-3949425600.947227,-2226764979.4934936,138466511.58062604,227242.9614271223,-407586.90131783194,3059.6964458103375 diff --git a/planet/internal/semianalytic/planets/testdata/modern.csv b/planet/internal/semianalytic/planets/testdata/modern.csv new file mode 100644 index 0000000..9e4d777 --- /dev/null +++ b/planet/internal/semianalytic/planets/testdata/modern.csv @@ -0,0 +1,876 @@ +1,2124783.7668421366,12966014.402752697,44091537.435792632,2329627.0320310546,-4874013.5706652459,1367552.0092589634,568617.89545292035 +1,2129016.4489093982,-34915363.422011293,35402620.992612861,6134203.4369306508,-3843385.3806195571,-2765802.0055035576,138764.070416729 +1,2136436.9887800012,-26650004.32076896,-64250458.305827275,-2670049.6365175056,3046001.8466106309,-1385699.920915832,-397913.80876064039 +1,2137657.8561469149,-52823074.5498579,-39395754.573036417,1791368.2801960313,1659053.5523407776,-3166731.8790181158,-411005.3709838161 +1,2145855.0392295346,-9494413.4743902069,-68893642.315869182,-4659596.9116879618,3329797.6485156622,-340604.90429031517,-340207.51535812893 +1,2152902.3387021516,22783347.390779406,-62425551.811224125,-7170720.4478582777,3114935.7644703547,1675954.9253170267,-157342.29861226626 +1,2156848.9035254759,-17017165.282555163,-67591962.139612749,-3852176.542047611,3241109.7436351231,-797005.98265212134,-368440.88505115628 +1,2163034.2226464031,53514647.86862269,-14216582.375077598,-6166606.6196904052,268864.92693417461,4275733.3129855348,319582.9452103597 +1,2178316.6305287816,-6311046.4152041338,-69133035.311477587,-4987636.3269616421,3351398.5303779822,-149814.28202059501,-326120.93572204519 +1,2179511.2975262469,-36717865.282970726,34009938.903690241,6184895.1603044914,-3709462.3736983426,-2899913.6413497464,113525.20979651572 +1,2181205.8389593726,-46573318.60754022,-48827086.796058886,422368.90759137308,2193667.4319772604,-2695248.0738038835,-423038.59322029131 +1,2184220.9753084038,27651894.460673034,-59516037.328977257,-7393965.0746992463,2978628.0529203648,2002144.3073291348,-117366.37969792988 +1,2187621.8174504573,-56407921.087815747,-30840340.499314442,2792124.7426338312,1156167.0735995015,-3492074.8935807231,-390185.47921596922 +1,2193280.3421586915,23197175.990019765,-62233507.215453595,-7196746.4853767129,3104448.5309753129,1700117.8989547768,-153275.79790973791 +1,2201381.6964990064,44855037.422095515,-41604480.023896776,-7556624.0969812796,2032826.1889982887,3300959.9255885808,76508.453738233249 +1,2205857.505220057,15438805.496610295,-65766349.518421069,-6757488.6574631343,3257198.4447903717,1193418.0997412808,-208150.03558951264 +1,2210230.8758518836,-55179096.843997702,-34148666.372462764,2398341.0601233388,1353544.7894430249,-3378427.9139518118,-399554.79946352681 +1,2212968.1213944298,-32798857.854785319,-60938824.777554058,-1860035.4896756392,2861444.1215942018,-1776485.2731495246,-411018.30521774536 +1,2222538.3715594551,-58584431.486441612,-3280278.6909962166,5206156.0212486442,-636448.43210921308,-4006640.3779397863,-264534.82947102532 +1,2226923.5099660973,-23352969.946481619,41797302.197896935,5560681.4357295055,-4516536.4813682176,-1878153.7634611425,269780.48036103073 +1,2245751.8693725881,-35592705.950152777,34818143.141232252,6136752.6063570725,-3793108.1251196475,-2825626.8072411222,124917.39494726696 +1,2252782.721694936,-5248066.4560087463,46002159.847483493,4213848.7662318107,-5021678.1747932173,-311306.66117830586,442721.42688255239 +1,2254413.3714484093,5029574.121365536,-68507565.37880528,-6016183.5584425284,3356387.7839559061,537591.56125165941,-269186.02094453771 +1,2269128.722823476,52499047.116220675,1343815.6673807458,-4777018.8720179424,-916912.3416706865,4407903.1162084667,442512.42186573957 +1,2270908.9872541567,-27050753.773668848,40078243.434551574,5765009.3838262511,-4333417.1775324373,-2179852.6483750581,226598.63416646194 +1,2274438.9162575109,-57453620.936298765,1990742.5157225512,5506302.8366624061,-1018020.3969070239,-4013933.5479231845,-230618.99559819169 +1,2289039.6210029274,-54487786.107040778,10724398.650736887,5933260.1209327234,-1682847.9826480323,-3938411.0259438828,-163033.53318888633 +1,2292477.7271920904,-58977189.139968835,-18252595.969281197,3998616.6888083159,374311.58675426035,-3828808.9942783942,-345348.63514096948 +1,2298199.8295391505,-55482998.364568092,-33179334.119383737,2460353.088853186,1296952.1580583814,-3417483.0836819867,-397722.06009107409 +1,2321593.1735340492,-59121991.656152822,-8975059.1610598583,4751635.2347488124,-241175.14478993826,-3971625.6834104517,-300225.91569542972 +1,2325795.0892633256,5550820.8046551654,45606334.847062998,3190526.029148092,-5019422.1031922707,671632.8503526157,519708.77861605695 +1,2333075.7192048468,51467055.132996798,-26944942.219851892,-6956477.9091970362,1130267.2646002772,3928549.4086760506,214556.66219834672 +1,2333814.6346200877,-51914360.70134826,15933013.098365132,6102748.5080359196,-2103070.7487278921,-3835906.6063099657,-116953.75976121223 +1,2340812.2168795345,43423643.465772584,-44045003.504918471,-7599981.6491349665,2164228.1417976893,3162386.6427646945,56742.638843265791 +1,2341811.3621834707,-23193708.556852952,41806341.362776645,5545363.8810436251,-4525078.646766671,-1874180.8461825801,266475.22956525086 +1,2343375.4317375645,51890303.574245624,4175218.4603789826,-4462904.1663021948,-1148304.5582878587,4391018.2874348378,463266.81477398443 +1,2350908.5652520433,-18364903.268272117,-67236686.39125976,-3769457.7631570501,3216661.8978081383,-891339.29577185796,-370009.20020716183 +1,2357229.4238089724,-52046787.857565053,-40499021.063482143,1517121.9705348455,1725043.2688727204,-3127651.4677473949,-413909.88493952964 +1,2373582.3852631706,-58875451.461099826,-6524321.7609828403,4905108.1075852374,-410459.15962693817,-3997025.6397475861,-287496.01823822531 +1,2381432.003570782,-24298461.311036531,-65215277.899544731,-3068713.1273553013,3099163.8574423576,-1256690.9891172596,-388325.57561931189 +1,2388283.1052446254,-51063835.471063584,-42167570.177542374,1274592.5445785059,1820922.6312627515,-3052351.20409422,-416596.71072158287 +1,2388960.8289901465,-13432493.266212294,44832830.489598364,4891140.4440728864,-4875071.8707198408,-1048637.1197792522,364186.41057036095 +1,2394343.1183717614,-58851935.180491157,-6485721.4766056305,4896941.4895377187,-413369.55059811368,-3998677.6102590831,-287716.66750849603 +1,2399890.7931725676,-57087997.420190722,-27989398.703664932,2979439.7215273171,985418.5286747464,-3592148.8891712492,-383580.53705720807 +1,2404276.3067153757,-47804654.04021851,22307889.149132729,6222238.0980896829,-2645002.3797406908,-3632393.2543160366,-52456.232734853489 +1,2416363.148186415,-164678.37793544913,-69142333.725710616,-5624330.572318241,3365303.2492687018,208566.93656320032,-292705.13228037371 +1,2419878.0425812113,-13102931.81954308,-68424620.557573542,-4376140.4469338385,3289750.057202165,-576209.4999516164,-349693.58005644381 +1,2421043.593609903,49749735.20511765,-32263408.254676975,-7209016.466991147,1463508.4873933773,3727310.767483233,169432.87474212627 +1,2428234.8883013246,-13855157.154797943,-68287841.309781387,-4298629.0853389036,3280661.9686200493,-622278.653265286,-352443.0878789234 +1,2449625.1125466572,30616484.162055649,-57652993.963782176,-7519837.918418074,2875695.4616893991,2183772.678188988,-85611.707456429212 +1,2451545,-19461726.624106042,-66913275.992914937,-3679854.1650373531,3196367.2521493547,-964605.58730194625,-372179.06333915843 +1,2459879.4459596435,-58397362.396014318,-3421284.2453254885,5076989.7694523372,-629408.09918260551,-4020851.9914078652,-270850.21209501388 +1,2460000.5,15223596.238767512,-66001761.499491058,-6790050.5445870981,3257175.8619800173,1160934.9677806632,-203892.16004638787 +1,2462172.70795898,-57328484.046884514,-26871361.31616677,3061473.527065549,917485.27675527812,-3628194.8990028957,-380667.66753675509 +1,2464311.0885961005,15539394.358764101,-65890370.570044324,-6810471.1723205354,3252453.7453392004,1180761.6426371667,-201723.23179423693 +1,2489119.3079359452,18447354.720304903,-64761351.777828753,-6987238.6243337272,3203583.0606636568,1365283.7865904856,-181482.98465339866 +1,2493305.4152186792,-56082686.464046299,6020098.0612489721,5623746.9732186981,-1324476.1516970065,-4006233.0930607924,-206683.39951830139 +1,2500226.2523234761,49689803.592045322,11160782.959778108,-3630078.4733150695,-1737596.7927800952,4291850.1554784151,510231.068080144 +1,2510245.5326521634,51311586.262617409,-28048303.549139824,-6985571.6543737659,1193501.8179792028,3881751.5095693576,208858.93716051767 +1,2511798.3773018145,-36004311.716444336,-58640766.038472362,-1513626.0545891945,2736546.003570037,-2004861.2865959415,-414217.11691693781 +1,2512644.2525519142,-6521538.7856699713,45840102.465706058,4350401.1811916996,-5012340.8476345493,-443030.97179567331,421613.82299744955 +1,2517331.7287996816,-54591809.055070832,-34946436.715969197,2122471.1252797535,1403976.9206283917,-3362980.8238567822,-403720.91329874535 +1,2520344.4697249075,5584076.8696773117,-68528155.448989242,-6124664.7842522319,3349908.512679162,553921.46986846288,-260456.14169577303 +1,2526421.1831567977,27527120.975940842,-59913870.184740402,-7422287.0591578754,2981113.4865997722,1963397.0318443854,-111130.51833051116 +1,2540524.5758900349,22639943.52943958,40418312.713188432,1250783.9449503077,-4513600.097167599,2218431.794566859,593378.35645758407 +1,2541290.1209324654,33674825.358564317,-55264816.45034688,-7601657.6284972606,2752416.6801564954,2391855.2273158384,-54735.571431946555 +1,2554601.5683580916,13677021.293015251,43886129.968708143,2355729.0347866337,-4863396.2482153121,1405426.8339309841,558171.26903125609 +1,2556458.3880203562,-31651787.684376605,37303807.334174193,5942937.8364355322,-4062904.6931967149,-2562144.4601223357,159645.97831690617 +1,2558024.5275621186,44327159.524390467,21376706.489752825,-2280926.7250115629,-2650329.2391151134,3970535.0354819088,567127.00239793444 +1,2560314.3991642101,35971795.899186753,31219603.191547628,-711687.6139786148,-3590337.1536393072,3351746.8163685496,601884.89594267088 +1,2563418.9224594044,-58078362.647543289,-2080098.9490712048,5113981.3455403727,-726635.30168806575,-4031968.1019035955,-264855.38592807407 +1,2567404.2967125247,-6841761.6717004851,-69180202.30758892,-5057396.5038848845,3342654.7193244826,-206550.70136918334,-321024.97727481701 +1,2594907.374978147,-51640459.820567816,15999859.137051241,6002702.076448828,-2118626.4184813658,-3848963.5152206942,-124069.07209562857 +1,2604347.3372789668,-20038211.867793486,-66720983.630243041,-3669094.3891715324,3183795.1758683645,-1009800.0844838921,-371820.58321923838 +1,2606533.5264888802,-53032921.945568867,-38206492.715788648,1667411.1776554836,1595905.2741317721,-3237363.0219694818,-410971.71239970252 +1,2607847.7268158202,-58598333.501366481,-19221495.637551509,3732863.843032741,437550.45643383579,-3826905.3491291362,-354422.32246124395 +1,2614126.3054855717,26974115.958981786,-60350360.708750248,-7409729.6712589441,2997766.6422170121,1918268.1530523601,-113897.14078731618 +1,2617517.4004221936,-55116149.662599877,8434263.4619038664,5688057.3243146772,-1513254.8532240896,-3989730.6821602704,-191155.46707061026 +1,2621375.0544140069,-10315513.96931963,45339823.746649064,4665512.3769824021,-4951787.9224381875,-789892.23470040236,383545.94878402766 +1,2628449.2268059347,-25278025.909613516,-64754743.449681506,-3041921.0088217654,3072289.9352895082,-1333815.0057000231,-387940.8348155744 +1,2631297.1088449671,53586741.569596931,-3488841.3123688758,-5137615.975197481,-542934.256755854,4377132.8474911014,409506.33665765909 +1,2637330.0907268082,-36941011.859945558,-57869219.172405496,-1423450.0273484208,2695365.7202892299,-2075838.7072578815,-414794.53042553429 +1,2638009.366482486,-43975751.267466076,26787337.585498717,6184284.7629372077,-3055427.4585098336,-3429505.577445522,-6014.3330458287182 +1,2645033.7652524901,14806488.099762354,43547986.354540013,2248512.1103573893,-4831611.7169329943,1503035.4334189033,560674.9859191759 +1,2645143.4395358092,-58625909.40636123,-6440556.9329251023,4770170.2859519226,-418643.55982890958,-4014585.0466837413,-292852.42190618633 +1,2647269.3922093054,-40140032.031312361,-55201653.526992589,-919043.27635890804,2549973.2655462786,-2289478.1531250677,-419133.54366638535 +1,2655644.4292540401,16513999.688365258,-65647557.792567872,-6902122.4083719887,3235060.7560355454,1228627.3161052372,-191004.98548192633 +1,2661371.2639583144,41715168.111732945,-46895163.945403017,-7632625.2915052939,2305799.5261857728,2985320.281096343,37879.438436584162 +1,2662708.3719234937,43291629.054492831,22966386.214802362,-2015501.7211648824,-2797954.7109987671,3890000.2966723135,573307.0364481979 +1,2663092.8206089395,-58350557.351440445,-20769543.807599667,3556023.9677891983,536993.30712492217,-3796200.4341134643,-361438.99147568463 +1,2676762.0467308033,31818193.313271295,-56950293.804359242,-7567428.9369020713,2830090.2307010596,2246850.0398603925,-69906.984768181064 +1,2688640.7039866913,39290504.275258332,-49870148.832277052,-7656030.6344455238,2465077.8183124545,2792800.7728948542,8366.3618190243578 +1,2690036.4130899739,3343105.1472951947,-68913152.389918655,-5988896.3934925999,3356555.9073154787,404999.27088335948,-268986.995453335 +1,2690409.7967724092,53931095.212461732,-14975238.137513831,-6094838.520230459,305168.5949832495,4229362.6332967868,321575.85685705318 +1,2694122.3170382171,6476036.0759719834,45440712.421150371,3167823.4284164994,-5015585.2032177476,734054.0735273246,512334.27885452425 +1,2694240.839356632,-49441583.985823058,-44347133.293690622,792208.14049681509,1948787.3151062413,-2957887.7020638413,-419693.56903436757 +1,2698063.5586520382,54042630.78048519,-13124095.305648332,-5949430.162797438,173050.3497269529,4263201.9284257973,336401.21634033282 +1,2706542.1496414882,-54823731.667437635,8985060.8643703498,5674964.1349173859,-1558646.8249717935,-3988442.5533841397,-189170.8769591146 +1,2707969.379098841,-36901614.036488093,-57859773.185913868,-1459016.2242926981,2695392.7714042552,-2078821.5174280766,-414204.08971524972 +1,2708042.1077418979,-58783993.888509952,-9057263.6420727633,4540502.6795508554,-237781.97000797899,-3994624.5962730194,-308550.71664388402 +1,2715319.9888405297,20665994.811715979,41362128.931046702,1559276.6522412372,-4611099.5673394799,2029519.591977922,582293.5898321073 +1,2715748.3422285491,53164128.500215314,-272275.9572997315,-4802659.5577187324,-795947.35100470379,4380459.505357597,433503.46862105769 +1,2719549.1874915874,-12331170.798557686,44948780.137280449,4822854.2454904141,-4908775.7953314334,-974993.97178533522,360655.54516251379 +1,2727924.9987669685,-58750102.011458829,-16323883.140820432,3927813.1215847246,249802.79877197152,-3890880.3105880087,-344083.90526674344 +1,2729067.6691969433,-58870616.251022249,-12675249.254319986,4239799.4581034426,7787.7984612443743,-3950510.486647822,-327293.06192102213 +1,2730844.1383527163,-29052901.538085386,-62908109.351652719,-2592023.9877586756,2970414.7289743172,-1576572.7629160301,-397106.23636056262 +1,2735255.6251134528,13439736.56612906,-66758421.286872134,-6727001.8933257665,3278272.2757137832,1027913.9302100766,-209301.3136973184 +1,2736457.1262553614,-58862649.185278051,-12836790.743536873,4222258.3389859013,18597.748013758544,-3948662.6530716382,-328208.57863238029 +1,2741562.3073366764,-57687771.07525073,-24224233.540184129,3172523.5636977954,755786.8567969217,-3715867.9940965609,-375174.91763354669 +1,2759892.8674206617,31569433.01272722,-57232188.163464651,-7566071.6476362264,2839989.6233281004,2221462.4733050968,-70568.249628199221 +1,2763238.5148602538,39086662.975840546,-50197359.621041335,-7657078.3349197702,2478816.1272191009,2769190.6235357448,7225.6921881418275 +1,2764103.4316422599,-6918676.662524146,-69206152.207359314,-5110071.7734252848,3339601.7904606783,-223684.91602745242,-317640.71295951685 +2,2130394.7890321361,-12996776.916604135,106756555.34112456,1956049.7624279479,-3014864.5045454097,-382718.74748189404,171582.82139498091 +2,2146840.8355040764,-104471244.10019922,24841005.975481588,6373404.8488168651,-715933.07298030145,-2958205.7563134222,8185.5261123780701 +2,2148905.6810626299,-61936485.56894502,-88909859.315708727,2601436.8480861881,2460705.0121296719,-1744564.7240926512,-163296.50788214387 +2,2153237.8137625144,98225821.4163571,-46645924.27496215,-6257944.9971922869,1279441.3080380575,2719763.7176460158,-43595.75417830293 +2,2156695.4082887038,-43976216.622580618,97985393.518530175,3682788.0645413855,-2771569.6613579909,-1256533.8457503426,147210.34591720838 +2,2166720.6533012707,99218407.006991431,-44477639.920179375,-6293450.9616501713,1219398.0679127965,2747442.1995873055,-39479.644982618818 +2,2176133.8904876267,50427477.197383441,-96503800.52151604,-4054290.0558264968,2660259.7708231104,1390256.3274690835,-138901.89560417453 +2,2191785.4924932523,-106843485.78208333,11385897.585503258,6354961.1186459009,-337787.38180027559,-3022984.9805368958,-15671.247618247526 +2,2202232.6774916872,107147831.20293564,-17746962.082783092,-6446507.2575542592,478674.87423501414,2971011.4040284106,7099.5380189405823 +2,2211902.6978225713,108341162.61742158,5933325.634294996,-6235035.2090902692,-178850.57754095615,3006888.516065557,46009.772430026394 +2,2219492.6101251878,26542486.257374573,-105567267.60041025,-2800754.6576422728,2913281.7670312095,726538.46597787761,-160866.03994298019 +2,2224845.7532236758,-81520607.825037763,-71034840.077170417,3894231.5291869631,1965777.0124025152,-2296310.2186990725,-141785.93764883702 +2,2229931.3683657194,4790749.4118161835,107541069.33606614,1009624.7814104897,-3033854.1154393237,118582.84123605459,177883.35549768872 +2,2236767.5323917423,-52164986.039795019,-95075286.116655722,1889456.5676953483,2631056.6108095404,-1470029.3954144325,-170697.94406339384 +2,2239823.7150299195,-11915673.965218054,106900330.14060876,1981734.39858694,-3018126.9320762348,-352022.36501346156,171259.16341043421 +2,2243982.5605033091,19381879.284379296,-107089457.97087297,-2421147.6941575836,2956483.5258348729,527511.63321759191,-165521.119443484 +2,2246000.1067288755,5159677.2006075457,-108665355.44427837,-1615303.4733769917,3001435.4440520904,131807.69525161842,-172905.15792301972 +2,2254568.8722611493,83788951.86578986,-69424601.211117208,-5714808.6868263176,1910437.6070228978,2317739.3626281023,-82834.430349840826 +2,2255307.8757766085,47676552.043045968,96801141.826169759,-1592680.175542725,-2724791.2519821445,1322853.7497923677,174472.02918555544 +2,2258464.0921073053,17700181.675436776,106264519.48030004,267482.0523249114,-2995416.816228515,481690.23708446254,179953.96951495306 +2,2259700.1447804528,-14946775.737725656,-107664131.83415665,-445629.1777420601,2976158.1947823591,-428702.22232997691,-178180.87444070823 +2,2267326.9533468271,-51875561.22028175,-95231128.64250949,1845926.7973202532,2635674.351131978,-1461849.8010303972,-171059.97490790818 +2,2268230.1343560666,-39988445.775344595,-100904134.52369,1084777.665506789,2791771.9370132945,-1128617.8751642657,-176040.03331959044 +2,2269790.3324046582,-72247152.147461265,-80602119.842490822,3207012.267928977,2231356.6863916591,-2034447.4816090118,-154625.46729490094 +2,2272355.0311716637,104397252.28336795,29125531.337356076,-5706068.7647385439,-824451.40109281836,2900650.0645266403,83586.454103943703 +2,2273560.1568500157,-90229867.610566169,58093454.899508841,5956302.8285423769,-1650605.3175061441,-2559510.3316047266,64360.729009837312 +2,2275853.0920192273,-81018847.479362756,-71613514.422250241,3822729.2933606864,1982029.2533366873,-2282098.9326409036,-143262.89975145168 +2,2283114.1130432012,97223128.962154582,-48679929.259188548,-6248394.821391264,1336547.6453302139,2692455.1904096976,-44264.054855197559 +2,2287939.7291643075,-88060005.114460856,61334557.141716972,5874524.9551318055,-1741666.9451131315,-2498426.3037718269,70084.036905373592 +2,2290407.9522969564,-81565749.845998242,69730942.482716188,5602881.6841895962,-1977907.405960537,-2316064.3283551317,86012.190364246431 +2,2293224.4117609258,95509544.536605194,-51991321.232349828,-6192368.7362375092,1428147.902068048,2644571.2907532789,-49938.561258471425 +2,2300881.2522288007,108495265.9611429,-2978169.6748066982,-6333574.3119863076,69102.507165297066,3010516.9733921979,33688.591583587629 +2,2301580.2231651647,85127654.243164122,66740461.727768958,-4103933.4840954277,-1876544.8338752803,2368114.7851303648,138570.19380624752 +2,2313224.7491350072,98172006.210923359,-46706866.034783721,-6284318.4623626331,1282090.0900777115,2719205.7110220203,-40063.212150745167 +2,2317281.6629972244,107986352.69542748,-11225998.065155007,-6405540.5786981983,298314.9475713257,2995600.2010113993,20582.984777511127 +2,2327974.7376275249,-96048700.264534831,-49170468.75642176,4942973.0374690518,1357635.6530255191,-2707778.5961233503,-113184.11376111071 +2,2328877.4974262533,-90101180.692245364,-59560388.27721002,4465674.1811713381,1646982.0053895956,-2538674.3890680349,-127823.4622013631 +2,2337239.865494295,38088577.893309742,-101973796.54641274,-3512962.2133365683,2813631.3530201828,1048009.974575181,-149674.05040929126 +2,2345034.3441174147,-107484055.75630163,2604886.4846478137,6261723.7218575636,-91025.813218628798,-3038558.3382444638,-33789.551061013859 +2,2361765.7735971734,104189021.75648816,-30819389.965718623,-6434135.4190604826,842072.81047443533,2887849.3913972313,-11272.490579288849 +2,2367170.1241980898,108443456.54434748,3478988.1885627024,-6233915.4291691231,-109977.00196781954,3010329.7362123188,45583.946881496595 +2,2371899.7620899873,102300419.76374266,35698622.022235282,-5456131.9636586923,-1007470.1924375532,2843269.4894540263,95457.835251026845 +2,2388692.6899023857,27594629.925828677,-105272712.76517342,-2984987.5757790231,2906278.9556813994,756735.49014469108,-158170.3104203683 +2,2396050.0548504004,-105116551.78812715,-23408209.104561415,5769993.4517371953,638077.53540513187,-2967148.6163647976,-76214.719502228632 +2,2396764.3799308646,-24576008.106652331,-105818307.00019225,19211.71392333238,2926712.9405794665,-697178.7138048691,-178521.4960429614 +2,2400481.644540838,-2436137.8864339697,107602258.76234604,1569510.6912216558,-3035513.0911532636,-84554.583317734374,174431.61443188577 +2,2401354.5910714809,69946713.140629143,82362444.760069266,-2950563.7104085707,-2315915.6029164814,1945619.0600445885,159776.34517300647 +2,2405019.4973287047,-102384963.08251128,32395244.444490697,6351361.6143935276,-927355.55676049693,-2898793.5540666506,15026.040176567962 +2,2408091.5590155432,78863834.811280221,73969679.876611218,-3572932.1383532709,-2079309.4961486468,2194012.6017023604,149480.18079712693 +2,2413552.223698826,-95514295.144009322,48988140.02465158,6176474.494249939,-1393868.1525451611,-2706933.4396214508,44351.163369966394 +2,2419671.225576506,-59312326.942214765,-90702199.702685982,2210065.6302263513,2511336.6487722662,-1669990.9800187014,-167552.26810051347 +2,2440977.577691061,-106443591.15778327,14772389.473321479,6347500.8411784954,-432213.65997956396,-3010504.8779519051,-15948.450850402545 +2,2443190.1347555555,-47332674.677174494,96437162.588552728,4044691.8600571281,-2726447.9165522312,-1350096.5852611093,139058.85673017293 +2,2451545,-107456499.11753505,-4885013.934682807,6135634.7884249436,119396.68107607722,-3036121.3946579923,-48387.664952766623 +2,2455076.4432032732,20202469.69597289,105843560.20693743,283454.43411242985,-2982497.1130464175,552198.47762233636,179688.65335834413 +2,2460000.5,72220860.428512469,80401285.338458344,-3063474.0295521943,-2260354.3476856155,2009106.9892069581,158011.54668176998 +2,2461559.3401571172,97686740.951600164,46733605.277595244,-4994174.8085723948,-1315433.0416159746,2716434.0450355057,113233.19531001789 +2,2462119.0103735076,-98751096.194552884,-43370993.874261916,5101620.6169397449,1196028.4285299559,-2784395.8185558799,-107287.01383539705 +2,2466508.2980600731,89260719.14185901,61171664.691463679,-4306350.2342275959,-1719731.8379031692,2483015.9986705738,133417.20221769597 +2,2467214.0734550403,8810735.1871943474,107335975.12843892,971622.01666727895,-3025936.6886442974,232022.39997548691,177758.6342321815 +2,2483861.154225477,-47808303.573620416,96205555.222776547,4094237.0471775178,-2719725.0345481192,-1363402.0411640089,137822.88206533148 +2,2487781.5183775919,15140384.725686122,-107725634.21192127,-2374651.6255159318,2976237.2743351609,410638.01918223378,-165820.94319232434 +2,2488976.8802520866,91756031.958667472,57395042.696522087,-4488147.4552586768,-1613962.073383762,2552437.8249193714,128630.49720090649 +2,2493896.7889425042,108061208.42409042,-10286780.463332528,-6370949.7944755591,273112.64489932999,2998532.2150433669,26200.517245331936 +2,2497732.6999815218,101782644.49525854,37109021.542570859,-5344531.8354322277,-1046487.0694103007,2829563.7942731827,99954.021182772427 +2,2506117.8615391958,-75670096.539423972,76136485.795998752,5430508.0099772615,-2157036.877098083,-2148871.8768474623,93989.245606856057 +2,2517246.1236592727,87485924.205242038,-64597370.207659014,-5951531.9894503849,1778853.1636963107,2422396.5208109119,-68103.880572804803 +2,2519218.527312689,-46661470.118275486,-97933960.678263262,1297647.4196195737,2710969.4988593324,-1314577.7341527706,-174699.90629232378 +2,2523540.996495964,92906854.713057578,-56483831.952075027,-6150222.0952572785,1553858.1923693751,2573194.5281268978,-52868.453645652597 +2,2532976.2550329855,89354846.758109152,-61966977.647714458,-6026083.171314368,1706042.0105205991,2474602.2309257267,-62818.738496299426 +2,2538811.6938372049,76281218.075712502,-77559189.314366594,-5499046.9113398101,2137983.0498474645,2110809.954223237,-92743.803650400761 +2,2540519.962584882,-106421189.03900115,14993982.877481461,6335892.2207540106,-438191.38837455597,-3009413.2713302528,-17947.066066026899 +2,2542683.7465258394,85901817.547060356,65754033.179139525,-3996482.2421126645,-1848211.9160363362,2390042.6454672697,140603.65865046394 +2,2545799.8918804177,106974861.14931338,-18589093.021483492,-6418745.0262438506,503700.17268130241,2967728.6601805501,13698.755786982236 +2,2554800.4063734324,106776949.19428028,18830185.224024165,-5865556.1552418079,-536408.70840691985,2966333.8787141424,73683.846441075002 +2,2564546.3797517838,-88018323.187250257,61446359.14626874,5949628.6705891034,-1743870.4967848875,-2495849.8482787418,63961.714101573954 +2,2568857.8037438397,-90389583.347125724,-59133104.793711975,4332305.1473217458,1635605.8713159913,-2545841.7248026794,-131014.32506063861 +2,2569657.7832248816,64466490.20769988,86692235.588022277,-2441407.5371490498,-2437090.9115038025,1792541.9454808733,166093.29044658484 +2,2571779.6596574797,-89149349.156777367,-61022180.234692827,4231649.5684545888,1688120.498298215,-2510643.5898897164,-133557.70956513248 +2,2573845.72326151,26327216.01133335,-105562122.24642898,-3052315.1826380515,2916008.0266248835,722305.68194506667,-156945.85809479569 +2,2574679.3848221404,-107556023.69052672,157198.68326226503,6179717.5024271552,-21889.377971977101,-3038728.253048047,-43098.181378476445 +2,2586633.328828319,-33972131.225953549,-103111828.25931066,436060.86562703649,2853765.9310128251,-959013.22219733789,-177908.61716380995 +2,2588889.3681069808,-7384815.1610423662,-108452211.97749172,-1170691.3901143111,2998900.5676504015,-216647.36950916002,-175324.6407883763 +2,2594277.8368477505,-20357624.612246543,-106712687.77534255,-405243.09969590412,2952380.3992813271,-578610.85599768138,-177952.83410889964 +2,2598041.6903311247,-105572981.67846031,20034444.722453244,6353456.9700508025,-579702.24229324155,-2985824.9941394809,-10844.529484177756 +2,2618007.208889639,-46943180.651924856,96644566.462484002,4133381.318128617,-2731630.7055824976,-1338611.111163781,136615.6564997129 +2,2619104.9895181251,29151593.000128984,103805782.99172674,-121385.57508185414,-2922859.3653956577,803608.13953064405,179552.91947082675 +2,2624348.9463801663,-102971597.52116089,-31818491.849661775,5425033.7441220116,873589.52487674647,-2904102.4159733667,-93545.578067472568 +2,2625973.7911196384,18919522.034037419,-107120932.55150159,-2688734.8196804789,2959909.0526253926,516413.87828858255,-161885.20828089525 +2,2628830.8427112484,-107440550.48820768,4516366.8980668746,6223956.9444451211,-144106.27776208657,-3035966.9634991945,-37287.315279048475 +2,2631525.3311457839,-107010002.96328291,10339315.339765346,6286207.3101040022,-307492.36664328317,-3024557.9813306523,-27820.232732257609 +2,2637366.4581031906,-106622090.96742213,13644423.494794331,6312515.3601201503,-400247.86604500382,-3014167.4369755979,-22487.694582013301 +2,2639974.7556735859,93411295.769036829,54700904.190817393,-4524685.5401008679,-1537895.6331777391,2598334.1927994937,127279.13193708523 +2,2645792.98103983,107533529.90087917,-14885300.649720408,-6382241.618357704,401395.70344651409,2983838.3170591206,22159.795590633734 +2,2648756.1380476817,55170479.598759726,92817843.562498584,-1752097.2185511866,-2610180.4328131326,1532855.7515543145,172660.47427424139 +2,2656427.132125603,-35990109.897135116,101293215.01994929,3600549.3421349232,-2860838.9647042225,-1029551.8259910308,148056.52731444628 +2,2656698.6733645685,-107121119.73842196,-10433281.679512506,5971465.7758826986,275168.76301405201,-3024305.3373376988,-61758.189804441783 +2,2666316.3092702208,-42613356.353102915,98659744.400841549,3945654.2258462361,-2787489.2860160815,-1216205.3885429057,140867.42253017504 +2,2673010.4253083887,85488938.318149686,66299868.378245324,-3872182.7612445042,-1862875.0295233368,2378429.237998852,142998.08643560988 +2,2686361.2508103959,-106368571.04835746,-16682122.225094575,5821475.9612268833,450104.30226602609,-3002092.8223516718,-72062.396383369371 +2,2686719.8063610215,80357075.687320024,72374172.240503892,-3475061.4428331759,-2033552.23857591,2235816.9736150336,150731.97398947936 +2,2687946.0875379848,-95188696.304943666,-50903120.492053099,4653211.1304734778,1406386.0346734803,-2681510.9589285585,-121789.01608641933 +2,2690127.1188382218,-20993803.069368895,105498738.93175229,2830885.9806306548,-2977278.028044784,-606742.94728528068,160746.46719970414 +2,2703026.4973107646,-39210254.481353708,-101193323.47140877,664781.45031678851,2801564.7552399705,-1105297.1198218167,-177180.36138177465 +2,2705875.4446990406,-72555208.242861077,79152756.971950665,5375755.6531584598,-2240909.4909499716,-2059994.7683031443,95861.52294731616 +2,2707884.5597810228,-38684248.379368179,100285320.27353823,3772566.7568220464,-2832650.2951226868,-1105475.9516828116,144486.8179669597 +2,2715993.567313022,-85970829.066260502,64316777.216375738,5913635.8465635898,-1823772.3193940907,-2437312.861882648,65919.900228848157 +2,2720331.5957055115,-30292203.885585174,-104267876.91084723,92698.326305434151,2886015.0742432633,-855675.12900154537,-178097.09402055194 +2,2724970.7942361315,-62607072.720521629,87274371.976272553,4943936.4133489579,-2468394.843341832,-1779520.8464806559,112832.82380376694 +2,2727321.1167756803,39884050.152848855,-101222692.29474467,-3868287.1819935082,2795557.5150661292,1099822.8236221352,-142144.71761688162 +2,2741241.8489915738,8617122.0959896557,-108394266.72902822,-2208499.2132693049,2996989.3328974824,229914.90851256746,-167185.04977287672 +2,2754094.9248608658,105371208.21537092,-26224167.401874777,-6420465.0282356367,716700.96435325744,2923421.7705006329,5777.4749525231518 +2,2760615.6160826953,107723473.80596858,-13300657.737775145,-6347255.4946056884,357867.01381234999,2989875.5513025471,27427.344118058292 +2,2775185.2759731333,47819291.778943598,-97718333.946358681,-4294306.2473612009,2698471.1744074919,1320596.8007715486,-132305.42953246782 +3,2123077.9044187958,150011688.32364291,-4241258.1592375692,7208.1649792749322,29471.886974029479,2565081.3685992337,5286.693869726344 +3,2125493.0129779568,-111583290.2605004,-101630426.61357938,-219871.82046368378,1689780.3911511921,-1910543.5137978822,-3727.003051445889 +3,2127240.1047301525,-119445230.19334109,86942365.420970991,163913.56071823146,-1557958.7831606984,-2088548.3497279307,-4409.2466582248999 +3,2143495.6778419479,120812273.52234247,-91338117.876217917,-163997.2586961943,1508933.6040261334,2045292.8065886854,4106.5576800473373 +3,2149469.4322525724,-4488844.1522830296,147008448.67706674,278068.9159929842,-2615717.4612184018,-86354.931075681263,-434.46976837275628 +3,2150883.2847262891,106698703.70117955,102780916.15704103,204516.2244351113,-1828802.4719599418,1845883.4541901203,3283.3933828052814 +3,2159451.4265684807,-127358297.57381056,-80209138.581441298,-159541.5260581648,1328450.9052149395,-2185690.0992473192,-3859.8008770271658 +3,2175797.8605743796,-78648054.109281704,124463670.00667813,207247.35865115302,-2218804.5551616787,-1382839.2755046138,-2611.0601263694443 +3,2183267.5953005878,36046562.640249446,-147800464.85623035,-244402.99772450206,2457567.9633107255,601763.23482280248,1251.6858224737146 +3,2185165.7134038047,149135620.35671857,-18968919.352376901,-17227.063332222766,281635.23380785418,2545181.8370437585,4268.5734950502056 +3,2200332.5143861603,-148717962.54599416,-13230015.504735576,-34749.253100045004,185191.76374602292,-2571775.4003664274,-4020.7826430957889 +3,2202394.2021528175,87398412.300151139,119241049.7859927,194151.0886945184,-2118820.3786569918,1513391.9544849047,2158.6603938397348 +3,2203638.2169229002,-137976172.22970027,-59106778.082380205,-104688.92080863682,970505.63416784024,-2373986.525872401,-3591.3712084605409 +3,2212723.8680658517,-137197573.32088569,55977648.261782281,70969.397643006596,-1015242.1963094863,-2391379.3626895184,-3663.2403926909083 +3,2212972.3553689867,114644900.64475699,94153896.807234898,150905.84227379179,-1676382.5938147057,1980747.7837061351,2798.7685449072906 +3,2229933.3254507133,-140986512.50352427,-51051180.930575065,-83080.533946719079,833445.86399560713,-2428397.9151699962,-3293.4870532434934 +3,2232785.4724917868,-94760713.766424328,112867217.8266563,146086.09699630857,-2013941.2758640004,-1663223.9652631052,-2450.1049365232034 +3,2233698.0268184901,92708268.383029133,-120274545.61421455,-155490.13020433355,1995642.584270369,1562955.651357004,2305.1131381122495 +3,2239601.3277611369,149274406.15819132,12851982.999730663,29969.864040719476,-263576.05968432972,2555902.6757260007,3358.6138438919866 +3,2239891.3682846432,57792617.007091194,-140668528.48428014,-180957.79873462129,2337972.6166632161,969725.07897013321,1486.4306111715623 +3,2245467.3026720122,133180522.0509557,66544927.382086709,96806.486308832973,-1193153.3062846353,2294051.0410899692,2851.6028003759529 +3,2247843.1749270088,-127709516.37755896,-79544141.336766839,-111695.00982602901,1317954.4075512597,-2193143.5733896042,-2678.4038896710417 +3,2268470.7773763468,141158417.24768898,48391596.868166931,66358.982274339593,-877407.2316201689,2426057.5327012273,2707.1584693855757 +3,2270168.4744764976,-39362613.568487935,-146664106.62807876,-169276.0147568929,2443171.8082777462,-675649.86895414023,-569.86071172616823 +3,2270501.8261081371,-108028657.79304723,-105418802.95214736,-127652.06285081056,1754949.8646630461,-1850577.5144110038,-1955.8524472373069 +3,2272736.6828372348,-10914402.69836783,-151646009.58840561,-170258.11376938535,2524378.6679331302,-193296.73559958581,-16.163688759808711 +3,2273352.3702764092,-132707778.34162451,65567993.894917957,62953.973611005807,-1182717.7662782772,-2316170.8270994499,-2668.7550696167414 +3,2283263.8718192382,-138184793.81499916,-58515097.084148057,-71552.967922931552,961048.82962745603,-2378576.6523080566,-2424.3540561015534 +3,2299798.5658109104,69664713.827363059,-135126395.1427865,-123230.04388826821,2245087.7839181903,1170553.4734940771,1261.4487402232478 +3,2302763.4744230416,140364790.14686653,-55546615.835393868,-41924.161918173202,904476.37440924009,2384328.2774453103,2277.3634235562631 +3,2309214.8682810999,-114995239.70957856,-97514096.55219245,-94202.910493416857,1622085.9562835814,-1971750.7710049052,-1645.7026294198179 +3,2324911.0294862143,-129287757.39698212,-76799496.942915082,-68293.579787276292,1271873.097151269,-2221697.0225293841,-1678.5644835013209 +3,2332706.751504404,132178329.03934515,-73470883.512846112,-46993.278050568013,1207933.788556793,2240691.9798667743,1729.8419754554477 +3,2333613.1814416167,-124168793.66483127,80164328.439567685,52070.045720141919,-1438500.8172972579,-2171286.9064987935,-1675.1358903300459 +3,2336877.6632670416,-82375356.723402396,122060962.39460605,82784.005281695558,-2175864.7150877472,-1448645.5136811121,-1151.6488272360357 +3,2345192.9798965924,118256272.91601372,89802382.623454019,65231.911444519697,-1598949.5746977858,2040772.4465272503,1270.1203609684173 +3,2370991.719167856,-8139397.2209163057,-151800778.35468492,-76496.517413793466,2527807.9108885429,-146965.70285964341,27.274224819769113 +3,2376378.8498725621,-148837433.30746043,5502485.7391477227,-3338.5474125657965,-137370.90136798413,-2581081.0693295458,-1213.2138423050703 +3,2380005.8086885167,-130934989.01918162,68901740.701846555,26047.281462083371,-1240734.6378228392,-2286873.9029324809,-1064.3880424420581 +3,2387124.1056553391,124520533.41984315,-86144065.707413092,-30429.969768302766,1422223.5524674957,2107328.3592495746,891.07190090155279 +3,2388122.4774242025,-92798389.236620784,-119349709.48581156,-50191.068503242801,1989635.9494099,-1588996.9339784875,-560.9513948937032 +3,2392154.3841063543,-62408568.66861137,-138155323.67176533,-53088.999059506292,2303341.6993882628,-1068747.8926171723,-326.47856049414906 +3,2395842.9038048694,26553839.920357917,-149762865.36253607,-51186.596843344632,2492213.5446501002,439989.85569802305,223.82663488938445 +3,2396629.7907888312,135851248.43356329,-66208126.677738436,-18723.344269040244,1085449.9374370063,2304423.9824411911,817.54310141583369 +3,2397629.8318821136,-70112899.460080311,-134302030.22985047,-47122.955463528509,2239407.0263908906,-1200473.8481442311,-339.18822267941482 +3,2404811.4480631137,-73648630.060925826,127452281.52332455,35069.309847220662,-2270619.3819699041,-1297103.5807032769,-431.57166773835235 +3,2412889.9097686643,-142097941.5655677,42587428.542010799,7400.0110113663695,-780877.05102880159,-2474795.4406829616,-608.11048226238427 +3,2413887.7417795858,64851436.000724196,132618190.96998402,32257.6565090521,-2354174.0780424606,1121241.4845317737,216.80026762013674 +3,2415269.3455272634,145677128.86032447,-38669477.139764711,-5836.8425546910275,618152.41780272464,2478201.8467555773,571.80948369008865 +3,2427281.7438184912,88461606.385481849,-123478719.31730016,-17630.714343533593,2050188.4784037748,1489530.2823447548,253.37537162525732 +3,2430223.9755300651,123998242.67263408,-86927445.26161392,-9986.5211663410064,1435428.2079573621,2097944.8645803961,296.57059829239574 +3,2438709.3066442958,99247635.112539679,109964916.4315235,9466.5441669590673,-1952648.1061745584,1714929.0908037524,120.76748755728502 +3,2443928.8440317437,-134635878.01032671,61580658.788458034,2170.3091401063589,-1112499.3695083465,-2350162.215970879,-115.19970126698291 +3,2445644.9856616813,107165956.22016843,102483553.35096519,3922.8551026753075,-1820905.0778939987,1850504.1533740473,60.89240552051254 +3,2451545,-26502570.878664158,144693953.17519942,-170.45460439614484,-2573548.5485954834,-473314.40293698892,3.6551352941262847 +3,2455780.6185128624,105645461.29676957,-108907021.64669758,2793.9896788983046,1805423.7614337029,1782321.7480238089,-53.756338100892059 +3,2459182.2556217411,58393303.764963448,135523608.35259447,-6522.4521358264565,-2405613.4382765656,1008736.6185510629,-40.702987243753476 +3,2460000.5,-135034590.84294599,60716760.608444914,-2422.2966263232124,-1097259.420800749,-2357174.8929438298,128.59845449763768 +3,2488482.5704794307,-125072151.83811666,78762082.856529325,-15526.59994477988,-1413200.522993651,-2187632.9450550205,534.3619144530968 +3,2489819.2025211975,138336406.33030823,55838303.181056008,-16342.387883410036,-1005083.9951052838,2376815.9287381172,-537.43463126762288 +3,2492685.2748244773,127448382.7669251,-81666009.531408712,17637.168564783173,1346764.4621657394,2157232.1248993133,-581.99233171737353 +3,2504033.6608409234,149001903.98494816,-20988324.523417957,2256.1416722391527,317275.74693319626,2538642.6912232731,-836.46133723586263 +3,2504602.7596786916,-143470795.074716,-42713918.696756542,18743.55157443769,692771.78495156334,-2476674.7721482934,793.01512386164291 +3,2512905.4735039109,-19759663.11139622,145787559.25074819,-54701.821923646647,-2592114.0283941482,-355627.43300621823,226.86265901519278 +3,2523346.855778534,93955105.089343816,-119292618.11895809,49262.284112349589,1980339.49533416,1582479.6267289852,-793.63685307933349 +3,2527986.3878183668,-137369089.29334643,-60225833.039895535,34973.540778753551,991906.35977525485,-2367229.6735181818,1076.4529904452784 +3,2529004.8281801906,-87202645.318379521,118709007.25100467,-52634.227951305089,-2115826.957999886,-1533862.9173304292,838.7762453586721 +3,2534204.6539322813,-126973690.48171455,-80520289.876087278,47648.965886335973,1336915.6960493869,-2183722.4510780177,1051.2597913317359 +3,2539832.3931754557,149875396.59254009,-11763882.956999924,-1847.0148301324639,159935.4423546294,2555726.3473824835,-1405.1079438022875 +3,2552765.0013598762,-121090699.70944327,84602071.04080826,-45223.540344218061,-1515429.9063848935,-2120014.1082991548,1427.8718847793959 +3,2554081.6564721013,150162166.16685972,-850546.38394512609,-8838.6469375721936,-26917.397196803959,2563526.0426308983,-1625.5557972412917 +3,2558285.6269039265,-148182962.20381489,-17888134.901124261,21979.426493493454,267036.58795215795,-2565365.7705797665,1680.2533932556826 +3,2558371.8932364495,1248388.483492488,-152001925.18433836,100658.99187427576,2532289.747012224,10918.874772678826,-179.4437676784693 +3,2563235.3643878875,138727443.33867124,54997012.728728287,-47754.042256185996,-989908.13499372115,2382199.4308211841,-1575.4886173099603 +3,2569990.0017094421,-138208734.75112897,-58139196.002090521,53415.50666112583,956639.69416408055,-2382665.1731449254,1675.6873767518255 +3,2579255.7230520854,133409670.87254651,-71237714.065146446,45042.551267348819,1170969.5479663748,2259998.6418462377,-1883.8490981844586 +3,2580334.9513992053,109117279.18879056,-105369387.07114293,74988.789097115543,1746549.8775629075,1841068.8044384667,-1610.5826073533524 +3,2585619.0314996373,-86033575.40095745,119550728.47904891,-91681.951831620288,-2130343.3796984991,-1513809.3354711833,1446.1299933124274 +3,2590927.9373818147,110086845.34143028,-104338471.54220723,79810.386571604133,1729305.938269506,1857547.4000994023,-1758.4644852556564 +3,2594131.938634336,-82685313.141472936,-126651543.58379926,119655.90544159478,2113779.7257669647,-1417504.7979442,1050.6632141535119 +3,2594454.9199183537,-144053355.8292357,-40432765.444242775,49481.342446562252,654267.45384081511,-2488459.8412133236,2140.7005429001429 +3,2599132.0830741855,-82948797.029328212,121691831.08966888,-102948.03181166803,-2167890.5476967101,-1460014.4510323354,1540.8271801377296 +3,2599597.916308701,-107721069.24867189,-105540837.42516913,107064.87105286878,1760059.5689963063,-1848893.368115522,1519.269335427522 +3,2599795.5498471037,82374904.509327263,122866268.32021031,-120878.81839351101,-2179012.8728040005,1422714.7326363705,-1090.9585371616918 +3,2602719.135731013,79007801.776871607,125003913.9938866,-125124.08454083838,-2216852.381188455,1364519.6336056162,-1051.5606376481949 +3,2610128.653321431,-142254465.63456875,41923113.855692856,-26257.405093118468,-768818.6702577261,-2479298.5168556268,2511.7661183371783 +3,2612009.4318707641,-119552800.05114856,-91468161.830381215,103896.0047373656,1522806.3859761443,-2054627.8439339323,1879.0890629943024 +3,2613361.6542204674,-46865697.775102489,139470315.88464218,-134326.78504103352,-2480877.1114372895,-830303.51034720975,1097.2917936555664 +3,2623207.6890664985,-6133148.7716235351,147048132.72161013,-155420.84931344297,-2612766.7779649817,-117825.37732443417,430.90343045279064 +3,2633947.2335361363,-77651518.638522565,-129861997.76119569,155975.90221696885,2167938.7496219794,-1331466.4828671366,1233.9865337485935 +3,2639963.4621254038,103352124.60400124,106335175.79587613,-137096.11071120756,-1886682.8151974948,1783271.0217673888,-1831.6238181411677 +3,2646597.6717841192,-41065696.398070909,141287104.89087972,-164692.94541715301,-2512498.3871785281,-729036.7264011834,1220.1441960702577 +3,2656885.4775461224,-144969194.89091039,31923077.282294001,-19537.3627225251,-594506.78481299209,-2524318.6149398903,3287.1822922308211 +3,2677018.0529618911,-129323575.32998081,-76431950.516611591,127401.7895153336,1268641.0327441106,-2226655.386988977,2896.594025087501 +3,2687156.7457698481,-78237588.102759495,124738926.9485514,-167873.41812346558,-2221194.5566240051,-1378537.4418958307,2379.3309720992934 +3,2687478.5469928114,30962033.602245949,144111163.68020093,-214904.30030768938,-2557193.1849871594,529765.08970899112,-333.93620951021325 +3,2691739.3598765568,111285880.63626042,-103045179.63412993,133131.71872914929,1707751.9568892352,1877555.5996259602,-3082.1990790775471 +3,2694998.6611173609,53085407.804646604,-142470044.54057875,204650.69770470588,2371044.9782421952,887693.19312099984,-1752.1036579228369 +3,2699607.7463223063,-133891483.96437524,63031305.892891757,-72002.142758700356,-1136992.4733846572,-2339647.1053115595,3783.2988162015422 +3,2712994.8481145767,134034969.06141903,65318287.9567357,-131293.15949098396,-1168117.1580433119,2302559.8832108085,-3484.1650726372031 +3,2720192.2663031323,31046979.72788436,-148838962.82236651,240158.28376969651,2478883.4036636152,514294.95084334986,-1348.9065214259449 +3,2734478.9223056557,118769006.59228401,-94145068.184117705,138620.17009446988,1558196.9512451862,2005821.8321727379,-3824.2553256477718 +3,2735766.2260085526,-131645406.36674798,67454162.141482398,-89607.261540026651,-1214323.1382982663,-2301713.164645215,4287.7679867646093 +3,2752197.1942264545,-124256034.33644654,79943905.784949973,-119187.69078103271,-1433068.5216303363,-2175836.7422941532,4356.1742847982823 +3,2758238.490190743,140874733.90005955,-54480259.478974365,69540.28018322635,887829.11816629942,2389029.886272348,-4714.3873750833636 +3,2759166.898219761,-148605037.11258277,7321211.307623622,21653.675632352337,-167019.0491953762,-2582020.4411891531,4930.1592116364418 +3,2768290.7817972419,-146083323.62731701,26641518.474851143,-15862.704909435051,-502136.28736247134,-2543363.9612122225,5079.0099674622679 +3,2770125.8040816123,-148772794.79671443,4171913.5172904702,28636.166757742481,-112620.89757022774,-2584172.7619273458,5090.124288144063 +3,2770289.6663790005,138740564.73909503,-59893951.207406834,82963.167516203088,979516.61235083942,2351557.4333962086,-4852.460234543546 +3,2776319.406326449,-141477786.47073495,44237438.68813888,-52284.683945855031,-808474.6397060107,-2467932.5366927022,5132.532099198218 +4,2123113.0349457869,169645325.01381919,-118332722.28601916,-6928585.6350790635,1289123.5480610505,1888372.9053240523,4702.7612428950169 +4,2127795.3645717041,-78032699.119610503,-209209803.01036894,-2241538.0028977222,2051796.6921908711,-559138.92873782071,-65804.148226055433 +4,2138104.6861491553,-68451583.494976684,-211649275.03314325,-2551488.2160453503,2081811.6195760921,-471734.19905077893,-64688.106626367015 +4,2143897.2885015784,83589941.16278632,212403974.8852616,2172033.8736001253,-1857418.8809273182,937609.33705121395,68304.337918905425 +4,2150094.8669681395,55419459.042192914,224291341.63690257,3164075.2536412505,-1941985.5174530847,673283.57437014079,65023.32018098791 +4,2156031.1187056098,70802001.770389259,-199323367.99133694,-5972043.853912374,2062097.7846084624,873832.51675802225,-36218.007088009057 +4,2169854.0779234455,196472315.143022,-64980277.802895755,-6493329.3864460196,747215.0098060678,2159829.9826729107,24980.40629461201 +4,2173847.7005283888,-25884761.946245044,-217305567.46284693,-3807805.8524572891,2167313.9105957923,-74133.372850670654,-58313.356927994741 +4,2174730.037998172,196208609.43652722,83864746.326499611,-3409001.7890076013,-732568.30285630084,2097019.6153501892,62479.038630553208 +4,2187010.1218484947,178953087.01365036,-103475672.48300931,-6812932.3606704017,1136847.0434628574,1985216.4861966881,11314.330982371532 +4,2191692.3820939488,-61822623.634353675,-213265195.1393767,-2793642.8292404963,2098787.3687698026,-409308.0803191727,-63235.53125906847 +4,2193343.1313160704,113329214.79442605,194147201.61351764,1056599.7370413269,-1718957.8771709725,1227894.5934295177,70220.13280006159 +4,2196093.6560216988,108876912.35167696,197245973.02786848,1238268.7686739669,-1744015.2287073245,1183952.2231034229,69942.673036993176 +4,2203231.3770349962,-244296458.84833631,-21277800.925866634,5919402.0192666603,268497.76620476466,-1912309.0025312218,-46532.843559259964 +4,2206390.2505630087,122626045.82446082,187061615.83904162,679948.42779132572,-1662267.0906534032,1320582.1128790977,70554.106063043102 +4,2212798.3843579576,-237129479.69922173,74716259.409015909,7705436.5711035272,-542172.61991492286,-1823454.2637537667,-23650.059222177853 +4,2215537.6106538624,-231877773.21279213,90343733.867995501,7889506.7002875423,-672966.46057336195,-1777143.2257908643,-19307.225651398596 +4,2220168.785808885,51371652.422582403,225311110.7683236,3331306.8862268901,-1953407.1489966027,637894.15217659948,63862.90098594447 +4,2222230.6847840799,49476311.069073729,225936042.08346221,3394590.5332879019,-1957244.0751907926,620462.76213057211,63577.367773736791 +4,2225351.8379692216,-67366771.710258275,-212260289.69295841,-2650064.666077795,2082286.9519091342,-458604.43312512053,-63432.695582161352 +4,2226714.9654775043,-89492167.240217909,-206180528.44713303,-1951498.5861476827,2007508.4560931765,-659173.39600163326,-65635.379827655343 +4,2230008.3344342182,-246550574.40793842,7798400.5157496287,6540868.1528948089,19783.893127021642,-1918711.3636488814,-40254.369689858228 +4,2235617.2172599081,-143020346.6247828,-180549324.88885352,-44446.070639633523,1727602.5857428545,-1125478.7854467633,-67962.944363549235 +4,2239455.3988043396,-50538559.693794653,234453209.22728118,6163366.5682347007,-1959177.3146639678,-268064.04498083959,45031.289166961105 +4,2244227.8197905556,22119882.849712588,232697333.7463007,4252174.2167414008,-1997018.4219869385,370930.62537675782,59199.411634333534 +4,2244866.8475764599,112801511.75172092,194272849.24082562,1118185.505388546,-1722974.2125274097,1224711.242029195,69818.39118792268 +4,2248541.2957229535,-244989733.72559643,37280627.624214903,7085445.7230677577,-229261.03433900399,-1895278.0520073532,-33387.11315841641 +4,2263212.2346964045,94753723.812403008,-187720599.53619394,-6329078.7466565287,1954442.8119548073,1118667.4636579317,-26981.297357937274 +4,2263306.3837560201,207482408.89712873,-14585519.426885368,-5631430.8112333566,233643.31524687199,2262774.3844072134,40955.417530933555 +4,2267110.5793651319,-246319691.73815671,-2736872.8900927696,6263923.7349811569,108024.34193228693,-1918691.3737874597,-42598.468095384211 +4,2270286.4218801963,110526739.65136781,195734234.83412483,1228591.3946142364,-1736625.8266831099,1203130.9774756378,69505.486020579425 +4,2275884.5760737974,-87658318.89073585,226033604.1341134,6939141.3792371461,-1865981.2318239934,-582923.02753428824,35678.8927590733 +4,2280256.3695080988,-166961672.05510375,-162936362.99684522,887010.61611706507,1546706.4875352434,-1322960.8906711934,-67050.533521097081 +4,2299656.7972613135,154730347.35682946,-137978167.72162998,-6810443.3995764069,1478011.3102088047,1738160.3381895858,-1502.8718880194483 +4,2303402.269203268,-154055629.39778405,192470576.08313009,7923069.4395677587,-1549815.217563757,-1133404.871085796,15865.785497967736 +4,2306519.2365563889,143365327.30602795,-150394018.848268,-6774353.3870361969,1599625.5325221827,1620389.9001893818,-6977.2571662339606 +4,2335563.956944535,84280200.530172676,211101290.31262013,2268388.0422518039,-1860218.6058178663,951394.95252414222,66808.791572105634 +4,2338728.2978442991,-14891255.98638241,-218004922.0580104,-4166690.8195951795,2171493.2770035462,34474.233245853167,-54097.825018123411 +4,2354258.4124045721,-182741362.12891811,167486397.36018407,8089252.1840300355,-1331845.7757078107,-1366979.6583888475,4996.9190746774566 +4,2355260.9334874172,81149625.890903622,-195093177.23598731,-6108598.7892291294,2015621.6188184072,981674.31402463466,-30212.526458221633 +4,2379052.4411693225,-240252421.45770839,64332227.150182679,7350166.6428052187,-460496.5843351942,-1844981.2427662532,-27008.83521575552 +4,2381556.983789904,143127473.10586086,167612044.83236706,-76937.782855225232,-1509467.5631579298,1536156.0524397432,69801.459805753155 +4,2404981.9574490115,19664994.454249669,232436831.85127446,4369725.8484374797,-2004578.4588565957,352994.71251588641,57198.709027249824 +4,2408033.9721599305,-168146953.71186453,-162658352.84904867,775270.99542372092,1535730.8082925442,-1326758.0884550363,-65884.824419238677 +4,2412745.0615201537,-247034482.32550263,6218560.0392962564,6258843.0682784189,27193.992374279158,-1914894.514934916,-40718.066174475258 +4,2415215.4677391909,148341347.71463433,161891124.46993902,-292503.05768463202,-1462471.3071019789,1591772.9468690171,69548.518144970003 +4,2423075.7402529335,-240858613.90874225,-43403973.712841198,5052528.5018781042,450546.2492867948,-1882198.7362451658,-50530.987101194631 +4,2436018.2546131993,-194970172.75350359,153800671.87094912,8029888.0224727076,-1216915.7108330608,-1465426.9860164903,-659.52592807958831 +4,2451545,208048153.2106522,-2007056.3593485206,-5156289.6411088854,100454.95495709612,2271979.8709806567,45126.439648045038 +4,2456340.8760012174,204815548.76836407,-31404803.562050201,-5686738.5274491142,397528.66411029437,2247827.2437879201,37335.96727644722 +4,2459036.1882308088,154004719.13764971,-138954542.60754138,-6689725.1454220014,1481943.9041166191,1733508.9875466446,-30.305129523003487 +4,2460000.5,-98563777.290617749,221732625.06119835,7064826.6290333532,-1833712.1052442756,-672397.65601629508,30887.745854786615 +4,2465340.1544623259,179898297.62481353,117754877.05563647,-1938735.1947472345,-1066686.8061785963,1930349.2917226185,66592.20840667533 +4,2470453.3672715141,-247110975.83001322,1940462.4924449222,6086007.9308302561,61126.619295657394,-1914172.9877650891,-41633.040691204675 +4,2471623.0257334891,11354822.489547646,233408763.83357087,4615891.8602974247,-2012385.6429902106,279784.73732476973,55089.469573685768 +4,2472762.5104311025,132819580.99833027,-160647391.28110349,-6616566.6627190672,1691985.1073590796,1513786.9229256329,-9632.057301856903 +4,2480244.0148984119,-20135941.348666333,-218530378.95609277,-4091694.1747443872,2162874.4718723046,-11847.665505636136,-53041.803185679171 +4,2493951.4627856016,-87769842.818381846,-207980041.1469889,-2229859.3020672728,2006299.237146927,-633402.134749213,-62099.834562723539 +4,2503450.8331075516,-241658804.06807217,-41053770.455058627,5003843.8683744362,427135.34128361859,-1884297.2580368617,-49931.459918647146 +4,2506415.4980710978,116803129.52151398,-173785384.51293328,-6481547.0346630262,1814960.1022497846,1348147.3497248585,-15709.621426048809 +4,2512080.3439736152,147151541.6640968,162569672.94728148,-149489.83995338395,-1474437.7716997056,1584670.8093146598,68995.182050365518 +4,2523377.9062283342,-239553125.3629677,-49931814.33257398,4736850.1524098283,502905.97363391519,-1869127.248657556,-51424.05254807802 +4,2535207.1375189004,-10263438.558421809,-218559534.43829498,-4348063.8749862453,2167470.6411799509,83628.057148566644,-50432.032569064235 +4,2540107.0117029399,163725906.17906001,-126793772.44234832,-6604218.1667409157,1358236.6111512866,1836348.6462197867,5950.6682698172544 +4,2541391.2220755946,-7484879.5056934878,-218461714.94429103,-4413587.2690519365,2168330.1377007524,110061.41194467133,-49820.665834589548 +4,2548327.9769965075,128374994.82663053,-164617506.68735364,-6543991.5530863851,1726898.0521512334,1469041.6752608311,-10545.790537222243 +4,2552167.0216208193,-238725798.29827631,70937112.77392891,7216095.2085210057,-521471.5080583269,-1825857.9627659102,-25908.62506741142 +4,2558348.0965355625,-237818986.47739956,73999093.830877289,7251414.5097503355,-547435.3447088073,-1817985.3172134024,-25145.1734560486 +4,2586173.6986432099,201375215.80958611,-46814245.450125106,-5774452.4180530785,549402.70335128019,2220495.9920893572,33707.755988768295 +4,2586821.8817577441,163867373.46283403,-126622668.70313495,-6562230.6536724586,1355223.560281506,1838491.1241320842,6510.9173961287825 +4,2587457.141883492,73671087.266344398,-199178416.47750264,-5947043.9792338191,2037835.7426207336,908991.54717990174,-29282.682353441807 +4,2587799.3621917288,-135464528.5410161,203947489.04182938,7516061.9106782153,-1669433.7727751825,-977234.06712890766,19088.3075639368 +4,2592767.6329422677,-237088826.67679438,-59244199.114133552,4379951.095642183,580791.81881398719,-1849313.1101318602,-52750.038881711334 +4,2595570.6256607161,-181587613.00844711,-151272842.49175256,1119453.5109126596,1413671.6040591232,-1426358.742967742,-63584.753773242322 +4,2597165.5957643311,201443754.65521264,-46449380.007352374,-5755004.13618044,545249.37371375225,2221675.6836766428,33884.842983558046 +4,2600287.8114953237,-247531281.40843168,10053128.078717042,6075701.5649824059,-12060.567460434442,-1909927.0791418024,-39968.619005501518 +4,2607492.6203447473,207910283.48000905,3946237.1335029835,-4834152.4297171207,34873.158821671328,2275093.1031386591,47133.878015930663 +4,2623222.3024637862,149985971.19654846,-143765007.14643833,-6564792.1847036825,1522018.4760895134,1693844.0033044366,-125.98436968495292 +4,2627245.3637496624,-47326786.289307892,-217371618.35602641,-3471612.8774654432,2118344.8273771852,-262168.8864667021,-55375.139977410101 +4,2636740.451056798,-233319964.55748531,-70709499.781218261,3984574.4575609691,678858.01508645015,-1820930.1706320157,-54355.514830413551 +4,2640142.7673517196,-246900195.82703435,-9617888.949677866,5587800.7943062419,153032.09804836285,-1909264.2829181168,-43882.833002781044 +4,2649868.000383121,-140663619.66752079,-184331339.04212555,-600757.74815180956,1736036.7095832503,-1086604.7357436009,-63549.393204489781 +4,2656833.4136699531,55268848.827246562,-206654734.80571753,-5654558.6805851078,2094225.6965362916,725071.13246719725,-33589.453647850496 +4,2668851.9062620592,-116775325.73924135,213492637.11464775,7227326.1615658049,-1765301.1661847329,-822132.20729387458,23726.200467235802 +4,2671582.1173583907,-84380467.418774784,225666703.16741255,6729681.4387513325,-1889460.1692464564,-550935.40942225081,32312.621941325557 +4,2676303.9893228849,87174933.860026062,207817178.63143951,2365485.0766224195,-1859037.2051736908,992378.0141705292,64162.644892580473 +4,2684668.2380999429,-143644910.118664,198627096.22668353,7527523.1439468767,-1625693.3142772024,-1044051.996662237,15620.712976411145 +4,2688095.6827056976,-131334625.84501463,205982754.75346962,7395630.9867256144,-1694687.5245221625,-942518.3643861427,19338.608304208996 +4,2690344.5702751265,-216979935.64346993,-105722092.60659036,2788351.3053439748,987091.86331160087,-1698307.2055209773,-58745.205607734664 +4,2694404.4992509354,-247734449.70718199,8531466.8411612287,5909637.2157671954,-2277.3590119044648,-1908668.1941973555,-40292.28696598651 +4,2702335.0091680395,192159046.20127708,89613602.379297972,-2539307.4716934762,-813655.46648931282,2081352.2604813443,62789.413978938639 +4,2703516.2717751106,-13070491.535139643,-219259857.97886568,-4334275.9614902586,2160258.3414121415,60317.42432517586,-48560.365278031728 +4,2704751.205092866,-233598533.65811998,-70694288.360857561,3892469.5835386026,675575.01061384613,-1819902.7420671803,-54066.251306014768 +4,2712309.2122985488,-232799377.84313229,-72894446.828920007,3815760.6348422007,694733.48985447909,-1813599.2971349312,-54345.227917013712 +4,2735247.0168861626,200083615.18083113,-51093689.29920873,-5658788.7229037462,587687.81477911049,2213274.9483570578,33401.065820047625 +4,2735868.0731230946,117714012.40432478,-173592957.99486879,-6366140.2799300663,1801799.2504191454,1360015.6001236464,-12429.835753958194 +4,2746785.936825885,-34934866.747886032,-218994782.0922069,-3838654.2065368756,2136295.3150548805,-144134.04970869905,-51773.056901526172 +4,2748781.4771107044,-160233112.16551113,-171166792.37276465,29043.599033149669,1596551.4941868454,-1245646.1224134518,-62766.262792688773 +4,2748820.8191905124,-88981265.576721385,-208757645.25198132,-2391003.7377972472,1994334.875100231,-635458.02546885575,-58914.09260691324 +4,2759589.5310204253,-200912833.97868666,145918780.64968872,7656305.2616522554,-1162594.2358414524,-1509136.4482597501,-5534.7114523534901 +4,2766271.320202806,134062952.94144316,174057156.15294161,644852.09700874356,-1590634.8962365494,1462085.8050994594,67055.517518414315 +4,2767540.6202090513,202915021.38749847,-38994685.407819957,-5428477.5970616294,463728.10882322147,2241262.1073482623,36959.542173689944 +4,2768777.0068734284,-18488780.071072724,-219594490.02783594,-4231997.6567985564,2154231.6757909236,10647.164150211775,-48613.228113276935 +5,2135878.3255382236,-3047065.8231792105,768313221.10374594,-2720532.6108266963,-1142146.7096663036,46616.10840449122,25900.780702840602 +5,2155908.6966380966,432033030.55674851,-630173266.60188508,-7532765.7440819098,919370.32349630888,689753.04068318359,-23482.514045799493 +5,2163851.6409292547,-339242527.02904052,-724216615.83259058,10387902.67141897,1011122.6309030496,-427318.56837138731,-21445.765954203933 +5,2171995.9205286475,-738430542.8439889,-341566950.69307816,18067395.562407993,463021.13635597914,-973950.75806142937,-6951.0682241655732 +5,2172248.0988236973,-580961764.47954011,-562496559.75238621,15297000.585456602,774517.57647153025,-760305.18807089573,-14828.537116441117 +5,2174855.2950005797,16736910.945616363,767962425.17135835,-3210806.662587997,-1140699.9522602332,76052.465718202322,25683.391146527705 +5,2198091.3403767394,-685452028.96469963,-435368079.00847256,17199836.471754167,594121.13446560851,-902809.55691585806,-10130.054922330324 +5,2206451.617668035,-804394777.22169411,-130494566.45087983,18738660.87720805,168810.18017305,-1063151.0185583043,150.15140717266868 +5,2207753.7489798958,285061224.10494488,-716168342.9091748,-3786062.3516559848,1037648.7889323725,470449.08606522146,-25304.028873773925 +5,2207875.7400087798,406339108.96991539,-647484050.28701413,-6794846.2734137252,944856.02588027203,653290.76807081827,-23883.380793344331 +5,2212051.1628840589,248915213.82872668,-731201712.11532259,-2908378.4565397929,1057904.7972681662,416861.41403807251,-25561.532725076453 +5,2223959.9364344976,-750233782.30960453,-315239945.72834736,18197036.822597411,425621.17672258813,-990602.35421217547,-5914.5481993652338 +5,2229904.4322534814,674344416.72418213,-324426840.29523939,-14051666.128683126,477497.02301179682,1071145.3293937673,-14858.233278236288 +5,2235420.0998771777,118978462.94445989,753968025.42836154,-5545141.9705997668,-1127315.5362248705,228895.99037758322,24669.772643992514 +5,2240621.5745898732,-710019384.35323632,379926481.2228232,14644895.432138838,-544028.13274631288,-944436.86945017392,15895.674923003984 +5,2240932.1351602222,-810666627.3182199,61468606.249013163,18129681.64752632,-96509.275877021093,-1074901.8017186774,6254.8583712603086 +5,2243632.109119867,564380375.0605886,487455023.7593739,-14625855.295836536,-750117.58322645118,906620.95141659048,13535.683345136869 +5,2251111.8840108467,319076297.09875643,-699780306.9242326,-4557594.5262283422,1015619.5942430289,521769.98383654369,-24955.973592289938 +5,2254853.136078815,-321520980.43478537,-731771162.53342485,10054213.205454556,1022257.3555024019,-401603.34238638123,-21594.194624575648 +5,2258494.7909046956,-793180480.13682616,-188067942.4013246,18657614.59179737,249211.95369271503,-1046897.4187670129,-1646.7396889165921 +5,2268238.6554903453,104649565.35543929,-772907942.32516539,592730.69644234097,1107526.4021670374,203243.79412089699,-25808.638443091422 +5,2269193.2636266379,741947858.11283934,17579697.20213934,-16835043.754111543,-38986.798697749531,1181458.4973462624,-3639.7805359017989 +5,2269466.8879292775,665156714.49150288,329567559.16794699,-16293485.497785656,-513726.12982995808,1064362.5351816225,7537.1814705930929 +5,2287760.5541462689,-276151942.60576856,731170709.16898525,3416327.659820172,-1068676.182047656,-347353.22425277147,25459.798236612856 +5,2288964.9040576192,-741988859.07650888,-334845780.68786681,18038434.666304182,452700.72926614358,-978087.99853645125,-6449.229297821189 +5,2291839.523325678,6650752.0829198807,767825249.32015693,-3110408.337593189,-1142147.8342031895,62017.00440185592,25535.151703805543 +5,2293064.7521002283,-809740633.44543624,-94952602.425346777,18637091.640094239,118735.05717691079,-1069468.3157075772,1445.5470224288838 +5,2297628.8028588234,-743257684.63461745,-334606411.48020327,18060664.075355511,451006.36519549147,-977128.09062922723,-6404.7228669891883 +5,2302385.5683018104,-441595909.90902531,-672164640.99954176,12565456.752101889,932288.13530485146,-567471.04829105712,-18835.808774679925 +5,2305986.5098443795,-814375451.43096936,-14394128.892201914,18427867.736655686,8404.9341985798237,-1077044.0912157907,3982.9805981491536 +5,2313999.1574641466,-516648820.920048,602663321.64874995,9304675.4469932672,-870450.51326585526,-682999.20656381128,22272.096410370475 +5,2325684.0196937127,708033412.11787009,216962347.91548991,-16803203.865218122,-343154.77185094071,1133831.9421844189,3305.0938369654909 +5,2327619.7010349352,-814187853.3452177,15840994.371353958,18285462.200133279,-33834.745080292356,-1076739.7487393997,4969.1841206079234 +5,2327728.0552795283,-809100478.87503994,-100582783.16584736,18625692.34531872,127482.52295230946,-1068364.1935171569,1301.3386051936536 +5,2331423.0743512763,-594063320.73162019,533213904.50715971,11292740.570157016,-766713.44209831418,-788610.80376481847,20352.468513548334 +5,2334348.0749315764,708979048.51447284,215483879.59157023,-16808648.927361492,-341117.3637279378,1133628.9118001279,3244.6253981217392 +5,2339380.1636606455,136266227.09643212,748648390.11027598,-6007318.4813273987,-1125189.7052970279,255946.80686510014,24331.358713335048 +5,2339599.4806024712,-113375741.72196691,764227275.9734813,-447258.25256182998,-1131207.6377415068,-112337.5249908409,25912.888818382558 +5,2345624.1771074785,-517783266.9723388,-619971842.03217041,14099341.052438948,854496.86654774845,-671002.82923200063,-16592.054525204618 +5,2348742.153723225,-587015927.23855531,540783816.74718881,11082377.471218126,-777718.2736791257,-777833.59076789219,20573.294093635825 +5,2351187.3427516278,663532027.8603363,-347931422.95215523,-13555227.214673333,512245.25393588393,1053522.0672377953,-15682.268646937458 +5,2359131.8720880845,6228785.1798617905,-785764392.4214884,2973192.9941578507,1115851.6789193435,62028.198247044202,-25348.335935457442 +5,2362474.2706970111,-792725404.84204733,-193857371.86491191,18600874.916395288,254466.61796599813,-1044333.7384978195,-1585.2754299658877 +5,2364440.5977529427,739360775.94924664,-60944235.487545736,-16391953.183173368,78972.304199014485,1180062.8419603605,-6458.2903589767247 +5,2368382.3174722637,579759210.02500558,-482653471.84013927,-11117153.694018152,709855.41876761219,922164.00656685093,-19633.024988508649 +5,2375583.6513581621,-754952042.36346221,-307153988.80570197,18193187.059686378,412974.02899907454,-994065.80784644186,-5321.2584967372986 +5,2385453.0990880718,355676940.42021853,-680826418.22369373,-5267174.4072548794,987092.32464276685,576788.03502899362,-24493.603253241658 +5,2390058.0502593592,588450618.44636512,-471347592.06673491,-11323880.590584906,692996.91675996862,935721.15593952488,-19330.073935132092 +5,2402562.5217212485,122687603.3345308,-770882210.90862012,365264.76259148616,1101297.1632284869,230353.00435568878,-25658.37023158478 +5,2411960.8065247755,711404230.47042608,-221904673.71853036,-15060752.57495896,322807.69077753619,1132448.7854832099,-11845.656183874011 +5,2413325.7482160358,-194794958.91916162,751962258.77436924,1313523.2518059732,-1107047.42883367,-229791.7484242091,25773.271324608981 +5,2419732.7801873079,-55275616.921830513,-786386752.58741701,4439742.536815661,1113746.8154410215,-26628.954528268165,-24872.133778118732 +5,2421173.2405077424,625132926.40299058,400609459.33871597,-15650318.908327432,-622882.38757850952,1004123.0738820842,9884.9459302137948 +5,2422185.4113829308,-398395912.06518865,678619600.7563653,6174554.3143809019,-987703.75467576832,-519667.68033052766,24267.396037707833 +5,2429724.3302172883,686397357.78019083,277766746.65614069,-16530870.503530204,-438414.97206334636,1100778.5619899055,5318.0813266251371 +5,2429925.9731603209,565770597.6914041,482476859.32042491,-14665698.832324143,-747551.43753481866,913288.93771810178,13013.702914537422 +5,2451545,598567592.11913395,439604704.97625065,-15226860.71770958,-683409.9670259892,963889.92093891476,11306.531424319433 +5,2460000.5,707285091.88147259,219379641.55929416,-16735519.963101966,-347920.30893545155,1132391.9556394636,3080.4903739767324 +5,2460147.1051810491,638280587.7672081,378227467.1978665,-15851495.862463322,-589113.08672378561,1025284.1826558058,8921.2423566974358 +5,2462309.3361005103,-722718422.47921133,-375812118.44555044,17732065.205692686,507834.478860726,-949849.46480934555,-7414.4947178978664 +5,2464763.7685172698,415943605.32182997,623620411.85288227,-11897395.029314464,-953326.83416788862,679731.77157706558,18502.766637834022 +5,2481036.9046858498,581102077.33173168,-481626696.79118037,-10962217.331434617,707474.01968037663,923433.52280144277,-19679.462638182289 +5,2483662.0114963688,-813150207.43436801,-55514230.435118787,18400670.965422906,63654.466876090977,-1074542.2006083275,3088.2225341708122 +5,2484229.0235512331,-552756949.65630651,-591097699.05605686,14831456.173572097,811719.16129432607,-719143.09721779765,-15115.41096591675 +5,2489511.5634945845,424452589.88423175,-636555076.84670353,-6789667.8067733459,925456.4571876491,678943.11324431328,-23532.267819068591 +5,2491570.5338518303,-454285964.13242137,645381932.14961183,7418117.5714086462,-938229.79378556542,-598336.54663324449,23476.462215050127 +5,2499752.2280493015,67499384.654955745,760818292.66868353,-4726864.6070285365,-1139400.9137625177,153570.40184965244,24768.745690158776 +5,2502188.1778422976,89447051.398887783,-776466399.37313783,1291561.3015315738,1108771.1892462706,182858.36416964346,-25507.78121103326 +5,2503793.0969782211,382881227.59184331,645709987.0738796,-11276709.64895311,-985108.28264633426,629759.37309670413,19307.807669651727 +5,2512514.4306263821,328579468.45517731,677291137.97040391,-10211495.533135932,-1030719.5146628773,545455.38064131222,20653.990487382031 +5,2512867.5055438899,-64093409.624407321,767135967.72498298,-1840164.4145437167,-1140156.3249423287,-41933.103587925005,25596.904793911566 +5,2515243.3510483974,153049414.65146872,-764065330.09827983,-155040.01202855937,1093246.7018421993,274140.15773373557,-25539.8203554053 +5,2516927.154422679,243456558.96270022,715726482.68074036,-8481440.4408121556,-1084168.2208279129,416759.34131895751,22382.386562865144 +5,2520088.6952209147,618012738.43663728,-429087511.23778689,-11935257.795130329,630065.72559244453,981962.75280717679,-18225.928071349183 +5,2523192.8059397186,-578484304.33188546,-567963363.41844463,15309436.418406211,777627.1842841811,-752846.26469253434,-14106.928295173046 +5,2528119.1030050176,17243389.14284984,-784834769.55036926,2972148.4186988235,1116096.9428466435,77768.885041193455,-25192.335636826032 +5,2528956.8314809357,714880855.94283831,-210896155.6748105,-15021264.145994512,306112.07677084551,1136863.0794013205,-11679.684688456447 +5,2529246.9921851079,729286639.31680286,128941444.58480257,-16795296.972595003,-210157.86302019423,1165751.4767194639,-303.64941851079908 +5,2530533.1307922672,-424746973.36445874,664178882.52476084,6620309.6401805002,-965120.73080815922,-556136.02889760258,23875.210618480076 +5,2571819.1351465164,411671025.38848764,-644374304.3471905,-6344498.3426672956,938523.09171153232,661950.34351528855,-23722.312881553928 +5,2582218.6518814121,-99571728.913283452,764992135.6070348,-1128605.2499173062,-1134881.9293316861,-92762.009734436477,25586.457761904458 +5,2591240.6544234846,-475409545.50134152,632539442.47280204,7779338.4628433455,-916915.87698833225,-625031.88553833496,23077.33780916714 +5,2593400.2533219298,332632110.66374487,-693299330.15055108,-4344422.1151093785,1004963.2633081481,542668.47258696705,-24670.317841553704 +5,2601771.6154178027,17365880.820692889,-785289893.32109106,3063854.517332322,1115375.2130001623,77722.951632075434,-25066.926412663892 +5,2606141.5071714013,56785386.425890349,-781772940.9459548,2178527.4325949196,1112093.2624244979,135927.23118280739,-25243.768069051985 +5,2609790.0129204965,-618252026.57212079,-527568740.96919191,16021027.614740131,718692.72745664534,-805433.06908243883,-12385.291447676731 +5,2610263.0888662059,-176280413.55726945,-774996798.68722451,7314605.091841965,1087120.9625433742,-196327.93353603245,-23228.193888943468 +5,2613936.763423834,-730543816.60989177,-362740896.58467627,17789487.53834483,488528.51372859697,-958131.29715913173,-6599.9703255166969 +5,2621051.1445405367,80787190.62584351,758931861.35119414,-5144421.7178302528,-1137499.4499902695,172824.71958250698,24425.943164820488 +5,2629110.775722194,642194211.68950272,368231536.1107645,-15851280.785320548,-577209.20293699927,1034914.4862909048,8193.6185530526527 +5,2650623.3883791873,710036075.90901434,205151024.26004052,-16625152.691843964,-329205.53920984018,1140248.5307863408,2187.7609083362368 +5,2653129.0835664799,-606619803.10172796,-540992034.19652665,15837124.518371025,736579.84002732835,-788994.76205011248,-12769.335979621812 +5,2657477.4609578112,-594516652.69662499,-553067768.80917501,15629807.372592513,755000.41306815704,-773022.43952417688,-13233.374601740557 +5,2661497.8709233468,-770473333.62588406,-270399118.08168209,18254129.570345312,359977.65220470325,-1012155.0437416092,-3420.5772831616296 +5,2663071.9105557292,626519520.29227233,-415548129.34749329,-11983706.230425311,610126.72723402327,995479.52108740434,-17957.768467158268 +5,2666157.4994829651,-581990181.32098532,-565032279.15309429,15403999.176299334,772437.10993300995,-757413.54851590877,-13673.223140128172 +5,2667111.4665725799,393034033.68136281,-657789995.97688997,-5732593.5780726643,955211.73388052185,633222.23394016782,-23959.506810529379 +5,2676519.3155034594,733581886.65943384,91590776.338880539,-16631018.169196581,-155622.0708091339,1176406.0663316152,-1871.8415413998528 +5,2690049.8256090512,419815072.99894708,619188891.13392591,-12078043.125303822,-950206.89743123029,687991.16031585936,17869.128335836711 +5,2690746.6324308612,-341011215.78751576,704986679.4588033,4337220.8543816917,-1032339.9369529076,-438476.38635030668,24788.443051106311 +5,2703412.4171453044,25808515.10442213,764415483.07903826,-4062827.9122811141,-1144340.8578327724,92951.081750250261,24841.660977056421 +5,2709958.7702327748,-119278451.81643446,-783494854.48634171,6216629.2449445063,1101931.0547403709,-116451.14222030441,-23787.215936577664 +5,2721325.9537258558,-574190359.3559649,551706956.60378349,10120827.660327446,-798598.50912683934,-760190.20835719688,21123.525581493228 +5,2730034.8583681118,-607482576.91068769,518060825.10246152,11004573.343179589,-748138.10024336795,-806014.7270927981,20219.76361422065 +5,2752501.1730946647,-762817619.5584234,-291532958.14116478,18166307.22808769,387937.53762905853,-1001752.0983276154,-3873.6856466580493 +5,2760651.9312156201,-769558273.78729165,250040270.32502735,15776308.846521176,-365936.46439305681,-1021023.2686807519,12852.794349239379 +5,2764220.4175721435,-144334024.00048193,758610199.17448461,-394889.01729886641,-1126541.8581994178,-156580.14931931393,25535.031252193465 +5,2778992.344652331,-420344926.71246386,-688231883.62691057,12511257.833449418,947444.84195938148,-535881.37083328154,-18306.642384160368 +6,2153095.596797646,-1203747239.0262923,686917034.87202489,33792899.201234274,-462408.49272337719,-722867.3535609761,31405.648650206163 +6,2156769.7502038977,-64821043.341551341,-1506569053.2694228,30604175.566465449,785767.56532732735,-32967.181818435791,-29829.055314946203 +6,2163152.4691946334,-786179733.72611785,1111142240.7761457,9818864.845538903,-729174.16344801942,-480686.17338471895,37229.196625612058 +6,2164564.9490184956,-1413689261.671587,119040372.1868912,52642740.830876179,-117397.75896854889,-828873.70932811464,20003.618802996898 +6,2168756.9533151798,846414734.35839832,-1230182975.3784938,-9977046.9354011435,639270.43519127392,475606.58068493957,-33643.684056628532 +6,2171194.9479112038,1259508693.7764909,573700210.1318754,-59564762.163532265,-395146.19349268067,761714.50141578133,1149.5738048301919 +6,2186677.354613733,-1396894220.4799426,-376296011.3798455,61324498.918336883,168533.84355629372,-803051.63409751246,8334.7776379577317 +6,2191705.7585542561,1412072027.1283298,-248963331.77827835,-50323323.45758155,94376.655611299764,825030.72852548503,-18943.280786352614 +6,2195400.8554627816,-762327564.84003043,1123594552.4184184,8885657.8700180743,-738786.44323200791,-466155.07096576178,37398.601910216508 +6,2198407.576837521,-1039739914.192189,-1063043832.5512978,60100484.759085186,548615.7063306187,-582272.75311481627,-10606.954968737904 +6,2210665.3857177175,23082645.080463223,-1512056110.0218432,26959777.986802187,785027.92881580268,12807.037094489791,-30872.720557235563 +6,2215121.887556266,741142634.22964728,1132501810.8604224,-49787747.258378558,-747009.13832542836,458364.67442187539,20684.76186701288 +6,2216388.9220632422,-333092969.52825683,1303746727.1547387,-11025069.726317706,-856492.88523405569,-204726.14867661658,37210.081417579335 +6,2217709.9421922415,-1231535067.108861,636400928.39602435,36341952.581007615,-432099.58446445927,-740162.13412829151,30488.433490492607 +6,2221685.5904193479,225484589.37111863,-1492440787.6740651,18633270.979589291,776015.88526753418,126755.85154991373,-32623.923111444914 +6,2258100.6093574455,796679348.84254026,1099087613.0225956,-51302319.638691805,-724774.97514513193,489646.07896828558,19434.259419195289 +6,2262367.3934228849,-1304655192.6196802,-656309430.64804506,63104751.460045651,325676.40296530433,-747244.24790950399,862.67032039504693 +6,2268580.4407982114,986704667.27850831,944127875.46571803,-55923191.74129077,-625555.2037875544,604386.55929378839,13549.604395128952 +6,2269643.816742592,156683420.1662074,1335519752.383008,-30475794.849056359,-878405.64954460459,99230.106748108185,32656.72476733091 +6,2280583.4791884967,7644110.7314632619,1343912616.2057099,-24763890.93671209,-882269.05818319053,3993.3378656656732,34569.228469165901 +6,2280648.8255675738,-49996856.913521439,1343004701.891099,-22484048.233897753,-881646.07186238945,-31776.791310736928,35198.2764355545 +6,2299395.2484001941,1425419961.6838064,-189616353.71512699,-52603851.842437632,61129.899851644579,826195.61197093897,-17370.605244753024 +6,2309105.9126805286,1113311320.5166156,-976076931.02142668,-26163732.073705003,500508.1951190012,626381.13297179178,-31055.31210823145 +6,2312881.3162881499,2259116.7643485288,1345218398.0554833,-24402413.956462704,-883464.44273563998,1764.3839459111625,34757.810257130965 +6,2314588.6081609842,-1232165036.0626714,631543964.441275,37097919.219468735,-427398.07608597376,-745813.80121797975,30301.450927095269 +6,2326374.4659422552,-1416046096.6098979,-194467987.33968818,59302292.282321684,65835.63787471049,-828941.66141394398,12333.892558467136 +6,2331842.3304965016,1426411885.8733833,-58289058.472499609,-55207670.699421965,-14466.746613215188,831297.00738228275,-14420.0886973705 +6,2354269.8012687992,1215514994.3597753,666644980.10255075,-59962469.505751424,-448126.07333988242,729172.22738635272,4634.4817734990111 +6,2354777.2401392735,933425803.16690981,998757056.25682712,-54767527.84207771,-656172.0625785907,568025.39878346492,15753.56321830283 +6,2359358.6707434491,-1255094247.0457361,-744159797.66047084,62863144.879211232,380214.37535925495,-720881.20380786504,-2101.718884946773 +6,2365526.3319133134,941110362.3298949,997022442.0261122,-54953017.133391656,-654127.4166177247,570405.95520926802,15649.686590814428 +6,2368738.708181411,-1346143453.0804148,374178312.1136331,46525577.768467747,-268734.55986372521,-805634.72767803434,25005.96412884444 +6,2377285.103205495,152472112.38087943,1339296804.9798594,-29889590.062830411,-875527.49397995451,93398.226375424885,32980.30734841169 +6,2378775.866992339,-1031977753.0711585,901953819.49031854,24742336.78171793,-596192.89883599104,-630881.0309394733,34803.238991734244 +6,2383106.0398752089,230496333.04700735,-1486754985.9843125,17309111.699934788,777791.77926918701,124305.63604457231,-32993.539711863326 +6,2388096.6243061726,104347009.89422905,1345767613.6187496,-27997057.59763217,-877939.30898048042,61759.510408713657,33627.561789048916 +6,2397451.0479451967,1145134132.059675,772694259.79711413,-59058031.899002969,-512322.00996276166,689048.92893192731,8079.9871573723221 +6,2401760.9760931241,-1410606869.1646435,-245300237.99068567,60183560.637302272,99063.452263581683,-825498.28036825662,10664.514826125511 +6,2407865.0991555988,1294964627.2631731,528173504.57139939,-60604965.326928981,-360058.813123097,771364.57557553577,660.69218642475664 +6,2424290.9945253641,-1083342572.3393917,-997470451.76663589,60480019.51121103,521459.14479307533,-616601.23862751317,-9847.4839147164494 +6,2427847.4440433807,1284555248.7480779,-700458833.55462515,-38697275.97632613,354567.05943330971,730348.55724948598,-26880.098094013632 +6,2429017.1947419792,1392248519.9170799,242125296.61747521,-59486914.184985429,-187261.84679954452,821166.15381588554,-6951.6544961250702 +6,2441500.4894659799,428799272.06291199,1282922545.0084364,-39494900.963560656,-835811.45536771696,263033.44653570926,28632.765220244466 +6,2443575.6368564777,-1172509796.4529352,728463872.43313539,33856508.413460538,-486498.570622114,-711380.83355771436,31758.899952841803 +6,2443741.7963145529,-1247130098.5308807,606678911.4115473,38949054.958243109,-411070.08592544153,-753115.81509354291,29490.163899996438 +6,2450947.4282442005,1269471559.7801776,577064658.45271707,-60518443.347338036,-389471.5119082501,759356.80427404644,2270.2094788075419 +6,2451545,958385161.21699953,982856392.47044671,-55213049.947278298,-642126.68947768735,581982.97617267922,15400.776513127179 +6,2455750.3823234607,-1379220109.2632594,-417129904.04515815,62162508.219213851,196218.33331282574,-799831.37620670581,6110.0753938503049 +6,2460000.5,1241392071.8150659,-785165977.84912229,-35750776.639076516,399544.40889694192,704825.60018134501,-28155.498162180695 +6,2462180.5191103588,1034029043.0182313,905044046.07903564,-56919557.489846423,-594517.76099790563,625745.86487178796,12780.06903594122 +6,2463091.8106102357,359071297.92064291,1302879870.4382412,-36958360.029413886,-848788.42107992317,220267.85697060256,29985.00944051359 +6,2473550.7291022469,597569281.93448329,1220673396.6913621,-44923548.689934388,-794630.32519100257,366186.59788818972,25333.005114485997 +6,2476084.6127119409,-1280053265.6345553,542475065.90706825,41661517.720502041,-369696.6179660359,-770255.13387186767,28082.646050688374 +6,2496525.487828257,-645510194.10129893,1193958951.4126565,5259625.5179881305,-779381.99909194,-399840.93185331702,38010.970865271622 +6,2497472.9586579432,-1231850546.9659381,633764577.37501454,38325310.261909112,-426211.20876671234,-744507.90896399808,29849.167899226049 +6,2497845.5785390702,-1357920793.900563,342632911.5967344,48376507.002107844,-248610.41361077389,-811080.00306509587,23904.165178845167 +6,2501180.5070854784,39899381.524902061,-1505754589.0404618,24274237.57591445,788506.33676043758,18902.850113084558,-31850.551249350621 +6,2508238.9229684048,-1232839585.2119586,630563043.10402286,38468427.946740016,-426005.17273352935,-745320.61952374852,29820.994701753356 +6,2510158.3015905884,-1170631565.4441535,-886845552.26026714,62005046.495351665,458812.79919784318,-667830.77826552652,-6900.3072663529501 +6,2512007.4812351018,96096680.840757787,-1501147998.6721892,21876187.621841636,787079.22733817343,52047.531940687753,-32376.917271875162 +6,2512368.9245871082,376859887.97386205,-1454012300.6315851,9835024.3125885595,761573.093213349,208013.84810987097,-34040.94469562007 +6,2524255.5583894355,1116699305.5472026,-973172252.7445631,-28092151.066206206,502056.81377351761,627416.59872232203,-30820.760083429897 +6,2526982.6265895991,863492407.41408217,1058093732.5536525,-52682809.250453368,-692092.63483256206,525236.48572445638,18736.660451451819 +6,2530634.8987397524,-1420474327.9409537,-84016908.926919684,58337618.061748378,2820.2709328641749,-836560.98716668435,14153.803683702934 +6,2535685.4570525601,1365665118.5019059,-490927468.71873689,-46416110.63472642,235634.93588419174,783038.69700422091,-22797.965870992233 +6,2549513.462057454,41832331.900920369,1347235038.3603518,-24587957.7877369,-878965.25315378723,23451.137370480668,34896.160748980255 +6,2549789.9413206,-200564566.66928306,1332954027.1204967,-14610644.25449877,-869997.03973727045,-126507.06885479437,37091.828662328422 +6,2549898.8782727919,-294783486.82484365,1315994589.1152182,-10538115.616685476,-859104.22347787116,-184723.09902331734,37646.61041379776 +6,2550477.7630432276,-761516268.86840534,1123986145.5723624,11473671.660877442,-735876.16683071514,-470320.94824507355,37553.081084076563 +6,2559684.4549945649,541075167.5087899,1243172360.1729083,-42792302.614604063,-811310.47175007174,332772.18146243249,26968.658547319701 +6,2568522.333576519,1431181335.3177528,-41388635.613993205,-56829936.532559551,-23866.336190267193,831599.56149606081,-13103.926391684743 +6,2576364.8290499886,-56817517.446717426,-1505545687.8130903,27689896.851376373,788090.98308960535,-34192.50156408242,-31124.394215130458 +6,2580346.2306431774,1131440581.7716227,792638487.00149775,-58917865.085237719,-524527.61019377015,681459.52453720488,9648.2157252397137 +6,2581013.5017562695,696357529.23196185,1168307603.2135322,-47722515.919187665,-762861.25070891203,426234.72328156227,23540.556240733884 +6,2583134.6942699528,-1016366389.7430738,916859070.64302683,25470374.430108186,-604298.93335450662,-624750.95888597239,34856.870192980234 +6,2583940.7128133583,-1360939268.8598306,322788248.76444793,49356613.229837477,-237402.89972605216,-816940.59108152462,23334.837753671589 +6,2586887.0820796345,-239656017.90462783,-1485145452.9600019,34673882.706960522,776232.81189820915,-138012.50413108201,-28963.694419094281 +6,2595979.8071701182,-1271858186.6728747,-707474931.21860445,63118284.65697483,359068.11616080161,-734958.29909658805,-2124.9921013652111 +6,2613918.92432243,194370939.50631475,1336500939.0743716,-30218100.983674325,-870867.76173920115,115375.09907421912,33199.415269100267 +6,2629635.4980433788,-466722603.97197139,-1422676441.0099819,42585972.082581691,745794.19567621697,-266196.58103799139,-25701.952400412865 +6,2630204.5888735987,-26974909.172907945,-1505045697.0318141,26184028.302195497,787440.10269531899,-21580.295245714962,-31472.93831735614 +6,2643865.4975548293,1432010239.0814011,-36770484.871774539,-57312479.47353708,-24875.867482319398,828172.79227508686,-12771.696418628713 +6,2649135.4012429519,-1410470097.4208636,-184705310.74525574,60088174.313514672,64811.247220986595,-834202.40870072134,11226.682454064168 +6,2650218.6509336894,-1078259905.488157,-996051071.70121539,60131565.448876128,521166.70863266301,-618961.75660623342,-10792.398177633548 +6,2650889.8206543275,-662883576.9327029,-1333743860.402719,48939603.464684971,701098.21500310593,-377650.03347345296,-22094.227278897837 +6,2657560.2208607197,-314745535.25810301,1313483185.7465882,-9013059.6022811085,-855150.22647889564,-200402.80286584201,37914.754948831673 +6,2668984.2928403942,-838861009.74289894,1071580126.3993827,16268167.995128743,-702386.92431562033,-520130.64360065508,37020.133703431711 +6,2689539.6668375419,-46591376.37357606,1350333119.000191,-20275165.504031185,-878444.81527654279,-33519.704997402718,36169.409623894826 +6,2696946.3337543304,1332106899.2272415,-612621023.20539844,-43974188.382624827,305612.48410622496,751489.55253056309,-24672.737575304523 +6,2702532.9992113304,-1391909454.4208841,171958179.38884968,53652819.889443219,-144907.14628620219,-833701.8589925844,19501.479703852168 +6,2706024.9523731228,351339027.73043567,-1461997089.0367184,9607362.5915547516,768070.92831089243,188810.74083501942,-34227.554300045544 +6,2706300.7134307083,558435041.84884775,-1393827265.2905889,96795.967067088233,731099.96886837436,304746.9921630079,-34620.174372566042 +6,2708198.337747226,1426754830.2422051,-227455355.41273785,-54183419.535515741,86571.906908593519,817363.20622134837,-16879.25281171238 +6,2712217.1621662453,-961625672.42610741,970260601.6423924,23198130.149558336,-636947.91939768963,-593759.65112791723,35518.650859726957 +6,2718674.3687459351,1386198250.1801705,-449661271.80112284,-48949988.335004829,215136.62324898198,787345.66484408733,-21510.095849228277 +6,2723910.4933589306,-1366615335.5571339,290124186.1276477,50797832.803687409,-215018.30462817725,-821622.23996358481,22074.506986116106 +6,2725612.9946367131,-1048202197.1666353,-1026811992.210546,59209587.547230877,540452.91728543467,-603451.3884131714,-12184.344562170136 +6,2732233.962060546,255041045.16739786,1331517577.273428,-31935424.624772493,-861733.41812712885,151641.84664175825,32577.775291062579 +6,2740025.2755707847,1344172271.3862197,-575536631.59140873,-45342709.317857601,286449.52533652802,760670.79495500948,-23901.54593992373 +6,2746241.5491961665,-1378154516.4799964,-387060727.84096515,62284689.033006981,183021.18604194414,-808779.54931182682,5571.3021926748652 +6,2749768.9846151434,858290173.63320577,-1221744851.5002198,-15244828.083292285,639930.0366926553,474676.99822430313,-33650.976629718738 +6,2752218.9950055759,1277707705.3697524,575666505.68686652,-61250552.393238865,-385320.11253661278,754082.51874902425,3528.0996937375266 +6,2753398.1626860881,547185399.67222595,1247491189.9309859,-42335485.383096851,-805623.84951995057,329500.82092722878,27494.379190834636 +6,2758938.1342819137,-333124302.45079029,-1455220228.4786191,36878775.794413336,771151.78926528769,-192231.65124702701,-28292.461038585381 +6,2767518.8769102735,-1408600726.6970999,-194061124.88696125,60481332.580539972,72040.616128346155,-831478.03106796998,10359.794552789672 +7,2123752.9964102167,1304033684.6531353,2593298613.6699266,-6954590.608528248,-530262.177288746,237291.41964236708,7948.2094000632414 +7,2129601.4901124639,-1917253176.6484458,1991251426.3652234,33180048.864111289,-427627.3689077454,-433966.60448942066,3925.1871918234142 +7,2135602.944141617,-2385189438.3380289,-1407815735.1785789,25957085.021450669,293735.90175606392,-532455.68490686105,-5974.3958656022287 +7,2142038.5267265309,875464295.43255281,-2792841759.7649908,-22519198.817663778,557407.42464774684,149098.79649026343,-6764.4961078020106 +7,2156908.4085306437,-101893169.30784424,2836392280.5936632,12463778.353793753,-593033.00638997229,-46727.891331720086,7633.0208768585417 +7,2161585.495555277,-2385738134.4302516,1362427166.9177666,36791606.876747236,-297511.66685589129,-536910.39284806815,1829.9277321417014 +7,2167151.4259267887,-2094822512.3101509,-1837788347.4808254,20469401.434682872,383442.19792175718,-469676.9052723943,-6881.7270575892107 +7,2172308.1785284574,638456423.84863579,-2845862477.6811023,-19512250.392579034,570072.2541491224,101875.35349751347,-7116.1975914477916 +7,2189087.7788742054,-961873596.1134094,2631353004.3685188,22917656.68692692,-556818.95278790989,-229425.10724604133,6443.9707140436776 +7,2211767.1053202171,2840488095.301826,922269020.67509031,-33767571.880891144,-185090.89744797902,533096.34021665761,4475.0630174730977 +7,2212542.907768805,2665407785.2846737,1324404114.0118914,-29928406.568681587,-265765.62056705076,501505.07874338329,5410.7627069506198 +7,2217631.4018468317,295895359.97009438,2842463796.0142832,7068740.8898912361,-590535.45345268119,34907.420246064459,7900.0450095977731 +7,2219638.9496332784,-880174905.56362665,2663408952.6440396,21854572.943016581,-562687.16830160352,-212171.44213196653,6576.9339355486181 +7,2220996.3820065907,-1596031214.1239753,2269845203.7797966,29727108.080237351,-484810.71711724892,-364283.77861296001,4944.8205567722125 +7,2221913.6571227377,-2007205730.2729559,1893738451.4170182,33663719.496074498,-408590.57032985077,-453687.36680700182,3608.7901622020886 +7,2238417.8020082596,2706978716.3887062,-1286560716.7039981,-40458367.134072028,248576.77987138572,505704.35087561602,-1318.8916347982013 +7,2243279.7227006438,2652698732.6898613,1346790918.3536904,-29613311.770875901,-270841.72413232172,499283.31420980702,5458.291094290973 +7,2247089.7512494391,1006326457.2678468,2709536634.8448992,-2797107.6612751517,-555532.39779060881,179271.49381540978,7961.265639783267 +7,2247313.1478887163,881194176.62195528,2746810447.8368464,-1015451.2131130875,-564568.82285161584,154309.56190103356,7986.2763914664147 +7,2248429.5026217187,232634192.39097261,2846038729.5553975,7871831.9168469412,-592137.82129369315,21559.813486895397,7859.5599097961567 +7,2250720.8750142092,-1096305547.096034,2572057171.9627018,24267383.533944961,-544717.14531884273,-257491.94522954241,6146.0381118632995 +7,2274430.6049504504,2517467642.5212584,1568959951.8000741,-26916996.702709649,-316635.49895506533,472945.83383472811,5961.3534403418762 +7,2280325.246383612,-479608544.63736033,2780705596.6330676,16928495.141716871,-584232.52660961077,-127337.6513957391,7161.4851403726752 +7,2280862.8416789966,-789786589.63681471,2694698672.7532654,20657924.105937283,-568498.85032793274,-192384.55310894913,6697.8235074213953 +7,2290550.3893450941,-1834196547.0416439,-2113359991.3479664,15878093.792625694,439846.19941850955,-411246.56270069559,-7335.1263493771912 +7,2298880.2865547244,2438309198.4195719,-1730372997.1735811,-38504371.770080678,336690.66444167576,454182.29346878472,-2682.1413724744662 +7,2319153.8051088182,-2540009903.9366708,-1077312504.8439972,29102179.161735859,225355.56795099331,-569371.5780990026,-5120.385617266641 +7,2324341.5736736408,-186294210.312417,-2874346421.4798717,-8516303.6432754286,583211.44457365584,-65001.660104499577,-7866.3428601796486 +7,2327779.2422686238,1704772461.6468301,-2422442199.1548901,-31471436.888789922,476680.23359993199,310855.00012419821,-5035.2434347675317 +7,2334750.7407710408,2798438733.2286358,1037546489.7732853,-32603884.69422856,-210373.56229394369,525539.09295933612,4757.2885837363528 +7,2336069.3736898471,2433163740.1442642,1687955686.5547395,-25334746.97717008,-340532.44161933759,455531.68279205723,6198.2171701972011 +7,2337165.9984414973,2007411631.5488338,2144868244.5744791,-18026984.165078383,-433462.46340693132,374566.4948088068,7082.755726454533 +7,2346227.2659458071,-2514867677.5414572,1087965408.5801036,36972281.720273823,-237791.41172706604,-566950.89392242557,943.94561767485868 +7,2346695.0639148173,-2612696466.3429513,817620394.03340411,37218786.600110747,-180133.95978061593,-587878.73350679176,109.82208162021365 +7,2353451.5425158404,-1075961279.4935389,-2624855773.7109704,4098464.719300421,539405.61810156552,-250962.37295803981,-7985.2593693810086 +7,2361466.0255018398,2777239178.2744126,-1138519544.4478199,-40547281.341504805,217718.1073797752,516966.26542374562,-878.03051749751342 +7,2364521.9667794039,2947687810.1394696,542856849.68384957,-36432207.094728209,-111478.47506707945,552916.20733367628,3534.4401021444878 +7,2368604.1468135021,1663086979.1755157,2402447747.7740254,-12625814.105945792,-488391.1626124066,308592.34800241422,7533.0764021973355 +7,2377820.2014073245,-2677971396.8671474,559229401.92117834,37030133.39517615,-125749.94426094256,-602388.71306592529,-627.36763628620076 +7,2383897.9090333795,-1204068312.7790678,-2561091352.0120873,6091408.8076089723,528010.34804893611,-278387.46678640682,-7924.7103980797201 +7,2394572.805921434,2997591323.4103618,188051867.11141554,-38326053.320266739,-40929.419601316004,561082.31838302093,2619.171658003529 +7,2403777.6541885016,-871878355.48796964,2663651888.4705687,21318793.868200276,-564092.8789014928,-209700.60745059073,6547.2423346539299 +7,2407127.0130708609,-2382455744.973444,1356296347.151974,36066915.631135896,-294789.84710810945,-538098.00666397798,1802.9840667537917 +7,2408672.655375001,-2695009929.5206738,463441051.05521363,36780838.525422856,-105345.77890518453,-607082.57308773173,-891.33052646569411 +7,2411841.1743798465,-2372479920.8069749,-1419335693.3474083,25566568.60676283,297813.64726920787,-531817.11316324677,-5869.6289713039714 +7,2425431.0701996032,2988411297.4692359,285976439.38754141,-37758501.321103424,-60792.05507868516,559360.24855199538,2866.3065541270571 +7,2427227.1492210347,2704839993.1581182,1252079745.890949,-30471610.562078122,-251519.93130594306,505777.31600377354,5153.8142127006877 +7,2427663.1035686666,2585803610.4032936,1467875442.5326858,-28121901.745816957,-294325.27854595508,483656.5972033072,5621.1640980938646 +7,2451545,2158974972.9957857,-2054625493.7976489,-35625467.419651441,400638.89416265185,399829.58211625228,-3705.8464605771378 +7,2451991.1767595215,2329370092.51618,-1868946277.8452768,-37140875.799696341,362801.11343936401,431862.94271435944,-3085.2649249215815 +7,2457928.8481083005,2701026323.815814,1259498650.5763497,-30295813.786817528,-252405.66011601122,504880.22919112485,5142.6389601137571 +7,2460000.5,1975976118.196568,2178684776.5442328,-17522032.07335148,-440521.30934478127,369047.5614090903,7064.6051323656184 +7,2461642.2032818804,1160316365.1829686,2657086756.1267967,-5175748.3660739819,-544087.63756101613,207408.0009218044,7827.4224337957421 +7,2469212.1355062453,-2562578001.3936043,960773850.00272536,36739334.223915502,-211667.08152519949,-578112.79545756185,593.88661063286736 +7,2471020.3579086461,-2734180961.567204,-133976134.52274925,34902124.951670937,24949.360920849886,-615995.67727075762,-2612.9770597004322 +7,2471162.4136285987,-2729291610.405129,-221394272.74140486,34513625.536880814,43869.54438280192,-614642.56585506455,-2855.7926008855661 +7,2476322.0480778301,-1007331795.6910628,-2652392522.6016283,3218416.8851324464,546766.48459358024,-236338.64262263908,-7959.2672149002592 +7,2496316.7715420248,-1140897161.7116141,2549265952.3042245,24195930.193111073,-542147.02700040734,-268857.47185074067,6033.6841707169897 +7,2501910.0519690546,-2725853136.0530233,-263735993.83427411,34287425.677070476,53255.240994057312,-613508.02556300571,-2975.0257565343004 +7,2516822.5260877223,3004955337.3846631,-82111190.343652964,-39150410.211939774,10733.369402031909,561307.93892524671,1946.5809099831836 +7,2516885.1612680587,3005410308.2589273,-46952145.75664679,-39025601.37347766,3798.0477063502117,561340.03801267676,2037.8585993316924 +7,2518917.4442586238,2789405002.010747,1066271099.1028513,-32085307.069817081,-213767.17267812579,521194.22659402882,4695.1299422606044 +7,2523821.7176947175,705075098.85730183,2787679546.2059236,1197285.6608292446,-573691.42342569889,116718.327474067,7823.9432984885761 +7,2527704.2701129932,-1506107365.8470244,2331291616.5963264,28068792.147791076,-498084.38061037555,-347507.4005586761,5151.4288031486594 +7,2528459.3003781531,-1860444558.1873529,2039517450.0805573,31565557.000976373,-438442.87130809849,-423836.95001437597,4091.3950601786119 +7,2535592.6118662572,-2013305584.2130368,-1921683987.1071436,18928537.440666545,402046.53970370523,-454417.62098564132,-6867.3791823017827 +7,2537157.4554273579,-1283702827.9870403,-2512838722.0813665,7312467.9623140022,520433.56609831931,-294923.82713367906,-7823.5234587633458 +7,2537242.6139001837,-1239183467.5531046,-2537545516.7554331,6645146.8537953114,525084.29979031661,-285313.60825023189,-7847.9788776918558 +7,2544116.8007905711,2360203499.4332695,-1833389715.8270626,-37224152.211204596,356633.66400097287,435989.35671069584,-2995.9018965878104 +7,2551952.4632479637,2016542311.7245703,2144636722.7196538,-18159006.382351879,-434047.2868995571,374995.35888716625,7001.2273362058677 +7,2563080.5686167339,-2731337694.6071749,-146348593.52416089,34750179.044978678,28036.646138703967,-615315.25548347388,-2626.9593725580698 +7,2563862.7203613077,-2669510818.6834154,-622772237.27642143,32199105.115049966,129422.21349364557,-600150.44442421617,-3875.0196923042786 +7,2566295.6187331146,-2002877602.7264283,-1933027725.0251281,18807271.539303344,405134.85090060084,-452318.302192709,-6887.9610965429838 +7,2576996.323614941,2917469555.0384703,-737891623.97235847,-40344333.688212708,140166.23186091785,543588.63272523589,165.80514479072531 +7,2588485.8000464565,-1199707541.6303411,2516689107.9321861,24647840.95437124,-534125.50523415185,-281662.49975744245,5847.2570751220783 +7,2592020.8963747104,-2576283165.2132483,915871822.21069849,36557120.5601594,-201218.49254356287,-583799.95739481924,480.14947696218462 +7,2592740.2669495451,-2687795588.2059579,486275278.14963639,36435992.020148814,-108174.67191673147,-607927.54596658598,-816.56101643057514 +7,2608301.3093838352,2983798690.4026723,-391616301.12201869,-39847039.480596714,72010.87273414644,554996.72926158377,1088.8740769823185 +7,2615980.9149100613,643433029.73005915,2804448217.1912107,1842500.8899799157,-577863.76354933681,104105.24055114368,7817.4169548466152 +7,2623166.9559104536,-2654966807.2685618,642591367.44031656,36487944.010080241,-141403.08324388257,-600444.72376997478,-355.15909920179968 +7,2626008.4031339227,-2530425165.6444745,-1074852187.091939,28693151.710297424,225565.47449225374,-570837.75909725181,-4942.0433104575804 +7,2626064.8631824339,-2517496338.5796404,-1107000846.628638,28411932.812075932,232413.82583244093,-567962.88407198433,-5019.7363217602433 +7,2643159.9042026065,2352426169.8079042,1807810800.1526945,-23737146.801840499,-362738.52056371904,438189.35618621658,6242.5819770849812 +7,2658020.1918030609,-2128900276.8162658,-1774926738.9912143,20968879.452178445,374376.72605971794,-480568.55321828194,-6529.9812218404331 +7,2666694.9985832777,2312195181.5991793,-1888603127.1376443,-36429717.882456936,369824.30427304545,427859.64637348911,-3217.8431173865029 +7,2669930.78636077,2997976641.9253211,-238356760.73967221,-39287831.847678214,43279.887540512696,557254.4462825571,1457.2475549246146 +7,2670398.0161253926,3006421175.6188583,22644351.58628102,-38455764.392383493,-7197.9092632112552,559282.56095002976,2102.4267520206845 +7,2674723.0943870451,1996996228.7258713,2166475843.5694656,-17843975.815262552,-435265.26002068137,370046.5935242449,6899.5359662366945 +7,2677087.1458455822,788562999.32053924,2777232120.7463861,-190062.23669976526,-570511.10359394632,133339.08413901157,7796.6504303983957 +7,2677605.595018568,488870082.2600615,2830545942.3587689,3848170.014400417,-584456.53167206445,71966.579034005656,7764.5260110440531 +7,2682169.5641611684,-1985301304.3537178,1915368313.5218735,32313524.023803994,-412737.49364634848,-452518.83122974163,3683.7121304963166 +7,2684642.9758196757,-2669886719.588593,576992186.58694637,36287087.101385549,-126888.55836225791,-602522.35668444273,-551.83605289175466 +7,2690777.7062257593,-1171105799.6934674,-2566804785.1958246,5827870.7556228945,531117.3668880678,-272903.82225032785,-7760.4401813603108 +7,2694209.0968220974,791830923.17126167,-2807658039.6673355,-20182749.215498991,562708.53934006603,132298.90413478023,-6740.4180007235373 +7,2701612.0685886201,2987069042.9029107,323230093.3620432,-37102314.572530687,-65674.453739233519,557232.34127126401,2828.0864674678628 +7,2707609.1820222782,876838530.16801202,2755561514.5782967,-1415584.7631790871,-564919.96316233871,150776.74236491509,7772.3288620552557 +7,2707832.8173061651,749638806.05997062,2786395048.0107284,323770.42207031389,-572455.1487389541,124916.68189085068,7780.3934628512752 +7,2711313.3298151349,-1254592945.4653137,2492930783.209816,24939558.870130211,-529309.24241634423,-291871.95237184723,5737.2742408925751 +7,2716841.5512455804,-2712811631.0118051,-352379513.41999173,33482827.160042789,72050.582164947176,-612388.1743507107,-3098.62314642709 +7,2721019.0382322818,-1398061259.0605884,-2435854122.6069064,9209988.6004023962,506177.3371037108,-321278.65735124244,-7607.9678822738215 +7,2723917.0772977131,233975905.43999901,-2880480955.612865,-13245138.020056725,583798.23177695728,20309.033215176216,-7406.2443562518383 +7,2727304.1327555794,2013554247.2416248,-2186232313.4622893,-33543339.6100876,431031.11702598655,370576.16451992199,-4213.7275078520024 +7,2733977.275089256,2721015462.0913992,1232538773.0386503,-30452407.715287559,-246924.44852230209,507574.66597782524,4984.8198783699181 +7,2735373.5716039599,2282765814.3800907,1887075433.1084538,-22494769.628926724,-377368.13585678389,424434.16136299074,6342.8350897404334 +7,2739571.2578420853,128912214.98517081,2851084085.5707502,8480048.7306234278,-590484.07656358054,-2304.1288923002535,7545.2552104993565 +7,2740730.9237641245,-552506554.8284682,2766902941.5736556,16887936.917616736,-579335.0913311668,-142827.98618221935,6889.7200672402441 +7,2749707.923809248,-2250465024.3545628,-1603912991.2493327,23125930.549562208,339111.53424880054,-506684.26865942782,-6154.6662129889601 +7,2751570.2696092292,-1459423668.3983612,-2396060390.368618,10187022.852795556,498754.33788798098,-334797.00422693277,-7559.5264913567407 +7,2762129.2466176376,3006030094.1533132,-142965815.27043498,-38979387.03481888,25955.057012559024,559991.97731548257,1630.3676657162646 +7,2780165.3175607352,-2323975125.3872609,-1486302870.3606186,24519461.023241762,314899.06875418633,-523249.2817710918,-5872.782801934798 +8,2130611.7016490297,-4501540203.5771122,-443489012.57004988,112617458.92056714,42854.5993893505,-463697.97932093649,8585.1502043687888 +8,2136332.2958398149,-3505296921.1048303,-2874841421.8223963,139779303.58255643,294202.01346768625,-360993.7713388917,655.45853563804337 +8,2140471.5518224426,-2006947264.6863983,-4063324320.8800988,129806911.56107756,416809.21503997443,-205713.99501988196,-5349.4941893312389 +8,2145115.3321211985,86566143.243716568,-4523773551.5109415,91154055.506232172,465939.23373329954,10648.658363248644,-10948.117334711635 +8,2153646.7729220241,3532686672.7047868,-2777533912.1750193,-24024670.221748535,286188.15998428239,370923.58820915926,-14226.849944674652 +8,2157495.174805752,4326019475.9988756,-1163275448.4768138,-75508923.05807735,117900.34334290633,456300.20784389874,-12107.292268177567 +8,2170420.9640699443,1995775696.7587075,3988062798.9405541,-127999203.2187288,-423968.4257163583,213390.2622574163,5359.6944518045275 +8,2174035.9458027477,360007605.02300864,4451652010.1988945,-99927331.257845193,-471498.88537377486,41583.487048723255,10007.057599486005 +8,2175239.5876699132,-209492535.11417452,4464856928.0911207,-87093801.326988876,-472982.40728821425,-19804.664462395966,11276.131466075096 +8,2175246.8741424042,-212938891.06625557,4464711216.3899841,-87011609.99914524,-472960.10601217032,-20173.306968446588,11283.145447883457 +8,2178660.138940698,-1774669307.2422128,4114073188.3506284,-43889438.822904788,-434614.32209375419,-182344.1566781381,13759.651523810579 +8,2194252.4274197817,-4068683476.8971391,-1994767546.4143205,134620562.90813029,204581.41714176995,-419322.3645013803,3927.9615822723795 +8,2197808.6561900261,-3090212675.2035842,-3319085946.2252517,139400306.67769587,341046.60114671756,-318624.42040959175,-1301.8136362690088 +8,2201394.8133116486,-1686056327.5810757,-4206531796.5879507,125378451.50516035,432251.47388775053,-172706.20628791474,-6399.934501251978 +8,2201942.3103989689,-1446660582.8094435,-4294427807.9854321,121672650.24910858,442055.69073657988,-148200.87184365699,-7134.7095732348389 +8,2237946.8255581148,-1384645337.1625447,4258295404.1862941,-55826545.137284808,-448641.44936141674,-142163.19206834881,13256.185338451183 +8,2246471.6720608342,-4212482925.3573151,1611349610.8030472,63717106.04194928,-169600.83936108617,-435637.91310341691,12873.877212551193 +8,2253228.9021657621,-4281573370.5489497,-1478525561.9585075,128949042.50678825,149787.00083083805,-441775.5553788481,5636.5390715918438 +8,2253549.8865589462,-4231099922.3237362,-1619543929.1229346,130686158.91181399,164680.21525501218,-436781.76223641727,5185.5968659797381 +8,2255600.6175926179,-3799217249.9417281,-2471475421.036231,138267734.35354227,254046.73709736229,-390177.98635783489,2199.4157216891963 +8,2257195.4500466366,-3346551542.7509909,-3057371207.8180952,139938586.00300646,312648.59912843094,-343836.68198063067,-111.95093670548243 +8,2260911.4868015177,-1966079746.8493695,-4083473592.2128506,129311044.57189198,419359.36570602335,-200507.88523263048,-5504.2028925456198 +8,2264337.206939836,-434743777.59809506,-4507285145.2369576,102778706.23543219,465412.70853146043,-41866.575934101762,-9837.1751075919765 +8,2266530.1337492219,585422109.10781682,-4483380298.9961071,78821428.491007224,462127.25470633816,62719.829204599846,-11940.048534724378 +8,2271408.2113666162,2672715813.3493323,-3625476059.2911377,13132769.028181078,374894.77702834539,280112.80400628818,-14408.573235334174 +8,2276097.4006803907,4047916876.4911733,-1933666687.5247586,-53356550.207526475,200011.04325969331,425707.77992662182,-13383.416223740498 +8,2276914.8015547134,4196583170.3500242,-1578607319.1409891,-64093787.955680564,163304.39986170919,442617.53613383882,-12867.864562194542 +8,2278679.8090668465,4409359865.7767172,-773706745.32482636,-85536633.433175027,76944.139409186973,465730.78392303508,-11351.588640384334 +8,2278761.4153937893,4415474775.6360035,-735678340.65861213,-86459746.075593293,72929.794657590086,466260.62214132393,-11273.148669869164 +8,2279222.2136139097,4443888462.2875118,-520216670.74786848,-91549819.307950243,50444.753555813899,468729.07167573716,-10817.294473361 +8,2289079.1836576555,2671761391.1978607,3568267338.1966453,-134964223.47402656,-378241.74844598171,283606.9932980985,2852.3365495062899 +8,2291213.3288700399,1803406361.0536544,4080443257.7449069,-125504318.00257933,-433226.87270752288,193618.456444073,6003.4753854280998 +8,2291770.2140345257,1559008962.405709,4180919045.5314898,-121940945.10737747,-444130.38697466045,167070.13267813422,6787.6263381931449 +8,2292752.6251051375,1115357065.1206529,4321547334.0028877,-114631724.3851361,-457961.93508348049,119180.80702402951,8071.2278249770307 +8,2293000.3730586977,1001573933.7189749,4349584204.791234,-112594025.02787597,-460521.63705392834,107155.4021690909,8376.7810273077412 +8,2300175.1073118928,-2267971136.0712938,3871475988.4665818,-27502283.611518424,-408965.94606763555,-233788.80813500052,14237.401796646656 +8,2310762.2702826909,-4516090486.1480684,-271742174.13249499,109522338.11635686,25999.285576563627,-466617.40561864112,8996.449741810693 +8,2330636.7151068226,2305456684.4753156,-3874233944.1169686,26683797.740969542,399674.53258948843,243244.34984594988,-14205.700707459519 +8,2343298.7898191474,4270494527.1071978,1300866594.552392,-125103878.08110164,-140688.90196373887,452994.70296084089,-6071.7207631993733 +8,2346262.8809786653,3654148250.8896966,2554348267.2944241,-136731686.86531192,-271154.56164190272,387728.42742878624,-1732.2869858583313 +8,2354615.1618713546,332214156.47192317,4454752607.2148018,-99383866.368734792,-470193.60813758464,37857.011358560521,10047.815817929695 +8,2370992.7824516967,-4516350591.1194553,-293794274.7576642,110048811.50214821,28015.562659816896,-466953.27440212213,8948.9362998148081 +8,2373948.8806485189,-4224820150.6474094,-1635043759.5635223,130953258.13884374,165839.31318433117,-434572.00430445519,5133.3024040441896 +8,2382631.3812187007,-1379423888.4731245,-4315809799.3283195,120629162.81425929,443534.411658308,-140092.50458552709,-7327.6991063650576 +8,2389208.1926018596,1628648490.9802358,-4210325697.1456652,49162253.020578735,435772.39800444187,171637.77063136251,-13572.306846573802 +8,2397904.0036513801,4288144616.3471293,-1297181804.5078053,-72084037.725864843,133641.79052362451,452148.60538932553,-12388.556425509323 +8,2403501.2887358326,4267101470.2635121,1308685388.4139054,-125236952.59539004,-140596.22279635596,452730.23249205545,-6068.7840266945832 +8,2423314.1935228184,-3291878829.2709017,3067926304.0416193,12665526.208394626,-322386.5759443079,-341633.76348100282,14446.374246720876 +8,2426810.3250820599,-4179453672.7549562,1702756035.5504658,61227489.499345794,-180270.17671690611,-432683.0709301359,13045.769297855006 +8,2428427.2917341487,-4409306943.97229,982713972.67666709,81327959.522674903,-103748.51162613487,-455169.78545350494,11763.841546263511 +8,2429597.5699358359,-4498652367.817977,444966147.10073686,94476157.059761062,-49143.590831672162,-463089.57136807055,10688.753203276292 +8,2430499.2743066289,-4523876537.254365,25621496.577693138,103704762.14258271,-6485.5485246864309,-466550.50439840811,9760.0260729285674 +8,2442512.3685318003,-1516326857.6480813,-4270101253.3896389,122860928.54620628,439263.04339446081,-153526.29543944821,-6936.3882966851243 +8,2442915.2675501364,-1338214505.204284,-4328292188.5330458,119963817.44731553,444790.1639103904,-135365.89335221157,-7442.5904775154204 +8,2445054.0861903615,-361531153.25943577,-4513224942.2772532,101256413.97691917,465805.27568941715,-35912.392506577555,-10008.100557651382 +8,2451545,2515045869.8652868,-3738714470.7545485,19032309.156529054,385853.97308510658,265820.57228461537,-14347.876408079434 +8,2455075.6939767599,3680414368.5302563,-2575349176.1164789,-31783278.368950848,266719.08935843216,388272.9668955424,-14126.148241228591 +8,2458180.9034184003,4298490336.6490717,-1258434104.0063851,-73156208.668618634,129660.59873150715,452826.06108211773,-12328.0223920054 +8,2460000.5,4454447819.2631941,-414301361.3401581,-94125146.201658666,40157.663656568962,471692.34042991709,-10622.495096099408 +8,2467275.3446910325,3470558196.6603708,2796242995.4203486,-137573803.69929424,-296383.46676020452,368143.77478957037,-757.85748724977361 +8,2472665.5748623828,1404991101.6551394,4235876085.7290063,-119620616.60249272,-448391.74644502142,152121.80389413188,7221.4317392731145 +8,2474522.5806957306,548854769.79629242,4433790046.6489677,-103950466.04341871,-469585.40337640792,59990.44198402678,9575.7541189377534 +8,2486527.0242732824,-4089757830.9311767,1906601971.4410529,55013609.543197669,-202111.0252654379,-422307.1448584079,13365.632971924106 +8,2501867.9424220412,-1880993619.4784524,-4126155231.5187559,128311453.53743042,425541.93097477249,-192297.52179298186,-5855.8267754312583 +8,2509740.3647796647,1689952247.6726317,-4185152788.8884659,47237244.64315068,432678.070238685,177398.38282870001,-13645.796171820346 +8,2510600.060933358,2055155801.5661516,-4015854389.1994753,35320385.96019458,416203.80575289414,216382.20045396325,-14056.154512466597 +8,2511909.952754871,2579164881.7492838,-3694850204.440309,16639305.089781553,382099.55688178906,272631.58556416596,-14408.89904528933 +8,2518576.4147364241,4322265371.3809443,-1167129033.1705673,-75608573.925866604,119479.27076904512,455158.75156173494,-12141.878624282013 +8,2525370.0137001472,4006023624.5630937,1961059799.3569775,-132745745.6428504,-210323.65765957677,425669.44641188363,-3906.8506845631155 +8,2531672.3758322261,1922577177.1636333,4023148357.2664075,-127192947.69382137,-425791.59123174765,204590.17862355805,5585.5176083378865 +8,2534824.4901680332,498342052.38547146,4439559320.4422646,-102929006.29032543,-470218.20302579255,55286.30410685423,9691.4013827729468 +8,2541816.7498776624,-2625110446.2304316,3645850779.0536532,-14569461.322786739,-383244.59409862768,-270442.28547872737,14422.021980817239 +8,2543036.073701357,-3070895417.5873909,3287331238.6740317,3107415.3627009355,-346723.68394888594,-317325.94990057556,14529.774582291235 +8,2546994.5257124593,-4142883219.4360399,1790607428.6862524,58642836.720905818,-189692.62707493181,-426957.08091778134,13181.667299294337 +8,2551052.7434542929,-4526193433.9997454,-51573718.972142957,105436927.26526648,2150.5357920604706,-465599.16471895081,9559.6966650015784 +8,2558314.4581084568,-3295761155.2466497,-3118266248.2066631,140204014.56534061,320764.81585671741,-338087.78936397546,-431.91082854534318 +8,2561890.4046752327,-1958100725.8851564,-4090751860.6025405,129409771.15190539,420877.40047634736,-200987.72102560525,-5578.9986223014475 +8,2564589.685945244,-760914840.92699397,-4465291790.1114178,109531758.72521909,459080.90627958684,-75863.104390858934,-9021.4875984333194 +8,2565365.3985973313,-402705260.04360342,-4510173189.0474281,102190270.21002667,464204.289063123,-39701.366105638313,-9901.9634829219503 +8,2577128.7351568425,4063158326.450985,-1899046528.6230195,-54582189.461109929,195324.14397010318,429416.14201630664,-13338.337280032993 +8,2577639.2677030656,4156848887.0918455,-1677337974.2934921,-61306355.070811465,171705.71896734723,438844.23145891394,-12998.860875238814 +8,2590334.2671391643,2551568660.1077352,3655932137.800746,-134153558.93984658,-388890.16345552931,272864.08933394216,3360.640120554986 +8,2595997.4277934292,37773325.03103219,4469555495.9632492,-92935556.987670049,-472655.42525418487,6209.7956580738428,10754.201784382836 +8,2596685.4703123774,-286895917.49701768,4462098033.6312971,-85307382.119633496,-470697.19817468972,-27739.298698894698,11409.702362744896 +8,2596902.4767300789,-388928940.05981785,4454940494.0463638,-82810114.572604403,-469648.39004792296,-38233.341019808875,11605.007660958332 +8,2599958.6811479013,-1782169056.1175373,4115713526.8735213,-43680559.438126236,-434429.57460043562,-183751.83576328747,13792.792755929899 +8,2607461.5385275586,-4193230528.7213321,1674681138.3096232,62225813.142429605,-177260.09163831593,-432059.0036235493,13001.539905357038 +8,2618665.2202832801,-3250023335.285738,-3168971013.4188023,140236923.48816052,325919.40749394702,-333797.546804482,-652.59817942365294 +8,2622969.6595060355,-1582570981.3352432,-4249876080.7904916,124034955.79179592,437769.22545015934,-161344.2189749362,-6780.1116099869841 +8,2636101.3604303026,3786803574.9413924,-2411196370.8757682,-37687369.190034173,249711.19062637916,398625.37203822302,-13974.815244520825 +8,2641305.6078180475,4468798320.0026255,-85895304.933750868,-101328004.63431399,6596.7738907046041,473413.23014396202,-9893.9751535678788 +8,2646186.7395561016,3915312960.5546041,2132161751.9031265,-134240821.67659056,-228126.16996093906,416630.56287164864,-3298.3681064604652 +8,2652672.8788275202,1663881276.0043931,4136756606.8938818,-123611043.02195102,-438270.92664133152,177630.39391310574,6431.2100302141353 +8,2655817.3911270499,219785749.22791481,4463263509.9894085,-97010220.425259113,-473179.29779453663,26586.317384063539,10369.654307483805 +8,2657675.7904352564,-656630609.61434937,4425518715.3262663,-76029772.428152174,-466737.09407372319,-66085.82882942626,12124.966785228149 +8,2660712.9689958571,-2019547932.2566075,4006991703.4158711,-35945852.310723387,-422853.19846211537,-208898.02393021624,14043.04767638558 +8,2673114.1483041537,-4457711275.731514,-821251442.49612474,119776838.96361506,81388.889813181144,-458516.1448204632,7564.4191923903936 +8,2690567.3579719397,1779461093.5289602,-4147398168.7292647,44401164.328235,427115.64410711214,187754.00468843628,-13722.99411462197 +8,2697604.4441013052,4074198250.6007028,-1875430067.8756185,-55383222.92921342,193806.32315346526,430636.83853234345,-13329.570761693534 +8,2699249.9134048764,4328807272.1854143,-1142596776.8940125,-76333170.184159264,115350.11566701748,456995.56269231607,-12077.248946626141 +8,2706019.8488313816,3997227680.8626189,1971891678.0083015,-132867371.08309914,-209945.04280054438,424710.63738171145,-3898.8831999334243 +8,2718979.4228122425,-1163171001.215251,4322869903.8510981,-62226712.085603632,-455440.08743300638,-118027.05269884451,12948.617456017653 +8,2720458.1684011132,-1821486575.6392598,4097686632.4290538,-42372266.789472751,-433225.53999166028,-186749.60482359972,13855.405861248497 +8,2722283.5085893935,-2573535703.2165141,3681941417.3559513,-16463193.678856162,-387642.15866765898,-266591.13521970907,14427.076269908897 +8,2725810.706325612,-3738582649.8687687,2519171796.5516214,34388362.155940868,-265802.23094043205,-387093.55974022025,14090.722544401899 +8,2728509.8327481919,-4297490641.7929354,1391891783.1064818,70496563.126268998,-147584.15665937983,-442149.17962123925,12530.842011877694 +8,2730729.1316622673,-4510474257.14645,380024121.28463286,96252962.544459686,-42231.984556383264,-465397.67224199011,10549.981431427683 +8,2731022.0789917279,-4520726329.4518166,243551733.70164382,99297856.406226501,-27766.870181607937,-466239.64912174264,10236.822079610331 +8,2742359.3750335346,-2020929810.8783176,-4062298751.94416,130333809.57045616,415873.15691872191,-205802.18714607431,-5353.064139407973 +8,2745002.8418905083,-857629252.40640724,-4452227774.6234665,111505708.18844627,458683.30284085334,-85617.601805857557,-8812.9795582470269 +8,2747657.219437053,373558148.4211542,-4510901256.8479471,84307866.610288709,464538.66325567727,40683.65653023425,-11578.860241889808 +8,2749649.840763649,1286739996.4421947,-4333380391.6026087,59558083.143449381,447752.89189448353,137546.06094952556,-13155.309086603038 +8,2752433.6495864699,2459078917.6055379,-3778101942.0373502,21065693.248819966,390401.14398965676,258384.44563385475,-14345.105804929401 +8,2761257.4085423201,4459503277.0635357,-308325355.11825836,-96585992.921304777,29516.137033078154,470814.36001048546,-10395.582251357788 diff --git a/planet/internal/semianalytic/state.go b/planet/internal/semianalytic/state.go new file mode 100644 index 0000000..c24b358 --- /dev/null +++ b/planet/internal/semianalytic/state.go @@ -0,0 +1,126 @@ +// Package semianalytic 内置半解析模型:VSOP2013/TOP2013 与 MPP02 结构、按 DE441 重拟的表,提供历元 L/B/R 与 ICRF 几何状态。 +package semianalytic + +import ( + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet/internal/semianalytic/lunar" + "b612.me/astro/planet/internal/semianalytic/planets" +) + +// 表以 TT 为自变量拟合;TDB−TT 小于 1.7 ms,直接当作 TDB 使用。 +const ( + coverStart = -1931455.0 + coverEnd = 5373545.0 + eps0 = 84381.448 / 3600 * math.Pi / 180 + emrat = 81.3005682214972 +) + +// Ephemeris 内置表;越过覆盖范围仍外推求值,只在 Coverage 中报告范围 / built-in tables; extrapolates beyond coverage. +type Ephemeris struct{} + +var _ ephem.Engine = Ephemeris{} + +// Apparent 地心视方向(ICRF 单位矢量)、几何地心距 km、光行时日 / apparent geocentric direction. +func (Ephemeris) Apparent(target ephem.Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) { + return Apparent(target, jdTT) +} + +func finite(x float64) bool { return !math.IsNaN(x) && !math.IsInf(x, 0) } + +type state struct{ p, v [3]float64 } + +func (a state) sub(b state) state { + for i := range a.p { + a.p[i] -= b.p[i] + a.v[i] -= b.v[i] + } + return a +} + +// State 目标相对中心的 ICRF 几何状态 / geometric ICRF state of target relative to center. +func (Ephemeris) State(target, center ephem.Body, jdTDB float64) (pos, vel [3]float64, err error) { + if !finite(jdTDB) { + return pos, vel, ephem.ErrOutOfRange + } + t, err := helio(target, jdTDB) + if err != nil { + return pos, vel, err + } + c, err := helio(center, jdTDB) + if err != nil { + return pos, vel, err + } + d := t.sub(c) + return toICRF(d.p), toICRF(d.v), nil +} + +// Coverage 可信区间 / trusted time span. +func (Ephemeris) Coverage(target ephem.Body) (start, end float64, ok bool) { + if _, err := helio(target, 2451545); err != nil { + return 0, 0, false + } + return coverStart, coverEnd, true +} + +// helio 日心 J2000 平黄道状态(km、km/日)。 +func helio(b ephem.Body, jd float64) (state, error) { + switch b { + case ephem.Sun: + return state{}, nil + case ephem.SSB: + return ssbHelio(jd), nil + case ephem.Earth, ephem.Moon: + emb, err := planetHelio(int(ephem.EMB), jd) + if err != nil { + return state{}, err + } + mp, mv := lunar.Default().PositionVelocityJ2000(jd) + k := 1.0 / (1 + emrat) + if b == ephem.Moon { + k -= 1 + } + for i := range mp { + emb.p[i] -= mp[i] * k + emb.v[i] -= mv[i] * k + } + return emb, nil + } + if b >= ephem.Mercury && b <= ephem.Neptune { + return planetHelio(int(b), jd) + } + return state{}, ephem.ErrUnsupportedBody +} + +func planetHelio(id int, jd float64) (state, error) { + p, v, err := planets.Default().HeliocentricState(id, jd) + return state{p, v}, err +} + +// ssbHelio 太阳系质心的日心状态:Σ GMᵢ·rᵢ / (GM☉ + Σ GMᵢ),冥王星等小天体不计。 +func ssbHelio(jd float64) state { + var s state + _, gmSun := planets.GM(1) + total := gmSun + for id := 1; id <= 8; id++ { + gm, _ := planets.GM(id) + h, _ := planetHelio(id, jd) + for i := range s.p { + s.p[i] += gm * h.p[i] + s.v[i] += gm * h.v[i] + } + total += gm + } + for i := range s.p { + s.p[i] /= total + s.v[i] /= total + } + return s +} + +// toICRF J2000 平黄道 → ICRF 赤道(绕 x 轴转 −ε0)。 +func toICRF(e [3]float64) [3]float64 { + s, c := math.Sincos(eps0) + return [3]float64{e[0], e[1]*c - e[2]*s, e[1]*s + e[2]*c} +} diff --git a/planet/internal/semianalytic/state_test.go b/planet/internal/semianalytic/state_test.go new file mode 100644 index 0000000..e42cfbe --- /dev/null +++ b/planet/internal/semianalytic/state_test.go @@ -0,0 +1,153 @@ +// 被测契约:内置表经 ICRF 适配后与 DE441 几何状态一致到表本身的精度;质心推算误差不影响光行差。 +package semianalytic + +import ( + "bufio" + "math" + "os" + "strconv" + "strings" + "testing" + + "b612.me/astro/internal/ephem" +) + +type sample struct { + target, center ephem.Body + jd float64 + p, v [3]float64 +} + +func loadSamples(t *testing.T) []sample { + f, err := os.Open("testdata/de441-states.csv") + if err != nil { + t.Fatal(err) + } + defer f.Close() + var out []sample + sc := bufio.NewScanner(f) + for sc.Scan() { + parts := strings.Split(sc.Text(), ",") + var v [9]float64 + for i := range v { + if v[i], err = strconv.ParseFloat(parts[i], 64); err != nil { + t.Fatal(err) + } + } + out = append(out, sample{ephem.Body(v[0]), ephem.Body(v[1]), v[2], [3]float64{v[3], v[4], v[5]}, [3]float64{v[6], v[7], v[8]}}) + } + if len(out) < 500 { + t.Fatalf("only %d samples", len(out)) + } + return out +} + +func norm(a, b [3]float64) float64 { + return math.Sqrt((a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2])) +} + +func modern(jd float64) bool { return jd >= 2086302.5 && jd <= 2816787.5 } + +func TestStatesMatchDE441(t *testing.T) { + type key struct { + target, center ephem.Body + modern bool + } + type acc struct{ n, dp, dv, maxDp, maxDv, maxAng float64 } + stats := map[key]*acc{} + e := Ephemeris{} + for _, s := range loadSamples(t) { + p, v, err := e.State(s.target, s.center, s.jd) + if err != nil { + t.Fatal(err) + } + k := key{s.target, s.center, modern(s.jd)} + if stats[k] == nil { + stats[k] = &acc{} + } + a := stats[k] + dp, dv := norm(p, s.p), norm(v, s.v)/86.4 + r := math.Sqrt(s.p[0]*s.p[0] + s.p[1]*s.p[1] + s.p[2]*s.p[2]) + a.n++ + a.dp += dp * dp + a.dv += dv * dv + a.maxDp = math.Max(a.maxDp, dp) + a.maxDv = math.Max(a.maxDv, dv) + a.maxAng = math.Max(a.maxAng, dp/r*206264.806) + } + // 上限:最大位置差 km、最大速度差 m/s;太阳相对质心的残差来自表外天体(冥王星、阋神星等)。 + type limit struct{ km, ms float64 } + limits := map[key]limit{ + {ephem.Sun, ephem.SSB, true}: {300, 1e-3}, {ephem.Sun, ephem.SSB, false}: {300, 1e-3}, + {ephem.Earth, ephem.SSB, true}: {300, 0.01}, {ephem.Earth, ephem.SSB, false}: {500, 0.05}, + {ephem.Earth, ephem.Sun, true}: {40, 0.01}, {ephem.Earth, ephem.Sun, false}: {250, 0.05}, + {ephem.Moon, ephem.Earth, true}: {0.6, 0.005}, {ephem.Moon, ephem.Earth, false}: {1.2, 0.008}, + {ephem.Mars, ephem.SSB, true}: {400, 0.04}, {ephem.Mars, ephem.SSB, false}: {1200, 0.1}, + {ephem.Jupiter, ephem.SSB, true}: {2000, 0.03}, {ephem.Jupiter, ephem.SSB, false}: {4000, 0.05}, + {ephem.Saturn, ephem.SSB, true}: {2500, 0.02}, {ephem.Saturn, ephem.SSB, false}: {7000, 0.04}, + } + for k, a := range stats { + t.Logf("%3d-%3d modern=%-5v n=%2.0f pos rms %.3g km max %.3g km (%.4f″) vel rms %.3g m/s max %.3g m/s", + k.target, k.center, k.modern, a.n, math.Sqrt(a.dp/a.n), a.maxDp, a.maxAng, math.Sqrt(a.dv/a.n), a.maxDv) + l, ok := limits[k] + if !ok { + t.Errorf("%d-%d modern=%v: no limit defined", k.target, k.center, k.modern) + continue + } + if a.maxDp > l.km || a.maxDv > l.ms { + t.Errorf("%d-%d modern=%v: max %.3g km / %.3g m/s exceeds %.3g km / %.3g m/s", + k.target, k.center, k.modern, a.maxDp, a.maxDv, l.km, l.ms) + } + } +} + +func TestStateAntisymmetricAndComposable(t *testing.T) { + e := Ephemeris{} + bodies := []ephem.Body{ephem.SSB, ephem.Sun, ephem.Mercury, ephem.EMB, ephem.Earth, ephem.Moon, ephem.Neptune} + for _, jd := range []float64{2451545.0, 2460000.5, 1000000.5} { + for _, a := range bodies { + for _, b := range bodies { + p1, v1, err1 := e.State(a, b, jd) + p2, v2, err2 := e.State(b, a, jd) + if err1 != nil || err2 != nil { + t.Fatal(err1, err2) + } + for i := range p1 { + if math.Abs(p1[i]+p2[i]) > 1e-6 || math.Abs(v1[i]+v2[i]) > 1e-9 { + t.Fatalf("%d-%d jd %.1f: not antisymmetric", a, b, jd) + } + } + } + // 经质心中转与直接求差一致 + ps, _, _ := e.State(a, ephem.SSB, jd) + pe, _, _ := e.State(ephem.Earth, ephem.SSB, jd) + pd, _, _ := e.State(a, ephem.Earth, jd) + for i := range pd { + if math.Abs(ps[i]-pe[i]-pd[i]) > 1e-4 { + t.Fatalf("%d via SSB differs from direct by %.3g km", a, ps[i]-pe[i]-pd[i]) + } + } + } + } +} + +func TestUnsupportedBodyAndCoverage(t *testing.T) { + e := Ephemeris{} + if _, _, err := e.State(ephem.Body(999), ephem.Sun, 2451545); err != ephem.ErrUnsupportedBody { + t.Fatalf("want ErrUnsupportedBody, got %v", err) + } + if _, _, ok := e.Coverage(ephem.Body(999)); ok { + t.Fatal("unsupported body reported coverage") + } + s, end, ok := e.Coverage(ephem.Moon) + if !ok || s >= 2086302.5 || end <= 2816787.5 { + t.Fatalf("coverage %v..%v ok=%v", s, end, ok) + } +} + +func BenchmarkEarthBarycentric(b *testing.B) { + e := Ephemeris{} + for i := 0; i < b.N; i++ { + e.State(ephem.Earth, ephem.SSB, 2451545.0+float64(i%7300)*10) + } +} diff --git a/planet/internal/semianalytic/testdata/de441-states.csv b/planet/internal/semianalytic/testdata/de441-states.csv new file mode 100644 index 0000000..60f05de --- /dev/null +++ b/planet/internal/semianalytic/testdata/de441-states.csv @@ -0,0 +1,560 @@ +10,0,-453760.43827763933,627503.58386753942,-124230.69050468586,-70199.078707220062,26.76776105652425,765.92648268149367,334.66502282017069 +399,0,-453760.43827763933,107103581.39105695,92605789.262403503,41615993.840190336,-1825853.499460791,1701479.4253012564,784308.67706974538 +301,399,-453760.43827763933,-378705.73482052982,-28103.816160514951,-4017.4131024926901,3342.9625418581395,-77819.182821935115,-44213.971528624854 +399,10,-453760.43827763933,106476077.80718942,92730019.952908188,41686192.918897554,-1825880.2672218478,1700713.4988185749,783974.01204692514 +4,0,-453760.43827763933,169899735.16094986,-110355741.70409171,-56951329.050765976,1390624.903042719,1643145.9958309424,681686.37711209245 +5,0,-453760.43827763933,-795152443.37427366,-114721004.00195973,-25286023.797442842,159314.46442211611,-988964.39427058503,-431497.11358438112 +6,0,-453760.43827763933,1018743245.1316302,782191583.20550311,289192256.68771762,-579827.2059701574,633218.12694378977,273048.03342590691 +10,0,-423678.70671046677,179909.8780418032,-63616.807055276884,-40950.160518573954,83.110891028374496,742.67766260582778,323.1304096576477 +399,0,-423678.70671046677,-149343645.39114499,9355483.6249353141,4992417.8648941768,-231365.52615053259,-2326441.6302622342,-1057956.1165324459 +301,399,-423678.70671046677,-362497.12572547793,-18989.414561057463,-26273.515087980777,9050.9046067815598,-87472.242646161438,-31950.617127462578 +399,10,-423678.70671046677,-149523555.26918679,9419100.4319905918,5033368.0254127504,-231448.63704156096,-2327184.3079248401,-1058279.2469421034 +4,0,-423678.70671046677,-124479878.0323623,-165270415.92394394,-69258047.861014932,1871669.9375692941,-978522.93242947722,-520494.3005525489 +5,0,-423678.70671046677,-789098416.28881919,127609049.23493271,79009712.306077182,-211886.21464492343,-986005.85410317755,-418932.84563305299 +6,0,-423678.70671046677,1055647465.7742503,-875380582.46072364,-384706841.81956458,509714.64545812609,617854.61940350023,236530.72638943736 +10,0,-341426.16644567705,1262769.5920344677,-12843.113056088087,-44673.111321944591,192.05195299485459,1122.6787803388454,472.82069914201332 +399,0,-341426.16644567705,-67420712.2168971,-123924288.49601531,-56057011.641266786,2249658.9569060388,-1043850.8712911939,-484948.76346071513 +301,399,-341426.16644567705,353508.68930772692,81476.007399842143,27496.600809656084,-25016.476000322597,84701.806706080868,29842.278451677859 +399,10,-341426.16644567705,-68683481.808931574,-123911445.38295922,-56012338.529944845,2249466.9049530439,-1044973.5500715327,-485421.58415985713 +4,0,-341426.16644567705,-194700914.1804221,132111452.73911095,67805538.859190211,-1106469.9289963758,-1453285.0502874847,-609306.52568247006 +5,0,-341426.16644567705,-772362855.05181193,190373590.34130129,105414962.08175862,-306762.49500183394,-965002.72271918959,-407101.11956292926 +6,0,-341426.16644567705,-1398022432.4734106,-562786993.05195999,-188571905.17817026,275356.2760138099,-674855.90839133295,-281669.90831381845 +10,0,-324796.10496353707,70937.878713973201,-693144.55331471935,-300420.81453991978,1158.1163208507717,119.39272746038905,22.442442342931706 +399,0,-324796.10496353707,42742866.585810877,127221490.89314052,57641128.49656865,-2510762.2874431824,685382.01387348806,323035.27252551523 +301,399,-324796.10496353707,-66186.945268243551,-351223.13583222032,-193947.72853952646,82731.122926523749,-10694.849705449889,-9210.9054652472169 +399,10,-324796.10496353707,42671928.707096905,127914635.44645524,57941549.311108567,-2511920.4037640332,685262.6211460277,323012.8300831723 +4,0,-324796.10496353707,-192197639.67468974,-111839819.18567654,-42395532.288457409,1253199.6871998506,-1518782.3089291244,-737951.33603576955 +5,0,-324796.10496353707,-250476891.47148129,678697469.34220517,300365365.55205262,-1071758.3956206813,-306557.97302640002,-100009.46333471482 +6,0,-324796.10496353707,1120314399.2619877,667410859.63375628,238772380.73121825,-496473.62316226226,685255.79174112098,292228.49759181245 +10,0,-240920.88948244418,-75454.751970275684,596690.9036632292,252642.55203370831,-955.3831380591871,375.06841084704524,194.94239957869917 +399,0,-240920.88948244418,76011539.575863644,-118842251.59630252,-54283713.502295278,2176925.0884052352,1187286.3231776035,529551.13488172321 +301,399,-240920.88948244418,-240195.25513634086,-291111.43127427995,-145459.04963837564,66462.133179508732,-48564.774128241814,-14620.894423581543 +399,10,-240920.88948244418,76086994.327833921,-119438942.49996576,-54536356.054328986,2177880.4715432944,1186911.2547667564,529356.19248214446 +4,0,-240920.88948244418,-83915284.901893944,-181679091.85636759,-78575043.891084388,2078302.2405602112,-621887.10568215535,-367304.18231882545 +5,0,-240920.88948244418,-506666036.12325954,-570435363.11525273,-230983463.82616255,866177.87226951157,-612820.75171545194,-290335.85495241586 +6,0,-240920.88948244418,1003176300.2264693,-936133043.14471006,-408882641.44364846,547291.55888138711,584496.16270809667,221230.75135872042 +10,0,-136305.25725227379,143094.43903393406,1167331.2967486593,489165.05573821237,-1227.9900662357579,-95.239974211215539,-7.8524917579509879 +399,0,-136305.25725227379,-5115705.7768299906,134794008.3806335,61059780.776681036,-2622807.104624155,-80473.118247179853,-26205.829805254147 +301,399,-136305.25725227379,-135071.25769134518,-321196.27134543657,-121554.68873813003,84752.537477942387,-29880.855052582217,-20935.866063526304 +399,10,-136305.25725227379,-5258800.2158639245,133626677.08388485,60570615.720942825,-2621579.1145579191,-80377.878272968635,-26197.977313496194 +4,0,-136305.25725227379,216383601.43630025,23271023.442273706,1580481.4558775835,-48056.937799281521,1988673.2289311781,900792.58517917059 +5,0,-136305.25725227379,164213995.9978635,-691617404.07310879,-303285467.8200323,1098572.9234916801,267409.22831109242,82755.005996207547 +6,0,-136305.25725227379,-1094650719.3921556,-1009686290.7719079,-377293906.60044181,534084.27739309974,-510907.40708203777,-223116.28406723318 +10,0,30779.849641129607,325285.19509265444,-622658.9547295532,-274866.49127055617,972.13826311861328,502.27178429537702,190.66368771312477 +399,0,30779.849641129607,-45218113.618455507,-133198911.02380063,-60038082.129406638,2409954.832107571,-692040.73699167243,-320568.28503903653 +301,399,30779.849641129607,199094.05585694313,270804.87592631578,132343.87062557787,-79923.592886510713,48124.246877130056,12789.008542954869 +399,10,30779.849641129607,-45543398.813548163,-132576252.06907107,-59763215.638136081,2408982.693844452,-692543.00877596776,-320758.94872674963 +4,0,30779.849641129607,33030995.020363957,217866655.9026649,97284527.145953894,-1922791.6625242801,326530.36135733215,225508.62360496089 +5,0,30779.849641129607,-545361056.97010326,524906872.12050623,242430111.90726534,-823948.08953940775,-681604.84083041106,-269986.58774138265 +6,0,30779.849641129607,819637518.14857697,971622073.76355374,369081670.34898657,-707248.41433020262,502549.67841657891,225637.97933998608 +10,0,140198.1254105469,1136951.4456759819,108880.12548372868,25778.181877663847,-438.61890392273813,884.48962166408126,390.50962613380051 +399,0,140198.1254105469,-15209072.633473158,133124392.88546264,60076419.945044287,-2606574.6103484146,-258689.69478386734,-109007.7718831063 +301,399,140198.1254105469,371038.12426981144,-66178.799296975136,-4907.703617952764,11832.909284232097,83387.721396486784,31184.345092606552 +399,10,140198.1254105469,-16346024.07914914,133015512.75997892,60050641.763166621,-2606135.9914444918,-259574.18440553138,-109398.28150924009 +4,0,140198.1254105469,-234960457.78411904,46245944.208213083,30351888.417449288,-324952.41276325844,-1760877.6552028595,-784648.10135903535 +5,0,140198.1254105469,-625425795.27779233,-465799679.98433721,-182795564.12458292,700261.29289416876,-764042.95712471497,-349954.52736126329 +6,0,140198.1254105469,-745394692.11534095,1064252205.1074734,455266286.54390258,-754618.17328486068,-396564.52445036377,-137355.34838903981 +10,0,323411.96748006181,34210.586069219898,128271.68157016362,63292.96387677118,-466.10778441045494,-643.41543448852235,-269.05260783345869 +399,0,323411.96748006181,100512294.32486616,-103165494.96654348,-46629080.087800294,1876626.5307019195,1561874.4839539139,697953.54083895206 +301,399,323411.96748006181,100989.65486904979,-324328.19912312925,-115916.64987385273,90173.587613736003,24588.562106619291,15048.821262504913 +399,10,323411.96748006181,100478083.73879693,-103293766.64811364,-46692373.051677063,1877092.6384863297,1562517.8993884025,698222.59344678547 +4,0,323411.96748006181,71103695.818554983,207630728.40813014,91403844.284720734,-1854135.9799148592,648971.27772321133,366827.35258264322 +5,0,323411.96748006181,599485102.14727378,-415860544.26402104,-196491215.54749018,683233.26214325998,869172.36671001406,355417.49964572699 +6,0,323411.96748006181,-900211237.9924432,961546193.65715563,419656291.39301538,-686917.49286723323,-486456.95044434589,-175228.77953201107 +10,0,410958.80400505499,-245059.3682541163,-434583.29653500795,-184808.03223093296,747.79765976975636,-553.92434138418332,-265.04289085256511 +399,0,410958.80400505499,-140060387.47585872,-52106971.392762199,-23092956.088610828,918364.03788714588,-2173934.3514118898,-978128.38693157409 +301,399,410958.80400505499,275106.02984830737,222320.62423730642,123035.74025066569,-57537.463286377104,66412.343673773561,23608.348773510788 +399,10,410958.80400505499,-139815328.10760459,-51672388.096227191,-22908148.056379896,917616.24022737611,-2173380.4270705059,-977863.34404072154 +4,0,410958.80400505499,-196355162.82393789,-111558567.97122648,-43036902.106849015,1224739.4642769785,-1503585.0770338448,-729774.47295531456 +5,0,410958.80400505499,560643734.63105357,467483956.1044026,185736622.29063299,-757380.37724661629,803656.17509993911,368130.10632862721 +6,0,410958.80400505499,-1469405970.5139503,-35741558.862380534,33853664.460445754,-40365.728163699059,-748731.75708044507,-302736.93673589337 +10,0,672459.66525789374,645539.54830305907,136502.11925789321,35538.858895883473,-15.518729950149929,884.47081105946381,384.14299420599144 +399,0,672459.66525789374,-149009947.22007665,416636.87278272526,379320.98538751283,-53385.621863982349,-2346655.7459996752,-1049183.6073999063 +301,399,672459.66525789374,-127310.13541820645,344764.77268082416,169050.11814553768,-78969.238528246686,-26836.008240724288,-4431.9900806051355 +399,10,672459.66525789374,-149655486.76837972,280134.75352483208,343782.12649162934,-53370.103134032208,-2347540.2168107345,-1049567.7503941124 +4,0,672459.66525789374,-50145022.153465472,217207973.84045941,100637677.99620563,-1912310.62804844,-304382.11815668899,-66944.861614348381 +5,0,672459.66525789374,-794368144.76506698,125644990.78110985,76196281.991929084,-208666.61002827447,-979694.47930937167,-416631.04395147809 +6,0,672459.66525789374,208355968.18891349,-1383893126.6364479,-569815786.85804617,773553.61521544191,136335.92253831017,28728.139764948082 +10,0,696936.53672289755,-770404.85808557493,-255223.29417820607,-86590.711500965583,787.57558923321039,-937.0387596381986,-421.24234051764654 +399,0,696936.53672289755,-150185135.05409065,-10977263.750148753,-4667819.9072268493,154542.00456880519,-2342472.2874880671,-1046962.7510517323 +301,399,696936.53672289755,205603.35032978654,299670.43087032996,126755.7124454584,-76286.400136153898,42905.76840027998,10799.865849996382 +399,10,696936.53672289755,-149414730.19600508,-10722040.455970546,-4581229.1957258834,153754.42897957197,-2341535.2487284285,-1046541.5087112148 +4,0,696936.53672289755,168874778.45740345,-107274081.73852165,-55077529.653738879,1342627.226751019,1690853.0633071044,718978.52745493792 +5,0,696936.53672289755,533621128.91751736,492084177.33193231,197397832.60611317,-799646.61553727638,769527.01443606988,353932.91383846977 +6,0,696936.53672289755,1315831347.1208186,346369890.16144598,95268737.134883881,-271878.46888971946,763502.7310255816,319840.64900005102 +10,0,1167745.7896862614,-133708.72319508885,1041902.6789467742,443816.62844419561,-1262.2881277491579,-249.50014957519255,-70.429732337884914 +399,0,1167745.7896862614,-149454357.81419855,5020613.9728487339,2286935.4602207472,-122204.01789723781,-2353325.7838803092,-1043204.5765919515 +301,399,1167745.7896862614,-96167.780640661716,344891.39719489869,143127.97551840311,-85820.888738375288,-18497.453442909478,-44.093726527256649 +399,10,1167745.7896862614,-149320649.09100348,3978711.2939019594,1843118.8317765514,-120941.72976948865,-2353076.2837307341,-1043134.1468596137 +4,0,1167745.7896862614,-3037789.5087923734,219462711.6726191,100175476.75606233,-1972812.9288323952,82675.244628950211,106300.82116090877 +5,0,1167745.7896862614,89733093.596783444,-707179735.06682336,-306976891.06356704,1114601.6418969852,174120.24055872232,45272.227803170987 +6,0,1167745.7896862614,532629719.49820274,-1292308305.7648468,-546812039.54359138,726302.3606329225,299527.36914683977,95138.270941744835 +10,0,1249492.6623223473,724161.14411717979,682292.41094350046,271074.18694952864,-759.11478480629989,764.72787280101375,343.89208115491624 +399,0,1249492.6623223473,-42835620.038486578,129051264.89645538,57113535.036888085,-2504002.5584751382,-698601.7558053761,-308315.04094684956 +301,399,1249492.6623223473,-72656.72027835995,321878.7892229259,139684.46265818179,-92170.504715571107,-22795.314999024577,-477.69461479725948 +399,10,1249492.6623223473,-43559781.182603754,128368972.48551188,56842460.849938557,-2503243.4436903321,-699366.48367817712,-308658.93302800448 +4,0,1249492.6623223473,4689673.5394349657,218421127.34269571,99502303.539471179,-1974594.3654773652,140993.1177642447,132072.68998593753 +5,0,1249492.6623223473,-506962062.43960947,-574592772.43059659,-234007933.4962554,866786.80498500052,-603400.43350880593,-282758.8261645991 +6,0,1249492.6623223473,-1305842478.3232687,471413341.50154948,241611019.27994251,-366986.34768341598,-708990.90283243079,-275466.97308580414 +10,0,1286243.8747144653,-502700.78423914744,-116365.88801059836,-42815.336192564442,631.36511564489376,-743.72805611893477,-333.32255120133499 +399,0,1286243.8747144653,126765170.1007296,-74631811.32028763,-33053969.787823867,1343083.449423417,1980299.5704166649,875648.8617137043 +301,399,1286243.8747144653,-325645.55342233181,198445.1098806262,82674.789149478078,-44803.523130046873,-65147.725763014714,-37466.277445392487 +399,10,1286243.8747144653,127267870.88496874,-74515445.432277039,-33011154.451631304,1342452.0843077721,1981043.2984727838,875982.18426490563 +4,0,1286243.8747144653,-76593836.754464939,-188771491.66355914,-83559384.223152295,2080974.6789679483,-498320.40545437078,-298419.18401374662 +5,0,1286243.8747144653,469074518.60016835,545857414.44471633,222640573.31567121,-889247.12279054744,680867.44697292952,316617.41456959402 +6,0,1286243.8747144653,618421159.54254699,-1256681460.1771402,-536338783.45012498,706162.48867637152,340885.15250775538,112367.80459898795 +10,0,1366926.0296145864,266645.2199337121,609036.44912596606,256497.93520170121,-891.73469916082286,130.86127969991588,74.614358731594038 +399,0,1366926.0296145864,51176075.488903083,-130549056.36901253,-57697982.31234879,2379077.8277257644,786386.52304830472,346865.18794627633 +301,399,1366926.0296145864,-367576.16793164611,101073.56945747137,56931.19923081249,-22791.884578142104,-74517.707861693503,-41031.276571204173 +399,10,1366926.0296145864,50909430.268969372,-131158092.8181385,-57954480.247550495,2379969.5624249252,786255.66176860477,346790.57358754479 +4,0,1366926.0296145864,81256792.994015694,199553520.89477363,88379900.709219798,-1846914.3975690817,778404.67555819778,417606.6805098654 +5,0,1366926.0296145864,-35209342.86299181,-718532938.26496387,-308392520.28327638,1120218.3473974604,8894.265589923667,-25565.055264334027 +6,0,1366926.0296145864,-750367080.24278295,1040795009.4428687,453711488.93813241,-748219.94295809406,-425988.34405618918,-145312.6374682457 +10,0,1394255.2611955395,-759977.80689333007,-231527.8038175565,-78138.784542168738,314.48291056921602,-1052.0564111998183,-455.98364927717017 +399,0,1394255.2611955395,-102965651.98664136,-102435446.11972897,-45181294.019446827,1852552.038553915,-1592841.203840204,-703760.68952724303 +301,399,1394255.2611955395,-40577.679762855172,-351293.19585238397,-194286.02508876473,84007.626002565332,-5859.5740515482021,-2602.5535224470559 +399,10,1394255.2611955395,-102205674.17974803,-102203918.31591141,-45103155.234904662,1852237.5556433457,-1591789.1474290045,-703304.7058779659 +4,0,1394255.2611955395,199957578.96918827,-48606513.194219477,-28896642.226250682,676064.31165367912,1979760.8798501482,881597.58173156902 +5,0,1394255.2611955395,678270823.9188621,289171905.12004817,106718132.30380945,-473461.89793303586,983903.02745740081,435923.13837477926 +6,0,1394255.2611955395,897478319.75767362,-1079286717.63485,-475276386.65027696,612122.58871399029,487850.63516792562,175740.66148788715 +10,0,1540037.7435625107,628498.90084087849,643025.38002604444,252205.42382998261,-923.63591148223816,460.45903843394262,216.02320948165141 +399,0,1540037.7435625107,2643526.329480005,-138662571.20653906,-61102844.92950689,2527111.0323729799,29005.059494638826,12431.730472547471 +301,399,1540037.7435625107,-374734.29918320896,-112233.267511338,-43819.304923593998,21798.633057134215,-78830.871977227507,-26495.913505385226 +399,10,1540037.7435625107,2015027.4286391265,-139305596.58656511,-61355050.353336871,2528034.6682844618,28544.60045620488,12215.70726306582 +4,0,1540037.7435625107,94058137.520531431,193028009.1459724,85134385.821311831,-1803487.3512046519,893427.99880858581,467242.28164426994 +5,0,1540037.7435625107,-255656396.39574125,-691410249.90966821,-290843832.97292084,1059937.4582405013,-279451.72740607447,-147659.63970557705 +6,0,1540037.7435625107,-1319723094.8317888,433367108.01214272,228939451.10908249,-342312.80205306987,-719810.09792945685,-281021.44501011219 +10,0,1565000.0455863921,486856.21582467703,175203.6131132115,55386.387190895708,-37.156090046814626,894.17758566081363,388.52662566763013 +399,0,1565000.0455863921,126473107.31323674,71903115.29829827,31622419.568302542,-1403691.6369292056,1996922.0020895831,879290.32156210719 +301,399,1565000.0455863921,128026.30753955245,311338.65034233034,166876.72068337351,-82702.2842225684,35064.946106137824,11094.02679858684 +399,10,1565000.0455863921,125986251.09741206,71727911.68518506,31567033.181111645,-1403654.4808391589,1996027.8245039221,878901.79493643949 +4,0,1565000.0455863921,-242991178.2676422,29265885.606154762,21269934.890574083,-190966.64007022683,-1743243.1217300082,-790704.12607539876 +5,0,1565000.0455863921,-801834811.80000269,101660317.35817008,64434678.62341217,-173334.63387270641,-981638.76002203347,-417804.2193836873 +6,0,1565000.0455863921,-95726863.640287668,-1405971995.382911,-571438330.49038398,779269.44017201581,-28746.695200808259,-42380.131152042246 +10,0,1889839.0155428546,719836.50959284685,68833.436357806044,14611.810434701043,189.77594036846585,768.69027282739853,330.97140741406986 +399,0,1889839.0155428546,-138364902.48478115,47296786.197665848,20676527.785093918,-939128.44165621756,-2215019.7108430685,-969654.34982069547 +301,399,1889839.0155428546,-261664.32007193565,-234427.28468436748,-124198.52134773135,60457.164867589687,-59035.655430183899,-33521.837339324367 +399,10,1889839.0155428546,-139084738.99437401,47227952.761308044,20661915.974659216,-939318.21759658598,-2215788.4011158962,-969985.32122810953 +4,0,1889839.0155428546,-148802749.47486317,177183231.31609508,85671177.23088935,-1566902.942095832,-1019305.1052165934,-418758.46154279157 +5,0,1889839.0155428546,-775392250.88077521,201971370.20056602,106348766.33179846,-327405.28233183734,-952729.50221286423,-401096.13944470783 +6,0,1889839.0155428546,1258794002.5284052,-660338298.37609601,-322359477.0464617,368562.75085798593,678644.82249033032,263468.69682902459 +10,0,2027804.330076247,62249.76468689536,-78143.086725076326,-48135.941918988698,779.55332266714379,279.39488519691173,104.83797036428177 +399,0,2027804.330076247,81634751.008104131,112749330.51308981,49235806.083713755,-2187344.5236724559,1296655.2028865039,566100.2017827424 +301,399,2027804.330076247,-99438.883430570364,330718.79413504899,127845.67404284328,-89696.478194018913,-14847.316331671209,-14973.797525035339 +399,10,2027804.330076247,81572501.243417233,112827473.59981488,49283942.025632747,-2188124.0769951232,1296375.8080013068,565995.36381237803 +4,0,2027804.330076247,71546620.496415392,199541564.84508044,89264070.884582832,-1894346.6473023503,723942.31680345186,387921.96363776835 +5,0,2027804.330076247,-279183816.54482061,668300795.94156718,293988326.13816065,-1066258.5211465377,-332482.10949176934,-116133.95912687063 +6,0,2027804.330076247,21947051.965795226,-1396746454.298749,-574829445.01212502,785298.14579425787,23971.187960232812,-22655.022211400901 +10,0,2086815.8666723573,-795679.73180566053,239894.17696613466,124242.09089082951,-383.61897730562356,-897.66428734228509,-380.27458307739965 +399,0,2086815.8666723573,-25223220.420830529,-137286886.74172539,-59878600.976765573,2497347.3756929459,-387364.76033586101,-168768.3644281578 +301,399,2086815.8666723573,242639.0815602541,259651.46321371198,134961.66818419099,-72169.299504405732,45881.606309370494,26635.470723991635 +399,10,2086815.8666723573,-24427540.689024869,-137526780.91869152,-60002843.067656405,2497730.9946702514,-386467.09604851872,-168388.08984508042 +4,0,2086815.8666723573,177865583.26885295,113899116.67111084,46953013.592959613,-1093655.9676846594,1710400.8997220506,815766.16147857718 +5,0,2086815.8666723573,615356770.45875466,-391609063.87699097,-183475462.81719759,637325.44311552017,901931.58072537044,371376.56521035632 +6,0,2086815.8666723573,100969230.44989257,1241465358.2270935,506248438.74994004,-880270.35741452023,46968.54298108731,55935.68221279078 +10,0,2099489.6272558859,-98908.210391892091,392943.82485257211,167873.49369500036,-831.87833971150985,-289.54238058273523,-107.79517335756842 +399,0,2099489.6272558859,-132056357.98271817,61903758.657800734,26982525.283405062,-1210852.9838009048,-2110916.1369288079,-920921.10023620527 +301,399,2099489.6272558859,391712.98889483511,42588.833707660437,-12469.274598196149,-10443.768712517811,76715.42291709948,38309.285415361141 +399,10,2099489.6272558859,-131957449.77232628,61510814.832948163,26814651.78971006,-1210021.1054611932,-2110626.5945482254,-920813.30506284779 +4,0,2099489.6272558859,-245723116.79959786,-7309822.3322583521,3830602.9564854666,137330.02411354604,-1746628.3824186435,-804288.8284863435 +5,0,2099489.6272558859,342923292.54386282,-627602503.6304276,-277986268.79383302,998242.10164144763,520791.09679809003,198732.53700891862 +6,0,2099489.6272558859,-1274548327.2677507,501067440.15145624,259168898.30899084,-384187.23247185623,-710029.8904072427,-276013.11018963216 +10,0,2120571.7387645813,601786.10790288902,202364.9246442047,72425.95854305214,-793.4456803889766,472.96230529418119,216.90279049818085 +399,0,2120571.7387645813,98285124.567659885,102089583.46455696,44508699.935892865,-1978291.1482050095,1551530.7261598012,676375.13476195699 +301,399,2120571.7387645813,-232664.07951657474,-261509.48931859434,-130387.51262889057,73533.620175558899,-46263.81487983033,-28157.646273923085 +399,10,2120571.7387645813,97683338.459757,101887218.53991276,44436273.97734981,-1977497.7025246206,1551057.7638545071,676158.23197145876 +4,0,2120571.7387645813,22690948.97972177,212498714.36542284,96705524.016103774,-1993737.0222408166,313596.84458833508,201787.7977233204 +5,0,2120571.7387645813,-288262031.83491343,-685543935.46227217,-287122969.79236215,1040450.9161762344,-318361.55747931561,-162534.21936913967 +6,0,2120571.7387645813,-1070678604.6756555,782671401.4617641,366689107.570135,-571257.27067430783,-607284.20141264203,-225984.73638259841 +10,0,2129691.5391471279,52245.763763670933,225140.13702041117,102809.25576647997,-748.60907804900398,-327.47818158253205,-125.92058462910524 +399,0,2129691.5391471279,118538196.75593223,82142475.74655503,35828784.098640934,-1593592.874858632,1877068.8624099866,818160.78655203525 +301,399,2129691.5391471279,-340349.264203161,163359.94846588373,35177.988661788404,-32815.259109088744,-75580.634058082796,-34649.741331709731 +399,10,2129691.5391471279,118485950.99216856,81917335.609534621,35725974.842874452,-1592844.265780583,1877396.340591569,818286.70713666442 +4,0,2129691.5391471279,-243993756.90561414,36225674.838996746,23698961.320317954,-269611.77785708749,-1722606.5235176869,-781497.63354250183 +5,0,2129691.5391471279,210745843.96482921,-682758418.63624871,-298318793.82373732,1074990.0808944716,339100.8185794758,118851.04141020701 +6,0,2129691.5391471279,225037283.11990792,1229574569.0415993,496456657.73483026,-870591.93229054357,118348.48426269997,85061.325074702807 +10,0,2136031.8966931319,300761.40511707781,-93971.897766627299,-52851.092133560487,829.21334531757122,157.24336169580829,51.446240717315618 +399,0,2136031.8966931319,-145968152.74305409,24276767.832828704,10554013.417778656,-502648.25399232126,-2327224.4506025868,-1014495.0470628883 +301,399,2136031.8966931319,-363542.74368751049,3755.1553276926279,-32372.437844518572,4684.211475105104,-84668.399258849167,-38921.515225957395 +399,10,2136031.8966931319,-146268914.14817116,24370739.730595332,10606864.509912217,-503477.46733763878,-2327381.6939642825,-1014546.4933036056 +4,0,2136031.8966931319,-92396357.856428593,-187197398.85271659,-83097454.783372059,1998067.4804792111,-607290.82764326804,-336322.68044812564 +5,0,2136031.8966931319,-176590887.26378736,692906463.36545205,301808567.79089618,-1111608.6944989644,-199645.37743766894,-58080.484139468594 +6,0,2136031.8966931319,530595907.32363993,-1289171416.3514507,-552632430.89649689,732952.83825500507,286562.45020076178,87274.140733310589 +10,0,2152046.0972028235,-804662.45225551585,-91646.232153783247,-20872.642403275175,-12.217527650470018,-950.1465811560048,-412.36321408844634 +399,0,2152046.0972028235,-55929728.21855174,124934648.09143758,54457127.202658847,-2430292.9634709298,-891747.46106921136,-388901.81901437155 +301,399,2152046.0972028235,-213468.03732085973,-288252.4029724896,-89982.696465872228,74521.657056449039,-49538.20590339003,-19937.256285700027 +399,10,2152046.0972028235,-55125065.766296223,125026294.32359137,54477999.845062122,-2430280.7459432795,-890797.3144880553,-388489.45580028312 +4,0,2152046.0972028235,203484691.07636839,51865557.816598415,17874843.331937201,-451048.93949722964,1989172.5825124797,924648.33287775808 +5,0,2152046.0972028235,722867886.04743457,-157510989.87702823,-85543086.945528582,259007.54405865876,1056758.6908656415,447158.11161576048 +6,0,2152046.0972028235,-508267495.77646565,1150054513.9644098,494552625.90265369,-821295.30204820575,-300666.37479726312,-89341.033100792483 +10,0,2191169.7453055531,-759845.81634070189,146044.40216129573,84533.518584882011,-115.1284416233411,-1187.804386918461,-501.46802951241983 +399,0,2191169.7453055531,-133577179.35463855,60098616.423502661,26179725.74245115,-1180543.5686987306,-2124903.8412678358,-925579.8879813212 +301,399,2191169.7453055531,-289951.21323598921,-206911.77295928448,-104637.33339931816,57327.537181398955,-67684.066274933735,-21453.001503787793 +399,10,2191169.7453055531,-132817333.53829785,59952572.021341369,26095192.223866269,-1180428.4402571071,-2123716.0368809174,-925078.41995180876 +4,0,2191169.7453055531,205896926.075528,-18099303.00457928,-14209748.031088138,315266.77474534209,2051664.3897380724,931319.66444016946 +5,0,2191169.7453055531,741069367.65622187,-18970615.556932595,-26463769.253140029,31385.388210380795,1084797.75893969,464765.05299046205 +6,0,2191169.7453055531,1294900291.5741408,-601894442.70103419,-302300824.42123652,334435.97224718379,692066.95444769517,270876.25262683601 +10,0,2222902.5223428425,-31811.921223032059,-437857.05956900999,-184506.89565301503,808.58258090666664,309.21598126670989,119.12582198111686 +399,0,2222902.5223428425,-46977710.394847795,127370599.82010448,55439735.525237501,-2480850.5502371769,-761267.3660579148,-331699.55435256142 +301,399,2222902.5223428425,377774.18447648734,123125.77356898785,66815.591044165194,-31009.989227167614,68310.62396753137,37260.194433984616 +399,10,2222902.5223428425,-46945898.473624766,127808456.8796735,55624242.420890518,-2481659.1328180833,-761576.58203918161,-331818.68017454253 +4,0,2222902.5223428425,78525237.33104755,195397281.7189236,87341290.080230415,-1877004.7706001916,792496.74316675356,416756.16362342658 +5,0,2222902.5223428425,-405506792.52435106,616706161.32620692,274623575.79391831,-979592.07322265918,-495999.84434182767,-188652.35980610672 +6,0,2222902.5223428425,1054887317.1394116,-944486035.12823963,-433580101.73650342,536020.20070164872,559956.39507466624,208006.51728907338 +10,0,2237832.9073254745,17274.874995160946,198266.20438364861,92708.483425302591,-797.69720940317825,-38.748273667062229,-2.5063441405931837 +399,0,2237832.9073254745,67742345.696188971,120439686.35588382,52421706.863086291,-2330818.5846573864,1076176.4576625729,468089.58520350611 +301,399,2237832.9073254745,-389466.47788223624,-80939.715276628733,-74611.34033356607,19949.183632475524,-74371.267878019091,-33081.600688428807 +399,10,2237832.9073254745,67725070.821193814,120241420.15150018,52328998.379660986,-2330020.8874479835,1076215.2059362398,468092.09154764673 +4,0,2237832.9073254745,161298398.46057799,-116237441.82282674,-57861915.90850284,1399421.8697204893,1655046.3517958715,718895.77710493864 +5,0,2237832.9073254745,15965723.845020093,-719680136.43737435,-309176251.59463125,1117402.0783430669,79148.247016362729,6412.9184276650285 +6,0,2237832.9073254745,-262641291.04375511,1215521949.018343,511823073.82079452,-867163.15764808271,-163389.15809150602,-30636.6211961872 +10,0,2243651.4172194409,-612543.77663846279,-272835.18075540283,-104564.27420752516,573.66531568860921,-857.24723507911494,-377.2730765211125 +399,0,2243651.4172194409,118082179.6347068,81494355.339958385,35483220.750790514,-1588263.3894983626,1879258.9775326706,817604.8724098953 +301,399,2243651.4172194409,-400358.99508082867,29497.166477665305,4392.3212913870811,-3444.1796453665006,-74328.962029269765,-40334.787624958488 +399,10,2243651.4172194409,118694723.41134526,81767190.520713791,35587785.024998039,-1588837.0548140511,1880116.2247677497,817982.14548641641 +4,0,2243651.4172194409,-178296492.98881412,154913689.48416907,76074393.751507685,-1371713.8250774362,-1222106.139690618,-521261.45480805839 +5,0,2243651.4172194409,549035406.38062036,468528850.15211165,187496448.57818928,-775223.32101531746,804314.68321204628,364188.48712537542 +6,0,2243651.4172194409,560457514.26709211,-1281846383.6349003,-551821135.75327826,725014.66176911478,299568.57522781764,92725.699703466642 +10,0,2254293.9172081626,899278.66154223622,718540.70017971541,281049.66639121919,-625.85045935375547,832.79436431692795,378.17313308830251 +399,0,2254293.9172081626,7170411.2295902967,135517779.24887457,58921814.968575045,-2614682.4303279612,92918.830549306236,40273.987093444593 +301,399,2254293.9172081626,380511.42355155386,7651.5220775306225,-22303.989354915917,-5616.3460308893264,83204.314382687036,30290.768787730598 +399,10,2254293.9172081626,6271132.5680480609,134799238.54869485,58640765.302183829,-2614056.5798686077,92086.036184989309,39895.813960356289 +4,0,2254293.9172081626,119144352.5474879,-154678277.9468779,-74302333.531088635,1811260.4764499622,1255839.6448417746,524411.35774215474 +5,0,2254293.9172081626,-747495091.18108141,-302156990.4034093,-111246623.36155902,434007.94928596308,-900408.45590670081,-396991.66149795044 +6,0,2254293.9172081626,477642506.86673558,-1314990718.9624736,-562007619.46510541,741919.03438888607,255266.55163186576,73734.399212020304 +10,0,2261064.7260303102,78279.925993263925,-309436.61472286488,-142189.29012209762,862.09990338308808,-441.80578720322882,-216.06734809690653 +399,0,2261064.7260303102,30066741.752402131,-137074691.406288,-59627976.785093144,2480505.2321906392,456405.70612564805,198815.35994236975 +301,399,2261064.7260303102,206668.07659891248,-324600.82258298993,-129551.26468621194,71782.80924915272,42206.110930854484,10416.677756978685 +399,10,2261064.7260303102,29988461.826408867,-136765254.79156512,-59485787.494971044,2479643.132287256,456847.51191285125,199031.42729046667 +4,0,2261064.7260303102,-87974325.519609347,-189256832.16448519,-84271723.576552853,2012800.5341086108,-566319.58366937935,-316493.53339307412 +5,0,2261064.7260303102,478557890.82745934,532802686.66184503,216803960.96996856,-879112.95465606602,704000.18020278343,323668.91203609551 +6,0,2261064.7260303102,-1338865088.5012829,350300852.61838943,201075609.78967202,-287665.60691885097,-744235.68245532527,-294455.07648196619 +10,0,2262317.2403235584,1165687.1981577098,189272.85614616086,40109.297382762554,352.01807493019174,1066.8028742116967,447.02270919003621 +399,0,2262317.2403235584,39258178.636320405,130690985.75357826,56806022.480646923,-2529667.5479012784,603972.82502993324,262583.42071400472 +301,399,2262317.2403235584,-153357.85277006775,-297640.9833009094,-124264.70397567004,86033.903907757078,-33086.440453248018,-23889.944223251921 +399,10,2262317.2403235584,38092491.438162692,130501712.8974321,56765913.183264159,-2530019.5659762085,602906.02215572156,262136.39800481472 +4,0,2262317.2403235584,-241525888.46378499,-31677011.764772642,-7712189.9726098655,361563.49845678295,-1723178.1549274859,-800200.94449534162 +5,0,2262317.2403235584,-726831770.318578,312744568.06009227,152052776.8201668,-497161.20061463816,-892748.13348372676,-370783.22377433715 +6,0,2262317.2403235584,-1319297281.3960383,-592417075.88443005,-188180546.47313049,304997.90800477844,-694585.35204007826,-299114.69789699547 +10,0,2287908.3421941791,4143.9516225847719,-508536.67761969334,-210540.44099679697,834.11764347571716,318.26445362703174,121.6400068030745 +399,0,2287908.3421941791,-21953756.194393236,132856552.8964617,57770928.25066182,-2585724.2973605297,-359462.22431590874,-156593.75812690536 +301,399,2287908.3421941791,-183279.72734864801,322234.80639927089,117508.20022240281,-75671.866680143154,-42825.783475537486,-11654.411327583935 +399,10,2287908.3421941791,-21957900.146015819,133365089.57408139,57981468.691658616,-2586558.4150040057,-359780.48876953579,-156715.39813370843 +4,0,2287908.3421941791,21324855.034284212,-195152176.95042789,-90046156.895709589,2169121.9733538395,371110.19101050834,109216.26181886965 +5,0,2287908.3421941791,-426734681.64347178,605755859.30249131,270348171.97192574,-961288.59246410045,-522202.50345125294,-200400.94405535512 +6,0,2287908.3421941791,1257204675.3826034,-679694059.92511404,-333569058.46586382,380592.31727740786,666824.27889892866,258648.57462729231 +10,0,2298029.5066036088,149182.21664803207,762304.2658384369,326880.27929181803,-1028.4615013844229,361.78414598241687,185.22163367148752 +399,0,2298029.5066036088,149204055.89610764,-17565035.370481946,-7628356.0678320909,297323.63310723199,2331615.3080781614,1013519.3929126936 +301,399,2298029.5066036088,91424.215324014425,-316199.46040638164,-168365.16250440106,88802.255996855427,18355.481261480385,13010.616232201619 +399,10,2298029.5066036088,149054873.6794596,-18327339.636320382,-7955236.3471239088,298352.09460861637,2331253.523932179,1013334.171279022 +4,0,2298029.5066036088,-245536212.94441086,-12847696.632431304,972601.05105793476,186711.45923905601,-1739224.6172007893,-802639.9804357473 +5,0,2298029.5066036088,-463816049.96891904,-608697436.81549561,-249726458.51609185,909843.44655625778,-540577.57513745583,-254226.77942001953 +6,0,2298029.5066036088,944010183.63852286,-1054014418.0031654,-474575367.1157136,597436.50193117408,495604.68340246088,178865.41231368008 +10,0,2327275.1795267262,910007.93583023327,232008.80454877202,63263.281612006467,337.5462780713616,995.27956453483102,417.82716311148369 +399,0,2327275.1795267262,143647267.50891253,40456719.808969781,17549005.176665574,-797059.61031048745,2249038.2975293007,977176.50684092485 +301,399,2327275.1795267262,87323.947360634804,365576.11818409711,139966.30951103941,-81553.179509460257,21727.920970028641,1931.7417379628182 +399,10,2327275.1795267262,142737259.5730823,40224711.004421011,17485741.895053569,-797397.15658855881,2248043.0179647659,976758.67967781343 +4,0,2327275.1795267262,159049076.4332011,139181059.03282508,59371987.396541491,-1361151.3141347459,1523451.4934614771,736387.40107031842 +5,0,2327275.1795267262,-714296602.53369629,335339240.3323164,161334263.99374431,-532413.3435945753,-876578.3091100161,-362928.68891950627 +6,0,2327275.1795267262,-1173435032.4939845,-835478786.27492797,-294479617.65006506,454189.69579373742,-611656.44855240791,-271541.85007022793 +10,0,2331417.6902572853,-94589.664800175218,-160192.94541794911,-65260.800358124317,659.47029771874793,445.69791172180629,179.59243235338104 +399,0,2331417.6902572853,-118286261.6833428,81072233.155126333,35218664.19565849,-1586075.4006784488,-1897211.1424093132,-824372.02340222429 +301,399,2331417.6902572853,140419.12349501252,-309112.10636629164,-170494.26783139259,81128.859121602392,34002.028975589928,17162.027873070998 +399,10,2331417.6902572853,-118191672.01854262,81232426.100544289,35283924.996016614,-1586734.8709761677,-1897656.8403210351,-824551.61583457759 +4,0,2331417.6902572853,126980158.65456004,167279108.00660402,73175239.822979823,-1633719.9057955747,1230260.9309102115,609525.40238416276 +5,0,2331417.6902572853,-590013082.14314902,488487456.87971961,223978044.10941631,-772267.53682600264,-726077.13716345839,-292495.13934535009 +6,0,2331417.6902572853,1389627952.1570451,-353825591.4134317,-205156888.99221995,185709.42000652538,741915.99945261178,297986.22219268058 +10,0,2334130.121555666,-1086143.9495426279,-219847.42405115435,-64694.834599984664,186.0312950162558,-1122.1563958897734,-489.85578255806757 +399,0,2334130.121555666,62820829.598802768,-126766391.76535879,-55035461.96210257,2293837.49019892,982123.11434910516,426894.24898472481 +301,399,2334130.121555666,273426.79222983122,245900.98786973953,109288.0992680341,-58321.856335629993,63477.33027527442,19113.464594928173 +399,10,2334130.121555666,63906973.548345394,-126546544.34130764,-54970767.127502583,2293651.4589039041,983245.27074499498,427384.10476728284 +4,0,2334130.121555666,175549501.89499348,114674710.71187784,47708162.571843922,-1120857.4164402767,1703759.650468349,812401.41925021389 +5,0,2334130.121555666,740215867.03228176,-29609341.718846321,-30833904.662636369,47224.179923605247,1084578.1187022834,464027.23056967143 +6,0,2334130.121555666,246782399.50608405,1228075576.6181493,495838623.96276319,-867934.95630151813,127113.87295366528,89428.029663003457 +10,0,2337320.8779854057,247781.09464800125,760656.66027396917,308356.13918567594,-1071.9904291138287,324.3273707172803,162.7283921881839 +399,0,2337320.8779854057,-138801010.39377666,-50850045.069251679,-22119195.935322523,921398.61577859218,-2196846.4748548018,-954214.61085856135 +301,399,2337320.8779854057,272151.9569991231,-210745.41005213559,-127319.14851891994,60040.053521680296,64700.162349085011,26969.98267228539 +399,10,2337320.8779854057,-139048791.48842466,-51610701.729525648,-22427552.074508198,922470.60620770592,-2197170.802225519,-954377.33925074944 +4,0,2337320.8779854057,-85314081.823724389,-189163802.14098251,-84400519.477199778,2018114.6429841318,-542730.87917616754,-304923.34053748229 +5,0,2337320.8779854057,-158585400.07909256,-714866745.87677062,-302732109.07814044,1092285.1377093592,-149090.44838522954,-90709.204233596713 +6,0,2337320.8779854057,-1394410665.3426485,-343762172.20947623,-82292085.436997533,155832.81411062955,-748735.43897035089,-315395.80726797786 +10,0,2338572.9299390446,-733978.74922599469,174340.12762066803,80586.478819036391,-26.297224820956512,-938.10202024663579,-399.68916456427877 +399,0,2338572.9299390446,148328742.56848523,-18217473.162346501,-7899460.913891511,299709.90792849491,2330619.1799540557,1012429.8602783752 +301,399,2338572.9299390446,-64879.838449984789,-348290.95227034763,-191722.62353033852,82361.999687221381,-14097.69892202246,-6482.097382440872 +399,10,2338572.9299390446,149062721.31771123,-18391813.289967168,-7980047.3927105479,299736.20515331585,2331557.2819743026,1012829.5494429396 +4,0,2338572.9299390446,-243196315.97516173,-33795216.106859848,-8763526.9237425346,378590.4975921553,-1721177.4697557397,-799698.84948832018 +5,0,2338572.9299390446,734325190.73275197,88533822.819950208,19967753.996009607,-149479.07212957929,1077887.043722837,465968.03120230272 +6,0,2338572.9299390446,-878597702.45175731,-1122927405.4403727,-425589110.66634417,625903.84882303828,-449119.14424768981,-211894.62505268989 +10,0,2380645.0336247454,756067.31772556435,1073743.4479081845,430337.95234245301,-1149.4669784392886,368.36603909390681,184.27074819553266 +399,0,2380645.0336247454,75704086.945917279,117856512.54998891,51126537.213543922,-2262098.9623230333,1190487.8040291902,516654.66509534133 +301,399,2380645.0336247454,-299828.70168225467,-219552.27352672815,-128929.34437176585,53132.336821870857,-64210.071117433443,-23991.509968363192 +399,10,2380645.0336247454,74948019.62819171,116782769.10208073,50696199.261201471,-2260949.4953445941,1190119.4379900964,516470.39434714586 +4,0,2380645.0336247454,9529633.8158030659,-195692404.0703823,-90044305.002909184,2172278.8080862262,260994.49578405605,59945.583371684348 +5,0,2380645.0336247454,-158645039.67992848,-714929520.24199855,-302697851.20587301,1091411.3363159264,-149896.36522988317,-90967.778175578293 +6,0,2380645.0336247454,-1332301204.026154,-544769054.48355901,-167781299.87718248,281502.22606058459,-708159.28645035345,-304226.06725073088 +10,0,2398495.5430682423,-813382.8601499541,188163.35546851746,105812.96681874918,-648.36141445616602,-673.44802757218088,-275.78359884720646 +399,0,2398495.5430682423,145184433.25485334,29920568.879924223,13012814.663183527,-600872.67680765036,2294699.8726837179,995742.15994458925 +301,399,2398495.5430682423,351789.95705485344,-87761.542996484786,-72508.475399086252,29179.363965001732,79097.81431623979,36890.270257887307 +399,10,2398495.5430682423,145997816.11500329,29732405.524455704,12907001.696364777,-600224.31539319421,2295373.3207112895,996017.94354343647 +4,0,2398495.5430682423,-15971621.25117249,-198341345.65587571,-90525510.915965661,2168894.3842597003,51378.882294792988,-35803.659820766472 +5,0,2398495.5430682423,399383559.21854508,-595874402.78166521,-265249433.49807611,949215.9281622601,599312.10434458393,233780.50733145353 +6,0,2398495.5430682423,414460115.10808611,1196090909.9417958,475824793.05548024,-840105.60428611003,221792.04683932051,127516.1036228661 +10,0,2413686.2331876168,839929.70712861617,-325304.9452569905,-164133.91465903239,626.6140324479544,802.5548546481474,329.1255376716656 +399,0,2413686.2331876168,-100036118.43424186,-103437886.52809952,-44899848.177016854,1873678.759278615,-1585674.8513307958,-687991.70691314165 +301,399,2413686.2331876168,382001.6778203845,-121636.96141207218,-46586.878627464175,28830.524575587653,68943.067477315402,37890.967236136654 +399,10,2413686.2331876168,-100876048.14137048,-103112581.58284253,-44735714.262357824,1873052.1452461672,-1586477.406185444,-688320.83245081326 +4,0,2413686.2331876168,140597850.27959752,-138999543.00265855,-67575742.698410675,1632130.2463554812,1458170.9681136615,624209.71184742101 +5,0,2413686.2331876168,-550093885.2495054,522437172.25928843,237410074.80718002,-827749.74255101872,-676570.00742913026,-269859.95385139366 +6,0,2413686.2331876168,-1002254336.39868,-1020244078.454566,-378161864.90899003,567705.40376237791,-516792.92818956164,-237669.47394473862 +10,0,2419099.8913994296,343914.59964714502,102953.98574393574,49388.08714776572,-460.10294156543392,576.23306505715243,257.25551012919374 +399,0,2419099.8913994296,-142234653.21965715,37750605.580937952,16378268.991582017,-753831.10094342066,-2277985.8785604178,-988283.2337963013 +301,399,2419099.8913994296,330465.09498018026,159111.63808003813,59289.255134753883,-42757.621619033402,70686.593698129829,39219.505745768809 +399,10,2419099.8913994296,-142578567.81930429,37647651.595194019,16328880.904434253,-753370.99800185533,-2278562.1116254744,-988540.48930643057 +4,0,2419099.8913994296,-25893181.851110358,-198540367.43971843,-90339856.826623246,2158144.2277900358,-42623.871731116851,-78368.766391681827 +5,0,2419099.8913994296,-652669497.5692457,-450128280.9605698,-177040917.86628282,658593.73067270021,-779832.90717987786,-350410.01211918594 +6,0,2419099.8913994296,1071344044.18525,816449749.00840116,291058551.28608662,-569745.48851004592,589449.86407574057,267766.5254858589 +10,0,2449479.6387276892,75081.602514802027,927433.65439988009,397033.6741555616,-814.07678957300607,491.48010129760723,234.713292983439 +399,0,2449479.6387276892,-103968663.65400779,-99415676.897257879,-43108842.645481184,1822218.973185248,-1636792.4526669548,-709567.20567037025 +301,399,2449479.6387276892,400410.79394945502,38679.054008454084,46204.845046199858,-9234.9207277465557,78444.329961096752,28309.882753571466 +399,10,2449479.6387276892,-104043745.2565226,-100343110.55165777,-43505876.319636747,1823033.0499748208,-1637283.9327682527,-709801.91896335362 +4,0,2449479.6387276892,207439263.78437772,-8027447.6480873972,-9319396.4328955077,201195.08939347428,2065010.675297691,941695.9000382747 +5,0,2449479.6387276892,-618520429.770805,-488078714.25227875,-194140595.65203217,716248.928536786,-736144.00399833068,-332992.03893947997 +6,0,2449479.6387276892,1324160760.1721256,-541000271.54681969,-280384061.5361827,302981.18812228832,704328.19487680553,277856.84240588039 +10,0,2460086.0360979214,-1317265.0220896695,-168207.0967738546,-37970.771917544334,374.54986188902626,-1158.5751704644761,-500.17831157467742 +399,0,2460086.0360979214,-77223377.710741818,-120359784.78198612,-52139826.254583478,2186475.7564471201,-1194253.8101822049,-517780.99330128281 +301,399,2460086.0360979214,54316.750983566046,346681.67736046016,179585.28833595663,-84945.188132126903,11871.274013299933,11082.609914700657 +399,10,2460086.0360979214,-75906112.688652143,-120191577.68521227,-52101855.482665934,2186101.2065852312,-1193095.2350117406,-517280.81498970813 +4,0,2460086.0360979214,-221319690.26114699,103772447.56349744,53573287.509449326,-901991.76357277448,-1528975.6976025545,-676930.87244392873 +5,0,2460086.0360979214,669996447.03957152,294411722.22733676,109886170.08994903,-491472.8730021526,984083.60738751781,433773.72708397254 +6,0,2460086.0360979214,1272808815.3423407,-649265205.93833685,-323001235.68284535,366036.81430117128,674867.13329718367,262988.82292738376 +10,0,2466525.8732528649,1009515.595290805,144528.37066467578,30470.566633707396,-343.01802979033636,1093.7691457061435,475.5197524746871 +399,0,2466525.8732528649,-39153517.554650351,129991062.3333901,56312458.075520687,-2518687.9699908067,-653331.74146838963,-283174.54296435753 +301,399,2466525.8732528649,331499.47501964122,-122602.80725114048,-87467.410212807357,41540.652785733881,75841.261780687273,34592.428464144738 +399,10,2466525.8732528649,-40163033.149941154,129846533.96272543,56281987.508886978,-2518344.9519610163,-654425.51061409584,-283650.06271683227 +4,0,2466525.8732528649,27361474.802295696,-194132156.20756927,-89791563.232515037,2155868.4144959594,418548.36627270246,133873.59573340151 +5,0,2466525.8732528649,-771257438.82232177,-247413066.93508676,-87275103.106291905,348377.46124626382,-931471.97101272224,-407716.74195366749 +6,0,2466525.8732528649,-1375496306.1974819,-422109912.54968184,-115119330.69852343,205554.84800567714,-733920.28002986975,-312028.15642904805 +10,0,2527446.1114655072,341554.84867037972,-116408.17788731915,-48015.809729443507,-374.83537313242181,636.80968137536445,279.25657026921868 +399,0,2527446.1114655072,132534475.31262048,62896629.289431185,27229610.862128798,-1229346.4550616192,2087955.5766267846,904248.70998558379 +301,399,2527446.1114655072,-188027.32820233703,-285026.91622550786,-121609.55600734055,79587.839328174479,-41430.704113165419,-27208.625601253316 +399,10,2527446.1114655072,132192920.4639501,63013037.467318505,27277626.67185824,-1228971.6196884867,2087318.7669454094,903969.45341531455 +4,0,2527446.1114655072,-242895133.00316998,44764800.867718413,26997113.431740779,-361697.07995916106,-1700089.2595502886,-770338.87035991717 +5,0,2527446.1114655072,-635861159.27323937,-469904173.45267457,-185926505.62821865,688530.03996844962,-758135.08000889269,-341536.10482362541 +6,0,2527446.1114655072,511590579.41765743,1166992153.0402052,460264212.49570036,-818119.7380957359,276789.24465253181,149885.3463559791 +10,0,2539199.4973181798,592024.32060003106,-1005741.8103577961,-437183.1763271892,1165.0845016322189,573.9263469779255,212.11040568260833 +399,0,2539199.4973181798,-7608632.229589262,133830567.80565083,57934820.934509456,-2610969.2518502241,-140905.48236879549,-60805.239254598448 +301,399,2539199.4973181798,231893.60862087458,-313203.02062146366,-113861.16622678936,68940.23835197759,45797.888114464244,13144.797283163156 +399,10,2539199.4973181798,-8200656.550189293,134836309.61600864,58372004.110836647,-2612134.3363518561,-141479.40871577343,-61017.349660281056 +4,0,2539199.4973181798,-224883801.83047602,-83475584.996833116,-32305303.852078564,840301.39894408488,-1599078.6953660585,-755848.51338077849 +5,0,2539199.4973181798,-424654780.12585855,605068152.71656477,269526922.91174763,-964459.21561168437,-519670.14411181328,-199321.24268936465 +6,0,2539199.4973181798,-353187776.99472535,1198917973.1387367,511072193.28259313,-850298.46838243701,-217428.17009526156,-52972.183756866303 +10,0,2544041.2294167071,836939.23356612574,127904.7066080209,30301.431275721901,114.30825669788662,915.08471239820165,392.42154823206732 +399,0,2544041.2294167071,-147185529.37817705,-17703089.235492367,-7679201.8657867163,293964.92098502954,-2351196.2993864887,-1017733.7076506928 +301,399,2544041.2294167071,333025.10852622986,146673.79478384554,92972.92839250993,-36822.855028851467,77672.451781815485,29091.357166977916 +399,10,2544041.2294167071,-148022468.61174318,-17830993.942100387,-7709503.297062438,293850.61272833165,-2352111.384098887,-1018126.1291989248 +4,0,2544041.2294167071,-188761876.31906784,-131177162.47933044,-55197209.615577817,1332826.6829586106,-1343496.8602285609,-651602.49167420145 +5,0,2544041.2294167071,-772615620.85085332,214482299.99874818,110585500.222857,-349512.94854648091,-944648.69970725442,-396275.79752662405 +6,0,2544041.2294167071,-89382091.86380288,-1391221680.9527254,-571370102.64462185,786150.14977697597,-36326.352864520188,-49161.404483834158 +10,0,2545005.9619791363,319385.20175392111,877942.91924749024,368194.71611340647,-1069.716662382308,401.675769043233,202.71466063053012 +399,0,2545005.9619791363,85124620.069521204,112114867.35411355,48513874.935304128,-2150814.6796580716,1345824.2695296176,582696.37148067018 +301,399,2545005.9619791363,-299547.83259084821,233066.03247600794,64227.264827243984,-59453.483863736088,-60122.041800686886,-23900.771888430205 +399,10,2545005.9619791363,84805234.867767289,111236924.43486606,48145680.219190724,-2149744.9629956898,1345422.5937605745,582493.6568200395 +4,0,2545005.9619791363,200149892.97587687,64733783.789097987,24382670.542180695,-599970.07709408482,1960364.987691571,915194.18408200378 +5,0,2545005.9619791363,-450447373.97640067,-617291268.08345711,-253580637.80019099,921541.8236191324,-523020.15076277009,-246382.20232220268 +6,0,2545005.9619791363,647194370.81544006,-1241289588.8336635,-541362352.71417415,706081.99826455873,340811.06840355211,110258.18498777834 +10,0,2545457.4552275087,-216875.51076475988,921266.16097223398,401885.89488835738,-1224.5937903664264,-218.11508384274325,-58.919242797562099 +399,0,2545457.4552275087,-117543965.95133653,83181026.594020009,36016113.870435469,-1605930.6541565589,-1887253.2085273708,-816714.0840450743 +301,399,2545457.4552275087,303623.85709019005,-249744.16484682262,-79330.969431824982,53740.863216446007,62508.449837572516,21265.81686237577 +399,10,2545457.4552275087,-117327090.44057177,82259760.433047771,35614227.975547113,-1604706.0603661924,-1887035.0934435281,-816655.16480227676 +4,0,2545457.4552275087,-121932447.93835436,-177008697.33207026,-78010357.328283727,1849018.2667936047,-827463.23217431956,-428457.41654404736 +5,0,2545457.4552275087,24505785.822398536,-719934036.08775759,-309048081.01980096,1114166.2503302167,91840.492003860811,12395.290705216226 +6,0,2545457.4552275087,943428177.7094574,-1051853116.0896277,-475897640.12044114,599177.72765812813,494636.08718891814,178503.35703357929 +10,0,2564512.9509296902,92588.650049497825,-21544.878419444809,-23554.097978756956,779.34457102535089,-300.70433197816413,-146.22791393974893 +399,0,2564512.9509296902,-136331793.30465278,-57280184.499361426,-24789454.223661128,1030633.4642513982,-2157283.8611461432,-933531.71639816626 +301,399,2564512.9509296902,-245854.54511743784,276146.42089901119,89853.187449268997,-70144.922940808232,-48034.711470503906,-26288.512093775069 +399,10,2564512.9509296902,-136424381.95470229,-57258639.620941982,-24765900.125682373,1029854.1196803729,-2156983.1568141649,-933385.48848422652 +4,0,2564512.9509296902,-225480327.17596674,94012196.917026773,49049414.341530509,-813403.49847555568,-1566623.8133742164,-697341.43204445869 +5,0,2564512.9509296902,319134167.652551,629155049.62078691,261814553.73676038,-1036836.7703212504,480791.96678278135,231034.32401582089 +6,0,2564512.9509296902,-853792122.41202283,-1138190776.1766675,-433590552.65281051,636006.88819036423,-436382.37860707246,-208153.93158309485 +10,0,2600234.2858049506,405397.52789709473,-268861.83801738103,-114295.33026414778,446.77407682631787,649.72400914242542,271.33493747298496 +399,0,2600234.2858049506,-95931557.078252971,102102232.33966252,44167315.820800424,-1989599.3385787597,-1553404.6186732196,-671536.5811326619 +301,399,2600234.2858049506,72063.034038886428,-376469.71021702886,-127081.61511419713,82576.961661738693,16556.821373176059,3362.4945734509365 +399,10,2600234.2858049506,-96336954.606150061,102371094.1776799,44281611.151064575,-1990046.1126555863,-1554054.3426823623,-671807.91607013484 +4,0,2600234.2858049506,-223997872.20275286,96040256.458059236,49921857.763252765,-835924.1809246355,-1556175.3824534079,-692237.78385204403 +5,0,2600234.2858049506,-715665191.72008276,332077733.58802575,159515390.73134491,-530296.74607639317,-879218.87692007201,-363818.06647567393 +6,0,2600234.2858049506,1365403321.4300685,-446548844.08135045,-244335290.75738522,243322.21896344356,723502.6966429872,288734.10638411768 +10,0,2608979.7969310419,756781.86520504975,100301.52636178937,19600.144660814145,195.43894887097014,847.26630652381289,362.2784095882659 +399,0,2608979.7969310419,-49123059.83593642,127157436.22002214,54964997.194116227,-2463190.1628198191,-810471.75377386727,-350129.81833870069 +301,399,2608979.7969310419,254916.75632757694,-221714.92567959428,-129237.59380484372,66271.138759831403,62221.051281405445,23957.054181329939 +399,10,2608979.7969310419,-49879841.701141469,127057134.69366035,54945397.049455412,-2463385.6017686902,-811319.0200803912,-350492.09674828895 +4,0,2608979.7969310419,88324400.646115124,189962859.89884046,84857433.214827508,-1855245.8369787289,887002.08173089416,455199.76973324217 +5,0,2608979.7969310419,-754836537.79344773,258426118.55497602,128886430.73282503,-417211.49627477449,-923558.45485388895,-385533.32287878386 +6,0,2608979.7969310419,208462280.84809926,-1376263257.3282206,-578572837.71794045,779741.66800624598,114062.05230632138,13160.269201257768 +10,0,2615533.9554745653,-178489.59800731501,16062.266476812571,4998.6098161895743,-219.05202912071508,-770.16973643549989,-330.48072810000679 +399,0,2615533.9554745653,2454286.1852386938,135124842.05837277,58418887.898823611,-2615808.8306244784,31447.129660463983,14016.498783887189 +301,399,2615533.9554745653,5637.1029513622634,-340188.21498626471,-156856.7331180945,90568.534520848974,7978.6441950346598,-5249.1309059334053 +399,10,2615533.9554745653,2632775.7832460087,135108779.79189596,58413889.289007425,-2615589.7785953577,32217.299396899482,14346.979511987196 +4,0,2615533.9554745653,-113590194.58340576,-181738805.5960173,-80446487.597750336,1891123.5615050513,-760037.08313260879,-397805.64315851784 +5,0,2615533.9554745653,687369892.49725759,-262281522.23530549,-128918045.61183235,427331.49013432645,1008937.6214926401,421843.16115539538 +6,0,2615533.9554745653,-1072841297.5604942,769685004.01460826,365350000.41551155,-563563.86045030993,-617701.66089311009,-231013.15705332524 +10,0,2635917.1527230339,257961.02770900208,395197.70261086046,164597.41248240109,-597.83804233350986,565.3393571275767,251.02433053376186 +399,0,2635917.1527230339,142808353.91453663,41546258.665558517,17930682.584758177,-814579.99929867638,2244154.5583349871,970118.96741961536 +301,399,2635917.1527230339,137373.96139597893,-339164.90022586286,-169793.43649566546,77819.00755605771,30064.546108347349,6502.4070448540788 +399,10,2635917.1527230339,142550392.88682762,41151060.962947659,17766085.172275778,-813982.16125634278,2243589.2189778597,969867.94308908156 +4,0,2635917.1527230339,-179892060.68048704,152877025.85960108,74783873.368933886,-1363219.0527400698,-1232247.978216996,-530234.90086710348 +5,0,2635917.1527230339,-512266198.42846155,-579213800.82084858,-235727648.99719551,859170.36080465605,-600385.78724378848,-277831.18600096786 +6,0,2635917.1527230339,-215527212.04651374,1227757534.1516883,517620350.32652283,-868594.17564871931,-141728.50887645304,-20691.848342505051 +10,0,2642390.5360429501,-1029235.8973237275,-336380.53689267271,-118875.61264126579,739.01563205183243,-793.98261354027488,-352.22908242407124 +399,0,2642390.5360429501,24663522.191442408,-137902888.62901181,-59572178.427150428,2495416.0155577632,388687.43634846021,167710.47510053124 +301,399,2642390.5360429501,-11065.7749697119,-373625.68672391772,-158730.23926307261,83566.995136054698,536.23958567766579,-8193.4524846218465 +399,10,2642390.5360429501,25692758.088766135,-137566508.09211913,-59453302.814509161,2494676.9999257112,389481.4189620004,168062.70418295532 +4,0,2642390.5360429501,19742742.759206221,-196140005.66824147,-90499901.865443602,2157057.623310972,368633.96643440175,113559.50776009531 +5,0,2642390.5360429501,422077691.01269501,571088319.98130131,234422851.38652307,-945416.49608351791,627229.19001237338,291384.32328029582 +6,0,2642390.5360429501,990883103.13520777,-1011931147.2500703,-462259622.18641692,576727.13610682974,517519.1740159638,188989.05749064338 +10,0,2649688.0114172399,-10973.093632877803,527612.43741151167,219209.7211009037,-1027.9709362111455,-241.24498361653275,-79.494056158407233 +399,0,2649688.0114172399,6430164.4737134753,-138881820.92212266,-60020420.532043539,2528450.6188950595,89645.156596132467,38437.39843066199 +301,399,2649688.0114172399,231603.12556655984,-273817.37268388271,-151967.06545255333,72571.807245051023,46213.81390494894,15301.245393264726 +399,10,2649688.0114172399,6441137.5673463531,-139409433.35953417,-60239630.253144443,2529478.5898312707,89886.401579748999,38516.892486820398 +4,0,2649688.0114172399,-216670992.70713976,109999648.21559718,56026990.336129099,-960899.69312555261,-1499380.4267490641,-663277.13852080156 +5,0,2649688.0114172399,297637437.54009432,-651358114.03983021,-286118125.06313449,1025935.597094204,459489.79417449085,172088.51381872428 +6,0,2649688.0114172399,-1303093872.8889651,-600984287.81563747,-191780064.07773927,317238.16619482578,-695554.91744519176,-301836.56827944884 +10,0,2700824.4628194338,331151.2804885598,197186.14983151684,76825.779257340546,-525.10627875576063,685.61053579906707,300.41261757022517 +399,0,2700824.4628194338,8231575.6674397774,-139161294.91459486,-60089125.178318746,2529027.6584768291,114536.53810581991,48895.420960990239 +301,399,2700824.4628194338,-374663.515338175,10758.773753970861,-26262.369229525328,-5017.4160009013685,-84107.187349356536,-31834.088365868043 +399,10,2700824.4628194338,7900424.3869512174,-139358481.06442636,-60165950.957576089,2529552.7647555848,113850.92757002084,48595.008343420013 +4,0,2700824.4628194338,104131033.45526709,-165403855.1614227,-78553795.221109524,1891216.1762117397,1123976.0841787448,467738.83513275615 +5,0,2700824.4628194338,-576700717.69105506,-529832006.11069691,-212989329.45346567,778950.79744882917,-680741.87415629672,-310165.42132240703 +6,0,2700824.4628194338,-494491760.65895081,1157071698.3857672,501054724.70388138,-819591.29354779795,-300372.91524002643,-88470.68821904737 +10,0,2711543.1667099777,-461854.25943179138,-1043042.4069318279,-430286.85477289982,996.6364647260159,-711.59292211588206,-333.5159379073433 +399,0,2711543.1667099777,120961399.29489869,77817039.46052669,33584753.641164802,-1526907.079927068,1917823.9777629334,828137.92833275627 +301,399,2711543.1667099777,152005.17981147766,-299295.63725988567,-166781.79879388586,84442.598584644569,29736.025592515467,11676.008427150839 +399,10,2711543.1667099777,121423253.55433048,78860081.867458522,34015040.495937705,-1527903.7163917939,1918535.5706850493,828471.44427066355 +4,0,2711543.1667099777,-237986100.8536658,65396740.303940028,36075171.203499012,-563743.13587951986,-1652515.7462323804,-744060.5450461203 +5,0,2711543.1667099777,564059959.89112532,447815755.93097049,178187669.43175572,-748628.49790223874,831686.25963101897,374036.70908793813 +6,0,2711543.1667099777,-460787709.6893537,1167580429.372082,503969374.42997319,-827939.27650321124,-281349.40033029392,-80199.546869382029 +10,0,2712215.8356112055,372982.40465777344,-1196232.9581941136,-519531.87932943774,1294.0285233877216,294.83068148122385,90.056465785886004 +399,0,2712215.8356112055,140907674.05312273,-52012400.866029859,-22483867.882688854,904383.85878020106,2189500.1243210291,944958.4864603926 +301,399,2712215.8356112055,-348199.96341067553,183864.77723930776,94433.62742363289,-41861.872570208776,-63929.071170824558,-35465.161334266682 +399,10,2712215.8356112055,140534691.64846495,-50816167.907835744,-21964336.003359415,903089.83025681332,2189205.2936395477,944868.42999460676 +4,0,2712215.8356112055,-227553954.84300566,88416632.583437324,46374784.188201025,-776716.4305632161,-1579768.3321870398,-705321.73898399423 +5,0,2712215.8356112055,-130499886.62386407,697335487.41221964,301684891.75219548,-1127591.499962443,-136309.60302587211,-31293.936323100115 +6,0,2712215.8356112055,-960407413.87181592,880513876.38730574,406981779.02026373,-636151.41240509704,-558141.16695132991,-203295.3245185822 +10,0,2725309.45320648,709091.53140688105,-440068.37662564928,-212490.35408248092,894.00146178408011,450.51738465008879,170.74555694873791 +399,0,2725309.45320648,40601999.041450851,-135145931.06814885,-58353481.338623077,2442997.9720001589,610969.85424073157,263182.68902857299 +301,399,2725309.45320648,-247753.72570178658,-261481.53720170259,-132086.96970115602,71165.29169588731,-45355.148478182404,-26455.765350949183 +399,10,2725309.45320648,39892907.510043971,-134705862.69152319,-58140990.984540597,2442103.970538375,610519.3368560815,263011.94347162422 +4,0,2725309.45320648,-246427762.5185186,20676792.122279793,15713432.627840398,-147275.26796392648,-1726203.5681825976,-788489.20511612529 +5,0,2725309.45320648,-237668146.48416796,678062825.14058065,295961572.6818561,-1089995.9103968637,-277241.53328875994,-92529.007711734477 +6,0,2725309.45320648,-1195513700.2337928,-786345045.76910031,-273333692.28833246,432977.38083647052,-632380.59364796197,-281105.10950867058 +10,0,2745392.846049882,-62615.297146284029,458152.72708088398,187560.33895600017,-529.23066772368588,-638.3164679535497,-265.04610713062584 +399,0,2745392.846049882,25750123.318370849,-137109815.71406034,-59166053.839187533,2494163.5962896477,390538.22829901107,167950.26587566419 +301,399,2745392.846049882,-70498.59513413161,-351508.32470929623,-178856.9412188679,84050.947732894041,-9112.7430943959407,-9553.4421642329526 +399,10,2745392.846049882,25812738.615517132,-137567968.44114122,-59353614.178143531,2494692.8269573711,391176.54476696457,168215.31198279484 +4,0,2745392.846049882,-75065969.99329856,-193592586.42468992,-86986759.414347216,2042391.7829828099,-446225.44528710115,-256032.1160433655 +5,0,2745392.846049882,626684592.21555567,-375604817.51297569,-175800842.60094872,607341.3926233711,921328.60198079923,379791.83979316906 +6,0,2745392.846049882,-1359601587.6215639,272166403.19865072,172670635.8878448,-231718.52519419254,-759966.50039688393,-304852.54400395852 +10,0,2774164.9606293491,448088.15840350912,-257791.99332652127,-107723.65267647651,-159.61715948365793,729.88457171715743,315.16708849041089 +399,0,2774164.9606293491,-140673890.28485256,-46177246.73052939,-19868953.756420221,817921.17510828236,-2237738.0868936959,-965232.26823120075 +301,399,2774164.9606293491,110414.62703517079,-332634.50232607871,-157867.01349444315,82333.594595852395,24863.998907889072,18679.103740099941 +399,10,2774164.9606293491,-141121978.44325608,-45919454.737202868,-19761230.103743743,818080.79226776608,-2238467.971465413,-965547.4353196912 +4,0,2774164.9606293491,-207311338.94758445,-112079356.04996613,-46254017.329790264,1119961.3592328541,-1465485.8538510173,-700510.13054519997 +5,0,2774164.9606293491,-762462565.25658274,-274847729.0026747,-99359721.299222812,387437.39980796317,-916921.88418233173,-401686.66957800038 +6,0,2774164.9606293491,1068257536.3366171,831040464.8123672,297539121.38924152,-572920.91490529943,580248.0495470009,265901.41996473819 +10,0,2790474.4587943852,757927.94421332434,-231315.39868980288,-126581.36674878166,740.64517388558716,609.08720491771476,244.58883023595885 +399,0,2790474.4587943852,47878699.519051813,128205703.79251131,55226019.905343294,-2479683.3842284335,744323.39801153191,321596.11743143015 +301,399,2790474.4587943852,-3523.0689736306667,-374677.83940377831,-125254.74451130629,86303.619071969384,2839.0666928581027,-1155.725634187884 +399,10,2790474.4587943852,47120771.574838489,128437019.19120112,55352601.272092074,-2480424.0294023193,743714.31080661411,321351.52860119421 +4,0,2790474.4587943852,-161188760.85381597,166764531.7281113,80533337.997066066,-1508924.6720310452,-1097065.5143238239,-466117.88377359248 +5,0,2790474.4587943852,-416969905.11767596,609292570.29630947,270686050.30951369,-972888.74867964198,-509259.58501910313,-194657.45059444063 +6,0,2790474.4587943852,-857050015.56909716,-1125692602.650573,-429134731.57915747,637004.26670721371,-442480.02181193873,-211694.1151886952 +10,0,3220882.2318521156,705416.8487658751,1112759.4760429095,446561.9351762397,-1081.7953491407343,628.52387553169183,295.82780570882113 +399,0,3220882.2318521156,-131600426.273826,-63714222.365568921,-27181987.919335734,1171390.0323369175,-2100744.7657913165,-900361.64265116374 +301,399,3220882.2318521156,287190.25853562355,204623.0590602383,88096.893050741404,-58679.44702403916,63242.674760293587,36499.494879429825 +399,10,3220882.2318521156,-132305843.12259188,-64826981.841611832,-27628549.854511973,1172471.8276860581,-2101373.2896668483,-900657.4704568726 +4,0,3220882.2318521156,127145929.47647579,-150416860.94788399,-71880153.395914376,1721679.9195421906,1335376.4408867529,575377.78740930685 +5,0,3220882.2318521156,-456373166.14974523,-616980280.776636,-252714114.49773023,912826.91283776076,-528810.26906156889,-247163.07552614264 +6,0,3220882.2318521156,-807066814.99702883,-1149338172.0408068,-443060640.4493888,657331.49981390988,-420322.85171357793,-205244.77834561619 +10,0,3522038.5148897273,110056.50484767664,-16613.006591152425,-19447.686801002634,787.88873079710515,-286.48651847621261,-137.19824613784144 +399,0,3522038.5148897273,133370206.05783606,62183357.141661592,26278764.118446916,-1200103.2453972441,2097299.0520725865,895123.93705909874 +301,399,3522038.5148897273,-17986.898892328143,-362193.04711612314,-161391.26644778624,84665.290674686039,-10145.092773908584,3519.1984598343424 +399,10,3522038.5148897273,133260149.55298838,62199970.148252748,26298211.805247918,-1200891.1341280411,2097585.5385910627,895261.13530523656 +4,0,3522038.5148897273,-39243030.689604692,209140429.64275125,96750182.783643201,-2024869.7930933982,-156551.4218204731,-31834.887610838785 +5,0,3522038.5148897273,293802376.47452813,635561851.78699481,264033414.70368987,-1060401.0486543966,450508.7366668178,216627.58475300754 +6,0,3522038.5148897273,-870310584.33829212,-1096696388.618474,-419762219.56776369,633792.15333532926,-459073.05057237478,-221580.23992920638 +10,0,3559348.6398703274,-548790.68918734929,286262.45392846817,146291.80013305493,-888.91812682720854,-372.71458541429809,-139.95395225597872 +399,0,3559348.6398703274,22784175.547997218,134430140.36742711,57221032.469525471,-2577702.5437567816,358732.66890176706,156701.31035158594 +301,399,3559348.6398703274,-179549.35987118632,325993.16425362229,149832.28890164942,-76535.775792740926,-35811.31441832093,-6914.8322999860638 +399,10,3559348.6398703274,23332966.237184566,134143877.91349864,57074740.669392414,-2576813.6256299545,359105.38348718133,156841.26430384192 +4,0,3559348.6398703274,-238430127.76609886,-62182142.675967455,-23884235.127862602,602288.64283137128,-1648839.0980139873,-768416.8006487767 +5,0,3559348.6398703274,93810257.110855103,-717385317.4362396,-307843204.99194074,1099259.7751216677,183831.95541153956,52832.085259415224 +6,0,3559348.6398703274,1182609672.4668341,722421180.54068661,248569149.56536657,-486909.41428985522,628044.16177704884,285831.20012196462 +10,0,3646878.6329298224,-749698.73736538005,22932.115104886343,27282.345596787658,93.692316364403382,-1065.5398277262204,-462.3818418687257 +399,0,3646878.6329298224,100089905.20911871,-104278072.45385405,-44497069.720660977,1887596.754755832,1558470.1129182847,659350.66471412824 +301,399,3646878.6329298224,362817.0748629272,30083.633481830359,-11490.14750187844,-3344.3878684526567,88014.438177131742,30138.084702202203 +399,10,3646878.6329298224,100839603.94648409,-104301004.56895894,-44524352.066257767,1887503.0624394675,1559535.6527460108,659813.04655599699 +4,0,3646878.6329298224,201623010.72873473,-33318180.405505005,-19091381.770639636,422803.35758950189,2053333.5251773715,934225.30486937752 +5,0,3646878.6329298224,735502629.55407512,-75308693.02887772,-49112512.037197709,113012.78495476655,1083356.4840918905,458786.67616147263 +6,0,3646878.6329298224,157037376.96542469,1257651465.2747264,520879183.92768192,-863104.83766322699,61024.265517823391,65417.880292777198 +10,0,3685691.7969500911,-861300.68953968037,351869.11709532182,174039.09387760653,-557.13603267194731,-1101.4842451308225,-451.4108318655139 +399,0,3685691.7969500911,104006229.83662674,97522347.68894279,41268466.139579229,-1862154.5735088601,1653700.0251794613,706085.1583323126 +301,399,3685691.7969500911,-332502.78835575283,-194372.69736206532,-103434.5827480033,49121.873954649163,-66799.957533849403,-21188.888950975143 +399,10,3685691.7969500911,104867530.52616642,97170478.571847469,41094427.045701623,-1861597.4374761879,1654801.5094245921,706536.5691641781 +4,0,3685691.7969500911,-246297246.48052597,41101818.998038232,23436025.872868404,-360263.37872430758,-1692749.607083021,-769914.00774753653 +5,0,3685691.7969500911,687174047.86679399,-262914130.87896961,-127856526.14426911,419271.66748449643,1011129.3816468153,420829.09163305222 +6,0,3685691.7969500911,814629057.17884731,-1136012401.2208102,-514945980.95201999,663726.4006903281,418249.04894464283,145133.12098811727 +10,0,3834580.5701606283,272121.94564087683,-56.618389920962727,-14677.089291653141,718.20269088086309,482.92855738713718,193.61096311684383 +399,0,3834580.5701606283,9677326.1669106279,-139358633.26135755,-59117744.375900045,2535317.7431928939,133500.90947394067,50983.194704818176 +301,399,3834580.5701606283,322472.32114823349,151287.07283136249,97818.239416882396,-43695.888397809264,75342.076161025543,28755.431035040714 +399,10,3834580.5701606283,9405204.221269751,-139358576.64296761,-59103067.286608391,2534599.5405020132,133017.98091655353,50789.583741701332 +4,0,3834580.5701606283,29516095.384629518,203706099.97997683,92900516.91818732,-2049868.1870130436,415844.78661446413,226667.14325408835 +5,0,3834580.5701606283,-371200085.35698694,627660660.21616507,275552739.2545166,-1017293.8578416235,-451944.25969808834,-168779.90464435017 +6,0,3834580.5701606283,-507658683.27496737,-1277452240.2328758,-514585155.63071573,750671.6282941422,-266367.20192498312,-147012.10059947686 +10,0,3910956.9868338462,-823352.60858663369,490566.91939699929,231245.27904739059,-653.29376559545597,-987.25368986948058,-398.67024979475588 +399,0,3910956.9868338462,96325284.770029023,-106783928.10711445,-45431995.074282996,1942068.0538848715,1503359.0934856324,631825.10601398477 +301,399,3910956.9868338462,-371632.0856950134,-41567.144876942039,-18264.739269733429,13175.107528721002,-85340.008891380843,-27427.286465473204 +399,10,3910956.9868338462,97148637.378615662,-107274495.02651145,-45663240.353330389,1942721.347650467,1504346.347175502,632223.77626377961 +4,0,3910956.9868338462,-193357361.18872902,138419750.19079736,66749778.316729128,-1281215.4629124489,-1316389.4258152,-581799.86757568538 +5,0,3910956.9868338462,671788294.6619041,-299853309.26521522,-142972153.54205498,475988.33237460459,986190.30174423137,408380.35906515794 +6,0,3910956.9868338462,343420180.64587504,-1325383717.8671291,-574855251.42316818,779387.15365075762,177938.25150005514,38821.473902110672 +10,0,4365292.1955561377,-470374.2744229791,719087.26395662525,320974.87432991189,-1008.5554868662208,-487.0203112786117,-183.64967498500494 +399,0,4365292.1955561377,-5750755.8401075527,-138739515.2606073,-58329470.302606203,2538700.1776225176,-96168.255539441321,-50063.812880695114 +301,399,4365292.1955561377,-243764.10760402028,-298089.83240705729,-128006.42416021228,66667.136080273616,-49133.028098025243,-12662.460558734794 +399,10,4365292.1955561377,-5280381.5656845737,-139458602.52456394,-58650445.176936112,2539708.7331093843,-95681.23522816271,-49880.163205710109 +4,0,4365292.1955561377,-76559620.544616058,-199719487.22411439,-90369076.555136099,1982576.3446111742,-426385.9889402978,-222358.75762053896 +5,0,4365292.1955561377,182541035.42921939,-702097613.83341956,-301890822.59975803,1068779.2520816161,302366.42139532743,103613.76622325028 +6,0,4365292.1955561377,1456873485.2846169,33833994.359257981,-53476710.24123054,-29364.095312228514,749910.36466922332,319878.90951038041 +10,0,4463509.6345559489,639504.67060380266,-590639.79318482406,-255308.714329521,337.85074212211907,832.51658640487756,340.94897714311895 +399,0,4463509.6345559489,-90608047.768275201,-110874952.9112087,-46217868.76097665,2014780.522994169,-1452693.1890121682,-618495.60598176473 +301,399,4463509.6345559489,-362781.14446261525,10094.23956592381,40846.71286958456,-3364.0845679902441,-84539.849233664165,-38129.234658447291 +399,10,4463509.6345559489,-91247552.438878998,-110284313.11802389,-45962560.046647131,2014442.6722520471,-1453525.705598573,-618836.55495890789 +4,0,4463509.6345559489,-114968253.74590702,-189421257.1374996,-85126135.027471319,1828903.1487474276,-732030.5240922462,-358727.11019392632 +5,0,4463509.6345559489,-809346194.34945035,64826353.12935394,46094099.236987948,-137489.75002276772,-984921.15119858074,-414195.04978393327 +6,0,4463509.6345559489,986281932.7473886,953654197.24144268,359736542.11237872,-620872.80976204283,507632.53366210917,244891.31186404263 +10,0,4751511.2397211073,-1278750.4926174963,496098.03276931861,250535.50718434656,-536.74401684777592,-1178.3893216467079,-484.12407730399491 +399,0,4751511.2397211073,101158078.0482174,100659938.92773394,41652031.146709815,-1900858.6012568541,1608766.0190202908,682868.52102462621 +301,399,4751511.2397211073,-217888.51695020497,-250589.93884703517,-133041.22438909113,74101.887946953982,-56325.203597452477,-19409.344885480819 +399,10,4751511.2397211073,102436828.5408349,100163840.89496462,41401495.639525466,-1900321.8572400063,1609944.4083419375,683352.64510193013 +4,0,4751511.2397211073,173930387.85539088,-102606239.04772317,-48747151.470482536,1130625.6138269848,1788251.7112173592,804166.9911084004 +5,0,4751511.2397211073,684770744.50092375,-273367029.58197844,-131425566.09753555,425193.59897124337,1006172.0457672633,415798.55149978586 +6,0,4751511.2397211073,1213120755.735692,-745148971.79660988,-374763263.34684646,451727.17128783627,622565.34559361369,245042.74254368604 +10,0,4836805.5100452611,646160.01533393026,898467.49851627753,373476.73622960848,-972.99975365246075,561.2671002800937,263.65117268101028 +399,0,4836805.5100452611,-82575890.962881774,-114750134.24058118,-47527007.7198359,2113739.7911811168,-1327325.8525787718,-566239.19191721221 +301,399,4836805.5100452611,-402155.54529306293,1885.0866488218307,42435.933481320739,-2821.6779986591064,-77381.229878840517,-31802.026990421127 +399,10,4836805.5100452611,-83222050.978215709,-115648601.73909746,-47900484.456065506,2114712.790934769,-1327887.1196790517,-566502.84308989323 +4,0,4836805.5100452611,169464054.64476943,112264292.64707901,49443175.507166527,-1242163.035353398,1729950.8117033236,802265.87650657562 +5,0,4836805.5100452611,-565420673.10670328,-551200605.52353716,-219982708.5710074,784528.31695648807,-660029.57833945099,-297163.11511526589 +6,0,4836805.5100452611,766622704.27409041,-1139498209.7470298,-523092481.01549476,692558.16035834036,395590.08065576403,137090.35458151219 +10,0,5068732.0711162677,19759.105967778785,-1078942.3415830047,-464503.99274743145,1176.342698814386,-323.72504105309946,-166.79642805755026 +399,0,5068732.0711162677,-105286780.42409669,-99728462.699190646,-40928112.502778672,1806285.4610295116,-1684448.0395283443,-713564.38401092531 +301,399,5068732.0711162677,59486.241197779775,361505.9684292078,174887.50090330839,-82687.210992255728,7942.0264184957887,10462.228980136808 +399,10,5068732.0711162677,-105306539.53006446,-98649520.357607648,-40463608.510031238,1805109.1183306973,-1684124.3144872913,-713397.58758286771 +4,0,5068732.0711162677,-127010563.11985897,-187575532.2808592,-84301907.109550267,1755079.4423116748,-812883.81732467737,-385531.8344932078 +5,0,5068732.0711162677,340585519.16016114,606363667.78401947,248084703.0578481,-1036177.7135462891,521316.01795226993,244164.95776344181 +6,0,5068732.0711162677,-1070545138.4277253,788559457.47666681,387919578.88665009,-543310.5874553062,-619354.33147116797,-240875.91829927298 +10,0,5113926.4576401599,854767.93400613242,614094.04534067481,237352.59922297261,-710.04915282154991,942.94605792769676,418.97025374472969 +399,0,5113926.4576401599,-89796110.63870123,107981215.8489171,45552490.783726625,-2058009.9968769827,-1480411.3970171621,-603602.8961995882 +301,399,5113926.4576401599,-279628.51858134568,254850.43192119896,87815.91144477576,-58379.281437255107,-63076.772211217954,-18480.713162779135 +399,10,5113926.4576401599,-90650878.57270737,107367121.80357642,45315138.184503652,-2057299.9477241612,-1481354.3430750896,-604021.86645333294 +4,0,5113926.4576401599,-247460203.20961854,24666852.381610889,13271222.264282024,-257319.7562977038,-1707187.9148789735,-775130.22432834352 +5,0,5113926.4576401599,-677494336.7458601,-433903246.67988849,-167390901.73140791,603727.88849768543,-803099.71704461752,-352988.18109985162 +6,0,5113926.4576401599,-1146308434.5083432,-779755748.28337586,-282225950.6534313,473385.7972441407,-629623.09217070509,-292360.65799079748 diff --git a/planet/model.go b/planet/model.go new file mode 100644 index 0000000..65a5c1d --- /dev/null +++ b/planet/model.go @@ -0,0 +1,17 @@ +package planet + +import ( + "b612.me/astro/internal/ephem" + "b612.me/astro/planet/internal/semianalytic" +) + +func init() { + ephem.RegisterModelEngine(ephem.Semianalytic, func() ephem.Engine { return semianalytic.Ephemeris{} }) +} + +func semianalyticActive() bool { return ephem.Active() == ephem.Semianalytic } + +// semiPlanet 与 WherePlanetN 同语义,调用方已做越界检查。 +func semiPlanet(xt, zn int, jde float64, n int) float64 { return semianalytic.Planet(xt, zn, jde, n) } + +func semiMoon(coord int, jde float64, n int) float64 { return semianalytic.Moon(coord, jde, n) } diff --git a/planet/moon.go b/planet/moon.go new file mode 100644 index 0000000..344f28e --- /dev/null +++ b/planet/moon.go @@ -0,0 +1,132 @@ +package planet + +import "math" + +var moonCir = [][][]float64{{ + //ML0 + { + 22639.586, 0.78475822, 8328.691424623, 1.5229241, 25.0719, -0.123598, 4586.438, 0.1873974, 7214.06286536, -2.184756, -18.860, 0.08280, 2369.914, 2.5429520, 15542.75428998, -0.661832, 6.212, -0.04080, 769.026, 3.140313, 16657.38284925, 3.04585, 50.144, -0.2472, 666.418, 1.527671, 628.30195521, -0.02664, 0.062, -0.0054, 411.596, 4.826607, 16866.9323150, -1.28012, -1.07, -0.0059, 211.656, 4.115028, -1114.6285593, -3.70768, -43.93, 0.2064, 205.436, 0.230523, 6585.7609101, -2.15812, -18.92, 0.0882, 191.956, 4.898507, 23871.4457146, 0.86109, 31.28, -0.164, 164.729, 2.586078, 14914.4523348, -0.6352, 6.15, -0.035, 147.321, 5.45530, -7700.3894694, -1.5496, -25.01, 0.118, 124.988, 0.48608, 7771.3771450, -0.3309, 3.11, -0.020, 109.380, 3.88323, 8956.9933798, 1.4963, 25.13, -0.129, 55.177, 5.57033, -1324.1780250, 0.6183, 7.3, -0.035, 45.100, 0.89898, 25195.623740, 0.2428, 24.0, -0.129, 39.533, 3.81213, -8538.240890, 2.8030, 26.1, -0.118, 38.430, 4.30115, 22756.817155, -2.8466, -12.6, 0.042, 36.124, 5.49587, 24986.074274, 4.5688, 75.2, -0.371, 30.773, 1.94559, 14428.125731, -4.3695, -37.7, 0.166, 28.397, 3.28586, 7842.364821, -2.2114, -18.8, 0.077, 24.358, 5.64142, 16171.056245, -0.6885, 6.3, -0.046, 18.585, 4.41371, -557.314280, -1.8538, -22.0, 0.10, 17.954, 3.58454, 8399.679100, -0.3576, 3.2, -0.03, 14.530, 4.9416, 23243.143759, 0.888, 31.2, -0.16, 14.380, 0.9709, 32200.137139, 2.384, 56.4, -0.29, 14.251, 5.7641, -2.301200, 1.523, 25.1, -0.12, 13.899, 0.3735, 31085.508580, -1.324, 12.4, -0.08, 13.194, 1.7595, -9443.319984, -5.231, -69.0, 0.33, 9.679, 3.0997, -16029.080894, -3.072, -50.1, 0.24, 9.366, 0.3016, 24080.995180, -3.465, -19.9, 0.08, 8.606, 4.1582, -1742.930514, -3.681, -44.0, 0.21, 8.453, 2.8416, 16100.068570, 1.192, 28.2, -0.14, 8.050, 2.6292, 14286.150380, -0.609, 6.1, -0.03, 7.630, 6.2388, 17285.684804, 3.019, 50.2, -0.25, 7.447, 1.4845, 1256.603910, -0.053, 0.1, -0.01, 7.371, 0.2736, 5957.458955, -2.131, -19.0, 0.09, 7.063, 5.6715, 33.757047, -0.308, -3.6, 0.02, 6.383, 4.7843, 7004.513400, 2.141, 32.4, -0.16, 5.742, 2.6572, 32409.686605, -1.942, 5, -0.05, 4.374, 4.3443, 22128.51520, -2.820, -13, 0.05, 3.998, 3.2545, 33524.31516, 1.766, 49, -0.25, 3.210, 2.2443, 14985.44001, -2.516, -16, 0.06, 2.915, 1.7138, 24499.74767, 0.834, 31, -0.17, 2.732, 1.9887, 13799.82378, -4.343, -38, 0.17, 2.568, 5.4122, -7072.08751, -1.576, -25, 0.11, 2.521, 3.2427, 8470.66678, -2.238, -19, 0.07, 2.489, 4.0719, -486.32660, -3.734, -44, 0.20, 2.146, 5.6135, -1952.47998, 0.645, 7, -0.03, 1.978, 2.7291, 39414.20000, 0.199, 37, -0.21, 1.934, 1.5682, 33314.76570, 6.092, 100, -0.5, 1.871, 0.4166, 30457.20662, -1.297, 12, -0.1, 1.753, 2.0582, -8886.00570, -3.38, -47, 0.2, 1.437, 2.386, -695.87607, 0.59, 7, 0, 1.373, 3.026, -209.54947, 4.33, 51, -0.2, 1.262, 5.940, 16728.37052, 1.17, 28, -0.1, 1.224, 6.172, 6656.74859, -4.04, -41, 0.2, 1.187, 5.873, 6099.43431, -5.89, -63, 0.3, 1.177, 1.014, 31571.83518, 2.41, 56, -0.3, 1.162, 3.840, 9585.29534, 1.47, 25, -0.1, 1.143, 5.639, 8364.73984, -2.18, -19, 0.1, 1.078, 1.229, 70.98768, -1.88, -22, 0.1, 1.059, 3.326, 40528.82856, 3.91, 81, -0.4, 0.990, 5.013, 40738.37803, -0.42, 30, -0.2, 0.948, 5.687, -17772.01141, -6.75, -94, 0.5, 0.876, 0.298, -0.35232, 0, 0, 0, 0.822, 2.994, 393.02097, 0, 0, 0, 0.788, 1.836, 8326.39022, 3.05, 50, -0.2, 0.752, 4.985, 22614.84180, 0.91, 31, -0.2, 0.740, 2.875, 8330.99262, 0, 0, 0, 0.669, 0.744, -24357.77232, -4.60, -75, 0.4, 0.644, 1.314, 8393.12577, -2.18, -19, 0.1, 0.639, 5.888, 575.33849, 0, 0, 0, 0.635, 1.116, 23385.11911, -2.87, -13, 0, 0.584, 5.197, 24428.75999, 2.71, 53, -0.3, 0.583, 3.513, -9095.55517, 0.95, 4, 0, 0.572, 6.059, 29970.88002, -5.03, -32, 0.1, 0.565, 2.960, 0.32863, 1.52, 25, -0.1, 0.561, 4.001, -17981.56087, -2.43, -43, 0.2, 0.557, 0.529, 7143.07519, -0.30, 3, 0, 0.546, 2.311, 25614.37623, 4.54, 75, -0.4, 0.536, 4.229, 15752.30376, -4.99, -45, 0.2, 0.493, 3.316, -8294.9344, -1.83, -29, 0.1, 0.491, 1.744, 8362.4485, 1.21, 21, -0.1, 0.478, 1.803, -10071.6219, -5.20, -69, 0.3, 0.454, 0.857, 15333.2048, 3.66, 57, -0.3, 0.445, 2.071, 8311.7707, -2.18, -19, 0.1, 0.426, 0.345, 23452.6932, -3.44, -20, 0.1, 0.420, 4.941, 33733.8646, -2.56, -2, 0, 0.413, 1.642, 17495.2343, -1.31, -1, 0, 0.404, 1.458, 23314.1314, -0.99, 9, -0.1, 0.395, 2.132, 38299.5714, -3.51, -6, 0, 0.382, 2.700, 31781.3846, -1.92, 5, 0, 0.375, 4.827, 6376.2114, 2.17, 32, -0.2, 0.361, 3.867, 16833.1753, -0.97, 3, 0, 0.358, 5.044, 15056.4277, -4.40, -38, 0.2, 0.350, 5.157, -8257.7037, -3.40, -47, 0.2, 0.344, 4.233, 157.7344, 0, 0, 0, 0.340, 2.672, 13657.8484, -0.58, 6, 0, 0.329, 5.610, 41853.0066, 3.29, 74, -0.4, 0.325, 5.895, -39.8149, 0, 0, 0, 0.309, 4.387, 21500.2132, -2.79, -13, 0.1, 0.302, 1.278, 786.0419, 0, 0, 0, 0.302, 5.341, -24567.3218, -0.27, -24, 0.1, 0.301, 1.045, 5889.8848, -1.57, -12, 0, 0.294, 4.201, -2371.2325, -3.65, -44, 0.2, 0.293, 3.704, 21642.1886, -6.55, -57, 0.2, 0.290, 4.069, 32828.4391, 2.36, 56, -0.3, 0.289, 3.472, 31713.8105, -1.35, 12, -0.1, 0.285, 5.407, -33.7814, 0.31, 4, 0, 0.283, 5.998, -16.9207, -3.71, -44, 0.2, 0.283, 2.772, 38785.8980, 0.23, 37, -0.2, 0.274, 5.343, 15613.7420, -2.54, -16, 0.1, 0.263, 3.997, 25823.9257, 0.22, 24, -0.1, 0.254, 0.600, 24638.3095, -1.61, 2, 0, 0.253, 1.344, 6447.1991, 0.29, 10, -0.1, 0.250, 0.887, 141.9754, -3.76, -44, 0.2, 0.247, 0.317, 5329.1570, -2.10, -19, 0.1, 0.245, 0.141, 36.0484, -3.71, -44, 0.2, 0.231, 2.287, 14357.1381, -2.49, -16, 0.1, 0.227, 5.158, 2.6298, 0, 0, 0, 0.219, 5.085, 47742.8914, 1.72, 63, -0.3, 0.211, 2.145, 6638.7244, -2.18, -19, 0.1, 0.201, 4.415, 39623.7495, -4.13, -14, 0, 0.194, 2.091, 588.4927, 0, 0, 0, 0.193, 3.057, -15400.7789, -3.10, -50, 0, 0.186, 5.598, 16799.3582, -0.72, 6, 0, 0.185, 3.886, 1150.6770, 0, 0, 0, 0.183, 1.619, 7178.0144, 1.52, 25, 0, 0.181, 2.635, 8328.3391, 1.52, 25, 0, 0.181, 2.077, 8329.0437, 1.52, 25, 0, 0.179, 3.215, -9652.8694, -0.90, -18, 0, 0.176, 1.716, -8815.0180, -5.26, -69, 0, 0.175, 5.673, 550.7553, 0, 0, 0, 0.170, 2.060, 31295.0580, -5.6, -39, 0, 0.167, 1.239, 7211.7617, -0.7, 6, 0, 0.165, 4.499, 14967.4158, -0.7, 6, 0, 0.164, 3.595, 15540.4531, 0.9, 31, 0, 0.164, 4.237, 522.3694, 0, 0, 0, 0.163, 4.633, 15545.0555, -2.2, -19, 0, 0.161, 0.478, 6428.0209, -2.2, -19, 0, 0.158, 2.03, 13171.5218, -4.3, -38, 0, 0.157, 2.28, 7216.3641, -3.7, -44, 0, 0.154, 5.65, 7935.6705, 1.5, 25, 0, 0.152, 0.46, 29828.9047, -1.3, 12, 0, 0.151, 1.19, -0.7113, 0, 0, 0, 0.150, 1.42, 23942.4334, -1.0, 9, 0, 0.144, 2.75, 7753.3529, 1.5, 25, 0, 0.137, 2.08, 7213.7105, -2.2, -19, 0, 0.137, 1.44, 7214.4152, -2.2, -19, 0, 0.136, 4.46, -1185.6162, -1.8, -22, 0, 0.136, 3.03, 8000.1048, -2.2, -19, 0, 0.134, 2.83, 14756.7124, -0.7, 6, 0, 0.131, 5.05, 6821.0419, -2.2, -19, 0, 0.128, 5.99, -17214.6971, -4.9, -72, 0, 0.127, 5.35, 8721.7124, 1.5, 25, 0, 0.126, 4.49, 46628.2629, -2.0, 19, 0, 0.125, 5.94, 7149.6285, 1.5, 25, 0, 0.124, 1.09, 49067.0695, 1.1, 55, 0, 0.121, 2.88, 15471.7666, 1.2, 28, 0, 0.111, 3.92, 41643.4571, 7.6, 125, -1, 0.110, 1.96, 8904.0299, 1.5, 25, 0, 0.106, 3.30, -18.0489, -2.2, -19, 0, 0.105, 2.30, -4.9310, 1.5, 25, 0, 0.104, 2.22, -6.5590, -1.9, -22, 0, 0.101, 1.44, 1884.9059, -0.1, 0, 0, 0.100, 5.92, 5471.1324, -5.9, -63, 0, 0.099, 1.12, 15149.7333, -0.7, 6, 0, 0.096, 4.73, 15508.9972, -0.4, 10, 0, 0.095, 5.18, 7230.9835, 1.5, 25, 0, 0.093, 3.37, 39900.5266, 3.9, 81, 0, 0.092, 2.01, 25057.0619, 2.7, 53, 0, 0.092, 1.21, -79.6298, 0, 0, 0, 0.092, 1.65, -26310.2523, -4.0, -68, 0, 0.091, 1.01, 42062.5561, -1.0, 23, 0, 0.090, 6.10, 29342.5781, -5.0, -32, 0, 0.090, 4.43, 15542.4020, -0.7, 6, 0, 0.090, 3.80, 15543.1066, -0.7, 6, 0, 0.089, 4.15, 6063.3859, -2.2, -19, 0, 0.086, 4.03, 52.9691, 0, 0, 0, 0.085, 0.49, 47952.4409, -2.6, 11, 0, 0.085, 1.60, 7632.8154, 2.1, 32, 0, 0.084, 0.22, 14392.0773, -0.7, 6, 0, 0.083, 6.22, 6028.4466, -4.0, -41, 0, 0.083, 0.63, -7909.9389, 2.8, 26, 0, 0.083, 5.20, -77.5523, 0, 0, 0, 0.082, 2.74, 8786.1467, -2.2, -19, 0, 0.080, 2.43, 9166.5428, -2.8, -26, 0, 0.080, 3.70, -25405.1732, 4.1, 27, 0, 0.078, 5.68, 48857.5200, 5.4, 106, -1, 0.077, 1.85, 8315.5735, -2.2, -19, 0, 0.075, 5.46, -18191.1103, 1.9, 8, 0, 0.075, 1.41, -16238.6304, 1.3, 1, 0, 0.074, 5.06, 40110.0761, -0.4, 30, 0, 0.072, 2.10, 64.4343, -3.7, -44, 0, 0.071, 2.17, 37671.2695, -3.5, -6, 0, 0.069, 1.71, 16693.4313, -0.7, 6, 0, 0.069, 3.33, -26100.7028, -8.3, -119, 1, 0.068, 1.09, 8329.4028, 1.5, 25, 0, 0.068, 3.62, 8327.9801, 1.5, 25, 0, 0.068, 2.41, 16833.1509, -1.0, 3, 0, 0.067, 3.40, 24709.2971, -3.5, -20, 0, 0.067, 1.65, 8346.7156, -0.3, 3, 0, 0.066, 2.61, 22547.2677, 1.5, 39, 0, 0.066, 3.50, 15576.5113, -1.0, 3, 0, 0.065, 5.76, 33037.9886, -2.0, 5, 0, 0.065, 4.58, 8322.1325, -0.3, 3, 0, 0.065, 6.20, 17913.9868, 3.0, 50, 0, 0.065, 1.50, 22685.8295, -1.0, 9, 0, 0.065, 2.37, 7180.3058, -1.9, -15, 0, 0.064, 1.06, 30943.5332, 2.4, 56, 0, 0.064, 1.89, 8288.8765, 1.5, 25, 0, 0.064, 4.70, 6.0335, 0.3, 4, 0, 0.063, 2.83, 8368.5063, 1.5, 25, 0, 0.063, 5.66, -2580.7819, 0.7, 7, 0, 0.062, 3.78, 7056.3285, -2.2, -19, 0, 0.061, 1.49, 8294.9100, 1.8, 29, 0, 0.061, 0.12, -10281.1714, -0.9, -18, 0, 0.061, 3.06, -8362.4729, -1.2, -21, 0, 0.061, 4.43, 8170.9571, 1.5, 25, 0, 0.059, 5.78, -13.1179, -3.7, -44, 0, 0.059, 5.97, 6625.5702, -2.2, -19, 0, 0.058, 5.01, -0.5080, -0.3, 0, 0, 0.058, 2.73, 7161.0938, -2.2, -19, 0, 0.057, 0.19, 7214.0629, -2.2, -19, 0, 0.057, 4.00, 22199.5029, -4.7, -35, 0, 0.057, 5.38, 8119.1420, 5.8, 76, 0, 0.056, 1.07, 7542.6495, 1.5, 25, 0, 0.056, 0.28, 8486.4258, 1.5, 25, 0, 0.054, 4.19, 16655.0816, 4.6, 75, 0, 0.053, 0.72, 7267.0320, -2.2, -19, 0, 0.053, 3.12, 12.6192, 0.6, 7, 0, 0.052, 2.99, -32896.013, -1.8, -49, 0, 0.052, 3.46, 1097.708, 0, 0, 0, 0.051, 5.37, -6443.786, -1.6, -25, 0, 0.051, 1.35, 7789.401, -2.2, -19, 0, 0.051, 5.83, 40042.502, 0.2, 38, 0, 0.051, 3.63, 9114.733, 1.5, 25, 0, 0.050, 1.51, 8504.484, -2.5, -22, 0, 0.050, 5.23, 16659.684, 1.5, 25, 0, 0.050, 1.15, 7247.820, -2.5, -23, 0, 0.047, 0.25, -1290.421, 0.3, 0, 0, 0.047, 4.67, -32686.464, -6.1, -100, 0, 0.047, 3.49, 548.678, 0, 0, 0, 0.047, 2.37, 6663.308, -2.2, -19, 0, 0.046, 0.98, 1572.084, 0, 0, 0, 0.046, 2.04, 14954.262, -0.7, 6, 0, 0.046, 3.72, 6691.693, -2.2, -19, 0, 0.045, 6.19, -235.287, 0, 0, 0, 0.044, 2.96, 32967.001, -0.1, 27, 0, 0.044, 3.82, -1671.943, -5.6, -66, 0, 0.043, 5.82, 1179.063, 0, 0, 0, 0.043, 0.07, 34152.617, 1.7, 49, 0, 0.043, 3.71, 6514.773, -0.3, 0, 0, 0.043, 5.62, 15.732, -2.5, -23, 0, 0.043, 5.80, 8351.233, -2.2, -19, 0, 0.042, 0.27, 7740.199, 1.5, 25, 0, 0.042, 6.14, 15385.020, -0.7, 6, 0, 0.042, 6.13, 7285.051, -4.1, -41, 0, 0.041, 1.27, 32757.451, 4.2, 78, 0, 0.041, 4.46, 8275.722, 1.5, 25, 0, 0.040, 0.23, 8381.661, 1.5, 25, 0, 0.040, 5.87, -766.864, 2.5, 29, 0, 0.040, 1.66, 254.431, 0, 0, 0, 0.040, 0.40, 9027.981, -0.4, 0, 0, 0.040, 2.96, 7777.936, 1.5, 25, 0, 0.039, 4.67, 33943.068, 6.1, 100, 0, 0.039, 3.52, 8326.062, 1.5, 25, 0, 0.039, 3.75, 21013.887, -6.5, -57, 0, 0.039, 5.60, 606.978, 0, 0, 0, 0.039, 1.19, 8331.321, 1.5, 25, 0, 0.039, 2.84, 7211.433, -2.2, -19, 0, 0.038, 0.67, 7216.693, -2.2, -19, 0, 0.038, 6.22, 25161.867, 0.6, 28, 0, 0.038, 4.40, 7806.322, 1.5, 25, 0, 0.038, 4.16, 9179.168, -2.2, -19, 0, 0.037, 4.73, 14991.999, -0.7, 6, 0, 0.036, 0.35, 67.514, -0.6, -7, 0, 0.036, 3.70, 25266.611, -1.6, 0, 0, 0.036, 5.39, 16328.796, -0.7, 6, 0, 0.035, 1.44, 7174.248, -2.2, -19, 0, 0.035, 5.00, 15684.730, -4.4, -38, 0, 0.035, 0.39, -15.419, -2.2, -19, 0, 0.035, 6.07, 15020.385, -0.7, 6, 0, 0.034, 6.01, 7371.797, -2.2, -19, 0, 0.034, 0.96, -16623.626, -3.4, -54, 0, 0.033, 6.24, 9479.368, 1.5, 25, 0, 0.033, 3.21, 23661.896, 5.2, 82, 0, 0.033, 4.06, 8311.418, -2.2, -19, 0, 0.033, 2.40, 1965.105, 0, 0, 0, 0.033, 5.17, 15489.785, -0.7, 6, 0, 0.033, 5.03, 21986.540, 0.9, 31, 0, 0.033, 4.10, 16691.140, 2.7, 46, 0, 0.033, 5.13, 47114.589, 1.7, 63, 0, 0.033, 4.45, 8917.184, 1.5, 25, 0, 0.033, 4.23, 2.078, 0, 0, 0, 0.032, 2.33, 75.251, 1.5, 25, 0, 0.032, 2.10, 7253.878, -2.2, -19, 0, 0.032, 3.11, -0.224, 1.5, 25, 0, 0.032, 4.43, 16640.462, -0.7, 6, 0, 0.032, 5.68, 8328.363, 0, 0, 0, 0.031, 5.32, 8329.020, 3.0, 50, 0, 0.031, 3.70, 16118.093, -0.7, 6, 0, 0.030, 3.67, 16721.817, -0.7, 6, 0, 0.030, 5.27, -1881.492, -1.2, -15, 0, 0.030, 5.72, 8157.839, -2.2, -19, 0, 0.029, 5.73, -18400.313, -6.7, -94, 0, 0.029, 2.76, 16.000, -2.2, -19, 0, 0.029, 1.75, 8879.447, 1.5, 25, 0, 0.029, 0.32, 8851.061, 1.5, 25, 0, 0.029, 0.90, 14704.903, 3.7, 57, 0, 0.028, 2.90, 15595.723, -0.7, 6, 0, 0.028, 5.88, 16864.631, 0.2, 24, 0, 0.028, 0.63, 16869.234, -2.8, -26, 0, 0.028, 4.04, -18609.863, -2.4, -43, 0, 0.027, 5.83, 6727.736, -5.9, -63, 0, 0.027, 6.12, 418.752, 4.3, 51, 0, 0.027, 0.14, 41157.131, 3.9, 81, 0, 0.026, 3.80, 15.542, 0, 0, 0, 0.026, 1.68, 50181.698, 4.8, 99, -1, 0.026, 0.32, 315.469, 0, 0, 0, 0.025, 5.67, 19.188, 0.3, 0, 0, 0.025, 3.16, 62.133, -2.2, -19, 0, 0.025, 3.76, 15502.939, -0.7, 6, 0, 0.025, 4.53, 45999.961, -2.0, 19, 0, 0.024, 3.21, 837.851, -4.4, -51, 0, 0.024, 2.82, 38157.596, 0.3, 37, 0, 0.024, 5.21, 15540.124, -0.7, 6, 0, 0.024, 0.26, 14218.576, 0, 13, 0, 0.024, 3.01, 15545.384, -0.7, 6, 0, 0.024, 1.16, -17424.247, -0.6, -21, 0, 0.023, 2.34, -67.574, 0.6, 7, 0, 0.023, 2.44, 18.024, -1.9, -22, 0, 0.023, 3.70, 469.400, 0, 0, 0, 0.023, 0.72, 7136.511, -2.2, -19, 0, 0.023, 4.50, 15582.569, -0.7, 6, 0, 0.023, 2.80, -16586.395, -4.9, -72, 0, 0.023, 1.51, 80.182, 0, 0, 0, 0.023, 1.09, 5261.583, -1.5, -12, 0, 0.023, 0.56, 54956.954, -0.5, 44, 0, 0.023, 4.01, 8550.860, -2.2, -19, 0, 0.023, 4.46, 38995.448, -4.1, -14, 0, 0.023, 3.82, 2358.126, 0, 0, 0, 0.022, 3.77, 32271.125, 0.5, 34, 0, 0.022, 0.82, 15935.775, -0.7, 6, 0, 0.022, 1.07, 24013.421, -2.9, -13, 0, 0.022, 0.40, 8940.078, -2.2, -19, 0, 0.022, 2.06, 15700.489, -0.7, 6, 0, 0.022, 4.27, 15124.002, -5.0, -45, 0, 0.021, 1.16, 56071.583, 3.2, 88, 0, 0.021, 5.58, 9572.189, -2.2, -19, 0, 0.020, 1.70, -17.273, -3.7, -44, 0, 0.020, 3.05, 214.617, 0, 0, 0, 0.020, 4.41, 8391.048, -2.2, -19, 0, 0.020, 5.95, 23869.145, 2.4, 56, 0, 0.020, 0.42, 40947.927, -4.7, -21, 0, 0.019, 1.39, 5818.897, 0.3, 10, 0, 0.019, 0.71, 23873.747, -0.7, 6, 0, 0.019, 2.81, 7291.615, -2.2, -19, 0, 0.019, 5.09, 8428.018, -2.2, -19, 0, 0.019, 4.14, 6518.187, -1.6, -12, 0, 0.019, 3.85, 21.330, 0, 0, 0, 0.018, 0.66, 14445.046, -0.7, 6, 0, 0.018, 1.65, 0.966, -4.0, -48, 0, 0.018, 5.64, -17143.709, -6.8, -94, 0, 0.018, 6.01, 7736.432, -2.2, -19, 0, 0.018, 2.74, 31153.083, -1.9, 5, 0, 0.018, 4.58, 6116.355, -2.2, -19, 0, 0.018, 2.28, 46.401, 0.3, 0, 0, 0.018, 3.80, 10213.597, 1.4, 25, 0, 0.018, 2.84, 56281.132, -1.1, 36, 0, 0.018, 3.53, 8249.062, 1.5, 25, 0, 0.017, 4.43, 20871.911, -3, -13, 0, 0.017, 4.44, 627.596, 0, 0, 0, 0.017, 1.85, 628.308, 0, 0, 0, 0.017, 1.19, 8408.321, 2, 25, 0, 0.017, 1.95, 7214.056, -2, -19, 0, 0.017, 1.57, 7214.070, -2, -19, 0, 0.017, 1.65, 13870.811, -6, -60, 0, 0.017, 0.30, 22.542, -4, -44, 0, 0.017, 2.62, -119.445, 0, 0, 0, 0.016, 4.87, 5747.909, 2, 32, 0, 0.016, 4.45, 14339.108, -1, 6, 0, 0.016, 1.83, 41366.680, 0, 30, 0, 0.016, 4.53, 16309.618, -3, -23, 0, 0.016, 2.54, 15542.754, -1, 6, 0, 0.016, 6.05, 1203.646, 0, 0, 0, 0.015, 5.2, 2751.147, 0, 0, 0, 0.015, 1.8, -10699.924, -5, -69, 0, 0.015, 0.4, 22824.391, -3, -20, 0, 0.015, 2.1, 30666.756, -6, -39, 0, 0.015, 2.1, 6010.417, -2, -19, 0, 0.015, 0.7, -23729.470, -5, -75, 0, 0.015, 1.4, 14363.691, -1, 6, 0, 0.015, 5.8, 16900.689, -2, 0, 0, 0.015, 5.2, 23800.458, 3, 53, 0, 0.015, 5.3, 6035.000, -2, -19, 0, 0.015, 1.2, 8251.139, 2, 25, 0, 0.015, 3.6, -8.860, 0, 0, 0, 0.015, 0.8, 882.739, 0, 0, 0, 0.015, 3.0, 1021.329, 0, 0, 0, 0.015, 0.6, 23296.107, 1, 31, 0, 0.014, 5.4, 7227.181, 2, 25, 0, 0.014, 0.1, 7213.352, -2, -19, 0, 0.014, 4.0, 15506.706, 3, 50, 0, 0.014, 3.4, 7214.774, -2, -19, 0, 0.014, 4.6, 6665.385, -2, -19, 0, 0.014, 0.1, -8.636, -2, -22, 0, 0.014, 3.1, 15465.202, -1, 6, 0, 0.014, 4.9, 508.863, 0, 0, 0, 0.014, 3.5, 8406.244, 2, 25, 0, 0.014, 1.3, 13313.497, -8, -82, 0, 0.014, 2.8, 49276.619, -3, 0, 0, 0.014, 0.1, 30528.194, -3, -10, 0, 0.013, 1.7, 25128.050, 1, 31, 0, 0.013, 2.9, 14128.405, -1, 6, 0, 0.013, 3.4, 57395.761, 3, 80, 0, 0.013, 2.7, 13029.546, -1, 6, 0, 0.013, 3.9, 7802.556, -2, -19, 0, 0.013, 1.6, 8258.802, -2, -19, 0, 0.013, 2.2, 8417.709, -2, -19, 0, 0.013, 0.7, 9965.210, -2, -19, 0, 0.013, 3.4, 50391.247, 0, 48, 0, 0.013, 3.0, 7134.433, -2, -19, 0, 0.013, 2.9, 30599.182, -5, -31, 0, 0.013, 3.6, -9723.857, 1, 0, 0, 0.013, 4.8, 7607.084, -2, -19, 0, 0.012, 0.8, 23837.689, 1, 35, 0, 0.012, 3.6, 4.409, -4, -44, 0, 0.012, 5.0, 16657.031, 3, 50, 0, 0.012, 4.4, 16657.735, 3, 50, 0, 0.012, 1.1, 15578.803, -4, -38, 0, 0.012, 6.0, -11.490, 0, 0, 0, 0.012, 1.9, 8164.398, 0, 0, 0, 0.012, 2.4, 31852.372, -4, -17, 0, 0.012, 2.4, 6607.085, -2, -19, 0, 0.012, 4.2, 8359.870, 0, 0, 0, 0.012, 0.5, 5799.713, -2, -19, 0, 0.012, 2.7, 7220.622, 0, 0, 0, 0.012, 4.3, -139.720, 0, 0, 0, 0.012, 2.3, 13728.836, -2, -16, 0, 0.011, 3.6, 14912.146, 1, 31, 0, 0.011, 4.7, 14916.748, -2, -19, 0}, + //ML1 + { + 1.67680, 4.66926, 628.301955, -0.0266, 0.1, -0.005, 0.51642, 3.3721, 6585.760910, -2.158, -18.9, 0.09, 0.41383, 5.7277, 14914.452335, -0.635, 6.2, -0.04, 0.37115, 3.9695, 7700.389469, 1.550, 25.0, -0.12, 0.27560, 0.7416, 8956.993380, 1.496, 25.1, -0.13, 0.24599, 4.2253, -2.301200, 1.523, 25.1, -0.12, 0.07118, 0.1443, 7842.36482, -2.211, -19, 0.08, 0.06128, 2.4998, 16171.05625, -0.688, 6, 0, 0.04516, 0.443, 8399.67910, -0.36, 3, 0, 0.04048, 5.771, 14286.15038, -0.61, 6, 0, 0.03747, 4.626, 1256.60391, -0.05, 0, 0, 0.03707, 3.415, 5957.45895, -2.13, -19, 0.1, 0.03649, 1.800, 23243.14376, 0.89, 31, -0.2, 0.02438, 0.042, 16029.08089, 3.07, 50, -0.2, 0.02165, 1.017, -1742.93051, -3.68, -44, 0.2, 0.01923, 3.097, 17285.68480, 3.02, 50, -0.3, 0.01692, 1.280, 0.3286, 1.52, 25, -0.1, 0.01361, 0.298, 8326.3902, 3.05, 50, -0.2, 0.01293, 4.013, 7072.0875, 1.58, 25, -0.1, 0.01276, 4.413, 8330.9926, 0, 0, 0, 0.01270, 0.101, 8470.6668, -2.24, -19, 0.1, 0.01097, 1.203, 22128.5152, -2.82, -13, 0, 0.01088, 2.545, 15542.7543, -0.66, 6, 0, 0.00835, 0.190, 7214.0629, -2.18, -19, 0.1, 0.00734, 4.855, 24499.7477, 0.83, 31, -0.2, 0.00686, 5.130, 13799.8238, -4.34, -38, 0.2, 0.00631, 0.930, -486.3266, -3.73, -44, 0, 0.00585, 0.699, 9585.2953, 1.5, 25, 0, 0.00566, 4.073, 8328.3391, 1.5, 25, 0, 0.00566, 0.638, 8329.0437, 1.5, 25, 0, 0.00539, 2.472, -1952.4800, 0.6, 7, 0, 0.00509, 2.88, -0.7113, 0, 0, 0, 0.00469, 3.56, 30457.2066, -1.3, 12, 0, 0.00387, 0.78, -0.3523, 0, 0, 0, 0.00378, 1.84, 22614.8418, 0.9, 31, 0, 0.00362, 5.53, -695.8761, 0.6, 7, 0, 0.00317, 2.80, 16728.3705, 1.2, 28, 0, 0.00303, 6.07, 157.7344, 0, 0, 0, 0.00300, 2.53, 33.7570, -0.3, -4, 0, 0.00295, 4.16, 31571.8352, 2.4, 56, 0, 0.00289, 5.98, 7211.7617, -0.7, 6, 0, 0.00285, 2.06, 15540.4531, 0.9, 31, 0, 0.00283, 2.65, 2.6298, 0, 0, 0, 0.00282, 6.17, 15545.0555, -2.2, -19, 0, 0.00278, 1.23, -39.8149, 0, 0, 0, 0.00272, 3.82, 7216.3641, -3.7, -44, 0, 0.00270, 4.37, 70.9877, -1.9, -22, 0, 0.00256, 5.81, 13657.8484, -0.6, 6, 0, 0.00244, 5.64, -0.2237, 1.5, 25, 0, 0.00240, 2.96, 8311.7707, -2.2, -19, 0, 0.00239, 0.87, -33.7814, 0.3, 4, 0, 0.00216, 2.31, 15.9995, -2.2, -19, 0, 0.00186, 3.46, 5329.1570, -2.1, -19, 0, 0.00169, 2.40, 24357.772, 4.6, 75, 0, 0.00161, 5.80, 8329.403, 1.5, 25, 0, 0.00161, 5.20, 8327.980, 1.5, 25, 0, 0.00160, 4.26, 23385.119, -2.9, -13, 0, 0.00156, 1.26, 550.755, 0, 0, 0, 0.00155, 1.25, 21500.213, -2.8, -13, 0, 0.00152, 0.60, -16.921, -3.7, -44, 0, 0.00150, 2.71, -79.630, 0, 0, 0, 0.00150, 5.29, 15.542, 0, 0, 0, 0.00148, 1.06, -2371.232, -3.7, -44, 0, 0.00141, 0.77, 8328.691, 1.5, 25, 0, 0.00141, 3.67, 7143.075, -0.3, 0, 0, 0.00138, 5.45, 25614.376, 4.5, 75, 0, 0.00129, 4.90, 23871.446, 0.9, 31, 0, 0.00126, 4.03, 141.975, -3.8, -44, 0, 0.00124, 6.01, 522.369, 0, 0, 0, 0.00120, 4.94, -10071.622, -5.2, -69, 0, 0.00118, 5.07, -15.419, -2.2, -19, 0, 0.00107, 3.49, 23452.693, -3.4, -20, 0, 0.00104, 4.78, 17495.234, -1.3, 0, 0, 0.00103, 1.44, -18.049, -2.2, -19, 0, 0.00102, 5.63, 15542.402, -0.7, 6, 0, 0.00102, 2.59, 15543.107, -0.7, 6, 0, 0.00100, 4.11, -6.559, -1.9, -22, 0, 0.00097, 0.08, 15400.779, 3.1, 50, 0, 0.00096, 5.84, 31781.385, -1.9, 5, 0, 0.00094, 1.08, 8328.363, 0, 0, 0, 0.00094, 2.46, 16799.358, -0.7, 6, 0, 0.00094, 1.69, 6376.211, 2.2, 32, 0, 0.00093, 3.64, 8329.020, 3.0, 50, 0, 0.00093, 2.65, 16655.082, 4.6, 75, 0, 0.00090, 1.90, 15056.428, -4.4, -38, 0, 0.00089, 1.59, 52.969, 0, 0, 0, 0.00088, 2.02, -8257.704, -3.4, -47, 0, 0.00088, 3.02, 7213.711, -2.2, -19, 0, 0.00087, 0.50, 7214.415, -2.2, -19, 0, 0.00087, 0.49, 16659.684, 1.5, 25, 0, 0.00082, 5.64, -4.931, 1.5, 25, 0, 0.00079, 5.17, 13171.522, -4.3, -38, 0, 0.00076, 3.60, 29828.905, -1.3, 12, 0, 0.00076, 4.08, 24567.322, 0.3, 24, 0, 0.00076, 4.58, 1884.906, -0.1, 0, 0, 0.00073, 0.33, 31713.811, -1.4, 12, 0, 0.00073, 0.93, 32828.439, 2.4, 56, 0, 0.00071, 5.91, 38785.898, 0.2, 37, 0, 0.00069, 2.20, 15613.742, -2.5, -16, 0, 0.00066, 3.87, 15.732, -2.5, -23, 0, 0.00066, 0.86, 25823.926, 0.2, 24, 0, 0.00065, 2.52, 8170.957, 1.5, 25, 0, 0.00063, 0.18, 8322.132, -0.3, 0, 0, 0.00060, 5.84, 8326.062, 1.5, 25, 0, 0.00060, 5.15, 8331.321, 1.5, 25, 0, 0.00060, 2.18, 8486.426, 1.5, 25, 0, 0.00058, 2.30, -1.731, -4, -44, 0, 0.00058, 5.43, 14357.138, -2, -16, 0, 0.00057, 3.09, 8294.910, 2, 29, 0, 0.00057, 4.67, -8362.473, -1, -21, 0, 0.00056, 4.15, 16833.151, -1, 0, 0, 0.00054, 1.93, 7056.329, -2, -19, 0, 0.00054, 5.27, 8315.574, -2, -19, 0, 0.00052, 5.6, 8311.418, -2, -19, 0, 0.00052, 2.7, -77.552, 0, 0, 0, 0.00051, 4.3, 7230.984, 2, 25, 0, 0.00050, 0.4, -0.508, 0, 0, 0, 0.00049, 5.4, 7211.433, -2, -19, 0, 0.00049, 4.4, 7216.693, -2, -19, 0, 0.00049, 4.3, 16864.631, 0, 24, 0, 0.00049, 2.2, 16869.234, -3, -26, 0, 0.00047, 6.1, 627.596, 0, 0, 0, 0.00047, 5.0, 12.619, 1, 7, 0, 0.00045, 4.9, -8815.018, -5, -69, 0, 0.00044, 1.6, 62.133, -2, -19, 0, 0.00042, 2.9, -13.118, -4, -44, 0, 0.00042, 4.1, -119.445, 0, 0, 0, 0.00041, 4.3, 22756.817, -3, -13, 0, 0.00041, 3.6, 8288.877, 2, 25, 0, 0.00040, 0.5, 6663.308, -2, -19, 0, 0.00040, 1.1, 8368.506, 2, 25, 0, 0.00039, 4.1, 6443.786, 2, 25, 0, 0.00039, 3.1, 16657.383, 3, 50, 0, 0.00038, 0.1, 16657.031, 3, 50, 0, 0.00038, 3.0, 16657.735, 3, 50, 0, 0.00038, 4.6, 23942.433, -1, 9, 0, 0.00037, 4.3, 15385.020, -1, 6, 0, 0.00037, 5.0, 548.678, 0, 0, 0, 0.00036, 1.8, 7213.352, -2, -19, 0, 0.00036, 1.7, 7214.774, -2, -19, 0, 0.00035, 1.1, 7777.936, 2, 25, 0, 0.00035, 1.6, -8.860, 0, 0, 0, 0.00035, 4.4, 23869.145, 2, 56, 0, 0.00035, 2.0, 6691.693, -2, -19, 0, 0.00034, 1.3, -1185.616, -2, -22, 0, 0.00034, 2.2, 23873.747, -1, 6, 0, 0.00033, 2.0, -235.287, 0, 0, 0, 0.00033, 3.1, 17913.987, 3, 50, 0, 0.00033, 1.0, 8351.233, -2, -19, 0}, + //ML2 + {0.004870, 4.6693, 628.30196, -0.027, 0, -0.01, 0.002280, 2.6746, -2.30120, 1.523, 25, -0.12, 0.001500, 3.372, 6585.76091, -2.16, -19, 0.1, 0.001200, 5.728, 14914.45233, -0.64, 6, 0, 0.001080, 3.969, 7700.38947, 1.55, 25, -0.1, 0.000800, 0.742, 8956.99338, 1.50, 25, -0.1, 0.000254, 6.002, 0.3286, 1.52, 25, -0.1, 0.000210, 0.144, 7842.3648, -2.21, -19, 0, 0.000180, 2.500, 16171.0562, -0.7, 6, 0, 0.000130, 0.44, 8399.6791, -0.4, 3, 0, 0.000126, 5.03, 8326.3902, 3.0, 50, 0, 0.000120, 5.77, 14286.1504, -0.6, 6, 0, 0.000118, 5.96, 8330.9926, 0, 0, 0, 0.000110, 1.80, 23243.1438, 0.9, 31, 0, 0.000110, 3.42, 5957.4590, -2.1, -19, 0, 0.000110, 4.63, 1256.6039, -0.1, 0, 0, 0.000099, 4.70, -0.7113, 0, 0, 0, 0.000070, 0.04, 16029.0809, 3.1, 50, 0, 0.000070, 5.14, 8328.3391, 1.5, 25, 0, 0.000070, 5.85, 8329.0437, 1.5, 25, 0, 0.000060, 1.02, -1742.9305, -3.7, -44, 0, 0.000060, 3.10, 17285.6848, 3.0, 50, 0, 0.000054, 5.69, -0.352, 0, 0, 0, 0.000043, 0.52, 15.542, 0, 0, 0, 0.000041, 2.03, 2.630, 0, 0, 0, 0.000040, 0.10, 8470.667, -2.2, -19, 0, 0.000040, 4.01, 7072.088, 1.6, 25, 0, 0.000036, 2.93, -8.860, -0.3, 0, 0, 0.000030, 1.20, 22128.515, -2.8, -13, 0, 0.000030, 2.54, 15542.754, -0.7, 6, 0, 0.000027, 4.43, 7211.762, -0.7, 6, 0, 0.000026, 0.51, 15540.453, 0.9, 31, 0, 0.000026, 1.44, 15545.055, -2.2, -19, 0, 0.000025, 5.37, 7216.364, -3.7, -44, 0}, + //ML3 + {0.00001200, 1.041, -2.3012, 1.52, 25, -0.1, 0.00000170, 0.31, -0.711, 0, 0, 0}}, + + { //精度1角秒 + //MB0 + {18461.240, 0.05710892, 8433.466157492, -0.6400617, -0.5345, -0.00294, 1010.167, 2.412663, 16762.15758211, 0.88286, 24.537, -0.1265, 999.694, 5.440038, -104.77473287, 2.16299, 25.606, -0.1207, 623.652, 0.915047, 7109.28813249, -0.02177, 6.746, -0.0379, 199.484, 1.815303, 15647.5290228, -2.82482, -19.39, 0.0799, 166.574, 4.842677, -1219.4032921, -1.5447, -18.33, 0.086, 117.261, 4.17086, 23976.2204475, -1.3019, 5.68, -0.044, 61.912, 4.76822, 25090.8490067, 2.4058, 49.61, -0.250, 33.357, 3.27060, 15437.979557, 1.5012, 31.8, -0.161, 31.760, 1.51241, 8223.916692, 3.6859, 50.7, -0.244, 29.577, 0.95817, 6480.986177, 0.0049, 6.7, -0.032, 15.566, 2.4871, -9548.094717, -3.068, -43.4, 0.21, 15.122, 0.2432, 32304.911872, 0.221, 30.7, -0.17, 12.094, 4.0135, 7737.590088, -0.048, 6.8, -0.04, 8.868, 1.8584, 15019.227068, -2.798, -19.5, 0.09, 8.045, 5.3812, 8399.709110, -0.332, 3.1, -0.02, 7.959, 4.2140, 23347.918492, -1.275, 5.6, -0.04, 7.435, 4.8858, -1847.705247, -1.518, -18.4, 0.09, 6.731, 3.8274, -16133.855627, -0.910, -24.5, 0.12, 6.580, 2.6732, 14323.350998, -2.207, -12.1, 0.04, 6.460, 3.1556, 9061.768113, -0.667, -0.5, -0.01, 6.296, 0.1713, 25300.398472, -1.920, -1.6, -0.01, 5.632, 0.8000, 733.076688, -2.190, -26, 0.12, 5.368, 2.1140, 16204.843302, -0.971, 3, -0.02, 5.311, 5.5111, 17390.459537, 0.856, 25, -0.13, 5.076, 2.2553, 523.52722, 2.136, 26, -0.13, 4.840, 6.1830, -7805.16420, 0.613, 1, 0, 4.806, 5.1414, -662.08901, 0.309, 4, -0.02, 3.984, 0.8406, 33419.54043, 3.929, 75, -0.37, 3.674, 5.0288, 22652.04242, -0.684, 13, -0.08, 2.998, 5.9291, 31190.28331, -3.487, -13, 0.04, 2.799, 2.1842, -16971.70705, 3.443, 27, -0.11, 2.414, 3.5735, 22861.59189, -5.010, -38, 0.16, 2.186, 3.9424, -9757.64418, 1.258, 8, -0.03, 2.146, 5.6262, 23766.67098, 3.024, 57, -0.29, 1.766, 3.3137, 14809.67760, 1.528, 32, -0.2, 1.624, 2.6013, 7318.83760, -4.35, -44, 0.2, 1.581, 3.8680, 16552.60812, 5.21, 76, -0.4, 1.520, 2.599, 40633.60330, 1.74, 56, -0.3, 1.516, 0.132, -17876.78614, -4.59, -68, 0.3, 1.510, 3.927, 8399.68473, -0.33, 3, 0, 1.318, 4.914, 16275.83098, -2.85, -19, 0.1, 1.264, 0.986, 24604.52240, -1.33, 6, 0, 1.192, 2.001, 39518.97474, -1.96, 12, -0.1, 1.135, 0.286, 31676.60992, 0.25, 31, -0.2, 1.086, 1.001, 5852.68422, 0.03, 7, 0, 1.019, 2.527, 33629.08990, -0.40, 23, -0.1, 0.823, 0.086, 16066.28151, 1.47, 32, -0.2, 0.804, 1.957, -33.78706, 0.28, 4, 0, 0.803, 5.212, 16833.14526, -1.00, 3, 0, 0.793, 1.472, -24462.54705, -2.43, -50, 0.2, 0.791, 1.658, -591.10134, -1.57, -18, 0.1, 0.667, 4.470, 24533.53473, 0.55, 28, -0.1, 0.650, 2.530, -10176.39667, -3.04, -43, 0.2, 0.639, 1.583, 25719.15096, 2.38, 50, -0.3, 0.634, 0.318, 5994.65957, -3.73, -37, 0.2, 0.631, 2.147, 8435.76736, -2.16, -26, 0.1, 0.630, 1.109, 8431.16496, 0.88, 25, -0.1, 0.596, 2.716, 13695.04904, -2.18, -12, 0.1, 0.589, 1.214, 7666.60241, 1.83, 29, -0.1, 0.473, 1.101, 30980.7338, 0.84, 38, -0.2, 0.456, 0.116, -71.0177, 1.85, 22, -0.1, 0.430, 2.786, -8990.7804, -1.21, -21, 0.1, 0.416, 1.454, 16728.4005, 1.19, 28, -0.1, 0.415, 5.072, 22023.7405, -0.66, 13, -0.1, 0.383, 4.257, 22719.6165, -1.25, 6, 0, 0.352, 2.972, 14880.6653, -0.35, 10, -0.1, 0.339, 5.972, 30561.9814, -3.46, -13, 0, 0.329, 1.587, -18086.3356, -0.26, -17, 0.1, 0.326, 1.016, 8467.2232, -0.95, -4, 0, 0.315, 1.902, 14390.9251, -2.77, -20, 0.1, 0.313, 4.611, 8852.2186, 3.66, 51, -0.2, 0.305, 0.616, 6551.9739, -1.88, -15, 0.1, 0.301, 4.728, -7595.6147, -3.71, -51, 0.2, 0.299, 1.874, 7143.0452, -0.33, 3, 0, 0.291, 3.156, -1428.9528, 2.78, 33, -0.2, 0.269, 4.929, -2476.0072, -1.49, -18, 0.1, 0.263, 3.196, 41748.2319, 5.45, 100, -0.5, 0.254, 3.387, -1009.8538, -5.87, -70, 0.3, 0.245, 1.930, 32514.4613, -4.10, -20, 0.1, 0.237, 3.342, 32933.2138, 0.19, 31, -0.2, 0.214, 3.617, 22233.2899, -4.98, -38, 0.2, 0.213, 4.357, 47847.6662, -0.44, 37, -0.2, 0.206, 3.872, 23418.9062, -3.16, -16, 0.1, 0.172, 5.772, 14951.6530, -2.2, -12, 0, 0.158, 2.04, 38890.6728, -1.9, 12, 0, 0.146, 1.70, 32095.3624, 4.5, 82, 0, 0.145, 4.29, 40843.1528, -2.6, 5, 0, 0.139, 2.90, 7876.1519, -2.5, -23, 0, 0.138, 4.95, 48962.2947, 3.3, 81, 0, 0.134, 3.97, 8365.8920, -0.1, 7, 0, 0.134, 4.06, -26205.4776, -6.1, -94, 0, 0.130, 1.40, -8643.0156, 5.0, 52, 0, 0.129, 5.67, 23138.3690, 3.1, 57, 0, 0.124, 2.64, 40005.3013, 1.8, 56, 0, 0.118, 4.88, 41957.7813, 1.1, 49, 0, 0.113, 3.78, -15505.5537, -0.9, -24, 0, 0.113, 4.87, 16904.1329, -2.9, -19, 0, 0.113, 1.84, 23280.3444, -0.7, 13, 0, 0.110, 0.43, -17319.4719, -2.7, -47, 0, 0.105, 1.61, 37.2006, -1.6, -18, 0, 0.102, 1.28, 25161.8367, 0.5, 28, 0, 0.095, 0.76, 1361.3786, -2.2, -25, 0, 0.094, 0.50, 29866.1053, -2.9, -6, 0, 0.092, 6.22, 24881.2995, 6.7, 101, 0, 0.088, 3.99, -10385.9461, 1.3, 8, 0, 0.085, 4.71, 70.9933, -1.9, -22, 0, 0.084, 0.86, 15613.7720, -2.5, -16, 0, 0.081, 4.43, 21537.4139, -4.4, -31, 0, 0.080, 1.86, -8365.9521, 0, -7, 0, 0.080, 0, 16728.3762, 1.2, 28, 0, 0.079, 2.44, -8919.7928, -3.1, -43, 0, 0.078, 3.69, -452.5395, -4.0, -48, 0, 0.075, 5.40, -32791.2385, -4.0, -75, 0, 0.073, 5.80, -1185.6462, -1.9, -22, 0, 0.070, 3.46, 16759.8564, 2.4, 50, 0, 0.069, 3.36, 14181.3756, 1.6, 32, 0, 0.068, 4.50, 16764.4588, -0.6, -1, 0, 0.067, 4.74, 8446.0854, 0, 7, 0, 0.066, 5.86, 24185.7699, -5.6, -46, 0, 0.064, 0.54, 32862.2262, 2.1, 53, 0, 0.063, 2.44, 24394.9729, 3.0, 57, 0, 0.063, 1.77, 5785.1101, 0.6, 14, 0, 0.062, 2.64, 6690.5356, -4.3, -45, 0, 0.062, 2.21, 1151.8292, 2.1, 26, 0, 0.062, 3.94, 34047.8424, 3.9, 75, 0, 0.060, 1.40, 38404.3462, -5.7, -32, 0, 0.058, 0.33, 31048.3080, 0.3, 31, 0, 0.057, 3.11, 9690.0701, -0.7, 0, 0, 0.057, 1.14, 30352.4319, 0.9, 38, 0, 0.056, 6.00, 8504.4538, -2.5, -22, 0, 0.055, 5.47, 18018.7615, 0.8, 25, 0, 0.055, 0.17, -18505.0881, -4.6, -69, 0, 0.055, 0.76, -9129.3422, 1.2, 8, 0, 0.054, 5.70, 7947.1396, -4.4, -44, 0, 0.053, 0.36, 5366.358, -3.7, -37, 0, 0.052, 4.01, -68.726, -1.5, -18, 0, 0.051, 2.74, 31818.585, -3.5, -13, 0, 0.051, 0.98, 16798.206, -2.8, -19, 0, 0.050, 5.94, 8293.747, -0.3, 0, 0, 0.049, 1.52, 15090.215, -4.7, -41, 0, 0.048, 3.46, 39309.425, 2.4, 63, 0, 0.046, 3.21, 23942.463, -1.0, 9, 0, 0.046, 3.35, 7143.070, -0.3, 0, 0, 0.042, 3.76, 46733.038, -4.1, -7, 0, 0.042, 5.18, 8288.351, 0, 7, 0, 0.040, 3.37, 16795.915, 0.6, 21, 0, 0.039, 4.54, -1776.718, -3.4, -40, 0, 0.039, 0.04, 8439.500, -0.3, 0, 0, 0.037, 3.91, 8479.867, -0.3, 0, 0, 0.037, 2.86, 38194.797, -1.3, 19, 0, 0.036, 1.04, 5224.382, 0.1, 7, 0, 0.036, 3.57, 15995.294, 3.4, 54, 0, 0.036, 5.33, 23209.357, 1.2, 35, 0, 0.035, 1.02, 8452.654, -0.3, 0, 0, 0.035, 4.31, 8294.904, 1.8, 29, 0, 0.035, 2.76, 13066.747, -2.2, -12, 0, 0.034, 6.07, 15508.967, -0.4, 10, 0, 0.032, 1.89, -17529.021, 1.6, 0, 0, 0.031, 5.70, 41261.905, 1.7, 56, 0, 0.031, 5.33, 30075.655, -7.2, -57, 0, 0.031, 5.97, -40.340, -1.5, -18, 0, 0.030, 2.87, 6533.950, 0, 7, 0, 0.030, 0.36, 49171.844, -1.1, 30, 0, 0.030, 4.40, 47219.364, -0.4, 37, 0, 0.030, 0.39, 23489.894, -5.0, -38, 0, 0.029, 5.12, 21395.439, -0.6, 13, 0, 0.029, 2.62, 8715.153, -0.3, 0, 0, 0.029, 2.94, 16826.592, -2.8, -19, 0, 0.028, 6.23, 31747.598, -1.6, 9, 0, 0.028, 0.43, 56176.358, 1.1, 62, 0, 0.027, 2.32, 8792.706, -0.3, 0, 0, 0.026, 3.02, 14252.363, -0.3, 10, 0, 0.025, 5.10, 40147.277, -2.0, 12, 0, 0.025, 4.59, 8433.795, 0.9, 25, 0, 0.025, 4.95, 8433.138, -2.2, -26, 0, 0.025, 1.03, -9338.545, -7.4, -95, 0, 0.024, 0.68, 17180.910, 5.2, 76, 0, 0.024, 3.81, 25057.092, 2.7, 53, 0, 0.024, 6.02, 29933.679, -3.4, -13, 0, 0.024, 2.37, -15924.306, -5.2, -76, 0, 0.024, 3.46, 8681.372, 0, 7, 0, 0.023, 2.80, 7108.936, 0, 7, 0, 0.023, 2.17, 7109.640, 0, 7, 0, 0.023, 2.57, -10804.699, -3.0, -44, 0, 0.022, 1.21, 6323.246, 0, 7, 0, 0.022, 3.22, 8259.965, 0, 7, 0, 0.022, 1.97, 7106.987, 1.5, 32, 0, 0.022, 3.00, 7111.589, -1.5, -18, 0, 0.022, 1.22, 14532.900, -6.5, -63, 0, 0.021, 0.66, 5923.672, -1.8, -15, 0, 0.021, 0.69, 24047.208, -3.2, -16, 0, 0.020, 5.51, -26415.027, -1.8, -42, 0, 0.020, 3.70, 16745.237, -2.8, -19, 0, 0.020, 1.26, 7038.300, 1.9, 29, 0, 0.020, 5.78, 6716.267, 0, 7, 0, 0.020, 3.76, 7895.330, 0, 7, 0, 0.019, 0.44, -121.695, -1.5, -18, 0, 0.018, 2.16, 15576.541, -0.9, 0, 0, 0.018, 0.09, -17248.484, -4.6, -68, 0, 0.018, 6.14, -7176.862, 0.6, 0, 0, 0.018, 5.55, 50076.923, 7.0, 125, -1, 0.017, 2.47, 8257.674, 3, 47, 0, 0.017, 4.20, 31609.036, 1, 38, 0, 0.017, 0.50, 175.762, -4, -48, 0, 0.017, 3.20, -2057.255, 3, 33, 0}, + //MB1 + {0.07430, 4.0998, 6480.98618, 0.005, 7, -0.03, 0.03043, 0.872, 7737.59009, -0.05, 7, 0, 0.02229, 5.000, 15019.22707, -2.80, -19, 0.1, 0.01999, 1.072, 23347.91849, -1.28, 6, 0, 0.01869, 1.744, -1847.70525, -1.52, -18, 0.1, 0.01696, 5.597, 16133.8556, 0.91, 24, -0.1, 0.01623, 0.014, 9061.7681, -0.67, 0, 0, 0.01419, 3.942, 733.0767, -2.19, -26, 0.1, 0.01338, 2.370, 17390.4595, 0.86, 25, -0.1, 0.01304, 5.633, 8399.6847, -0.33, 3, 0, 0.01279, 0.886, -523.5272, -2.14, -26, 0.1, 0.01215, 3.242, 7805.1642, -0.61, -1, 0, 0.01088, 3.686, 8435.7674, -2.16, -26, 0.1, 0.01088, 5.853, 8431.1650, 0.88, 25, -0.1, 0.00546, 4.143, 5852.6842, 0, 7, 0, 0.00443, 0.17, 14809.6776, 1.5, 32, 0, 0.00342, 2.24, 8399.7091, -0.3, 3, 0, 0.00330, 1.77, 16275.8310, -2.9, -19, 0, 0.00318, 4.13, 24604.5224, -1.3, 6, 0, 0.00296, 0.90, 7109.2881, 0, 7, 0, 0.00285, 3.43, 31676.6099, 0.2, 31, 0, 0.00207, 3.23, 16066.2815, 1.5, 32, 0, 0.00202, 2.07, 16833.1453, -1.0, 3, 0, 0.00202, 5.10, -33.7871, 0.3, 4, 0, 0.00200, 1.67, 24462.5471, 2.4, 50, 0, 0.00198, 4.80, -591.1013, -1.6, -18, 0, 0.00193, 1.12, 22719.6165, -1.2, 6, 0, 0.00164, 5.67, -10176.397, -3.0, -43, 0, 0.00161, 4.73, 25719.151, 2.4, 50, 0, 0.00158, 5.04, 14390.925, -2.8, -20, 0, 0.00149, 5.86, 13695.049, -2.2, -12, 0, 0.00135, 1.79, -2476.007, -1.5, -18, 0, 0.00121, 1.93, 16759.856, 2.4, 50, 0, 0.00117, 6.04, 16764.459, -0.6, 0, 0, 0.00104, 1.93, 22023.740, -0.7, 13, 0, 0.00085, 2.83, 30561.981, -3.5, -13, 0, 0.00079, 4.81, -8852.219, -3.7, -51, 0, 0.00076, 1.59, -7595.615, -3.7, -51, 0, 0.00075, 2.91, 8433.795, 0.9, 25, 0, 0.00075, 0.35, 8433.138, -2.2, -26, 0, 0.00073, 0.13, 70.993, -1.9, -22, 0, 0.00069, 1.70, 16728.376, 1.2, 28, 0, 0.00068, 0.83, 8365.892, -0.1, 7, 0, 0.00060, 0.20, 32933.214, 0.2, 31, 0, 0.00060, 0.31, 8446.085, 0, 7, 0}, + //MB2 + {0.000220, 4.100, 6480.9862, 0, 7, 0, 0.000101, 5.24, 8435.7674, -2.2, -26, 0, 0.000101, 4.30, 8431.1650, 0.9, 25, 0, 0.000090, 0.87, 7737.5901, 0, 7, 0, 0.000060, 1.07, 23347.9185, -1.3, 6, 0, 0.000060, 5.00, 15019.2271, -2.8, -19, 0, 0.000050, 1.74, -1847.705, -1.5, -18, 0, 0.000050, 5.60, 16133.856, 0.9, 24, 0, 0.000050, 0.01, 9061.768, -0.7, 0, 0, 0.000040, 3.24, 7805.164, -0.6, 0, 0, 0.000040, 3.94, 733.077, -2.2, -26, 0, 0.000040, 0.89, -523.527, -2.1, -26, 0}}, + + { //精度1千米 + //MR0 + {385000.510, 0, 0, 0, 0, 0, 20905.354, 5.4971472, 8328.691424623, 1.522924, 25.0719, -0.12360, 3699.111, 4.8997864, 7214.06286536, -2.184756, -18.860, 0.0828, 2955.967, 0.972156, 15542.75428998, -0.66183, 6.212, -0.0408, 569.925, 1.569516, 16657.3828492, 3.04585, 50.14, -0.2472, 246.158, 5.68582, -1114.6285593, -3.7077, -43.93, 0.206, 204.586, 1.01528, 14914.4523348, -0.6352, 6.15, -0.035, 170.733, 3.32771, 23871.4457146, 0.8611, 31.28, -0.164, 152.138, 4.94291, 6585.7609101, -2.1581, -18.92, 0.088, 129.620, 0.74291, -7700.3894694, -1.5496, -25.01, 0.118, 108.743, 5.19847, 7771.3771450, -0.3309, 3.1, -0.020, 104.755, 2.31243, 8956.993380, 1.4963, 25.1, -0.129, 79.661, 5.38293, -8538.240890, 2.8030, 26.1, -0.118, 48.888, 6.24006, 628.301955, -0.0266, 0.1, -0.005, 34.783, 2.73035, 22756.817155, -2.847, -12.6, 0.04, 30.824, 4.0706, 16171.056245, -0.688, 6.3, -0.05, 24.208, 1.7151, 7842.364821, -2.211, -18.8, 0.08, 23.210, 3.9251, 24986.074274, 4.569, 75.2, -0.37, 21.636, 0.3748, 14428.125731, -4.370, -37.7, 0.17, 16.675, 2.0137, 8399.679100, -0.358, 3.2, -0.03, 14.403, 3.3303, -9443.319984, -5.231, -69.0, 0.33, 12.831, 3.3708, 23243.143759, 0.888, 31.2, -0.16, 11.650, 5.0859, 31085.508580, -1.324, 12, -0.08, 10.445, 5.6833, 32200.13714, 2.384, 56, -0.29, 10.321, 0.8579, -1324.17803, 0.618, 7, -0.03, 10.056, 5.7290, -1742.93051, -3.681, -44, 0.21, 9.884, 1.0584, 14286.15038, -0.609, 6, -0.03, 8.752, 4.7856, -9652.86945, -0.905, -18, 0.09, 8.379, 5.9845, -557.31428, -1.854, -22, 0.10, 7.003, 4.6705, -16029.08089, -3.072, -50, 0.24, 6.322, 1.2708, 16100.06857, 1.192, 28, -0.14, 5.751, 4.6680, 17285.68480, 3.019, 50, -0.25, 4.950, 4.9860, 5957.45895, -2.131, -19, 0.09, 4.421, 4.5969, -209.54947, 4.326, 51, -0.24, 4.131, 3.2135, 7004.51340, 2.141, 32, -0.16, 3.958, 2.7735, 22128.51520, -2.820, -13, 0.05, 3.258, 0.6735, 14985.44001, -2.52, -16, 0.1, 3.148, 0.114, 16866.93231, -1.28, -1, 0, 2.616, 0.143, 24499.74767, 0.83, 31, -0.2, 2.354, 1.672, 8470.66678, -2.24, -19, 0.1, 2.117, 0.700, -7072.08751, -1.58, -25, 0.1, 1.897, 0.418, 13799.82378, -4.34, -38, 0.2, 1.739, 3.629, -8886.00570, -3.38, -47, 0.2, 1.571, 5.129, 30457.20662, -1.30, 12, -0.1, 1.423, 1.158, 39414.20000, 0.20, 37, -0.2, 1.419, 6.171, 23314.13143, -0.99, 9, -0.1, 1.166, 2.269, 9585.29534, 1.47, 25, -0.1, 1.117, 6.281, 33314.76570, 6.09, 100, -0.5, 1.066, 6.197, 1256.60391, -0.05, 0, 0, 1.059, 4.068, 8364.73984, -2.18, -19, 0.1, 0.933, 4.369, 16728.3705, 1.17, 28, -0.1, 0.862, 4.601, 6656.7486, -4.04, -41, 0.2, 0.851, 2.800, 70.9877, -1.88, -22, 0.1, 0.849, 5.726, 31571.8352, 2.41, 56, -0.3, 0.796, 5.084, -9095.5552, 0.95, 4, 0, 0.779, 0.975, -17772.0114, -6.75, -94, 0.5, 0.774, 2.658, 15752.3038, -4.99, -45, 0.2, 0.728, 0.266, 8326.3902, 3.05, 50, -0.2, 0.683, 1.304, 8330.9926, 0, 0, 0, 0.670, 1.756, 40528.8286, 3.91, 81, -0.4, 0.658, 3.414, 22614.8418, 0.91, 31, -0.2, 0.657, 0.901, -1952.4800, 0.64, 7, 0, 0.598, 6.026, 8393.1258, -2.18, -19, 0.1, 0.596, 5.014, 24080.9952, -3.46, -20, 0.1, 0.579, 5.829, 23385.1191, -2.87, -13, 0, 0.514, 4.302, 6099.4343, -5.89, -63, 0.3, 0.508, 1.830, 14218.5763, -0.04, 13, -0.1, 0.498, 5.242, 7143.0752, -0.30, 3, 0, 0.495, 3.373, -10071.6219, -5.20, -69, 0.3, 0.473, 2.430, -17981.5609, -2.43, -43, 0.2, 0.456, 4.887, -8294.9344, -1.83, -29, 0.1, 0.453, 0.173, 8362.4485, 1.21, 21, -0.1, 0.423, 4.489, 29970.8800, -5.03, -32, 0.1, 0.422, 2.315, -24357.7723, -4.60, -75, 0.4, 0.411, 1.102, 13657.8484, -0.58, 6, 0, 0.410, 0.500, 8311.7707, -2.18, -19, 0.1, 0.379, 3.626, 24428.7600, 2.71, 53, 0, 0.355, 0.740, 25614.3762, 4.54, 75, 0, 0.343, 5.772, -2371.2325, -3.7, -44, 0, 0.335, 0.857, 9166.5428, -2.8, -26, 0, 0.332, 0.444, -8257.7037, -3.4, -47, 0, 0.323, 4.829, -10281.1714, -0.9, -18, 0, 0.322, 5.758, 5889.8848, -1.6, -12, 0, 0.287, 0.56, 38299.5714, -3.5, -6, 0, 0.284, 5.57, 15333.2048, 3.7, 57, 0, 0.279, 2.82, 21500.2132, -2.8, -13, 0, 0.256, 0.72, 14357.1381, -2.5, -16, 0, 0.248, 2.20, -7909.9389, 2.8, 26, 0, 0.245, 1.90, 31713.8105, -1.4, 12, 0, 0.237, 3.47, 15056.4277, -4.4, -38, 0, 0.213, 3.77, 15613.7420, -2.5, -16, 0, 0.213, 2.50, 32828.4391, 2.4, 56, 0, 0.209, 3.26, 6376.2114, 2.2, 32, 0, 0.205, 2.93, 14967.4158, -0.7, 6, 0, 0.205, 2.02, 15540.4531, 0.9, 31, 0, 0.204, 3.06, 15545.0555, -2.2, -19, 0, 0.203, 1.20, 38785.8980, 0.2, 37, 0, 0.201, 6.06, 6447.1991, 0.3, 10, 0, 0.186, 6.13, -16238.6304, 1.3, 1, 0, 0.183, 2.13, 21642.1886, -6.6, -57, 0, 0.169, 3.29, -8815.0180, -5.3, -69, 0, 0.167, 1.06, 8328.3391, 1.5, 25, 0, 0.167, 0.51, 8329.0437, 1.5, 25, 0, 0.167, 1.26, 14756.7124, -0.7, 6, 0, 0.158, 0.07, 17495.2343, -1.3, -1, 0, 0.157, 0.57, 6638.7244, -2.2, -19, 0, 0.157, 6.21, 22685.8295, -1.0, 9, 0, 0.148, 5.03, 5329.1570, -2.1, -19, 0, 0.148, 4.03, 16799.3582, -0.7, 6, 0, 0.145, 0.05, 7178.0144, 1.5, 25, 0, 0.144, 5.64, -486.3266, -3.7, -44, 0, 0.139, 3.51, 47742.8914, 1.7, 63, 0, 0.138, 4.07, 7935.6705, 1.5, 25, 0, 0.136, 4.63, -15400.7789, -3.1, -50, 0, 0.136, 3.96, -695.8761, 0.6, 7, 0, 0.135, 5.95, 7211.7617, -0.7, 6, 0, 0.128, 5.17, 29828.9047, -1.3, 12, 0, 0.127, 1.18, 7753.3529, 1.5, 25, 0, 0.127, 0.71, 7216.3641, -3.7, -44, 0, 0.124, 5.83, 15149.7333, -0.7, 6, 0, 0.121, 1.46, 8000.1048, -2.2, -19, 0, 0.120, 3.78, 8721.7124, 1.5, 25, 0, 0.116, 5.19, 6428.0209, -2.2, -19, 0, 0.114, 2.89, -1185.6162, -1.8, -22, 0, 0.112, 2.85, 15542.4020, -0.7, 6, 0, 0.112, 2.23, 15543.1066, -0.7, 6, 0, 0.110, 0.51, 7213.7105, -2.2, -19, 0, 0.110, 6.15, 7214.4152, -2.2, -19, 0, 0.110, 1.31, 15471.7666, 1.2, 28, 0, 0.109, 2.46, 141.9754, -3.8, -44, 0, 0.108, 0.46, 13171.5218, -4.3, -38, 0, 0.108, 6.13, 23942.4334, -1.0, 9, 0, 0.107, 3.15, 15508.9972, -0.4, 10, 0, 0.105, 0.39, 8904.030, 1.5, 25, 0, 0.105, 4.93, 14392.077, -0.7, 6, 0, 0.103, 2.47, 25195.624, 0.2, 24, 0, 0.101, 3.48, 6821.042, -2.2, -19, 0, 0.099, 4.37, 7149.629, 1.5, 25, 0, 0.099, 1.27, -17214.697, -4.9, -72, 0, 0.096, 1.93, 15576.511, -1.0, 0, 0, 0.086, 2.92, 46628.263, -2.0, 19, 0, 0.085, 6.22, 8504.484, -2.5, -22, 0, 0.080, 3.40, -2438.807, -3.1, -37, 0, 0.080, 1.17, 8786.147, -2.2, -19, 0, 0.077, 3.61, 7230.984, 1.5, 25, 0, 0.071, 0.28, 8315.574, -2.2, -19, 0, 0.067, 4.53, 29342.578, -5.0, -32, 0, 0.065, 2.24, 31642.823, 0.5, 34, 0, 0.063, 5.80, 8329.403, 1.5, 25, 0, 0.063, 2.05, 8327.980, 1.5, 25, 0, 0.062, 0.08, 8346.716, -0.3, 0, 0, 0.061, 4.85, 36.048, -3.7, -44, 0, 0.061, 2.58, 6063.386, -2.2, -19, 0, 0.061, 4.30, -766.864, 2.5, 29, 0, 0.060, 3.01, 8322.132, -0.3, 0, 0, 0.059, 0.44, 25057.062, 2.7, 53, 0, 0.059, 0.31, 8288.877, 1.5, 25, 0, 0.059, 2.35, 41643.457, 7.6, 125, -1, 0.059, 1.26, 8368.506, 1.5, 25, 0, 0.058, 1.80, 39900.527, 3.9, 81, 0, 0.058, 1.87, 13590.274, 0, 13, 0, 0.057, 0.47, 14954.262, -0.7, 6, 0, 0.057, 6.20, 8294.910, 1.8, 29, 0, 0.056, 4.63, -8362.473, -1.2, -21, 0, 0.055, 2.86, 8170.957, 1.5, 25, 0, 0.055, 0.03, 7632.815, 2.1, 32, 0, 0.053, 0.80, 7180.306, -1.9, -15, 0, 0.053, 4.64, 6028.447, -4.0, -41, 0, 0.053, 4.57, 15385.020, -0.7, 6, 0, 0.052, 0.60, 37671.269, -3.5, -6, 0, 0.052, 4.99, 8486.426, 1.5, 25, 0, 0.051, 4.62, 17913.987, 3.0, 50, 0, 0.050, 1.64, 837.851, -4.4, -51, 0, 0.049, 5.79, 7542.649, 1.5, 25, 0, 0.049, 2.06, 9114.733, 1.5, 25, 0, 0.049, 2.21, 7056.329, -2.2, -19, 0, 0.049, 4.90, 7214.063, -2.2, -19, 0, 0.048, 5.39, -1671.943, -5.6, -66, 0, 0.047, 4.90, -26100.703, -8.3, -119, 1, 0.047, 1.60, -9024.567, -0.9, -18, 0, 0.046, 1.16, 7161.094, -2.2, -19, 0, 0.046, 5.77, 30943.533, 2.4, 56, 0, 0.046, 2.43, 22199.503, -4.7, -35, 0, 0.046, 3.16, 14991.999, -0.7, 6, 0, 0.044, 4.11, 48857.520, 5.4, 106, -1, 0.044, 4.39, 6625.570, -2.2, -19, 0, 0.044, 6.06, 7789.401, -2.2, -19, 0, 0.043, 0.14, 16693.431, -0.7, 6, 0, 0.043, 4.50, 15020.385, -0.7, 6, 0, 0.043, 4.35, 5471.132, -5.9, -63, 0, 0.043, 4.32, 575.338, 0, 0, 0, 0.043, 5.43, 7267.032, -2.2, -19, 0, 0.043, 3.82, 16328.796, -0.7, 6, 0, 0.042, 2.73, -17424.247, -0.6, -21, 0, 0.041, 3.60, 15489.785, -0.7, 6, 0, 0.040, 2.62, 16655.082, 4.6, 75, 0, 0.040, 4.23, 8351.233, -2.2, -19, 0, 0.039, 0.66, -6443.786, -1.6, -25, 0, 0.039, 2.13, 16118.093, -0.7, 6, 0, 0.039, 5.86, 7247.820, -2.5, -23, 0, 0.038, 4.56, 7285.051, -4.1, -41, 0, 0.038, 2.59, 9179.168, -2.2, -19, 0, 0.038, 1.42, 393.021, 0, 0, 0, 0.038, 4.94, 8381.661, 1.5, 25, 0, 0.037, 5.06, 23452.693, -3.4, -20, 0, 0.037, 5.11, 9027.981, -0.4, 0, 0, 0.037, 4.98, 7740.199, 1.5, 25, 0, 0.037, 3.66, 16659.684, 1.5, 25, 0, 0.037, 2.89, 8275.722, 1.5, 25, 0, 0.037, 4.26, 40042.502, 0.2, 38, 0, 0.036, 1.95, 8326.062, 1.5, 25, 0, 0.036, 5.90, 8331.321, 1.5, 25, 0, 0.035, 1.33, 15595.723, -0.7, 6, 0, 0.035, 1.39, 7777.936, 2, 25, 0, 0.035, 0.80, 6663.308, -2, -19, 0, 0.035, 0.53, 64.434, -4, -44, 0, 0.034, 2.15, 6691.693, -2, -19, 0, 0.034, 1.90, -8467.253, 1, 0, 0, 0.033, 2.83, 7806.322, 2, 25, 0, 0.033, 4.67, 9479.368, 2, 25, 0, 0.033, 1.41, 418.752, 4, 51, 0}, + //MR1 + {0.5139, 4.1569, 14914.452335, -0.635, 6.2, -0.04, 0.3824, 1.8013, 6585.760910, -2.158, -19, 0.09, 0.3265, 2.3987, 7700.38947, 1.550, 25, -0.12, 0.2640, 5.4540, 8956.99338, 1.496, 25, -0.13, 0.1230, 3.0985, 628.30196, -0.027, 0, 0, 0.0775, 0.929, 16171.05625, -0.69, 6, 0, 0.0607, 4.857, 7842.36482, -2.21, -19, 0.1, 0.0497, 4.200, 14286.15038, -0.61, 6, 0, 0.0419, 5.155, 8399.67910, -0.36, 3, 0, 0.0322, 0.229, 23243.1438, 0.89, 31, -0.2, 0.0253, 2.587, -1742.9305, -3.68, -44, 0.2, 0.0249, 1.844, 5957.4590, -2.13, -19, 0.1, 0.0176, 4.754, 16029.0809, 3.07, 50, -0.2, 0.0145, 1.526, 17285.6848, 3.02, 50, -0.3, 0.0137, 1.004, 15542.7543, -0.66, 6, 0, 0.0126, 5.010, 8326.3902, 3.05, 50, 0, 0.0119, 4.814, 8470.6668, -2.24, -19, 0, 0.0118, 2.843, 8330.9926, 0, 0, 0, 0.0107, 2.442, 7072.0875, 1.6, 25, 0, 0.0099, 5.92, 22128.5152, -2.8, -13, 0, 0.0066, 3.28, 24499.7477, 0.8, 31, 0, 0.0065, 4.90, 7214.0629, -2.2, -19, 0, 0.0059, 5.41, 9585.2953, 1.5, 25, 0, 0.0054, 3.06, 1256.6039, -0.1, 0, 0, 0.0052, 2.50, 8328.3391, 1.5, 25, 0, 0.0052, 5.35, 8329.0437, 1.5, 25, 0, 0.0048, 3.56, 13799.8238, -4.3, -38, 0, 0.0039, 1.99, 30457.2066, -1.3, 12, 0, 0.0035, 0.49, 15540.4531, 0.9, 31, 0, 0.0035, 4.60, 15545.0555, -2.2, -19, 0, 0.0033, 0.27, 22614.842, 0.9, 31, 0, 0.0031, 4.24, 13657.848, -0.6, 6, 0, 0.0023, 1.23, 16728.371, 1.2, 28, 0, 0.0023, 5.50, 8328.691, 1.5, 25, 0, 0.0023, 0, 0, 0, 0, 0, 0.0023, 4.41, 7211.762, -0.7, 6, 0, 0.0022, 1.39, 8311.771, -2.2, -19, 0, 0.0022, 2.25, 7216.364, -3.7, -44, 0, 0.0021, 5.94, 70.988, -1.9, -22, 0, 0.0021, 2.58, 31571.835, 2.4, 56, 0, 0.0017, 2.63, -2371.232, -3.7, -44, 0, 0.0016, 4.04, -1952.480, 0.6, 7, 0, 0.0015, 4.23, 8329.403, 1.5, 25, 0, 0.0015, 3.63, 8327.980, 1.5, 25, 0, 0.0015, 2.69, 23385.119, -2.9, -13, 0, 0.0014, 5.96, 21500.213, -2.8, -13, 0, 0.0013, 4.06, 15542.402, -0.7, 6, 0, 0.0013, 1.02, 15543.107, -0.7, 6, 0, 0.0013, 2.10, 7143.075, -0.3, 0, 0, 0.0012, 0.23, -10071.622, -5.2, -69, 0, 0.0011, 3.33, 23871.446, 1, 31, 0, 0.0011, 1.89, 5329.157, -2, -19, 0}, + //MR2 + {0.001490, 4.157, 14914.45233, -0.64, 6, 0, 0.001110, 1.801, 6585.7609, -2.16, -19, 0.1, 0.000950, 2.399, 7700.3895, 1.55, 25, -0.1, 0.000770, 5.454, 8956.9934, 1.50, 25, -0.1, 0.000360, 3.098, 628.3020, 0, 0, 0, 0.000230, 0.93, 16171.0562, -0.7, 6, 0, 0.000180, 4.86, 7842.3648, -2.2, -19, 0, 0.000140, 4.20, 14286.1504, -0.6, 6, 0, 0.000120, 5.16, 8399.6791, -0.4, 0, 0, 0.000116, 3.46, 8326.390, 3.0, 50, 0, 0.000109, 4.39, 8330.993, 0, 0, 0, 0.000090, 0.23, 23243.144, 0.9, 31, 0}}} + +// GetMoonCir 返回旧月球级数系数的独立副本 / an independent copy of the legacy lunar-series coefficients. +func GetMoonCir() [][][]float64 { + result := make([][][]float64, len(moonCir)) + for coordinateIndex, coordinate := range moonCir { + result[coordinateIndex] = make([][]float64, len(coordinate)) + for seriesIndex, series := range coordinate { + result[coordinateIndex][seriesIndex] = append([]float64(nil), series...) + } + } + return result +} + +// WhereMoon 当前星历模型下的地心月球历元平黄道几何量 / geocentric Moon of date under the active ephemeris model. +func WhereMoon(coordIndex int, jd float64) float64 { + return WhereMoonN(coordIndex, jd, -1) +} + +// WhereMoonN WhereMoon 的 n 变体:coordIndex 0 黄经、1 黄纬(弧度)、2 距离(km);旧级数黄经连续累加,半解析模型落在 [0, 2π),n >= 0 用快速表 / n-parameterised WhereMoon. +func WhereMoonN(coordIndex int, jd float64, n int) float64 { + if semianalyticActive() && coordIndex >= 0 && coordIndex <= 2 { + return semiMoon(coordIndex, jd, n) + } + return elpMoonN(coordIndex, jd, n) +} + +// moonPhaseCompensationThreshold 是启用线性相位补偿的最小 |t|(世纪数)倒数尺度: +// 更小的项本身已低于纳角秒级相位舍入,无需补偿。 +const moonPhaseCompensationThreshold = 1e6 + +func elpMoonN(coordIndex int, jd float64, n int) float64 { + arcsecPerRadian := 180.0 * 3600.0 / math.Pi + // Preserve the low parts of both the epoch subtraction and century division. + days := jd - 2451545.0 + origin := days - jd + daysLow := (jd - (days - origin)) - (2451545 + origin) + t := days / 36525.0 + tLow := (math.FMA(-t, 36525, days) + daysLow) / 36525 + // Small angular terms have sub-nanoarcsecond phase roundoff already. + // Compensate only the larger terms; distance needs no angular precision. + phaseThreshold := moonPhaseCompensationThreshold / math.Abs(t) + ob := moonCir[coordIndex] + var v float64 + var tn float64 = 1 + t2 := t * t + t3 := t2 * t + t4 := t3 * t + t5 := t4 * t + tx := t - 10 + if coordIndex == 0 { + // 黄经线性项必须始终走补偿版本:MoonCalcNew 与 HMoonTrueLo 是同一个物理量的两个入口, + // 只在其中一个入口补偿会让两者差约 1e-9 度。 + longitude := moonReducedLinearPhase(3.81034409, 8399.684730072, t, tLow) + v += (longitude - 3.319e-05*t2 + 3.11e-08*t3 - 2.033e-10*t4) * arcsecPerRadian + v += 5028.792262*t + 1.1124406*t2 + 0.00007699*t3 - 0.000023479*t4 - 0.0000000178*t5 //岁差(角秒) + if tx > 0 { + v += -0.866 + 1.43*tx + 0.054*tx*tx //对公元3000年至公元5000年的拟合,最大误差小于10角秒 + } + } + t2 /= 1e4 + t3 /= 1e8 + t4 /= 1e8 + nScalars := n * 6 + if nScalars < 0 { + nScalars = len(ob[0]) + } + for i := 0; i < len(ob); i++ { + F := ob[i] + seriesPhaseThreshold := phaseThreshold / math.Abs(tn) + N := math.Floor(float64(nScalars*len(F))/float64(len(ob[0])) + 0.5) + if i != 0 { + N += 6 + } + if N >= float64(len(F)) { + N = float64(len(F)) + } + var c float64 = 0 + for j := 0; float64(j) < N; j += 6 { + phase := F[j+1] + t*F[j+2] + if coordIndex != 2 && math.Abs(F[j]*F[j+2]) > seriesPhaseThreshold { + phase = moonReducedLinearPhase(F[j+1], F[j+2], t, tLow) + } + c += F[j] * math.Cos(phase+t2*F[j+3]+t3*F[j+4]+t4*F[j+5]) + } + v += c * tn + tn *= t + } + if coordIndex != 2 { + v /= arcsecPerRadian + } + return v +} + +// moonReducedLinearPhase 小增量不进长期项乘积;2π 低位防止整圈处约化跳变。 +func moonReducedLinearPhase(offset, rate, t, tLow float64) float64 { + const twoPiLow = 2.4492935982947064e-16 + product := rate * t + roundoff := math.FMA(rate, t, -product) + rate*tLow + turns := math.Round(product / (2 * math.Pi)) + return math.FMA(-turns, 2*math.Pi, product) + (roundoff - turns*twoPiLow) + offset +} diff --git a/planet/moon_model.go b/planet/moon_model.go new file mode 100644 index 0000000..703c920 --- /dev/null +++ b/planet/moon_model.go @@ -0,0 +1,11 @@ +package planet + +import "b612.me/astro/internal/ephem" + +// WhereMoonModelN 按指定模型计算月球几何量 / evaluate lunar geometry with an explicit model. +func WhereMoonModelN(model ephem.Model, coordIndex int, jd float64, n int) float64 { + if model == ephem.Semianalytic && coordIndex >= 0 && coordIndex <= 2 { + return semiMoon(coordIndex, jd, n) + } + return elpMoonN(coordIndex, jd, n) +} diff --git a/planet/planet.go b/planet/planet.go index dcc5729..3e07932 100644 --- a/planet/planet.go +++ b/planet/planet.go @@ -1,11 +1,12 @@ package planet import ( + "b612.me/astro/internal/ephem" . "b612.me/astro/tools" "math" ) -// WherePlanet 天体 xt 在儒略日 jde 的 VSOP 结果 / VSOP result for body xt at Julian day jde. +// WherePlanet 天体 xt 在儒略日 jde 的历元平黄道几何量,按当前星历模型求值 / geometric position of body xt of date under the active ephemeris model. // // xt 取 -1 或 0..7:0 为地球,1..7 依次为水星、金星、火星、木星、土星、天王星、海王星;-1 表示地球,zn 取 0 时给日心黄经。 // zn 取 0 黄经、1 黄纬、2 日心距(AU);xt 或 zn 越界返回 NaN 而不 panic。 @@ -15,14 +16,19 @@ func WherePlanet(xt, zn int, jde float64) float64 { return WherePlanetN(xt, zn, jde, -1) } -// WherePlanetN 同 WherePlanet 的截断版 / truncated form of WherePlanet. -// -// n < 0 时使用全部项;否则保留约 n 个主项并按比例缩短高阶项。取值域与越界行为同 WherePlanet。 -// When n < 0 all terms are used; otherwise roughly n principal terms are kept and higher orders scaled proportionally. Domain and out-of-range behavior match WherePlanet. +// WherePlanetN WherePlanet 的 n 变体;n < 0 取完整级数,半解析模型下 n >= 0 用快速表,Legacy 按 n 截断 / n-parameterised WherePlanet. func WherePlanetN(xt, zn int, jde float64, n int) float64 { + return WherePlanetModelN(ephem.Active(), xt, zn, jde, n) +} + +// WherePlanetModelN 按指定模型计算行星几何量 / evaluate planetary geometry with an explicit model. +func WherePlanetModelN(model ephem.Model, xt, zn int, jde float64, n int) float64 { if xt < -1 || xt > 7 || zn < 0 || zn > 2 { return math.NaN() } + if model == ephem.Semianalytic { + return semiPlanet(xt, zn, jde, n) + } sata := 0 if xt == -1 { diff --git a/planet/planet_test.go b/planet/planet_test.go index d1f463f..24aa722 100644 --- a/planet/planet_test.go +++ b/planet/planet_test.go @@ -3,6 +3,8 @@ package planet import ( "math" "testing" + + "b612.me/astro/internal/ephemtest" ) func TestPlanetViewsMatchRawCuts(t *testing.T) { @@ -80,6 +82,7 @@ func assertWherePlanetNaN(t *testing.T, xt, zn int, jd float64) { } func TestWherePlanetGoldenValues(t *testing.T) { + ephemtest.PinLegacy(t) jd := 2451545.0 cases := []struct { xt, zn int diff --git a/planet/semianalytic_test.go b/planet/semianalytic_test.go new file mode 100644 index 0000000..51a64ec --- /dev/null +++ b/planet/semianalytic_test.go @@ -0,0 +1,141 @@ +// 被测契约:半解析模型在 1800–2200 年与旧级数相差不超过旧级数自身误差量级;n ≥ 0 走快速档且不劣于旧级数 n=16;越界返回 NaN,切回后逐位还原。 +package planet + +import ( + "math" + "strconv" + "testing" + + "b612.me/astro/internal/ephem" + + "b612.me/astro/internal/ephemtest" +) + +func useSemianalytic(t testing.TB) { + prev := ephem.Active() + ephem.SetActive(ephem.Semianalytic) + t.Cleanup(func() { ephem.SetActive(prev) }) +} + +func angleDiffArcsec(a, b float64) float64 { + return math.Abs(math.Mod(a-b+540, 360)-180) * 3600 +} + +type lbrDeg struct{ l, b, r float64 } + +func sampleAll(jds []float64) map[[2]int]lbrDeg { + out := map[[2]int]lbrDeg{} + for i, jd := range jds { + for xt := -1; xt <= 7; xt++ { + out[[2]int{xt, i}] = lbrDeg{WherePlanet(xt, 0, jd), WherePlanet(xt, 1, jd), WherePlanet(xt, 2, jd)} + } + out[[2]int{-9, i}] = lbrDeg{ + math.Mod(WhereMoon(0, jd)*180/math.Pi, 360), WhereMoon(1, jd) * 180 / math.Pi, WhereMoon(2, jd), + } + } + return out +} + +func TestSemianalyticNearLegacyModernEra(t *testing.T) { + var jds []float64 + for jd := 2378496.5; jd <= 2524593.5; jd += 1461.37 { + jds = append(jds, jd) + } + ephemtest.PinLegacy(t) + legacy := sampleAll(jds) + useSemianalytic(t) + semi := sampleAll(jds) + // 角秒;距离 AU(行星)或 km(月球,-9)。 + tolA := map[int]float64{-1: 0.5, 0: 0.5, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 6, 7: 10, -9: 10} + tolR := map[int]float64{-1: 2e-7, 0: 2e-7, 1: 2e-7, 2: 3e-7, 3: 1e-6, 4: 4e-6, 5: 1e-5, 6: 2e-4, 7: 2e-4, -9: 5} + worst := map[int][3]float64{} + for k, want := range legacy { + got := semi[k] + w := worst[k[0]] + w[0] = math.Max(w[0], angleDiffArcsec(got.l, want.l)*math.Cos(want.b*math.Pi/180)) + w[1] = math.Max(w[1], math.Abs(got.b-want.b)*3600) + w[2] = math.Max(w[2], math.Abs(got.r-want.r)) + worst[k[0]] = w + } + for xt, w := range worst { + t.Logf("xt %d: max ΔL %.3f″ ΔB %.3f″ ΔR %.3g", xt, w[0], w[1], w[2]) + if w[0] > tolA[xt] || w[1] > tolA[xt] || w[2] > tolR[xt] { + t.Errorf("xt %d differs from legacy beyond expected truncation error", xt) + } + } +} + +func TestSemianalyticDomainAndRestore(t *testing.T) { + ephemtest.PinLegacy(t) + jd := 2460000.5 + before := WherePlanetN(3, 0, jd, 16) + moonBefore := WhereMoonN(0, jd, 8) + useSemianalytic(t) + for _, c := range [][2]int{{-2, 0}, {8, 0}, {0, -1}, {0, 3}} { + if v := WherePlanet(c[0], c[1], jd); !math.IsNaN(v) { + t.Errorf("xt %d zn %d: want NaN, got %v", c[0], c[1], v) + } + } + if WherePlanetN(3, 0, jd, 16) != WherePlanetN(3, 0, jd, 8) || WhereMoonN(0, jd, 16) != WhereMoonN(0, jd, 0) { + t.Error("every n >= 0 must use the same fast tier") + } + if WherePlanetN(3, 0, jd, 16) == WherePlanetN(3, 0, jd, -1) { + t.Error("n >= 0 did not switch to the fast tier") + } + ephem.SetActive(ephem.Legacy) + if WherePlanetN(3, 0, jd, 16) != before || WhereMoonN(0, jd, 8) != moonBefore { + t.Error("switching back did not restore legacy values bit for bit") + } +} + +// 快速档相对全表的最大差(角秒;月球另有 km),全表本身对 DE441 已在 0.02″ 内。 +func TestSemianalyticFastTierNearFull(t *testing.T) { + useSemianalytic(t) + var worst [9]float64 + worstKM := 0.0 + for jd := -1931455.0; jd <= 5373545.0; jd += 3652.5 * 7.3 { + for xt := 0; xt <= 7; xt++ { + d := angleDiffArcsec(WherePlanetN(xt, 0, jd, 0), WherePlanetN(xt, 0, jd, -1))*math.Cos(WherePlanetN(xt, 1, jd, -1)*math.Pi/180) + + math.Abs(WherePlanetN(xt, 1, jd, 0)-WherePlanetN(xt, 1, jd, -1))*3600 + worst[xt] = math.Max(worst[xt], d) + } + ml := angleDiffArcsec(WhereMoonN(0, jd, 0)*180/math.Pi, WhereMoonN(0, jd, -1)*180/math.Pi) + + math.Abs(WhereMoonN(1, jd, 0)-WhereMoonN(1, jd, -1))*206264.806 + worst[8] = math.Max(worst[8], ml) + worstKM = math.Max(worstKM, math.Abs(WhereMoonN(2, jd, 0)-WhereMoonN(2, jd, -1))) + } + limits := [9]float64{2, 0.6, 1, 8, 15, 60, 75, 20, 60} + for i, w := range worst { + t.Logf("slot %d: max %.2f″", i, w) + if w > limits[i] { + t.Errorf("slot %d: fast tier differs from full table by %.2f″ (limit %.0f″)", i, w, limits[i]) + } + } + if worstKM > 20 { + t.Errorf("moon distance: fast tier differs by %.1f km", worstKM) + } +} + +func BenchmarkWherePlanetSemianalytic(b *testing.B) { + useSemianalytic(b) + for _, n := range []int{-1, 16} { + n := n + b.Run("n="+strconv.Itoa(n), func(b *testing.B) { + for xt := 0; xt <= 7; xt++ { + xt := xt + b.Run(strconv.Itoa(xt), func(b *testing.B) { + for i := 0; i < b.N; i++ { + jd := 2451545.0 + float64(i%7300)*100 + _ = WherePlanetN(xt, 0, jd, n) + WherePlanetN(xt, 1, jd, n) + WherePlanetN(xt, 2, jd, n) + } + }) + } + b.Run("moon", func(b *testing.B) { + for i := 0; i < b.N; i++ { + jd := 2451545.0 + float64(i%7300)*10.3 + _ = WhereMoonN(0, jd, n) + WhereMoonN(1, jd, n) + WhereMoonN(2, jd, n) + } + }) + }) + } +} diff --git a/planet/spk/apparent_test.go b/planet/spk/apparent_test.go new file mode 100644 index 0000000..837554b --- /dev/null +++ b/planet/spk/apparent_test.go @@ -0,0 +1,45 @@ +package spk + +import ( + "math" + "os" + "testing" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet/internal/semianalytic" +) + +// SPK 只给几何状态,实例应走通用矢量视位置链;与按 DE441 拟合的内置表相比只剩表的拟合误差。 +func TestSPKInstanceApparentMatchesSemianalytic(t *testing.T) { + path := de441Path() + if _, err := os.Stat(path); err != nil { + t.Skip("DE441 file not available; set ASTRO_DE441_BSP") + } + file, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + inst := ephem.NewInstance(file) + worst := 0.0 + for _, jd := range []float64{2451545.0, 2460409.25, 2470000.5} { + for _, b := range []ephem.Body{ephem.Sun, ephem.Moon, ephem.Mercury, ephem.Venus, ephem.Mars, ephem.Jupiter, ephem.Saturn, ephem.Uranus, ephem.Neptune} { + got, gd, _, err := inst.Apparent(b, jd) + if err != nil { + t.Fatalf("jd %.1f body %d: %v", jd, b, err) + } + want, wd, _, err := semianalytic.Ephemeris{}.Apparent(b, jd) + if err != nil { + t.Fatal(err) + } + c := got[0]*want[0] + got[1]*want[1] + got[2]*want[2] + arcsec := math.Acos(math.Min(1, c)) * 206264.806 + worst = math.Max(worst, arcsec) + // 内置表的几何距离取自快速档,外行星可差数万千米。 + if arcsec > 1.5 || math.Abs(gd-wd)/wd > 5e-5 { + t.Errorf("jd %.1f body %d: %.4f\" dist rel %.2e", jd, b, arcsec, (gd-wd)/wd) + } + } + } + t.Logf("worst %.4f\"", worst) +} diff --git a/planet/spk/guard_test.go b/planet/spk/guard_test.go new file mode 100644 index 0000000..144ef2c --- /dev/null +++ b/planet/spk/guard_test.go @@ -0,0 +1,84 @@ +// 被测契约:畸形阶数与非有限系数报 ErrInvalidFile 而不是 panic 或返回 NaN;无关段的覆盖缺口不阻断有效路径。 +package spk + +import ( + "encoding/binary" + "errors" + "math" + "testing" + + "b612.me/astro/internal/ephem" +) + +func type21Record(maxdim, kqmax1, kq int) []float64 { + record := make([]float64, 4*maxdim+12) + record[0] = float64(maxdim) + for index := 0; index < maxdim; index++ { + record[2+index] = 1 + } + record[4*maxdim+8] = float64(kqmax1) + for component := 0; component < 3; component++ { + record[4*maxdim+9+component] = float64(kq) + } + return record +} + +func TestType21RejectsInconsistentOrders(t *testing.T) { + cases := []struct{ maxdim, kqmax1, kq int }{ + {15, 3, 15}, + {15, 27, 1}, + {15, 17, 1}, + {15, 5, 5}, + } + for _, c := range cases { + _, _, err := evaluateType21(0, type21Record(c.maxdim, c.kqmax1, c.kq)) + if !errors.Is(err, ErrInvalidFile) { + t.Errorf("maxdim=%d kqmax1=%d kq=%d: err = %v, want %v", c.maxdim, c.kqmax1, c.kq, err, ErrInvalidFile) + } + } + if _, _, err := evaluateType21(0, type21Record(15, 16, 15)); err != nil { + t.Errorf("maximal valid orders: err = %v", err) + } +} + +func TestType21RejectsNonFiniteRecord(t *testing.T) { + record := type21Record(15, 3, 1) + record[15+2] = math.NaN() + if _, _, err := evaluateType21(0, record); !errors.Is(err, ErrInvalidFile) { + t.Errorf("err = %v, want %v", err, ErrInvalidFile) + } +} + +func TestType2RejectsNonFiniteCoefficient(t *testing.T) { + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", + testSPKSegment{target: Mars, center: Sun, frame: 1, typ: 2, start: 0, end: 10, words: type2Words(0, 10, []float64{5, 5, math.NaN(), 20, 30})}, + ) + file, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + if _, _, err := file.State(Mars, Sun, spkJ2000+5/spkSecondsDay); !errors.Is(err, ErrInvalidFile) { + t.Errorf("err = %v, want %v", err, ErrInvalidFile) + } +} + +func TestUnrelatedSegmentCoverageDoesNotBlockDirectPath(t *testing.T) { + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", + testSPKSegment{target: Mars, center: Sun, frame: 1, typ: 2, start: 0, end: 10, words: type2Words(0, 10, []float64{5, 5, 10, 20, 30})}, + testSPKSegment{target: Sun, center: SSB, frame: 1, typ: 2, start: 100, end: 110, words: type2Words(100, 10, []float64{105, 5, 1, 2, 3})}, + ) + file, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + pos, _, err := file.State(Mars, Sun, spkJ2000+5/spkSecondsDay) + if err != nil { + t.Fatalf("Mars→Sun err = %v", err) + } + assertVector(t, pos, [3]float64{10, 20, 30}) + if _, _, err := file.State(Mars, SSB, spkJ2000+5/spkSecondsDay); err != ephem.ErrOutOfRange { + t.Errorf("Mars→SSB err = %v, want %v", err, ephem.ErrOutOfRange) + } +} diff --git a/planet/spk/spk.go b/planet/spk/spk.go new file mode 100644 index 0000000..c373ac0 --- /dev/null +++ b/planet/spk/spk.go @@ -0,0 +1,844 @@ +package spk + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "os" + "sync" + + "b612.me/astro/internal/ephem" +) + +const ( + dafRecordBytes = 1024 + dafFileHeadSize = 1024 + dafND = 2 + dafNI = 6 + dafSummaryWords = dafND + (dafNI+1)/2 + dafSummaryBytes = dafSummaryWords * 8 + dafHeaderWords = 3 + dafWordBytes = 8 + spkJ2000 = 2451545.0 + spkSecondsDay = 86400.0 + spkCacheRecords = 16 +) + +var ( + // ErrInvalidFile 无效 SPK 文件 / invalid SPK file. + ErrInvalidFile = errors.New("spk: invalid DAF/SPK file") + // ErrUnsupportedFrame 不支持的参考框架 / unsupported reference frame. + ErrUnsupportedFrame = errors.New("spk: unsupported reference frame") + // ErrUnsupportedType 不支持的段类型 / unsupported segment type. + ErrUnsupportedType = errors.New("spk: unsupported SPK data type") + // ErrClosed 文件已关闭 / file is closed. + ErrClosed = errors.New("spk: file is closed") + errInvalidDAF = ErrInvalidFile + errClosed = ErrClosed +) + +// Body NAIF 天体编号 / NAIF body identifier. +type Body = ephem.Body + +const ( + // SSB 太阳系质心 / solar-system barycenter. + SSB = ephem.SSB + // Mercury 水星 / Mercury. + Mercury = ephem.Mercury + // Venus 金星 / Venus. + Venus = ephem.Venus + // EMB 地月质心 / Earth-Moon barycenter. + EMB = ephem.EMB + // Mars 火星 / Mars. + Mars = ephem.Mars + // Jupiter 木星 / Jupiter. + Jupiter = ephem.Jupiter + // Saturn 土星 / Saturn. + Saturn = ephem.Saturn + // Uranus 天王星 / Uranus. + Uranus = ephem.Uranus + // Neptune 海王星 / Neptune. + Neptune = ephem.Neptune + // Sun 太阳 / the Sun. + Sun = ephem.Sun + // Moon 月球 / the Moon. + Moon = ephem.Moon + // Earth 地球 / Earth. + Earth = ephem.Earth +) + +// Segment 描述一个支持的 SPK 段 / describes a supported SPK segment. +type Segment struct { + Target Body + Center Body + Frame int + Type int + Start float64 + End float64 +} + +type summarySegment struct { + target Body + center Body + frame int + dataType int + startET float64 + endET float64 + startWord int64 + endWord int64 + initET float64 + interval float64 + recordSize int + records int + maxDim int + distanceScale float64 + timeScale float64 + initJD float64 + initFR float64 + epochStartWord int64 + directoryCount int + cacheMu sync.Mutex + cache map[int][]float64 + cacheOrder []int + owner *File +} + +// File 只读 SPK 文件 / read-only SPK file. +type File struct { + mu sync.RWMutex + file *os.File + order binary.ByteOrder + segments []*summarySegment + known map[Body]bool + closed bool + size int64 +} + +// Open 打开并解析 SPK 文件 / opens and indexes an SPK file. +func Open(path string) (*File, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + spk := &File{file: f, known: make(map[Body]bool)} + stat, err := f.Stat() + if err != nil { + _ = f.Close() + return nil, err + } + spk.size = stat.Size() + if err := spk.index(); err != nil { + _ = f.Close() + return nil, err + } + return spk, nil +} + +// Load Open 的别名 / alias for Open. +func Load(path string) (*File, error) { return Open(path) } + +// Close 关闭文件 / closes the file. +func (s *File) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + s.closed = true + return s.file.Close() +} + +// Segments 返回支持段的快照 / returns a snapshot of supported segments. +func (s *File) Segments() []Segment { + s.mu.RLock() + defer s.mu.RUnlock() + result := make([]Segment, 0, len(s.segments)) + for _, segment := range s.segments { + result = append(result, Segment{ + Target: segment.target, + Center: segment.center, + Frame: segment.frame, + Type: segment.dataType, + Start: spkJ2000 + segment.startET/spkSecondsDay, + End: spkJ2000 + segment.endET/spkSecondsDay, + }) + } + return result +} + +// Coverage 返回天体覆盖的 TDB 儒略日范围 / returns the body's TDB Julian-date coverage. +func (s *File) Coverage(target Body) (start, end float64, ok bool) { + s.mu.RLock() + defer s.mu.RUnlock() + if s.closed { + return 0, 0, false + } + for _, segment := range s.segments { + if segment.target != target || segment.frame != 1 { + continue + } + segmentStart := spkJ2000 + segment.startET/spkSecondsDay + segmentEnd := spkJ2000 + segment.endET/spkSecondsDay + if !ok || segmentStart < start { + start = segmentStart + } + if !ok || segmentEnd > end { + end = segmentEnd + } + ok = true + } + return start, end, ok +} + +// Bodies 返回文件中出现的天体编号 / returns body identifiers present in the file. +func (s *File) Bodies() []Body { + s.mu.RLock() + defer s.mu.RUnlock() + if s.closed { + return nil + } + result := make([]Body, 0, len(s.known)) + for body := range s.known { + result = append(result, body) + } + return result +} + +// State 返回目标相对中心的 ICRF 状态,速度为 km/日 / returns an ICRF state, with velocity in km/day. +func (s *File) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + s.mu.RLock() + defer s.mu.RUnlock() + if s.closed { + return pos, vel, errClosed + } + if math.IsNaN(jdTDB) || math.IsInf(jdTDB, 0) { + return pos, vel, ephem.ErrOutOfRange + } + if target == center { + return pos, vel, nil + } + return s.composeState(target, center, jdTDB) +} + +func (s *File) index() error { + header := make([]byte, dafFileHeadSize) + if _, err := s.file.ReadAt(header, 0); err != nil { + return err + } + if string(header[:8]) != "DAF/SPK " { + return errInvalidDAF + } + order, err := byteOrderFor(string(header[88:96])) + if err != nil { + return err + } + s.order = order + if int32Value(s.order, header, 8) != dafND || int32Value(s.order, header, 12) != dafNI { + return fmt.Errorf("%w: ND/NI must be 2/6", errInvalidDAF) + } + first := int(int32Value(s.order, header, 76)) + last := int(int32Value(s.order, header, 80)) + if first < 2 || last < 2 || int64(first)*dafRecordBytes > s.size || int64(last)*dafRecordBytes > s.size { + return fmt.Errorf("%w: invalid summary record range", errInvalidDAF) + } + seen := make(map[int]bool) + previous := 0 + supported := 0 + for record := first; record != 0; { + if seen[record] { + return fmt.Errorf("%w: cyclic summary record chain", errInvalidDAF) + } + seen[record] = true + data, err := s.readRecord(record) + if err != nil { + return err + } + nextValue := float64Value(s.order, data[0:8]) + previousValue := float64Value(s.order, data[8:16]) + countValue := float64Value(s.order, data[16:24]) + if !validInteger(nextValue, 0, s.size/dafRecordBytes) || previousValue != float64(previous) || !validInteger(countValue, 0, (dafRecordBytes-24)/dafSummaryBytes) { + return fmt.Errorf("%w: invalid summary controls", errInvalidDAF) + } + next, count := int(nextValue), int(countValue) + if next == 0 && record != last { + return fmt.Errorf("%w: final summary disagrees with backward pointer", errInvalidDAF) + } + for index := 0; index < count; index++ { + base := dafHeaderWords*8 + index*dafSummaryBytes + segment, err := s.parseSummary(data[base : base+dafSummaryBytes]) + if err != nil { + return err + } + if segment.frame == 1 && (segment.dataType == 2 || segment.dataType == 3 || segment.dataType == 20 || segment.dataType == 21) { + if err := segment.loadMeta(s); err != nil { + return err + } + supported++ + } else { + continue + } + s.segments = append(s.segments, segment) + s.known[segment.target] = true + s.known[segment.center] = true + } + previous, record = record, next + } + if supported == 0 { + return fmt.Errorf("%w: no supported J2000 segments", errInvalidDAF) + } + return nil +} + +func (s *File) parseSummary(data []byte) (*summarySegment, error) { + if len(data) != dafSummaryBytes { + return nil, errInvalidDAF + } + segment := &summarySegment{ + startET: float64Value(s.order, data[0:8]), + endET: float64Value(s.order, data[8:16]), + target: Body(int32Value(s.order, data, 16)), + center: Body(int32Value(s.order, data, 20)), + frame: int(int32Value(s.order, data, 24)), + dataType: int(int32Value(s.order, data, 28)), + startWord: int64(int32Value(s.order, data, 32)), + endWord: int64(int32Value(s.order, data, 36)), + cache: make(map[int][]float64), + owner: s, + } + if !finite(segment.startET) || !finite(segment.endET) || segment.endET < segment.startET || segment.startWord < 1 || segment.endWord < segment.startWord || segment.endWord > s.size/dafWordBytes { + return nil, fmt.Errorf("%w: invalid segment summary", errInvalidDAF) + } + return segment, nil +} + +func (segment *summarySegment) loadMeta(s *File) error { + if segment.endWord-segment.startWord+1 < 4 { + return fmt.Errorf("%w: segment trailer is missing", errInvalidDAF) + } + if segment.dataType == 20 { + trailer, err := s.readWords(segment.endWord-6, 7) + if err != nil { + return err + } + if !finitePositive(trailer[0]) || !finitePositive(trailer[1]) || !finite(trailer[2]) || math.Abs(trailer[3]) >= 1 || !finitePositive(trailer[4]) || !validInteger(trailer[5], 6, segment.endWord-segment.startWord) || !validInteger(trailer[6], 1, segment.endWord-segment.startWord) { + return fmt.Errorf("%w: invalid type 20 trailer", errInvalidDAF) + } + segment.distanceScale = trailer[0] + segment.timeScale = trailer[1] + segment.initJD = trailer[2] + segment.initFR = trailer[3] + segment.interval = trailer[4] + segment.recordSize = int(trailer[5]) + segment.records = int(trailer[6]) + wordCount := segment.endWord - segment.startWord - 6 + if int64(segment.recordSize)*int64(segment.records) != wordCount || segment.recordSize < 6 || segment.recordSize%3 != 0 { + return fmt.Errorf("%w: invalid type 20 record size", errInvalidDAF) + } + return nil + } + if segment.dataType == 21 { + trailer, err := s.readWords(segment.endWord-1, 2) + if err != nil { + return err + } + if !validInteger(trailer[0], 1, 25) || !validInteger(trailer[1], 1, segment.endWord-segment.startWord) { + return fmt.Errorf("%w: invalid type 21 trailer", errInvalidDAF) + } + segment.maxDim = int(trailer[0]) + segment.recordSize = 4*segment.maxDim + 11 + segment.records = int(trailer[1]) + segment.epochStartWord = segment.startWord + int64(segment.recordSize*segment.records) + segment.directoryCount = segment.records / 100 + wordCount := segment.endWord - segment.startWord + 1 + want := int64(segment.recordSize*segment.records + segment.records + segment.directoryCount + 2) + if want != wordCount { + return fmt.Errorf("%w: invalid type 21 segment size", errInvalidDAF) + } + return nil + } + trailerWords := segment.endWord - 3 + trailer, err := s.readWords(trailerWords, 4) + if err != nil { + return err + } + segment.initET = trailer[0] + segment.interval = trailer[1] + wordCount := segment.endWord - segment.startWord - 3 + if !finite(segment.initET) || !finitePositive(segment.interval) || !validInteger(trailer[2], 5, wordCount) || !validInteger(trailer[3], 1, wordCount) { + return fmt.Errorf("%w: invalid type %d trailer", errInvalidDAF, segment.dataType) + } + segment.recordSize = int(trailer[2]) + segment.records = int(trailer[3]) + if int64(segment.recordSize)*int64(segment.records) != wordCount { + return fmt.Errorf("%w: invalid type %d trailer", errInvalidDAF, segment.dataType) + } + components := 3 + if segment.dataType == 3 { + components = 6 + } + if (segment.recordSize-2)%components != 0 || (segment.recordSize-2)/components < 1 { + return fmt.Errorf("%w: invalid type %d record size %d", errInvalidDAF, segment.dataType, segment.recordSize) + } + return nil +} + +func chosenSegment(segment *summarySegment) (*summarySegment, error) { + if segment == nil { + return nil, ephem.ErrOutOfRange + } + if segment.frame != 1 { + return nil, ErrUnsupportedFrame + } + if segment.dataType != 2 && segment.dataType != 3 && segment.dataType != 20 && segment.dataType != 21 { + return nil, ErrUnsupportedType + } + return segment, nil +} + +type stateEdge struct { + to Body + segment *summarySegment + reverse bool +} + +func (s *File) composeState(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if !s.known[target] || !s.known[center] { + return pos, vel, ephem.ErrUnsupportedBody + } + // 一次倒序扫描求出每个目标在 jdTDB 处覆盖它的最后一段,后加载的段优先。 + chosen := make(map[Body]*summarySegment) + for index := len(s.segments) - 1; index >= 0; index-- { + segment := s.segments[index] + if _, ok := chosen[segment.target]; ok { + continue + } + if jdTDB >= spkJ2000+segment.startET/spkSecondsDay && jdTDB <= spkJ2000+segment.endET/spkSecondsDay { + chosen[segment.target] = segment + } + } + graph := make(map[Body][]stateEdge) + selected := make(map[Body]bool) + for index := len(s.segments) - 1; index >= 0; index-- { + segment := s.segments[index] + if selected[segment.target] { + continue + } + current, selectErr := chosenSegment(chosen[segment.target]) + if selectErr != nil { + // 覆盖缺口留到 BFS 结束再报:别的路径仍可能连通。 + if selectErr != ephem.ErrOutOfRange && (segment.target == target || segment.target == center) { + return pos, vel, selectErr + } + continue + } + segment = current + selected[segment.target] = true + graph[segment.target] = append(graph[segment.target], stateEdge{to: segment.center, segment: segment}) + graph[segment.center] = append(graph[segment.center], stateEdge{to: segment.target, segment: segment, reverse: true}) + } + type node struct { + body Body + pos [3]float64 + vel [3]float64 + } + queue := []node{{body: target}} + visited := map[Body]bool{target: true} + for head := 0; head < len(queue); head++ { + current := queue[head] + if current.body == center { + return current.pos, current.vel, nil + } + for _, edge := range graph[current.body] { + if visited[edge.to] { + continue + } + edgePos, edgeVel, edgeErr := edge.segment.state(jdTDB) + if edgeErr != nil { + return pos, vel, edgeErr + } + if edge.reverse { + negateVector(&edgePos) + negateVector(&edgeVel) + } + addVector(&edgePos, current.pos) + addVector(&edgeVel, current.vel) + visited[edge.to] = true + queue = append(queue, node{body: edge.to, pos: edgePos, vel: edgeVel}) + } + } + return pos, vel, ephem.ErrOutOfRange +} + +func (segment *summarySegment) state(jdTDB float64) (pos, vel [3]float64, err error) { + if segment.dataType == 20 { + return segment.stateType20(jdTDB) + } + if segment.dataType == 21 { + return segment.stateType21(jdTDB) + } + et := (jdTDB - spkJ2000) * spkSecondsDay + index := int(math.Floor((et - segment.initET) / segment.interval)) + if index < 0 { + index = 0 + } + if index >= segment.records { + index = segment.records - 1 + } + if jdTDB < spkJ2000+segment.startET/spkSecondsDay || jdTDB > spkJ2000+segment.endET/spkSecondsDay { + return pos, vel, ephem.ErrOutOfRange + } + record, err := segment.readRecord(index) + if err != nil { + return pos, vel, err + } + if !finite(record[0]) || !finitePositive(record[1]) || !finiteAll(record[2:]) { + return pos, vel, errInvalidDAF + } + x := (et - record[0]) / record[1] + tolerance := math.Abs(math.Nextafter(jdTDB, math.Inf(1))-jdTDB) * spkSecondsDay / record[1] + if x < -1-tolerance || x > 1+tolerance { + return pos, vel, ephem.ErrOutOfRange + } + x = math.Max(-1, math.Min(1, x)) + components := 3 + if segment.dataType == 3 { + components = 6 + } + coefficientCount := (segment.recordSize - 2) / components + for component := 0; component < 3; component++ { + value, derivative := chebyshev(record[2+component*coefficientCount:2+(component+1)*coefficientCount], x) + pos[component] = value + if segment.dataType == 2 { + vel[component] = derivative / record[1] * spkSecondsDay + } else { + velocityStart := 2 + (component+3)*coefficientCount + vel[component], _ = chebyshev(record[velocityStart:velocityStart+coefficientCount], x) + vel[component] *= spkSecondsDay + } + } + return pos, vel, nil +} + +func (segment *summarySegment) stateType20(jdTDB float64) (pos, vel [3]float64, err error) { + if jdTDB < spkJ2000+segment.startET/spkSecondsDay || jdTDB > spkJ2000+segment.endET/spkSecondsDay { + return pos, vel, ephem.ErrOutOfRange + } + et := (jdTDB - spkJ2000) * spkSecondsDay + init := (segment.initJD - spkJ2000 + segment.initFR) * spkSecondsDay + interval := segment.interval * spkSecondsDay + if !finitePositive(interval) { + return pos, vel, errInvalidDAF + } + index := int(math.Floor((et - init) / interval)) + if index < 0 { + index = 0 + } + if index >= segment.records { + index = segment.records - 1 + } + record, err := segment.readRecord(index) + if err != nil { + return pos, vel, err + } + nterms := segment.recordSize / 3 + coefficientCount := nterms - 1 + if len(record) != segment.recordSize || coefficientCount < 1 || !finiteAll(record) { + return pos, vel, errInvalidDAF + } + recbeg := (segment.initJD - spkJ2000 + float64(index)*segment.interval) * spkSecondsDay + radius := interval / 2 + mid := recbeg + segment.initFR*spkSecondsDay + radius + x := (et - mid) / radius + if !finite(x) || x < -1-1e-12 || x > 1+1e-12 { + return pos, vel, ephem.ErrOutOfRange + } + x = math.Max(-1, math.Min(1, x)) + velocityScale := segment.distanceScale / segment.timeScale + for component := 0; component < 3; component++ { + base := component * nterms + coefficients := record[base : base+coefficientCount] + value, _ := chebyshev(coefficients, x) + integral := chebyshevIntegral(coefficients, x) + pos[component] = record[base+coefficientCount]*segment.distanceScale + radius*integral*velocityScale + vel[component] = value * velocityScale * spkSecondsDay + } + return pos, vel, nil +} + +func (segment *summarySegment) stateType21(jdTDB float64) (pos, vel [3]float64, err error) { + if jdTDB < spkJ2000+segment.startET/spkSecondsDay || jdTDB > spkJ2000+segment.endET/spkSecondsDay { + return pos, vel, ephem.ErrOutOfRange + } + et := (jdTDB - spkJ2000) * spkSecondsDay + epochs, err := segment.owner.readWords(segment.epochStartWord, segment.records) + if err != nil { + return pos, vel, err + } + index := 0 + for index < len(epochs) && epochs[index] < et { + index++ + } + if index >= len(epochs) { + index = len(epochs) - 1 + } + record, err := segment.readRecord(index) + if err != nil { + return pos, vel, err + } + maxdim := segment.maxDim + record = append([]float64{float64(maxdim)}, record...) + pos, vel, err = evaluateType21(et, record) + for component := range vel { + vel[component] *= spkSecondsDay + } + return pos, vel, err +} + +func evaluateType21(et float64, record []float64) (statePos, stateVel [3]float64, err error) { + if len(record) < 72 { + return statePos, stateVel, errInvalidDAF + } + maxdim := int(math.Round(record[0])) + if maxdim < 15 || maxdim > 25 || len(record) != 4*maxdim+12 || !finiteAll(record) { + return statePos, stateVel, errInvalidDAF + } + tl := record[1] + g := record[2 : 2+maxdim] + refPos := [3]float64{record[maxdim+2], record[maxdim+4], record[maxdim+6]} + refVel := [3]float64{record[maxdim+3], record[maxdim+5], record[maxdim+7]} + kqmax1 := int(math.Round(record[4*maxdim+8])) + kq := [3]int{int(math.Round(record[4*maxdim+9])), int(math.Round(record[4*maxdim+10])), int(math.Round(record[4*maxdim+11]))} + // 差分表与步长表各只有 MAXDIM 项,权重表只有 KQMAX1+1 项。 + if kqmax1 < 3 || kqmax1 > maxdim+1 { + return statePos, stateVel, errInvalidDAF + } + for _, value := range g[:kqmax1-2] { + if !finite(value) || value == 0 { + return statePos, stateVel, errInvalidDAF + } + } + for _, value := range kq { + if value < 1 || value > kqmax1-1 { + return statePos, stateVel, errInvalidDAF + } + } + delta := et - tl + tp := delta + mq2 := kqmax1 - 2 + ks := kqmax1 - 1 + fc := make([]float64, kqmax1+1) + wc := make([]float64, kqmax1) + weights := make([]float64, kqmax1+2) + for j := 1; j <= mq2; j++ { + fc[j] = tp / g[j-1] + wc[j-1] = delta / g[j-1] + tp = delta + g[j-1] + } + for j := 1; j <= kqmax1; j++ { + weights[j-1] = 1 / float64(j) + } + jx := 0 + ks1 := ks - 1 + for ks >= 2 { + jx++ + for j := 1; j <= jx; j++ { + weights[j+ks-1] = fc[j]*weights[j+ks1-1] - wc[j-1]*weights[j+ks-1] + } + ks = ks1 + ks1-- + } + for component := 0; component < 3; component++ { + sum := 0.0 + for j := kq[component]; j >= 1; j-- { + sum += record[8+(component+1)*maxdim+j-1] * weights[j+ks-1] + } + statePos[component] = refPos[component] + delta*(refVel[component]+delta*sum) + } + for j := 1; j <= jx; j++ { + weights[j+ks-1] = fc[j]*weights[j+ks1-1] - wc[j-1]*weights[j+ks-1] + } + ks-- + for component := 0; component < 3; component++ { + sum := 0.0 + for j := kq[component]; j >= 1; j-- { + sum += record[8+(component+1)*maxdim+j-1] * weights[j+ks-1] + } + stateVel[component] = refVel[component] + delta*sum + } + return statePos, stateVel, nil +} + +func chebyshevIntegral(coefficients []float64, x float64) float64 { + degree := len(coefficients) - 1 + if degree < 0 { + return 0 + } + s2 := 2 * x + a2 := coefficients[0] + if len(coefficients) >= 3 { + a2 -= coefficients[2] * 0.5 + } + adegree1, adegree2 := 0.0, 0.0 + if degree >= 2 { + adegree1 = coefficients[degree-1] * 0.5 / float64(degree) + } + if degree >= 1 { + adegree2 = coefficients[degree] * 0.5 / float64(degree+1) + } + f2, f1 := 0.0, 0.0 + f0 := a2 + if degree > 0 { + f0 = adegree2 + } + z2, z1, z0 := 0.0, 0.0, f0 + w2, w1, w0 := 0.0, 0.0, 0.0 + for index := len(coefficients); index > 1; index-- { + var coefficient float64 + switch { + case index == 2: + coefficient = a2 + case index < len(coefficients): + coefficient = (coefficients[index-2] - coefficients[index]) * 0.5 / float64(index-1) + default: + coefficient = adegree1 + } + f2, f1 = f1, f0 + f0 = coefficient + (s2*f1 - f2) + z2, z1 = z1, z0 + z0 = coefficient - z2 + w2, w1 = w1, w0 + w0 = coefficients[index-1] + (s2*w1 - w2) + } + c0 := z1 + return c0 + x*f0 - f1 +} + +func (segment *summarySegment) readRecord(index int) ([]float64, error) { + if index < 0 || index >= segment.records { + return nil, ephem.ErrOutOfRange + } + segment.cacheMu.Lock() + if record, ok := segment.cache[index]; ok { + copyRecord := append([]float64(nil), record...) + segment.cacheMu.Unlock() + return copyRecord, nil + } + segment.cacheMu.Unlock() + record, err := segment.owner.readWords(segment.startWord+int64(index*segment.recordSize), segment.recordSize) + if err != nil { + return nil, err + } + segment.cacheMu.Lock() + defer segment.cacheMu.Unlock() + if previous, ok := segment.cache[index]; ok { + return append([]float64(nil), previous...), nil + } + if len(segment.cacheOrder) >= spkCacheRecords { + delete(segment.cache, segment.cacheOrder[0]) + segment.cacheOrder = segment.cacheOrder[1:] + } + segment.cache[index] = record + segment.cacheOrder = append(segment.cacheOrder, index) + return append([]float64(nil), record...), nil +} + +func chebyshev(coefficients []float64, x float64) (value, derivative float64) { + t0, t1 := 1.0, x + d0, d1 := 0.0, 1.0 + for index, coefficient := range coefficients { + switch index { + case 0: + value += coefficient + case 1: + value += coefficient * t1 + derivative += coefficient + default: + t2 := 2*x*t1 - t0 + d2 := 2*t1 + 2*x*d1 - d0 + value += coefficient * t2 + derivative += coefficient * d2 + t0, t1 = t1, t2 + d0, d1 = d1, d2 + } + } + return value, derivative +} + +func (s *File) readRecord(record int) ([]byte, error) { + if record < 1 || int64(record)*dafRecordBytes > s.size { + return nil, errInvalidDAF + } + data := make([]byte, dafRecordBytes) + if _, err := s.file.ReadAt(data, int64(record-1)*dafRecordBytes); err != nil { + return nil, err + } + return data, nil +} + +func (s *File) readWords(startWord int64, count int) ([]float64, error) { + if startWord < 1 || count < 1 || int64(count) > s.size/dafWordBytes || startWord > s.size/dafWordBytes-int64(count)+1 { + return nil, errInvalidDAF + } + data := make([]byte, count*dafWordBytes) + if _, err := s.file.ReadAt(data, (startWord-1)*dafWordBytes); err != nil { + return nil, err + } + words := make([]float64, count) + for index := range words { + words[index] = float64Value(s.order, data[index*dafWordBytes:(index+1)*dafWordBytes]) + } + return words, nil +} + +func byteOrderFor(format string) (binary.ByteOrder, error) { + switch format { + case "LTL-IEEE": + return binary.LittleEndian, nil + case "BIG-IEEE": + return binary.BigEndian, nil + default: + return nil, fmt.Errorf("%w: binary format %q", errInvalidDAF, format) + } +} + +func int32Value(order binary.ByteOrder, data []byte, offset int) int32 { + return int32(order.Uint32(data[offset : offset+4])) +} + +func float64Value(order binary.ByteOrder, data []byte) float64 { + return math.Float64frombits(order.Uint64(data)) +} + +func finitePositive(value float64) bool { + return value > 0 && finite(value) +} + +func finite(value float64) bool { return !math.IsNaN(value) && !math.IsInf(value, 0) } + +func finiteAll(values []float64) bool { + for _, value := range values { + if !finite(value) { + return false + } + } + return true +} + +func validInteger(value float64, minimum, maximum int64) bool { + return finite(value) && value >= float64(minimum) && value <= float64(maximum) && value == math.Trunc(value) +} + +func addVector(dst *[3]float64, src [3]float64) { + for index := range dst { + dst[index] += src[index] + } +} + +func negateVector(value *[3]float64) { + for index := range value { + value[index] = -value[index] + } +} + +var _ ephem.Engine = (*File)(nil) diff --git a/planet/spk/spk_test.go b/planet/spk/spk_test.go new file mode 100644 index 0000000..57ed066 --- /dev/null +++ b/planet/spk/spk_test.go @@ -0,0 +1,441 @@ +package spk + +import ( + "bufio" + "encoding/binary" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "b612.me/astro/internal/ephem" +) + +// de441Path 真实 DE441 核文件路径,环境变量 ASTRO_DE441_BSP 优先。 +func de441Path() string { + if path := os.Getenv("ASTRO_DE441_BSP"); path != "" { + return path + } + return "/tmp/de441data/de441_part-2.bsp" +} + +type testSPKSegment struct { + target Body + center Body + frame int + typ int + start float64 + end float64 + words []float64 +} + +func TestType2StateCoverageAndBoundary(t *testing.T) { + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", testSPKSegment{ + target: Mars, + center: Sun, + frame: 1, + typ: 2, + start: -10, + end: 10, + words: type2Words(-10, 10, + []float64{-5, 5, 10, 2.5, 0, 0, 20, 0}, + []float64{5, 5, 20, 2.5, 0, 0, 40, 0}, + ), + }) + spk, err := Open(path) + if err != nil { + t.Fatalf("open type21: %+v", err) + } + defer spk.Close() + start, end, ok := spk.Coverage(Mars) + if !ok || math.Abs(start-(spkJ2000-10/spkSecondsDay)) > 1e-12 || math.Abs(end-(spkJ2000+10/spkSecondsDay)) > 1e-12 { + t.Fatalf("coverage = %.15f..%.15f/%v", start, end, ok) + } + pos, vel, err := spk.State(Mars, Sun, spkJ2000-5/spkSecondsDay) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{10, 0, 20}) + assertVector(t, vel, [3]float64{43200, 0, 0}) + pos, _, err = spk.State(Mars, Sun, spkJ2000+10/spkSecondsDay) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{22.5, 0, 40}) + _, _, err = spk.State(Mars, Sun, spkJ2000+11/spkSecondsDay) + if err != ephem.ErrOutOfRange { + t.Fatalf("out-of-range error = %v", err) + } +} + +func TestType3BigEndianAndStateComposition(t *testing.T) { + path := writeTestSPK(t, binary.BigEndian, "BIG-IEEE", + testSPKSegment{ + target: Earth, + center: EMB, + frame: 1, + typ: 3, + start: -10, + end: 10, + words: type3Words(0, 10, []float64{1, 2, 3}, []float64{0.1, 0.2, 0.3}), + }, + testSPKSegment{ + target: EMB, + center: SSB, + frame: 1, + typ: 2, + start: -10, + end: 10, + words: type2Words(-10, 20, []float64{0, 10, 10, 20, 30}), + }, + ) + spk, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer spk.Close() + pos, vel, err := spk.State(Earth, SSB, spkJ2000) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{11, 22, 33}) + assertVector(t, vel, [3]float64{0.1 * spkSecondsDay, 0.2 * spkSecondsDay, 0.3 * spkSecondsDay}) + pos, vel, err = spk.State(SSB, Earth, spkJ2000) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{-11, -22, -33}) + assertVector(t, vel, [3]float64{-0.1 * spkSecondsDay, -0.2 * spkSecondsDay, -0.3 * spkSecondsDay}) +} + +func TestType20State(t *testing.T) { + intervalDays := 2 / spkSecondsDay + record := []float64{1, 10, 1, 20, 1, 30} + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", testSPKSegment{ + target: Mars, + center: Sun, + frame: 1, + typ: 20, + start: -1, + end: 1, + words: append(record, []float64{1, 1, spkJ2000, 0, intervalDays, 6, 1}...), + }) + spk, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer spk.Close() + pos, vel, err := spk.State(Mars, Sun, spkJ2000+1/spkSecondsDay) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{10, 20, 30}) + assertVector(t, vel, [3]float64{spkSecondsDay, spkSecondsDay, spkSecondsDay}) +} + +func TestType21State(t *testing.T) { + maxdim := 15 + full := make([]float64, 4*maxdim+12) + full[0] = 15 + full[1] = 0 + for index := 0; index < maxdim; index++ { + full[2+index] = 1 + } + full[maxdim+2] = 10 + full[maxdim+4] = 20 + full[maxdim+6] = 30 + full[maxdim+3] = 1 + full[maxdim+5] = 2 + full[maxdim+7] = 3 + full[8+maxdim] = 1 + full[8+2*maxdim] = 1 + full[8+3*maxdim] = 1 + full[4*maxdim+8] = 3 + full[4*maxdim+9] = 1 + full[4*maxdim+10] = 1 + full[4*maxdim+11] = 1 + words := append(append([]float64(nil), full[1:]...), 2, 15, 1) + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", testSPKSegment{ + target: Mars, + center: Sun, + frame: 1, + typ: 21, + start: 0, + end: 2, + words: words, + }) + spk, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer spk.Close() + pos, vel, err := spk.State(Mars, Sun, spkJ2000+2/spkSecondsDay) + if err != nil { + t.Fatal(err) + } + assertVectorTolerance(t, pos, [3]float64{14, 26, 38}, 1e-3) + assertVectorTolerance(t, vel, [3]float64{3 * spkSecondsDay, 4 * spkSecondsDay, 5 * spkSecondsDay}, 2) +} + +func TestLaterSegmentHasPriorityAndClose(t *testing.T) { + path := writeTestSPK(t, binary.LittleEndian, "LTL-IEEE", + testSPKSegment{target: Venus, center: Sun, frame: 1, typ: 2, start: -1, end: 1, words: type2Words(-1, 2, []float64{0, 1, 1, 0, 0})}, + testSPKSegment{target: Venus, center: Sun, frame: 1, typ: 2, start: -1, end: 1, words: type2Words(-1, 2, []float64{0, 2, 2, 0, 0})}, + ) + spk, err := Load(path) + if err != nil { + t.Fatal(err) + } + pos, _, err := spk.State(Venus, Sun, spkJ2000) + if err != nil { + t.Fatal(err) + } + assertVector(t, pos, [3]float64{2, 0, 0}) + if err := spk.Close(); err != nil { + t.Fatal(err) + } + if _, _, err := spk.State(Venus, Sun, spkJ2000); err != errClosed { + t.Fatalf("closed state error = %v", err) + } +} + +func TestOpenRejectsUnsupportedSPK(t *testing.T) { + path := filepath.Join(t.TempDir(), "bad.bsp") + if err := os.WriteFile(path, []byte("not an SPK"), 0o600); err != nil { + t.Fatal(err) + } + if _, err := Open(path); err == nil { + t.Fatal("invalid SPK was accepted") + } +} + +func type2Words(init, interval float64, records ...[]float64) []float64 { + if len(records) == 0 || len(records[0]) < 5 || (len(records[0])-2)%3 != 0 { + panic("invalid type2 test record") + } + recordSize := len(records[0]) + words := make([]float64, 0, recordSize*len(records)+4) + for _, record := range records { + if len(record) != recordSize { + panic("type2 record sizes differ") + } + words = append(words, record...) + } + words = append(words, init, interval, float64(recordSize), float64(len(records))) + return words +} + +func type3Words(mid, radius float64, position, velocity []float64) []float64 { + words := []float64{mid, radius} + words = append(words, position...) + words = append(words, velocity...) + words = append(words, mid-radius, radius*2, 2+6, 1) + return words +} + +func writeTestSPK(t *testing.T, order binary.ByteOrder, format string, segments ...testSPKSegment) string { + t.Helper() + const summaryRecord = 2 + const nameRecord = 3 + const firstDataRecord = 4 + wordsPerRecord := int64(dafRecordBytes / dafWordBytes) + dataWord := int64(firstDataRecord-1)*wordsPerRecord + 1 + type indexedSegment struct { + testSPKSegment + startWord int64 + endWord int64 + } + indexed := make([]indexedSegment, len(segments)) + for index, segment := range segments { + indexed[index] = indexedSegment{testSPKSegment: segment, startWord: dataWord} + dataWord += int64(len(segment.words)) + indexed[index].endWord = dataWord - 1 + } + dataRecords := int((dataWord-1+wordsPerRecord-1)/wordsPerRecord) + 1 + fileData := make([]byte, dataRecords*dafRecordBytes) + copy(fileData[0:8], []byte("DAF/SPK ")) + order.PutUint32(fileData[8:12], uint32(dafND)) + order.PutUint32(fileData[12:16], uint32(dafNI)) + copy(fileData[16:28], []byte("test SPK")) + order.PutUint32(fileData[76:80], summaryRecord) + order.PutUint32(fileData[80:84], summaryRecord) + order.PutUint32(fileData[84:88], uint32(dataWord)) + copy(fileData[88:96], []byte(format)) + summaryOffset := (summaryRecord - 1) * dafRecordBytes + putF64(order, fileData[summaryOffset:summaryOffset+8], 0) + putF64(order, fileData[summaryOffset+8:summaryOffset+16], 0) + putF64(order, fileData[summaryOffset+16:summaryOffset+24], float64(len(indexed))) + _ = nameRecord + for index, segment := range indexed { + offset := summaryOffset + 24 + index*dafSummaryBytes + putF64(order, fileData[offset:offset+8], segment.start) + putF64(order, fileData[offset+8:offset+16], segment.end) + putI32(order, fileData[offset+16:offset+20], int32(segment.target)) + putI32(order, fileData[offset+20:offset+24], int32(segment.center)) + putI32(order, fileData[offset+24:offset+28], int32(segment.frame)) + putI32(order, fileData[offset+28:offset+32], int32(segment.typ)) + putI32(order, fileData[offset+32:offset+36], int32(segment.startWord)) + putI32(order, fileData[offset+36:offset+40], int32(segment.endWord)) + for word, value := range segment.words { + putF64(order, fileData[(segment.startWord-1+int64(word))*dafWordBytes:(segment.startWord+int64(word))*dafWordBytes], value) + } + } + path := filepath.Join(t.TempDir(), "test.bsp") + if err := os.WriteFile(path, fileData, 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func putF64(order binary.ByteOrder, destination []byte, value float64) { + order.PutUint64(destination, math.Float64bits(value)) +} + +func putI32(order binary.ByteOrder, destination []byte, value int32) { + order.PutUint32(destination, uint32(value)) +} + +func assertVector(t *testing.T, got, want [3]float64) { + assertVectorTolerance(t, got, want, 1e-5) +} + +func assertVectorTolerance(t *testing.T, got, want [3]float64, tolerance float64) { + t.Helper() + for index := range got { + if math.Abs(got[index]-want[index]) > tolerance { + t.Fatalf("vector[%d] = %.12f, want %.12f", index, got[index], want[index]) + } + } +} + +// TestDE441AccuracyVsSkyfield 验收测试:读取真实 DE441 文件,对照 skyfield / acceptance test: read real DE441, compare with skyfield. +func TestDE441AccuracyVsSkyfield(t *testing.T) { + spkPath := de441Path() + if _, err := os.Stat(spkPath); os.IsNotExist(err) { + t.Skip("DE441 file not available; set ASTRO_DE441_BSP") + } + refPath := filepath.Join("testdata", "de441_ref.csv") + + spk, err := Open(spkPath) + if err != nil { + t.Fatal(err) + } + defer spk.Close() + + f, err := os.Open(refPath) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var maxPosErr, maxVelErr float64 + lineNum := 0 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + lineNum++ + parts := strings.Split(scanner.Text(), ",") + if len(parts) != 8 { + continue + } + code, _ := strconv.Atoi(parts[0]) + jd, _ := strconv.ParseFloat(parts[1], 64) + refPos := [3]float64{} + refVel := [3]float64{} + for i := 0; i < 3; i++ { + refPos[i], _ = strconv.ParseFloat(parts[2+i], 64) + refVel[i], _ = strconv.ParseFloat(parts[5+i], 64) + } + + pos, vel, err := spk.State(Body(code), Sun, jd) + if err != nil { + t.Fatalf("line %d: State(%d, Sun, %.1f) error: %v", lineNum, code, jd, err) + } + + for i := 0; i < 3; i++ { + dPos := math.Abs(pos[i] - refPos[i]) + dVel := math.Abs(vel[i] - refVel[i]) + if dPos > maxPosErr { + maxPosErr = dPos + } + if dVel > maxVelErr { + maxVelErr = dVel + } + } + } + + // 验收标准:位置 < 100 m,速度 < 0.1 mm/s / acceptance: position < 100 m, velocity < 0.1 mm/s. + if maxPosErr > 0.1 { + t.Errorf("max position error = %.6f km (> 100 m)", maxPosErr) + } + if maxVelErr > 0.01 { // 0.1 mm/s = 0.01 km/d + t.Errorf("max velocity error = %.9f km/d (> 0.1 mm/s)", maxVelErr) + } + t.Logf("max errors: position %.3e km, velocity %.3e km/d", maxPosErr, maxVelErr) +} + +func TestType20IntegralConstantVelocity(t *testing.T) { + if got, want := chebyshevIntegral([]float64{2}, 0.25), 0.5; got != want { + t.Fatalf("integral = %.17g, want %.17g", got, want) + } +} + +func TestType21SyntheticRecord(t *testing.T) { + maxdim := 15 + record := make([]float64, 4*maxdim+12) + record[0] = float64(maxdim) + record[1] = 0 + for index := 0; index < maxdim; index++ { + record[2+index] = 1 + } + record[maxdim+2] = 10 + record[maxdim+4] = 20 + record[maxdim+6] = 30 + record[maxdim+3] = 1 + record[maxdim+5] = 2 + record[maxdim+7] = 3 + record[4*maxdim+8] = 3 + record[4*maxdim+9] = 1 + record[4*maxdim+10] = 1 + record[4*maxdim+11] = 1 + for component := 0; component < 3; component++ { + record[8+(component+1)*maxdim] = 1 + } + gotPos, gotVel, err := evaluateType21(2, record) + if err != nil { + t.Fatal(err) + } + if gotPos != [3]float64{14, 26, 38} || gotVel != [3]float64{3, 4, 5} { + t.Fatalf("state = %v, %v", gotPos, gotVel) + } +} + +func TestHorizonsType21CeresAgainstCSPICE(t *testing.T) { + path := filepath.Join("testdata", "ceres.bsp") + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Skip("Horizons Ceres SPK is not available") + } + file, err := Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + tests := []struct { + jd float64 + pos [3]float64 + vel [3]float64 + }{ + {2461041.5, [3]float64{381172803.95163149, 202285004.74382541, 17811912.880624987}, [3]float64{-8.4262244193126747 * spkSecondsDay, 12.764612139351195 * spkSecondsDay, 7.7363904450095804 * spkSecondsDay}}, + {2461042.5, [3]float64{380442435.16916716, 203386620.60955107, 18480226.044434257}, [3]float64{-8.480449226845364 * spkSecondsDay, 12.735725686923541 * spkSecondsDay, 7.7338055274662123 * spkSecondsDay}}, + {2461043.5, [3]float64{379707384.25882173, 204485733.05701393, 19148311.683061387}, [3]float64{-8.5346068958188273 * spkSecondsDay, 12.706662654284424 * spkSecondsDay, 7.7311236408537631 * spkSecondsDay}}, + } + for _, test := range tests { + pos, vel, stateErr := file.State(Body(20000001), Sun, test.jd) + if stateErr != nil { + t.Fatal(stateErr) + } + assertVectorTolerance(t, pos, test.pos, 1e-6) + assertVectorTolerance(t, vel, test.vel, 1e-6) + } +} diff --git a/planet/spk/testdata/ceres.bsp b/planet/spk/testdata/ceres.bsp new file mode 100644 index 0000000000000000000000000000000000000000..079b9b89be9d00dcfbf03197081c1545bd135d46 GIT binary patch literal 69632 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 literal 0 HcmV?d00001 diff --git a/planet/spk/testdata/de441_ref.csv b/planet/spk/testdata/de441_ref.csv new file mode 100644 index 0000000..0a6d772 --- /dev/null +++ b/planet/spk/testdata/de441_ref.csv @@ -0,0 +1,45 @@ +1,2451545.000000,-19461726.355853721,-59927967.773480386,-29992772.679831419,3196367.296468922403,-736963.944484227919,-725165.666795568075 +1,2455197.500000,7615348.314506317,40349559.970724382,20764230.250799619,-4994527.427748897113,573399.385201107943,824151.435103199328 +1,2460409.250000,-59095102.744904339,-12208835.836776096,-397061.253618861,-80157.589996882845,-3500633.117726195138,-1861734.035449085291 +1,2470000.500000,-58423379.476459160,-21095218.873128951,-5219094.863423973,577984.675783590879,-3315398.069772230461,-1831093.688917552819 +1,2488069.500000,35715235.692744091,-45902347.318416558,-28220925.763680972,2657160.074483643752,2349521.284594707657,980587.628848225228 +2,2451545.000000,-107456494.052190557,-6922528.774882653,3686187.045620657,119396.680928361806,-2766339.512497388292,-1252094.584890355356 +2,2455197.500000,7980400.349791029,-98757537.297539294,-44937823.562456690,2997233.027447930072,261290.997697278799,-72100.389786119573 +2,2460409.250000,104033519.902185202,-26158564.735811118,-18352357.632673483,855986.988639243646,2649988.735226617660,1138248.387975588674 +2,2470000.500000,-68287385.053574130,-78212941.874442697,-30880999.369566523,2327074.364669556729,-1699613.802507086657,-912090.491982413805 +2,2488069.500000,102852993.216383740,33439527.000706431,8554308.473454434,-961949.957846128964,2585099.317701762542,1224659.527287552366 +4,2451545.000000,208048140.641832381,209619.173358767,-5529162.313239297,100454.898522694188,2066550.598105826415,945144.452117909910 +4,2455197.500000,-109157269.549356803,196726101.683266968,93181300.810612798,-1791776.105491590220,-709366.836053605075,-276972.610298371932 +4,2460409.250000,152691947.616703689,-126257132.256598845,-62030955.482422978,1496812.112169315806,1577887.720354233636,683358.677785717067 +4,2470000.500000,107630427.253999338,-162708449.197838932,-77528699.089063302,1876069.888584472705,1158239.285938726505,480780.601375605213 +4,2488069.500000,91203205.631215408,188672444.573695242,84102402.739245221,-1836876.649216917576,913745.130979382899,468367.767875669757 +5,2451545.000000,598567583.597928882,409386350.116554618,160894347.123006970,-683409.976292214124,879854.211247629719,393786.926219645888 +5,2455197.500000,674546437.387645483,-291887651.141048610,-141534852.950989753,475266.914842320664,989687.270147228497,412637.771498839953 +5,2460409.250000,435947579.421117783,563448248.670763493,230897554.998261392,-931739.974492124049,645272.707379632047,299261.391436264093 +5,2470000.500000,-538636165.009431601,531267619.259489059,240803282.070978582,-844445.853253175621,-664204.002779098344,-264128.109617178678 +5,2488069.500000,-803903147.176483989,-132064236.644811392,-37056634.120138355,174569.878011514316,-974561.869908771943,-421903.878750392585 +6,2451545.000000,958385124.219723463,923715658.081787348,340300865.572902262,-642126.700622105272,527832.834165259846,245629.459105411050 +6,2455197.500000,-1415832919.845060587,13356405.577122003,66480591.272885621,-67221.690704686960,-773182.582146253320,-316438.760555763729 +6,2460409.250000,1370501763.601192951,-421096086.748522639,-232938609.442695707,228454.155326918553,731091.306874577655,292130.717300834775 +6,2470000.500000,841483890.628529906,-1122639452.952589512,-500061931.231320977,642654.884433073457,444128.590101163660,155757.490514981735 +6,2488069.500000,-1369282076.754732370,-447490210.113554776,-125815618.898983285,220805.838523651590,-730244.951046228060,-311279.220554805885 +7,2451545.000000,2158975019.759225368,-1870910802.754762411,-849968734.846995473,400638.893986218667,368310.601333641505,155643.038094210089 +7,2455197.500000,2997435943.653147697,-194589174.388139933,-127611353.968718126,40958.326044318790,513239.595447454543,224198.539512870426 +7,2460409.250000,1789526966.016673088,2136360112.069344521,910337186.673249483,-471282.549542022636,302221.967622562137,139025.602688870422 +7,2470000.500000,-2690167831.288533211,437221284.444555461,229510953.261675596,-110923.075897150295,-556571.606659970246,-242188.063348572468 +7,2488069.500000,2822174470.747951508,911931163.314849496,359554751.563923001,-197081.774681179988,481963.416306072671,213875.698730149888 +8,2451545.000000,2515046428.529101849,-3437774104.132060051,-1469713457.826672316,385853.937104861659,249592.869656585477,92573.436759929973 +8,2455197.500000,3712593238.298039436,-2305923479.038077354,-1036254439.833165526,261633.561518639035,365043.385950069467,142918.714684926119 +8,2460409.250000,4466604507.374163628,-163850563.700708121,-178261172.000086695,19352.899797939608,437790.884244481393,178719.607153477846 +8,2470000.500000,2527937114.220953465,3422784599.666448116,1338029333.502541065,-390204.538922515116,245177.586645644769,110050.696193157419 +8,2488069.500000,-4347198940.227345467,1100540332.667645454,558700600.743709564,-130479.251811205046,-417745.715501652157,-167755.317465597502 +301,2451545.000000,-26790642.061618209,132490700.504490972,57480615.983234145,-2518622.958077146206,-491109.695287754876,-213988.565447278350 +301,2455197.500000,-26413263.099680163,133102337.661037028,57708299.928026907,-2664399.190042089205,-448878.810475415608,-203923.795661982935 +301,2460409.250000,-141274051.615975320,-44769623.371007383,-19403911.675677348,770024.379791575717,-2163063.666139910463,-928279.042105002794 +301,2470000.500000,52633568.632847480,-130780085.628255159,-56678008.516612209,2320825.419408109970,870837.598782564630,369012.737071698008 +301,2488069.500000,-23887291.531281494,133377636.959700242,57798398.255998142,-2617892.707621465903,-459650.174950940651,-207580.434036122402 +399,2451545.000000,-26499033.677430496,132757417.338345051,57556718.470540732,-2574224.070085791871,-433559.719480311556,-187954.024564559892 +399,2455197.500000,-26331886.663379308,132783019.476163641,57564916.131770104,-2572895.233377746306,-431404.139418599079,-186901.856015971338 +399,2460409.250000,-141614561.775911272,-44875366.929020450,-19451994.188401155,798726.151406769990,-2241899.490074577276,-971954.226801195182 +399,2470000.500000,52344709.055075817,-131003495.311840236,-56779941.691634409,2375381.176934503950,803200.719235604280,348196.328601448564 +399,2488069.500000,-23547771.928055670,133241770.144845873,57731796.154016882,-2581876.036687009037,-386210.308926678495,-167170.899428247882 diff --git a/saturn/api_default.go b/saturn/api_default.go new file mode 100644 index 0000000..c2fbe8b --- /dev/null +++ b/saturn/api_default.go @@ -0,0 +1,272 @@ +package saturn + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 土星升交点黄经 / ascending node longitude of Saturn. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 土星升交点黄经(n 变体) / n-parameterised ascending node longitude of Saturn. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 土星降交点黄经 / descending node longitude of Saturn. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 土星降交点黄经(n 变体) / n-parameterised descending node longitude of Saturn. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 土星视直径,单位角秒 / apparent Saturn diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 土星视直径(n 变体),单位角秒 / n-parameterised apparent Saturn diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastEasternQuadrature 上一次东方照,取 date 所在当地民用日 / previous eastern quadrature, on the local civil day of date. +func LastEasternQuadrature(date time.Time) time.Time { + return defaultModule.LastEasternQuadrature(date) +} + +// LastOpposition 上一次冲日,取 date 所在当地民用日 / previous opposition, on the local civil day of date. +func LastOpposition(date time.Time) time.Time { return defaultModule.LastOpposition(date) } + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastWesternQuadrature 上一次西方照,取 date 所在当地民用日 / previous western quadrature, on the local civil day of date. +func LastWesternQuadrature(date time.Time) time.Time { + return defaultModule.LastWesternQuadrature(date) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextEasternQuadrature 下一次东方照,取 date 所在当地民用日 / next eastern quadrature, on the local civil day of date. +func NextEasternQuadrature(date time.Time) time.Time { + return defaultModule.NextEasternQuadrature(date) +} + +// NextOpposition 下一次冲日,取 date 所在当地民用日 / next opposition, on the local civil day of date. +func NextOpposition(date time.Time) time.Time { return defaultModule.NextOpposition(date) } + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextWesternQuadrature 下一次西方照,取 date 所在当地民用日 / next western quadrature, on the local civil day of date. +func NextWesternQuadrature(date time.Time) time.Time { + return defaultModule.NextWesternQuadrature(date) +} + +// ParallacticAngle 土星视差角(天顶方向角) / Saturn parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 土星视差角(天顶方向角)(n 变体) / n-parameterised Saturn parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 土星物理观测参数 / physical observing parameters of Saturn. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 土星物理观测参数(n 变体) / n-parameterised physical observing parameters of Saturn. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// PhysicalSystemIII 土星 System III 物理观测参数 / Saturn System III physical observing parameters. +func PhysicalSystemIII(date time.Time) PhysicalInfo { return defaultModule.PhysicalSystemIII(date) } + +// PhysicalSystemIIIN 土星 System III 物理观测参数(n 变体) / n-parameterised Saturn System III physical observing parameters. +func PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { + return defaultModule.PhysicalSystemIIIN(date, n) +} + +// Ring 土星环观测参数 / Saturn ring observing parameters. +func Ring(date time.Time) RingInfo { return defaultModule.Ring(date) } + +// RingN 土星环观测参数(n 变体) / n-parameterised Saturn ring observing parameters. +func RingN(date time.Time, n int) RingInfo { return defaultModule.RingN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 土星视半径,单位角秒 / apparent Saturn semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 土星视半径(n 变体),单位角秒 / n-parameterised apparent Saturn semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/saturn/context.go b/saturn/context.go new file mode 100644 index 0000000..ae99d93 --- /dev/null +++ b/saturn/context.go @@ -0,0 +1,13 @@ +package saturn + +import ( + "b612.me/astro/internal/ephem" +) + +// Saturn 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Saturn struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Saturn { return &Saturn{inst: inst} } + +var defaultModule Saturn diff --git a/saturn/context_test.go b/saturn/context_test.go new file mode 100644 index 0000000..a834f31 --- /dev/null +++ b/saturn/context_test.go @@ -0,0 +1,96 @@ +package saturn + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastEasternQuadrature(date time.Time) time.Time + LastOpposition(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastWesternQuadrature(date time.Time) time.Time + NextConjunction(date time.Time) time.Time + NextEasternQuadrature(date time.Time) time.Time + NextOpposition(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextWesternQuadrature(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + PhysicalSystemIII(date time.Time) PhysicalInfo + PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo + Ring(date time.Time) RingInfo + RingN(date time.Time, n int) RingInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Saturn)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/saturn/diameter.go b/saturn/diameter.go index ed02c46..14bb41f 100644 --- a/saturn/diameter.go +++ b/saturn/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 土星视半径,单位角秒 / apparent Saturn semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Saturn) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 土星视半径(截断版),单位角秒 / truncated apparent Saturn semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 土星视半径(n 变体),单位角秒 / n-parameterised apparent Saturn semidiameter in arcseconds. +func (module *Saturn) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnSemidiameterN(basic.UTC2TT(jd), n) + return basic.SaturnSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 土星视直径,单位角秒 / apparent Saturn diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Saturn) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 土星视直径(截断版),单位角秒 / truncated apparent Saturn diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 土星视直径(n 变体),单位角秒 / n-parameterised apparent Saturn diameter in arcseconds. +func (module *Saturn) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnDiameterN(basic.UTC2TT(jd), n) + return basic.SaturnDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/saturn/doc.go b/saturn/doc.go index 1ddbcd7..f93b92d 100644 --- a/saturn/doc.go +++ b/saturn/doc.go @@ -1,3 +1,7 @@ // Package saturn 土星位置、升落、合冲、留、方照、相位、视直径、物理星历与土星环观测参数;角度单位为度,环长短轴单位为角秒。 // Package saturn covers Saturn position, rise/set, conjunction, opposition, station, quadrature, phase, diameter, physical ephemeris and ring parameters; angles are degrees and ring axes are arcseconds. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package saturn diff --git a/saturn/nodes.go b/saturn/nodes.go index 5c09c8d..8c1b431 100644 --- a/saturn/nodes.go +++ b/saturn/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 土星升交点黄经 / ascending node longitude of Saturn. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Saturn) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 土星升交点黄经(截断版) / truncated ascending node longitude of Saturn. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 土星升交点黄经(n 变体) / n-parameterised ascending node longitude of Saturn. +func (module *Saturn) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnAscendingNodeN(basic.UTC2TT(jd), n) + return basic.SaturnAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 土星降交点黄经 / descending node longitude of Saturn. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Saturn) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 土星降交点黄经(截断版) / truncated descending node longitude of Saturn. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 土星降交点黄经(n 变体) / n-parameterised descending node longitude of Saturn. +func (module *Saturn) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnDescendingNodeN(basic.UTC2TT(jd), n) + return basic.SaturnDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/saturn/parallactic.go b/saturn/parallactic.go index 37b7eeb..01df536 100644 --- a/saturn/parallactic.go +++ b/saturn/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 土星视差角(天顶方向角) / Saturn parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Saturn) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项土星视差角(天顶方向角) / truncated Saturn parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 土星视差角(天顶方向角)(n 变体) / n-parameterised Saturn parallactic angle. +func (module *Saturn) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/saturn/phase.go b/saturn/phase.go index bd1e5ef..495ff49 100644 --- a/saturn/phase.go +++ b/saturn/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Saturn) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.SaturnPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Saturn) PhaseAngleN(date time.Time, n int) float64 { + return basic.SaturnPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Saturn) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.SaturnIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Saturn) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.SaturnIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Saturn) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Saturn) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Saturn) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.SaturnBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Saturn) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.SaturnBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/saturn/physical.go b/saturn/physical.go index ed95d69..3a048d0 100644 --- a/saturn/physical.go +++ b/saturn/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 土星物理观测参数 / physical observing parameters of Saturn. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Saturn) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 土星物理观测参数(截断版) / truncated physical observing parameters of Saturn. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 土星物理观测参数(n 变体) / n-parameterised physical observing parameters of Saturn. +func (module *Saturn) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.SaturnPhysicalN(basic.UTC2TT(jd), n) + info := basic.SaturnPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, @@ -39,11 +39,11 @@ func PhysicalN(date time.Time, n int) PhysicalInfo { } // PhysicalSystemIII 土星 System III 物理观测参数 / Saturn System III physical observing parameters. -func PhysicalSystemIII(date time.Time) PhysicalInfo { - return Physical(date) +func (module *Saturn) PhysicalSystemIII(date time.Time) PhysicalInfo { + return module.Physical(date) } -// PhysicalSystemIIIN 土星 System III 物理观测参数(截断版) / truncated Saturn System III physical observing parameters. -func PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { - return PhysicalN(date, n) +// PhysicalSystemIIIN 土星 System III 物理观测参数(n 变体) / n-parameterised Saturn System III physical observing parameters. +func (module *Saturn) PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { + return module.PhysicalN(date, n) } diff --git a/saturn/ring.go b/saturn/ring.go index 5f69137..c2c4d80 100644 --- a/saturn/ring.go +++ b/saturn/ring.go @@ -24,14 +24,14 @@ type RingInfo struct { } // Ring 土星环观测参数 / Saturn ring observing parameters. -func Ring(date time.Time) RingInfo { - return RingN(date, -1) +func (module *Saturn) Ring(date time.Time) RingInfo { + return module.RingN(date, -1) } -// RingN 土星环观测参数(截断版) / truncated Saturn ring observing parameters. -func RingN(date time.Time, n int) RingInfo { +// RingN 土星环观测参数(n 变体) / n-parameterised Saturn ring observing parameters. +func (module *Saturn) RingN(date time.Time, n int) RingInfo { jd := calendar.Date2JD(date.UTC()) - earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis := basic.SaturnRingParametersN(basic.UTC2TT(jd), n) + earthLatitude, sunLatitude, positionAngle, deltaU, majorAxis, minorAxis := basic.SaturnRingParametersNWithInstance(basic.UTC2TT(jd), n, module.inst) return RingInfo{ EarthLatitude: earthLatitude, SunLatitude: sunLatitude, diff --git a/saturn/rise_set_atmosphere.go b/saturn/rise_set_atmosphere.go new file mode 100644 index 0000000..dcbb45b --- /dev/null +++ b/saturn/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package saturn + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 土星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Saturn) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.SaturnRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 土星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Saturn) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.SaturnSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 土星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 土星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/saturn/saturn.go b/saturn/saturn.go index dcee9d5..2a12257 100644 --- a/saturn/saturn.go +++ b/saturn/saturn.go @@ -1,11 +1,11 @@ package saturn import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回土星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Saturn at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Saturn) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentLo(basic.UTC2TT(jd)) + return basic.SaturnApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回土星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Saturn at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Saturn) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentBo(basic.UTC2TT(jd)) + return basic.SaturnApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回土星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Saturn at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Saturn) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentRa(basic.UTC2TT(jd)) + return basic.SaturnApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回土星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Saturn at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Saturn) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentDec(basic.UTC2TT(jd)) + return basic.SaturnApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回土星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Saturn at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Saturn) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentRaDec(basic.UTC2TT(jd)) + return basic.SaturnApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回土星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Saturn at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Saturn) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnMag(basic.UTC2TT(jd)) + return basic.SaturnMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回土星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Saturn to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Saturn) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthSaturnAway(basic.UTC2TT(jd)) + return basic.EarthSaturnAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回土星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Saturn to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Saturn) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(5, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(5, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Saturn) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnHeight(localJD, lon, lat, timezone) + return basic.SaturnHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Saturn) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Saturn) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnAzimuth(localJD, lon, lat, timezone) + return basic.SaturnAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Saturn) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnHourAngle(localJD, lon, timezone) + return basic.SaturnHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Saturn) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.SaturnCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.SaturnCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Saturn) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.SaturnRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.SaturnRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Saturn) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Saturn) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,114 +146,84 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.SaturnSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.SaturnSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Saturn) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Saturn) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastOpposition 上一次冲日 / previous opposition. -// -// 返回 date 当前或之前最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or before date, keeping date's time zone. -func LastOpposition(date time.Time) time.Time { +func (module *Saturn) LastOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnOppositionWithInstance(jde, module.inst), date.Location(), false) } // NextOpposition 下一次冲日 / next opposition. -// -// 返回 date 当前或之后最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or after date, keeping date's time zone. -func NextOpposition(date time.Time) time.Time { +func (module *Saturn) NextOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnOppositionWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Saturn) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Saturn) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Saturn) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Saturn) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // LastEasternQuadrature 上一次东方照 / previous eastern quadrature. -// -// 返回 date 当前或之前最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or before date, keeping date's time zone. -func LastEasternQuadrature(date time.Time) time.Time { +func (module *Saturn) LastEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextEasternQuadrature 下一次东方照 / next eastern quadrature. -// -// 返回 date 当前或之后最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or after date, keeping date's time zone. -func NextEasternQuadrature(date time.Time) time.Time { +func (module *Saturn) NextEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // LastWesternQuadrature 上一次西方照 / previous western quadrature. -// -// 返回 date 当前或之前最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or before date, keeping date's time zone. -func LastWesternQuadrature(date time.Time) time.Time { +func (module *Saturn) LastWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastSaturnWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastSaturnWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextWesternQuadrature 下一次西方照 / next western quadrature. -// -// 返回 date 当前或之后最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or after date, keeping date's time zone. -func NextWesternQuadrature(date time.Time) time.Time { +func (module *Saturn) NextWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextSaturnWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextSaturnWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Saturn) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.SaturnMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/saturn/truncated.go b/saturn/truncated.go index 0808ce7..0c9dda3 100644 --- a/saturn/truncated.go +++ b/saturn/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Saturn) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentLoN(basic.UTC2TT(jd), n) + return basic.SaturnApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Saturn) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentBoN(basic.UTC2TT(jd), n) + return basic.SaturnApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Saturn) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentRaN(basic.UTC2TT(jd), n) + return basic.SaturnApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Saturn) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentDecN(basic.UTC2TT(jd), n) + return basic.SaturnApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Saturn) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnApparentRaDecN(basic.UTC2TT(jd), n) + return basic.SaturnApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Saturn) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.SaturnMagN(basic.UTC2TT(jd), n) + return basic.SaturnMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Saturn) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthSaturnAwayN(basic.UTC2TT(jd), n) + return basic.EarthSaturnAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Saturn) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(5, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(5, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Saturn) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnHeightN(localJD, lon, lat, timezone, n) + return basic.SaturnHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Saturn) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Saturn) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnAzimuthN(localJD, lon, lat, timezone, n) + return basic.SaturnAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Saturn) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SaturnHourAngleN(localJD, lon, timezone, n) + return basic.SaturnHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Saturn) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.SaturnCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.SaturnCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Saturn) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.SaturnRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.SaturnRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Saturn) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Saturn) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.SaturnSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.SaturnSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/semantics_regression_test.go b/semantics_regression_test.go index 20127c0..6a76e64 100644 --- a/semantics_regression_test.go +++ b/semantics_regression_test.go @@ -189,7 +189,8 @@ func TestObservationZenithMatchesIndependentFormula(t *testing.T) { jde := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - tt := basic.UTC2TT(jde - timezone/24) + utcJD := jde - timezone/24 + tt := basic.UTC2TT(utcJD) checks := []struct { name string @@ -199,7 +200,7 @@ func TestObservationZenithMatchesIndependentFormula(t *testing.T) { }{ {"sun", sunTolerance, sun.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(sun.HourAngle(date, place.lon, place.lat), basic.HSunApparentDec(tt), place.lat)}, + topocentricZenith(sun.HourAngle(date, place.lon, place.lat), basic.HSunApparentRa(tt), basic.HSunApparentDec(tt), basic.EarthAway(tt), place.lat, place.lon, utcJD)}, {"moon", moonTolerance, moon.Zenith(date, place.lon, place.lat), zenithFromHourAngle(moon.HourAngle(date, place.lon, place.lat), moon.ApparentDec(date, place.lon, place.lat), place.lat)}, @@ -211,25 +212,25 @@ func TestObservationZenithMatchesIndependentFormula(t *testing.T) { zenithFromHourAngle(star.HourAngle(date, starRa, place.lon), starDec, place.lat)}, {"mercury", planetTolerance, mercury.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(mercury.HourAngle(date, place.lon), mercury.ApparentDec(date), place.lat)}, + topocentricZenith(mercury.HourAngle(date, place.lon), mercury.ApparentRa(date), mercury.ApparentDec(date), basic.EarthMercuryAway(tt), place.lat, place.lon, utcJD)}, {"venus", planetTolerance, venus.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(venus.HourAngle(date, place.lon), venus.ApparentDec(date), place.lat)}, + topocentricZenith(venus.HourAngle(date, place.lon), venus.ApparentRa(date), venus.ApparentDec(date), basic.EarthVenusAway(tt), place.lat, place.lon, utcJD)}, {"mars", planetTolerance, mars.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(mars.HourAngle(date, place.lon), mars.ApparentDec(date), place.lat)}, + topocentricZenith(mars.HourAngle(date, place.lon), mars.ApparentRa(date), mars.ApparentDec(date), basic.EarthMarsAway(tt), place.lat, place.lon, utcJD)}, {"jupiter", planetTolerance, jupiter.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(jupiter.HourAngle(date, place.lon), jupiter.ApparentDec(date), place.lat)}, + topocentricZenith(jupiter.HourAngle(date, place.lon), jupiter.ApparentRa(date), jupiter.ApparentDec(date), basic.EarthJupiterAway(tt), place.lat, place.lon, utcJD)}, {"saturn", planetTolerance, saturn.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(saturn.HourAngle(date, place.lon), saturn.ApparentDec(date), place.lat)}, + topocentricZenith(saturn.HourAngle(date, place.lon), saturn.ApparentRa(date), saturn.ApparentDec(date), basic.EarthSaturnAway(tt), place.lat, place.lon, utcJD)}, {"uranus", planetTolerance, uranus.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(uranus.HourAngle(date, place.lon), uranus.ApparentDec(date), place.lat)}, + topocentricZenith(uranus.HourAngle(date, place.lon), uranus.ApparentRa(date), uranus.ApparentDec(date), basic.EarthUranusAway(tt), place.lat, place.lon, utcJD)}, {"neptune", planetTolerance, neptune.Zenith(date, place.lon, place.lat), - zenithFromHourAngle(neptune.HourAngle(date, place.lon), neptune.ApparentDec(date), place.lat)}, + topocentricZenith(neptune.HourAngle(date, place.lon), neptune.ApparentRa(date), neptune.ApparentDec(date), basic.EarthNeptuneAway(tt), place.lat, place.lon, utcJD)}, } for _, tc := range checks { @@ -242,6 +243,12 @@ func TestObservationZenithMatchesIndependentFormula(t *testing.T) { } } +// topocentricZenith 由地心时角、地心视赤经赤纬与地心距(AU)先做站心视差修正,再独立求天顶距,单位度。 +func topocentricZenith(hourAngle, ra, dec, distanceAU, lat, lon, utcJD float64) float64 { + topocentricRA, topocentricDec := basic.TopocentricRaDec(ra, dec, lat, lon, utcJD, distanceAU, 0) + return zenithFromHourAngle(hourAngle+ra-topocentricRA, topocentricDec, lat) +} + // zenithFromHourAngle 由时角与赤纬按 cos z = sinφ·sinδ + cosφ·cosδ·cos H 独立求天顶距,单位度。 func zenithFromHourAngle(hourAngle, dec, lat float64) float64 { rad := math.Pi / 180 diff --git a/star/catalog/badfile_test.go b/star/catalog/badfile_test.go new file mode 100644 index 0000000..9bd16bc --- /dev/null +++ b/star/catalog/badfile_test.go @@ -0,0 +1,120 @@ +package catalog + +import ( + "bytes" + "encoding/binary" + "os" + "strings" + "testing" + "testing/fstest" +) + +func TestStellariumBadFiles(t *testing.T) { + data := mustFixture(t) + bigEndianMagic := make([]byte, 4) + binary.BigEndian.PutUint32(bigEndianMagic, stellariumMagic) + cases := map[string][]byte{ + "truncated": data[:len(data)-7], + "trailing": append(append([]byte(nil), data...), 0), + "bad magic": append([]byte{1, 2, 3, 4}, data[4:]...), + "short head": data[:20], + "big endian": append(bigEndianMagic, data[4:]...), + "bad version": append(append(append([]byte(nil), data[:8]...), 1, 0, 0, 0), data[12:]...), + } + for name, input := range cases { + if _, err := ReadStellariumStars(bytes.NewReader(input), ReadOptions{Strict: true}); err == nil { + t.Fatalf("%s: strict read should fail", name) + } + } + loose, err := ReadStellariumStars(bytes.NewReader(cases["truncated"]), ReadOptions{}) + if err != nil || loose.Skipped() != 1 || loose.Len() == 0 { + t.Fatalf("loose truncated read = %d entries, %d skipped, %v", loose.Len(), loose.Skipped(), err) + } +} + +func TestStellariumMaxMagnitude(t *testing.T) { + data := mustFixture(t) + dir := fstest.MapFS{"stars_0_0v0_18.cat": &fstest.MapFile{Data: data}} + all, err := ReadStellariumDir(dir, StellariumOptions{}, ReadOptions{Strict: true}) + if err != nil { + t.Fatal(err) + } + bright, err := ReadStellariumDir(dir, StellariumOptions{MaxMagnitude: 0.5}, ReadOptions{Strict: true}) + if err != nil { + t.Fatal(err) + } + if bright.Len() == 0 || bright.Len() >= all.Len() { + t.Fatalf("MaxMagnitude 0.5 kept %d of %d", bright.Len(), all.Len()) + } + for _, entry := range bright.Entries() { + if entry.Magnitude > 0.5 { + t.Fatalf("%s V %.2f exceeds limit", entry.ID, entry.Magnitude) + } + } + // 文件头 mag_min = -2.00,阈值更亮时整片跳过。 + none, err := ReadStellariumDir(dir, StellariumOptions{MaxMagnitude: -3}, ReadOptions{Strict: true}) + if err != nil || none.Len() != 0 { + t.Fatalf("whole shard should be skipped: %d, %v", none.Len(), err) + } +} + +func TestCrossIDBadLength(t *testing.T) { + data := mustFixture(t) + crossID, err := os.ReadFile("testdata/cross-id.cat") + if err != nil { + t.Fatal(err) + } + dir := fstest.MapFS{ + "stars_0_0v0_18.cat": &fstest.MapFile{Data: data}, + "cross-id.cat": &fstest.MapFile{Data: crossID[:len(crossID)-3]}, + } + if _, err := ReadStellariumDir(dir, StellariumOptions{CrossID: true}, ReadOptions{Strict: true}); err == nil { + t.Fatal("cross-id length not a multiple of 21 should fail in strict mode") + } +} + +func TestCDSBadLines(t *testing.T) { + if _, err := ReadHipparcos2007(strings.NewReader(" 1234 garbage\n"), ReadOptions{Strict: true}); err == nil { + t.Fatal("short hip2 line should fail in strict mode") + } + loose, err := ReadHipparcos2007(strings.NewReader(" 1234 garbage\n"), ReadOptions{}) + if err != nil || loose.Len() != 0 || loose.Skipped() != 1 { + t.Fatalf("loose hip2 = %d entries, %d skipped, %v", loose.Len(), loose.Skipped(), err) + } +} + +// hipParallaxLine 造一条最小可解析的 hip2 定宽记录,parallax 为 43..50 列的原始文本。 +func hipParallaxLine(parallax string) string { + line := []byte(strings.Repeat(" ", 160)) + put := func(value string, from, to int) { copy(line[from:to], value) } + put(" 42", 0, 6) + put(" 1.00000000", 15, 28) + put(" -0.20000000", 29, 42) + put(parallax, 43, 50) + put(" -1.00000", 51, 59) + put(" -2.00000", 60, 68) + put(" -1.4600", 129, 136) + put(" 0.000", 152, 158) + return string(line) +} + +func TestHipparcosNonFiniteParallax(t *testing.T) { + for _, parallax := range []string{" +Inf", " -Inf"} { + input := strings.NewReader(hipParallaxLine(parallax) + "\n") + if _, err := ReadHipparcos2007(input, ReadOptions{Strict: true}); err == nil { + t.Fatalf("parallax %q: strict mode should reject a non-finite parallax", parallax) + } + loose, err := ReadHipparcos2007(strings.NewReader(hipParallaxLine(parallax)+"\n"), ReadOptions{}) + if err != nil || loose.Len() != 0 || loose.Skipped() != 1 { + t.Fatalf("parallax %q: loose = %d entries, %d skipped, %v", parallax, loose.Len(), loose.Skipped(), err) + } + } + // 有限的负视差是“按无视差处理”,不能一起被拒。 + fine, err := ReadHipparcos2007(strings.NewReader(hipParallaxLine(" -1.0")+"\n"), ReadOptions{Strict: true}) + if err != nil || fine.Len() != 1 { + t.Fatalf("finite negative parallax should load: len=%d err=%v", fine.Len(), err) + } + if entry, ok := fine.Lookup("HIP 42"); !ok || entry.Coordinate.ParallaxMas != 0 { + t.Fatalf("finite negative parallax should become 0: %v %v", entry.Coordinate.ParallaxMas, ok) + } +} diff --git a/star/catalog/catalog.go b/star/catalog/catalog.go new file mode 100644 index 0000000..62c5488 --- /dev/null +++ b/star/catalog/catalog.go @@ -0,0 +1,165 @@ +package catalog + +import ( + "math" + "sort" + "strings" +) + +// Near 返回星表历元下的锥形邻域,按角距由近到远 / cone search at catalog epochs, nearest first. +func (c *Catalog) Near(ra, dec, radius float64) []Entry { + if c == nil || !finite(ra) || !finite(dec) || !finite(radius) || radius < 0 || dec < -90 || dec > 90 { + return nil + } + ra = normalizeRA(ra) + // 锥面不含天极时,小圆的 RA 半宽恰为 asin(sin r / cos δ),且 |δ|+r < 90 保证比值 < 1;含天极时整带扫描。 + halfWidth := 180.0 + if math.Abs(dec)+radius < 90 { + halfWidth = math.Asin(math.Sin(radius*math.Pi/180)/math.Cos(dec*math.Pi/180)) * 180 / math.Pi + } + type hit struct { + index int + distance float64 + } + hits := make([]hit, 0) + collect := func(indexes []int) { + for _, index := range indexes { + coordinate := c.entries[index].Coordinate + if distance := angularDistanceDeg(ra, dec, coordinate.RA, coordinate.Dec); distance <= radius+1e-12 { + hits = append(hits, hit{index: index, distance: distance}) + } + } + } + for band := decBand(dec - radius); band <= decBand(dec+radius); band++ { + indexes := c.bands[band] + if halfWidth >= 180 { + collect(indexes) + continue + } + low, high := ra-halfWidth-1e-9, ra+halfWidth+1e-9 + collect(c.raRange(indexes, math.Max(low, 0), math.Min(high, 360))) + if low < 0 { + collect(c.raRange(indexes, low+360, 360)) + } + if high > 360 { + collect(c.raRange(indexes, 0, high-360)) + } + } + sort.SliceStable(hits, func(i, j int) bool { return hits[i].distance < hits[j].distance }) + result := make([]Entry, len(hits)) + for i, item := range hits { + result[i] = cloneEntry(c.entries[item.index]) + } + return result +} + +// raRange 返回带内 RA ∈ [low, high] 的下标切片;带内已按 RA 升序。 +func (c *Catalog) raRange(indexes []int, low, high float64) []int { + start := sort.Search(len(indexes), func(i int) bool { return c.entries[indexes[i]].Coordinate.RA >= low }) + end := sort.Search(len(indexes), func(i int) bool { return c.entries[indexes[i]].Coordinate.RA > high }) + if start >= end { + return nil + } + return indexes[start:end] +} + +// Band 返回赤纬带内的记录 / records in a declination band. +func (c *Catalog) Band(decMin, decMax float64) []Entry { + if c == nil || !finite(decMin) || !finite(decMax) || decMin > decMax || decMax < -90 || decMin > 90 { + return nil + } + if decMin < -90 { + decMin = -90 + } + if decMax > 90 { + decMax = 90 + } + result := make([]Entry, 0) + for band := decBand(decMin); band <= decBand(decMax); band++ { + for _, index := range c.bands[band] { + entry := c.entries[index] + if entry.Coordinate.Dec >= decMin && entry.Coordinate.Dec <= decMax { + result = append(result, cloneEntry(entry)) + } + } + } + return result +} + +// EclipticBandFilter 创建按 J2000 平黄道纬度过滤的函数 / build a mean-ecliptic latitude filter. +func EclipticBandFilter(halfWidthDeg float64) func(Entry) bool { + if !finite(halfWidthDeg) || halfWidthDeg < 0 { + return func(Entry) bool { return false } + } + if halfWidthDeg >= 90 { + return func(Entry) bool { return true } + } + const obliquity = 23.439291111 + return func(entry Entry) bool { + ra := entry.Coordinate.RA * math.Pi / 180 + dec := entry.Coordinate.Dec * math.Pi / 180 + eps := obliquity * math.Pi / 180 + beta := math.Asin(math.Sin(dec)*math.Cos(eps) - math.Cos(dec)*math.Sin(eps)*math.Sin(ra)) + return math.Abs(beta*180/math.Pi) <= halfWidthDeg + } +} + +// MagnitudeFilter 创建按 V 星等过滤的函数 / build a V-magnitude filter. +func MagnitudeFilter(maxV float64) func(Entry) bool { + if !finite(maxV) { + return func(Entry) bool { return false } + } + return func(entry Entry) bool { + value, ok := VMagnitude(entry) + return ok && value <= maxV + } +} + +// VMagnitude 将支持的波段换算为 V / convert supported bands to V magnitude. +func VMagnitude(entry Entry) (float64, bool) { + if !finite(entry.Magnitude) { + return 0, false + } + switch strings.ToUpper(string(entry.Band)) { + case "V": + return entry.Magnitude, true + case "G": + if !finite(entry.BPRP) || entry.BPRP <= -0.5 || entry.BPRP >= 5.0 { + return 0, false + } + x := entry.BPRP + gMinusV := -0.02704 + 0.01424*x - 0.2156*x*x + 0.01426*x*x*x + return entry.Magnitude - gMinusV, true + default: + return 0, false + } +} + +func angularDistanceDeg(ra1, dec1, ra2, dec2 float64) float64 { + const rad = math.Pi / 180 + lat1, lat2 := dec1*rad, dec2*rad + dra := math.Abs((ra1 - ra2) * rad) + if dra > math.Pi { + dra = 2*math.Pi - dra + } + value := math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(dra) + if value > 1 { + value = 1 + } + if value < -1 { + value = -1 + } + return math.Acos(value) / rad +} + +func normalizeRA(value float64) float64 { + value = math.Mod(value, 360) + if value < 0 { + value += 360 + } + return value +} + +func finite(value float64) bool { + return !math.IsNaN(value) && !math.IsInf(value, 0) +} diff --git a/star/catalog/catalog_test.go b/star/catalog/catalog_test.go new file mode 100644 index 0000000..0097f12 --- /dev/null +++ b/star/catalog/catalog_test.go @@ -0,0 +1,256 @@ +package catalog + +import ( + "math" + "os" + "strings" + "testing" + "testing/fstest" + + "b612.me/astro/basic" +) + +func TestReadCSVAndFilters(t *testing.T) { + input := "source_id,ra,dec,pmra,pmdec,parallax,radial_velocity,phot_g_mean_mag,bp_rp\n" + + "1,360,0,1,2,-3,,5,1\n" + + "2,1,91,1,2,3,0,6,1\n" + catalog, err := ReadCSV(strings.NewReader(input), GaiaDR3Columns(), ReadOptions{}) + if err != nil { + t.Fatal(err) + } + if catalog.Len() != 1 || catalog.Skipped() != 1 { + t.Fatalf("len/skipped = %d/%d", catalog.Len(), catalog.Skipped()) + } + entry, ok := catalog.Lookup("gaia dr3 1") + if !ok || entry.Coordinate.RA != 0 || entry.Coordinate.ParallaxMas != 0 || entry.Coordinate.RadialVelocityKmPerSecond != 0 { + t.Fatalf("unexpected entry: %#v", entry) + } + if !math.IsNaN(entry.BV) { + t.Fatalf("missing B-V = %v", entry.BV) + } + if value, ok := VMagnitude(entry); !ok || math.Abs(value-5.21414) > 1e-4 { + t.Fatalf("V magnitude = %v, %v", value, ok) + } +} + +func TestReadCSVStreamsQuotedNewlinesAndComments(t *testing.T) { + input := "\ufeff# exported\n\nid,ra,dec,name\n" + + "a,10,20,\"two\nlines\"\n" + + "# note\n// note\n" + + "b,350,-20,plain\n" + columns := ColumnMap{ID: "id", RA: "ra", Dec: "dec", Epoch: julianYearTime(2000), Frame: basic.CoordinateFrameICRS} + catalog, err := ReadCSV(strings.NewReader(input), columns, ReadOptions{}) + if err != nil { + t.Fatal(err) + } + if catalog.Len() != 2 || catalog.Skipped() != 0 { + t.Fatalf("len/skipped = %d/%d", catalog.Len(), catalog.Skipped()) + } + _, err = ReadCSV(strings.NewReader("id,ra,dec\na,1,2\nb,x\"y,3\n"), columns, ReadOptions{Strict: true}) + if err == nil || !strings.Contains(err.Error(), "line 3") { + t.Fatalf("strict parse error = %v", err) + } +} + +func TestReadCSVReportsRecordStartLine(t *testing.T) { + columns := ColumnMap{ID: "id", RA: "ra", Dec: "dec", Epoch: julianYearTime(2000), Frame: basic.CoordinateFrameICRS} + cases := []struct { + input string + want string + }{ + {"id,ra,dec,name\na,1,2,\"x\ny\nz\"\n,3,4,q\n", "line 5:"}, + {"# c\n\nid,ra,dec,name\r\na,1,2,\"x\r\ny\"\r\n# note\r\n\r\nb,bad,4,q\r\n", "line 8:"}, + {"id,ra,dec,name\n\"m\nn\",1,2,p\n\"k\nj\",bad,3,q\n", "line 4:"}, + {"id,ra,dec,name\na,1,2," + strings.Repeat("w", 200000) + "\nb,1,2,\"s\nt\"\n,1,2,q\n", "line 5:"}, + } + for _, c := range cases { + _, err := ReadCSV(strings.NewReader(c.input), columns, ReadOptions{Strict: true}) + if err == nil || !strings.HasPrefix(err.Error(), c.want) { + t.Errorf("err = %v, want prefix %q", err, c.want) + } + } +} + +func TestCSVStrictAndCosineConvention(t *testing.T) { + columns := ColumnMap{ID: "id", RA: "ra", Dec: "dec", PMRA: "pmra", PMDec: "pmdec", Epoch: GaiaDR3Columns().Epoch, Frame: basic.CoordinateFrameICRS} + withoutCos, err := ReadCSV(strings.NewReader("id,ra,dec,pmra,pmdec\npolar,0,80,10,2\n"), columns, ReadOptions{}) + if err != nil { + t.Fatal(err) + } + columns.PMRAIncludesCosDec = true + withCos, err := ReadCSV(strings.NewReader("id,ra,dec,pmra,pmdec\npolar,0,80,1.7364818,2\n"), columns, ReadOptions{}) + if err != nil { + t.Fatal(err) + } + if math.Abs(withoutCos.Entries()[0].Coordinate.ProperMotionRACosDecMasPerYear-withCos.Entries()[0].Coordinate.ProperMotionRACosDecMasPerYear) > 1e-5 { + t.Fatal("pmRA cosine convention differs") + } + _, err = ReadCSV(strings.NewReader("id,ra,dec\nx,0,91\n"), columns, ReadOptions{Strict: true}) + if err == nil || !strings.Contains(err.Error(), "record 2") { + t.Fatalf("strict error = %v", err) + } +} + +func TestCatalogSearchAndMerge(t *testing.T) { + base := newCatalog() + base.add(Entry{ID: "A", Aliases: []string{"Alpha"}, Coordinate: basic.StarCoordinate{ID: "A", RA: 359.9, Dec: 0, Epoch: GaiaDR3Columns().Epoch, Frame: basic.CoordinateFrameICRS}}) + base.finalize() + other := newCatalog() + other.add(Entry{ID: "A", Aliases: []string{"Beta"}, Coordinate: base.entries[0].Coordinate}) + other.finalize() + merged := base.Merge(other) + if _, ok := merged.Lookup("beta"); !ok { + t.Fatal("merged alias missing") + } + if got := merged.Near(0, 0, 1); len(got) != 1 { + t.Fatalf("near across zero = %d", len(got)) + } + if got := merged.Band(-1, 1); len(got) != 1 { + t.Fatalf("band = %d", len(got)) + } +} + +func TestMergeKeepsComponents(t *testing.T) { + coordinate := basic.StarCoordinate{ID: "HIP 1", RA: 10, Dec: 0, Epoch: GaiaDR3Columns().Epoch, Frame: basic.CoordinateFrameICRS} + first := newCatalog() + first.add(Entry{ID: "HIP 1", Component: "A", Magnitude: 1, Coordinate: coordinate}) + first.finalize() + second := newCatalog() + second.add(Entry{ID: "HIP 1", Component: "B", Magnitude: 8, Coordinate: coordinate}) + second.finalize() + merged := first.Merge(second) + if merged.Len() != 2 { + t.Fatalf("merged len = %d", merged.Len()) + } + if entry, ok := merged.Lookup("HIP 1"); !ok || entry.Component != "A" { + t.Fatalf("lookup component = %q", entry.Component) + } +} + +func TestReadStellariumFixture(t *testing.T) { + file, err := os.Open("testdata/stars_0_0v0_18.cat") + if err != nil { + t.Fatal(err) + } + defer file.Close() + catalog, err := ReadStellariumStars(file, ReadOptions{Strict: true}) + if err != nil { + t.Fatal(err) + } + checks := map[string][4]float64{ + "HIP 32349": {101.284621, -16.721552, -546.06, -1223.15}, + "HIP 91262": {279.235881, 38.784961, 0, 0}, + "HIP 24436": {78.634473, -8.201636, 0, 0}, + } + for id, want := range checks { + entry, ok := catalog.Lookup(id) + if !ok { + t.Fatalf("missing %s", id) + } + got := entry.Coordinate + if math.Abs(got.RA-want[0]) > 1e-6 || math.Abs(got.Dec-want[1]) > 1e-6 { + t.Fatalf("%s position = %.9f %.9f", id, got.RA, got.Dec) + } + if want[2] != 0 && math.Abs(got.ProperMotionRACosDecMasPerYear-want[2]) > 0.02 { + t.Fatalf("%s pmRA = %.6f", id, got.ProperMotionRACosDecMasPerYear) + } + } +} + +func TestCrossIDLengthAndStellariumDir(t *testing.T) { + fixture := func(name string) []byte { + data, err := os.ReadFile("testdata/" + name) + if err != nil { + t.Fatal(err) + } + return data + } + catalog, err := ReadStellariumDir(fstest.MapFS{ + "stars_0_0v0_18.cat": &fstest.MapFile{Data: fixture("stars_0_0v0_18.cat")}, + "name.fab": &fstest.MapFile{Data: fixture("name.fab")}, + "extra_name.fab": &fstest.MapFile{Data: fixture("extra_name.fab")}, + "cross-id.cat": &fstest.MapFile{Data: fixture("cross-id.cat")}, + }, StellariumOptions{Names: true, CrossID: true}, ReadOptions{Strict: true}) + if err != nil { + t.Fatal(err) + } + entry, ok := catalog.Lookup("HIP 32349") + if !ok || !contains(entry.Aliases, "SAO 151881") || !contains(entry.Aliases, "α CMa") { + t.Fatalf("aliases missing: %#v", entry.Aliases) + } +} + +func contains(values []string, want string) bool { + for _, value := range values { + if value == want { + return true + } + } + return false +} + +func mustFixture(t *testing.T) []byte { + t.Helper() + data, err := os.ReadFile("testdata/stars_0_0v0_18.cat") + if err != nil { + t.Fatal(err) + } + return data +} + +func TestReadCSVRejectsNonFiniteMotion(t *testing.T) { + header := "source_id,ra,dec,pmra,pmdec,parallax,radial_velocity,phot_g_mean_mag,bp_rp\n" + for _, row := range []string{"1,10,0,1,2,3,+Inf,5,1\n", "1,10,0,1,2,3,-Inf,5,1\n", "1,10,0,1,2,+Inf,0,5,1\n", "1,10,0,1,2,3,1e6,5,1\n"} { + if _, err := ReadCSV(strings.NewReader(header+row), GaiaDR3Columns(), ReadOptions{Strict: true}); err == nil { + t.Errorf("strict read accepted %q", row) + } + catalog, err := ReadCSV(strings.NewReader(header+row), GaiaDR3Columns(), ReadOptions{}) + if err != nil || catalog.Len() != 0 || catalog.Skipped() != 1 { + t.Errorf("lenient read of %q: len %d skipped %d err %v, want the record skipped", row, catalog.Len(), catalog.Skipped(), err) + } + } +} + +func TestReadCSVDropsDuplicateIDs(t *testing.T) { + input := "source_id,ra,dec,pmra,pmdec,parallax,radial_velocity,phot_g_mean_mag,bp_rp\n" + + "1,10,5,0,0,1,,5,1\n" + + "1,20,6,0,0,1,,6,1\n" + + "2,30,7,0,0,1,,7,1\n" + catalog, err := ReadCSV(strings.NewReader(input), GaiaDR3Columns(), ReadOptions{}) + if err != nil { + t.Fatal(err) + } + if catalog.Len() != 2 || catalog.Skipped() != 1 { + t.Fatalf("len/skipped = %d/%d", catalog.Len(), catalog.Skipped()) + } + entry, ok := catalog.Lookup("gaia dr3 1") + if !ok || entry.Coordinate.RA != 10 { + t.Fatalf("lookup kept %#v", entry) + } + if near := catalog.Near(20, 6, 0.5); len(near) != 0 { + t.Fatalf("dropped duplicate still reachable through Near: %#v", near) + } +} + +func TestCSVNonFiniteParallax(t *testing.T) { + columns := ColumnMap{ID: "id", RA: "ra", Dec: "dec", Parallax: "plx", + Epoch: julianYearTime(2000), Frame: basic.CoordinateFrameICRS} + for _, parallax := range []string{"+Inf", "-Inf"} { + input := "id,ra,dec,plx\nx,10,20," + parallax + "\n" + if _, err := ReadCSV(strings.NewReader(input), columns, ReadOptions{Strict: true}); err == nil { + t.Fatalf("parallax %q: strict mode should reject a non-finite parallax", parallax) + } + loose, err := ReadCSV(strings.NewReader(input), columns, ReadOptions{}) + if err != nil || loose.Len() != 0 || loose.Skipped() != 1 { + t.Fatalf("parallax %q: loose = %d entries, %d skipped, %v", parallax, loose.Len(), loose.Skipped(), err) + } + } + // 有限的负视差按无视差处理,仍然入库。 + fine, err := ReadCSV(strings.NewReader("id,ra,dec,plx\nx,10,20,-1.5\n"), columns, ReadOptions{Strict: true}) + if err != nil || fine.Len() != 1 { + t.Fatalf("finite negative parallax should load: len=%d err=%v", fine.Len(), err) + } + if entry, ok := fine.Lookup("x"); !ok || entry.Coordinate.ParallaxMas != 0 { + t.Fatalf("finite negative parallax should become 0: %v %v", entry.Coordinate.ParallaxMas, ok) + } +} diff --git a/star/catalog/cds.go b/star/catalog/cds.go new file mode 100644 index 0000000..463db8a --- /dev/null +++ b/star/catalog/cds.go @@ -0,0 +1,244 @@ +package catalog + +import ( + "bufio" + "fmt" + "io" + "math" + "strings" + "time" + + "b612.me/astro/basic" +) + +// ReadHipparcos2007 读取 Hipparcos 定宽星表 / read the Hipparcos fixed-width catalog. +func ReadHipparcos2007(r io.Reader, options ReadOptions) (*Catalog, error) { + catalog := newCatalog() + scanner := bufio.NewScanner(r) + scanner.Buffer(make([]byte, 64*1024), 2*1024*1024) + lineNumber := 0 + for scanner.Scan() { + lineNumber++ + line := scanner.Bytes() + if isIgnorableFixedLine(line) { + continue + } + entry, err := hipparcosEntry(line, lineNumber) + if err != nil { + if options.Strict { + return nil, err + } + catalog.skipped++ + continue + } + if err := addEntry(catalog, entry, lineNumber, options); err != nil { + return nil, err + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + catalog.finalize() + return catalog, nil +} + +func hipparcosEntry(line []byte, lineNumber int) (Entry, error) { + hip, present, err := fixedInt(line, 0, 6) + if err != nil || !present || hip <= 0 { + return Entry{}, fixedError(lineNumber, "invalid HIP") + } + ra, present, err := fixedFloat(line, 15, 28) + if err != nil || !present { + return Entry{}, fixedError(lineNumber, "invalid RArad") + } + dec, present, err := fixedFloat(line, 29, 42) + if err != nil || !present { + return Entry{}, fixedError(lineNumber, "invalid DErad") + } + parallax, _, err := fixedFloat(line, 43, 50) + if err != nil { + return Entry{}, fixedError(lineNumber, "invalid Plx") + } + pmRA, _, err := fixedFloat(line, 51, 59) + if err != nil { + return Entry{}, fixedError(lineNumber, "invalid pmRA") + } + pmDec, _, err := fixedFloat(line, 60, 68) + if err != nil { + return Entry{}, fixedError(lineNumber, "invalid pmDE") + } + magnitude, magnitudePresent, err := fixedFloat(line, 129, 136) + if err != nil { + return Entry{}, fixedError(lineNumber, "invalid Hpmag") + } + bv, bvPresent, err := fixedFloat(line, 152, 158) + if err != nil { + return Entry{}, fixedError(lineNumber, "invalid B-V") + } + id := fmt.Sprintf("HIP %d", hip) + // 非正视差按无视差处理;非有限值原样保留,交给 Validate 拒收。 + if finite(parallax) && parallax <= 0 { + parallax = 0 + } + if !magnitudePresent { + magnitude = math.NaN() + } + if !bvPresent { + bv = math.NaN() + } + return Entry{ + ID: id, + Coordinate: parseCoordinate(id, angularFromRadians(ra), angularFromRadians(dec), pmRA, pmDec, parallax, 0, + julianYearTime(1991.25), basic.CoordinateFrameICRS), + Magnitude: magnitude, + Band: BandHp, + BPRP: math.NaN(), + BV: bv, + }, nil +} + +// ReadBSC5 读取 BSC5 定宽星表 / read the BSC5 fixed-width catalog. +func ReadBSC5(r io.Reader, options ReadOptions) (*Catalog, error) { + catalog := newCatalog() + scanner := bufio.NewScanner(r) + scanner.Buffer(make([]byte, 64*1024), 2*1024*1024) + lineNumber := 0 + for scanner.Scan() { + lineNumber++ + line := scanner.Bytes() + if isIgnorableFixedLine(line) { + continue + } + entry, present, err := bsc5Entry(line, lineNumber) + if err != nil { + if options.Strict { + return nil, err + } + catalog.skipped++ + continue + } + if !present { + catalog.skipped++ + continue + } + if err := addEntry(catalog, entry, lineNumber, options); err != nil { + return nil, err + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + catalog.finalize() + return catalog, nil +} + +func bsc5Entry(line []byte, lineNumber int) (Entry, bool, error) { + hr, present, err := fixedInt(line, 0, 4) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid HR") + } + if !present || hr <= 0 { + return Entry{}, false, nil + } + name, _ := fixedField(line, 4, 14) + hd, _, err := fixedInt(line, 25, 31) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid HD") + } + sao, _, err := fixedInt(line, 31, 37) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid SAO") + } + raHour, raHourPresent, err := fixedInt(line, 75, 77) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid RA hour") + } + raMinute, raMinutePresent, err := fixedInt(line, 77, 79) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid RA minute") + } + raSecond, raSecondPresent, err := fixedFloat(line, 79, 83) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid RA second") + } + decSign, decSignPresent := fixedField(line, 83, 84) + decDegree, decDegreePresent, err := fixedInt(line, 84, 86) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid Dec degree") + } + decMinute, decMinutePresent, err := fixedInt(line, 86, 88) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid Dec minute") + } + decSecond, decSecondPresent, err := fixedInt(line, 88, 90) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid Dec second") + } + if !raHourPresent || !raMinutePresent || !raSecondPresent || !decSignPresent || !decDegreePresent || !decMinutePresent || !decSecondPresent { + return Entry{}, false, nil + } + ra := (float64(raHour) + float64(raMinute)/60 + raSecond/3600) * 15 + dec := float64(decDegree) + float64(decMinute)/60 + float64(decSecond)/3600 + if strings.HasPrefix(decSign, "-") { + dec = -dec + } + vmag, vmPresent, err := fixedFloat(line, 102, 107) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid Vmag") + } + bv, bvPresent, err := fixedFloat(line, 109, 114) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid B-V") + } + pmRA, _, err := fixedFloat(line, 148, 154) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid pmRA") + } + pmDec, _, err := fixedFloat(line, 154, 160) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid pmDE") + } + parallax, _, err := fixedFloat(line, 161, 166) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid parallax") + } + radial, _, err := fixedInt(line, 166, 170) + if err != nil { + return Entry{}, false, fixedError(lineNumber, "invalid radial velocity") + } + id := fmt.Sprintf("HR %d", hr) + aliases := make([]string, 0, 3) + if name != "" { + aliases = append(aliases, strings.Join(strings.Fields(name), " ")) + } + if hd > 0 { + aliases = append(aliases, fmt.Sprintf("HD %d", hd)) + } + if sao > 0 { + aliases = append(aliases, fmt.Sprintf("SAO %d", sao)) + } + if !vmPresent { + vmag = math.NaN() + } + if !bvPresent { + bv = math.NaN() + } + // 非正视差按无视差处理;非有限值原样保留,交给 Validate 拒收。 + if finite(parallax) && parallax <= 0 { + parallax = 0 + } + return Entry{ + ID: id, Aliases: aliases, + Coordinate: parseCoordinate(id, ra, dec, pmRA*1000, pmDec*1000, parallax*1000, float64(radial), time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), basic.CoordinateFrameJ2000), + Magnitude: vmag, Band: BandV, BPRP: math.NaN(), BV: bv, + }, true, nil +} + +func isIgnorableFixedLine(line []byte) bool { + trimmed := strings.TrimSpace(string(line)) + return trimmed == "" || strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, "//") +} + +func fixedError(line int, reason string) error { + return fmt.Errorf("record %d: %s", line, reason) +} diff --git a/star/catalog/cross_test.go b/star/catalog/cross_test.go new file mode 100644 index 0000000..62a60f5 --- /dev/null +++ b/star/catalog/cross_test.go @@ -0,0 +1,141 @@ +package catalog + +import ( + "math" + "os" + "strings" + "testing" + "time" + + "b612.me/astro/basic" +) + +// propagateToJ2000 按线性空间运动把记录推到 J2000.0,返回赤经赤纬(度)。 +func propagateToJ2000(entry Entry) (float64, float64) { + c := entry.Coordinate + j2000 := basic.JD2DateByZone(2451545.0, time.UTC, false) + years := j2000.Sub(c.Epoch).Hours() / 24 / 365.25 + ra := c.RA * math.Pi / 180 + dec := c.Dec * math.Pi / 180 + p := [3]float64{math.Cos(dec) * math.Cos(ra), math.Cos(dec) * math.Sin(ra), math.Sin(dec)} + east := [3]float64{-math.Sin(ra), math.Cos(ra), 0} + north := [3]float64{-math.Sin(dec) * math.Cos(ra), -math.Sin(dec) * math.Sin(ra), math.Cos(dec)} + masToRad := math.Pi / 180 / 3600 / 1000 + var q [3]float64 + for i := range q { + q[i] = p[i] + (east[i]*c.ProperMotionRACosDecMasPerYear+north[i]*c.ProperMotionDecMasPerYear)*masToRad*years + } + return math.Mod(math.Atan2(q[1], q[0])*180/math.Pi+360, 360), math.Atan2(q[2], math.Hypot(q[0], q[1])) * 180 / math.Pi +} + +func separationMas(ra1, dec1, ra2, dec2 float64) float64 { + dra := (ra1 - ra2) * math.Cos(dec1*math.Pi/180) + if dra > 180 { + dra -= 360 + } + if dra < -180 { + dra += 360 + } + return math.Hypot(dra, dec1-dec2) * 3600 * 1000 +} + +func readFixtureCatalog(t *testing.T, name string, read func(*os.File) (*Catalog, error)) *Catalog { + t.Helper() + file, err := os.Open("testdata/" + name) + if err != nil { + t.Fatal(err) + } + defer file.Close() + catalog, err := read(file) + if err != nil { + t.Fatal(err) + } + return catalog +} + +func TestCrossFormatPositionsAtJ2000(t *testing.T) { + stellarium := readFixtureCatalog(t, "stars_0_0v0_18.cat", func(f *os.File) (*Catalog, error) { + return ReadStellariumStars(f, ReadOptions{Strict: true}) + }) + hip := readFixtureCatalog(t, "hip2_sample.dat", func(f *os.File) (*Catalog, error) { + return ReadHipparcos2007(f, ReadOptions{Strict: true}) + }) + bsc := readFixtureCatalog(t, "bsc5_sample.dat", func(f *os.File) (*Catalog, error) { + return ReadBSC5(f, ReadOptions{}) + }) + if bsc.Skipped() != 1 { + t.Fatalf("BSC5 skipped = %d, want the record without coordinates", bsc.Skipped()) + } + stars := map[string]string{"HIP 32349": "HR 2491", "HIP 91262": "HR 7001", "HIP 24436": "HR 1713", "HIP 21421": "HR 1457", "HIP 11767": "HR 424"} + for hipID, hrID := range stars { + h, ok := hip.Lookup(hipID) + if !ok { + t.Fatalf("hip2 missing %s", hipID) + } + b, ok := bsc.Lookup(hrID) + if !ok { + t.Fatalf("BSC5 missing %s", hrID) + } + hra, hdec := propagateToJ2000(h) + bra, bdec := propagateToJ2000(b) + // BSC5 只给到 0.1 s 与 1″,实测最大约 0.75″。 + if d := separationMas(hra, hdec, bra, bdec); d > 1000 { + t.Fatalf("%s hip2 vs BSC5 = %.1f mas", hipID, d) + } + if s, ok := stellarium.Lookup(hipID); ok { + sra, sdec := propagateToJ2000(s) + // 两者同源于 Hipparcos 2007,实测 ≤ 0.5 mas。 + if d := separationMas(hra, hdec, sra, sdec); d > 2 { + t.Fatalf("%s hip2 vs Stellarium = %.1f mas", hipID, d) + } + } + } +} + +func TestCDSFieldConventions(t *testing.T) { + hip := readFixtureCatalog(t, "hip2_sample.dat", func(f *os.File) (*Catalog, error) { + return ReadHipparcos2007(f, ReadOptions{Strict: true}) + }) + sirius, _ := hip.Lookup("HIP 32349") + if math.Abs(sirius.Coordinate.ParallaxMas-379.21) > 1e-6 || math.Abs(sirius.Coordinate.ProperMotionRACosDecMasPerYear+546.01) > 1e-6 || sirius.Band != "Hp" { + t.Fatalf("hip2 Sirius = %#v", sirius) + } + bsc := readFixtureCatalog(t, "bsc5_sample.dat", func(f *os.File) (*Catalog, error) { + return ReadBSC5(f, ReadOptions{}) + }) + vega, ok := bsc.Lookup("alp lyr") + if !ok { + vega, ok = bsc.Lookup("HD 172167") + } + if !ok || math.Abs(vega.Coordinate.ParallaxMas-123) > 1e-6 || vega.Coordinate.RadialVelocityKmPerSecond != -14 || vega.Band != "V" { + t.Fatalf("BSC5 Vega = %#v", vega) + } + if math.Abs(vega.Coordinate.ProperMotionRACosDecMasPerYear-202) > 1e-6 { + t.Fatalf("BSC5 pmRA must already include cos(dec): %.3f", vega.Coordinate.ProperMotionRACosDecMasPerYear) + } +} + +func TestCommonNames(t *testing.T) { + base := readFixtureCatalog(t, "stars_0_0v0_18.cat", func(f *os.File) (*Catalog, error) { + return ReadStellariumStars(f, ReadOptions{Strict: true}) + }) + names := "# comment\n 32349|_(\"Sirius\") 2\n91262|_(\"Vega\") 2\n32349|_(\"天狼\") 1\n24436|Rigel\n" + named, err := base.WithNames(strings.NewReader(names), ReadOptions{Strict: true}) + if err != nil { + t.Fatal(err) + } + for _, name := range []string{"sirius", "天狼", "VEGA", "Rigel"} { + if _, ok := named.Lookup(name); !ok { + t.Fatalf("lookup %q failed", name) + } + } + if entry, _ := named.Lookup("Sirius"); entry.ID != "HIP 32349" || entry.Magnitude > -1 { + t.Fatalf("Sirius = %s V %.2f", entry.ID, entry.Magnitude) + } + if _, ok := base.Lookup("Sirius"); ok { + t.Fatal("WithNames must not modify the receiver") + } + if _, err := base.WithNames(strings.NewReader("32349|_(\"broken\n"), ReadOptions{Strict: true}); err == nil { + t.Fatal("unterminated name should fail in strict mode") + } +} diff --git a/star/catalog/csv.go b/star/catalog/csv.go new file mode 100644 index 0000000..3971de3 --- /dev/null +++ b/star/catalog/csv.go @@ -0,0 +1,314 @@ +package catalog + +import ( + "bufio" + "encoding/csv" + "errors" + "fmt" + "io" + "math" + "strings" +) + +// ReadCSV 读取 CSV/TSV 星表 / read a CSV or TSV catalog. +func ReadCSV(r io.Reader, columns ColumnMap, options ReadOptions) (*Catalog, error) { + buffered := bufio.NewReaderSize(r, 64*1024) + header, headerLine, delimiter, err := readCSVHeader(buffered, options.Delimiter) + if err != nil { + return nil, err + } + indexes := buildColumnIndexes(header, columns) + if columns.ID == "" || columns.RA == "" || columns.Dec == "" || indexes.id < 0 || indexes.ra < 0 || indexes.dec < 0 { + return nil, fmt.Errorf("header line %d: required ID/RA/Dec column missing", headerLine) + } + if columns.EpochColumn != "" && indexes.epoch < 0 { + return nil, fmt.Errorf("header line %d: epoch column %q missing", headerLine, columns.EpochColumn) + } + + catalog := newCatalog() + source := &csvLineSource{r: buffered} + reader := csv.NewReader(source) + reader.Comma = delimiter + reader.Comment = '#' + reader.FieldsPerRecord = -1 + reader.ReuseRecord = true + for { + fields, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + var parseErr *csv.ParseError + if !errors.As(err, &parseErr) { + return nil, err + } + if options.Strict { + return nil, fmt.Errorf("line %d: %w", parseErr.Line+headerLine, err) + } + catalog.skipped++ + continue + } + // 引号内换行是记录跨越的行数,从当前行回推即得记录首行。 + line := source.line + headerLine + for _, field := range fields { + line -= strings.Count(field, "\n") + } + first := strings.TrimSpace(fields[0]) + if (len(fields) == 1 && first == "") || strings.HasPrefix(first, "#") || strings.HasPrefix(first, "//") || + isDelimitedSeparator(fields) || isDelimitedUnits(fields, indexes) { + continue + } + entry, parseErr := csvEntry(fields, indexes, columns, line) + if parseErr != nil { + if options.Strict { + return nil, parseErr + } + catalog.skipped++ + continue + } + if err := addEntry(catalog, entry, line, options); err != nil { + return nil, err + } + } + catalog.finalize() + return catalog, nil +} + +// csvLineSource 每次 Read 至多交付一行,line 为已开始交付的行数;csv.Reader 只在需要时才读,因此 line 即其当前所在行。 +type csvLineSource struct { + r *bufio.Reader + pending []byte + err error + line int + midLine bool +} + +func (s *csvLineSource) Read(p []byte) (int, error) { + if len(s.pending) == 0 { + if s.err != nil { + return 0, s.err + } + chunk, err := s.r.ReadSlice('\n') + if err != nil && err != bufio.ErrBufferFull { + s.err = err + } + if len(chunk) == 0 { + return 0, s.err + } + if !s.midLine { + s.line++ + } + s.midLine = chunk[len(chunk)-1] != '\n' + s.pending = chunk + } + n := copy(p, s.pending) + s.pending = s.pending[n:] + return n, nil +} + +// readCSVHeader 跳过注释与空行读出表头;未指定分隔符时按表头里 Tab 与逗号的多少判定。 +func readCSVHeader(r *bufio.Reader, delimiter rune) ([]string, int, rune, error) { + lineNumber := 0 + for { + line, err := r.ReadString('\n') + if line == "" && err != nil { + if err == io.EOF { + return nil, 0, 0, fmt.Errorf("missing header") + } + return nil, 0, 0, err + } + lineNumber++ + if lineNumber == 1 { + line = strings.TrimPrefix(line, "\ufeff") + } + trimmed := strings.TrimSpace(line) + if trimmed == "" || strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, "//") { + if err != nil { + return nil, 0, 0, fmt.Errorf("missing header") + } + continue + } + if delimiter == 0 { + delimiter = ',' + if strings.Count(line, "\t") > strings.Count(line, ",") { + delimiter = '\t' + } + } + reader := csv.NewReader(strings.NewReader(line)) + reader.Comma = delimiter + reader.FieldsPerRecord = -1 + fields, parseErr := reader.Read() + if parseErr != nil { + return nil, 0, 0, fmt.Errorf("header line %d: %w", lineNumber, parseErr) + } + return fields, lineNumber, delimiter, nil + } +} + +func isDelimitedSeparator(fields []string) bool { + if len(fields) == 0 { + return true + } + for _, field := range fields { + value := strings.TrimSpace(field) + if value == "---" { + return true + } + } + return false +} + +func isDelimitedUnits(fields []string, indexes columnIndexes) bool { + if indexes.ra < 0 || indexes.dec < 0 || indexes.id < 0 { + return false + } + ra := strings.ToLower(csvField(fields, indexes.ra)) + dec := strings.ToLower(csvField(fields, indexes.dec)) + if ra == "" || dec == "" || (strings.ContainsAny(ra, ".0123456789") && strings.ContainsAny(dec, ".0123456789")) { + return false + } + units := []string{"deg", "rad", "mas", "mag", "km/s", "arcsec", "hour", "hours"} + for _, value := range append([]string{ra, dec}, strings.ToLower(csvField(fields, indexes.id))) { + for _, unit := range units { + if strings.Contains(value, unit) { + return true + } + } + } + return false +} + +type columnIndexes struct { + id, ra, dec, pmRA, pmDec, parallax, radial, magnitude, bprp, bv, epoch int +} + +func buildColumnIndexes(header []string, columns ColumnMap) columnIndexes { + lookup := make(map[string]int, len(header)) + for i, name := range header { + lookup[normalizeColumn(name)] = i + } + indexes := columnIndexes{ + id: -1, ra: -1, dec: -1, pmRA: -1, pmDec: -1, parallax: -1, radial: -1, magnitude: -1, bprp: -1, bv: -1, epoch: -1, + } + set := func(name string) int { + if name == "" { + return -1 + } + index, ok := lookup[normalizeColumn(name)] + if !ok { + return -1 + } + return index + } + indexes.id, indexes.ra, indexes.dec = set(columns.ID), set(columns.RA), set(columns.Dec) + indexes.pmRA, indexes.pmDec, indexes.parallax = set(columns.PMRA), set(columns.PMDec), set(columns.Parallax) + indexes.radial, indexes.magnitude, indexes.bprp = set(columns.RadialVelocity), set(columns.Magnitude), set(columns.BPRP) + indexes.bv, indexes.epoch = set(columns.BV), set(columns.EpochColumn) + return indexes +} + +func normalizeColumn(value string) string { + return strings.ToLower(strings.TrimSpace(value)) +} + +func csvField(fields []string, index int) string { + if index < 0 || index >= len(fields) { + return "" + } + return strings.TrimSpace(fields[index]) +} + +func csvEntry(fields []string, indexes columnIndexes, columns ColumnMap, line int) (Entry, error) { + if columns.ID == "" || columns.RA == "" || columns.Dec == "" || indexes.id < 0 || indexes.ra < 0 || indexes.dec < 0 { + return Entry{}, fmt.Errorf("line %d: required ID/RA/Dec column missing", line) + } + id := strings.TrimSpace(csvField(fields, indexes.id)) + if id == "" { + return Entry{}, fmt.Errorf("line %d: missing ID", line) + } + if columns.IDPrefix != "" { + id = columns.IDPrefix + id + } + ra, raPresent, err := parseFloat(csvField(fields, indexes.ra)) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid RA: %w", line, err) + } + if !raPresent { + return Entry{}, fmt.Errorf("line %d: missing RA", line) + } + dec, decPresent, err := parseFloat(csvField(fields, indexes.dec)) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid Dec: %w", line, err) + } + if !decPresent { + return Entry{}, fmt.Errorf("line %d: missing Dec", line) + } + if columns.AnglesInRadians { + ra = angularFromRadians(ra) + dec = angularFromRadians(dec) + } + pmRA, _, err := optionalFloat(fields, indexes.pmRA) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid PMRA: %w", line, err) + } + pmDec, _, err := optionalFloat(fields, indexes.pmDec) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid PMDec: %w", line, err) + } + if !columns.PMRAIncludesCosDec { + pmRA *= math.Cos(dec * math.Pi / 180) + } + parallax, _, err := optionalFloat(fields, indexes.parallax) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid parallax: %w", line, err) + } + radial, _, err := optionalFloat(fields, indexes.radial) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid radial velocity: %w", line, err) + } + magnitude, magnitudePresent, err := optionalFloat(fields, indexes.magnitude) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid magnitude: %w", line, err) + } + bprp, bprpPresent, err := optionalFloat(fields, indexes.bprp) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid BP-RP: %w", line, err) + } + bv, bvPresent, err := optionalFloat(fields, indexes.bv) + if err != nil { + return Entry{}, fmt.Errorf("line %d: invalid B-V: %w", line, err) + } + epoch := columns.Epoch + if columns.EpochColumn != "" { + year, present, epochErr := optionalFloat(fields, indexes.epoch) + if epochErr != nil { + return Entry{}, fmt.Errorf("line %d: invalid epoch: %w", line, epochErr) + } + if present { + epoch = julianYearTime(year) + } + } + if epoch.IsZero() { + return Entry{}, fmt.Errorf("line %d: epoch is required", line) + } + if !magnitudePresent { + magnitude = math.NaN() + } + if !bprpPresent { + bprp = math.NaN() + } + if !bvPresent { + bv = math.NaN() + } + return Entry{ + ID: id, Coordinate: parseCoordinate(id, ra, dec, pmRA, pmDec, parallax, radial, epoch, columns.Frame), + Magnitude: magnitude, Band: columns.Band, BPRP: bprp, BV: bv, + }, nil +} + +func optionalFloat(fields []string, index int) (float64, bool, error) { + if index < 0 { + return 0, false, nil + } + return parseFloat(csvField(fields, index)) +} diff --git a/star/catalog/doc.go b/star/catalog/doc.go new file mode 100644 index 0000000..22bb9a7 --- /dev/null +++ b/star/catalog/doc.go @@ -0,0 +1,2 @@ +// Package catalog 读取外部恒星星表并输出可直接用于搜索的恒星坐标 / external star catalogs as searchable star coordinates. +package catalog diff --git a/star/catalog/names.go b/star/catalog/names.go new file mode 100644 index 0000000..a513d4c --- /dev/null +++ b/star/catalog/names.go @@ -0,0 +1,23 @@ +package catalog + +import "io" + +// WithNames 返回并入名称文件后的新星表;每行 `HIP 号或 Gaia 号|名称`, +// 名称可写作 `α_And` 或 `_("Sirius") 2`。原星表不变。 +// +// WithNames returns a new catalog with names merged from an `id|name` file. +// Names may be written as `α_And` or `_("Sirius") 2`. The receiver is unchanged. +func (c *Catalog) WithNames(r io.Reader, read ReadOptions) (*Catalog, error) { + named := newCatalog() + if c != nil { + for _, entry := range c.entries { + named.add(cloneEntry(entry)) + } + named.skipped = c.skipped + } + if err := applyNameLines(r, named, "names", read); err != nil { + return nil, err + } + named.finalize() + return named, nil +} diff --git a/star/catalog/near_test.go b/star/catalog/near_test.go new file mode 100644 index 0000000..fe8df5c --- /dev/null +++ b/star/catalog/near_test.go @@ -0,0 +1,54 @@ +package catalog + +import ( + "fmt" + "math/rand" + "sort" + "testing" + + "b612.me/astro/basic" +) + +func TestNearMatchesBruteForce(t *testing.T) { + random := rand.New(rand.NewSource(1)) + catalog := newCatalog() + for i := 0; i < 4000; i++ { + ra := random.Float64() * 360 + dec := random.Float64()*180 - 90 + if i < 40 { + // 贴近 RA 0/360 与天极的记录。 + ra = []float64{0, 359.999, 0.001, 180}[i%4] + dec = []float64{0, 89.9, -89.9, 45}[i/4%4] + } + id := fmt.Sprintf("S %d", i) + catalog.add(Entry{ID: id, Coordinate: parseCoordinate(id, ra, dec, 0, 0, 0, 0, julianYearTime(2000), basic.CoordinateFrameICRS)}) + } + catalog.finalize() + queries := [][3]float64{ + {0, 0, 1}, {359.9, 0, 0.5}, {0.05, 60, 3}, {180, 89.5, 2}, {10, -88, 5}, + {270, 30, 20}, {90, -45, 60}, {0, 0, 120}, {359.999, 89.9, 0.2}, + {0, 0, 60}, {30, 20, 65}, {200, -10, 75}, {0, 0, 180}, {10, 10, 200}, + } + for _, query := range queries { + got := catalog.Near(query[0], query[1], query[2]) + var want []string + for _, entry := range catalog.entries { + if angularDistanceDeg(normalizeRA(query[0]), query[1], entry.Coordinate.RA, entry.Coordinate.Dec) <= query[2]+1e-12 { + want = append(want, entry.ID) + } + } + ids := make([]string, len(got)) + for i, entry := range got { + ids[i] = entry.ID + if i > 0 && angularDistanceDeg(query[0], query[1], got[i-1].Coordinate.RA, got[i-1].Coordinate.Dec) > + angularDistanceDeg(query[0], query[1], entry.Coordinate.RA, entry.Coordinate.Dec) { + t.Fatalf("query %v: result not sorted by distance", query) + } + } + sort.Strings(ids) + sort.Strings(want) + if fmt.Sprint(ids) != fmt.Sprint(want) { + t.Fatalf("query %v: got %d, want %d", query, len(ids), len(want)) + } + } +} diff --git a/star/catalog/parse.go b/star/catalog/parse.go new file mode 100644 index 0000000..caa2aa3 --- /dev/null +++ b/star/catalog/parse.go @@ -0,0 +1,164 @@ +package catalog + +import ( + "fmt" + "math" + "strconv" + "strings" + "time" + + "b612.me/astro/basic" +) + +// GaiaDR3Columns 返回 Gaia DR3 归档导出的列映射 / column map for Gaia DR3 archive exports. +func GaiaDR3Columns() ColumnMap { + return ColumnMap{ + ID: "source_id", RA: "ra", Dec: "dec", PMRA: "pmra", PMDec: "pmdec", + Parallax: "parallax", RadialVelocity: "radial_velocity", Magnitude: "phot_g_mean_mag", + BPRP: "bp_rp", Band: BandG, Epoch: julianYearTime(2016.0), Frame: basic.CoordinateFrameICRS, + IDPrefix: "Gaia DR3 ", PMRAIncludesCosDec: true, + } +} + +// Hipparcos2007Columns 返回 VizieR 导出的 Hipparcos 2007 新归算 CSV 列映射,角度已换算为度;原始 hip2.dat 用 ReadHipparcos2007 / +// column map for VizieR CSV exports of Hipparcos 2007 in degrees; use ReadHipparcos2007 for raw hip2.dat. +func Hipparcos2007Columns() ColumnMap { + return ColumnMap{ + ID: "HIP", RA: "RArad", Dec: "DErad", PMRA: "pmRA", PMDec: "pmDE", + Parallax: "Plx", Magnitude: "Hpmag", BV: "B-V", Band: BandHp, + Epoch: julianYearTime(1991.25), Frame: basic.CoordinateFrameICRS, IDPrefix: "HIP ", + PMRAIncludesCosDec: true, AnglesInRadians: false, + } +} + +func julianYearTime(year float64) time.Time { + return basic.JD2DateByZone(2451545.0+(year-2000.0)*365.25, time.UTC, false) +} + +func parseMissing(value string) bool { + switch strings.ToLower(strings.TrimSpace(value)) { + case "", "nan", "--", "null": + return true + default: + return false + } +} + +func parseFloat(value string) (float64, bool, error) { + if parseMissing(value) { + return 0, false, nil + } + parsed, err := strconv.ParseFloat(strings.TrimSpace(value), 64) + if err != nil { + return 0, true, err + } + return parsed, true, nil +} + +func parseInt(value string) (int64, bool, error) { + if parseMissing(value) { + return 0, false, nil + } + parsed, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64) + if err != nil { + return 0, true, err + } + return parsed, true, nil +} + +func parseUint(value string) (uint64, bool, error) { + if parseMissing(value) { + return 0, false, nil + } + parsed, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64) + if err != nil { + return 0, true, err + } + return parsed, true, nil +} + +func normalizedFrame(frame basic.CoordinateFrame) basic.CoordinateFrame { + if frame == "" { + return basic.CoordinateFrameICRS + } + return frame +} + +func addEntry(c *Catalog, entry Entry, record int, options ReadOptions) error { + if entry.ID == "" { + return recordFailure(c, options, record, "missing ID") + } + if err := entry.Coordinate.Validate(); err != nil { + return recordFailure(c, options, record, err.Error()) + } + if options.Filter != nil && !options.Filter(entry) { + return nil + } + c.add(entry) + return nil +} + +func recordFailure(c *Catalog, options ReadOptions, record int, reason interface{}) error { + if options.Strict { + return fmt.Errorf("record %d: %v", record, reason) + } + c.skipped++ + return nil +} + +func parseCoordinate(id string, ra, dec, pmRA, pmDec, parallax, radial float64, epoch time.Time, frame basic.CoordinateFrame) basic.StarCoordinate { + if ra == 360 || ra == -0 { + ra = 0 + } else { + ra = normalizeRA(ra) + } + // 负视差是测量噪声,按无视差处理;非有限值原样保留,交给 Validate 拒收。 + if finite(parallax) && parallax < 0 { + parallax = 0 + } + return basic.StarCoordinate{ + ID: id, + RA: ra, + Dec: dec, + Epoch: epoch, + Frame: normalizedFrame(frame), + ProperMotionRACosDecMasPerYear: pmRA, + ProperMotionDecMasPerYear: pmDec, + ParallaxMas: parallax, + RadialVelocityKmPerSecond: radial, + } +} + +func fixedFloat(line []byte, start, end int) (float64, bool, error) { + value, ok := fixedField(line, start, end) + if !ok { + return 0, false, nil + } + return parseFloat(value) +} + +func fixedInt(line []byte, start, end int) (int64, bool, error) { + value, ok := fixedField(line, start, end) + if !ok { + return 0, false, nil + } + return parseInt(value) +} + +func fixedField(line []byte, start, end int) (string, bool) { + if start < 0 || end < start || start >= len(line) || end > len(line) { + return "", false + } + return strings.TrimSpace(string(line[start:end])), true +} + +func checkFinite(value float64) error { + if !finite(value) { + return fmt.Errorf("non-finite value %v", value) + } + return nil +} + +func angularFromRadians(value float64) float64 { + return value * 180 / math.Pi +} diff --git a/star/catalog/smoke_test.go b/star/catalog/smoke_test.go new file mode 100644 index 0000000..67c6cb2 --- /dev/null +++ b/star/catalog/smoke_test.go @@ -0,0 +1,41 @@ +package catalog + +import ( + "math" + "os" + "testing" + "time" + + "b612.me/astro/basic" +) + +func TestCatalogCoordinatesFeedSearches(t *testing.T) { + bsc := readFixtureCatalog(t, "bsc5_sample.dat", func(f *os.File) (*Catalog, error) { + return ReadBSC5(f, ReadOptions{}) + }) + target, ok := bsc.Lookup("HR 4799") + if !ok { + t.Fatal("HR 4799 missing") + } + location := time.FixedZone("CST", 8*3600) + best, err := basic.FindBestStarOccultations( + time.Date(2025, 6, 5, 0, 0, 0, 0, location), + time.Date(2025, 6, 6, 0, 0, 0, 0, location), + target.Coordinate, basic.OccultationSearchOptions{MaxEvents: 1}) + if err != nil || len(best) != 1 { + t.Fatalf("occultation = %d events, %v", len(best), err) + } + if d := best[0].Greatest.Sub(time.Date(2025, 6, 5, 20, 2, 7, 700000000, location)).Seconds(); math.Abs(d) > 5 { + t.Fatalf("greatest off by %.1f s", d) + } + + sirius, ok := bsc.Lookup("HR 2491") + if !ok { + t.Fatal("HR 2491 missing") + } + startJD := basic.Date2JD(time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)) + events := basic.FindStarHeliacalEventsWithInstance(sirius.Coordinate, sirius.Magnitude, startJD, startJD+366, 31.2, 30.0, basic.HeliacalOptions{}, nil) + if len(events) == 0 { + t.Fatal("no heliacal events for Sirius at Cairo") + } +} diff --git a/star/catalog/stellarium.go b/star/catalog/stellarium.go new file mode 100644 index 0000000..7e9e8eb --- /dev/null +++ b/star/catalog/stellarium.go @@ -0,0 +1,443 @@ +package catalog + +import ( + "bufio" + "encoding/binary" + "errors" + "fmt" + "io" + "io/fs" + "math" + "sort" + "strconv" + "strings" + "time" + + "b612.me/astro/basic" +) + +// StellariumOptions 控制 Stellarium 附加文件 / Stellarium side-file options. +type StellariumOptions struct { + MaxMagnitude float64 // 0 表示不限,可为负 / 0 means unlimited; may be negative + Names bool // 读 name.fab 与 extra_name.fab / read name files + CrossID bool // 读 cross-id.cat / read cross identifiers +} + +const stellariumMagic uint32 = 0x835F040A + +// ReadStellariumStars 读取单个 Stellarium 星表分片 / read one Stellarium star shard. +func ReadStellariumStars(r io.Reader, read ReadOptions) (*Catalog, error) { + return readStellariumStars(r, StellariumOptions{}, read) +} + +func readStellariumStars(r io.Reader, stellarium StellariumOptions, read ReadOptions) (*Catalog, error) { + reader := bufio.NewReaderSize(r, 64*1024) + var headerBytes [28]byte + if _, err := io.ReadFull(reader, headerBytes[:]); err != nil { + return nil, fmt.Errorf("stellarium header: %w", err) + } + if binary.LittleEndian.Uint32(headerBytes[0:4]) != stellariumMagic { + if binary.BigEndian.Uint32(headerBytes[0:4]) == stellariumMagic { + return nil, fmt.Errorf("stellarium big-endian files are unsupported") + } + return nil, fmt.Errorf("stellarium header: invalid magic") + } + typeID := binary.LittleEndian.Uint32(headerBytes[4:8]) + major := binary.LittleEndian.Uint32(headerBytes[8:12]) + minor := binary.LittleEndian.Uint32(headerBytes[12:16]) + level := binary.LittleEndian.Uint32(headerBytes[16:20]) + magMin := int32(binary.LittleEndian.Uint32(headerBytes[20:24])) + epochJD := math.Float32frombits(binary.LittleEndian.Uint32(headerBytes[24:28])) + if major > 0 { + return nil, fmt.Errorf("stellarium header: unsupported major version %d", major) + } + if typeID > 2 { + return nil, fmt.Errorf("stellarium header: unsupported record type %d", typeID) + } + if level > 8 { + return nil, fmt.Errorf("stellarium header: unsupported level %d", level) + } + if epochJD != 2457389.0 { + return nil, fmt.Errorf("stellarium header: unsupported epoch %.7g", epochJD) + } + if stellarium.MaxMagnitude != 0 && float64(magMin)/1000 > stellarium.MaxMagnitude { + catalog := newCatalog() + catalog.finalize() + return catalog, nil + } + zoneCount := uint64(20) + for i := uint32(0); i < level; i++ { + zoneCount *= 4 + } + zoneCount++ + counts := make([]uint32, zoneCount) + var countBytes [4]byte + for i := range counts { + if _, err := io.ReadFull(reader, countBytes[:]); err != nil { + return nil, fmt.Errorf("stellarium counts: %w", err) + } + counts[i] = binary.LittleEndian.Uint32(countBytes[:]) + } + catalog := newCatalog() + recordSize := map[uint32]int{0: 48, 1: 32, 2: 16}[typeID] + recordNumber := 0 + buffer := make([]byte, recordSize) + epoch := basic.JD2DateByZone(float64(epochJD), time.UTC, false) + for _, count := range counts { + for i := uint32(0); i < count; i++ { + recordNumber++ + if _, err := io.ReadFull(reader, buffer); err != nil { + if read.Strict { + return nil, fmt.Errorf("record %d: truncated record: %w", recordNumber, err) + } + catalog.skipped++ + catalog.finalize() + return catalog, nil + } + entry, err := stellariumEntry(buffer, typeID, epoch, recordNumber) + if err != nil { + if read.Strict { + return nil, err + } + catalog.skipped++ + continue + } + if stellarium.MaxMagnitude != 0 && (!finite(entry.Magnitude) || entry.Magnitude > stellarium.MaxMagnitude) { + continue + } + if err := addEntry(catalog, entry, recordNumber, read); err != nil { + return nil, err + } + } + } + var trailing [1]byte + if n, err := reader.Read(trailing[:]); n != 0 || err != io.EOF { + if read.Strict { + if err == nil { + return nil, fmt.Errorf("stellarium file has trailing bytes") + } + return nil, fmt.Errorf("stellarium file length: %w", err) + } + catalog.skipped++ + } + _ = minor + catalog.finalize() + return catalog, nil +} + +func stellariumEntry(record []byte, typeID uint32, epoch time.Time, recordNumber int) (Entry, error) { + switch typeID { + case 0: + return stellariumStar1(record, epoch, recordNumber) + case 1: + return stellariumStar2(record, epoch, recordNumber) + case 2: + return stellariumStar3(record, epoch, recordNumber) + default: + return Entry{}, fmt.Errorf("record %d: unsupported record type %d", recordNumber, typeID) + } +} + +func stellariumStar1(record []byte, epoch time.Time, recordNumber int) (Entry, error) { + gaiaID := int64(binary.LittleEndian.Uint64(record[0:8])) + x := [3]float64{ + float64(int32(binary.LittleEndian.Uint32(record[8:12]))) / 2e9, + float64(int32(binary.LittleEndian.Uint32(record[12:16]))) / 2e9, + float64(int32(binary.LittleEndian.Uint32(record[16:20]))) / 2e9, + } + dx := [3]float64{ + float64(int32(binary.LittleEndian.Uint32(record[20:24]))) / 1000, + float64(int32(binary.LittleEndian.Uint32(record[24:28]))) / 1000, + float64(int32(binary.LittleEndian.Uint32(record[28:32]))) / 1000, + } + norm := math.Sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]) + if !finite(norm) || norm == 0 { + return Entry{}, fmt.Errorf("record %d: zero position vector", recordNumber) + } + ra := normalizeRA(math.Atan2(x[1], x[0]) * 180 / math.Pi) + dec := math.Asin(clamp(x[2]/norm, -1, 1)) * 180 / math.Pi + alpha := ra * math.Pi / 180 + delta := dec * math.Pi / 180 + pmRA := -math.Sin(alpha)*dx[0] + math.Cos(alpha)*dx[1] + pmDec := -math.Sin(delta)*math.Cos(alpha)*dx[0] - math.Sin(delta)*math.Sin(alpha)*dx[1] + math.Cos(delta)*dx[2] + radialPM := math.Cos(delta)*math.Cos(alpha)*dx[0] + math.Cos(delta)*math.Sin(alpha)*dx[1] + math.Sin(delta)*dx[2] + if !finite(radialPM) || math.Abs(radialPM) > 1 { + return Entry{}, fmt.Errorf("record %d: non-tangential proper motion", recordNumber) + } + bv := float64(int16(binary.LittleEndian.Uint16(record[32:34]))) / 1000 + vmag := float64(int16(binary.LittleEndian.Uint16(record[34:36]))) / 1000 + parallax := float64(binary.LittleEndian.Uint16(record[36:38])) * 0.02 + radial := float64(int16(binary.LittleEndian.Uint16(record[40:42]))) / 10 + hipPacked := uint32(record[45]) | uint32(record[46])<<8 | uint32(record[47])<<16 + hip := hipPacked >> 5 + componentNumber := hipPacked & 31 + component := componentName(componentNumber) + id := "" + aliases := make([]string, 0, 1) + if hip > 0 { + id = fmt.Sprintf("HIP %d", hip) + if gaiaID > 0 { + aliases = append(aliases, fmt.Sprintf("Gaia DR3 %d", gaiaID)) + } + } else if gaiaID > 0 { + id = fmt.Sprintf("Gaia DR3 %d", gaiaID) + } + if id == "" { + return Entry{}, fmt.Errorf("record %d: missing HIP and Gaia ID", recordNumber) + } + return Entry{ID: id, Aliases: aliases, Component: component, Coordinate: parseCoordinate(id, ra, dec, pmRA, pmDec, parallax, radial, epoch, basic.CoordinateFrameICRS), Magnitude: vmag, Band: BandV, BPRP: math.NaN(), BV: bv}, nil +} + +func stellariumStar2(record []byte, epoch time.Time, recordNumber int) (Entry, error) { + gaiaID := int64(binary.LittleEndian.Uint64(record[0:8])) + ra := float64(int32(binary.LittleEndian.Uint32(record[8:12]))) / 3600000 + dec := float64(int32(binary.LittleEndian.Uint32(record[12:16]))) / 3600000 + pmRA := float64(int32(binary.LittleEndian.Uint32(record[16:20]))) / 1000 + pmDec := float64(int32(binary.LittleEndian.Uint32(record[20:24]))) / 1000 + pmRA *= math.Cos(dec * math.Pi / 180) + bv := float64(int16(binary.LittleEndian.Uint16(record[24:26]))) / 1000 + vmag := float64(int16(binary.LittleEndian.Uint16(record[26:28]))) / 1000 + parallax := float64(binary.LittleEndian.Uint16(record[28:30])) / 100 + if gaiaID <= 0 { + return Entry{}, fmt.Errorf("record %d: missing Gaia ID", recordNumber) + } + id := fmt.Sprintf("Gaia DR3 %d", gaiaID) + return Entry{ID: id, Coordinate: parseCoordinate(id, ra, dec, pmRA, pmDec, parallax, 0, epoch, basic.CoordinateFrameICRS), Magnitude: vmag, Band: BandV, BPRP: math.NaN(), BV: bv}, nil +} + +func stellariumStar3(record []byte, epoch time.Time, recordNumber int) (Entry, error) { + raValue := uint32(record[8]) | uint32(record[9])<<8 | uint32(record[10])<<16 + decValue := uint32(record[11]) | uint32(record[12])<<8 | uint32(record[13])<<16 + ra := float64(raValue) * 0.1 / 3600 + dec := (float64(decValue) - 90*36000) * 0.1 / 3600 + vmag := (float64(record[15])*20 + 16000) / 1000 + bv := float64(record[14])*0.025 - 1 + gaiaID := int64(binary.LittleEndian.Uint64(record[0:8])) + if gaiaID <= 0 { + return Entry{}, fmt.Errorf("record %d: missing Gaia ID", recordNumber) + } + id := fmt.Sprintf("Gaia DR3 %d", gaiaID) + return Entry{ID: id, Coordinate: parseCoordinate(id, ra, dec, 0, 0, 0, 0, epoch, basic.CoordinateFrameICRS), Magnitude: vmag, Band: BandV, BPRP: math.NaN(), BV: bv}, nil +} + +func clamp(value, low, high float64) float64 { + if value < low { + return low + } + if value > high { + return high + } + return value +} + +func componentName(value uint32) string { + if value == 0 { + return "" + } + if value <= 26 { + return string(rune('A' + value - 1)) + } + return strconv.FormatUint(uint64(value), 10) +} + +// ReadStellariumDir 读取 Stellarium 星表目录 / read a Stellarium catalog directory. +func ReadStellariumDir(dir fs.FS, stellarium StellariumOptions, read ReadOptions) (*Catalog, error) { + entries, err := fs.ReadDir(dir, ".") + if err != nil { + return nil, err + } + shards := make([]string, 0) + for _, entry := range entries { + if !entry.IsDir() && isStellariumShard(entry.Name()) { + shards = append(shards, entry.Name()) + } + } + sort.Strings(shards) + merged := newCatalog() + for _, name := range shards { + file, openErr := dir.Open(name) + if openErr != nil { + return nil, openErr + } + part, readErr := readStellariumStars(file, stellarium, read) + closeErr := file.Close() + if readErr != nil { + return nil, fmt.Errorf("%s: %w", name, readErr) + } + if closeErr != nil { + return nil, closeErr + } + merged = merged.Merge(part) + } + if stellarium.Names { + for _, name := range []string{"name.fab", "extra_name.fab"} { + if err := addFabAliases(dir, merged, name, read); err != nil { + return nil, err + } + } + } + if stellarium.CrossID { + if err := addCrossIDAliases(dir, merged, "cross-id.cat", read); err != nil { + return nil, err + } + } + merged.finalize() + return merged, nil +} + +func isStellariumShard(name string) bool { + if !strings.HasPrefix(name, "stars_") || !strings.HasSuffix(name, ".cat") { + return false + } + rest := strings.TrimPrefix(strings.TrimSuffix(name, ".cat"), "stars_") + if rest == "" || rest[0] < '0' || rest[0] > '9' { + return false + } + return strings.Contains(rest, "_") +} + +func addFabAliases(dir fs.FS, catalog *Catalog, name string, read ReadOptions) error { + file, err := dir.Open(name) + if err != nil { + if errorsIsNotExist(err) { + return nil + } + return err + } + defer file.Close() + return applyNameLines(file, catalog, name, read) +} + +// applyNameLines 读 `id|名称` 行并把名称并入对应记录的别名。 +func applyNameLines(r io.Reader, catalog *Catalog, label string, read ReadOptions) error { + scanner := bufio.NewScanner(r) + scanner.Buffer(make([]byte, 64*1024), 2*1024*1024) + index := buildCatalogNumberIndex(catalog) + lineNumber := 0 + for scanner.Scan() { + lineNumber++ + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") { + continue + } + parts := strings.SplitN(line, "|", 2) + id, err := uint64(0), error(nil) + if len(parts) == 2 { + id, err = strconv.ParseUint(strings.TrimSpace(parts[0]), 10, 64) + } + alias := "" + if len(parts) == 2 && err == nil { + alias = nameField(parts[1]) + } + if alias == "" { + if read.Strict { + return fmt.Errorf("%s line %d: expected id|name", label, lineNumber) + } + catalog.skipped++ + continue + } + for _, i := range index.lookup(id, 0) { + catalog.entries[i].Aliases = append(catalog.entries[i].Aliases, alias) + } + } + return scanner.Err() +} + +// nameField 取名称字段:`_("Sirius") 2` 取引号内文字,否则把 `_` 换成空格。 +func nameField(field string) string { + field = strings.TrimSpace(field) + if strings.HasPrefix(field, `_("`) { + end := strings.Index(field[3:], `")`) + if end < 0 { + return "" + } + return strings.TrimSpace(field[3 : 3+end]) + } + return strings.TrimSpace(strings.ReplaceAll(field, "_", " ")) +} + +func addCrossIDAliases(dir fs.FS, catalog *Catalog, name string, read ReadOptions) error { + file, err := dir.Open(name) + if err != nil { + if errorsIsNotExist(err) { + return nil + } + return err + } + defer file.Close() + reader := bufio.NewReaderSize(file, 64*1024) + index := buildCatalogNumberIndex(catalog) + var record [21]byte + recordNumber := 0 + for { + n, readErr := io.ReadFull(reader, record[:]) + if readErr == io.EOF && n == 0 { + break + } + recordNumber++ + if readErr != nil { + if read.Strict { + return fmt.Errorf("%s record %d: length is not 21 bytes", name, recordNumber) + } + catalog.skipped++ + break + } + id := binary.LittleEndian.Uint64(record[0:8]) + component := uint32(record[8]) + aliases := make([]string, 0, 3) + if value := int32(binary.LittleEndian.Uint32(record[9:13])); value != 0 { + aliases = append(aliases, fmt.Sprintf("SAO %d", value)) + } + if value := int32(binary.LittleEndian.Uint32(record[13:17])); value != 0 { + aliases = append(aliases, fmt.Sprintf("HD %d", value)) + } + if value := int32(binary.LittleEndian.Uint32(record[17:21])); value != 0 { + aliases = append(aliases, fmt.Sprintf("HR %d", value)) + } + for _, i := range index.lookup(id, component) { + catalog.entries[i].Aliases = append(catalog.entries[i].Aliases, aliases...) + } + } + return nil +} + +type catalogNumberIndex struct { + hip, gaia map[uint64][]int + hipComponents map[string][]int +} + +func buildCatalogNumberIndex(catalog *Catalog) catalogNumberIndex { + index := catalogNumberIndex{hip: make(map[uint64][]int), gaia: make(map[uint64][]int), hipComponents: make(map[string][]int)} + for i, entry := range catalog.entries { + if strings.HasPrefix(entry.ID, "HIP ") { + value, err := strconv.ParseUint(strings.TrimSpace(strings.TrimPrefix(entry.ID, "HIP ")), 10, 64) + if err == nil { + index.hip[value] = append(index.hip[value], i) + index.hipComponents[fmt.Sprintf("%d\x00%s", value, entry.Component)] = append(index.hipComponents[fmt.Sprintf("%d\x00%s", value, entry.Component)], i) + } + } else if strings.HasPrefix(entry.ID, "Gaia DR3 ") { + value, err := strconv.ParseUint(strings.TrimSpace(strings.TrimPrefix(entry.ID, "Gaia DR3 ")), 10, 64) + if err == nil { + index.gaia[value] = append(index.gaia[value], i) + } + } + } + return index +} + +func (index catalogNumberIndex) lookup(id uint64, component uint32) []int { + if id <= 120416 { + if component == 0 { + return index.hip[id] + } + return index.hipComponents[fmt.Sprintf("%d\x00%s", id, componentName(component))] + } + if component != 0 { + return nil + } + return index.gaia[id] +} + +func errorsIsNotExist(err error) bool { + return errors.Is(err, fs.ErrNotExist) +} diff --git a/star/catalog/testdata/bsc5_sample.dat b/star/catalog/testdata/bsc5_sample.dat new file mode 100644 index 0000000..2529927 --- /dev/null +++ b/star/catalog/testdata/bsc5_sample.dat @@ -0,0 +1,7 @@ + 92 NOVA 1572 B Cas * + 424 1Alp UMiBD+88 8 8890 308 907 1477 Alp UMi 012233.7+884626023148.7+891551123.28 26.46 2.02 +0.60 +0.38 +0.31 F7:Ib-II v+0.038-0.015 +.007-017SBO 17 6.8 18.4AB 5* +1457 87Alp TauBD+16 629 29139 94027 168I 3321 Alp Tau 043010.9+161830043555.2+163033180.97-20.25 0.85 +1.54 +1.90 +0.94 K5+III v+0.063-0.190 +.048+054SB < 17 9.8 121.7AC 6* +1713 19Bet OriBD-08 1063 34085131907 194I 3823 1882 050943.9-081901051432.3-081206209.24-25.25 0.12 -0.03 -0.66 -0.02 B8Ia: e 0.000-0.001 +.013+021SB 33 6.5 9.5AB 4* +2491 9Alp CMaBD-16 1591 48915151881 257I 5423 064044.6-163444064508.9-164258227.22-08.88-1.46 0.00 -0.05 -0.03 A1Vm -0.553-1.205 +.375-008SBO 13 10.3 11.2AB 4* +7001 3Alp LyrBD+38 3238 172167 67174 699I 11510 Alp Lyr 183333.1+384126183656.3+384701 67.44 19.24 0.03 0.00 -0.01 -0.03 A0Va +0.202+0.286 +.123-014V 15 10.4 62.8AB 5* +4799 25 VirBD-05 3535 1097041388731324 123138.2-051651123647.4-054955296.26 56.85 5.87 +0.07 +0.09 A3V -0.028-0.018 -006V diff --git a/star/catalog/testdata/cross-id.cat b/star/catalog/testdata/cross-id.cat new file mode 100644 index 0000000000000000000000000000000000000000..fb5ff97ef81075be66df527d86c506cab1026fe2 GIT binary patch literal 21 Ycma#8V*mq2&p;*y;r$E@yEz#c03mJz4*&oF literal 0 HcmV?d00001 diff --git a/star/catalog/testdata/extra_name.fab b/star/catalog/testdata/extra_name.fab new file mode 100644 index 0000000..b10577f --- /dev/null +++ b/star/catalog/testdata/extra_name.fab @@ -0,0 +1,11 @@ + 24436|Σ_668 + 24436|β_555 + 32349|AGC_1 + 32349|LEC_1 + 32349|HL_3 + 32349|β_1411 + 91262|Σ_II_9 + 91262|MBA_2 + 91262|MET_11 + 91262|H_5_39 + 91262|SMR_33 diff --git a/star/catalog/testdata/hip2_sample.dat b/star/catalog/testdata/hip2_sample.dat new file mode 100644 index 0000000..35def5d --- /dev/null +++ b/star/catalog/testdata/hip2_sample.dat @@ -0,0 +1,5 @@ + 11767 5 0 1 0.6622851337 1.5579531082 7.54 44.48 -11.85 0.09 0.10 0.11 0.11 0.13 127 1.08 2 0.0 0 2.1077 0.0021 0.014 1 0.636 0.003 0.700 1.02 1.23 1.04 -0.42 -2.11 1.01 1.67 -0.26 -0.93 1.06 -0.06 1.42 0.64 2.87 1.00 + 21421 1 0 1 1.2039281519 0.2881496983 48.94 63.45 -188.94 0.64 0.43 0.77 0.84 0.65 61 0.18 0 2.3 0 1.0024 0.0026 0.010 0 1.538 0.008 1.670 1.14 -1.03 1.06 -0.55 0.35 1.07 0.05 0.14 0.06 1.21 0.17 -0.33 0.53 -1.05 1.00 + 24436 5 0 1 1.3724302998 -0.1431456148 3.78 1.31 0.50 0.30 0.25 0.34 0.40 0.30 73 12.27 0 0.0 0 0.1930 0.0015 0.014 1 -0.030 0.600 0.030 1.06 -0.07 1.17 -0.54 0.61 1.00 0.19 1.79 0.08 1.00 2.42 3.61 -0.20 -0.46 1.00 + 32349 0 4 1 1.7678185359 -0.2916993748 379.21 -546.01 -1223.07 1.21 1.04 1.58 1.33 1.24 47 0.00 0 0.0 0 -1.0876 0.0024 0.040 0 0.009 0.007 -0.020 0.83 -0.04 0.96 -0.21 0.32 0.68 0.46 -0.12 0.13 0.90 -0.20 0.62 -0.14 -0.16 1.00 + 91262 1 0 1 4.8735545728 0.6768909262 130.23 200.94 286.23 0.26 0.28 0.36 0.32 0.40 98 -0.14 0 1.8 0 0.0868 0.0021 0.014 2 -0.001 0.005 -0.010 1.01 0.00 1.00 0.24 0.08 1.11 -0.02 -0.09 1.14 1.00 -0.09 -0.18 0.76 -0.08 1.00 diff --git a/star/catalog/testdata/name.fab b/star/catalog/testdata/name.fab new file mode 100644 index 0000000..d8a73fc --- /dev/null +++ b/star/catalog/testdata/name.fab @@ -0,0 +1,6 @@ + 32349|α_CMa + 32349|9_CMa + 91262|α_Lyr + 91262|3_Lyr + 24436|β_Ori + 24436|19_Ori diff --git a/star/catalog/testdata/stars_0_0v0_18.cat b/star/catalog/testdata/stars_0_0v0_18.cat new file mode 100644 index 0000000000000000000000000000000000000000..7426fe19f8d9290630f0835293bf55edb314e0b8 GIT binary patch literal 352 zcmd;LiEn0r0wE}E@Z5a{$jmS!of)V?jv-%Cg`x64Cxga;ejSFM$qbhJ8tq$G?qtX|)ttyL_(1!N5(7iL z2?N7Upv!BS{|Z+!vN3G`$G{+tWWH6igSfuj+TtZe|K4%^{{P~an7xM5mc4y<@NL*!e2OzGykS1dRg++zRymsfcApMk;Wmw?wihLishM51G7&sp6V+8;wGgR;Z literal 0 HcmV?d00001 diff --git a/star/catalog/types.go b/star/catalog/types.go new file mode 100644 index 0000000..e699cc8 --- /dev/null +++ b/star/catalog/types.go @@ -0,0 +1,242 @@ +package catalog + +import ( + "math" + "sort" + "strings" + "time" + + "b612.me/astro/basic" +) + +// Entry 一条外挂星表记录 / one external catalog record. +type Entry struct { + ID string + Aliases []string + Coordinate basic.StarCoordinate + Magnitude float64 + Band Band + BPRP float64 + BV float64 + Component string +} + +// Band 星等波段 / photometric band. +type Band string + +const ( + // BandV Johnson V 波段 / Johnson V band. + BandV Band = "V" + // BandG Gaia G 波段 / Gaia G band. + BandG Band = "G" + // BandHp Hipparcos Hp 波段 / Hipparcos Hp band. + BandHp Band = "Hp" +) + +// ColumnMap 描述分隔文本的列与单位 / delimited-text columns and units. +type ColumnMap struct { + ID, RA, Dec, PMRA, PMDec, Parallax, RadialVelocity, Magnitude, BPRP, BV string + PMRAIncludesCosDec bool + Band Band + Epoch time.Time + EpochColumn string + Frame basic.CoordinateFrame + IDPrefix string + AnglesInRadians bool +} + +// ReadOptions 控制读取严格性和入选过滤 / read strictness and admission filtering. +type ReadOptions struct { + Strict bool + Delimiter rune + Filter func(Entry) bool +} + +// Catalog 是不可变的外挂星表容器 / immutable external-star catalog. +type Catalog struct { + entries []Entry + lookup map[string]int + // bands 按 0.5° 赤纬分带,带内按 RA 升序存放记录下标。 + bands [decBandCount][]int + skipped int +} + +func newCatalog() *Catalog { + return &Catalog{lookup: make(map[string]int)} +} + +func (c *Catalog) add(entry Entry) { + entry.ID = strings.TrimSpace(entry.ID) + entry.Aliases = uniqueStrings(entry.Aliases) + c.entries = append(c.entries, entry) +} + +// dropDuplicates 同主 ID 同分量只保留首条,其余计入跳过数。 +func (c *Catalog) dropDuplicates() { + seen := make(map[string]struct{}, len(c.entries)) + kept := c.entries[:0] + for _, entry := range c.entries { + if normalizeKey(entry.ID) != "" { + key := entryKey(entry) + if _, exists := seen[key]; exists { + c.skipped++ + continue + } + seen[key] = struct{}{} + } + kept = append(kept, entry) + } + c.entries = kept +} + +func (c *Catalog) finalize() { + c.dropDuplicates() + c.lookup = make(map[string]int, len(c.entries)*2) + c.bands = [decBandCount][]int{} + for i := range c.entries { + entry := &c.entries[i] + entry.Aliases = uniqueStrings(entry.Aliases) + if key := normalizeKey(entry.ID); key != "" { + if _, exists := c.lookup[key]; !exists { + c.lookup[key] = i + } + } + for _, alias := range entry.Aliases { + key := normalizeKey(alias) + if key != "" { + if _, exists := c.lookup[key]; !exists { + c.lookup[key] = i + } + } + } + band := decBand(entry.Coordinate.Dec) + c.bands[band] = append(c.bands[band], i) + } + for band := range c.bands { + if len(c.bands[band]) < 2 { + continue + } + sort.SliceStable(c.bands[band], func(i, j int) bool { + return c.entries[c.bands[band][i]].Coordinate.RA < c.entries[c.bands[band][j]].Coordinate.RA + }) + } +} + +// Len 返回记录数 / record count. +func (c *Catalog) Len() int { + if c == nil { + return 0 + } + return len(c.entries) +} + +// Entries 返回记录快照 / record snapshot. +func (c *Catalog) Entries() []Entry { + if c == nil { + return nil + } + entries := make([]Entry, len(c.entries)) + copy(entries, c.entries) + for i := range entries { + entries[i].Aliases = append([]string(nil), entries[i].Aliases...) + } + return entries +} + +// Lookup 按主 ID 或别名精确查找,同 ID 多分量时返回最先读入者 / exact lookup by ID or alias; first-read component wins. +func (c *Catalog) Lookup(id string) (Entry, bool) { + if c == nil { + return Entry{}, false + } + i, ok := c.lookup[normalizeKey(id)] + if !ok { + return Entry{}, false + } + entry := c.entries[i] + entry.Aliases = append([]string(nil), entry.Aliases...) + return entry, true +} + +// Skipped 返回宽松模式跳过的记录数 / number of skipped records. +func (c *Catalog) Skipped() int { + if c == nil { + return 0 + } + return c.skipped +} + +// Merge 合并两个星表,后者覆盖同主 ID 同分量记录 / merge catalogs; right-hand records win per ID and component. +func (c *Catalog) Merge(other *Catalog) *Catalog { + merged := newCatalog() + if c != nil { + for _, entry := range c.entries { + merged.add(cloneEntry(entry)) + } + merged.skipped += c.skipped + } + if other != nil { + positions := make(map[string]int, len(merged.entries)) + for i := range merged.entries { + positions[entryKey(merged.entries[i])] = i + } + for _, entry := range other.entries { + key := entryKey(entry) + if i, ok := positions[key]; ok { + aliases := append(merged.entries[i].Aliases, entry.Aliases...) + merged.entries[i] = cloneEntry(entry) + merged.entries[i].Aliases = uniqueStrings(aliases) + } else { + positions[key] = len(merged.entries) + merged.add(cloneEntry(entry)) + } + } + merged.skipped += other.skipped + } + merged.finalize() + return merged +} + +// entryKey 以主 ID 与分量区分记录,双星各分量共用主 ID。 +func entryKey(entry Entry) string { + return normalizeKey(entry.ID) + "\x00" + entry.Component +} + +func cloneEntry(entry Entry) Entry { + entry.Aliases = append([]string(nil), entry.Aliases...) + return entry +} + +func normalizeKey(value string) string { + return strings.ToLower(strings.Join(strings.Fields(value), " ")) +} + +func uniqueStrings(values []string) []string { + seen := make(map[string]struct{}, len(values)) + result := make([]string, 0, len(values)) + for _, value := range values { + value = strings.Join(strings.Fields(strings.TrimSpace(value)), " ") + if value == "" { + continue + } + key := normalizeKey(value) + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + result = append(result, value) + } + return result +} + +const decBandCount = 360 + +func decBand(dec float64) int { + band := int(math.Floor((dec + 90) * 2)) + if band < 0 { + return 0 + } + if band >= decBandCount { + return decBandCount - 1 + } + return band +} diff --git a/star/rise_set_atmosphere.go b/star/rise_set_atmosphere.go new file mode 100644 index 0000000..41842b5 --- /dev/null +++ b/star/rise_set_atmosphere.go @@ -0,0 +1,23 @@ +package star + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 恒星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射;其余参数同 RiseTime / stellar rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, ra, dec, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.StarRiseTimeWithAtmosphere(basic.Date2JD(date), ra, dec, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 恒星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射;其余参数同 SetTime / stellar set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, ra, dec, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.StarSetTimeWithAtmosphere(basic.Date2JD(date), ra, dec, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC) + return riseSetResult(date, jd, err) +} diff --git a/star/rise_set_atmosphere_test.go b/star/rise_set_atmosphere_test.go new file mode 100644 index 0000000..4ead466 --- /dev/null +++ b/star/rise_set_atmosphere_test.go @@ -0,0 +1,29 @@ +package star + +import ( + "math" + "testing" + "time" +) + +func TestStarRiseSetWithAtmosphere(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + date := time.Date(2025, 1, 1, 0, 0, 0, 0, loc) + for _, rise := range []bool{true, false} { + plainFn, atmosFn := SetTime, SetTimeWithAtmosphere + if rise { + plainFn, atmosFn = RiseTime, RiseTimeWithAtmosphere + } + plain, err := plainFn(date, 101.29, -16.72, 116.4, 39.9, 0, true) + if err != nil { + t.Fatal(err) + } + std, err := atmosFn(date, 101.29, -16.72, 116.4, 39.9, 0, 1010, 10) + if err != nil { + t.Fatal(err) + } + if d := math.Abs(std.Sub(plain).Seconds()); d > 5 { + t.Errorf("rise=%v: standard weather differs by %.2f s", rise, d) + } + } +} diff --git a/sun/api_default.go b/sun/api_default.go new file mode 100644 index 0000000..8241ecf --- /dev/null +++ b/sun/api_default.go @@ -0,0 +1,203 @@ +package sun + +import ( + "time" +) + +// Altitude 太阳站心视高度角,不含大气折射 / topocentric apparent solar altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 太阳站心视高度角(n 变体) / n-parameterised topocentric apparent solar altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentAltitude 太阳视高度角 / apparent solar altitude. +func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return defaultModule.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) +} + +// ApparentAltitudeN 太阳视高度角(n 变体) / n-parameterised apparent solar altitude. +func ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { + return defaultModule.ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n) +} + +// ApparentDec 太阳地心视赤纬 / apparent geocentric declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentLo 太阳视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentRa 太阳地心视赤经 / apparent geocentric right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 太阳地心视赤经、视赤纬 / apparent geocentric right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentSolarTime 真太阳时,结果时区为按经度换算的地方平太阳时 / apparent solar time, result is a longitude-derived mean-solar zone. +func ApparentSolarTime(date time.Time, lon float64) time.Time { + return defaultModule.ApparentSolarTime(date, lon) +} + +// ApparentSolarTimeN 真太阳时(n 变体) / n-parameterised apparent solar time. +func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.ApparentSolarTimeN(date, lon, n) +} + +// ApparentZenith 太阳视天顶距 / apparent solar zenith distance. +func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return defaultModule.ApparentZenith(date, lon, lat, pressureHPa, temperatureC) +} + +// ApparentZenithN 太阳视天顶距(n 变体) / n-parameterised apparent solar zenith distance. +func ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { + return defaultModule.ApparentZenithN(date, lon, lat, pressureHPa, temperatureC, n) +} + +// Azimuth 太阳站心方位角,自正北向东量 / topocentric solar azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 太阳站心方位角(n 变体) / n-parameterised topocentric solar azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// CulminationTime 太阳中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 太阳中天时刻(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// Diameter 太阳视直径,单位角秒 / apparent solar diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 太阳视直径(n 变体),单位角秒 / n-parameterised apparent solar diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 日落时刻别名 / deprecated sunset alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 日落时刻别名(n 变体) / deprecated n-parameterised sunset alias. +// +// Deprecated: use SetTimeN instead. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 日地距离 / Earth-Sun distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EquationTime 均时差 / equation of time. +func EquationTime(date time.Time) float64 { return defaultModule.EquationTime(date) } + +// EveningTwilight 暮光终时,取 date 所在当地民用日 / evening twilight, on the local civil day of date. +func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { + return defaultModule.EveningTwilight(date, lon, lat, angle) +} + +// EveningTwilightN 暮光终时(n 变体),取 date 所在当地民用日 / n-parameterised evening twilight, on the local civil day of date. +func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { + return defaultModule.EveningTwilightN(date, lon, lat, angle, n) +} + +// HourAngle 太阳地心视时角,不含站心视差,lat 不参与计算仅为参数形状一致 / geocentric apparent solar hour angle without topocentric parallax, lat is unused, kept for signature symmetry. +func HourAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.HourAngle(date, lon, lat) +} + +// HourAngleN 太阳时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, lat, n) +} + +// MorningTwilight 晨光始时,angle 常取 -6/-12/-18 度 / morning twilight, angle typically -6/-12/-18. +func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { + return defaultModule.MorningTwilight(date, lon, lat, angle) +} + +// MorningTwilightN 晨光始时(n 变体) / n-parameterised morning twilight. +func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { + return defaultModule.MorningTwilightN(date, lon, lat, angle, n) +} + +// ParallacticAngle 太阳视差角(天顶方向角) / solar parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 太阳视差角(天顶方向角)(n 变体) / n-parameterised solar parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Physical 太阳物理观测参数 / physical observing parameters of the Sun. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 太阳物理观测参数(n 变体) / n-parameterised physical observing parameters of the Sun. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 日出时刻,取 date 所在当地民用日;aero 为 true 时按上缘过地平线 / sunrise time, on the local civil day of date; aero uses an upper-limb crossing. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 日出时刻(n 变体) / n-parameterised sunrise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 太阳视半径,单位角秒 / apparent solar semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 太阳视半径(n 变体),单位角秒 / n-parameterised apparent solar semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 日落时刻,取 date 所在当地民用日 / sunset time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 日落时刻(n 变体),取 date 所在当地民用日 / n-parameterised sunset time, on the local civil day of date. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// TrueBo 太阳真黄纬 / true ecliptic latitude. +func TrueBo(date time.Time) float64 { return defaultModule.TrueBo(date) } + +// TrueBoN 太阳真黄纬(n 变体) / n-parameterised true ecliptic latitude. +func TrueBoN(date time.Time, n int) float64 { return defaultModule.TrueBoN(date, n) } + +// TrueLo 太阳真黄经 / true ecliptic longitude. +func TrueLo(date time.Time) float64 { return defaultModule.TrueLo(date) } + +// TrueLoN 太阳真黄经(n 变体) / n-parameterised true ecliptic longitude. +func TrueLoN(date time.Time, n int) float64 { return defaultModule.TrueLoN(date, n) } + +// Zenith 太阳天顶距 / solar zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 太阳天顶距(n 变体) / n-parameterised solar zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// RiseTimeWithAtmosphere 日出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunrise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 日落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunset time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/sun/atmosphere_test.go b/sun/atmosphere_test.go new file mode 100644 index 0000000..f48cbca --- /dev/null +++ b/sun/atmosphere_test.go @@ -0,0 +1,26 @@ +package sun + +import ( + "testing" + "time" +) + +func TestRiseSetWithAtmosphere(t *testing.T) { + date := time.Date(2025, 1, 1, 0, 0, 0, 0, time.FixedZone("CST", 8*3600)) + base, err := RiseTime(date, 116.4, 39.9, 0, true) + if err != nil { + t.Fatal(err) + } + same, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1010, 10) + if diff := same.Sub(base); err != nil || diff > 10*time.Millisecond || diff < -10*time.Millisecond { + t.Fatalf("standard weather %v vs %v (%v)", same, base, err) + } + cold, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1040, -30) + if err != nil || !cold.Before(base) { + t.Fatalf("cold dense air should advance sunrise: %v vs %v (%v)", cold, base, err) + } + set, err := SetTimeWithAtmosphere(date, 116.4, 39.9, 0, 1040, -30) + if err != nil || set.Location() != date.Location() { + t.Fatalf("sunset: %v %v", set, err) + } +} diff --git a/sun/context.go b/sun/context.go new file mode 100644 index 0000000..ec11b92 --- /dev/null +++ b/sun/context.go @@ -0,0 +1,38 @@ +package sun + +import ( + "time" + + "b612.me/astro/internal/ephem" +) + +// Sun 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Sun struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Sun { return &Sun{inst: inst} } + +var defaultModule Sun + +// AxialtiltNutation 交角章动(IAU 2000B) / nutation in obliquity, IAU 2000B. +func (module *Sun) AxialtiltNutation(date time.Time) float64 { return AxialtiltNutation(date) } + +// AxialtiltNutation1980 交角章动(IAU 1980) / nutation in obliquity, IAU 1980. +func (module *Sun) AxialtiltNutation1980(date time.Time) float64 { return AxialtiltNutation1980(date) } + +// EclipticNutation 黄经章动(IAU 2000B) / nutation in longitude, IAU 2000B. +func (module *Sun) EclipticNutation(date time.Time) float64 { return EclipticNutation(date) } + +// EclipticNutation1980 黄经章动(IAU 1980) / nutation in longitude, IAU 1980. +func (module *Sun) EclipticNutation1980(date time.Time) float64 { return EclipticNutation1980(date) } + +// EclipticObliquity 黄赤交角 / ecliptic obliquity. +func (module *Sun) EclipticObliquity(date time.Time, nutation bool) float64 { + return EclipticObliquity(date, nutation) +} + +// GeometricLo 太阳几何黄经 / geometric ecliptic longitude. +func (module *Sun) GeometricLo(date time.Time) float64 { return GeometricLo(date) } + +// MidFunc 太阳中心差 / solar equation of center. +func (module *Sun) MidFunc(date time.Time) float64 { return MidFunc(date) } diff --git a/sun/context_test.go b/sun/context_test.go new file mode 100644 index 0000000..7e67364 --- /dev/null +++ b/sun/context_test.go @@ -0,0 +1,79 @@ +package sun + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 + ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 + ApparentDec(date time.Time) float64 + ApparentLo(date time.Time) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentSolarTime(date time.Time, lon float64) time.Time + ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time + ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 + ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 + AxialtiltNutation(date time.Time) float64 + AxialtiltNutation1980(date time.Time) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EclipticNutation(date time.Time) float64 + EclipticNutation1980(date time.Time) float64 + EclipticObliquity(date time.Time, nutation bool) float64 + EquationTime(date time.Time) float64 + EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) + EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) + GeometricLo(date time.Time) float64 + HourAngle(date time.Time, lon, lat float64) float64 + HourAngleN(date time.Time, lon, lat float64, n int) float64 + MidFunc(date time.Time) float64 + MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) + MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + TrueBo(date time.Time) float64 + TrueBoN(date time.Time, n int) float64 + TrueLo(date time.Time) float64 + TrueLoN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Sun)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/sun/diameter.go b/sun/diameter.go index d180de5..3758a47 100644 --- a/sun/diameter.go +++ b/sun/diameter.go @@ -7,23 +7,23 @@ import ( ) // Semidiameter 太阳视半径,单位角秒 / apparent solar semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Sun) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 太阳视半径(截断版),单位角秒 / truncated apparent solar semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 太阳视半径(n 变体),单位角秒 / n-parameterised apparent solar semidiameter in arcseconds. +func (module *Sun) SemidiameterN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.SunSemidiameterN(basic.UTC2TT(jd), n) + return basic.SunSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 太阳视直径,单位角秒 / apparent solar diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Sun) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 太阳视直径(截断版),单位角秒 / truncated apparent solar diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 太阳视直径(n 变体),单位角秒 / n-parameterised apparent solar diameter in arcseconds. +func (module *Sun) DiameterN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.SunDiameterN(basic.UTC2TT(jd), n) + return basic.SunDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/sun/doc.go b/sun/doc.go index bb0c689..12b53a6 100644 --- a/sun/doc.go +++ b/sun/doc.go @@ -1,3 +1,7 @@ // Package sun 太阳位置、升落、晨昏朦影、均时差、真太阳时、视高度角、视差角、视直径与日面物理星历;角度单位为度。 // Package sun covers solar position, rise/set, twilight, equation of time, apparent solar time, apparent altitude, parallactic angle, diameter and solar physical ephemeris; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package sun diff --git a/sun/parallactic.go b/sun/parallactic.go index 1b27665..19f026b 100644 --- a/sun/parallactic.go +++ b/sun/parallactic.go @@ -7,18 +7,16 @@ import ( ) // ParallacticAngle 太阳视差角(天顶方向角) / solar parallactic angle. -// -// lon/lat 为观测者经纬度,东正西负、北正南负;返回值为有符号视差角,单位度。 -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon, lat), ApparentDec(date), lat) +func (module *Sun) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon, lat), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项太阳视差角(天顶方向角) / truncated solar parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { +// ParallacticAngleN 太阳视差角(天顶方向角)(n 变体) / n-parameterised solar parallactic angle. +func (module *Sun) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { jd := basic.Date2JD(date.UTC()) return basic.ParallacticAngleByHourAngle( - HourAngleN(date, lon, lat, n), - basic.HSunApparentDecN(basic.UTC2TT(jd), n), + module.HourAngleN(date, lon, lat, n), + basic.HSunApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst), lat, ) } diff --git a/sun/physical.go b/sun/physical.go index 03ab370..d699d80 100644 --- a/sun/physical.go +++ b/sun/physical.go @@ -17,14 +17,14 @@ type PhysicalInfo struct { } // Physical 太阳物理观测参数 / physical observing parameters of the Sun. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Sun) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 太阳物理观测参数(截断版) / truncated physical observing parameters of the Sun. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 太阳物理观测参数(n 变体) / n-parameterised physical observing parameters of the Sun. +func (module *Sun) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.SunPhysicalN(basic.UTC2TT(jd), n) + info := basic.SunPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ P: info.P, B0: info.B0, diff --git a/sun/refraction.go b/sun/refraction.go index 0ede3ec..a000528 100644 --- a/sun/refraction.go +++ b/sun/refraction.go @@ -7,21 +7,21 @@ import ( ) // ApparentAltitude 太阳视高度角 / apparent solar altitude. -func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { - return basic.ApparentAltitude(Altitude(date, lon, lat), pressureHPa, temperatureC) +func (module *Sun) ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return basic.ApparentAltitude(module.Altitude(date, lon, lat), pressureHPa, temperatureC) } // ApparentZenith 太阳视天顶距 / apparent solar zenith distance. -func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { - return 90 - ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) +func (module *Sun) ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return 90 - module.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) } -// ApparentAltitudeN 太阳视高度角(截断版) / truncated apparent solar altitude. -func ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { - return basic.ApparentAltitude(AltitudeN(date, lon, lat, n), pressureHPa, temperatureC) +// ApparentAltitudeN 太阳视高度角(n 变体) / n-parameterised apparent solar altitude. +func (module *Sun) ApparentAltitudeN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { + return basic.ApparentAltitude(module.AltitudeN(date, lon, lat, n), pressureHPa, temperatureC) } -// ApparentZenithN 太阳视天顶距(截断版) / truncated apparent solar zenith distance. -func ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { - return 90 - ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n) +// ApparentZenithN 太阳视天顶距(n 变体) / n-parameterised apparent solar zenith distance. +func (module *Sun) ApparentZenithN(date time.Time, lon, lat, pressureHPa, temperatureC float64, n int) float64 { + return 90 - module.ApparentAltitudeN(date, lon, lat, pressureHPa, temperatureC, n) } diff --git a/sun/rise_set_altitude.go b/sun/rise_set_altitude.go new file mode 100644 index 0000000..3b76b65 --- /dev/null +++ b/sun/rise_set_altitude.go @@ -0,0 +1,42 @@ +package sun + +import ( + "time" + + "b612.me/astro/basic" +) + +// AltitudeLimb 取高度的位置(上缘、中心、下缘)/ limb used for the altitude. +type AltitudeLimb = basic.AltitudeLimb + +const ( + LimbUpper = basic.AltitudeLimbUpper + LimbCenter = basic.AltitudeLimbCenter + LimbLower = basic.AltitudeLimbLower +) + +// AltitudeOptions 升落选项;零值为上缘、标准气象、计入折射 / altitude crossing options. +type AltitudeOptions = basic.AltitudeCrossingOptions + +// AltitudeState 一个民用日内的穿越状态 / crossing state over one civil day. +type AltitudeState = basic.AltitudeCrossingState + +const ( + AltitudeCrosses = basic.AltitudeCrosses + AltitudeAlwaysAbove = basic.AltitudeAlwaysAbove + AltitudeAlwaysBelow = basic.AltitudeAlwaysBelow + AltitudeTangent = basic.AltitudeTangent +) + +// AltitudeCrossingResult date 所在民用日内首次升、落与残差范围(度)/ first rise and set on the civil day. +type AltitudeCrossingResult = basic.AltitudeCrossingResult + +// RiseSet 太阳在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制太阳缘与大气 / solar rise and set on the civil day. +func (module *Sun) RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return basic.SkyAltitudeCrossingOnDate(basic.SkySun, date, lon, lat, height, -basic.HeightDegreeByLat(height, lat), options, module.inst) +} + +// RiseSet 太阳在 date 所在民用日内穿过观测者地平线(含 height 带来的地平俯角)的升落;options 控制太阳缘与大气 / solar rise and set on the civil day. +func RiseSet(date time.Time, lon, lat, height float64, options AltitudeOptions) (AltitudeCrossingResult, error) { + return defaultModule.RiseSet(date, lon, lat, height, options) +} diff --git a/sun/rise_set_altitude_test.go b/sun/rise_set_altitude_test.go new file mode 100644 index 0000000..7debc97 --- /dev/null +++ b/sun/rise_set_altitude_test.go @@ -0,0 +1,74 @@ +// 被测契约:默认选项与 RiseTime/SetTime 一致;夏令时切换日结果落在当天;实例方法使用绑定实例。 +package sun + +import ( + "testing" + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +func TestRiseSetMatchesRiseTimeSetTime(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + for i := 0; i < 30; i++ { + date := time.Date(2025, 1, 1, 12, 0, 0, 0, loc).AddDate(0, 0, i*12) + got, err := RiseSet(date, 116.4, 39.9, 50, basic.AltitudeCrossingOptions{}) + if err != nil { + t.Fatal(err) + } + checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, 116.4, 39.9, 50, true) }) + checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, 116.4, 39.9, 50, true) }) + } +} + +func TestRiseSetDaylightSavingDays(t *testing.T) { + loc, err := time.LoadLocation("America/New_York") + if err != nil { + t.Skip(err) + } + for _, date := range []time.Time{ + time.Date(2023, 3, 12, 12, 0, 0, 0, loc), + time.Date(2023, 11, 5, 12, 0, 0, 0, loc), + } { + got, err := RiseSet(date, -74, 40.7, 0, basic.AltitudeCrossingOptions{}) + if err != nil { + t.Fatal(err) + } + checkEvent(t, date, "rise", got.Rise, got.HasRise, func() (time.Time, error) { return RiseTime(date, -74, 40.7, 0, true) }) + checkEvent(t, date, "set", got.Set, got.HasSet, func() (time.Time, error) { return SetTime(date, -74, 40.7, 0, true) }) + if got.Rise.Location() != loc || got.Rise.YearDay() != date.YearDay() || got.Set.YearDay() != date.YearDay() { + t.Errorf("%s: events %v %v outside civil day", date.Format("2006-01-02"), got.Rise, got.Set) + } + } +} + +func TestRiseSetUsesModuleInstance(t *testing.T) { + date := time.Date(2025, 3, 20, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + inst := ephem.NewModelInstance(ephem.Legacy) + got, err := NewWithInstance(inst).RiseSet(date, 116.4, 39.9, 0, basic.AltitudeCrossingOptions{Limb: basic.AltitudeLimbCenter}) + if err != nil { + t.Fatal(err) + } + want, err := basic.SkyAltitudeCrossingOnDate(basic.SkySun, date, 116.4, 39.9, 0, 0, basic.AltitudeCrossingOptions{Limb: basic.AltitudeLimbCenter}, inst) + if err != nil { + t.Fatal(err) + } + if !got.Rise.Equal(want.Rise) || !got.Set.Equal(want.Set) { + t.Errorf("instance result %+v, want %+v", got, want) + } +} + +func checkEvent(t *testing.T, date time.Time, kind string, got time.Time, has bool, ref func() (time.Time, error)) { + t.Helper() + want, err := ref() + if has != (err == nil) { + t.Errorf("%s %s: has=%v, reference err=%v", date.Format("2006-01-02"), kind, has, err) + return + } + if has { + if d := got.Sub(want); d > 100*time.Millisecond || d < -100*time.Millisecond { + t.Errorf("%s %s: %v, reference %v", date.Format("2006-01-02"), kind, got, want) + } + } +} diff --git a/sun/sun.go b/sun/sun.go index 8ab452a..fa96031 100644 --- a/sun/sun.go +++ b/sun/sun.go @@ -1,12 +1,12 @@ package sun import ( - "b612.me/astro/internal/civiltime" "errors" "math" "time" "b612.me/astro/basic" + "b612.me/astro/internal/civiltime" ) var ( @@ -50,15 +50,9 @@ func twilightResult(date time.Time, jd float64, err error) (time.Time, error) { */ // RiseTime 日出时刻 / sunrise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负; -// height 为椭球高(大地高),单位米;aero 为 true 时按动态标准大气折射和实时太阳视半径计算上缘过地平线。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive; -// height 为观测者椭球高(大地高),单位米;aero 使用动态标准折射和实时太阳视半径计算上缘过地平线。 -// height is observer elevation in meters; aero uses dynamic standard refraction and the instantaneous solar semidiameter for an upper-limb crossing. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Sun) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return RiseTime(d, lon, lat, height, aero) + return module.RiseTime(d, lon, lat, height, aero) }); handled { return result, err } @@ -72,17 +66,14 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e _, loc := date.Zone() timezone := float64(loc) / 3600.0 // 返回值保持与输入 date 一致的时区。 - riseJD, err := basic.GetSunRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.GetSunRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } -// RiseTimeN 截断项日出时刻 / truncated sunrise time. -// -// 参数与 RiseTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as RiseTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 日出时刻(n 变体) / n-parameterised sunrise time. +func (module *Sun) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return RiseTimeN(d, lon, lat, height, aero, n) + return module.RiseTimeN(d, lon, lat, height, aero, n) }); handled { return result, err } @@ -94,37 +85,24 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.GetSunRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.GetSunRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 日落时刻别名 / deprecated sunset alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Sun) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } -// DownTimeN 截断项日落时刻别名 / deprecated truncated sunset alias. -// -// Deprecated: use SetTimeN instead. -// -// 参数与 SetTimeN 相同,仅为兼容旧接口保留。 -// Same as SetTimeN and kept only for backward compatibility. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 日落时刻别名(n 变体) / deprecated n-parameterised sunset alias. +func (module *Sun) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } // SetTime 日落时刻 / sunset time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的日落时刻。 -// Uses the same inputs as RiseTime and returns the sunset time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Sun) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return SetTime(d, lon, lat, height, aero) + return module.SetTime(d, lon, lat, height, aero) }); handled { return result, err } @@ -136,17 +114,14 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - downJD, err := basic.GetSunSetTime(localJD, lon, lat, timezone, aeroFloat, height) + downJD, err := basic.GetSunSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, downJD, err) } -// SetTimeN 截断项日落时刻 / truncated sunset time. -// -// 参数与 RiseTimeN 相同,返回给定当地日期内的日落时刻。 -// Uses the same inputs as RiseTimeN and returns the sunset time on the corresponding local civil day. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 日落时刻(n 变体) / n-parameterised sunset time. +func (module *Sun) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return SetTimeN(d, lon, lat, height, aero, n) + return module.SetTimeN(d, lon, lat, height, aero, n) }); handled { return result, err } @@ -158,19 +133,14 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - downJD, err := basic.GetSunSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + downJD, err := basic.GetSunSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, downJD, err) } // MorningTwilight 晨光始时 / morning twilight. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon/lat 为观测者经纬度,东正西负、北正南负; -// angle 为目标太阳高度角,常用 -6/-12/-18 度,分别对应民用、航海、天文朦影。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon/lat are east-positive and north-positive; -// angle is the target solar altitude in degrees, typically -6, -12, or -18 for civil, nautical, and astronomical twilight. -func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { +func (module *Sun) MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return MorningTwilight(d, lon, lat, angle) + return module.MorningTwilight(d, lon, lat, angle) }); handled { return result, err } @@ -178,17 +148,14 @@ func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD, err := basic.MorningTwilight(localJD, lon, lat, timezone, angle) + calcJD, err := basic.MorningTwilightWithInstance(localJD, lon, lat, timezone, angle, module.inst) return twilightResult(date, calcJD, err) } -// MorningTwilightN 截断项晨光始时 / truncated morning twilight. -// -// 参数与 MorningTwilight 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as MorningTwilight. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { +// MorningTwilightN 晨光始时(n 变体) / n-parameterised morning twilight. +func (module *Sun) MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return MorningTwilightN(d, lon, lat, angle, n) + return module.MorningTwilightN(d, lon, lat, angle, n) }); handled { return result, err } @@ -196,17 +163,14 @@ func MorningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD, err := basic.MorningTwilightN(localJD, lon, lat, timezone, angle, n) + calcJD, err := basic.MorningTwilightNWithInstance(localJD, lon, lat, timezone, angle, n, module.inst) return twilightResult(date, calcJD, err) } // EveningTwilight 暮光终时 / evening twilight. -// -// 参数与 MorningTwilight 相同,返回对应当地日期的暮光结束时刻。 -// Uses the same inputs as MorningTwilight and returns the evening-twilight time on the corresponding local civil day. -func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { +func (module *Sun) EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return EveningTwilight(d, lon, lat, angle) + return module.EveningTwilight(d, lon, lat, angle) }); handled { return result, err } @@ -215,17 +179,14 @@ func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) _, loc := date.Zone() timezone := float64(loc) / 3600.0 //不需要进行力学时转换,会在GetBanTime中转换 - calcJD, err := basic.EveningTwilight(localJD, lon, lat, timezone, angle) + calcJD, err := basic.EveningTwilightWithInstance(localJD, lon, lat, timezone, angle, module.inst) return twilightResult(date, calcJD, err) } -// EveningTwilightN 截断项暮光终时 / truncated evening twilight. -// -// 参数与 MorningTwilightN 相同,返回对应当地日期的暮光结束时刻。 -// Uses the same inputs as MorningTwilightN and returns the evening-twilight time on the corresponding local civil day. -func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { +// EveningTwilightN 暮光终时(n 变体) / n-parameterised evening twilight. +func (module *Sun) EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time, error) { if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { - return EveningTwilightN(d, lon, lat, angle, n) + return module.EveningTwilightN(d, lon, lat, angle, n) }); handled { return result, err } @@ -233,7 +194,7 @@ func EveningTwilightN(date time.Time, lon, lat, angle float64, n int) (time.Time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD, err := basic.EveningTwilightN(localJD, lon, lat, timezone, angle, n) + calcJD, err := basic.EveningTwilightNWithInstance(localJD, lon, lat, timezone, angle, n, module.inst) return twilightResult(date, calcJD, err) } @@ -305,81 +266,57 @@ func GeometricLo(date time.Time) float64 { } // TrueLo 太阳真黄经 / true ecliptic longitude. -// -// 返回 date 对应绝对时刻的太阳真黄经,单位度。 -// Returns the Sun's true ecliptic longitude at the instant represented by date, in degrees. -func TrueLo(date time.Time) float64 { +func (module *Sun) TrueLo(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunTrueLo(basic.UTC2TT(jd)) + return basic.HSunTrueLoWithInstance(basic.UTC2TT(jd), module.inst) } -// TrueLoN 截断项太阳真黄经 / truncated true ecliptic longitude. -// -// 参数与 TrueLo 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as TrueLo. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func TrueLoN(date time.Time, n int) float64 { +// TrueLoN 太阳真黄经(n 变体) / n-parameterised true ecliptic longitude. +func (module *Sun) TrueLoN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HSunTrueLoN(basic.UTC2TT(jd), n) + return basic.HSunTrueLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } // TrueBo 太阳真黄纬 / true ecliptic latitude. -// -// 返回 date 对应绝对时刻的太阳真黄纬,单位度。 -// Returns the Sun's true ecliptic latitude at the instant represented by date, in degrees. -func TrueBo(date time.Time) float64 { +func (module *Sun) TrueBo(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunTrueBo(basic.UTC2TT(jd)) + return basic.HSunTrueBoWithInstance(basic.UTC2TT(jd), module.inst) } -// TrueBoN 截断项太阳真黄纬 / truncated true ecliptic latitude. -// -// 参数与 TrueBo 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as TrueBo. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func TrueBoN(date time.Time, n int) float64 { +// TrueBoN 太阳真黄纬(n 变体) / n-parameterised true ecliptic latitude. +func (module *Sun) TrueBoN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.HSunTrueBoN(basic.UTC2TT(jd), n) + return basic.HSunTrueBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } // ApparentLo 太阳视黄经 / apparent ecliptic longitude. -// -// 返回 date 对应绝对时刻的太阳视黄经,单位度。 -// Returns the Sun's apparent ecliptic longitude at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Sun) ApparentLo(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunApparentLo(basic.UTC2TT(jd)) + return basic.HSunApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 太阳地心视赤经 / apparent geocentric right ascension. -// -// 返回 date 对应绝对时刻的太阳地心视赤经,单位度。 -// Returns the Sun's apparent geocentric right ascension at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Sun) ApparentRa(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunApparentRa(basic.UTC2TT(jd)) + return basic.HSunApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 太阳地心视赤纬 / apparent geocentric declination. -// -// 返回 date 对应绝对时刻的太阳地心视赤纬,单位度。 -// Returns the Sun's apparent geocentric declination at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Sun) ApparentDec(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunApparentDec(basic.UTC2TT(jd)) + return basic.HSunApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 太阳地心视赤经、视赤纬 / apparent geocentric right ascension and declination. -// -// 返回 date 对应绝对时刻的太阳地心视赤经与视赤纬,单位度。 -// Returns the Sun's apparent geocentric right ascension and declination at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Sun) ApparentRaDec(date time.Time) (float64, float64) { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.HSunApparentRaDec(basic.UTC2TT(jd)) + return basic.HSunApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // MidFunc 太阳中心差 / solar equation of center. @@ -393,142 +330,101 @@ func MidFunc(date time.Time) float64 { } // EquationTime 均时差 / equation of time. -// -// 返回 date 对应绝对时刻的均时差,单位小时。 -// Returns the equation of time at the instant represented by date, in hours. -func EquationTime(date time.Time) float64 { +func (module *Sun) EquationTime(date time.Time) float64 { //转换为UTC时间 jd := basic.Date2JD(date.UTC()) - return basic.SunTime(basic.UTC2TT(jd)) + return basic.SunTimeWithInstance(basic.UTC2TT(jd), module.inst) } -// HourAngle 太阳时角 / hour angle. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;返回值单位度。 -// lat 目前不参与计算,仅为与其他观测接口保持参数形状一致。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the result is in degrees. -// lat is currently unused and kept only for API symmetry with the other observation helpers. -func HourAngle(date time.Time, lon, lat float64) float64 { +// HourAngle 太阳地心视时角,不含站心视差 / geocentric apparent solar hour angle without topocentric parallax. +func (module *Sun) HourAngle(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunTimeAngle(localJD, lon, lat, timezone) + return basic.SunTimeAngleWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngleN 截断项太阳时角 / truncated hour angle. -// -// 参数与 HourAngle 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as HourAngle. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func HourAngleN(date time.Time, lon, lat float64, n int) float64 { +// HourAngleN 太阳时角(n 变体) / n-parameterised hour angle. +func (module *Sun) HourAngleN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunTimeAngleN(localJD, lon, lat, timezone, n) + return basic.SunTimeAngleNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// Azimuth 太阳方位角 / azimuth. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 太阳站心方位角 / topocentric solar azimuth. +func (module *Sun) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunAzimuth(localJD, lon, lat, timezone) + return basic.SunAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// AzimuthN 截断项太阳方位角 / truncated azimuth. -// -// 参数与 Azimuth 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as Azimuth. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 太阳站心方位角(n 变体) / n-parameterised topocentric solar azimuth. +func (module *Sun) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunAzimuthN(localJD, lon, lat, timezone, n) + return basic.SunAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// Altitude 太阳高度角 / solar altitude. -// -// date 为观测时刻,会读取其时区参与地方时计算;lon/lat 为观测者经纬度,东正西负、北正南负;返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon/lat are east-positive and north-positive; the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 太阳站心视高度角,不含大气折射 / topocentric apparent solar altitude without refraction. +func (module *Sun) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunHeight(localJD, lon, lat, timezone) + return basic.SunHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 太阳天顶距 / solar zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Sun) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// AltitudeN 截断项太阳高度角 / truncated solar altitude. -// -// 参数与 Altitude 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as Altitude. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 太阳站心视高度角(n 变体) / n-parameterised topocentric apparent solar altitude. +func (module *Sun) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.SunHeightN(localJD, lon, lat, timezone, n) + return basic.SunHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 截断项太阳天顶距 / truncated solar zenith distance. -// -// 参数与 AltitudeN 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as AltitudeN and returns the zenith distance in degrees. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 太阳天顶距(n 变体) / n-parameterised solar zenith distance. +func (module *Sun) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } // CulminationTime 太阳中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Sun) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) + 0.5 _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.CulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.CulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// CulminationTimeN 截断项太阳中天时刻 / truncated culmination time. -// -// 参数与 CulminationTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as CulminationTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 太阳中天时刻(n 变体) / n-parameterised culmination time. +func (module *Sun) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) + 0.5 _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.CulminationTimeN(localJD, lon, timezone, n) - timezone/24.00 + calcJD := basic.CulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // EarthDistance 日地距离 / Earth-Sun distance. -// -// 返回 date 对应绝对时刻的日地距离,单位 AU。 -// Returns the Earth-Sun distance at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Sun) EarthDistance(date time.Time) float64 { jd := basic.Date2JD(date.UTC()) jde := basic.UTC2TT(jd) - return basic.EarthAway(jde) + return basic.EarthAwayWithInstance(jde, module.inst) } // ApparentSolarTime 真太阳时 / apparent solar time. -// -// 返回 date 这一绝对时刻在给定经度 lon 处对应的真太阳时,结果时区为按经度换算的地方平太阳时区。 -// Returns the apparent solar time for the instant represented by date at longitude lon. The result uses a synthetic local-solar time zone derived from longitude. -func ApparentSolarTime(date time.Time, lon float64) time.Time { +func (module *Sun) ApparentSolarTime(date time.Time, lon float64) time.Time { //真太阳时=太阳时角+12小时 - trueTime := (HourAngle(date, lon, 0) + 180) / 15 + trueTime := (module.HourAngle(date, lon, 0) + 180) / 15 if trueTime > 24 { trueTime -= 24 } @@ -548,12 +444,9 @@ func ApparentSolarTime(date time.Time, lon float64) time.Time { time.FixedZone("LTZ", int(lon*3600.00/15.0))) } -// ApparentSolarTimeN 截断项真太阳时 / truncated apparent solar time. -// -// 参数与 ApparentSolarTime 相同;n<0 使用当前仓库内嵌的全部 VSOP 项,其余值用于截断太阳位置级数。 -// Uses the same inputs as ApparentSolarTime. n<0 keeps all embedded VSOP terms in this repository; other values truncate the solar series. -func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time { - trueTime := (HourAngleN(date, lon, 0, n) + 180) / 15 +// ApparentSolarTimeN 真太阳时(n 变体) / n-parameterised apparent solar time. +func (module *Sun) ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time { + trueTime := (module.HourAngleN(date, lon, 0, n) + 180) / 15 if trueTime > 24 { trueTime -= 24 } @@ -569,3 +462,31 @@ func ApparentSolarTimeN(date time.Time, lon float64, n int) time.Time { int(trueTime), int(minute), int(second), int((second-math.Floor(second))*1000000000), time.FixedZone("LTZ", int(lon*3600.00/15.0))) } + +// RiseTimeWithAtmosphere 日出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunrise time with explicit pressure and temperature. +func (module *Sun) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { + return module.RiseTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC) + }); handled { + return result, err + } + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + localJD := basic.Date2JD(date) + _, loc := date.Zone() + jd, err := basic.GetSunRiseTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 日落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / sunset time with explicit pressure and temperature. +func (module *Sun) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { + return module.SetTimeWithAtmosphere(d, lon, lat, height, pressureHPa, temperatureC) + }); handled { + return result, err + } + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + localJD := basic.Date2JD(date) + _, loc := date.Zone() + jd, err := basic.GetSunSetTimeWithAtmosphere(localJD, lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} diff --git a/sun/sun_test.go b/sun/sun_test.go index 103ac05..9f68482 100644 --- a/sun/sun_test.go +++ b/sun/sun_test.go @@ -19,7 +19,7 @@ func TestSun(t *testing.T) { if err != nil { t.Fatal(err) } - if d.Format("2006-01-02 15:04:05") != "2020-01-01 08:41:20" { + if d.Format("2006-01-02 15:04:05") != "2020-01-01 08:41:21" { t.Fatal(d.Format("2006-01-02 15:04:05")) } bo := TrueBo(now) diff --git a/sundial/planar.go b/sundial/planar.go index 18aa2fd..1abc307 100644 --- a/sundial/planar.go +++ b/sundial/planar.go @@ -538,6 +538,7 @@ func intervalInteriorSampleAngles(interval HourAngleInterval, step float64) []fl return nil } + step = math.Max(step, minHourAngleSampleStep) samples := make([]float64, 0, int(math.Ceil((interval.End-interval.Start)/step))) first := math.Ceil((interval.Start+negligibleHourAngle)/step) * step for hourAngle := first; hourAngle < interval.End-negligibleHourAngle; hourAngle += step { @@ -554,3 +555,6 @@ func fullDayHourAngleIntervals() []HourAngleInterval { } const negligibleHourAngle = 1e-12 + +// minHourAngleSampleStep 时角采样步长下限,单位度;单区间最多约 36 万个样本。 +const minHourAngleSampleStep = 1e-3 diff --git a/sundial/planar_test.go b/sundial/planar_test.go index 82ea0be..474a997 100644 --- a/sundial/planar_test.go +++ b/sundial/planar_test.go @@ -338,3 +338,16 @@ func assertClose(t *testing.T, name string, got, want, tol float64) { t.Fatalf("%s mismatch: got %.12f want %.12f tol %.12f", name, got, want, tol) } } + +func TestDeclinationCurveBoundsTinyStep(t *testing.T) { + dial := PlanarDial{Latitude: 40, PlaneNormalZenithDistance: 0} + for _, step := range []float64{1e-300, 1e-9} { + samples := 0 + for _, segment := range dial.DeclinationCurve(10, step) { + samples += len(segment.Samples) + } + if samples == 0 || samples > 400000 { + t.Fatalf("step %g: samples = %d", step, samples) + } + } +} diff --git a/timescale.go b/timescale.go index 6585f26..d933330 100644 --- a/timescale.go +++ b/timescale.go @@ -7,26 +7,23 @@ import ( ) // TimeScale 导出时间标签的时标,零值为民用 UTC / output-label time scale, defaulting to civil UTC. -type TimeScale int +type TimeScale = basic.TimeScale const ( // TimeScaleUTC 默认民用时标 / default civil time scale. - TimeScaleUTC TimeScale = iota + TimeScaleUTC = basic.TimeScaleUTC // TimeScaleUT1 UT1 读数,输出 Location 须为 nil 或 UTC / UT1 reading; output Location must be nil or UTC. - TimeScaleUT1 + TimeScaleUT1 = basic.TimeScaleUT1 ) // LabelIn 把民用时刻换成该时标下的时刻值 / converts a civil instant to a value in one scale. func LabelIn(scale TimeScale, date time.Time) time.Time { - if scale == TimeScaleUT1 { - return UT1FromUTC(date) - } - return date + return basic.LabelIn(scale, date) } // UT1FromUTC 民用时刻转 UT1 读数,以 UTC Location 承载 / converts a civil instant to a UT1 reading in a UTC Location. func UT1FromUTC(date time.Time) time.Time { - return utcLabel(basic.UTC2UT1(basic.Date2JD(date.UTC()))) + return basic.UT1FromUTC(date) } // UTCFromUT1 是 UT1FromUTC 的逆 / inverts UT1FromUTC. @@ -46,7 +43,7 @@ func UTCFromTT(tt time.Time) time.Time { // DUT1 返回民用时刻的 UT1−UTC,单位秒 / UT1−UTC in seconds. func DUT1(date time.Time) float64 { - return basic.DUT1Seconds(basic.Date2JD(date.UTC())) + return basic.DUT1(date) } // TCGFromTT 地球时转地心坐标时 / converts TT to TCG. diff --git a/uranus/api_default.go b/uranus/api_default.go new file mode 100644 index 0000000..bf5653f --- /dev/null +++ b/uranus/api_default.go @@ -0,0 +1,266 @@ +package uranus + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 天王星升交点黄经 / ascending node longitude of Uranus. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 天王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Uranus. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 天王星降交点黄经 / descending node longitude of Uranus. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 天王星降交点黄经(n 变体) / n-parameterised descending node longitude of Uranus. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 天王星视直径,单位角秒 / apparent Uranus diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 天王星视直径(n 变体),单位角秒 / n-parameterised apparent Uranus diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastEasternQuadrature 上一次东方照,取 date 所在当地民用日 / previous eastern quadrature, on the local civil day of date. +func LastEasternQuadrature(date time.Time) time.Time { + return defaultModule.LastEasternQuadrature(date) +} + +// LastOpposition 上一次冲日,取 date 所在当地民用日 / previous opposition, on the local civil day of date. +func LastOpposition(date time.Time) time.Time { return defaultModule.LastOpposition(date) } + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastWesternQuadrature 上一次西方照,取 date 所在当地民用日 / previous western quadrature, on the local civil day of date. +func LastWesternQuadrature(date time.Time) time.Time { + return defaultModule.LastWesternQuadrature(date) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextEasternQuadrature 下一次东方照,取 date 所在当地民用日 / next eastern quadrature, on the local civil day of date. +func NextEasternQuadrature(date time.Time) time.Time { + return defaultModule.NextEasternQuadrature(date) +} + +// NextOpposition 下一次冲日,取 date 所在当地民用日 / next opposition, on the local civil day of date. +func NextOpposition(date time.Time) time.Time { return defaultModule.NextOpposition(date) } + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextWesternQuadrature 下一次西方照,取 date 所在当地民用日 / next western quadrature, on the local civil day of date. +func NextWesternQuadrature(date time.Time) time.Time { + return defaultModule.NextWesternQuadrature(date) +} + +// ParallacticAngle 天王星视差角(天顶方向角) / Uranus parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 天王星视差角(天顶方向角)(n 变体) / n-parameterised Uranus parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 天王星物理观测参数 / physical observing parameters of Uranus. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 天王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Uranus. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// PhysicalSystemIII 天王星 System III 物理观测参数 / Uranus System III physical observing parameters. +func PhysicalSystemIII(date time.Time) PhysicalInfo { return defaultModule.PhysicalSystemIII(date) } + +// PhysicalSystemIIIN 天王星 System III 物理观测参数(n 变体) / n-parameterised Uranus System III physical observing parameters. +func PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { + return defaultModule.PhysicalSystemIIIN(date, n) +} + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 天王星视半径,单位角秒 / apparent Uranus semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 天王星视半径(n 变体),单位角秒 / n-parameterised apparent Uranus semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/uranus/context.go b/uranus/context.go new file mode 100644 index 0000000..1f7e4c0 --- /dev/null +++ b/uranus/context.go @@ -0,0 +1,13 @@ +package uranus + +import ( + "b612.me/astro/internal/ephem" +) + +// Uranus 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Uranus struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Uranus { return &Uranus{inst: inst} } + +var defaultModule Uranus diff --git a/uranus/context_test.go b/uranus/context_test.go new file mode 100644 index 0000000..97823a3 --- /dev/null +++ b/uranus/context_test.go @@ -0,0 +1,94 @@ +package uranus + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastEasternQuadrature(date time.Time) time.Time + LastOpposition(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastWesternQuadrature(date time.Time) time.Time + NextConjunction(date time.Time) time.Time + NextEasternQuadrature(date time.Time) time.Time + NextOpposition(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextWesternQuadrature(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + PhysicalSystemIII(date time.Time) PhysicalInfo + PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Uranus)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/uranus/diameter.go b/uranus/diameter.go index a416f4f..82a7385 100644 --- a/uranus/diameter.go +++ b/uranus/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 天王星视半径,单位角秒 / apparent Uranus semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Uranus) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 天王星视半径(截断版),单位角秒 / truncated apparent Uranus semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 天王星视半径(n 变体),单位角秒 / n-parameterised apparent Uranus semidiameter in arcseconds. +func (module *Uranus) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusSemidiameterN(basic.UTC2TT(jd), n) + return basic.UranusSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 天王星视直径,单位角秒 / apparent Uranus diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Uranus) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 天王星视直径(截断版),单位角秒 / truncated apparent Uranus diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 天王星视直径(n 变体),单位角秒 / n-parameterised apparent Uranus diameter in arcseconds. +func (module *Uranus) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusDiameterN(basic.UTC2TT(jd), n) + return basic.UranusDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/uranus/doc.go b/uranus/doc.go index a416d18..6c5fc8b 100644 --- a/uranus/doc.go +++ b/uranus/doc.go @@ -1,3 +1,7 @@ // Package uranus 天王星位置、升落、合冲、留、方照、相位、视星等、视直径与物理星历,角度单位为度。 // Package uranus covers Uranus position, rise/set, conjunction, opposition, station, quadrature, phase, magnitude, apparent diameter and physical ephemeris; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package uranus diff --git a/uranus/nodes.go b/uranus/nodes.go index ec78177..d551e56 100644 --- a/uranus/nodes.go +++ b/uranus/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 天王星升交点黄经 / ascending node longitude of Uranus. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Uranus) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 天王星升交点黄经(截断版) / truncated ascending node longitude of Uranus. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 天王星升交点黄经(n 变体) / n-parameterised ascending node longitude of Uranus. +func (module *Uranus) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusAscendingNodeN(basic.UTC2TT(jd), n) + return basic.UranusAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 天王星降交点黄经 / descending node longitude of Uranus. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Uranus) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 天王星降交点黄经(截断版) / truncated descending node longitude of Uranus. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 天王星降交点黄经(n 变体) / n-parameterised descending node longitude of Uranus. +func (module *Uranus) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusDescendingNodeN(basic.UTC2TT(jd), n) + return basic.UranusDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/uranus/parallactic.go b/uranus/parallactic.go index 5f69785..1c946cc 100644 --- a/uranus/parallactic.go +++ b/uranus/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 天王星视差角(天顶方向角) / Uranus parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Uranus) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项天王星视差角(天顶方向角) / truncated Uranus parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 天王星视差角(天顶方向角)(n 变体) / n-parameterised Uranus parallactic angle. +func (module *Uranus) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/uranus/phase.go b/uranus/phase.go index d9bdacc..35bb484 100644 --- a/uranus/phase.go +++ b/uranus/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Uranus) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.UranusPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Uranus) PhaseAngleN(date time.Time, n int) float64 { + return basic.UranusPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Uranus) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.UranusIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Uranus) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.UranusIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Uranus) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Uranus) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Uranus) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.UranusBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Uranus) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.UranusBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/uranus/physical.go b/uranus/physical.go index 7deac0a..5d54179 100644 --- a/uranus/physical.go +++ b/uranus/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 天王星物理观测参数 / physical observing parameters of Uranus. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Uranus) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 天王星物理观测参数(截断版) / truncated physical observing parameters of Uranus. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 天王星物理观测参数(n 变体) / n-parameterised physical observing parameters of Uranus. +func (module *Uranus) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.UranusPhysicalN(basic.UTC2TT(jd), n) + info := basic.UranusPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, @@ -39,11 +39,11 @@ func PhysicalN(date time.Time, n int) PhysicalInfo { } // PhysicalSystemIII 天王星 System III 物理观测参数 / Uranus System III physical observing parameters. -func PhysicalSystemIII(date time.Time) PhysicalInfo { - return Physical(date) +func (module *Uranus) PhysicalSystemIII(date time.Time) PhysicalInfo { + return module.Physical(date) } -// PhysicalSystemIIIN 天王星 System III 物理观测参数(截断版) / truncated Uranus System III physical observing parameters. -func PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { - return PhysicalN(date, n) +// PhysicalSystemIIIN 天王星 System III 物理观测参数(n 变体) / n-parameterised Uranus System III physical observing parameters. +func (module *Uranus) PhysicalSystemIIIN(date time.Time, n int) PhysicalInfo { + return module.PhysicalN(date, n) } diff --git a/uranus/rise_set_atmosphere.go b/uranus/rise_set_atmosphere.go new file mode 100644 index 0000000..8c5d9cc --- /dev/null +++ b/uranus/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package uranus + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 天王星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Uranus) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.UranusRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 天王星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Uranus) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.UranusSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 天王星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 天王星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/uranus/truncated.go b/uranus/truncated.go index a76a1bd..b960eef 100644 --- a/uranus/truncated.go +++ b/uranus/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Uranus) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentLoN(basic.UTC2TT(jd), n) + return basic.UranusApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Uranus) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentBoN(basic.UTC2TT(jd), n) + return basic.UranusApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Uranus) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentRaN(basic.UTC2TT(jd), n) + return basic.UranusApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Uranus) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentDecN(basic.UTC2TT(jd), n) + return basic.UranusApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Uranus) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentRaDecN(basic.UTC2TT(jd), n) + return basic.UranusApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Uranus) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusMagN(basic.UTC2TT(jd), n) + return basic.UranusMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Uranus) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthUranusAwayN(basic.UTC2TT(jd), n) + return basic.EarthUranusAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Uranus) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(6, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(6, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Uranus) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusHeightN(localJD, lon, lat, timezone, n) + return basic.UranusHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Uranus) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Uranus) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusAzimuthN(localJD, lon, lat, timezone, n) + return basic.UranusAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Uranus) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusHourAngleN(localJD, lon, timezone, n) + return basic.UranusHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Uranus) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.UranusCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.UranusCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Uranus) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.UranusRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.UranusRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Uranus) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Uranus) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.UranusSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.UranusSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/uranus/uranus.go b/uranus/uranus.go index 91213ac..908f770 100644 --- a/uranus/uranus.go +++ b/uranus/uranus.go @@ -1,11 +1,11 @@ package uranus import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回天王星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Uranus at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Uranus) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentLo(basic.UTC2TT(jd)) + return basic.UranusApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回天王星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Uranus at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Uranus) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentBo(basic.UTC2TT(jd)) + return basic.UranusApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回天王星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Uranus at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Uranus) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentRa(basic.UTC2TT(jd)) + return basic.UranusApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回天王星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Uranus at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Uranus) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentDec(basic.UTC2TT(jd)) + return basic.UranusApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回天王星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Uranus at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Uranus) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.UranusApparentRaDec(basic.UTC2TT(jd)) + return basic.UranusApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回天王星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Uranus at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Uranus) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.UranusMag(basic.UTC2TT(jd)) + return basic.UranusMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回天王星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Uranus to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Uranus) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthUranusAway(basic.UTC2TT(jd)) + return basic.EarthUranusAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回天王星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Uranus to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Uranus) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(6, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(6, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Uranus) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusHeight(localJD, lon, lat, timezone) + return basic.UranusHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Uranus) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Uranus) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusAzimuth(localJD, lon, lat, timezone) + return basic.UranusAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Uranus) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.UranusHourAngle(localJD, lon, timezone) + return basic.UranusHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Uranus) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.UranusCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.UranusCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Uranus) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.UranusRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.UranusRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Uranus) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Uranus) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,114 +146,84 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.UranusSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.UranusSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Uranus) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Uranus) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastOpposition 上一次冲日 / previous opposition. -// -// 返回 date 当前或之前最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or before date, keeping date's time zone. -func LastOpposition(date time.Time) time.Time { +func (module *Uranus) LastOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusOppositionWithInstance(jde, module.inst), date.Location(), false) } // NextOpposition 下一次冲日 / next opposition. -// -// 返回 date 当前或之后最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or after date, keeping date's time zone. -func NextOpposition(date time.Time) time.Time { +func (module *Uranus) NextOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusOppositionWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Uranus) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Uranus) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Uranus) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Uranus) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // LastEasternQuadrature 上一次东方照 / previous eastern quadrature. -// -// 返回 date 当前或之前最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or before date, keeping date's time zone. -func LastEasternQuadrature(date time.Time) time.Time { +func (module *Uranus) LastEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextEasternQuadrature 下一次东方照 / next eastern quadrature. -// -// 返回 date 当前或之后最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or after date, keeping date's time zone. -func NextEasternQuadrature(date time.Time) time.Time { +func (module *Uranus) NextEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // LastWesternQuadrature 上一次西方照 / previous western quadrature. -// -// 返回 date 当前或之前最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or before date, keeping date's time zone. -func LastWesternQuadrature(date time.Time) time.Time { +func (module *Uranus) LastWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastUranusWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastUranusWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextWesternQuadrature 下一次西方照 / next western quadrature. -// -// 返回 date 当前或之后最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or after date, keeping date's time zone. -func NextWesternQuadrature(date time.Time) time.Time { +func (module *Uranus) NextWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextUranusWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextUranusWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Uranus) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.UranusMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/venus/api_default.go b/venus/api_default.go new file mode 100644 index 0000000..6f0c1dd --- /dev/null +++ b/venus/api_default.go @@ -0,0 +1,302 @@ +package venus + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 金星升交点黄经 / ascending node longitude of Venus. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 金星升交点黄经(n 变体) / n-parameterised ascending node longitude of Venus. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// ClosestTransit 最近一次地心金星凌日 / closest geocentric Venus transit. +func ClosestTransit(date time.Time) TransitInfo { return defaultModule.ClosestTransit(date) } + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 金星降交点黄经 / descending node longitude of Venus. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 金星降交点黄经(n 变体) / n-parameterised descending node longitude of Venus. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 金星视直径,单位角秒 / apparent Venus diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 金星视直径(n 变体),单位角秒 / n-parameterised apparent Venus diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastGreatestElongation 上一次大距,取 date 所在当地民用日 / previous greatest elongation, on the local civil day of date. +func LastGreatestElongation(date time.Time) time.Time { + return defaultModule.LastGreatestElongation(date) +} + +// LastGreatestElongationEast 上一次东大距,取 date 所在当地民用日 / previous greatest eastern elongation, on the local civil day of date. +func LastGreatestElongationEast(date time.Time) time.Time { + return defaultModule.LastGreatestElongationEast(date) +} + +// LastGreatestElongationWest 上一次西大距,取 date 所在当地民用日 / previous greatest western elongation, on the local civil day of date. +func LastGreatestElongationWest(date time.Time) time.Time { + return defaultModule.LastGreatestElongationWest(date) +} + +// LastInferiorConjunction 上一次下合,取 date 所在当地民用日 / previous inferior conjunction, on the local civil day of date. +func LastInferiorConjunction(date time.Time) time.Time { + return defaultModule.LastInferiorConjunction(date) +} + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrograde 上一次留,取 date 所在当地民用日 / previous stationary point, on the local civil day of date. +func LastRetrograde(date time.Time) time.Time { return defaultModule.LastRetrograde(date) } + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastSuperiorConjunction 上一次上合,取 date 所在当地民用日 / previous superior conjunction, on the local civil day of date. +func LastSuperiorConjunction(date time.Time) time.Time { + return defaultModule.LastSuperiorConjunction(date) +} + +// LastTransit 上一次地心金星凌日 / previous geocentric Venus transit. +func LastTransit(date time.Time) TransitInfo { return defaultModule.LastTransit(date) } + +// LocalTransit 地心凌日在观测地的站心接触与凌甚 / topocentric contacts of a transit seen from the site. +func LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo { + return defaultModule.LocalTransit(transit, lon, lat, height) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextGreatestElongation 下一次大距,取 date 所在当地民用日 / next greatest elongation, on the local civil day of date. +func NextGreatestElongation(date time.Time) time.Time { + return defaultModule.NextGreatestElongation(date) +} + +// NextGreatestElongationEast 下一次东大距,取 date 所在当地民用日 / next greatest eastern elongation, on the local civil day of date. +func NextGreatestElongationEast(date time.Time) time.Time { + return defaultModule.NextGreatestElongationEast(date) +} + +// NextGreatestElongationWest 下一次西大距,取 date 所在当地民用日 / next greatest western elongation, on the local civil day of date. +func NextGreatestElongationWest(date time.Time) time.Time { + return defaultModule.NextGreatestElongationWest(date) +} + +// NextInferiorConjunction 下一次下合,取 date 所在当地民用日 / next inferior conjunction, on the local civil day of date. +func NextInferiorConjunction(date time.Time) time.Time { + return defaultModule.NextInferiorConjunction(date) +} + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrograde 下一次留,取 date 所在当地民用日 / next stationary point, on the local civil day of date. +func NextRetrograde(date time.Time) time.Time { return defaultModule.NextRetrograde(date) } + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextSuperiorConjunction 下一次上合,取 date 所在当地民用日 / next superior conjunction, on the local civil day of date. +func NextSuperiorConjunction(date time.Time) time.Time { + return defaultModule.NextSuperiorConjunction(date) +} + +// NextTransit 下一次地心金星凌日 / next geocentric Venus transit. +func NextTransit(date time.Time) TransitInfo { return defaultModule.NextTransit(date) } + +// ParallacticAngle 金星视差角(天顶方向角) / Venus parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 金星视差角(天顶方向角)(n 变体) / n-parameterised Venus parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 金星物理观测参数 / physical observing parameters of Venus. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 金星物理观测参数(n 变体) / n-parameterised physical observing parameters of Venus. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 金星视半径,单位角秒 / apparent Venus semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 金星视半径(n 变体),单位角秒 / n-parameterised apparent Venus semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/venus/context.go b/venus/context.go new file mode 100644 index 0000000..a547a58 --- /dev/null +++ b/venus/context.go @@ -0,0 +1,13 @@ +package venus + +import ( + "b612.me/astro/internal/ephem" +) + +// Venus 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Venus struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Venus { return &Venus{inst: inst} } + +var defaultModule Venus diff --git a/venus/context_test.go b/venus/context_test.go new file mode 100644 index 0000000..7e03c18 --- /dev/null +++ b/venus/context_test.go @@ -0,0 +1,102 @@ +package venus + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + ClosestTransit(date time.Time) TransitInfo + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastGreatestElongation(date time.Time) time.Time + LastGreatestElongationEast(date time.Time) time.Time + LastGreatestElongationWest(date time.Time) time.Time + LastInferiorConjunction(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastSuperiorConjunction(date time.Time) time.Time + LastTransit(date time.Time) TransitInfo + LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo + NextConjunction(date time.Time) time.Time + NextGreatestElongation(date time.Time) time.Time + NextGreatestElongationEast(date time.Time) time.Time + NextGreatestElongationWest(date time.Time) time.Time + NextInferiorConjunction(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextSuperiorConjunction(date time.Time) time.Time + NextTransit(date time.Time) TransitInfo + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Venus)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/venus/diameter.go b/venus/diameter.go index 9cc6a58..b6b2b60 100644 --- a/venus/diameter.go +++ b/venus/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 金星视半径,单位角秒 / apparent Venus semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Venus) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 金星视半径(截断版),单位角秒 / truncated apparent Venus semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 金星视半径(n 变体),单位角秒 / n-parameterised apparent Venus semidiameter in arcseconds. +func (module *Venus) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusSemidiameterN(basic.UTC2TT(jd), n) + return basic.VenusSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 金星视直径,单位角秒 / apparent Venus diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Venus) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 金星视直径(截断版),单位角秒 / truncated apparent Venus diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 金星视直径(n 变体),单位角秒 / n-parameterised apparent Venus diameter in arcseconds. +func (module *Venus) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusDiameterN(basic.UTC2TT(jd), n) + return basic.VenusDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/venus/doc.go b/venus/doc.go index 4c64419..b6876dc 100644 --- a/venus/doc.go +++ b/venus/doc.go @@ -1,3 +1,7 @@ // Package venus 金星位置、升落、合日、留、大距、地心凌日、相位、视星等、视直径与物理星历,角度单位为度。 // Package venus covers Venus position, rise/set, conjunction, station, elongation, geocentric transit, phase, magnitude, apparent diameter and physical ephemeris; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package venus diff --git a/venus/nodes.go b/venus/nodes.go index 8c95434..4febede 100644 --- a/venus/nodes.go +++ b/venus/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 金星升交点黄经 / ascending node longitude of Venus. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Venus) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 金星升交点黄经(截断版) / truncated ascending node longitude of Venus. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 金星升交点黄经(n 变体) / n-parameterised ascending node longitude of Venus. +func (module *Venus) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusAscendingNodeN(basic.UTC2TT(jd), n) + return basic.VenusAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 金星降交点黄经 / descending node longitude of Venus. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Venus) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 金星降交点黄经(截断版) / truncated descending node longitude of Venus. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 金星降交点黄经(n 变体) / n-parameterised descending node longitude of Venus. +func (module *Venus) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusDescendingNodeN(basic.UTC2TT(jd), n) + return basic.VenusDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/venus/parallactic.go b/venus/parallactic.go index cae5eec..80037f6 100644 --- a/venus/parallactic.go +++ b/venus/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 金星视差角(天顶方向角) / Venus parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Venus) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项金星视差角(天顶方向角) / truncated Venus parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 金星视差角(天顶方向角)(n 变体) / n-parameterised Venus parallactic angle. +func (module *Venus) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/venus/phase.go b/venus/phase.go index 59baecd..a29ed47 100644 --- a/venus/phase.go +++ b/venus/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Venus) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.VenusPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Venus) PhaseAngleN(date time.Time, n int) float64 { + return basic.VenusPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Venus) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.VenusIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Venus) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.VenusIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Venus) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Venus) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Venus) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.VenusBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Venus) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.VenusBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/venus/physical.go b/venus/physical.go index d143df3..55ac418 100644 --- a/venus/physical.go +++ b/venus/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 金星物理观测参数 / physical observing parameters of Venus. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Venus) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 金星物理观测参数(截断版) / truncated physical observing parameters of Venus. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 金星物理观测参数(n 变体) / n-parameterised physical observing parameters of Venus. +func (module *Venus) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.VenusPhysicalN(basic.UTC2TT(jd), n) + info := basic.VenusPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, diff --git a/venus/rise_set_atmosphere.go b/venus/rise_set_atmosphere.go new file mode 100644 index 0000000..c4918b5 --- /dev/null +++ b/venus/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package venus + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 金星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Venus) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.VenusRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 金星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Venus) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.VenusSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 金星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 金星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/venus/transit.go b/venus/transit.go index 262cb72..c0bcd6f 100644 --- a/venus/transit.go +++ b/venus/transit.go @@ -32,18 +32,18 @@ type TransitInfo struct { } // NextTransit 下一次地心金星凌日 / next geocentric Venus transit. -func NextTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.NextVenusTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Venus) NextTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.NextVenusTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } // LastTransit 上一次地心金星凌日 / previous geocentric Venus transit. -func LastTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.LastVenusTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Venus) LastTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.LastVenusTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } // ClosestTransit 最近一次地心金星凌日 / closest geocentric Venus transit. -func ClosestTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.ClosestVenusTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Venus) ClosestTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.ClosestVenusTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } func transitInfoFromBasic(result basic.PlanetTransitResult, loc *time.Location) TransitInfo { @@ -71,3 +71,26 @@ func transitInfoFromBasic(result basic.PlanetTransitResult, loc *time.Location) } return info } + +// LocalTransitInfo 站心金星凌日信息 / topocentric Venus transit information. +// +// 时刻保持调用者传入的 TransitInfo 的时区;太阳高度为几何中心高度,不含折射。 +// Times keep the timezone of the supplied TransitInfo; Sun altitudes are geometric centre altitudes without refraction. +type LocalTransitInfo = basic.PlanetTransitLocalInfo + +// LocalTransitContact 站心凌日接触 / topocentric transit contact. +type LocalTransitContact = basic.PlanetTransitLocalContactInfo + +// LocalTransit 地心凌日 transit 在观测地的站心接触与凌甚 / topocentric contacts of transit seen from the site. +// +// lon、lat 为度,东经、北纬为正;height 为海拔米。 +// lon and lat are in degrees, east and north positive; height is in meters. +func (module *Venus) LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo { + if !transit.Valid { + return LocalTransitInfo{} + } + result := basic.PlanetTransitResultFromTimes(2, + transit.Start, transit.InternalStart, transit.Greatest, transit.InternalEnd, transit.End) + local := basic.PlanetTransitLocalWithInstance(result, basic.Observer{Longitude: lon, Latitude: lat, Height: height}, module.inst) + return basic.PlanetTransitLocalInfoFromResult(local, transit.Greatest.Location()) +} diff --git a/venus/transit_test.go b/venus/transit_test.go index 7d3646c..0121964 100644 --- a/venus/transit_test.go +++ b/venus/transit_test.go @@ -21,3 +21,19 @@ func TestTransitWrappers(t *testing.T) { t.Fatalf("unexpected durations: %+v", info) } } + +func TestLocalTransitWrapper(t *testing.T) { + info := NextTransit(time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC)) + local := LocalTransit(info, 151.2093, -33.8688, 0) + if !local.Valid || !local.HasInternal || !local.Visible { + t.Fatalf("unexpected local transit: %+v", local) + } + // 悉尼 2012 金星凌日:一触约 22:16 UTC,太阳全程在地平上。 + want := time.Date(2012, 6, 5, 22, 16, 0, 0, time.UTC) + if diff := local.Start.Time.Sub(want); diff < -time.Minute || diff > time.Minute { + t.Fatalf("C1 %s", local.Start.Time) + } + if !local.Sunrise.IsZero() || !local.Sunset.IsZero() || local.End.SunAltitude <= 0 { + t.Fatalf("unexpected sun events: %+v", local) + } +} diff --git a/venus/truncated.go b/venus/truncated.go index 6911035..9811301 100644 --- a/venus/truncated.go +++ b/venus/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Venus) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentLoN(basic.UTC2TT(jd), n) + return basic.VenusApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Venus) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentBoN(basic.UTC2TT(jd), n) + return basic.VenusApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Venus) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentRaN(basic.UTC2TT(jd), n) + return basic.VenusApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Venus) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentDecN(basic.UTC2TT(jd), n) + return basic.VenusApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Venus) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentRaDecN(basic.UTC2TT(jd), n) + return basic.VenusApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Venus) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusMagN(basic.UTC2TT(jd), n) + return basic.VenusMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Venus) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthVenusAwayN(basic.UTC2TT(jd), n) + return basic.EarthVenusAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Venus) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(2, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(2, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Venus) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusHeightN(localJD, lon, lat, timezone, n) + return basic.VenusHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Venus) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Venus) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusAzimuthN(localJD, lon, lat, timezone, n) + return basic.VenusAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Venus) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusHourAngleN(localJD, lon, timezone, n) + return basic.VenusHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Venus) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.VenusCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.VenusCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Venus) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.VenusRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.VenusRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Venus) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Venus) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.VenusSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.VenusSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/venus/venus.go b/venus/venus.go index 0797a06..542efe3 100644 --- a/venus/venus.go +++ b/venus/venus.go @@ -1,11 +1,11 @@ package venus import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回金星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Venus at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Venus) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentLo(basic.UTC2TT(jd)) + return basic.VenusApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回金星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Venus at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Venus) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentBo(basic.UTC2TT(jd)) + return basic.VenusApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回金星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Venus at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Venus) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentRa(basic.UTC2TT(jd)) + return basic.VenusApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回金星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Venus at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Venus) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentDec(basic.UTC2TT(jd)) + return basic.VenusApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回金星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Venus at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Venus) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.VenusApparentRaDec(basic.UTC2TT(jd)) + return basic.VenusApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回金星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Venus at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Venus) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.VenusMag(basic.UTC2TT(jd)) + return basic.VenusMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回金星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Venus to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Venus) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthVenusAway(basic.UTC2TT(jd)) + return basic.EarthVenusAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回金星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Venus to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Venus) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(2, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(2, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Venus) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusHeight(localJD, lon, lat, timezone) + return basic.VenusHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Venus) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Venus) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusAzimuth(localJD, lon, lat, timezone) + return basic.VenusAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Venus) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.VenusHourAngle(localJD, lon, timezone) + return basic.VenusHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Venus) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.VenusCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.VenusCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Venus) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.VenusRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.VenusRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Venus) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Venus) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,168 +146,120 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.VenusSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.VenusSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Venus) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Venus) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastInferiorConjunction 上一次下合 / previous inferior conjunction. -// -// 返回 date 当前或之前最近一次下合时刻,结果保持 date 的时区。 -// Returns the nearest inferior conjunction at or before date, keeping date's time zone. -func LastInferiorConjunction(date time.Time) time.Time { +func (module *Venus) LastInferiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusInferiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusInferiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextInferiorConjunction 下一次下合 / next inferior conjunction. -// -// 返回 date 当前或之后最近一次下合时刻,结果保持 date 的时区。 -// Returns the nearest inferior conjunction at or after date, keeping date's time zone. -func NextInferiorConjunction(date time.Time) time.Time { +func (module *Venus) NextInferiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusInferiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusInferiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastSuperiorConjunction 上一次上合 / previous superior conjunction. -// -// 返回 date 当前或之前最近一次上合时刻,结果保持 date 的时区。 -// Returns the nearest superior conjunction at or before date, keeping date's time zone. -func LastSuperiorConjunction(date time.Time) time.Time { +func (module *Venus) LastSuperiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusSuperiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusSuperiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextSuperiorConjunction 下一次上合 / next superior conjunction. -// -// 返回 date 当前或之后最近一次上合时刻,结果保持 date 的时区。 -// Returns the nearest superior conjunction at or after date, keeping date's time zone. -func NextSuperiorConjunction(date time.Time) time.Time { +func (module *Venus) NextSuperiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusSuperiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusSuperiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastRetrograde 上一次留 / previous stationary point. -// -// 返回 date 当前或之前最近一次留时刻,不区分顺转逆还是逆转顺,结果保持 date 的时区。 -// Returns the nearest stationary point at or before date, regardless of the direction change, keeping date's time zone. -func LastRetrograde(date time.Time) time.Time { +func (module *Venus) LastRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextRetrograde 下一次留 / next stationary point. -// -// 返回 date 当前或之后最近一次留时刻,不区分顺转逆还是逆转顺,结果保持 date 的时区。 -// Returns the nearest stationary point at or after date, regardless of the direction change, keeping date's time zone. -func NextRetrograde(date time.Time) time.Time { +func (module *Venus) NextRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Venus) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusProgradeToRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusProgradeToRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Venus) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusProgradeToRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusProgradeToRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Venus) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusRetrogradeToProgradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusRetrogradeToProgradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Venus) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusRetrogradeToProgradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusRetrogradeToProgradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongation 上一次大距 / previous greatest elongation. -// -// 返回 date 当前或之前最近一次大距时刻,不区分东西大距,结果保持 date 的时区。 -// Returns the nearest greatest elongation at or before date, regardless of east or west, keeping date's time zone. -func LastGreatestElongation(date time.Time) time.Time { +func (module *Venus) LastGreatestElongation(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusGreatestElongationInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusGreatestElongationInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongation 下一次大距 / next greatest elongation. -// -// 返回 date 当前或之后最近一次大距时刻,不区分东西大距,结果保持 date 的时区。 -// Returns the nearest greatest elongation at or after date, regardless of east or west, keeping date's time zone. -func NextGreatestElongation(date time.Time) time.Time { +func (module *Venus) NextGreatestElongation(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusGreatestElongationInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusGreatestElongationInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongationEast 上一次东大距 / previous greatest eastern elongation. -// -// 返回 date 当前或之前最近一次东大距时刻,结果保持 date 的时区。 -// Returns the nearest eastern greatest elongation at or before date, keeping date's time zone. -func LastGreatestElongationEast(date time.Time) time.Time { +func (module *Venus) LastGreatestElongationEast(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusGreatestElongationEastInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusGreatestElongationEastInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongationEast 下一次东大距 / next greatest eastern elongation. -// -// 返回 date 当前或之后最近一次东大距时刻,结果保持 date 的时区。 -// Returns the nearest eastern greatest elongation at or after date, keeping date's time zone. -func NextGreatestElongationEast(date time.Time) time.Time { +func (module *Venus) NextGreatestElongationEast(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusGreatestElongationEastInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusGreatestElongationEastInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongationWest 上一次西大距 / previous greatest western elongation. -// -// 返回 date 当前或之前最近一次西大距时刻,结果保持 date 的时区。 -// Returns the nearest western greatest elongation at or before date, keeping date's time zone. -func LastGreatestElongationWest(date time.Time) time.Time { +func (module *Venus) LastGreatestElongationWest(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastVenusGreatestElongationWestInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastVenusGreatestElongationWestInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongationWest 下一次西大距 / next greatest western elongation. -// -// 返回 date 当前或之后最近一次西大距时刻,结果保持 date 的时区。 -// Returns the nearest western greatest elongation at or after date, keeping date's time zone. -func NextGreatestElongationWest(date time.Time) time.Time { +func (module *Venus) NextGreatestElongationWest(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextVenusGreatestElongationWestInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextVenusGreatestElongationWestInclusiveWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Venus) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.VenusMag2018WithInstance(basic.UTC2TT(jd), module.inst) }

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ᤪ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

5l#_*m)tclaj;JZ9~Oe&a#`Qd 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 z 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 xqY5 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�XjUOluG11fJGA{ds<;2aZ0}PxwU8*Sf^$ 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Ν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�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 170 { - stardata.RadVel, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[166:170])), 64) - } - if len(star) >= 179 { - stardata.RotVel, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[176:179])), 64) - } - if len(star) > 161 { - rc, _ := strconv.ParseFloat(string(bytes.TrimSpace(star[162:166])), 64) - if rc != 0 { - stardata.Pc = 1 / rc - } - } - return stardata, nil -} - -func parseRa(star []byte) (float64, error) { - var sec float64 - var err error - ra := float64(0) - for i := 75; i < 77; i++ { - if star[i] == ' ' { - continue - } - ra = ra*10 + float64(star[i]-48) - } - minute := uint8(0) - for i := 77; i < 79; i++ { - if star[i] == ' ' { - continue - } - minute = minute*10 + (star[i] - 48) - } - ori := string(bytes.TrimSpace(star[79:83])) - if ori != "" { - sec, err = strconv.ParseFloat(ori, 64) - if err != nil { - return ra, err - } - } - ra += float64(minute)/60 + sec/3600 - return ra * 15, nil -} - -func parseDec(star []byte) (float64, error) { - var sec float64 - var err error - underZero := false - if star[83] == '-' { - underZero = true - } - dec := float64(0) - for i := 84; i < 86; i++ { - if star[i] == ' ' { - continue - } - dec = dec*10 + float64(star[i]-48) - } - minute := uint8(0) - for i := 86; i < 88; i++ { - if star[i] == ' ' { - continue - } - minute = minute*10 + (star[i] - 48) - } - ori := string(bytes.TrimSpace(star[88:90])) - if ori != "" { - sec, err = strconv.ParseFloat(ori, 64) - if err != nil { - return dec, err - } - } - dec += float64(minute)/60 + sec/3600 - if underZero { - dec = -dec - } - return dec, nil -} - -var stardat [][]byte -var starhdindex map[string]int var hr2detail map[uint16][]string var hr2hip map[uint16]uint32 var chnidx map[string]uint16 @@ -173,16 +52,9 @@ func LoadStarData() error { } func initStarData() error { - data := initStarCatalogData() - stardat = bytes.Split(data, []byte("\n")) - parsedStarData = make([]InnerStarData, len(stardat)) - cachedStarData = make([]StarData, len(stardat)) - for i, row := range stardat { - parsed, err := parseStarData(row) - if err != nil { - return fmt.Errorf("parse star %d failed: %w", i+1, err) - } - parsedStarData[i] = parsed + parsedStarData = initStarCatalogData() + cachedStarData = make([]StarData, len(parsedStarData)) + for i, parsed := range parsedStarData { cachedStarData[i] = fullStarData(parsed) } chnidx = make(map[string]uint16, len(hr2detail)*2) diff --git a/basic/star_catalog_cache_test.go b/basic/star_catalog_cache_test.go index 0768395..ad8aeb1 100644 --- a/basic/star_catalog_cache_test.go +++ b/basic/star_catalog_cache_test.go @@ -5,26 +5,15 @@ import ( "testing" ) -func TestStarDataCacheMatchesRawRows(t *testing.T) { +func TestStarDataCacheMatchesDecodedRows(t *testing.T) { if err := LoadStarData(); err != nil { t.Fatal(err) } - if len(parsedStarData) != len(stardat) { - t.Fatalf("parsed cache length mismatch: got %d want %d", len(parsedStarData), len(stardat)) + if len(cachedStarData) != len(parsedStarData) { + t.Fatalf("full cache length mismatch: got %d want %d", len(cachedStarData), len(parsedStarData)) } - if len(cachedStarData) != len(stardat) { - t.Fatalf("full cache length mismatch: got %d want %d", len(cachedStarData), len(stardat)) - } - for i, row := range stardat { - parsed, err := parseStarData(row) - if err != nil { - t.Fatalf("parse row %d failed: %v", i+1, err) - } - if parsed != parsedStarData[i] { - t.Fatalf("parsed cache mismatch at HR %d", i+1) - } - full := fullStarData(parsed) - if full != cachedStarData[i] { + for i, parsed := range parsedStarData { + if full := fullStarData(parsed); full != cachedStarData[i] { t.Fatalf("full cache mismatch at HR %d", i+1) } } @@ -108,7 +97,7 @@ func TestStarDataByHRInvalid(t *testing.T) { if err := LoadStarData(); err != nil { t.Fatal(err) } - for _, hr := range []int{0, -1, len(stardat) + 1} { + for _, hr := range []int{0, -1, len(cachedStarData) + 1} { if _, err := StarDataByHR(hr); err == nil { t.Fatalf("StarDataByHR(%d) expected error", hr) } @@ -118,7 +107,6 @@ func TestStarDataByHRInvalid(t *testing.T) { func BenchmarkInitStarData(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - stardat = nil hr2detail = nil hr2hip = nil chnidx = nil diff --git a/basic/star_catalog_data.go b/basic/star_catalog_data.go index 9a8fc28..a2ea735 100644 --- a/basic/star_catalog_data.go +++ b/basic/star_catalog_data.go @@ -5,38 +5,38 @@ import ( "compress/gzip" _ "embed" "encoding/binary" + "errors" "fmt" - "io" + "io/ioutil" + "math" ) -// star_catalog.dat gzip 解压后的布局 / star_catalog.dat layout after gzip decompression: -// magic[8] | version[1] | rawDataLen[4] | rawData | stringCount[2](字符串计数) | -// repeated(stringLen[uvarint] + stringBytes)(字符串表) | -// maxHR[2] | repeated(maxHR * 6 * stringIndex[2]) | repeated(maxHR * hip[4])(星表记录)。 -const starCatalogMagic = "STRCAT01" +// starCatalogMagic star_catalog.dat 为 gzip 压缩、按列存储的 uvarint 整数格式。 +const starCatalogMagic = "STRCAT02" + +const starCatalogVersion = 2 + +const starCatalogDetailFields = 6 //go:embed star_catalog.dat var starCatalogCompressed []byte -// supplementalStarDetails 保存嵌入载荷没有对应命名条目的星表元数据 / supplementalStarDetails contains catalog metadata that is maintained in -// 源码形式 / source form when the embedded payload has no corresponding named entry. -// 六个字段按编码顺序排列:中文名、别名、拜耳命名、通用名、中文星座和 IAU 星座 / The six fields follow the encoded detail order: Chinese name, alias, Bayer -// 命名、通用名、中文星座和 IAU 星座 / designation, common name, Chinese constellation, and IAU constellation. +// supplementalStarDetails 载荷中无命名条目的星表元数据,字段依次为中文名、别名、拜耳命名、通用名、中文星座、IAU 星座。 var supplementalStarDetails = map[uint16][]string{ 4799: {"进贤增九", "", "室女座25", "", "室女座", "Virgo"}, } -func initStarCatalogData() []byte { +func initStarCatalogData() []InnerStarData { reader, err := gzip.NewReader(bytes.NewReader(starCatalogCompressed)) if err != nil { panic(err) } defer reader.Close() - payload, err := io.ReadAll(reader) + payload, err := ioutil.ReadAll(reader) if err != nil { panic(err) } - data, detail, hip, err := decodeStarCatalogPayload(payload) + stars, detail, hip, err := decodeStarCatalogPayload(payload) if err != nil { panic(err) } @@ -45,87 +45,180 @@ func initStarCatalogData() []byte { } hr2detail = detail hr2hip = hip - return data + return stars } -func decodeStarCatalogPayload(payload []byte) ([]byte, map[uint16][]string, map[uint16]uint32, error) { - reader := bytes.NewReader(payload) +// starCatalogReader 顺序读取 uvarint 载荷。 +type starCatalogReader struct { + data []byte + pos int + err error +} - var magic [len(starCatalogMagic)]byte - if _, err := io.ReadFull(reader, magic[:]); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog magic: %w", err) +func (r *starCatalogReader) uvarint() uint64 { + if r.err != nil { + return 0 } - if string(magic[:]) != starCatalogMagic { - return nil, nil, nil, fmt.Errorf("invalid star catalog magic %q", string(magic[:])) + value, n := binary.Uvarint(r.data[r.pos:]) + if n <= 0 { + r.err = errors.New("truncated star catalog") + return 0 } + r.pos += n + return value +} - version, err := reader.ReadByte() - if err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog version: %w", err) +func (r *starCatalogReader) bytes(n uint64) []byte { + if r.err != nil { + return nil } - if version != 1 { + if n > uint64(len(r.data)-r.pos) { + r.err = errors.New("truncated star catalog") + return nil + } + value := r.data[r.pos : r.pos+int(n)] + r.pos += int(n) + return value +} + +// 解码公式须与编码端一致,运算顺序不可调换,否则结果不再逐位相同。 + +// starCatalogScaled 解码符号-幅值定点数,保留 -0。 +func starCatalogScaled(v uint64, scale float64) float64 { + value := float64(v>>1) / scale + if v&1 != 0 { + return math.Copysign(value, -1) + } + return value +} + +func starCatalogRa(v uint64) float64 { + ra := float64(v / 36000) + minute := (v / 600) % 60 + sec := float64(v%600) / 10 + ra += float64(minute)/60 + sec/3600 + return ra * 15 +} + +func starCatalogDec(v uint64) float64 { + total := v >> 1 + dec := float64(total / 3600) + dec += float64((total/60)%60)/60 + float64(total%60)/3600 + if v&1 != 0 { + dec = -dec + } + return dec +} + +func starCatalogMag(v uint64) float64 { + if v == 0 { + return 9999.9999 + } + return starCatalogScaled(v-1, 100) +} + +func starCatalogPc(v uint64) float64 { + if rc := starCatalogScaled(v, 1000); rc != 0 { + return 1 / rc + } + return 0 +} + +func starCatalogZigzag(v uint64) int64 { + return int64(v>>1) ^ -int64(v&1) +} + +func decodeStarCatalogPayload(payload []byte) ([]InnerStarData, map[uint16][]string, map[uint16]uint32, error) { + if len(payload) < len(starCatalogMagic)+1 || string(payload[:len(starCatalogMagic)]) != starCatalogMagic { + return nil, nil, nil, errors.New("invalid star catalog magic") + } + if version := payload[len(starCatalogMagic)]; version != starCatalogVersion { return nil, nil, nil, fmt.Errorf("unsupported star catalog version %d", version) } - - var rawLen uint32 - if err := binary.Read(reader, binary.LittleEndian, &rawLen); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog data length: %w", err) + r := &starCatalogReader{data: payload, pos: len(starCatalogMagic) + 1} + count := r.uvarint() + if r.err == nil && count > math.MaxUint16 { + return nil, nil, nil, fmt.Errorf("star catalog count %d out of range", count) } - data := make([]byte, rawLen) - if _, err := io.ReadFull(reader, data); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog data: %w", err) + stars := make([]InnerStarData, count) + for i := range stars { + stars[i].HR = uint16(i + 1) + stars[i].Name = string(r.bytes(r.uvarint())) + } + hd := int64(0) + for i := range stars { + hd += starCatalogZigzag(r.uvarint()) + stars[i].HD = uint32(hd) + } + for i := range stars { + stars[i].Ra = starCatalogRa(r.uvarint()) + } + for i := range stars { + stars[i].Dec = starCatalogDec(r.uvarint()) + } + for i := range stars { + stars[i].Mag = starCatalogMag(r.uvarint()) + } + for i := range stars { + stars[i].PmRA = starCatalogScaled(r.uvarint(), 1000) + } + for i := range stars { + stars[i].PmDec = starCatalogScaled(r.uvarint(), 1000) + } + for i := range stars { + stars[i].RadVel = starCatalogScaled(r.uvarint(), 1) + } + for i := range stars { + stars[i].RotVel = starCatalogScaled(r.uvarint(), 1) + } + for i := range stars { + stars[i].Pc = starCatalogPc(r.uvarint()) } - var stringCount uint16 - if err := binary.Read(reader, binary.LittleEndian, &stringCount); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog string count: %w", err) + stringCount := r.uvarint() + if r.err == nil && stringCount > uint64(len(payload)) { + return nil, nil, nil, fmt.Errorf("star catalog string count %d out of range", stringCount) } - stringsTable := make([]string, int(stringCount)+1) + stringsTable := make([]string, stringCount+1) for i := 1; i < len(stringsTable); i++ { - length, err := binary.ReadUvarint(reader) - if err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog string length: %w", err) - } - value := make([]byte, length) - if _, err := io.ReadFull(reader, value); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog string: %w", err) - } - stringsTable[i] = string(value) + stringsTable[i] = string(r.bytes(r.uvarint())) } - - var maxHR uint16 - if err := binary.Read(reader, binary.LittleEndian, &maxHR); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog max HR: %w", err) + indexes := make([][starCatalogDetailFields]uint64, count) + for k := 0; k < starCatalogDetailFields; k++ { + for i := range indexes { + indexes[i][k] = r.uvarint() + } } detail := make(map[uint16][]string) - for hr := uint16(1); hr <= maxHR; hr++ { - record := make([]string, 6) + for i, record := range indexes { hasValue := false - for i := range record { - var index uint16 - if err := binary.Read(reader, binary.LittleEndian, &index); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog detail index: %w", err) + values := make([]string, starCatalogDetailFields) + for k, index := range record { + if index >= uint64(len(stringsTable)) { + return nil, nil, nil, fmt.Errorf("detail string index out of range: hr=%d index=%d", i+1, index) } - if int(index) >= len(stringsTable) { - return nil, nil, nil, fmt.Errorf("detail string index out of range: hr=%d index=%d", hr, index) - } - record[i] = stringsTable[index] + values[k] = stringsTable[index] hasValue = hasValue || index != 0 } if hasValue { - detail[hr] = record + detail[uint16(i+1)] = values } } - hip := make(map[uint16]uint32) - for hr := uint16(1); hr <= maxHR; hr++ { - var value uint32 - if err := binary.Read(reader, binary.LittleEndian, &value); err != nil { - return nil, nil, nil, fmt.Errorf("read star catalog HIP: %w", err) - } - if value != 0 { - hip[hr] = value + prevHIP := int64(0) + for i := range stars { + value := r.uvarint() + if value == 0 { + continue } + prevHIP += starCatalogZigzag(value - 1) + hip[uint16(i+1)] = uint32(prevHIP) } - return data, detail, hip, nil + if r.err != nil { + return nil, nil, nil, r.err + } + if r.pos != len(payload) { + return nil, nil, nil, fmt.Errorf("%d trailing bytes in star catalog", len(payload)-r.pos) + } + return stars, detail, hip, nil } diff --git a/basic/star_catalog_format_test.go b/basic/star_catalog_format_test.go new file mode 100644 index 0000000..ac60dee --- /dev/null +++ b/basic/star_catalog_format_test.go @@ -0,0 +1,49 @@ +package basic + +import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "math" + "testing" +) + +// starCatalogDigestWant 解码后星表的摘要,锁定逐位一致。 +const starCatalogDigestWant = "6a538c97a35dfc9fd2ecad2268ec9a15c3efa1bf4f9ee1cff752448c9c5a7a53" + +func starCatalogDigest(stars []StarData) string { + hash := sha256.New() + var buf [8]byte + writeUint := func(v uint64) { + binary.LittleEndian.PutUint64(buf[:], v) + hash.Write(buf[:]) + } + writeString := func(s string) { + writeUint(uint64(len(s))) + hash.Write([]byte(s)) + } + writeUint(uint64(len(stars))) + for _, star := range stars { + writeUint(uint64(star.HR)) + writeUint(uint64(star.HD)) + writeUint(uint64(star.HIP)) + writeString(star.Name) + for _, v := range []float64{star.Ra, star.Dec, star.Mag, star.PmRA, star.PmDec, star.RadVel, star.RotVel, star.Pc} { + writeUint(math.Float64bits(v)) + } + for _, s := range []string{star.ChineseName, star.ChineseAlias, star.ChineseBayerName, star.CommonName, + star.CommonAliasName, star.Cst, star.CstChinese} { + writeString(s) + } + } + return hex.EncodeToString(hash.Sum(nil)) +} + +func TestStarCatalogDigestMatchesLegacyParser(t *testing.T) { + if err := LoadStarData(); err != nil { + t.Fatal(err) + } + if got := starCatalogDigest(cachedStarData); got != starCatalogDigestWant { + t.Fatalf("star catalog digest = %s, want %s", got, starCatalogDigestWant) + } +} diff --git a/basic/star_catalog_test.go b/basic/star_catalog_test.go index d26be33..caedf39 100644 --- a/basic/star_catalog_test.go +++ b/basic/star_catalog_test.go @@ -10,9 +10,12 @@ func Test_ParseStar(t *testing.T) { if err := LoadStarData(); err != nil { t.Fatal(err) } - for _, v := range stardat { - if _, err := parseStarData(v); err != nil { - t.Fatal(err) + if len(parsedStarData) != 9110 { + t.Fatalf("star count = %d, want 9110", len(parsedStarData)) + } + for i, star := range parsedStarData { + if int(star.HR) != i+1 { + t.Fatalf("star %d carries HR %d", i+1, star.HR) } } } diff --git a/basic/sun.go b/basic/sun.go index e6a67c2..34bc776 100644 --- a/basic/sun.go +++ b/basic/sun.go @@ -1,6 +1,7 @@ package basic import ( + "b612.me/astro/internal/ephem" "b612.me/astro/planet" . "b612.me/astro/tools" ) @@ -126,6 +127,9 @@ func HSunApparentLo(jde float64) float64 { } func HSunApparentLoN(jde float64, n int) float64 { + if lo, _, _, _, ok := vectorApparent(ephem.Sun, jde, n); ok { + return lo + } lo := HSunTrueLoN(jde, n) lo = lo + Nutation2000Bi(jde) + SunLoGXCN(jde, n) return lo @@ -157,13 +161,11 @@ func HSunApparentRaDecN(jde float64, n int) (float64, float64) { return ra, dec } -func hSunApparentRaDecDistanceN(jde float64, n int) (ra, dec, distanceAU float64) { - trueLongitude := HSunTrueLoN(jde, n) - trueLatitude := HSunTrueBoN(jde, n) - distanceAU = EarthAwayN(jde, n) - apparentLongitude := trueLongitude + Nutation2000Bi(jde) - 20.49552/distanceAU/3600 - ra, dec = LoBoToRaDec(jde, apparentLongitude, trueLatitude) - return ra, dec, distanceAU +func hSunApparentRaDecDistanceN(jde float64, n int, instances ...*Instance) (ra, dec, distanceAU float64) { + inst := instanceValue(instances) + + ra, dec = sunApparentRaDecN(jde, n, inst) + return ra, dec, EarthAwayNWithInstance(jde, n, inst) } func HSunApparentRa(jde float64) float64 { // '太阳视赤经 @@ -171,7 +173,7 @@ func HSunApparentRa(jde float64) float64 { // '太阳视赤经 } func HSunApparentRaN(jde float64, n int) float64 { // '太阳视赤经 - return LoToRa(jde, HSunApparentLoN(jde, n), HSunTrueBoN(jde, n)) + return sunApparentRaN(jde, n, nil) } func HSunTrueRa(jde float64) float64 { @@ -193,7 +195,8 @@ func HSunApparentDec(jde float64) float64 { // '太阳视赤纬 } func HSunApparentDecN(jde float64, n int) float64 { // '太阳视赤纬 - return ArcSin(Sin(EclipticObliquity(jde, true)) * Sin(HSunApparentLoN(jde, n))) + _, dec := sunApparentRaDecN(jde, n, nil) + return dec } func HSunTrueDec(jde float64) float64 { // '太阳真赤纬 diff --git a/basic/sun_ephemeris_instance.go b/basic/sun_ephemeris_instance.go new file mode 100644 index 0000000..5e272e6 --- /dev/null +++ b/basic/sun_ephemeris_instance.go @@ -0,0 +1,143 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/ephem" + "b612.me/astro/planet" + . "b612.me/astro/tools" +) + +// HSunTrueLoWithInstance 指定星历实例的太阳真黄经 / true solar longitude with an explicit ephemeris instance. +func HSunTrueLoWithInstance(jde float64, inst *Instance) float64 { + return HSunTrueLoNWithInstance(jde, -1, inst) +} + +// HSunTrueLoNWithInstance 指定星历实例的太阳真黄经(n 变体) / n-parameterised true solar longitude with an explicit ephemeris instance. +func HSunTrueLoNWithInstance(jde float64, n int, inst *Instance) float64 { + if inst == nil { + return HSunTrueLoN(jde, n) + } + if inst.Model() != ephem.Custom { + return planet.WherePlanetModelN(inst.Model(), 0, 0, jde, n) + } + if lo, _, _, ok := instanceStateEcliptic(ephem.Sun, jde, inst); ok { + return lo + } + return math.NaN() +} + +// HSunTrueBoWithInstance 指定星历实例的太阳真黄纬 / true solar latitude with an explicit ephemeris instance. +func HSunTrueBoWithInstance(jde float64, inst *Instance) float64 { + return HSunTrueBoNWithInstance(jde, -1, inst) +} + +// HSunTrueBoNWithInstance 指定星历实例的太阳真黄纬(n 变体) / n-parameterised true solar latitude with an explicit ephemeris instance. +func HSunTrueBoNWithInstance(jde float64, n int, inst *Instance) float64 { + if inst == nil { + return HSunTrueBoN(jde, n) + } + if inst.Model() != ephem.Custom { + return planet.WherePlanetModelN(inst.Model(), 0, 1, jde, n) + } + if _, bo, _, ok := instanceStateEcliptic(ephem.Sun, jde, inst); ok { + return bo + } + return math.NaN() +} + +// EarthAwayNWithInstance 指定星历实例的日地距离 / Sun-Earth distance with an explicit ephemeris instance. +func EarthAwayNWithInstance(jde float64, n int, inst *Instance) float64 { + if inst == nil { + return EarthAwayN(jde, n) + } + if inst.Model() != ephem.Custom { + return planet.WherePlanetModelN(inst.Model(), 0, 2, jde, n) + } + if _, _, distanceAU, ok := instanceStateEcliptic(ephem.Sun, jde, inst); ok { + return distanceAU + } + return math.NaN() +} + +// HSunApparentLoWithInstance 指定星历实例的太阳视黄经 / apparent solar longitude with an explicit ephemeris instance. +func HSunApparentLoWithInstance(jde float64, inst *Instance) float64 { + return HSunApparentLoNWithInstance(jde, -1, inst) +} + +// HSunApparentLoNWithInstance 指定星历实例的太阳视黄经(n 变体) / n-parameterised apparent solar longitude with an explicit ephemeris instance. +func HSunApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + if lo, _, _, _, ok, err := vectorApparentWithInstance(ephem.Sun, jde, n, inst); err != nil { + return math.NaN() + } else if ok { + return lo + } + lo := HSunTrueLoNWithInstance(jde, n, inst) + return lo + Nutation2000Bi(jde) - 20.49552/EarthAwayNWithInstance(jde, n, inst)/3600 +} + +// HSunApparentRaDecWithInstance 指定星历实例的太阳视赤经纬 / apparent solar right ascension and declination with an explicit ephemeris instance. +func HSunApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + return HSunApparentRaDecNWithInstance(jde, -1, inst) +} + +// HSunApparentRaDecNWithInstance 指定星历实例的太阳视赤经纬(n 变体) / n-parameterised apparent solar right ascension and declination with an explicit ephemeris instance. +func HSunApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + // 内置模型与包级 HSunApparentRaDecN 同口径。 + if inst == nil { + return HSunApparentRaDecN(jde, n) + } + if inst.Model() != ephem.Custom { + ra, dec, _ := hSunApparentRaDecDistanceN(jde, n, inst) + return ra, dec + } + if lo, bo, _, _, ok, err := vectorApparentWithInstance(ephem.Sun, jde, n, inst); err != nil { + return math.NaN(), math.NaN() + } else if ok { + return LoBoToRaDec(jde, lo, bo) + } + return sunApparentRaDecN(jde, n, inst) +} + +// HSunApparentRaWithInstance 指定星历实例的太阳视赤经 / apparent solar right ascension with an explicit ephemeris instance. +func HSunApparentRaWithInstance(jde float64, inst *Instance) float64 { + return HSunApparentRaNWithInstance(jde, -1, inst) +} + +// HSunApparentRaNWithInstance 指定星历实例的太阳视赤经(n 变体) / n-parameterised apparent solar right ascension with an explicit ephemeris instance. +func HSunApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return sunApparentRaN(jde, n, inst) +} + +// HSunApparentDecWithInstance 指定星历实例的太阳视赤纬 / apparent solar declination with an explicit ephemeris instance. +func HSunApparentDecWithInstance(jde float64, inst *Instance) float64 { + return HSunApparentDecNWithInstance(jde, -1, inst) +} + +// HSunApparentDecNWithInstance 指定星历实例的太阳视赤纬(n 变体) / n-parameterised apparent solar declination with an explicit ephemeris instance. +func HSunApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + _, dec := sunApparentRaDecN(jde, n, inst) + return dec +} + +// HSunTrueRaWithInstance 指定星历实例的太阳真赤经 / true solar right ascension with an explicit ephemeris instance. +func HSunTrueRaWithInstance(jde float64, inst *Instance) float64 { + return HSunTrueRaNWithInstance(jde, -1, inst) +} + +// HSunTrueRaNWithInstance 指定星历实例的太阳真赤经(n 变体) / n-parameterised true solar right ascension with an explicit ephemeris instance. +func HSunTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + lo := HSunTrueLoNWithInstance(jde, n, inst) + eps := TrueObliquity(jde) + return ArcTan2(Cos(eps)*Sin(lo), Cos(lo)) +} + +// HSunTrueDecWithInstance 指定星历实例的太阳真赤纬 / true solar declination with an explicit ephemeris instance. +func HSunTrueDecWithInstance(jde float64, inst *Instance) float64 { + return HSunTrueDecNWithInstance(jde, -1, inst) +} + +// HSunTrueDecNWithInstance 指定星历实例的太阳真赤纬(n 变体) / n-parameterised true solar declination with an explicit ephemeris instance. +func HSunTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + return ArcSin(Sin(EclipticObliquity(jde, false)) * Sin(HSunTrueLoNWithInstance(jde, n, inst))) +} diff --git a/basic/sun_moon_alias_ephemeris_instance.go b/basic/sun_moon_alias_ephemeris_instance.go new file mode 100644 index 0000000..315ce92 --- /dev/null +++ b/basic/sun_moon_alias_ephemeris_instance.go @@ -0,0 +1,277 @@ +package basic + +import ( + "b612.me/astro/internal/ephem" + . "b612.me/astro/tools" +) + +// sunLowPrecisionAlias 报告低精度太阳别名是否沿用与模型无关的解析公式,只有自定义引擎改走实例。 +func sunLowPrecisionAlias(inst *Instance) bool { + return inst == nil || inst.Model() != ephem.Custom +} + +// SunTrueLoWithInstance 太阳真黄经 / true solar longitude with an explicit ephemeris instance. +func SunTrueLoWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunTrueLo(jde) + } + return HSunTrueLoWithInstance(jde, inst) +} + +// SunTrueLoNWithInstance 太阳真黄经(n 变体) / n-parameterised true solar longitude with an explicit ephemeris instance. +func SunTrueLoNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunTrueLoNWithInstance(jde, n, inst) +} + +// SunTrueBoWithInstance 太阳真黄纬 / true solar latitude with an explicit ephemeris instance. +func SunTrueBoWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HSunTrueBo(jde) + } + return HSunTrueBoWithInstance(jde, inst) +} + +// SunTrueBoNWithInstance 太阳真黄纬(n 变体) / n-parameterised true solar latitude with an explicit ephemeris instance. +func SunTrueBoNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunTrueBoNWithInstance(jde, n, inst) +} + +// SunApparentLoWithInstance 太阳视黄经 / apparent solar longitude with an explicit ephemeris instance. +func SunApparentLoWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunApparentLo(jde) + } + return HSunApparentLoWithInstance(jde, inst) +} + +// SunApparentLoNWithInstance 太阳视黄经(n 变体) / n-parameterised apparent solar longitude with an explicit ephemeris instance. +func SunApparentLoNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunApparentLoNWithInstance(jde, n, inst) +} + +// SunApparentRaWithInstance 太阳视赤经 / apparent solar right ascension with an explicit ephemeris instance. +func SunApparentRaWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunApparentRa(jde) + } + return HSunApparentRaWithInstance(jde, inst) +} + +// SunApparentRaNWithInstance 太阳视赤经(n 变体) / n-parameterised apparent solar right ascension with an explicit ephemeris instance. +func SunApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunApparentRaNWithInstance(jde, n, inst) +} + +// SunApparentDecWithInstance 太阳视赤纬 / apparent solar declination with an explicit ephemeris instance. +func SunApparentDecWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunApparentDec(jde) + } + return HSunApparentDecWithInstance(jde, inst) +} + +// SunApparentDecNWithInstance 太阳视赤纬(n 变体) / n-parameterised apparent solar declination with an explicit ephemeris instance. +func SunApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunApparentDecNWithInstance(jde, n, inst) +} + +// SunApparentRaDecWithInstance 太阳视赤经纬 / apparent solar right ascension and declination with an explicit ephemeris instance. +func SunApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + if sunLowPrecisionAlias(inst) { + return SunApparentRaDec(jde) + } + return HSunApparentRaDecWithInstance(jde, inst) +} + +// SunApparentRaDecNWithInstance 太阳视赤经纬(n 变体) / n-parameterised apparent solar right ascension and declination with an explicit ephemeris instance. +func SunApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return HSunApparentRaDecNWithInstance(jde, n, inst) +} + +// SunTrueRaWithInstance 太阳真赤经 / true solar right ascension with an explicit ephemeris instance. +func SunTrueRaWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunTrueRa(jde) + } + return HSunTrueRaWithInstance(jde, inst) +} + +// SunTrueRaNWithInstance 太阳真赤经(n 变体) / n-parameterised true solar right ascension with an explicit ephemeris instance. +func SunTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunTrueRaNWithInstance(jde, n, inst) +} + +// SunTrueDecWithInstance 太阳真赤纬 / true solar declination with an explicit ephemeris instance. +func SunTrueDecWithInstance(jde float64, inst *Instance) float64 { + if sunLowPrecisionAlias(inst) { + return SunTrueDec(jde) + } + return HSunTrueDecWithInstance(jde, inst) +} + +// SunTrueDecNWithInstance 太阳真赤纬(n 变体) / n-parameterised true solar declination with an explicit ephemeris instance. +func SunTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + return HSunTrueDecNWithInstance(jde, n, inst) +} + +// SunTrueRaDecWithInstance 太阳真赤经纬 / true solar right ascension and declination with an explicit ephemeris instance. +func SunTrueRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + if sunLowPrecisionAlias(inst) { + return SunTrueRa(jde), SunTrueDec(jde) + } + return HSunTrueRaWithInstance(jde, inst), HSunTrueDecWithInstance(jde, inst) +} + +// SunTrueRaDecNWithInstance 太阳真赤经纬(n 变体) / n-parameterised true solar right ascension and declination with an explicit ephemeris instance. +func SunTrueRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return HSunTrueRaNWithInstance(jde, n, inst), HSunTrueDecNWithInstance(jde, n, inst) +} + +// EarthAwayWithInstance 日地距离 / Earth-Sun distance with an explicit ephemeris instance. +func EarthAwayWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return EarthAway(jde) + } + return EarthAwayNWithInstance(jde, -1, inst) +} + +// MoonGeocentricApparentRaWithInstance 月球地心视赤经 / apparent geocentric lunar right ascension with an explicit ephemeris instance. +func MoonGeocentricApparentRaWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonGeocentricApparentRa(jde) + } + return MoonGeocentricApparentRaNWithInstance(jde, -1, inst) +} + +// MoonGeocentricApparentRaNWithInstance 月球地心视赤经(n 变体) / n-parameterised apparent geocentric lunar right ascension with an explicit ephemeris instance. +func MoonGeocentricApparentRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonApparentRaN(jde, n, inst) +} + +// MoonGeocentricApparentDecWithInstance 月球地心视赤纬 / apparent geocentric lunar declination with an explicit ephemeris instance. +func MoonGeocentricApparentDecWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonGeocentricApparentDec(jde) + } + return MoonGeocentricApparentDecNWithInstance(jde, -1, inst) +} + +// MoonGeocentricApparentDecNWithInstance 月球地心视赤纬(n 变体) / n-parameterised apparent geocentric lunar declination with an explicit ephemeris instance. +func MoonGeocentricApparentDecNWithInstance(jde float64, n int, inst *Instance) float64 { + bo := HMoonTrueBoNWithInstance(jde, n, inst) + lo := HMoonApparentLoNWithInstance(jde, n, inst) + return ArcSin(Sin(bo)*Cos(TrueObliquity(jde)) + Cos(bo)*Sin(TrueObliquity(jde))*Sin(lo)) +} + +// MoonGeocentricApparentRaDecWithInstance 月球地心视赤经纬 / apparent geocentric lunar right ascension and declination with an explicit ephemeris instance. +func MoonGeocentricApparentRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + if inst == nil { + return HMoonGeocentricApparentRaDec(jde) + } + return MoonGeocentricApparentRaNWithInstance(jde, -1, inst), MoonGeocentricApparentDecNWithInstance(jde, -1, inst) +} + +// MoonGeocentricApparentRaDecNWithInstance 月球地心视赤经纬(n 变体) / n-parameterised apparent geocentric lunar right ascension and declination with an explicit ephemeris instance. +func MoonGeocentricApparentRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return MoonGeocentricApparentRaNWithInstance(jde, n, inst), MoonGeocentricApparentDecNWithInstance(jde, n, inst) +} + +// MoonGeocentricTrueRaWithInstance 月球地心真赤经 / true geocentric lunar right ascension with an explicit ephemeris instance. +func MoonGeocentricTrueRaWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonGeocentricTrueRa(jde) + } + return MoonGeocentricTrueRaNWithInstance(jde, -1, inst) +} + +// MoonGeocentricTrueRaNWithInstance 月球地心真赤经(n 变体) / n-parameterised true geocentric lunar right ascension with an explicit ephemeris instance. +func MoonGeocentricTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return LoToRa(jde, HMoonTrueLoNWithInstance(jde, n, inst), HMoonTrueBoNWithInstance(jde, n, inst)) +} + +// MoonGeocentricTrueDecWithInstance 月球地心真赤纬 / true geocentric lunar declination with an explicit ephemeris instance. +func MoonGeocentricTrueDecWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonGeocentricTrueDec(jde) + } + return MoonGeocentricTrueDecNWithInstance(jde, -1, inst) +} + +// MoonGeocentricTrueDecNWithInstance 月球地心真赤纬(n 变体) / n-parameterised true geocentric lunar declination with an explicit ephemeris instance. +func MoonGeocentricTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + lo := HMoonTrueLoNWithInstance(jde, n, inst) + bo := HMoonTrueBoNWithInstance(jde, n, inst) + return ArcSin(Sin(bo)*Cos(TrueObliquity(jde)) + Cos(bo)*Sin(TrueObliquity(jde))*Sin(lo)) +} + +// MoonGeocentricTrueRaDecWithInstance 月球地心真赤经纬 / true geocentric lunar right ascension and declination with an explicit ephemeris instance. +func MoonGeocentricTrueRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + if inst == nil { + return HMoonGeocentricTrueRaDec(jde) + } + return MoonGeocentricTrueRaWithInstance(jde, inst), MoonGeocentricTrueDecWithInstance(jde, inst) +} + +// MoonGeocentricTrueRaDecNWithInstance 月球地心真赤经纬(n 变体) / n-parameterised true geocentric lunar right ascension and declination with an explicit ephemeris instance. +func MoonGeocentricTrueRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return MoonGeocentricTrueRaNWithInstance(jde, n, inst), MoonGeocentricTrueDecNWithInstance(jde, n, inst) +} + +// MoonTrueRaWithInstance 月球真赤经 / lunar true right ascension with an explicit ephemeris instance. +func MoonTrueRaWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonTrueRa(jde) + } + return moonApparentRaN(jde, -1, inst) +} + +// MoonTrueRaNWithInstance 月球真赤经(n 变体) / n-parameterised lunar true right ascension with an explicit ephemeris instance. +func MoonTrueRaNWithInstance(jde float64, n int, inst *Instance) float64 { + return moonApparentRaN(jde, n, inst) +} + +// MoonTrueDecWithInstance 月球真赤纬 / lunar true declination with an explicit ephemeris instance. +func MoonTrueDecWithInstance(jde float64, inst *Instance) float64 { + if inst == nil { + return HMoonTrueDec(jde) + } + return MoonTrueDecNWithInstance(jde, -1, inst) +} + +// MoonTrueDecNWithInstance 月球真赤纬(n 变体) / n-parameterised lunar true declination with an explicit ephemeris instance. +func MoonTrueDecNWithInstance(jde float64, n int, inst *Instance) float64 { + lo := HMoonApparentLoNWithInstance(jde, n, inst) + bo := HMoonTrueBoNWithInstance(jde, n, inst) + return ArcSin(Sin(bo)*Cos(TrueObliquity(jde)) + Cos(bo)*Sin(TrueObliquity(jde))*Sin(lo)) +} + +// MoonTrueRaDecWithInstance 月球真赤经纬 / lunar true right ascension and declination with an explicit ephemeris instance. +func MoonTrueRaDecWithInstance(jde float64, inst *Instance) (float64, float64) { + if inst == nil { + return HMoonTrueRaDec(jde) + } + return MoonTrueRaWithInstance(jde, inst), MoonTrueDecWithInstance(jde, inst) +} + +// MoonTrueRaDecNWithInstance 月球真赤经纬(n 变体) / n-parameterised lunar true right ascension and declination with an explicit ephemeris instance. +func MoonTrueRaDecNWithInstance(jde float64, n int, inst *Instance) (float64, float64) { + return MoonTrueRaNWithInstance(jde, n, inst), MoonTrueDecNWithInstance(jde, n, inst) +} + +// MoonApparentRaWithInstance 月球站心视赤经 / topocentric apparent lunar right ascension with an explicit ephemeris instance. +func MoonApparentRaWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + if inst == nil { + return HMoonApparentRa(jd, lon, lat, tz) + } + ra, _ := HMoonApparentRaDecWithInstance(jd, lon, lat, tz, inst) + return ra +} + +// MoonApparentDecWithInstance 月球站心视赤纬 / topocentric apparent lunar declination with an explicit ephemeris instance. +func MoonApparentDecWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + if inst == nil { + return HMoonApparentDec(jd, lon, lat, tz) + } + _, dec := HMoonApparentRaDecWithInstance(jd, lon, lat, tz, inst) + return dec +} diff --git a/basic/sun_observation.go b/basic/sun_observation.go index 11f1765..28af280 100644 --- a/basic/sun_observation.go +++ b/basic/sun_observation.go @@ -18,7 +18,7 @@ func CulminationTime(localJD, lon, tz float64) float64 { //实际中天时间 return localJD + tmp/24.0 - SunTime(localJD)/24.0 } -// CulminationTimeN 截断项太阳中天时刻 / truncated solar culmination time. +// CulminationTimeN 太阳中天时刻(n 变体) / n-parameterised solar culmination time. func CulminationTimeN(localJD, lon, tz float64, n int) float64 { //实际中天时间 localJD = math.Floor(localJD) tmp := longitudeOffsetDegrees(tz*15-lon) * 4 / 60 @@ -35,18 +35,18 @@ func longitudeOffsetDegrees(offset float64) float64 { return offset } -// EveningTwilight 昏朦影;localJD 是本地民用日锚点(当地 0 时),只取整数日。 -func EveningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, error) { +// EveningTwilightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func EveningTwilightWithInstance(localJD, lon, lat, tz, targetAltitude float64, inst *Instance) (float64, error) { localJD = math.Floor(localJD) + 1.5 localTimeZone := math.Round(lon / 15) - culminationTime := CulminationTime(localJD, lon, localTimeZone) - if SunHeight(culminationTime, lon, lat, localTimeZone) < targetAltitude { + culminationTime := CulminationTimeWithInstance(localJD, lon, localTimeZone, inst) + if SunHeightWithInstance(culminationTime, lon, lat, localTimeZone, inst) < targetAltitude { return 0, ErrNeverRise } - if SunHeight(culminationTime+0.5, lon, lat, localTimeZone) > targetAltitude { + if SunHeightWithInstance(culminationTime+0.5, lon, lat, localTimeZone, inst) > targetAltitude { return 0, ErrNeverSet } - tmp := (Sin(targetAltitude) - Sin(HSunApparentDec(culminationTime))*Sin(lat)) / (Cos(HSunApparentDec(culminationTime)) * Cos(lat)) + tmp := (Sin(targetAltitude) - Sin(HSunApparentDecWithInstance(culminationTime, inst))*Sin(lat)) / (Cos(HSunApparentDecWithInstance(culminationTime, inst)) * Cos(lat)) var sundown float64 if math.Abs(tmp) <= 1 && lat < 85 { hourOffset := ArcCos(tmp) / 15 @@ -54,7 +54,7 @@ func EveningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er } else { sundown = culminationTime i := 0 - for LowSunHeight(sundown, lon, lat, localTimeZone) > targetAltitude { + for LowSunHeightWithInstance(sundown, lon, lat, localTimeZone, inst) > targetAltitude { i++ sundown += 15.0 / 60.0 / 24.0 if i > 48 { @@ -65,8 +65,8 @@ func EveningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er estimateJD := sundown - 5.00/24.00/60.00 var ok bool estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 { - stDegree := SunHeight(prevJD, lon, lat, localTimeZone) - targetAltitude - stDegreep := (SunHeight(prevJD+0.000005, lon, lat, localTimeZone) - SunHeight(prevJD-0.000005, lon, lat, localTimeZone)) / 0.00001 + stDegree := SunHeightWithInstance(prevJD, lon, lat, localTimeZone, inst) - targetAltitude + stDegreep := (SunHeightWithInstance(prevJD+0.000005, lon, lat, localTimeZone, inst) - SunHeightWithInstance(prevJD-0.000005, lon, lat, localTimeZone, inst)) / 0.00001 return stDegree / stDegreep }) if !ok { @@ -75,17 +75,18 @@ func EveningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er return estimateJD - localTimeZone/24 + tz/24, nil } -func EveningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (float64, error) { +// EveningTwilightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func EveningTwilightNWithInstance(localJD, lon, lat, tz, targetAltitude float64, n int, inst *Instance) (float64, error) { localJD = math.Floor(localJD) + 1.5 localTimeZone := math.Round(lon / 15) - culminationTime := CulminationTimeN(localJD, lon, localTimeZone, n) - if SunHeightN(culminationTime, lon, lat, localTimeZone, n) < targetAltitude { + culminationTime := CulminationTimeNWithInstance(localJD, lon, localTimeZone, n, inst) + if SunHeightNWithInstance(culminationTime, lon, lat, localTimeZone, n, inst) < targetAltitude { return 0, ErrNeverRise } - if SunHeightN(culminationTime+0.5, lon, lat, localTimeZone, n) > targetAltitude { + if SunHeightNWithInstance(culminationTime+0.5, lon, lat, localTimeZone, n, inst) > targetAltitude { return 0, ErrNeverSet } - tmp := (Sin(targetAltitude) - Sin(HSunApparentDecN(culminationTime, n))*Sin(lat)) / (Cos(HSunApparentDecN(culminationTime, n)) * Cos(lat)) + tmp := (Sin(targetAltitude) - Sin(HSunApparentDecNWithInstance(culminationTime, n, inst))*Sin(lat)) / (Cos(HSunApparentDecNWithInstance(culminationTime, n, inst)) * Cos(lat)) var sundown float64 if math.Abs(tmp) <= 1 && lat < 85 { hourOffset := ArcCos(tmp) / 15 @@ -93,7 +94,7 @@ func EveningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (flo } else { sundown = culminationTime i := 0 - for lowSunHeightForN(sundown, lon, lat, localTimeZone, n) > targetAltitude { + for lowSunHeightForN(sundown, lon, lat, localTimeZone, n, inst) > targetAltitude { i++ sundown += 15.0 / 60.0 / 24.0 if i > 48 { @@ -104,8 +105,8 @@ func EveningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (flo estimateJD := sundown - 5.00/24.00/60.00 var ok bool estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 { - stDegree := SunHeightN(prevJD, lon, lat, localTimeZone, n) - targetAltitude - stDegreep := (SunHeightN(prevJD+0.000005, lon, lat, localTimeZone, n) - SunHeightN(prevJD-0.000005, lon, lat, localTimeZone, n)) / 0.00001 + stDegree := SunHeightNWithInstance(prevJD, lon, lat, localTimeZone, n, inst) - targetAltitude + stDegreep := (SunHeightNWithInstance(prevJD+0.000005, lon, lat, localTimeZone, n, inst) - SunHeightNWithInstance(prevJD-0.000005, lon, lat, localTimeZone, n, inst)) / 0.00001 return stDegree / stDegreep }) if !ok { @@ -114,7 +115,8 @@ func EveningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (flo return estimateJD - localTimeZone/24 + tz/24, nil } -func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, error) { +// MorningTwilightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MorningTwilightWithInstance(localJD, lon, lat, tz, targetAltitude float64, inst *Instance) (float64, error) { // 调整到中午12点 localJD = math.Floor(localJD) + 1.5 @@ -122,18 +124,18 @@ func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er localTimeZone := math.Round(lon / 15) // 计算太阳上中天时间 - culminationTime := CulminationTime(localJD, lon, localTimeZone) + culminationTime := CulminationTimeWithInstance(localJD, lon, localTimeZone, inst) // 检查极夜和极昼条件 - if SunHeight(culminationTime, lon, lat, localTimeZone) < targetAltitude { + if SunHeightWithInstance(culminationTime, lon, lat, localTimeZone, inst) < targetAltitude { return 0, ErrNeverRise } - if SunHeight(culminationTime-0.5, lon, lat, localTimeZone) > targetAltitude { + if SunHeightWithInstance(culminationTime-0.5, lon, lat, localTimeZone, inst) > targetAltitude { return 0, ErrNeverSet } // 计算日出时间 - sunDec := HSunApparentDec(culminationTime) + sunDec := HSunApparentDecWithInstance(culminationTime, inst) tmp := (Sin(targetAltitude) - Sin(sunDec)*Sin(lat)) / (Cos(sunDec) * Cos(lat)) var sunrise float64 @@ -142,7 +144,7 @@ func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er sunrise = culminationTime - hourAngle/24 - 25.0/(24.0*60.0) } else { sunrise = culminationTime - for i := 0; i < 48 && LowSunHeight(sunrise, lon, lat, localTimeZone) > targetAltitude; i++ { + for i := 0; i < 48 && LowSunHeightWithInstance(sunrise, lon, lat, localTimeZone, inst) > targetAltitude; i++ { sunrise -= 15.0 / (60.0 * 24.0) // 每次减少15分钟 } } @@ -150,8 +152,8 @@ func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er estimateJD := sunrise - 5.0/(24.0*60.0) var ok bool estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 { - heightDiff := SunHeight(prevJD, lon, lat, localTimeZone) - targetAltitude - heightDerivative := (SunHeight(prevJD+0.000005, lon, lat, localTimeZone) - SunHeight(prevJD-0.000005, lon, lat, localTimeZone)) / 0.00001 + heightDiff := SunHeightWithInstance(prevJD, lon, lat, localTimeZone, inst) - targetAltitude + heightDerivative := (SunHeightWithInstance(prevJD+0.000005, lon, lat, localTimeZone, inst) - SunHeightWithInstance(prevJD-0.000005, lon, lat, localTimeZone, inst)) / 0.00001 return heightDiff / heightDerivative }) if !ok { @@ -161,18 +163,19 @@ func MorningTwilight(localJD, lon, lat, tz, targetAltitude float64) (float64, er return estimateJD - localTimeZone/24 + tz/24, nil } -func MorningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (float64, error) { +// MorningTwilightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func MorningTwilightNWithInstance(localJD, lon, lat, tz, targetAltitude float64, n int, inst *Instance) (float64, error) { localJD = math.Floor(localJD) + 1.5 localTimeZone := math.Round(lon / 15) - culminationTime := CulminationTimeN(localJD, lon, localTimeZone, n) - if SunHeightN(culminationTime, lon, lat, localTimeZone, n) < targetAltitude { + culminationTime := CulminationTimeNWithInstance(localJD, lon, localTimeZone, n, inst) + if SunHeightNWithInstance(culminationTime, lon, lat, localTimeZone, n, inst) < targetAltitude { return 0, ErrNeverRise } - if SunHeightN(culminationTime-0.5, lon, lat, localTimeZone, n) > targetAltitude { + if SunHeightNWithInstance(culminationTime-0.5, lon, lat, localTimeZone, n, inst) > targetAltitude { return 0, ErrNeverSet } - sunDec := HSunApparentDecN(culminationTime, n) + sunDec := HSunApparentDecNWithInstance(culminationTime, n, inst) tmp := (Sin(targetAltitude) - Sin(sunDec)*Sin(lat)) / (Cos(sunDec) * Cos(lat)) var sunrise float64 @@ -181,7 +184,7 @@ func MorningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (flo sunrise = culminationTime - hourAngle/24 - 25.0/(24.0*60.0) } else { sunrise = culminationTime - for i := 0; i < 48 && lowSunHeightForN(sunrise, lon, lat, localTimeZone, n) > targetAltitude; i++ { + for i := 0; i < 48 && lowSunHeightForN(sunrise, lon, lat, localTimeZone, n, inst) > targetAltitude; i++ { sunrise -= 15.0 / (60.0 * 24.0) } } @@ -189,8 +192,8 @@ func MorningTwilightN(localJD, lon, lat, tz, targetAltitude float64, n int) (flo estimateJD := sunrise - 5.0/(24.0*60.0) var ok bool estimateJD, ok = eventNewtonRefine(estimateJD, 0.00001, func(prevJD float64) float64 { - heightDiff := SunHeightN(prevJD, lon, lat, localTimeZone, n) - targetAltitude - heightDerivative := (SunHeightN(prevJD+0.000005, lon, lat, localTimeZone, n) - SunHeightN(prevJD-0.000005, lon, lat, localTimeZone, n)) / 0.00001 + heightDiff := SunHeightNWithInstance(prevJD, lon, lat, localTimeZone, n, inst) - targetAltitude + heightDerivative := (SunHeightNWithInstance(prevJD+0.000005, lon, lat, localTimeZone, n, inst) - SunHeightNWithInstance(prevJD-0.000005, lon, lat, localTimeZone, n, inst)) / 0.00001 return heightDiff / heightDerivative }) if !ok { @@ -226,61 +229,67 @@ type sunObservationState struct { distanceAU float64 } -func sunObservationStateN(jd, lon, lat, tz float64, n int) sunObservationState { - calculationJD := jd - tz/24.0 - tt := UTC2TT(calculationJD) - siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(calculationJD))*15 + lon) - ra, dec, distanceAU := hSunApparentRaDecDistanceN(tt, n) - hourAngle := Limit360(siderealTime - ra) - altitudeSine := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) +func sunObservationStateN(jd, lon, lat, tz float64, n int, instances ...*Instance) sunObservationState { + hourAngle, dec, distanceAU := sunTopocentricHourAngleDecN(jd, lon, lat, tz, n, instanceValue(instances)) return sunObservationState{ - altitude: ArcSin(altitudeSine), + altitude: horizontalAltitude(hourAngle, dec, lat), distanceAU: distanceAU, } } -func sunRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int) float64 { - state := sunObservationStateN(jd, longitude, latitude, timeZone, n) +func sunRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int, instances ...*Instance) float64 { + return sunRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height, + refractionStandardPressureHPa, refractionStandardTemperatureC, n, instanceValue(instances)) +} + +func sunRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height, pressureHPa, temperatureC float64, n int, inst *Instance) float64 { + state := sunObservationStateN(jd, longitude, latitude, timeZone, n, inst) // 相对观测者下沉地平线的视上缘高度角 / Apparent upper-limb altitude relative to the observer's depressed horizon. residual := state.altitude + HeightDegreeByLat(height, latitude) if zenithShift != 0 { - residual += RefractionFromTrueAltitude(state.altitude, refractionStandardPressureHPa, refractionStandardTemperatureC) + residual += RefractionFromTrueAltitude(state.altitude, pressureHPa, temperatureC) residual += angularSemidiameterFromAU(sunEquatorialRadiusKM, state.distanceAU) / 3600 } return residual } func sunRiseSetOnCivilDay(candidate, slope, civilDayStart, longitude, latitude, requestedTimeZone, - localTimeZone, zenithShift, height float64, isSunrise bool, n int, fallbackErr error) (float64, error) { + localTimeZone, zenithShift, height float64, isSunrise bool, n int, fallbackErr error, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + if eventRiseSetCandidateValid(candidate, civilDayStart, slope, isSunrise) { return candidate, nil } return eventDirectionalRiseSetSearch(civilDayStart, isSunrise, fallbackErr, func(outputJD float64) float64 { localJD := outputJD + localTimeZone/24 - requestedTimeZone/24 - return sunRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, n) + return sunRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, n, inst) }) } -// GetSunRiseTime 精确计算日出时间,传入本地民用日 0 时锚点 -func GetSunRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { - return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, true) +// GetSunRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetSunRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) { + return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, true, inst) } -func GetSunRiseTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) { - return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, true, n) +// GetSunRiseTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetSunRiseTimeNWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int, inst *Instance) (float64, error) { + return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, true, n, inst) } -// GetSunSetTime 精确计算日落时间,传入本地民用日 0 时锚点 -func GetSunSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) { - return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, false) +// GetSunSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetSunSetTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) { + return calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height, false, inst) } -func GetSunSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int) (float64, error) { - return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, false, n) +// GetSunSetTimeNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func GetSunSetTimeNWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, n int, inst *Instance) (float64, error) { + return calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height, false, n, inst) } // calculateSunRiseSetTime 统一的日出日落计算函数 -func calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool) (float64, error) { +func calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) { return 0, ErrInvalidObservationInput } @@ -290,24 +299,26 @@ func calculateSunRiseSetTime(julianDay, longitude, latitude, timeZone, zenithShi sunAngle := StandardAltitudeSun(zenithShift, height, latitude) // 获取太阳上中天时间 - solarNoonTime := CulminationTime(julianDay, longitude, naturalTimeZone) + solarNoonTime := CulminationTimeWithInstance(julianDay, longitude, naturalTimeZone, inst) // 检查极夜极昼条件 - if err := checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise); err != nil { + if err := checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, inst); err != nil { return sunRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, timeZone, - naturalTimeZone, zenithShift, height, isSunrise, -1, err) + naturalTimeZone, zenithShift, height, isSunrise, -1, err, inst) } // 计算初始估算时间 - initialTime := calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height, isSunrise) + initialTime := calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height, isSunrise, inst) // 牛顿-拉夫逊迭代求精确解 - result, slope := sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone) + result, slope := sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone, inst) return sunRiseSetOnCivilDay(result, slope, civilDayStart, longitude, latitude, timeZone, - naturalTimeZone, zenithShift, height, isSunrise, -1, nil) + naturalTimeZone, zenithShift, height, isSunrise, -1, nil, inst) } -func calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool, n int) (float64, error) { +func calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithShift, height float64, isSunrise bool, n int, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) { return 0, ErrInvalidObservationInput } @@ -316,21 +327,23 @@ func calculateSunRiseSetTimeN(julianDay, longitude, latitude, timeZone, zenithSh naturalTimeZone := math.Round(longitude / 15) sunAngle := StandardAltitudeSun(zenithShift, height, latitude) - solarNoonTime := CulminationTimeN(julianDay, longitude, naturalTimeZone, n) - if err := checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, n); err != nil { + solarNoonTime := CulminationTimeNWithInstance(julianDay, longitude, naturalTimeZone, n, inst) + if err := checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, n, inst); err != nil { return sunRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude, timeZone, - naturalTimeZone, zenithShift, height, isSunrise, n, err) + naturalTimeZone, zenithShift, height, isSunrise, n, err, inst) } - initialTime := calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height, isSunrise, n) - result, slope := sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone, n) + initialTime := calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height, isSunrise, n, inst) + result, slope := sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone, n, inst) return sunRiseSetOnCivilDay(result, slope, civilDayStart, longitude, latitude, timeZone, - naturalTimeZone, zenithShift, height, isSunrise, n, nil) + naturalTimeZone, zenithShift, height, isSunrise, n, nil, inst) } // checkPolarConditions 检查极夜极昼条件 -func checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool) error { - if sunRiseSetResidual(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1) < 0 { +func checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, instances ...*Instance) error { + inst := instanceValue(instances) + + if sunRiseSetResidual(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst) < 0 { return ErrNeverRise } @@ -339,15 +352,17 @@ func checkPolarConditions(solarNoonTime, longitude, latitude, naturalTimeZone, z checkTime = solarNoonTime - 0.5 } - if sunRiseSetResidual(checkTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1) > 0 { + if sunRiseSetResidual(checkTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst) > 0 { return ErrNeverSet } return nil } -func checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, n int) error { - if sunRiseSetResidual(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, n) < 0 { +func checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, n int, instances ...*Instance) error { + inst := instanceValue(instances) + + if sunRiseSetResidual(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst) < 0 { return ErrNeverRise } @@ -356,7 +371,7 @@ func checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, checkTime = solarNoonTime - 0.5 } - if sunRiseSetResidual(checkTime, longitude, latitude, naturalTimeZone, zenithShift, height, n) > 0 { + if sunRiseSetResidual(checkTime, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst) > 0 { return ErrNeverSet } @@ -364,9 +379,11 @@ func checkPolarConditionsN(solarNoonTime, longitude, latitude, naturalTimeZone, } // calculateInitialSunTime 计算日出日落的初始估算时间 -func calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height float64, isSunrise bool) float64 { +func calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height float64, isSunrise bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + // 使用球面三角法计算: (sin(ho)-sin(φ)*sin(δ))/(cos(φ)*cos(δ)) - apparentDeclination := HSunApparentDec(solarNoonTime) + apparentDeclination := HSunApparentDecWithInstance(solarNoonTime, inst) cosHourAngle := (Sin(sunAngle) - Sin(apparentDeclination)*Sin(latitude)) / (Cos(apparentDeclination) * Cos(latitude)) if math.Abs(cosHourAngle) <= 1 && latitude < 85 { @@ -384,12 +401,14 @@ func calculateInitialSunTime(solarNoonTime, longitude, latitude, naturalTimeZone } } else { // 使用迭代逼近法(极地条件) - return iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise) + return iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, inst) } } -func calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height float64, isSunrise bool, n int) float64 { - apparentDeclination := HSunApparentDecN(solarNoonTime, n) +func calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZone, sunAngle, zenithShift, height float64, isSunrise bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + apparentDeclination := HSunApparentDecNWithInstance(solarNoonTime, n, inst) cosHourAngle := (Sin(sunAngle) - Sin(apparentDeclination)*Sin(latitude)) / (Cos(apparentDeclination) * Cos(latitude)) if math.Abs(cosHourAngle) <= 1 && latitude < 85 { @@ -405,11 +424,13 @@ func calculateInitialSunTimeN(solarNoonTime, longitude, latitude, naturalTimeZon return solarNoonTime + hourAngle/24 + timeOffset } - return iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, n) + return iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height, isSunrise, n, inst) } // iterativeApproach 迭代逼近法计算(用于极地等特殊条件) -func iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool) float64 { +func iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + estimatedTime := solarNoonTime stepSize := 15.0 / 60.0 / 24.0 // 15分钟步长 if isSunrise { @@ -417,14 +438,16 @@ func iterativeApproach(solarNoonTime, longitude, latitude, naturalTimeZone, zeni } const maxIterations = 48 - for i := 0; i < maxIterations && sunRiseSetResidual(estimatedTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1) > 0; i++ { + for i := 0; i < maxIterations && sunRiseSetResidual(estimatedTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst) > 0; i++ { estimatedTime += stepSize } return estimatedTime } -func iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, n int) float64 { +func iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zenithShift, height float64, isSunrise bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + estimatedTime := solarNoonTime stepSize := 15.0 / 60.0 / 24.0 if isSunrise { @@ -432,7 +455,7 @@ func iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zen } const maxIterations = 48 - for i := 0; i < maxIterations && sunRiseSetResidual(estimatedTime, longitude, latitude, naturalTimeZone, zenithShift, height, n) > 0; i++ { + for i := 0; i < maxIterations && sunRiseSetResidual(estimatedTime, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst) > 0; i++ { estimatedTime += stepSize } @@ -440,7 +463,9 @@ func iterativeApproachN(solarNoonTime, longitude, latitude, naturalTimeZone, zen } // sunRiseSetNewtonRaphsonIteration 牛顿-拉夫逊迭代法求精确解 -func sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone float64) (float64, float64) { +func sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + const ( convergenceThreshold = 0.00001 derivativeStep = 0.000005 @@ -450,9 +475,9 @@ func sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalT slope := math.NaN() var ok bool currentTime, ok = eventNewtonRefine(currentTime, convergenceThreshold, func(previousTime float64) float64 { - functionValue := sunRiseSetResidual(previousTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1) - slope = (sunRiseSetResidual(previousTime+derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, -1) - - sunRiseSetResidual(previousTime-derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, -1)) / (2 * derivativeStep) + functionValue := sunRiseSetResidual(previousTime, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst) + slope = (sunRiseSetResidual(previousTime+derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst) - + sunRiseSetResidual(previousTime-derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, -1, inst)) / (2 * derivativeStep) return functionValue / slope }) if !ok { @@ -463,7 +488,9 @@ func sunRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, naturalT return currentTime - naturalTimeZone/24 + timeZone/24, slope } -func sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone float64, n int) (float64, float64) { +func sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, naturalTimeZone, zenithShift, height, timeZone float64, n int, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + const ( convergenceThreshold = 0.00001 derivativeStep = 0.000005 @@ -473,9 +500,9 @@ func sunRiseSetNewtonRaphsonIterationN(initialTime, longitude, latitude, natural slope := math.NaN() var ok bool currentTime, ok = eventNewtonRefine(currentTime, convergenceThreshold, func(previousTime float64) float64 { - functionValue := sunRiseSetResidual(previousTime, longitude, latitude, naturalTimeZone, zenithShift, height, n) - slope = (sunRiseSetResidual(previousTime+derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, n) - - sunRiseSetResidual(previousTime-derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, n)) / (2 * derivativeStep) + functionValue := sunRiseSetResidual(previousTime, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst) + slope = (sunRiseSetResidual(previousTime+derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst) - + sunRiseSetResidual(previousTime-derivativeStep, longitude, latitude, naturalTimeZone, zenithShift, height, n, inst)) / (2 * derivativeStep) return functionValue / slope }) if !ok { @@ -507,47 +534,19 @@ func LowSunHeight(jd, lon, lat, tz float64) float64 { return ArcSin(tmp2) } -func lowSunHeightForN(jd, lon, lat, tz float64, n int) float64 { +func lowSunHeightForN(jd, lon, lat, tz float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + if n < 0 { - return LowSunHeight(jd, lon, lat, tz) + return LowSunHeightWithInstance(jd, lon, lat, tz, inst) } - return SunHeightN(jd, lon, lat, tz, n) + return SunHeightNWithInstance(jd, lon, lat, tz, n, inst) } func SunAzimuth(jd, lon, lat, tz float64) float64 { - //tmp := (tz*15 - lon) * 4 / 60 - //truejd := jd - tmp/24 - calcjd := jd - tz/24 - st := Limit360(ApparentSiderealTime(UTC2UT1(calcjd))*15 + lon) - hourAngle := Limit360(st - HSunApparentRa(UTC2TT(calcjd))) - tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(HSunApparentDec(UTC2TT(calcjd)))*Cos(lat)) - azimuth := ArcTan(tmp2) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth + return sunAzimuthN(jd, lon, lat, tz, -1, nil) } func SunAzimuthN(jd, lon, lat, tz float64, n int) float64 { - calcjd := jd - tz/24 - st := Limit360(ApparentSiderealTime(UTC2UT1(calcjd))*15 + lon) - hourAngle := Limit360(st - HSunApparentRaN(UTC2TT(calcjd), n)) - tmp2 := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(HSunApparentDecN(UTC2TT(calcjd), n))*Cos(lat)) - azimuth := ArcTan(tmp2) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth + return sunAzimuthN(jd, lon, lat, tz, n, nil) } diff --git a/basic/sun_observation_ephemeris_instance.go b/basic/sun_observation_ephemeris_instance.go new file mode 100644 index 0000000..d31f8ab --- /dev/null +++ b/basic/sun_observation_ephemeris_instance.go @@ -0,0 +1,133 @@ +package basic + +import ( + "math" + + . "b612.me/astro/tools" +) + +func sunTimeWithInstance(jde float64, n int, inst *Instance) float64 { + if inst == nil { + if n < 0 { + return SunTime(jde) + } + return SunTimeN(jde, n) + } + // 均时差必须用平黄经 SunLo,真黄经会丢掉中心差。 + rightAscension := HSunApparentRaNWithInstance(jde, n, inst) + time := (SunLo(jde) - 0.0057183 - rightAscension + Nutation2000Bi(jde)*Cos(TrueObliquity(jde))) / 15 + if time > 23 { + time -= 24 + } + return time +} + +func sunHeightWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + if inst == nil { + if n < 0 { + return SunHeight(jd, lon, lat, tz) + } + return SunHeightN(jd, lon, lat, tz, n) + } + return sunObservationStateN(jd, lon, lat, tz, n, inst).altitude +} + +// SunTimeWithInstance 太阳均时差 / equation of time with an explicit ephemeris instance. +func SunTimeWithInstance(jde float64, inst *Instance) float64 { + return sunTimeWithInstance(jde, -1, inst) +} + +// SunTimeNWithInstance 太阳均时差(n 变体) / n-parameterised equation of time with an explicit ephemeris instance. +func SunTimeNWithInstance(jde float64, n int, inst *Instance) float64 { + return sunTimeWithInstance(jde, n, inst) +} + +// CulminationTimeWithInstance 太阳中天时刻 / solar culmination time with an explicit ephemeris instance. +func CulminationTimeWithInstance(localJD, lon, tz float64, inst *Instance) float64 { + if inst == nil { + return CulminationTime(localJD, lon, tz) + } + localJD = math.Floor(localJD) + tmp := longitudeOffsetDegrees(tz*15-lon) * 4 / 60 + return localJD + tmp/24 - SunTimeWithInstance(localJD, inst)/24 +} + +// CulminationTimeNWithInstance 太阳中天时刻(n 变体) / n-parameterised solar culmination time with an explicit ephemeris instance. +func CulminationTimeNWithInstance(localJD, lon, tz float64, n int, inst *Instance) float64 { + if inst == nil { + return CulminationTimeN(localJD, lon, tz, n) + } + localJD = math.Floor(localJD) + tmp := longitudeOffsetDegrees(tz*15-lon) * 4 / 60 + return localJD + tmp/24 - SunTimeNWithInstance(localJD, n, inst)/24 +} + +// SunTimeAngleWithInstance 太阳时角 / solar hour angle with an explicit ephemeris instance. +func SunTimeAngleWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + if inst == nil { + return SunTimeAngle(jd, lon, lat, tz) + } + calculationJD := jd - tz/24 + siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(calculationJD))*15 + lon) + hourAngle := siderealTime - HSunApparentRaWithInstance(UTC2TT(calculationJD), inst) + if hourAngle < 0 { + hourAngle += 360 + } + return hourAngle +} + +// SunTimeAngleNWithInstance 太阳时角(n 变体) / n-parameterised solar hour angle with an explicit ephemeris instance. +func SunTimeAngleNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + if inst == nil { + return SunTimeAngleN(jd, lon, lat, tz, n) + } + calculationJD := jd - tz/24 + siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(calculationJD))*15 + lon) + hourAngle := siderealTime - HSunApparentRaNWithInstance(UTC2TT(calculationJD), n, inst) + if hourAngle < 0 { + hourAngle += 360 + } + return hourAngle +} + +// SunHeightWithInstance 太阳高度角 / solar altitude with an explicit ephemeris instance. +func SunHeightWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return sunHeightWithInstance(jd, lon, lat, tz, -1, inst) +} + +// SunHeightNWithInstance 太阳高度角(n 变体) / n-parameterised solar altitude with an explicit ephemeris instance. +func SunHeightNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + return sunHeightWithInstance(jd, lon, lat, tz, n, inst) +} + +// LowSunHeightWithInstance 低精度太阳高度角 / low-precision solar altitude with an explicit ephemeris instance. +func LowSunHeightWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + if inst == nil { + return LowSunHeight(jd, lon, lat, tz) + } + calculationJD := jd - tz/24 + siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(calculationJD))*15 + lon) + ra, dec := HSunApparentRaDecWithInstance(UTC2TT(calculationJD), inst) + hourAngle := Limit360(siderealTime - ra) + return ArcSin(Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)) +} + +// SunAzimuthWithInstance 太阳方位角 / solar azimuth with an explicit ephemeris instance. +func SunAzimuthWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 { + return sunAzimuthWithInstance(jd, lon, lat, tz, -1, inst) +} + +// SunAzimuthNWithInstance 太阳方位角(n 变体) / n-parameterised solar azimuth with an explicit ephemeris instance. +func SunAzimuthNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + return sunAzimuthWithInstance(jd, lon, lat, tz, n, inst) +} + +func sunAzimuthWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + if inst == nil { + if n < 0 { + return SunAzimuth(jd, lon, lat, tz) + } + return SunAzimuthN(jd, lon, lat, tz, n) + } + return sunAzimuthN(jd, lon, lat, tz, n, inst) +} diff --git a/basic/sun_physical.go b/basic/sun_physical.go index de7f4c7..85cb2a1 100644 --- a/basic/sun_physical.go +++ b/basic/sun_physical.go @@ -30,9 +30,9 @@ func SunPhysical(jde float64) SunPhysicalInfo { return SunPhysicalN(jde, -1) } -// SunPhysicalN 太阳物理观测参数(截断版) / truncated physical observing parameters of the Sun. -func SunPhysicalN(jde float64, n int) SunPhysicalInfo { - lambda := HSunApparentLoN(jde, n) +// SunPhysicalNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func SunPhysicalNWithInstance(jde float64, n int, inst *Instance) SunPhysicalInfo { + lambda := HSunApparentLoNWithInstance(jde, n, inst) epsilon := TrueObliquity(jde) k := sunPhysicalKBaseDeg + sunPhysicalKRateDeg*(jde-sunPhysicalKEpochJD)/36525.0 theta := (jde - sunCarringtonStartJD) * 360.0 / sunCarringtonRotationDays diff --git a/basic/sun_physical_test.go b/basic/sun_physical_test.go index 09d6a7b..0cc7ff0 100644 --- a/basic/sun_physical_test.go +++ b/basic/sun_physical_test.go @@ -16,7 +16,6 @@ type sunPhysicalSample struct { } func TestSunPhysicalMatchesHorizonsBaseline(t *testing.T) { - // Baseline is generated from JPL Horizons by scripts/generate_sun_physical_baseline.sh. data, err := os.ReadFile("testdata/sun_physical_baseline.json") if err != nil { t.Fatalf("read baseline: %v", err) diff --git a/basic/testdata/semianalytic/jupiter_event_baseline.json b/basic/testdata/semianalytic/jupiter_event_baseline.json new file mode 100644 index 0000000..db17aa4 --- /dev/null +++ b/basic/testdata/semianalytic/jupiter_event_baseline.json @@ -0,0 +1,1732 @@ +{ + "samples": [ + { + "input_utc": "1991-03-11T05:17:23.456Z", + "tt_jd_bits": 4702512152945916237, + "events": { + "LastJupiterConjunction": 4702511639717012620, + "LastJupiterEasternQuadrature": 4702511393973012502, + "LastJupiterOpposition": 4702512064463236620, + "LastJupiterProgradeToRetrograde": 4702511936733572185, + "LastJupiterRetrogradeToPrograde": 4702511338140647854, + "LastJupiterWesternQuadrature": 4702511882517375690, + "NextJupiterConjunction": 4702512495925443449, + "NextJupiterEasternQuadrature": 4702512249695175741, + "NextJupiterOpposition": 4702512914882489890, + "NextJupiterProgradeToRetrograde": 4702512787041232554, + "NextJupiterRetrogradeToPrograde": 4702512194567421550, + "NextJupiterWesternQuadrature": 4702512732981189693 + } + }, + { + "input_utc": "1991-10-24T09:26:40.456Z", + "tt_jd_bits": 4702512640796462585, + "events": { + "LastJupiterConjunction": 4702512495925443449, + "LastJupiterEasternQuadrature": 4702512249695175741, + "LastJupiterOpposition": 4702512064463236620, + "LastJupiterProgradeToRetrograde": 4702511936733572185, + "LastJupiterRetrogradeToPrograde": 4702512194567421550, + "LastJupiterWesternQuadrature": 4702511882517375690, + "NextJupiterConjunction": 4702513348128945305, + "NextJupiterEasternQuadrature": 4702513103067048699, + "NextJupiterOpposition": 4702512914882489890, + "NextJupiterProgradeToRetrograde": 4702512787041232554, + "NextJupiterRetrogradeToPrograde": 4702513048376039518, + "NextJupiterWesternQuadrature": 4702512732981189693 + } + }, + { + "input_utc": "1992-06-07T13:35:57.456Z", + "tt_jd_bits": 4702513128647008932, + "events": { + "LastJupiterConjunction": 4702512495925443449, + "LastJupiterEasternQuadrature": 4702513103067048699, + "LastJupiterOpposition": 4702512914882489890, + "LastJupiterProgradeToRetrograde": 4702512787041232554, + "LastJupiterRetrogradeToPrograde": 4702513048376039516, + "LastJupiterWesternQuadrature": 4702512732981189693, + "NextJupiterConjunction": 4702513348128945305, + "NextJupiterEasternQuadrature": 4702513955120488411, + "NextJupiterOpposition": 4702513764159242873, + "NextJupiterProgradeToRetrograde": 4702513635402149085, + "NextJupiterRetrogradeToPrograde": 4702513899777250709, + "NextJupiterWesternQuadrature": 4702513580732728436 + } + }, + { + "input_utc": "1993-01-20T17:45:14.456Z", + "tt_jd_bits": 4702513616497580134, + "events": { + "LastJupiterConjunction": 4702513348128945305, + "LastJupiterEasternQuadrature": 4702513103067048699, + "LastJupiterOpposition": 4702512914882489890, + "LastJupiterProgradeToRetrograde": 4702512787041232554, + "LastJupiterRetrogradeToPrograde": 4702513048376039518, + "LastJupiterWesternQuadrature": 4702513580732728436, + "NextJupiterConjunction": 4702514197794183218, + "NextJupiterEasternQuadrature": 4702513955120488411, + "NextJupiterOpposition": 4702513764159242873, + "NextJupiterProgradeToRetrograde": 4702513635402149085, + "NextJupiterRetrogradeToPrograde": 4702513899777250709, + "NextJupiterWesternQuadrature": 4702514428184996343 + } + }, + { + "input_utc": "1993-09-04T21:54:31.456Z", + "tt_jd_bits": 4702514104348151337, + "events": { + "LastJupiterConjunction": 4702513348128945305, + "LastJupiterEasternQuadrature": 4702513955120488411, + "LastJupiterOpposition": 4702513764159242873, + "LastJupiterProgradeToRetrograde": 4702513635402149085, + "LastJupiterRetrogradeToPrograde": 4702513899777250709, + "LastJupiterWesternQuadrature": 4702513580732728436, + "NextJupiterConjunction": 4702514197794183218, + "NextJupiterEasternQuadrature": 4702514807158095234, + "NextJupiterOpposition": 4702514614284672284, + "NextJupiterProgradeToRetrograde": 4702514484375761159, + "NextJupiterRetrogradeToPrograde": 4702514750232579882, + "NextJupiterWesternQuadrature": 4702514428184996343 + } + }, + { + "input_utc": "1994-04-20T02:03:48.456Z", + "tt_jd_bits": 4702514592198697684, + "events": { + "LastJupiterConjunction": 4702514197794183218, + "LastJupiterEasternQuadrature": 4702513955120488411, + "LastJupiterOpposition": 4702513764159242873, + "LastJupiterProgradeToRetrograde": 4702514484375761159, + "LastJupiterRetrogradeToPrograde": 4702513899777250709, + "LastJupiterWesternQuadrature": 4702514428184996343, + "NextJupiterConjunction": 4702515046903579625, + "NextJupiterEasternQuadrature": 4702514807158095234, + "NextJupiterOpposition": 4702514614284672284, + "NextJupiterProgradeToRetrograde": 4702515336181910643, + "NextJupiterRetrogradeToPrograde": 4702514750232579882, + "NextJupiterWesternQuadrature": 4702515277713988613 + } + }, + { + "input_utc": "1994-12-03T06:13:05.456Z", + "tt_jd_bits": 4702515080049268888, + "events": { + "LastJupiterConjunction": 4702515046903579625, + "LastJupiterEasternQuadrature": 4702514807158095234, + "LastJupiterOpposition": 4702514614284672284, + "LastJupiterProgradeToRetrograde": 4702514484375761159, + "LastJupiterRetrogradeToPrograde": 4702514750232579882, + "LastJupiterWesternQuadrature": 4702514428184996343, + "NextJupiterConjunction": 4702515897478373452, + "NextJupiterEasternQuadrature": 4702515660462348923, + "NextJupiterOpposition": 4702515467054813064, + "NextJupiterProgradeToRetrograde": 4702515336181910643, + "NextJupiterRetrogradeToPrograde": 4702515601124866503, + "NextJupiterWesternQuadrature": 4702515277713988613 + } + }, + { + "input_utc": "1995-07-18T10:22:22.456Z", + "tt_jd_bits": 4702515567899815235, + "events": { + "LastJupiterConjunction": 4702515046903579625, + "LastJupiterEasternQuadrature": 4702514807158095234, + "LastJupiterOpposition": 4702515467054813064, + "LastJupiterProgradeToRetrograde": 4702515336181910643, + "LastJupiterRetrogradeToPrograde": 4702514750232579882, + "LastJupiterWesternQuadrature": 4702515277713988613, + "NextJupiterConjunction": 4702515897478373452, + "NextJupiterEasternQuadrature": 4702515660462348923, + "NextJupiterOpposition": 4702516323930526658, + "NextJupiterProgradeToRetrograde": 4702516193453094495, + "NextJupiterRetrogradeToPrograde": 4702515601124866503, + "NextJupiterWesternQuadrature": 4702516131575705913 + } + }, + { + "input_utc": "1996-02-29T06:31:39.456Z", + "tt_jd_bits": 4702516052887074907, + "events": { + "LastJupiterConjunction": 4702515897478373452, + "LastJupiterEasternQuadrature": 4702515660462348923, + "LastJupiterOpposition": 4702515467054813064, + "LastJupiterProgradeToRetrograde": 4702515336181910643, + "LastJupiterRetrogradeToPrograde": 4702515601124866503, + "LastJupiterWesternQuadrature": 4702515277713988613, + "NextJupiterConjunction": 4702516751407755873, + "NextJupiterEasternQuadrature": 4702516516159983202, + "NextJupiterOpposition": 4702516323930526658, + "NextJupiterProgradeToRetrograde": 4702516193453094495, + "NextJupiterRetrogradeToPrograde": 4702516455127076537, + "NextJupiterWesternQuadrature": 4702516131575705913 + } + }, + { + "input_utc": "1996-10-13T10:40:56.456Z", + "tt_jd_bits": 4702516540737621254, + "events": { + "LastJupiterConjunction": 4702515897478373452, + "LastJupiterEasternQuadrature": 4702516516159983202, + "LastJupiterOpposition": 4702516323930526658, + "LastJupiterProgradeToRetrograde": 4702516193453094495, + "LastJupiterRetrogradeToPrograde": 4702516455127076536, + "LastJupiterWesternQuadrature": 4702516131575705913, + "NextJupiterConjunction": 4702516751407755873, + "NextJupiterEasternQuadrature": 4702517374743580578, + "NextJupiterOpposition": 4702517185247368069, + "NextJupiterProgradeToRetrograde": 4702517056174162728, + "NextJupiterRetrogradeToPrograde": 4702517313542566335, + "NextJupiterWesternQuadrature": 4702516991222788291 + } + }, + { + "input_utc": "1997-05-28T14:50:13.456Z", + "tt_jd_bits": 4702517028588167602, + "events": { + "LastJupiterConjunction": 4702516751407755873, + "LastJupiterEasternQuadrature": 4702516516159983202, + "LastJupiterOpposition": 4702516323930526658, + "LastJupiterProgradeToRetrograde": 4702516193453094495, + "LastJupiterRetrogradeToPrograde": 4702516455127076537, + "LastJupiterWesternQuadrature": 4702516991222788291, + "NextJupiterConjunction": 4702517610018423635, + "NextJupiterEasternQuadrature": 4702517374743580578, + "NextJupiterOpposition": 4702517185247368069, + "NextJupiterProgradeToRetrograde": 4702517056174162728, + "NextJupiterRetrogradeToPrograde": 4702517313542566335, + "NextJupiterWesternQuadrature": 4702517856327985406 + } + }, + { + "input_utc": "1998-01-10T17:20:30.456Z", + "tt_jd_bits": 4702517516291099303, + "events": { + "LastJupiterConjunction": 4702516751407755873, + "LastJupiterEasternQuadrature": 4702517374743580578, + "LastJupiterOpposition": 4702517185247368069, + "LastJupiterProgradeToRetrograde": 4702517056174162728, + "LastJupiterRetrogradeToPrograde": 4702517313542566335, + "LastJupiterWesternQuadrature": 4702516991222788291, + "NextJupiterConjunction": 4702517610018423635, + "NextJupiterEasternQuadrature": 4702518235789103668, + "NextJupiterOpposition": 4702518049732310450, + "NextJupiterProgradeToRetrograde": 4702517922209431808, + "NextJupiterRetrogradeToPrograde": 4702518176266583881, + "NextJupiterWesternQuadrature": 4702517856327985406 + } + }, + { + "input_utc": "1998-08-25T21:29:47.456Z", + "tt_jd_bits": 4702518004141645651, + "events": { + "LastJupiterConjunction": 4702517610018423635, + "LastJupiterEasternQuadrature": 4702517374743580578, + "LastJupiterOpposition": 4702517185247368069, + "LastJupiterProgradeToRetrograde": 4702517922209431808, + "LastJupiterRetrogradeToPrograde": 4702517313542566335, + "LastJupiterWesternQuadrature": 4702517856327985406, + "NextJupiterConjunction": 4702518473067532604, + "NextJupiterEasternQuadrature": 4702518235789103668, + "NextJupiterOpposition": 4702518049732310450, + "NextJupiterProgradeToRetrograde": 4702518787154295667, + "NextJupiterRetrogradeToPrograde": 4702518176266583881, + "NextJupiterWesternQuadrature": 4702518723959510877 + } + }, + { + "input_utc": "1999-04-10T01:35:23.456Z", + "tt_jd_bits": 4702518491986723868, + "events": { + "LastJupiterConjunction": 4702518473067532604, + "LastJupiterEasternQuadrature": 4702518235789103668, + "LastJupiterOpposition": 4702518049732310450, + "LastJupiterProgradeToRetrograde": 4702517922209431808, + "LastJupiterRetrogradeToPrograde": 4702518176266583881, + "LastJupiterWesternQuadrature": 4702517856327985406, + "NextJupiterConjunction": 4702519338321929243, + "NextJupiterEasternQuadrature": 4702519097687911871, + "NextJupiterOpposition": 4702518914455502652, + "NextJupiterProgradeToRetrograde": 4702518787154295667, + "NextJupiterRetrogradeToPrograde": 4702519039884020798, + "NextJupiterWesternQuadrature": 4702518723959510877 + } + }, + { + "input_utc": "1999-11-23T05:44:40.456Z", + "tt_jd_bits": 4702518979837270216, + "events": { + "LastJupiterConjunction": 4702518473067532604, + "LastJupiterEasternQuadrature": 4702518235789103668, + "LastJupiterOpposition": 4702518914455502652, + "LastJupiterProgradeToRetrograde": 4702518787154295667, + "LastJupiterRetrogradeToPrograde": 4702518176266583881, + "LastJupiterWesternQuadrature": 4702518723959510877, + "NextJupiterConjunction": 4702519338321929243, + "NextJupiterEasternQuadrature": 4702519097687911871, + "NextJupiterOpposition": 4702519776235616013, + "NextJupiterProgradeToRetrograde": 4702519648440844497, + "NextJupiterRetrogradeToPrograde": 4702519039884020798, + "NextJupiterWesternQuadrature": 4702519589634038153 + } + }, + { + "input_utc": "2000-07-07T09:53:57.456Z", + "tt_jd_bits": 4702519467687816563, + "events": { + "LastJupiterConjunction": 4702519338321929243, + "LastJupiterEasternQuadrature": 4702519097687911871, + "LastJupiterOpposition": 4702518914455502652, + "LastJupiterProgradeToRetrograde": 4702518787154295667, + "LastJupiterRetrogradeToPrograde": 4702519039884020798, + "LastJupiterWesternQuadrature": 4702518723959510877, + "NextJupiterConjunction": 4702520202370163856, + "NextJupiterEasternQuadrature": 4702519958453308414, + "NextJupiterOpposition": 4702519776235616013, + "NextJupiterProgradeToRetrograde": 4702519648440844497, + "NextJupiterRetrogradeToPrograde": 4702519901906597261, + "NextJupiterWesternQuadrature": 4702519589634038153 + } + }, + { + "input_utc": "2001-02-18T06:03:14.456Z", + "tt_jd_bits": 4702519952675051380, + "events": { + "LastJupiterConjunction": 4702519338321929243, + "LastJupiterEasternQuadrature": 4702519097687911871, + "LastJupiterOpposition": 4702519776235616013, + "LastJupiterProgradeToRetrograde": 4702519648440844497, + "LastJupiterRetrogradeToPrograde": 4702519901906597261, + "LastJupiterWesternQuadrature": 4702519589634038153, + "NextJupiterConjunction": 4702520202370163856, + "NextJupiterEasternQuadrature": 4702519958453308414, + "NextJupiterOpposition": 4702520633410522885, + "NextJupiterProgradeToRetrograde": 4702520505561003456, + "NextJupiterRetrogradeToPrograde": 4702520760902612018, + "NextJupiterWesternQuadrature": 4702520450002368788 + } + }, + { + "input_utc": "2001-10-03T10:12:31.456Z", + "tt_jd_bits": 4702520440525597728, + "events": { + "LastJupiterConjunction": 4702520202370163856, + "LastJupiterEasternQuadrature": 4702519958453308414, + "LastJupiterOpposition": 4702519776235616013, + "LastJupiterProgradeToRetrograde": 4702519648440844497, + "LastJupiterRetrogradeToPrograde": 4702519901906597261, + "LastJupiterWesternQuadrature": 4702519589634038153, + "NextJupiterConjunction": 4702521062497845376, + "NextJupiterEasternQuadrature": 4702520816619029323, + "NextJupiterOpposition": 4702520633410522885, + "NextJupiterProgradeToRetrograde": 4702520505561003456, + "NextJupiterRetrogradeToPrograde": 4702520760902612018, + "NextJupiterWesternQuadrature": 4702520450002368788 + } + }, + { + "input_utc": "2002-05-18T14:21:48.456Z", + "tt_jd_bits": 4702520928376144075, + "events": { + "LastJupiterConjunction": 4702520202370163856, + "LastJupiterEasternQuadrature": 4702520816619029323, + "LastJupiterOpposition": 4702520633410522885, + "LastJupiterProgradeToRetrograde": 4702520505561003456, + "LastJupiterRetrogradeToPrograde": 4702520760902612018, + "LastJupiterWesternQuadrature": 4702520450002368788, + "NextJupiterConjunction": 4702521062497845376, + "NextJupiterEasternQuadrature": 4702521671866607659, + "NextJupiterOpposition": 4702521486257843655, + "NextJupiterProgradeToRetrograde": 4702521358490633847, + "NextJupiterRetrogradeToPrograde": 4702521616888415816, + "NextJupiterWesternQuadrature": 4702521304428736524 + } + }, + { + "input_utc": "2002-12-31T18:31:05.456Z", + "tt_jd_bits": 4702521416226690422, + "events": { + "LastJupiterConjunction": 4702521062497845376, + "LastJupiterEasternQuadrature": 4702520816619029323, + "LastJupiterOpposition": 4702520633410522885, + "LastJupiterProgradeToRetrograde": 4702521358490633847, + "LastJupiterRetrogradeToPrograde": 4702520760902612018, + "LastJupiterWesternQuadrature": 4702521304428736524, + "NextJupiterConjunction": 4702521917985855430, + "NextJupiterEasternQuadrature": 4702521671866607659, + "NextJupiterOpposition": 4702521486257843655, + "NextJupiterProgradeToRetrograde": 4702522208314915907, + "NextJupiterRetrogradeToPrograde": 4702521616888415816, + "NextJupiterWesternQuadrature": 4702522154284300978 + } + }, + { + "input_utc": "2003-08-15T22:40:22.456Z", + "tt_jd_bits": 4702521904077236770, + "events": { + "LastJupiterConjunction": 4702521062497845376, + "LastJupiterEasternQuadrature": 4702521671866607659, + "LastJupiterOpposition": 4702521486257843655, + "LastJupiterProgradeToRetrograde": 4702521358490633847, + "LastJupiterRetrogradeToPrograde": 4702521616888415816, + "LastJupiterWesternQuadrature": 4702521304428736524, + "NextJupiterConjunction": 4702521917985855430, + "NextJupiterEasternQuadrature": 4702522524867228697, + "NextJupiterOpposition": 4702522336293893979, + "NextJupiterProgradeToRetrograde": 4702522208314915907, + "NextJupiterRetrogradeToPrograde": 4702522470158124246, + "NextJupiterWesternQuadrature": 4702522154284300978 + } + }, + { + "input_utc": "2004-03-30T02:49:39.456Z", + "tt_jd_bits": 4702522391927783117, + "events": { + "LastJupiterConjunction": 4702521917985855430, + "LastJupiterEasternQuadrature": 4702521671866607659, + "LastJupiterOpposition": 4702522336293893979, + "LastJupiterProgradeToRetrograde": 4702522208314915907, + "LastJupiterRetrogradeToPrograde": 4702521616888415816, + "LastJupiterWesternQuadrature": 4702522154284300978, + "NextJupiterConjunction": 4702522769611689768, + "NextJupiterEasternQuadrature": 4702522524867228697, + "NextJupiterOpposition": 4702523185481663723, + "NextJupiterProgradeToRetrograde": 4702523056647900438, + "NextJupiterRetrogradeToPrograde": 4702522470158124246, + "NextJupiterWesternQuadrature": 4702523001754930387 + } + }, + { + "input_utc": "2004-11-12T05:19:56.456Z", + "tt_jd_bits": 4702522879630689964, + "events": { + "LastJupiterConjunction": 4702522769611689768, + "LastJupiterEasternQuadrature": 4702522524867228697, + "LastJupiterOpposition": 4702522336293893979, + "LastJupiterProgradeToRetrograde": 4702522208314915907, + "LastJupiterRetrogradeToPrograde": 4702522470158124246, + "LastJupiterWesternQuadrature": 4702522154284300978, + "NextJupiterConjunction": 4702523619040346990, + "NextJupiterEasternQuadrature": 4702523376757271166, + "NextJupiterOpposition": 4702523185481663723, + "NextJupiterProgradeToRetrograde": 4702523056647900438, + "NextJupiterRetrogradeToPrograde": 4702523321353238901, + "NextJupiterWesternQuadrature": 4702523001754930387 + } + }, + { + "input_utc": "2005-06-27T09:29:13.456Z", + "tt_jd_bits": 4702523367481236311, + "events": { + "LastJupiterConjunction": 4702522769611689768, + "LastJupiterEasternQuadrature": 4702522524867228697, + "LastJupiterOpposition": 4702523185481663723, + "LastJupiterProgradeToRetrograde": 4702523056647900438, + "LastJupiterRetrogradeToPrograde": 4702523321353238898, + "LastJupiterWesternQuadrature": 4702523001754930387, + "NextJupiterConjunction": 4702523619040346990, + "NextJupiterEasternQuadrature": 4702523376757271166, + "NextJupiterOpposition": 4702524035804970436, + "NextJupiterProgradeToRetrograde": 4702523905663624695, + "NextJupiterRetrogradeToPrograde": 4702524171499669562, + "NextJupiterWesternQuadrature": 4702523849302140088 + } + }, + { + "input_utc": "2006-02-08T05:38:30.456Z", + "tt_jd_bits": 4702523852468495983, + "events": { + "LastJupiterConjunction": 4702523619040346990, + "LastJupiterEasternQuadrature": 4702523376757271166, + "LastJupiterOpposition": 4702523185481663723, + "LastJupiterProgradeToRetrograde": 4702523056647900438, + "LastJupiterRetrogradeToPrograde": 4702523321353238901, + "LastJupiterWesternQuadrature": 4702523849302140088, + "NextJupiterConjunction": 4702524468223240294, + "NextJupiterEasternQuadrature": 4702524228832709253, + "NextJupiterOpposition": 4702524035804970436, + "NextJupiterProgradeToRetrograde": 4702523905663624695, + "NextJupiterRetrogradeToPrograde": 4702524171499669562, + "NextJupiterWesternQuadrature": 4702524699351306591 + } + }, + { + "input_utc": "2006-09-23T09:47:47.456Z", + "tt_jd_bits": 4702524340319042330, + "events": { + "LastJupiterConjunction": 4702523619040346990, + "LastJupiterEasternQuadrature": 4702524228832709253, + "LastJupiterOpposition": 4702524035804970436, + "LastJupiterProgradeToRetrograde": 4702523905663624695, + "LastJupiterRetrogradeToPrograde": 4702524171499669562, + "LastJupiterWesternQuadrature": 4702523849302140088, + "NextJupiterConjunction": 4702524468223240294, + "NextJupiterEasternQuadrature": 4702525082476556773, + "NextJupiterOpposition": 4702524889125945740, + "NextJupiterProgradeToRetrograde": 4702524758348277020, + "NextJupiterRetrogradeToPrograde": 4702525022873871692, + "NextJupiterWesternQuadrature": 4702524699351306591 + } + }, + { + "input_utc": "2007-05-08T13:53:23.456Z", + "tt_jd_bits": 4702524828164095693, + "events": { + "LastJupiterConjunction": 4702524468223240294, + "LastJupiterEasternQuadrature": 4702524228832709253, + "LastJupiterOpposition": 4702524035804970436, + "LastJupiterProgradeToRetrograde": 4702524758348277020, + "LastJupiterRetrogradeToPrograde": 4702524171499669562, + "LastJupiterWesternQuadrature": 4702524699351306591, + "NextJupiterConjunction": 4702525319223979778, + "NextJupiterEasternQuadrature": 4702525082476556773, + "NextJupiterOpposition": 4702524889125945740, + "NextJupiterProgradeToRetrograde": 4702525616388038372, + "NextJupiterRetrogradeToPrograde": 4702525022873871692, + "NextJupiterWesternQuadrature": 4702525554037828411 + } + }, + { + "input_utc": "2007-12-21T18:02:40.456Z", + "tt_jd_bits": 4702525316014642041, + "events": { + "LastJupiterConjunction": 4702524468223240294, + "LastJupiterEasternQuadrature": 4702525082476556773, + "LastJupiterOpposition": 4702524889125945740, + "LastJupiterProgradeToRetrograde": 4702524758348277020, + "LastJupiterRetrogradeToPrograde": 4702525022873871692, + "LastJupiterWesternQuadrature": 4702524699351306591, + "NextJupiterConjunction": 4702525319223979778, + "NextJupiterEasternQuadrature": 4702525938677881194, + "NextJupiterOpposition": 4702525746727377977, + "NextJupiterProgradeToRetrograde": 4702525616388038372, + "NextJupiterRetrogradeToPrograde": 4702525877346821269, + "NextJupiterWesternQuadrature": 4702525554037828411 + } + }, + { + "input_utc": "2008-08-04T22:11:57.456Z", + "tt_jd_bits": 4702525803865188388, + "events": { + "LastJupiterConjunction": 4702525319223979778, + "LastJupiterEasternQuadrature": 4702525082476556773, + "LastJupiterOpposition": 4702525746727377977, + "LastJupiterProgradeToRetrograde": 4702525616388038372, + "LastJupiterRetrogradeToPrograde": 4702525022873871692, + "LastJupiterWesternQuadrature": 4702525554037828411, + "NextJupiterConjunction": 4702526173905210046, + "NextJupiterEasternQuadrature": 4702525938677881194, + "NextJupiterOpposition": 4702526608783663316, + "NextJupiterProgradeToRetrograde": 4702526480112933471, + "NextJupiterRetrogradeToPrograde": 4702525877346821269, + "NextJupiterWesternQuadrature": 4702526414693471474 + } + }, + { + "input_utc": "2009-03-20T02:21:14.456Z", + "tt_jd_bits": 4702526291715759590, + "events": { + "LastJupiterConjunction": 4702526173905210046, + "LastJupiterEasternQuadrature": 4702525938677881194, + "LastJupiterOpposition": 4702525746727377977, + "LastJupiterProgradeToRetrograde": 4702525616388038372, + "LastJupiterRetrogradeToPrograde": 4702525877346821269, + "LastJupiterWesternQuadrature": 4702525554037828411, + "NextJupiterConjunction": 4702527033345979976, + "NextJupiterEasternQuadrature": 4702526797833950667, + "NextJupiterOpposition": 4702526608783663316, + "NextJupiterProgradeToRetrograde": 4702526480112933471, + "NextJupiterRetrogradeToPrograde": 4702526736805521956, + "NextJupiterWesternQuadrature": 4702526414693471474 + } + }, + { + "input_utc": "2009-11-02T06:30:31.456Z", + "tt_jd_bits": 4702526779566305938, + "events": { + "LastJupiterConjunction": 4702526173905210046, + "LastJupiterEasternQuadrature": 4702525938677881194, + "LastJupiterOpposition": 4702526608783663316, + "LastJupiterProgradeToRetrograde": 4702526480112933471, + "LastJupiterRetrogradeToPrograde": 4702526736805521956, + "LastJupiterWesternQuadrature": 4702526414693471474, + "NextJupiterConjunction": 4702527033345979976, + "NextJupiterEasternQuadrature": 4702526797833950667, + "NextJupiterOpposition": 4702527473657079946, + "NextJupiterProgradeToRetrograde": 4702527346265792180, + "NextJupiterRetrogradeToPrograde": 4702527599853542486, + "NextJupiterWesternQuadrature": 4702527280539942363 + } + }, + { + "input_utc": "2010-06-17T10:39:48.456Z", + "tt_jd_bits": 4702527267416852285, + "events": { + "LastJupiterConjunction": 4702527033345979976, + "LastJupiterEasternQuadrature": 4702526797833950667, + "LastJupiterOpposition": 4702526608783663316, + "LastJupiterProgradeToRetrograde": 4702526480112933471, + "LastJupiterRetrogradeToPrograde": 4702526736805521956, + "LastJupiterWesternQuadrature": 4702526414693471474, + "NextJupiterConjunction": 4702527896986511441, + "NextJupiterEasternQuadrature": 4702527659252098549, + "NextJupiterOpposition": 4702527473657079946, + "NextJupiterProgradeToRetrograde": 4702527346265792180, + "NextJupiterRetrogradeToPrograde": 4702527599853542486, + "NextJupiterWesternQuadrature": 4702527280539942363 + } + }, + { + "input_utc": "2011-01-29T06:49:05.456Z", + "tt_jd_bits": 4702527752404087102, + "events": { + "LastJupiterConjunction": 4702527033345979976, + "LastJupiterEasternQuadrature": 4702527659252098549, + "LastJupiterOpposition": 4702527473657079946, + "LastJupiterProgradeToRetrograde": 4702527346265792180, + "LastJupiterRetrogradeToPrograde": 4702527599853542486, + "LastJupiterWesternQuadrature": 4702527280539942363, + "NextJupiterConjunction": 4702527896986511441, + "NextJupiterEasternQuadrature": 4702528521180306142, + "NextJupiterOpposition": 4702528338207370916, + "NextJupiterProgradeToRetrograde": 4702528210771857493, + "NextJupiterRetrogradeToPrograde": 4702528463633127667, + "NextJupiterWesternQuadrature": 4702528148243761502 + } + }, + { + "input_utc": "2011-09-13T09:19:22.456Z", + "tt_jd_bits": 4702528240106993949, + "events": { + "LastJupiterConjunction": 4702527896986511441, + "LastJupiterEasternQuadrature": 4702527659252098549, + "LastJupiterOpposition": 4702527473657079946, + "LastJupiterProgradeToRetrograde": 4702528210771857493, + "LastJupiterRetrogradeToPrograde": 4702527599853542486, + "LastJupiterWesternQuadrature": 4702528148243761502, + "NextJupiterConjunction": 4702528762306740501, + "NextJupiterEasternQuadrature": 4702528521180306142, + "NextJupiterOpposition": 4702528338207370916, + "NextJupiterProgradeToRetrograde": 4702529071584506103, + "NextJupiterRetrogradeToPrograde": 4702528463633127667, + "NextJupiterWesternQuadrature": 4702529013282142981 + } + }, + { + "input_utc": "2012-04-27T13:28:39.456Z", + "tt_jd_bits": 4702528727957540296, + "events": { + "LastJupiterConjunction": 4702527896986511441, + "LastJupiterEasternQuadrature": 4702528521180306142, + "LastJupiterOpposition": 4702528338207370916, + "LastJupiterProgradeToRetrograde": 4702528210771857493, + "LastJupiterRetrogradeToPrograde": 4702528463633127667, + "LastJupiterWesternQuadrature": 4702528148243761502, + "NextJupiterConjunction": 4702528762306740501, + "NextJupiterEasternQuadrature": 4702529381592324502, + "NextJupiterOpposition": 4702529199353119769, + "NextJupiterProgradeToRetrograde": 4702529071584506103, + "NextJupiterRetrogradeToPrograde": 4702529325215308994, + "NextJupiterWesternQuadrature": 4702529013282142981 + } + }, + { + "input_utc": "2012-12-10T17:37:56.456Z", + "tt_jd_bits": 4702529215808111498, + "events": { + "LastJupiterConjunction": 4702528762306740501, + "LastJupiterEasternQuadrature": 4702528521180306142, + "LastJupiterOpposition": 4702529199353119769, + "LastJupiterProgradeToRetrograde": 4702529071584506103, + "LastJupiterRetrogradeToPrograde": 4702528463633127667, + "LastJupiterWesternQuadrature": 4702529013282142981, + "NextJupiterConjunction": 4702529625846150011, + "NextJupiterEasternQuadrature": 4702529381592324502, + "NextJupiterOpposition": 4702530055790787450, + "NextJupiterProgradeToRetrograde": 4702529927850115727, + "NextJupiterRetrogradeToPrograde": 4702529325215308994, + "NextJupiterWesternQuadrature": 4702529872713563813 + } + }, + { + "input_utc": "2013-07-25T21:47:13.456Z", + "tt_jd_bits": 4702529703658657846, + "events": { + "LastJupiterConjunction": 4702529625846150011, + "LastJupiterEasternQuadrature": 4702529381592324502, + "LastJupiterOpposition": 4702529199353119769, + "LastJupiterProgradeToRetrograde": 4702529071584506103, + "LastJupiterRetrogradeToPrograde": 4702529325215308994, + "LastJupiterWesternQuadrature": 4702529013282142981, + "NextJupiterConjunction": 4702530485246205430, + "NextJupiterEasternQuadrature": 4702530239263412377, + "NextJupiterOpposition": 4702530055790787450, + "NextJupiterProgradeToRetrograde": 4702529927850115727, + "NextJupiterRetrogradeToPrograde": 4702530183635691426, + "NextJupiterWesternQuadrature": 4702529872713563813 + } + }, + { + "input_utc": "2014-03-10T01:56:30.456Z", + "tt_jd_bits": 4702530191509204193, + "events": { + "LastJupiterConjunction": 4702529625846150011, + "LastJupiterEasternQuadrature": 4702529381592324502, + "LastJupiterOpposition": 4702530055790787450, + "LastJupiterProgradeToRetrograde": 4702529927850115727, + "LastJupiterRetrogradeToPrograde": 4702530183635691426, + "LastJupiterWesternQuadrature": 4702529872713563813, + "NextJupiterConjunction": 4702530485246205430, + "NextJupiterEasternQuadrature": 4702530239263412377, + "NextJupiterOpposition": 4702530908086288928, + "NextJupiterProgradeToRetrograde": 4702530780352614306, + "NextJupiterRetrogradeToPrograde": 4702531039231776308, + "NextJupiterWesternQuadrature": 4702530726333373418 + } + }, + { + "input_utc": "2014-10-23T06:05:47.456Z", + "tt_jd_bits": 4702530679359750541, + "events": { + "LastJupiterConjunction": 4702530485246205430, + "LastJupiterEasternQuadrature": 4702530239263412377, + "LastJupiterOpposition": 4702530055790787450, + "LastJupiterProgradeToRetrograde": 4702529927850115727, + "LastJupiterRetrogradeToPrograde": 4702530183635691426, + "LastJupiterWesternQuadrature": 4702529872713563813, + "NextJupiterConjunction": 4702531340061737657, + "NextJupiterEasternQuadrature": 4702531094085273298, + "NextJupiterOpposition": 4702530908086288928, + "NextJupiterProgradeToRetrograde": 4702530780352614306, + "NextJupiterRetrogradeToPrograde": 4702531039231776308, + "NextJupiterWesternQuadrature": 4702530726333373418 + } + }, + { + "input_utc": "2015-06-07T10:11:23.456Z", + "tt_jd_bits": 4702531167204803903, + "events": { + "LastJupiterConjunction": 4702530485246205430, + "LastJupiterEasternQuadrature": 4702531094085273298, + "LastJupiterOpposition": 4702530908086288928, + "LastJupiterProgradeToRetrograde": 4702530780352614306, + "LastJupiterRetrogradeToPrograde": 4702531039231776308, + "LastJupiterWesternQuadrature": 4702530726333373418, + "NextJupiterConjunction": 4702531340061737657, + "NextJupiterEasternQuadrature": 4702531946807824433, + "NextJupiterOpposition": 4702531757829128992, + "NextJupiterProgradeToRetrograde": 4702531629752352633, + "NextJupiterRetrogradeToPrograde": 4702531892068924335, + "NextJupiterWesternQuadrature": 4702531575658544590 + } + }, + { + "input_utc": "2016-01-19T06:20:40.456Z", + "tt_jd_bits": 4702531652192063576, + "events": { + "LastJupiterConjunction": 4702531340061737657, + "LastJupiterEasternQuadrature": 4702531094085273298, + "LastJupiterOpposition": 4702530908086288928, + "LastJupiterProgradeToRetrograde": 4702531629752352633, + "LastJupiterRetrogradeToPrograde": 4702531039231776308, + "LastJupiterWesternQuadrature": 4702531575658544590, + "NextJupiterConjunction": 4702532191267004195, + "NextJupiterEasternQuadrature": 4702531946807824433, + "NextJupiterOpposition": 4702531757829128992, + "NextJupiterProgradeToRetrograde": 4702532477989657578, + "NextJupiterRetrogradeToPrograde": 4702531892068924335, + "NextJupiterWesternQuadrature": 4702532422990713411 + } + }, + { + "input_utc": "2016-09-02T10:29:57.456Z", + "tt_jd_bits": 4702532140042609924, + "events": { + "LastJupiterConjunction": 4702531340061737657, + "LastJupiterEasternQuadrature": 4702531946807824433, + "LastJupiterOpposition": 4702531757829128992, + "LastJupiterProgradeToRetrograde": 4702531629752352633, + "LastJupiterRetrogradeToPrograde": 4702531892068924335, + "LastJupiterWesternQuadrature": 4702531575658544590, + "NextJupiterConjunction": 4702532191267004195, + "NextJupiterEasternQuadrature": 4702532798623165754, + "NextJupiterOpposition": 4702532607042507489, + "NextJupiterProgradeToRetrograde": 4702532477989657578, + "NextJupiterRetrogradeToPrograde": 4702532742987172719, + "NextJupiterWesternQuadrature": 4702532422990713411 + } + }, + { + "input_utc": "2017-04-17T14:39:14.456Z", + "tt_jd_bits": 4702532627893181126, + "events": { + "LastJupiterConjunction": 4702532191267004195, + "LastJupiterEasternQuadrature": 4702531946807824433, + "LastJupiterOpposition": 4702532607042507489, + "LastJupiterProgradeToRetrograde": 4702532477989657578, + "LastJupiterRetrogradeToPrograde": 4702531892068924335, + "LastJupiterWesternQuadrature": 4702532422990713411, + "NextJupiterConjunction": 4702533040521769387, + "NextJupiterEasternQuadrature": 4702532798623165754, + "NextJupiterOpposition": 4702533457714634378, + "NextJupiterProgradeToRetrograde": 4702533327528957514, + "NextJupiterRetrogradeToPrograde": 4702532742987172719, + "NextJupiterWesternQuadrature": 4702533270766621283 + } + }, + { + "input_utc": "2017-11-30T18:48:31.456Z", + "tt_jd_bits": 4702533115743727473, + "events": { + "LastJupiterConjunction": 4702533040521769387, + "LastJupiterEasternQuadrature": 4702532798623165754, + "LastJupiterOpposition": 4702532607042507489, + "LastJupiterProgradeToRetrograde": 4702532477989657578, + "LastJupiterRetrogradeToPrograde": 4702532742987172719, + "LastJupiterWesternQuadrature": 4702532422990713411, + "NextJupiterConjunction": 4702533889886944756, + "NextJupiterEasternQuadrature": 4702533650881330300, + "NextJupiterOpposition": 4702533457714634378, + "NextJupiterProgradeToRetrograde": 4702533327528957514, + "NextJupiterRetrogradeToPrograde": 4702533593289599713, + "NextJupiterWesternQuadrature": 4702533270766621283 + } + }, + { + "input_utc": "2018-07-15T21:18:48.456Z", + "tt_jd_bits": 4702533603446634320, + "events": { + "LastJupiterConjunction": 4702533040521769387, + "LastJupiterEasternQuadrature": 4702532798623165754, + "LastJupiterOpposition": 4702533457714634378, + "LastJupiterProgradeToRetrograde": 4702533327528957514, + "LastJupiterRetrogradeToPrograde": 4702533593289599713, + "LastJupiterWesternQuadrature": 4702533270766621283, + "NextJupiterConjunction": 4702533889886944756, + "NextJupiterEasternQuadrature": 4702533650881330300, + "NextJupiterOpposition": 4702534311590893876, + "NextJupiterProgradeToRetrograde": 4702534180736365165, + "NextJupiterRetrogradeToPrograde": 4702534444823697906, + "NextJupiterWesternQuadrature": 4702534121361764961 + } + }, + { + "input_utc": "2019-02-28T01:28:05.456Z", + "tt_jd_bits": 4702534091297180667, + "events": { + "LastJupiterConjunction": 4702533889886944756, + "LastJupiterEasternQuadrature": 4702533650881330300, + "LastJupiterOpposition": 4702533457714634378, + "LastJupiterProgradeToRetrograde": 4702533327528957514, + "LastJupiterRetrogradeToPrograde": 4702533593289599713, + "LastJupiterWesternQuadrature": 4702533270766621283, + "NextJupiterConjunction": 4702534741352700914, + "NextJupiterEasternQuadrature": 4702534504862757352, + "NextJupiterOpposition": 4702534311590893876, + "NextJupiterProgradeToRetrograde": 4702534180736365165, + "NextJupiterRetrogradeToPrograde": 4702534444823697906, + "NextJupiterWesternQuadrature": 4702534121361764961 + } + }, + { + "input_utc": "2019-10-13T05:37:22.456Z", + "tt_jd_bits": 4702534579147727015, + "events": { + "LastJupiterConjunction": 4702533889886944756, + "LastJupiterEasternQuadrature": 4702534504862757352, + "LastJupiterOpposition": 4702534311590893876, + "LastJupiterProgradeToRetrograde": 4702534180736365165, + "LastJupiterRetrogradeToPrograde": 4702534444823697906, + "LastJupiterWesternQuadrature": 4702534121361764961, + "NextJupiterConjunction": 4702534741352700914, + "NextJupiterEasternQuadrature": 4702535361541379329, + "NextJupiterOpposition": 4702535169914397810, + "NextJupiterProgradeToRetrograde": 4702535039856600034, + "NextJupiterRetrogradeToPrograde": 4702535300189392750, + "NextJupiterWesternQuadrature": 4702534976909582523 + } + }, + { + "input_utc": "2020-05-27T09:46:39.456Z", + "tt_jd_bits": 4702535066998273362, + "events": { + "LastJupiterConjunction": 4702534741352700914, + "LastJupiterEasternQuadrature": 4702534504862757352, + "LastJupiterOpposition": 4702534311590893876, + "LastJupiterProgradeToRetrograde": 4702535039856600034, + "LastJupiterRetrogradeToPrograde": 4702534444823697906, + "LastJupiterWesternQuadrature": 4702534976909582523, + "NextJupiterConjunction": 4702535596698593217, + "NextJupiterEasternQuadrature": 4702535361541379329, + "NextJupiterOpposition": 4702535169914397810, + "NextJupiterProgradeToRetrograde": 4702535904045179664, + "NextJupiterRetrogradeToPrograde": 4702535300189392750, + "NextJupiterWesternQuadrature": 4702535838415570408 + } + }, + { + "input_utc": "1991-04-17T05:17:23.456Z", + "tt_jd_bits": 4702512232402811213, + "events": { + "LastJupiterConjunction": 4702511639717012620, + "LastJupiterEasternQuadrature": 4702511393973012502, + "LastJupiterOpposition": 4702512064463236620, + "LastJupiterProgradeToRetrograde": 4702511936733572185, + "LastJupiterRetrogradeToPrograde": 4702512194567421550, + "LastJupiterWesternQuadrature": 4702511882517375690, + "NextJupiterConjunction": 4702512495925443449, + "NextJupiterEasternQuadrature": 4702512249695175741, + "NextJupiterOpposition": 4702512914882489890, + "NextJupiterProgradeToRetrograde": 4702512787041232554, + "NextJupiterRetrogradeToPrograde": 4702513048376039516, + "NextJupiterWesternQuadrature": 4702512732981189693 + } + }, + { + "input_utc": "1991-09-13T12:30:46.456Z", + "tt_jd_bits": 4702512553024182836, + "events": { + "LastJupiterConjunction": 4702512495925443449, + "LastJupiterEasternQuadrature": 4702512249695175741, + "LastJupiterOpposition": 4702512064463236620, + "LastJupiterProgradeToRetrograde": 4702511936733572185, + "LastJupiterRetrogradeToPrograde": 4702512194567421550, + "LastJupiterWesternQuadrature": 4702511882517375690, + "NextJupiterConjunction": 4702513348128945305, + "NextJupiterEasternQuadrature": 4702513103067048699, + "NextJupiterOpposition": 4702512914882489890, + "NextJupiterProgradeToRetrograde": 4702512787041232554, + "NextJupiterRetrogradeToPrograde": 4702513048376039518, + "NextJupiterWesternQuadrature": 4702512732981189693 + } + }, + { + "input_utc": "1992-02-09T19:44:09.456Z", + "tt_jd_bits": 4702512873645554458, + "events": { + "LastJupiterConjunction": 4702512495925443449, + "LastJupiterEasternQuadrature": 4702512249695175741, + "LastJupiterOpposition": 4702512064463236620, + "LastJupiterProgradeToRetrograde": 4702512787041232554, + "LastJupiterRetrogradeToPrograde": 4702512194567421550, + "LastJupiterWesternQuadrature": 4702512732981189693, + "NextJupiterConjunction": 4702513348128945305, + "NextJupiterEasternQuadrature": 4702513103067048699, + "NextJupiterOpposition": 4702512914882489890, + "NextJupiterProgradeToRetrograde": 4702513635402149085, + "NextJupiterRetrogradeToPrograde": 4702513048376039518, + "NextJupiterWesternQuadrature": 4702513580732728436 + } + }, + { + "input_utc": "1992-07-08T02:57:32.456Z", + "tt_jd_bits": 4702513194266950936, + "events": { + "LastJupiterConjunction": 4702512495925443449, + "LastJupiterEasternQuadrature": 4702513103067048699, + "LastJupiterOpposition": 4702512914882489890, + "LastJupiterProgradeToRetrograde": 4702512787041232554, + "LastJupiterRetrogradeToPrograde": 4702513048376039516, + "LastJupiterWesternQuadrature": 4702512732981189693, + "NextJupiterConjunction": 4702513348128945305, + "NextJupiterEasternQuadrature": 4702513955120488411, + "NextJupiterOpposition": 4702513764159242873, + "NextJupiterProgradeToRetrograde": 4702513635402149085, + "NextJupiterRetrogradeToPrograde": 4702513899777250709, + "NextJupiterWesternQuadrature": 4702513580732728436 + } + }, + { + "input_utc": "1992-12-04T10:10:55.456Z", + "tt_jd_bits": 4702513514888322559, + "events": { + "LastJupiterConjunction": 4702513348128945305, + "LastJupiterEasternQuadrature": 4702513103067048699, + "LastJupiterOpposition": 4702512914882489890, + "LastJupiterProgradeToRetrograde": 4702512787041232554, + "LastJupiterRetrogradeToPrograde": 4702513048376039518, + "LastJupiterWesternQuadrature": 4702512732981189693, + "NextJupiterConjunction": 4702514197794183218, + "NextJupiterEasternQuadrature": 4702513955120488411, + "NextJupiterOpposition": 4702513764159242873, + "NextJupiterProgradeToRetrograde": 4702513635402149085, + "NextJupiterRetrogradeToPrograde": 4702513899777250709, + "NextJupiterWesternQuadrature": 4702513580732728436 + } + }, + { + "input_utc": "1993-05-02T17:24:18.456Z", + "tt_jd_bits": 4702513835509694181, + "events": { + "LastJupiterConjunction": 4702513348128945305, + "LastJupiterEasternQuadrature": 4702513103067048699, + "LastJupiterOpposition": 4702513764159242873, + "LastJupiterProgradeToRetrograde": 4702513635402149085, + "LastJupiterRetrogradeToPrograde": 4702513048376039516, + "LastJupiterWesternQuadrature": 4702513580732728436, + "NextJupiterConjunction": 4702514197794183218, + "NextJupiterEasternQuadrature": 4702513955120488411, + "NextJupiterOpposition": 4702514614284672284, + "NextJupiterProgradeToRetrograde": 4702514484375761159, + "NextJupiterRetrogradeToPrograde": 4702513899777250709, + "NextJupiterWesternQuadrature": 4702514428184996343 + } + }, + { + "input_utc": "1993-09-27T06:37:41.456Z", + "tt_jd_bits": 4702514152372994275, + "events": { + "LastJupiterConjunction": 4702513348128945305, + "LastJupiterEasternQuadrature": 4702513955120488411, + "LastJupiterOpposition": 4702513764159242873, + "LastJupiterProgradeToRetrograde": 4702513635402149085, + "LastJupiterRetrogradeToPrograde": 4702513899777250709, + "LastJupiterWesternQuadrature": 4702513580732728436, + "NextJupiterConjunction": 4702514197794183218, + "NextJupiterEasternQuadrature": 4702514807158095234, + "NextJupiterOpposition": 4702514614284672284, + "NextJupiterProgradeToRetrograde": 4702514484375761159, + "NextJupiterRetrogradeToPrograde": 4702514750232579882, + "NextJupiterWesternQuadrature": 4702514428184996343 + } + }, + { + "input_utc": "1994-02-23T13:51:04.456Z", + "tt_jd_bits": 4702514472994365897, + "events": { + "LastJupiterConjunction": 4702514197794183218, + "LastJupiterEasternQuadrature": 4702513955120488411, + "LastJupiterOpposition": 4702513764159242873, + "LastJupiterProgradeToRetrograde": 4702513635402149085, + "LastJupiterRetrogradeToPrograde": 4702513899777250709, + "LastJupiterWesternQuadrature": 4702514428184996343, + "NextJupiterConjunction": 4702515046903579625, + "NextJupiterEasternQuadrature": 4702514807158095234, + "NextJupiterOpposition": 4702514614284672284, + "NextJupiterProgradeToRetrograde": 4702514484375761159, + "NextJupiterRetrogradeToPrograde": 4702514750232579882, + "NextJupiterWesternQuadrature": 4702515277713988613 + } + }, + { + "input_utc": "1994-07-22T21:04:27.456Z", + "tt_jd_bits": 4702514793615762376, + "events": { + "LastJupiterConjunction": 4702514197794183218, + "LastJupiterEasternQuadrature": 4702513955120488411, + "LastJupiterOpposition": 4702514614284672284, + "LastJupiterProgradeToRetrograde": 4702514484375761159, + "LastJupiterRetrogradeToPrograde": 4702514750232579882, + "LastJupiterWesternQuadrature": 4702514428184996343, + "NextJupiterConjunction": 4702515046903579625, + "NextJupiterEasternQuadrature": 4702514807158095234, + "NextJupiterOpposition": 4702515467054813064, + "NextJupiterProgradeToRetrograde": 4702515336181910643, + "NextJupiterRetrogradeToPrograde": 4702515601124866503, + "NextJupiterWesternQuadrature": 4702515277713988613 + } + }, + { + "input_utc": "1994-12-19T04:17:50.456Z", + "tt_jd_bits": 4702515114237133999, + "events": { + "LastJupiterConjunction": 4702515046903579625, + "LastJupiterEasternQuadrature": 4702514807158095234, + "LastJupiterOpposition": 4702514614284672284, + "LastJupiterProgradeToRetrograde": 4702514484375761159, + "LastJupiterRetrogradeToPrograde": 4702514750232579882, + "LastJupiterWesternQuadrature": 4702514428184996343, + "NextJupiterConjunction": 4702515897478373452, + "NextJupiterEasternQuadrature": 4702515660462348923, + "NextJupiterOpposition": 4702515467054813064, + "NextJupiterProgradeToRetrograde": 4702515336181910643, + "NextJupiterRetrogradeToPrograde": 4702515601124866503, + "NextJupiterWesternQuadrature": 4702515277713988613 + } + }, + { + "input_utc": "1995-05-17T09:21:13.456Z", + "tt_jd_bits": 4702515434664635570, + "events": { + "LastJupiterConjunction": 4702515046903579625, + "LastJupiterEasternQuadrature": 4702514807158095234, + "LastJupiterOpposition": 4702514614284672284, + "LastJupiterProgradeToRetrograde": 4702515336181910643, + "LastJupiterRetrogradeToPrograde": 4702514750232579882, + "LastJupiterWesternQuadrature": 4702515277713988613, + "NextJupiterConjunction": 4702515897478373452, + "NextJupiterEasternQuadrature": 4702515660462348923, + "NextJupiterOpposition": 4702515467054813064, + "NextJupiterProgradeToRetrograde": 4702516193453094495, + "NextJupiterRetrogradeToPrograde": 4702515601124866503, + "NextJupiterWesternQuadrature": 4702516131575705913 + } + }, + { + "input_utc": "1995-10-13T16:34:36.456Z", + "tt_jd_bits": 4702515755286007192, + "events": { + "LastJupiterConjunction": 4702515046903579625, + "LastJupiterEasternQuadrature": 4702515660462348923, + "LastJupiterOpposition": 4702515467054813064, + "LastJupiterProgradeToRetrograde": 4702515336181910643, + "LastJupiterRetrogradeToPrograde": 4702515601124866503, + "LastJupiterWesternQuadrature": 4702515277713988613, + "NextJupiterConjunction": 4702515897478373452, + "NextJupiterEasternQuadrature": 4702516516159983202, + "NextJupiterOpposition": 4702516323930526658, + "NextJupiterProgradeToRetrograde": 4702516193453094495, + "NextJupiterRetrogradeToPrograde": 4702516455127076536, + "NextJupiterWesternQuadrature": 4702516131575705913 + } + }, + { + "input_utc": "1996-03-09T05:47:59.456Z", + "tt_jd_bits": 4702516072149307286, + "events": { + "LastJupiterConjunction": 4702515897478373452, + "LastJupiterEasternQuadrature": 4702515660462348923, + "LastJupiterOpposition": 4702515467054813064, + "LastJupiterProgradeToRetrograde": 4702515336181910643, + "LastJupiterRetrogradeToPrograde": 4702515601124866503, + "LastJupiterWesternQuadrature": 4702515277713988613, + "NextJupiterConjunction": 4702516751407755873, + "NextJupiterEasternQuadrature": 4702516516159983202, + "NextJupiterOpposition": 4702516323930526658, + "NextJupiterProgradeToRetrograde": 4702516193453094495, + "NextJupiterRetrogradeToPrograde": 4702516455127076537, + "NextJupiterWesternQuadrature": 4702516131575705913 + } + }, + { + "input_utc": "1996-08-05T13:01:22.456Z", + "tt_jd_bits": 4702516392770678908, + "events": { + "LastJupiterConjunction": 4702515897478373452, + "LastJupiterEasternQuadrature": 4702515660462348923, + "LastJupiterOpposition": 4702516323930526658, + "LastJupiterProgradeToRetrograde": 4702516193453094495, + "LastJupiterRetrogradeToPrograde": 4702515601124866503, + "LastJupiterWesternQuadrature": 4702516131575705913, + "NextJupiterConjunction": 4702516751407755873, + "NextJupiterEasternQuadrature": 4702516516159983202, + "NextJupiterOpposition": 4702517185247368069, + "NextJupiterProgradeToRetrograde": 4702517056174162728, + "NextJupiterRetrogradeToPrograde": 4702516455127076536, + "NextJupiterWesternQuadrature": 4702516991222788291 + } + }, + { + "input_utc": "1997-01-01T20:14:45.456Z", + "tt_jd_bits": 4702516713392050531, + "events": { + "LastJupiterConjunction": 4702515897478373452, + "LastJupiterEasternQuadrature": 4702516516159983202, + "LastJupiterOpposition": 4702516323930526658, + "LastJupiterProgradeToRetrograde": 4702516193453094495, + "LastJupiterRetrogradeToPrograde": 4702516455127076537, + "LastJupiterWesternQuadrature": 4702516131575705913, + "NextJupiterConjunction": 4702516751407755873, + "NextJupiterEasternQuadrature": 4702517374743580578, + "NextJupiterOpposition": 4702517185247368069, + "NextJupiterProgradeToRetrograde": 4702517056174162728, + "NextJupiterRetrogradeToPrograde": 4702517313542566335, + "NextJupiterWesternQuadrature": 4702516991222788291 + } + }, + { + "input_utc": "1997-05-31T03:22:23.456Z", + "tt_jd_bits": 4702517034004847132, + "events": { + "LastJupiterConjunction": 4702516751407755873, + "LastJupiterEasternQuadrature": 4702516516159983202, + "LastJupiterOpposition": 4702516323930526658, + "LastJupiterProgradeToRetrograde": 4702516193453094495, + "LastJupiterRetrogradeToPrograde": 4702516455127076537, + "LastJupiterWesternQuadrature": 4702516991222788291, + "NextJupiterConjunction": 4702517610018423635, + "NextJupiterEasternQuadrature": 4702517374743580578, + "NextJupiterOpposition": 4702517185247368069, + "NextJupiterProgradeToRetrograde": 4702517056174162728, + "NextJupiterRetrogradeToPrograde": 4702517313542566335, + "NextJupiterWesternQuadrature": 4702517856327985406 + } + }, + { + "input_utc": "1997-10-27T10:35:46.456Z", + "tt_jd_bits": 4702517354626243610, + "events": { + "LastJupiterConjunction": 4702516751407755873, + "LastJupiterEasternQuadrature": 4702516516159983202, + "LastJupiterOpposition": 4702517185247368069, + "LastJupiterProgradeToRetrograde": 4702517056174162728, + "LastJupiterRetrogradeToPrograde": 4702517313542566335, + "LastJupiterWesternQuadrature": 4702516991222788291, + "NextJupiterConjunction": 4702517610018423635, + "NextJupiterEasternQuadrature": 4702517374743580578, + "NextJupiterOpposition": 4702518049732310450, + "NextJupiterProgradeToRetrograde": 4702517922209431808, + "NextJupiterRetrogradeToPrograde": 4702518176266583881, + "NextJupiterWesternQuadrature": 4702517856327985406 + } + }, + { + "input_utc": "1998-03-25T17:49:09.456Z", + "tt_jd_bits": 4702517675247615232, + "events": { + "LastJupiterConjunction": 4702517610018423635, + "LastJupiterEasternQuadrature": 4702517374743580578, + "LastJupiterOpposition": 4702517185247368069, + "LastJupiterProgradeToRetrograde": 4702517056174162728, + "LastJupiterRetrogradeToPrograde": 4702517313542566335, + "LastJupiterWesternQuadrature": 4702516991222788291, + "NextJupiterConjunction": 4702518473067532604, + "NextJupiterEasternQuadrature": 4702518235789103668, + "NextJupiterOpposition": 4702518049732310450, + "NextJupiterProgradeToRetrograde": 4702517922209431808, + "NextJupiterRetrogradeToPrograde": 4702518176266583881, + "NextJupiterWesternQuadrature": 4702517856327985406 + } + }, + { + "input_utc": "1998-08-20T07:02:32.456Z", + "tt_jd_bits": 4702517992110890471, + "events": { + "LastJupiterConjunction": 4702517610018423635, + "LastJupiterEasternQuadrature": 4702517374743580578, + "LastJupiterOpposition": 4702517185247368069, + "LastJupiterProgradeToRetrograde": 4702517922209431808, + "LastJupiterRetrogradeToPrograde": 4702517313542566335, + "LastJupiterWesternQuadrature": 4702517856327985406, + "NextJupiterConjunction": 4702518473067532604, + "NextJupiterEasternQuadrature": 4702518235789103668, + "NextJupiterOpposition": 4702518049732310450, + "NextJupiterProgradeToRetrograde": 4702518787154295667, + "NextJupiterRetrogradeToPrograde": 4702518176266583881, + "NextJupiterWesternQuadrature": 4702518723959510877 + } + }, + { + "input_utc": "1999-01-16T14:15:55.456Z", + "tt_jd_bits": 4702518312732286948, + "events": { + "LastJupiterConjunction": 4702517610018423635, + "LastJupiterEasternQuadrature": 4702518235789103668, + "LastJupiterOpposition": 4702518049732310450, + "LastJupiterProgradeToRetrograde": 4702517922209431808, + "LastJupiterRetrogradeToPrograde": 4702518176266583881, + "LastJupiterWesternQuadrature": 4702517856327985406, + "NextJupiterConjunction": 4702518473067532604, + "NextJupiterEasternQuadrature": 4702519097687911871, + "NextJupiterOpposition": 4702518914455502652, + "NextJupiterProgradeToRetrograde": 4702518787154295667, + "NextJupiterRetrogradeToPrograde": 4702519039884020798, + "NextJupiterWesternQuadrature": 4702518723959510877 + } + }, + { + "input_utc": "1999-06-14T19:19:18.456Z", + "tt_jd_bits": 4702518633159788519, + "events": { + "LastJupiterConjunction": 4702518473067532604, + "LastJupiterEasternQuadrature": 4702518235789103668, + "LastJupiterOpposition": 4702518049732310450, + "LastJupiterProgradeToRetrograde": 4702517922209431808, + "LastJupiterRetrogradeToPrograde": 4702518176266583881, + "LastJupiterWesternQuadrature": 4702517856327985406, + "NextJupiterConjunction": 4702519338321929243, + "NextJupiterEasternQuadrature": 4702519097687911871, + "NextJupiterOpposition": 4702518914455502652, + "NextJupiterProgradeToRetrograde": 4702518787154295667, + "NextJupiterRetrogradeToPrograde": 4702519039884020798, + "NextJupiterWesternQuadrature": 4702518723959510877 + } + }, + { + "input_utc": "1999-11-11T02:32:41.456Z", + "tt_jd_bits": 4702518953781160142, + "events": { + "LastJupiterConjunction": 4702518473067532604, + "LastJupiterEasternQuadrature": 4702518235789103668, + "LastJupiterOpposition": 4702518914455502652, + "LastJupiterProgradeToRetrograde": 4702518787154295667, + "LastJupiterRetrogradeToPrograde": 4702518176266583881, + "LastJupiterWesternQuadrature": 4702518723959510877, + "NextJupiterConjunction": 4702519338321929243, + "NextJupiterEasternQuadrature": 4702519097687911871, + "NextJupiterOpposition": 4702519776235616013, + "NextJupiterProgradeToRetrograde": 4702519648440844497, + "NextJupiterRetrogradeToPrograde": 4702519039884020798, + "NextJupiterWesternQuadrature": 4702519589634038153 + } + }, + { + "input_utc": "2000-04-08T09:46:04.456Z", + "tt_jd_bits": 4702519274402531765, + "events": { + "LastJupiterConjunction": 4702518473067532604, + "LastJupiterEasternQuadrature": 4702519097687911871, + "LastJupiterOpposition": 4702518914455502652, + "LastJupiterProgradeToRetrograde": 4702518787154295667, + "LastJupiterRetrogradeToPrograde": 4702519039884020798, + "LastJupiterWesternQuadrature": 4702518723959510877, + "NextJupiterConjunction": 4702519338321929243, + "NextJupiterEasternQuadrature": 4702519958453308414, + "NextJupiterOpposition": 4702519776235616013, + "NextJupiterProgradeToRetrograde": 4702519648440844497, + "NextJupiterRetrogradeToPrograde": 4702519901906597261, + "NextJupiterWesternQuadrature": 4702519589634038153 + } + }, + { + "input_utc": "2000-09-04T16:59:27.456Z", + "tt_jd_bits": 4702519595023903387, + "events": { + "LastJupiterConjunction": 4702519338321929243, + "LastJupiterEasternQuadrature": 4702519097687911871, + "LastJupiterOpposition": 4702518914455502652, + "LastJupiterProgradeToRetrograde": 4702518787154295667, + "LastJupiterRetrogradeToPrograde": 4702519039884020798, + "LastJupiterWesternQuadrature": 4702519589634038153, + "NextJupiterConjunction": 4702520202370163856, + "NextJupiterEasternQuadrature": 4702519958453308414, + "NextJupiterOpposition": 4702519776235616013, + "NextJupiterProgradeToRetrograde": 4702519648440844497, + "NextJupiterRetrogradeToPrograde": 4702519901906597261, + "NextJupiterWesternQuadrature": 4702520450002368788 + } + }, + { + "input_utc": "2001-01-30T06:12:50.456Z", + "tt_jd_bits": 4702519911887178626, + "events": { + "LastJupiterConjunction": 4702519338321929243, + "LastJupiterEasternQuadrature": 4702519097687911871, + "LastJupiterOpposition": 4702519776235616013, + "LastJupiterProgradeToRetrograde": 4702519648440844497, + "LastJupiterRetrogradeToPrograde": 4702519901906597261, + "LastJupiterWesternQuadrature": 4702519589634038153, + "NextJupiterConjunction": 4702520202370163856, + "NextJupiterEasternQuadrature": 4702519958453308414, + "NextJupiterOpposition": 4702520633410522885, + "NextJupiterProgradeToRetrograde": 4702520505561003456, + "NextJupiterRetrogradeToPrograde": 4702520760902612018, + "NextJupiterWesternQuadrature": 4702520450002368788 + } + }, + { + "input_utc": "2001-06-28T13:26:13.456Z", + "tt_jd_bits": 4702520232508550248, + "events": { + "LastJupiterConjunction": 4702520202370163856, + "LastJupiterEasternQuadrature": 4702519958453308414, + "LastJupiterOpposition": 4702519776235616013, + "LastJupiterProgradeToRetrograde": 4702519648440844497, + "LastJupiterRetrogradeToPrograde": 4702519901906597261, + "LastJupiterWesternQuadrature": 4702519589634038153, + "NextJupiterConjunction": 4702521062497845376, + "NextJupiterEasternQuadrature": 4702520816619029323, + "NextJupiterOpposition": 4702520633410522885, + "NextJupiterProgradeToRetrograde": 4702520505561003456, + "NextJupiterRetrogradeToPrograde": 4702520760902612018, + "NextJupiterWesternQuadrature": 4702520450002368788 + } + }, + { + "input_utc": "2001-11-24T20:39:36.456Z", + "tt_jd_bits": 4702520553129921871, + "events": { + "LastJupiterConjunction": 4702520202370163856, + "LastJupiterEasternQuadrature": 4702519958453308414, + "LastJupiterOpposition": 4702519776235616013, + "LastJupiterProgradeToRetrograde": 4702520505561003456, + "LastJupiterRetrogradeToPrograde": 4702519901906597261, + "LastJupiterWesternQuadrature": 4702520450002368788, + "NextJupiterConjunction": 4702521062497845376, + "NextJupiterEasternQuadrature": 4702520816619029323, + "NextJupiterOpposition": 4702520633410522885, + "NextJupiterProgradeToRetrograde": 4702521358490633847, + "NextJupiterRetrogradeToPrograde": 4702520760902612018, + "NextJupiterWesternQuadrature": 4702521304428736524 + } + }, + { + "input_utc": "2002-04-23T03:52:59.456Z", + "tt_jd_bits": 4702520873751293494, + "events": { + "LastJupiterConjunction": 4702520202370163856, + "LastJupiterEasternQuadrature": 4702520816619029323, + "LastJupiterOpposition": 4702520633410522885, + "LastJupiterProgradeToRetrograde": 4702520505561003456, + "LastJupiterRetrogradeToPrograde": 4702520760902612018, + "LastJupiterWesternQuadrature": 4702520450002368788, + "NextJupiterConjunction": 4702521062497845376, + "NextJupiterEasternQuadrature": 4702521671866607659, + "NextJupiterOpposition": 4702521486257843655, + "NextJupiterProgradeToRetrograde": 4702521358490633847, + "NextJupiterRetrogradeToPrograde": 4702521616888415816, + "NextJupiterWesternQuadrature": 4702521304428736524 + } + }, + { + "input_utc": "2002-09-19T11:06:22.456Z", + "tt_jd_bits": 4702521194372665116, + "events": { + "LastJupiterConjunction": 4702521062497845376, + "LastJupiterEasternQuadrature": 4702520816619029323, + "LastJupiterOpposition": 4702520633410522885, + "LastJupiterProgradeToRetrograde": 4702520505561003456, + "LastJupiterRetrogradeToPrograde": 4702520760902612018, + "LastJupiterWesternQuadrature": 4702520450002368788, + "NextJupiterConjunction": 4702521917985855430, + "NextJupiterEasternQuadrature": 4702521671866607659, + "NextJupiterOpposition": 4702521486257843655, + "NextJupiterProgradeToRetrograde": 4702521358490633847, + "NextJupiterRetrogradeToPrograde": 4702521616888415816, + "NextJupiterWesternQuadrature": 4702521304428736524 + } + }, + { + "input_utc": "2003-02-15T18:19:45.456Z", + "tt_jd_bits": 4702521514994036739, + "events": { + "LastJupiterConjunction": 4702521062497845376, + "LastJupiterEasternQuadrature": 4702520816619029323, + "LastJupiterOpposition": 4702521486257843655, + "LastJupiterProgradeToRetrograde": 4702521358490633847, + "LastJupiterRetrogradeToPrograde": 4702520760902612018, + "LastJupiterWesternQuadrature": 4702521304428736524, + "NextJupiterConjunction": 4702521917985855430, + "NextJupiterEasternQuadrature": 4702521671866607659, + "NextJupiterOpposition": 4702522336293893979, + "NextJupiterProgradeToRetrograde": 4702522208314915907, + "NextJupiterRetrogradeToPrograde": 4702521616888415816, + "NextJupiterWesternQuadrature": 4702522154284300978 + } + }, + { + "input_utc": "2003-07-13T05:17:23.456Z", + "tt_jd_bits": 4702521831654866904, + "events": { + "LastJupiterConjunction": 4702521062497845376, + "LastJupiterEasternQuadrature": 4702521671866607659, + "LastJupiterOpposition": 4702521486257843655, + "LastJupiterProgradeToRetrograde": 4702521358490633847, + "LastJupiterRetrogradeToPrograde": 4702521616888415816, + "LastJupiterWesternQuadrature": 4702521304428736524, + "NextJupiterConjunction": 4702521917985855430, + "NextJupiterEasternQuadrature": 4702522524867228697, + "NextJupiterOpposition": 4702522336293893979, + "NextJupiterProgradeToRetrograde": 4702522208314915907, + "NextJupiterRetrogradeToPrograde": 4702522470158124246, + "NextJupiterWesternQuadrature": 4702522154284300978 + } + }, + { + "input_utc": "2003-12-09T12:30:46.456Z", + "tt_jd_bits": 4702522152276238527, + "events": { + "LastJupiterConjunction": 4702521917985855430, + "LastJupiterEasternQuadrature": 4702521671866607659, + "LastJupiterOpposition": 4702521486257843655, + "LastJupiterProgradeToRetrograde": 4702521358490633847, + "LastJupiterRetrogradeToPrograde": 4702521616888415816, + "LastJupiterWesternQuadrature": 4702521304428736524, + "NextJupiterConjunction": 4702522769611689768, + "NextJupiterEasternQuadrature": 4702522524867228697, + "NextJupiterOpposition": 4702522336293893979, + "NextJupiterProgradeToRetrograde": 4702522208314915907, + "NextJupiterRetrogradeToPrograde": 4702522470158124246, + "NextJupiterWesternQuadrature": 4702522154284300978 + } + }, + { + "input_utc": "2004-05-06T19:44:09.456Z", + "tt_jd_bits": 4702522472897610149, + "events": { + "LastJupiterConjunction": 4702521917985855430, + "LastJupiterEasternQuadrature": 4702521671866607659, + "LastJupiterOpposition": 4702522336293893979, + "LastJupiterProgradeToRetrograde": 4702522208314915907, + "LastJupiterRetrogradeToPrograde": 4702522470158124246, + "LastJupiterWesternQuadrature": 4702522154284300978, + "NextJupiterConjunction": 4702522769611689768, + "NextJupiterEasternQuadrature": 4702522524867228697, + "NextJupiterOpposition": 4702523185481663723, + "NextJupiterProgradeToRetrograde": 4702523056647900438, + "NextJupiterRetrogradeToPrograde": 4702523321353238901, + "NextJupiterWesternQuadrature": 4702523001754930387 + } + }, + { + "input_utc": "2004-10-03T02:57:32.456Z", + "tt_jd_bits": 4702522793518981772, + "events": { + "LastJupiterConjunction": 4702522769611689768, + "LastJupiterEasternQuadrature": 4702522524867228697, + "LastJupiterOpposition": 4702522336293893979, + "LastJupiterProgradeToRetrograde": 4702522208314915907, + "LastJupiterRetrogradeToPrograde": 4702522470158124246, + "LastJupiterWesternQuadrature": 4702522154284300978, + "NextJupiterConjunction": 4702523619040346990, + "NextJupiterEasternQuadrature": 4702523376757271166, + "NextJupiterOpposition": 4702523185481663723, + "NextJupiterProgradeToRetrograde": 4702523056647900438, + "NextJupiterRetrogradeToPrograde": 4702523321353238901, + "NextJupiterWesternQuadrature": 4702523001754930387 + } + }, + { + "input_utc": "2005-03-01T10:10:55.456Z", + "tt_jd_bits": 4702523114140353395, + "events": { + "LastJupiterConjunction": 4702522769611689768, + "LastJupiterEasternQuadrature": 4702522524867228697, + "LastJupiterOpposition": 4702522336293893979, + "LastJupiterProgradeToRetrograde": 4702523056647900438, + "LastJupiterRetrogradeToPrograde": 4702522470158124246, + "LastJupiterWesternQuadrature": 4702523001754930387, + "NextJupiterConjunction": 4702523619040346990, + "NextJupiterEasternQuadrature": 4702523376757271166, + "NextJupiterOpposition": 4702523185481663723, + "NextJupiterProgradeToRetrograde": 4702523905663624695, + "NextJupiterRetrogradeToPrograde": 4702523321353238901, + "NextJupiterWesternQuadrature": 4702523849302140088 + } + }, + { + "input_utc": "2005-07-28T17:24:18.456Z", + "tt_jd_bits": 4702523434761725017, + "events": { + "LastJupiterConjunction": 4702522769611689768, + "LastJupiterEasternQuadrature": 4702523376757271166, + "LastJupiterOpposition": 4702523185481663723, + "LastJupiterProgradeToRetrograde": 4702523056647900438, + "LastJupiterRetrogradeToPrograde": 4702523321353238898, + "LastJupiterWesternQuadrature": 4702523001754930387, + "NextJupiterConjunction": 4702523619040346990, + "NextJupiterEasternQuadrature": 4702524228832709253, + "NextJupiterOpposition": 4702524035804970436, + "NextJupiterProgradeToRetrograde": 4702523905663624695, + "NextJupiterRetrogradeToPrograde": 4702524171499669562, + "NextJupiterWesternQuadrature": 4702523849302140088 + } + }, + { + "input_utc": "2005-12-23T06:37:41.456Z", + "tt_jd_bits": 4702523751625000256, + "events": { + "LastJupiterConjunction": 4702523619040346990, + "LastJupiterEasternQuadrature": 4702523376757271166, + "LastJupiterOpposition": 4702523185481663723, + "LastJupiterProgradeToRetrograde": 4702523056647900438, + "LastJupiterRetrogradeToPrograde": 4702523321353238901, + "LastJupiterWesternQuadrature": 4702523001754930387, + "NextJupiterConjunction": 4702524468223240294, + "NextJupiterEasternQuadrature": 4702524228832709253, + "NextJupiterOpposition": 4702524035804970436, + "NextJupiterProgradeToRetrograde": 4702523905663624695, + "NextJupiterRetrogradeToPrograde": 4702524171499669562, + "NextJupiterWesternQuadrature": 4702523849302140088 + } + }, + { + "input_utc": "2006-05-21T13:51:04.456Z", + "tt_jd_bits": 4702524072246396733, + "events": { + "LastJupiterConjunction": 4702523619040346990, + "LastJupiterEasternQuadrature": 4702523376757271166, + "LastJupiterOpposition": 4702524035804970436, + "LastJupiterProgradeToRetrograde": 4702523905663624695, + "LastJupiterRetrogradeToPrograde": 4702523321353238901, + "LastJupiterWesternQuadrature": 4702523849302140088, + "NextJupiterConjunction": 4702524468223240294, + "NextJupiterEasternQuadrature": 4702524228832709253, + "NextJupiterOpposition": 4702524889125945740, + "NextJupiterProgradeToRetrograde": 4702524758348277020, + "NextJupiterRetrogradeToPrograde": 4702524171499669562, + "NextJupiterWesternQuadrature": 4702524699351306591 + } + }, + { + "input_utc": "2006-10-17T21:04:27.456Z", + "tt_jd_bits": 4702524392867768356, + "events": { + "LastJupiterConjunction": 4702523619040346990, + "LastJupiterEasternQuadrature": 4702524228832709253, + "LastJupiterOpposition": 4702524035804970436, + "LastJupiterProgradeToRetrograde": 4702523905663624695, + "LastJupiterRetrogradeToPrograde": 4702524171499669562, + "LastJupiterWesternQuadrature": 4702523849302140088, + "NextJupiterConjunction": 4702524468223240294, + "NextJupiterEasternQuadrature": 4702525082476556773, + "NextJupiterOpposition": 4702524889125945740, + "NextJupiterProgradeToRetrograde": 4702524758348277020, + "NextJupiterRetrogradeToPrograde": 4702525022873871690, + "NextJupiterWesternQuadrature": 4702524699351306591 + } + }, + { + "input_utc": "2007-03-16T04:17:50.456Z", + "tt_jd_bits": 4702524713489139979, + "events": { + "LastJupiterConjunction": 4702524468223240294, + "LastJupiterEasternQuadrature": 4702524228832709253, + "LastJupiterOpposition": 4702524035804970436, + "LastJupiterProgradeToRetrograde": 4702523905663624695, + "LastJupiterRetrogradeToPrograde": 4702524171499669562, + "LastJupiterWesternQuadrature": 4702524699351306591, + "NextJupiterConjunction": 4702525319223979778, + "NextJupiterEasternQuadrature": 4702525082476556773, + "NextJupiterOpposition": 4702524889125945740, + "NextJupiterProgradeToRetrograde": 4702524758348277020, + "NextJupiterRetrogradeToPrograde": 4702525022873871692, + "NextJupiterWesternQuadrature": 4702525554037828411 + } + }, + { + "input_utc": "2007-08-12T09:21:13.456Z", + "tt_jd_bits": 4702525033916641550, + "events": { + "LastJupiterConjunction": 4702524468223240294, + "LastJupiterEasternQuadrature": 4702524228832709253, + "LastJupiterOpposition": 4702524889125945740, + "LastJupiterProgradeToRetrograde": 4702524758348277020, + "LastJupiterRetrogradeToPrograde": 4702525022873871692, + "LastJupiterWesternQuadrature": 4702524699351306591, + "NextJupiterConjunction": 4702525319223979778, + "NextJupiterEasternQuadrature": 4702525082476556773, + "NextJupiterOpposition": 4702525746727377977, + "NextJupiterProgradeToRetrograde": 4702525616388038372, + "NextJupiterRetrogradeToPrograde": 4702525877346821269, + "NextJupiterWesternQuadrature": 4702525554037828411 + } + }, + { + "input_utc": "2008-01-08T16:34:36.456Z", + "tt_jd_bits": 4702525354538013172, + "events": { + "LastJupiterConjunction": 4702525319223979778, + "LastJupiterEasternQuadrature": 4702525082476556773, + "LastJupiterOpposition": 4702524889125945740, + "LastJupiterProgradeToRetrograde": 4702524758348277020, + "LastJupiterRetrogradeToPrograde": 4702525022873871692, + "LastJupiterWesternQuadrature": 4702524699351306591, + "NextJupiterConjunction": 4702526173905210046, + "NextJupiterEasternQuadrature": 4702525938677881194, + "NextJupiterOpposition": 4702525746727377977, + "NextJupiterProgradeToRetrograde": 4702525616388038372, + "NextJupiterRetrogradeToPrograde": 4702525877346821269, + "NextJupiterWesternQuadrature": 4702525554037828411 + } + }, + { + "input_utc": "2008-06-04T05:47:59.456Z", + "tt_jd_bits": 4702525671401288411, + "events": { + "LastJupiterConjunction": 4702525319223979778, + "LastJupiterEasternQuadrature": 4702525082476556773, + "LastJupiterOpposition": 4702524889125945740, + "LastJupiterProgradeToRetrograde": 4702525616388038372, + "LastJupiterRetrogradeToPrograde": 4702525022873871692, + "LastJupiterWesternQuadrature": 4702525554037828411, + "NextJupiterConjunction": 4702526173905210046, + "NextJupiterEasternQuadrature": 4702525938677881194, + "NextJupiterOpposition": 4702525746727377977, + "NextJupiterProgradeToRetrograde": 4702526480112933471, + "NextJupiterRetrogradeToPrograde": 4702525877346821269, + "NextJupiterWesternQuadrature": 4702526414693471474 + } + }, + { + "input_utc": "2008-10-31T13:01:22.456Z", + "tt_jd_bits": 4702525992022660033, + "events": { + "LastJupiterConjunction": 4702525319223979778, + "LastJupiterEasternQuadrature": 4702525938677881194, + "LastJupiterOpposition": 4702525746727377977, + "LastJupiterProgradeToRetrograde": 4702525616388038372, + "LastJupiterRetrogradeToPrograde": 4702525877346821269, + "LastJupiterWesternQuadrature": 4702525554037828411, + "NextJupiterConjunction": 4702526173905210046, + "NextJupiterEasternQuadrature": 4702526797833950667, + "NextJupiterOpposition": 4702526608783663316, + "NextJupiterProgradeToRetrograde": 4702526480112933471, + "NextJupiterRetrogradeToPrograde": 4702526736805521956, + "NextJupiterWesternQuadrature": 4702526414693471474 + } + }, + { + "input_utc": "2009-03-29T20:14:45.456Z", + "tt_jd_bits": 4702526312644056511, + "events": { + "LastJupiterConjunction": 4702526173905210046, + "LastJupiterEasternQuadrature": 4702525938677881194, + "LastJupiterOpposition": 4702525746727377977, + "LastJupiterProgradeToRetrograde": 4702525616388038372, + "LastJupiterRetrogradeToPrograde": 4702525877346821269, + "LastJupiterWesternQuadrature": 4702525554037828411, + "NextJupiterConjunction": 4702527033345979976, + "NextJupiterEasternQuadrature": 4702526797833950667, + "NextJupiterOpposition": 4702526608783663316, + "NextJupiterProgradeToRetrograde": 4702526480112933471, + "NextJupiterRetrogradeToPrograde": 4702526736805521956, + "NextJupiterWesternQuadrature": 4702526414693471474 + } + }, + { + "input_utc": "2009-08-26T03:22:23.456Z", + "tt_jd_bits": 4702526633256853112, + "events": { + "LastJupiterConjunction": 4702526173905210046, + "LastJupiterEasternQuadrature": 4702525938677881194, + "LastJupiterOpposition": 4702526608783663316, + "LastJupiterProgradeToRetrograde": 4702526480112933471, + "LastJupiterRetrogradeToPrograde": 4702525877346821269, + "LastJupiterWesternQuadrature": 4702526414693471474, + "NextJupiterConjunction": 4702527033345979976, + "NextJupiterEasternQuadrature": 4702526797833950667, + "NextJupiterOpposition": 4702527473657079946, + "NextJupiterProgradeToRetrograde": 4702527346265792180, + "NextJupiterRetrogradeToPrograde": 4702526736805521956, + "NextJupiterWesternQuadrature": 4702527280539942363 + } + }, + { + "input_utc": "2010-01-22T10:35:46.456Z", + "tt_jd_bits": 4702526953878224735, + "events": { + "LastJupiterConjunction": 4702526173905210046, + "LastJupiterEasternQuadrature": 4702526797833950667, + "LastJupiterOpposition": 4702526608783663316, + "LastJupiterProgradeToRetrograde": 4702526480112933471, + "LastJupiterRetrogradeToPrograde": 4702526736805521956, + "LastJupiterWesternQuadrature": 4702526414693471474, + "NextJupiterConjunction": 4702527033345979976, + "NextJupiterEasternQuadrature": 4702527659252098549, + "NextJupiterOpposition": 4702527473657079946, + "NextJupiterProgradeToRetrograde": 4702527346265792180, + "NextJupiterRetrogradeToPrograde": 4702527599853542486, + "NextJupiterWesternQuadrature": 4702527280539942363 + } + }, + { + "input_utc": "2010-06-20T17:49:09.456Z", + "tt_jd_bits": 4702527274499596357, + "events": { + "LastJupiterConjunction": 4702527033345979976, + "LastJupiterEasternQuadrature": 4702526797833950667, + "LastJupiterOpposition": 4702526608783663316, + "LastJupiterProgradeToRetrograde": 4702526480112933471, + "LastJupiterRetrogradeToPrograde": 4702526736805521956, + "LastJupiterWesternQuadrature": 4702526414693471474, + "NextJupiterConjunction": 4702527896986511441, + "NextJupiterEasternQuadrature": 4702527659252098549, + "NextJupiterOpposition": 4702527473657079946, + "NextJupiterProgradeToRetrograde": 4702527346265792180, + "NextJupiterRetrogradeToPrograde": 4702527599853542486, + "NextJupiterWesternQuadrature": 4702527280539942363 + } + } + ] +} diff --git a/basic/testdata/semianalytic/lunar_eclipse_chauvenet.json b/basic/testdata/semianalytic/lunar_eclipse_chauvenet.json new file mode 100644 index 0000000..f7350dd --- /dev/null +++ b/basic/testdata/semianalytic/lunar_eclipse_chauvenet.json @@ -0,0 +1,50 @@ +{ + "2022-11-08 total/Magnitude": 1.3634869047622862, + "2022-11-08 total/Maximum": 2459891.958586728, + "2022-11-08 total/PartialEnd": 2459892.03512003, + "2022-11-08 total/PartialStart": 2459891.882020431, + "2022-11-08 total/PenumbralEnd": 2459892.0826407457, + "2022-11-08 total/PenumbralStart": 2459891.8346056496, + "2022-11-08 total/TotalEnd": 2459891.988323316, + "2022-11-08 total/TotalStart": 2459891.9288280853, + "2023-05-05 penumbral/PenumbralEnd": 2460070.315918079, + "2023-05-05 penumbral/PenumbralStart": 2460070.1342426105, + "2023-10-28 partial/Magnitude": 0.12726125019322218, + "2023-10-28 partial/Maximum": 2460246.3439447195, + "2023-10-28 partial/PartialEnd": 2460246.371335672, + "2023-10-28 partial/PartialStart": 2460246.316430631, + "2023-10-28 partial/PenumbralEnd": 2460246.437086942, + "2023-10-28 partial/PenumbralStart": 2460246.25076785, + "2024-03-25 penumbral/PenumbralEnd": 2460394.8999089086, + "2024-03-25 penumbral/PenumbralStart": 2460394.702885803, + "2024-09-18 partial/Magnitude": 0.09051791066257686, + "2024-09-18 partial/Maximum": 2460571.614876337, + "2024-09-18 partial/PartialEnd": 2460571.637426848, + "2024-09-18 partial/PartialStart": 2460571.5923553584, + "2024-09-18 partial/PenumbralEnd": 2460571.701651626, + "2024-09-18 partial/PenumbralStart": 2460571.528113657, + "2025-03-14 total/Magnitude": 1.1828338861983332, + "2025-03-14 total/Maximum": 2460748.7916221363, + "2025-03-14 total/PartialEnd": 2460748.867608604, + "2025-03-14 total/PartialStart": 2460748.71561116, + "2025-03-14 total/PenumbralEnd": 2460748.918761355, + "2025-03-14 total/PenumbralStart": 2460748.6645234893, + "2025-03-14 total/TotalEnd": 2460748.8146363995, + "2025-03-14 total/TotalStart": 2460748.7685898594, + "2025-09-07 total/Magnitude": 1.3672149936217577, + "2025-09-07 total/Maximum": 2460926.2590030446, + "2025-09-07 total/PartialEnd": 2460926.3319610883, + "2025-09-07 total/PartialStart": 2460926.1860739747, + "2025-09-07 total/PenumbralEnd": 2460926.3734494476, + "2025-09-07 total/PenumbralStart": 2460926.1445031273, + "2025-09-07 total/TotalEnd": 2460926.287786038, + "2025-09-07 total/TotalStart": 2460926.2302399734, + "2026-03-03 total/Magnitude": 1.1555608960337593, + "2026-03-03 total/Maximum": 2461102.9825472333, + "2026-03-03 total/PartialEnd": 2461103.0546876886, + "2026-03-03 total/PartialStart": 2461102.910363666, + "2026-03-03 total/PenumbralEnd": 2461103.101283176, + "2026-03-03 total/PenumbralStart": 2461102.8638709616, + "2026-03-03 total/TotalEnd": 2461103.0031395727, + "2026-03-03 total/TotalStart": 2461102.9619226186 +} diff --git a/basic/testdata/semianalytic/mars_event_baseline.json b/basic/testdata/semianalytic/mars_event_baseline.json new file mode 100644 index 0000000..b28258a --- /dev/null +++ b/basic/testdata/semianalytic/mars_event_baseline.json @@ -0,0 +1,1732 @@ +{ + "samples": [ + { + "input_utc": "1992-08-17T09:21:45.678Z", + "tt_jd_bits": 4702513280739287797, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702512146017106614, + "LastMarsOpposition": 4702511930972117016, + "LastMarsProgradeToRetrograde": 4702511848567019430, + "LastMarsRetrogradeToPrograde": 4702512005746468175, + "LastMarsWesternQuadrature": 4702511701091983777, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702513793695807840, + "NextMarsOpposition": 4702513589022112418, + "NextMarsProgradeToRetrograde": 4702513504713701435, + "NextMarsRetrogradeToPrograde": 4702513671731043679, + "NextMarsWesternQuadrature": 4702513385284735539 + } + }, + { + "input_utc": "1993-03-16T13:30:58.678Z", + "tt_jd_bits": 4702513734229996356, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702512146017106614, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702513793695807840, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1993-10-13T17:40:11.678Z", + "tt_jd_bits": 4702514187720729769, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1994-05-12T21:49:24.678Z", + "tt_jd_bits": 4702514641211438328, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1994-12-10T01:58:37.678Z", + "tt_jd_bits": 4702515094702171743, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1995-07-09T06:07:50.678Z", + "tt_jd_bits": 4702515548192880302, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1996-02-05T10:17:03.678Z", + "tt_jd_bits": 4702516001683613716, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1996-09-02T10:26:16.678Z", + "tt_jd_bits": 4702516452668924686, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1997-04-01T14:35:29.678Z", + "tt_jd_bits": 4702516906159633244, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1997-10-29T18:44:42.678Z", + "tt_jd_bits": 4702517359650366658, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1998-05-28T22:53:55.678Z", + "tt_jd_bits": 4702517813141075217, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1998-12-26T01:24:08.678Z", + "tt_jd_bits": 4702518266484144275, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1999-07-25T05:33:21.678Z", + "tt_jd_bits": 4702518719974877689, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2000-02-21T09:39:45.678Z", + "tt_jd_bits": 4702519173461385730, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2000-09-18T09:48:58.678Z", + "tt_jd_bits": 4702519624446696700, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2001-04-17T13:58:11.678Z", + "tt_jd_bits": 4702520077937405259, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2001-11-14T18:07:24.678Z", + "tt_jd_bits": 4702520531428113818, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2002-06-13T22:16:37.678Z", + "tt_jd_bits": 4702520984918822377, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2003-01-11T02:25:50.678Z", + "tt_jd_bits": 4702521438409530935, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2003-08-10T06:35:03.678Z", + "tt_jd_bits": 4702521891900239494, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2004-03-08T10:44:16.678Z", + "tt_jd_bits": 4702522345390948053, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2004-10-04T10:53:29.678Z", + "tt_jd_bits": 4702522796376259023, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2005-05-03T13:23:42.678Z", + "tt_jd_bits": 4702523249719328081, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2005-11-30T17:32:55.678Z", + "tt_jd_bits": 4702523703210036640, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2006-06-29T21:42:08.678Z", + "tt_jd_bits": 4702524156700770054, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2007-01-27T01:51:21.678Z", + "tt_jd_bits": 4702524610191478612, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2007-08-26T05:57:45.678Z", + "tt_jd_bits": 4702525063677986654, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951971, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2008-03-24T10:06:58.678Z", + "tt_jd_bits": 4702525517168695212, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2008-10-20T10:16:11.678Z", + "tt_jd_bits": 4702525968154006182, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2009-05-19T14:25:24.678Z", + "tt_jd_bits": 4702526421644739596, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2009-12-16T18:34:37.678Z", + "tt_jd_bits": 4702526875135448155, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2010-07-15T22:43:50.678Z", + "tt_jd_bits": 4702527328626156714, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2011-02-12T02:53:03.678Z", + "tt_jd_bits": 4702527782116865273, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2011-09-11T05:23:16.678Z", + "tt_jd_bits": 4702528235459934331, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2012-04-09T09:32:29.678Z", + "tt_jd_bits": 4702528688950642890, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2012-11-05T09:41:42.678Z", + "tt_jd_bits": 4702529139935978714, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702528817168167686, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702528702087700033, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702530473238594305, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702530346783957818, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2013-06-04T13:50:55.678Z", + "tt_jd_bits": 4702529593426687273, + "events": { + "LastMarsConjunction": 4702529491283802892, + "LastMarsEasternQuadrature": 4702528817168167686, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702528702087700033, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702531182749856804, + "NextMarsEasternQuadrature": 4702530473238594305, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702530346783957818, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2014-01-01T18:00:08.678Z", + "tt_jd_bits": 4702530046917395832, + "events": { + "LastMarsConjunction": 4702529491283802892, + "LastMarsEasternQuadrature": 4702528817168167686, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702528702087700033, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702531182749856804, + "NextMarsEasternQuadrature": 4702530473238594305, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702530346783957818, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2014-07-31T22:09:21.678Z", + "tt_jd_bits": 4702530500408104391, + "events": { + "LastMarsConjunction": 4702529491283802892, + "LastMarsEasternQuadrature": 4702530473238594305, + "LastMarsOpposition": 4702530255495088888, + "LastMarsProgradeToRetrograde": 4702530173883974649, + "LastMarsRetrogradeToPrograde": 4702530346783957818, + "LastMarsWesternQuadrature": 4702530049620877746, + "NextMarsConjunction": 4702531182749856804, + "NextMarsEasternQuadrature": 4702532164570884470, + "NextMarsOpposition": 4702531918920045550, + "NextMarsProgradeToRetrograde": 4702531842933170053, + "NextMarsRetrogradeToPrograde": 4702532002409923296, + "NextMarsWesternQuadrature": 4702531693510212533 + } + }, + { + "input_utc": "2015-02-28T02:15:45.678Z", + "tt_jd_bits": 4702530953894612432, + "events": { + "LastMarsConjunction": 4702529491283802892, + "LastMarsEasternQuadrature": 4702530473238594305, + "LastMarsOpposition": 4702530255495088888, + "LastMarsProgradeToRetrograde": 4702530173883974649, + "LastMarsRetrogradeToPrograde": 4702530346783957818, + "LastMarsWesternQuadrature": 4702530049620877746, + "NextMarsConjunction": 4702531182749856804, + "NextMarsEasternQuadrature": 4702532164570884470, + "NextMarsOpposition": 4702531918920045550, + "NextMarsProgradeToRetrograde": 4702531842933170053, + "NextMarsRetrogradeToPrograde": 4702532002409923296, + "NextMarsWesternQuadrature": 4702531693510212533 + } + }, + { + "input_utc": "2015-09-27T06:24:58.678Z", + "tt_jd_bits": 4702531407385345847, + "events": { + "LastMarsConjunction": 4702531182749856804, + "LastMarsEasternQuadrature": 4702530473238594305, + "LastMarsOpposition": 4702530255495088888, + "LastMarsProgradeToRetrograde": 4702530173883974649, + "LastMarsRetrogradeToPrograde": 4702530346783957818, + "LastMarsWesternQuadrature": 4702530049620877746, + "NextMarsConjunction": 4702532843560971546, + "NextMarsEasternQuadrature": 4702532164570884470, + "NextMarsOpposition": 4702531918920045550, + "NextMarsProgradeToRetrograde": 4702531842933170053, + "NextMarsRetrogradeToPrograde": 4702532002409923296, + "NextMarsWesternQuadrature": 4702531693510212533 + } + }, + { + "input_utc": "2016-04-25T10:34:11.678Z", + "tt_jd_bits": 4702531860876054406, + "events": { + "LastMarsConjunction": 4702531182749856804, + "LastMarsEasternQuadrature": 4702530473238594305, + "LastMarsOpposition": 4702530255495088888, + "LastMarsProgradeToRetrograde": 4702531842933170053, + "LastMarsRetrogradeToPrograde": 4702530346783957818, + "LastMarsWesternQuadrature": 4702531693510212533, + "NextMarsConjunction": 4702532843560971546, + "NextMarsEasternQuadrature": 4702532164570884470, + "NextMarsOpposition": 4702531918920045550, + "NextMarsProgradeToRetrograde": 4702533566263329544, + "NextMarsRetrogradeToPrograde": 4702532002409923296, + "NextMarsWesternQuadrature": 4702533360315536487 + } + }, + { + "input_utc": "2016-11-21T10:43:24.678Z", + "tt_jd_bits": 4702532311861365375, + "events": { + "LastMarsConjunction": 4702531182749856804, + "LastMarsEasternQuadrature": 4702532164570884470, + "LastMarsOpposition": 4702531918920045550, + "LastMarsProgradeToRetrograde": 4702531842933170053, + "LastMarsRetrogradeToPrograde": 4702532002409923296, + "LastMarsWesternQuadrature": 4702531693510212533, + "NextMarsConjunction": 4702532843560971546, + "NextMarsEasternQuadrature": 4702533904384138651, + "NextMarsOpposition": 4702533627774877114, + "NextMarsProgradeToRetrograde": 4702533566263329544, + "NextMarsRetrogradeToPrograde": 4702533696942122103, + "NextMarsWesternQuadrature": 4702533360315536487 + } + }, + { + "input_utc": "2017-06-20T14:52:37.678Z", + "tt_jd_bits": 4702532765352098789, + "events": { + "LastMarsConjunction": 4702531182749856804, + "LastMarsEasternQuadrature": 4702532164570884470, + "LastMarsOpposition": 4702531918920045550, + "LastMarsProgradeToRetrograde": 4702531842933170053, + "LastMarsRetrogradeToPrograde": 4702532002409923296, + "LastMarsWesternQuadrature": 4702531693510212533, + "NextMarsConjunction": 4702532843560971546, + "NextMarsEasternQuadrature": 4702533904384138651, + "NextMarsOpposition": 4702533627774877114, + "NextMarsProgradeToRetrograde": 4702533566263329544, + "NextMarsRetrogradeToPrograde": 4702533696942122103, + "NextMarsWesternQuadrature": 4702533360315536487 + } + }, + { + "input_utc": "2018-01-17T17:22:50.678Z", + "tt_jd_bits": 4702533218695167847, + "events": { + "LastMarsConjunction": 4702532843560971546, + "LastMarsEasternQuadrature": 4702532164570884470, + "LastMarsOpposition": 4702531918920045550, + "LastMarsProgradeToRetrograde": 4702531842933170053, + "LastMarsRetrogradeToPrograde": 4702532002409923296, + "LastMarsWesternQuadrature": 4702531693510212533, + "NextMarsConjunction": 4702534491554129973, + "NextMarsEasternQuadrature": 4702533904384138651, + "NextMarsOpposition": 4702533627774877114, + "NextMarsProgradeToRetrograde": 4702533566263329544, + "NextMarsRetrogradeToPrograde": 4702533696942122103, + "NextMarsWesternQuadrature": 4702533360315536487 + } + }, + { + "input_utc": "2018-08-16T21:32:03.678Z", + "tt_jd_bits": 4702533672185876406, + "events": { + "LastMarsConjunction": 4702532843560971546, + "LastMarsEasternQuadrature": 4702532164570884470, + "LastMarsOpposition": 4702533627774877114, + "LastMarsProgradeToRetrograde": 4702533566263329544, + "LastMarsRetrogradeToPrograde": 4702532002409923296, + "LastMarsWesternQuadrature": 4702533360315536487, + "NextMarsConjunction": 4702534491554129973, + "NextMarsEasternQuadrature": 4702533904384138651, + "NextMarsOpposition": 4702535366718512354, + "NextMarsProgradeToRetrograde": 4702535293199235303, + "NextMarsRetrogradeToPrograde": 4702533696942122103, + "NextMarsWesternQuadrature": 4702535089312876648 + } + }, + { + "input_utc": "2019-03-16T01:41:16.678Z", + "tt_jd_bits": 4702534125676584965, + "events": { + "LastMarsConjunction": 4702532843560971546, + "LastMarsEasternQuadrature": 4702533904384138651, + "LastMarsOpposition": 4702533627774877114, + "LastMarsProgradeToRetrograde": 4702533566263329544, + "LastMarsRetrogradeToPrograde": 4702533696942122103, + "LastMarsWesternQuadrature": 4702533360315536487, + "NextMarsConjunction": 4702534491554129973, + "NextMarsEasternQuadrature": 4702535603937534015, + "NextMarsOpposition": 4702535366718512354, + "NextMarsProgradeToRetrograde": 4702535293199235303, + "NextMarsRetrogradeToPrograde": 4702535437223704075, + "NextMarsWesternQuadrature": 4702535089312876648 + } + }, + { + "input_utc": "2019-10-13T05:50:29.678Z", + "tt_jd_bits": 4702534579167293524, + "events": { + "LastMarsConjunction": 4702534491554129973, + "LastMarsEasternQuadrature": 4702533904384138651, + "LastMarsOpposition": 4702533627774877114, + "LastMarsProgradeToRetrograde": 4702533566263329544, + "LastMarsRetrogradeToPrograde": 4702533696942122103, + "LastMarsWesternQuadrature": 4702533360315536487, + "NextMarsConjunction": 4702536138075438739, + "NextMarsEasternQuadrature": 4702535603937534015, + "NextMarsOpposition": 4702535366718512354, + "NextMarsProgradeToRetrograde": 4702535293199235303, + "NextMarsRetrogradeToPrograde": 4702535437223704075, + "NextMarsWesternQuadrature": 4702535089312876648 + } + }, + { + "input_utc": "1992-10-29T09:21:45.678Z", + "tt_jd_bits": 4702513437505594101, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702512146017106614, + "LastMarsOpposition": 4702511930972117016, + "LastMarsProgradeToRetrograde": 4702511848567019430, + "LastMarsRetrogradeToPrograde": 4702512005746468175, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702513793695807840, + "NextMarsOpposition": 4702513589022112418, + "NextMarsProgradeToRetrograde": 4702513504713701435, + "NextMarsRetrogradeToPrograde": 4702513671731043679, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1993-04-04T17:28:02.678Z", + "tt_jd_bits": 4702513775385725105, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702512146017106614, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702513793695807840, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1993-09-09T01:34:19.678Z", + "tt_jd_bits": 4702514113265880965, + "events": { + "LastMarsConjunction": 4702512672991051927, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702514347420544798, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1994-02-13T09:40:36.678Z", + "tt_jd_bits": 4702514451146011969, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1994-07-20T17:46:53.678Z", + "tt_jd_bits": 4702514789026167830, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702513385284735539, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702515034746704416 + } + }, + { + "input_utc": "1994-12-23T09:53:10.678Z", + "tt_jd_bits": 4702515123327159421, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702513793695807840, + "LastMarsOpposition": 4702513589022112418, + "LastMarsProgradeToRetrograde": 4702513504713701435, + "LastMarsRetrogradeToPrograde": 4702513671731043679, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702515436148801525, + "NextMarsOpposition": 4702515232188449372, + "NextMarsProgradeToRetrograde": 4702515148308262688, + "NextMarsRetrogradeToPrograde": 4702515321508626925, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1995-05-29T17:59:27.678Z", + "tt_jd_bits": 4702515461207290425, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1995-11-03T02:05:44.678Z", + "tt_jd_bits": 4702515799087421430, + "events": { + "LastMarsConjunction": 4702514347420544798, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702516062146379011, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1996-04-08T10:12:01.678Z", + "tt_jd_bits": 4702516136967577289, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1996-09-12T18:18:18.678Z", + "tt_jd_bits": 4702516474847708294, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702515148308262688, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702515034746704416, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702516790519650071, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702516673165474998 + } + }, + { + "input_utc": "1997-02-15T10:24:35.678Z", + "tt_jd_bits": 4702516809148699885, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702515436148801525, + "LastMarsOpposition": 4702515232188449372, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702515321508626925, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702517083255927083, + "NextMarsOpposition": 4702516873348013000, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702516965530015812, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1997-07-22T18:30:52.678Z", + "tt_jd_bits": 4702517147028855744, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1997-12-27T02:37:09.678Z", + "tt_jd_bits": 4702517484908986749, + "events": { + "LastMarsConjunction": 4702516062146379011, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702517778499968266, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1998-06-02T09:25:26.678Z", + "tt_jd_bits": 4702517822672795723, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1998-11-06T17:31:43.678Z", + "tt_jd_bits": 4702518160552926727, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702516790519650071, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702516673165474998, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702518443319578133, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702518311829521000 + } + }, + { + "input_utc": "1999-04-11T09:38:00.678Z", + "tt_jd_bits": 4702518494853943173, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702517083255927083, + "LastMarsOpposition": 4702516873348013000, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702516965530015812, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702518749266968259, + "NextMarsOpposition": 4702518523484655757, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702518612758768857, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "1999-09-15T17:44:17.678Z", + "tt_jd_bits": 4702518832734074178, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2000-02-20T01:45:45.678Z", + "tt_jd_bits": 4702519170607022048, + "events": { + "LastMarsConjunction": 4702517778499968266, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702519455332245554, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2000-07-26T09:52:02.678Z", + "tt_jd_bits": 4702519508487153053, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2000-12-30T17:58:19.678Z", + "tt_jd_bits": 4702519846367284057, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702518443319578133, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702518311829521000, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702520129599628872, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702519961901919610 + } + }, + { + "input_utc": "2001-06-04T10:04:36.678Z", + "tt_jd_bits": 4702520180668275648, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702518749266968259, + "LastMarsOpposition": 4702518523484655757, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702518612758768857, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702520463915073325, + "NextMarsOpposition": 4702520200681880312, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702520278429752669, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2001-11-08T18:10:53.678Z", + "tt_jd_bits": 4702520518548406653, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2002-04-15T02:17:10.678Z", + "tt_jd_bits": 4702520856428537657, + "events": { + "LastMarsConjunction": 4702519455332245554, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702521109471670542, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2002-09-19T10:23:27.678Z", + "tt_jd_bits": 4702521194308668662, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2003-02-23T18:29:44.678Z", + "tt_jd_bits": 4702521532188799666, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702519961901919610, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702521646667683544 + } + }, + { + "input_utc": "2003-07-29T10:36:01.678Z", + "tt_jd_bits": 4702521866489791257, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702520463915073325, + "LastMarsOpposition": 4702520200681880312, + "LastMarsProgradeToRetrograde": 4702520129599628872, + "LastMarsRetrogradeToPrograde": 4702520278429752669, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702522196782356836, + "NextMarsOpposition": 4702521931573037088, + "NextMarsProgradeToRetrograde": 4702521869637227623, + "NextMarsRetrogradeToPrograde": 4702521999942709623, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2004-01-02T17:24:18.678Z", + "tt_jd_bits": 4702522204253600231, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2004-06-08T01:30:35.678Z", + "tt_jd_bits": 4702522542133731235, + "events": { + "LastMarsConjunction": 4702521109471670542, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702522755753470403, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2004-11-12T09:36:52.678Z", + "tt_jd_bits": 4702522880013862240, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2005-04-18T17:43:09.678Z", + "tt_jd_bits": 4702523217893993244, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702521646667683544, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702523400900646508 + } + }, + { + "input_utc": "2005-09-21T09:49:26.678Z", + "tt_jd_bits": 4702523552194984836, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702522196782356836, + "LastMarsOpposition": 4702521931573037088, + "LastMarsProgradeToRetrograde": 4702521869637227623, + "LastMarsRetrogradeToPrograde": 4702521999942709623, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702523876976101971, + "NextMarsOpposition": 4702523652958006281, + "NextMarsProgradeToRetrograde": 4702523573662358264, + "NextMarsRetrogradeToPrograde": 4702523725150993248, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2006-02-25T17:55:43.678Z", + "tt_jd_bits": 4702523890075140695, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2006-08-02T02:02:00.678Z", + "tt_jd_bits": 4702524227955271700, + "events": { + "LastMarsConjunction": 4702522755753470403, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702524404470744277, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951971, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2007-01-06T10:08:17.678Z", + "tt_jd_bits": 4702524565835402704, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2007-06-12T18:09:45.678Z", + "tt_jd_bits": 4702524903708350575, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702523400900646508, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702525112250446069 + } + }, + { + "input_utc": "2007-11-15T10:16:02.678Z", + "tt_jd_bits": 4702525238009342166, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702523876976101971, + "LastMarsOpposition": 4702523652958006281, + "LastMarsProgradeToRetrograde": 4702523573662358264, + "LastMarsRetrogradeToPrograde": 4702523725150993248, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702525529803751600, + "NextMarsOpposition": 4702525322610418841, + "NextMarsProgradeToRetrograde": 4702525238535367700, + "NextMarsRetrogradeToPrograde": 4702525402190951972, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2008-04-20T18:22:19.678Z", + "tt_jd_bits": 4702525575889473170, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2008-09-25T02:28:36.678Z", + "tt_jd_bits": 4702525913769604175, + "events": { + "LastMarsConjunction": 4702524404470744277, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702526067991700689, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2009-03-01T10:34:53.678Z", + "tt_jd_bits": 4702526251649760034, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2009-08-05T17:23:10.678Z", + "tt_jd_bits": 4702526589413569008, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702525238535367700, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702525112250446069, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702526885607937654, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702526771102266031 + } + }, + { + "input_utc": "2010-01-08T09:29:27.678Z", + "tt_jd_bits": 4702526923714560599, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702525529803751600, + "LastMarsOpposition": 4702525322610418841, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702525402190951972, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702527173148402421, + "NextMarsOpposition": 4702526969725028939, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702527056790729770, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2010-06-14T17:35:44.678Z", + "tt_jd_bits": 4702527261594691603, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2010-11-19T01:42:01.678Z", + "tt_jd_bits": 4702527599474822608, + "events": { + "LastMarsConjunction": 4702526067991700689, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702527766168569310, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2011-04-25T09:48:18.678Z", + "tt_jd_bits": 4702527937354953612, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700040, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2011-09-29T17:54:35.678Z", + "tt_jd_bits": 4702528275235084617, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702526885607937654, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702526771102266031, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702528527088139234, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702528412244915264 + } + }, + { + "input_utc": "2012-03-03T10:00:52.678Z", + "tt_jd_bits": 4702528609536076208, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702527173148402421, + "LastMarsOpposition": 4702526969725028939, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702527056790729770, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702528817168167686, + "NextMarsOpposition": 4702528610443332879, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702528702087700033, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2012-08-07T18:07:09.678Z", + "tt_jd_bits": 4702528947416232068, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702528817168167686, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702528702087700033, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702530473238594305, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702530346783957818, + "NextMarsWesternQuadrature": 4702530049620877746 + } + }, + { + "input_utc": "2013-01-12T02:13:26.678Z", + "tt_jd_bits": 4702529285296363072, + "events": { + "LastMarsConjunction": 4702527766168569310, + "LastMarsEasternQuadrature": 4702528817168167686, + "LastMarsOpposition": 4702528610443332879, + "LastMarsProgradeToRetrograde": 4702528527088139234, + "LastMarsRetrogradeToPrograde": 4702528702087700033, + "LastMarsWesternQuadrature": 4702528412244915264, + "NextMarsConjunction": 4702529491283802892, + "NextMarsEasternQuadrature": 4702530473238594305, + "NextMarsOpposition": 4702530255495088888, + "NextMarsProgradeToRetrograde": 4702530173883974649, + "NextMarsRetrogradeToPrograde": 4702530346783957818, + "NextMarsWesternQuadrature": 4702530049620877746 + } + } + ] +} diff --git a/basic/testdata/semianalytic/mercury_event_baseline.json b/basic/testdata/semianalytic/mercury_event_baseline.json new file mode 100644 index 0000000..da4d1c8 --- /dev/null +++ b/basic/testdata/semianalytic/mercury_event_baseline.json @@ -0,0 +1,2308 @@ +{ + "samples": [ + { + "input_utc": "1995-01-15T12:34:56.789Z", + "tt_jd_bits": 4702515172960530022, + "events": { + "LastMercuryConjunction": 4702515103405453253, + "LastMercuryGreatestElongation": 4702515021517794732, + "LastMercuryGreatestElongationEast": 4702514934912611226, + "LastMercuryGreatestElongationWest": 4702515021517794732, + "LastMercuryInferiorConjunction": 4702514987622229028, + "LastMercuryProgradeToRetrograde": 4702514962196530577, + "LastMercuryRetrograde": 4702515005854790928, + "LastMercuryRetrogradeToPrograde": 4702515005854790928, + "LastMercurySuperiorConjunction": 4702515103405453253, + "NextMercuryConjunction": 4702515214693455615, + "NextMercuryGreatestElongation": 4702515181210033236, + "NextMercuryGreatestElongationEast": 4702515181210033236, + "NextMercuryGreatestElongationWest": 4702515269464472674, + "NextMercuryInferiorConjunction": 4702515214693455615, + "NextMercuryProgradeToRetrograde": 4702515194423148411, + "NextMercuryRetrograde": 4702515194423148411, + "NextMercuryRetrogradeToPrograde": 4702515240096207209, + "NextMercurySuperiorConjunction": 4702515364105430851 + } + }, + { + "input_utc": "1995-06-01T15:42:07.789Z", + "tt_jd_bits": 4702515467444937817, + "events": { + "LastMercuryConjunction": 4702515364105430840, + "LastMercuryGreatestElongation": 4702515423269902574, + "LastMercuryGreatestElongationEast": 4702515423269902574, + "LastMercuryGreatestElongationWest": 4702515269464472662, + "LastMercuryInferiorConjunction": 4702515214693455639, + "LastMercuryProgradeToRetrograde": 4702515450268316245, + "LastMercuryRetrograde": 4702515450268316245, + "LastMercuryRetrogradeToPrograde": 4702515240096207209, + "LastMercurySuperiorConjunction": 4702515364105430840, + "NextMercuryConjunction": 4702515475132306815, + "NextMercuryGreatestElongation": 4702515527588870252, + "NextMercuryGreatestElongationEast": 4702515681197673902, + "NextMercuryGreatestElongationWest": 4702515527588870252, + "NextMercuryInferiorConjunction": 4702515475132306815, + "NextMercuryProgradeToRetrograde": 4702515709270598966, + "NextMercuryRetrograde": 4702515500954237132, + "NextMercuryRetrogradeToPrograde": 4702515500954237132, + "NextMercurySuperiorConjunction": 4702515588636630993 + } + }, + { + "input_utc": "1995-10-16T18:49:18.789Z", + "tt_jd_bits": 4702515761929345612, + "events": { + "LastMercuryConjunction": 4702515736735711275, + "LastMercuryGreatestElongation": 4702515681197673990, + "LastMercuryGreatestElongationEast": 4702515681197673990, + "LastMercuryGreatestElongationWest": 4702515527588870218, + "LastMercuryInferiorConjunction": 4702515736735711275, + "LastMercuryProgradeToRetrograde": 4702515709270598966, + "LastMercuryRetrograde": 4702515754612402123, + "LastMercuryRetrogradeToPrograde": 4702515754612402123, + "LastMercurySuperiorConjunction": 4702515588636631050, + "NextMercuryConjunction": 4702515842333660753, + "NextMercuryGreatestElongation": 4702515770061198786, + "NextMercuryGreatestElongationEast": 4702515929246795582, + "NextMercuryGreatestElongationWest": 4702515770061198786, + "NextMercuryInferiorConjunction": 4702515964045090023, + "NextMercuryProgradeToRetrograde": 4702515943890285850, + "NextMercuryRetrograde": 4702515943890285850, + "NextMercuryRetrogradeToPrograde": 4702515988415305582, + "NextMercurySuperiorConjunction": 4702515842333660753 + } + }, + { + "input_utc": "1996-03-01T21:56:29.789Z", + "tt_jd_bits": 4702516056413778263, + "events": { + "LastMercuryConjunction": 4702515964045090079, + "LastMercuryGreatestElongation": 4702516015471090596, + "LastMercuryGreatestElongationEast": 4702515929246795510, + "LastMercuryGreatestElongationWest": 4702516015471090596, + "LastMercuryInferiorConjunction": 4702515964045090079, + "LastMercuryProgradeToRetrograde": 4702515943890285850, + "LastMercuryRetrograde": 4702515988415305582, + "LastMercuryRetrogradeToPrograde": 4702515988415305582, + "LastMercurySuperiorConjunction": 4702515842333660798, + "NextMercuryConjunction": 4702516113112506778, + "NextMercuryGreatestElongation": 4702516168964293794, + "NextMercuryGreatestElongationEast": 4702516168964293794, + "NextMercuryGreatestElongationWest": 4702516272138534776, + "NextMercuryInferiorConjunction": 4702516215618182008, + "NextMercuryProgradeToRetrograde": 4702516192756674343, + "NextMercuryRetrograde": 4702516192756674343, + "NextMercuryRetrogradeToPrograde": 4702516241948935106, + "NextMercurySuperiorConjunction": 4702516113112506778 + } + }, + { + "input_utc": "1996-07-17T01:03:40.789Z", + "tt_jd_bits": 4702516350898186058, + "events": { + "LastMercuryConjunction": 4702516338722821522, + "LastMercuryGreatestElongation": 4702516272138534784, + "LastMercuryGreatestElongationEast": 4702516168964293858, + "LastMercuryGreatestElongationWest": 4702516272138534784, + "LastMercuryInferiorConjunction": 4702516215618181978, + "LastMercuryProgradeToRetrograde": 4702516192756674343, + "LastMercuryRetrograde": 4702516241948935106, + "LastMercuryRetrogradeToPrograde": 4702516241948935106, + "LastMercurySuperiorConjunction": 4702516338722821522, + "NextMercuryConjunction": 4702516485116611931, + "NextMercuryGreatestElongation": 4702516427408912980, + "NextMercuryGreatestElongationEast": 4702516427408912980, + "NextMercuryGreatestElongationWest": 4702516518824249742, + "NextMercuryInferiorConjunction": 4702516485116611931, + "NextMercuryProgradeToRetrograde": 4702516455638852666, + "NextMercuryRetrograde": 4702516455638852666, + "NextMercuryRetrogradeToPrograde": 4702516503104069980, + "NextMercurySuperiorConjunction": 4702516582722765862 + } + }, + { + "input_utc": "1996-12-01T04:10:51.789Z", + "tt_jd_bits": 4702516645382593853, + "events": { + "LastMercuryConjunction": 4702516582722765957, + "LastMercuryGreatestElongation": 4702516518824249708, + "LastMercuryGreatestElongationEast": 4702516427408912986, + "LastMercuryGreatestElongationWest": 4702516518824249708, + "LastMercuryInferiorConjunction": 4702516485116611789, + "LastMercuryProgradeToRetrograde": 4702516455638852666, + "LastMercuryRetrograde": 4702516503104069980, + "LastMercuryRetrogradeToPrograde": 4702516503104069980, + "LastMercurySuperiorConjunction": 4702516582722765957, + "NextMercuryConjunction": 4702516713848025220, + "NextMercuryGreatestElongation": 4702516676832042980, + "NextMercuryGreatestElongationEast": 4702516676832042980, + "NextMercuryGreatestElongationWest": 4702516761408453046, + "NextMercuryInferiorConjunction": 4702516713848025220, + "NextMercuryProgradeToRetrograde": 4702516693552924059, + "NextMercuryRetrograde": 4702516693552924059, + "NextMercuryRetrogradeToPrograde": 4702516737013192068, + "NextMercurySuperiorConjunction": 4702516861148405908 + } + }, + { + "input_utc": "1997-04-17T07:18:02.789Z", + "tt_jd_bits": 4702516939867001648, + "events": { + "LastMercuryConjunction": 4702516861148405880, + "LastMercuryGreatestElongation": 4702516915691034456, + "LastMercuryGreatestElongationEast": 4702516915691034456, + "LastMercuryGreatestElongationWest": 4702516761408453146, + "LastMercuryInferiorConjunction": 4702516713848025152, + "LastMercuryProgradeToRetrograde": 4702516935346191870, + "LastMercuryRetrograde": 4702516935346191870, + "LastMercuryRetrogradeToPrograde": 4702516737013192068, + "LastMercurySuperiorConjunction": 4702516861148405880, + "NextMercuryConjunction": 4702516957334635778, + "NextMercuryGreatestElongation": 4702517016458023750, + "NextMercuryGreatestElongationEast": 4702517173332815718, + "NextMercuryGreatestElongationWest": 4702517016458023750, + "NextMercuryInferiorConjunction": 4702516957334635778, + "NextMercuryProgradeToRetrograde": 4702517201445547580, + "NextMercuryRetrograde": 4702516983785722589, + "NextMercuryRetrogradeToPrograde": 4702516983785722589, + "NextMercurySuperiorConjunction": 4702517089109299154 + } + }, + { + "input_utc": "1997-08-31T13:25:13.789Z", + "tt_jd_bits": 4702517232472386106, + "events": { + "LastMercuryConjunction": 4702517089109299273, + "LastMercuryGreatestElongation": 4702517173332815716, + "LastMercuryGreatestElongationEast": 4702517173332815716, + "LastMercuryGreatestElongationWest": 4702517016458023742, + "LastMercuryInferiorConjunction": 4702516957334635921, + "LastMercuryProgradeToRetrograde": 4702517201445547580, + "LastMercuryRetrograde": 4702517201445547580, + "LastMercuryRetrogradeToPrograde": 4702516983785722589, + "LastMercurySuperiorConjunction": 4702517089109299273, + "NextMercuryConjunction": 4702517232496649900, + "NextMercuryGreatestElongation": 4702517267537726312, + "NextMercuryGreatestElongationEast": 4702517423902486606, + "NextMercuryGreatestElongationWest": 4702517267537726312, + "NextMercuryInferiorConjunction": 4702517232496649900, + "NextMercuryProgradeToRetrograde": 4702517443160895144, + "NextMercuryRetrograde": 4702517251119941877, + "NextMercuryRetrogradeToPrograde": 4702517251119941877, + "NextMercurySuperiorConjunction": 4702517325494165633 + } + }, + { + "input_utc": "1998-01-15T16:32:24.789Z", + "tt_jd_bits": 4702517526956793901, + "events": { + "LastMercuryConjunction": 4702517463904042078, + "LastMercuryGreatestElongation": 4702517507425936280, + "LastMercuryGreatestElongationEast": 4702517423902486594, + "LastMercuryGreatestElongationWest": 4702517507425936280, + "LastMercuryInferiorConjunction": 4702517463904042078, + "LastMercuryProgradeToRetrograde": 4702517443160895144, + "LastMercuryRetrograde": 4702517485795666430, + "LastMercuryRetrogradeToPrograde": 4702517485795666430, + "LastMercurySuperiorConjunction": 4702517325494165775, + "NextMercuryConjunction": 4702517607838368432, + "NextMercuryGreatestElongation": 4702517663264508198, + "NextMercuryGreatestElongationEast": 4702517663264508198, + "NextMercuryGreatestElongationWest": 4702517761050820392, + "NextMercuryInferiorConjunction": 4702517700904711919, + "NextMercuryProgradeToRetrograde": 4702517679268486973, + "NextMercuryRetrograde": 4702517679268486973, + "NextMercuryRetrogradeToPrograde": 4702517727533323045, + "NextMercurySuperiorConjunction": 4702517607838368432 + } + }, + { + "input_utc": "1998-06-01T19:39:35.789Z", + "tt_jd_bits": 4702517821441201696, + "events": { + "LastMercuryConjunction": 4702517700904711889, + "LastMercuryGreatestElongation": 4702517761050820446, + "LastMercuryGreatestElongationEast": 4702517663264508296, + "LastMercuryGreatestElongationWest": 4702517761050820446, + "LastMercuryInferiorConjunction": 4702517700904711889, + "LastMercuryProgradeToRetrograde": 4702517679268486973, + "LastMercuryRetrograde": 4702517727533323045, + "LastMercuryRetrogradeToPrograde": 4702517727533323045, + "LastMercurySuperiorConjunction": 4702517607838368386, + "NextMercuryConjunction": 4702517839629279461, + "NextMercuryGreatestElongation": 4702517918758347212, + "NextMercuryGreatestElongationEast": 4702517918758347212, + "NextMercuryGreatestElongationWest": 4702518015925175572, + "NextMercuryInferiorConjunction": 4702517978575629873, + "NextMercuryProgradeToRetrograde": 4702517946845516795, + "NextMercuryRetrograde": 4702517946845516795, + "NextMercuryRetrogradeToPrograde": 4702517998394273966, + "NextMercurySuperiorConjunction": 4702517839629279461 + } + }, + { + "input_utc": "1998-10-16T22:46:46.789Z", + "tt_jd_bits": 4702518115925609491, + "events": { + "LastMercuryConjunction": 4702518070563236932, + "LastMercuryGreatestElongation": 4702518015925175526, + "LastMercuryGreatestElongationEast": 4702517918758347278, + "LastMercuryGreatestElongationWest": 4702518015925175526, + "LastMercuryInferiorConjunction": 4702517978575629941, + "LastMercuryProgradeToRetrograde": 4702517946845516795, + "LastMercuryRetrograde": 4702517998394273966, + "LastMercuryRetrogradeToPrograde": 4702517998394273966, + "LastMercurySuperiorConjunction": 4702518070563236932, + "NextMercuryConjunction": 4702518214046535063, + "NextMercuryGreatestElongation": 4702518170532141338, + "NextMercuryGreatestElongationEast": 4702518170532141338, + "NextMercuryGreatestElongationWest": 4702518253821597554, + "NextMercuryInferiorConjunction": 4702518214046535063, + "NextMercuryProgradeToRetrograde": 4702518192496706607, + "NextMercuryRetrograde": 4702518192496706607, + "NextMercuryRetrogradeToPrograde": 4702518234697963901, + "NextMercurySuperiorConjunction": 4702518352733449492 + } + }, + { + "input_utc": "1999-03-03T00:36:57.789Z", + "tt_jd_bits": 4702518410295211419, + "events": { + "LastMercuryConjunction": 4702518352733449525, + "LastMercuryGreatestElongation": 4702518253821597586, + "LastMercuryGreatestElongationEast": 4702518170532141348, + "LastMercuryGreatestElongationWest": 4702518253821597586, + "LastMercuryInferiorConjunction": 4702518214046535014, + "LastMercuryProgradeToRetrograde": 4702518192496706607, + "LastMercuryRetrograde": 4702518234697963901, + "LastMercuryRetrogradeToPrograde": 4702518234697963901, + "LastMercurySuperiorConjunction": 4702518352733449525, + "NextMercuryConjunction": 4702518446318833325, + "NextMercuryGreatestElongation": 4702518411403125476, + "NextMercuryGreatestElongationEast": 4702518411403125476, + "NextMercuryGreatestElongationWest": 4702518506164242048, + "NextMercuryInferiorConjunction": 4702518446318833325, + "NextMercuryProgradeToRetrograde": 4702518425040582798, + "NextMercuryRetrograde": 4702518425040582798, + "NextMercuryRetrogradeToPrograde": 4702518473028257806, + "NextMercurySuperiorConjunction": 4702518590122894574 + } + }, + { + "input_utc": "1999-07-18T03:44:08.789Z", + "tt_jd_bits": 4702518704779619214, + "events": { + "LastMercuryConjunction": 4702518590122894632, + "LastMercuryGreatestElongation": 4702518663561353254, + "LastMercuryGreatestElongationEast": 4702518663561353254, + "LastMercuryGreatestElongationWest": 4702518506164242106, + "LastMercuryInferiorConjunction": 4702518446318833346, + "LastMercuryProgradeToRetrograde": 4702518691858419375, + "LastMercuryRetrograde": 4702518691858419375, + "LastMercuryRetrogradeToPrograde": 4702518473028257806, + "LastMercurySuperiorConjunction": 4702518590122894632, + "NextMercuryConjunction": 4702518723037141662, + "NextMercuryGreatestElongation": 4702518763692083092, + "NextMercuryGreatestElongationEast": 4702518916888231954, + "NextMercuryGreatestElongationWest": 4702518763692083092, + "NextMercuryInferiorConjunction": 4702518723037141662, + "NextMercuryProgradeToRetrograde": 4702518941359740699, + "NextMercuryRetrograde": 4702518744556183974, + "NextMercuryRetrogradeToPrograde": 4702518744556183974, + "NextMercurySuperiorConjunction": 4702518817451684848 + } + }, + { + "input_utc": "1999-12-02T06:48:56.789Z", + "tt_jd_bits": 4702518999260472725, + "events": { + "LastMercuryConjunction": 4702518964116923311, + "LastMercuryGreatestElongation": 4702518916888231938, + "LastMercuryGreatestElongationEast": 4702518916888231938, + "LastMercuryGreatestElongationWest": 4702518763692083120, + "LastMercuryInferiorConjunction": 4702518964116923311, + "LastMercuryProgradeToRetrograde": 4702518941359740699, + "LastMercuryRetrograde": 4702518983648921716, + "LastMercuryRetrogradeToPrograde": 4702518983648921716, + "LastMercurySuperiorConjunction": 4702518817451684937, + "NextMercuryConjunction": 4702519095403440176, + "NextMercuryGreatestElongation": 4702519000861134746, + "NextMercuryGreatestElongationEast": 4702519159822379194, + "NextMercuryGreatestElongationWest": 4702519000861134746, + "NextMercuryInferiorConjunction": 4702519193280296364, + "NextMercuryProgradeToRetrograde": 4702519172442321242, + "NextMercuryRetrograde": 4702519172442321242, + "NextMercuryRetrogradeToPrograde": 4702519219748409294, + "NextMercurySuperiorConjunction": 4702519095403440176 + } + }, + { + "input_utc": "2000-04-16T12:56:07.789Z", + "tt_jd_bits": 4702519291865832328, + "events": { + "LastMercuryConjunction": 4702519193280296482, + "LastMercuryGreatestElongation": 4702519251803219508, + "LastMercuryGreatestElongationEast": 4702519159822379184, + "LastMercuryGreatestElongationWest": 4702519251803219508, + "LastMercuryInferiorConjunction": 4702519193280296482, + "LastMercuryProgradeToRetrograde": 4702519172442321242, + "LastMercuryRetrograde": 4702519219748409294, + "LastMercuryRetrogradeToPrograde": 4702519219748409294, + "LastMercurySuperiorConjunction": 4702519095403440188, + "NextMercuryConjunction": 4702519340440977937, + "NextMercuryGreatestElongation": 4702519407882301248, + "NextMercuryGreatestElongationEast": 4702519407882301248, + "NextMercuryGreatestElongationWest": 4702519510579408246, + "NextMercuryInferiorConjunction": 4702519465689295655, + "NextMercuryProgradeToRetrograde": 4702519436384939955, + "NextMercuryRetrograde": 4702519436384939955, + "NextMercuryRetrogradeToPrograde": 4702519489175360650, + "NextMercurySuperiorConjunction": 4702519340440977937 + } + }, + { + "input_utc": "2000-08-31T16:03:18.789Z", + "tt_jd_bits": 4702519586350240123, + "events": { + "LastMercuryConjunction": 4702519565682295701, + "LastMercuryGreatestElongation": 4702519510579408154, + "LastMercuryGreatestElongationEast": 4702519407882301334, + "LastMercuryGreatestElongationWest": 4702519510579408154, + "LastMercuryInferiorConjunction": 4702519465689295791, + "LastMercuryProgradeToRetrograde": 4702519436384939955, + "LastMercuryRetrograde": 4702519489175360650, + "LastMercuryRetrogradeToPrograde": 4702519489175360650, + "LastMercurySuperiorConjunction": 4702519565682295701, + "NextMercuryConjunction": 4702519713953421984, + "NextMercuryGreatestElongation": 4702519663143563490, + "NextMercuryGreatestElongationEast": 4702519663143563490, + "NextMercuryGreatestElongationWest": 4702519748601407724, + "NextMercuryInferiorConjunction": 4702519713953421984, + "NextMercuryProgradeToRetrograde": 4702519689585860799, + "NextMercuryRetrograde": 4702519689585860799, + "NextMercuryRetrogradeToPrograde": 4702519732580126465, + "NextMercurySuperiorConjunction": 4702519835754627438 + } + }, + { + "input_utc": "2001-01-15T19:10:29.789Z", + "tt_jd_bits": 4702519880834647918, + "events": { + "LastMercuryConjunction": 4702519835754627336, + "LastMercuryGreatestElongation": 4702519748601407750, + "LastMercuryGreatestElongationEast": 4702519663143563394, + "LastMercuryGreatestElongationWest": 4702519748601407750, + "LastMercuryInferiorConjunction": 4702519713953421858, + "LastMercuryProgradeToRetrograde": 4702519689585860799, + "LastMercuryRetrograde": 4702519732580126465, + "LastMercuryRetrogradeToPrograde": 4702519732580126465, + "LastMercurySuperiorConjunction": 4702519835754627336, + "NextMercuryConjunction": 4702519941419676387, + "NextMercuryGreatestElongation": 4702519908254178592, + "NextMercuryGreatestElongationEast": 4702519908254178592, + "NextMercuryGreatestElongationWest": 4702519997780906794, + "NextMercuryInferiorConjunction": 4702519941419676387, + "NextMercuryProgradeToRetrograde": 4702519920986224858, + "NextMercuryRetrograde": 4702519920986224858, + "NextMercuryRetrogradeToPrograde": 4702519967286591545, + "NextMercurySuperiorConjunction": 4702520090412487082 + } + }, + { + "input_utc": "2001-06-01T22:17:40.789Z", + "tt_jd_bits": 4702520175319055713, + "events": { + "LastMercuryConjunction": 4702520090412487035, + "LastMercuryGreatestElongation": 4702520152254564662, + "LastMercuryGreatestElongationEast": 4702520152254564662, + "LastMercuryGreatestElongationWest": 4702519997780906874, + "LastMercuryInferiorConjunction": 4702519941419676309, + "LastMercuryProgradeToRetrograde": 4702519920986224858, + "LastMercuryRetrograde": 4702519967286591545, + "LastMercuryRetrogradeToPrograde": 4702519967286591545, + "LastMercurySuperiorConjunction": 4702520090412487035, + "NextMercuryConjunction": 4702520206736692082, + "NextMercuryGreatestElongation": 4702520256453370960, + "NextMercuryGreatestElongationEast": 4702520409403191654, + "NextMercuryGreatestElongationWest": 4702520256453370960, + "NextMercuryInferiorConjunction": 4702520206736692082, + "NextMercuryProgradeToRetrograde": 4702520180247003822, + "NextMercuryRetrograde": 4702520180247003822, + "NextMercuryRetrogradeToPrograde": 4702520231917627674, + "NextMercurySuperiorConjunction": 4702520314863960876 + } + }, + { + "input_utc": "2001-10-17T01:24:51.789Z", + "tt_jd_bits": 4702520469803463508, + "events": { + "LastMercuryConjunction": 4702520463385815278, + "LastMercuryGreatestElongation": 4702520409403191742, + "LastMercuryGreatestElongationEast": 4702520409403191742, + "LastMercuryGreatestElongationWest": 4702520256453370982, + "LastMercuryInferiorConjunction": 4702520463385815278, + "LastMercuryProgradeToRetrograde": 4702520437091209095, + "LastMercuryRetrograde": 4702520437091209095, + "LastMercuryRetrogradeToPrograde": 4702520231917627674, + "LastMercurySuperiorConjunction": 4702520314863960968, + "NextMercuryConjunction": 4702520574687095398, + "NextMercuryGreatestElongation": 4702520496921898306, + "NextMercuryGreatestElongationEast": 4702520656483145714, + "NextMercuryGreatestElongationWest": 4702520496921898306, + "NextMercuryInferiorConjunction": 4702520690411081481, + "NextMercuryProgradeToRetrograde": 4702520670223798773, + "NextMercuryRetrograde": 4702520481419358818, + "NextMercuryRetrogradeToPrograde": 4702520481419358818, + "NextMercurySuperiorConjunction": 4702520574687095398 + } + }, + { + "input_utc": "2002-03-03T04:32:02.789Z", + "tt_jd_bits": 4702520764287871303, + "events": { + "LastMercuryConjunction": 4702520690411081569, + "LastMercuryGreatestElongation": 4702520743848930260, + "LastMercuryGreatestElongationEast": 4702520656483145674, + "LastMercuryGreatestElongationWest": 4702520743848930260, + "LastMercuryInferiorConjunction": 4702520690411081569, + "LastMercuryProgradeToRetrograde": 4702520670223798773, + "LastMercuryRetrograde": 4702520715381633727, + "LastMercuryRetrogradeToPrograde": 4702520715381633727, + "LastMercurySuperiorConjunction": 4702520574687095547, + "NextMercuryConjunction": 4702520839839903290, + "NextMercuryGreatestElongation": 4702520897355592188, + "NextMercuryGreatestElongationEast": 4702520897355592188, + "NextMercuryGreatestElongationWest": 4702521001401327410, + "NextMercuryInferiorConjunction": 4702520947056957228, + "NextMercuryProgradeToRetrograde": 4702520923220166780, + "NextMercuryRetrograde": 4702520923220166780, + "NextMercuryRetrogradeToPrograde": 4702520973149683216, + "NextMercurySuperiorConjunction": 4702520839839903290 + } + }, + { + "input_utc": "2002-07-18T07:39:13.789Z", + "tt_jd_bits": 4702521058772279098, + "events": { + "LastMercuryConjunction": 4702520947056957268, + "LastMercuryGreatestElongation": 4702521001401327410, + "LastMercuryGreatestElongationEast": 4702520897355592228, + "LastMercuryGreatestElongationWest": 4702521001401327410, + "LastMercuryInferiorConjunction": 4702520947056957268, + "LastMercuryProgradeToRetrograde": 4702520923220166780, + "LastMercuryRetrograde": 4702520973149683216, + "LastMercuryRetrogradeToPrograde": 4702520973149683216, + "LastMercurySuperiorConjunction": 4702520839839903214, + "NextMercuryConjunction": 4702521064688048506, + "NextMercuryGreatestElongation": 4702521155652682328, + "NextMercuryGreatestElongationEast": 4702521155652682328, + "NextMercuryGreatestElongationWest": 4702521245595004408, + "NextMercuryInferiorConjunction": 4702521212214220604, + "NextMercuryProgradeToRetrograde": 4702521183874806444, + "NextMercuryRetrograde": 4702521183874806444, + "NextMercuryRetrogradeToPrograde": 4702521230067431482, + "NextMercurySuperiorConjunction": 4702521064688048506 + } + }, + { + "input_utc": "2002-12-01T13:46:24.789Z", + "tt_jd_bits": 4702521351377638701, + "events": { + "LastMercuryConjunction": 4702521314052929479, + "LastMercuryGreatestElongation": 4702521245595004428, + "LastMercuryGreatestElongationEast": 4702521155652682264, + "LastMercuryGreatestElongationWest": 4702521245595004428, + "LastMercuryInferiorConjunction": 4702521212214220641, + "LastMercuryProgradeToRetrograde": 4702521183874806444, + "LastMercuryRetrograde": 4702521230067431482, + "LastMercuryRetrogradeToPrograde": 4702521230067431482, + "LastMercurySuperiorConjunction": 4702521314052929479, + "NextMercuryConjunction": 4702521439983305760, + "NextMercuryGreatestElongation": 4702521404334668710, + "NextMercuryGreatestElongationEast": 4702521404334668710, + "NextMercuryGreatestElongationWest": 4702521489825413960, + "NextMercuryInferiorConjunction": 4702521439983305760, + "NextMercuryProgradeToRetrograde": 4702521419810272902, + "NextMercuryRetrograde": 4702521419810272902, + "NextMercuryRetrogradeToPrograde": 4702521463851076111, + "NextMercurySuperiorConjunction": 4702521588476144098 + } + }, + { + "input_utc": "2003-04-17T15:36:35.789Z", + "tt_jd_bits": 4702521645747215774, + "events": { + "LastMercuryConjunction": 4702521588476144056, + "LastMercuryGreatestElongation": 4702521643511939738, + "LastMercuryGreatestElongationEast": 4702521643511939738, + "LastMercuryGreatestElongationWest": 4702521489825413918, + "LastMercuryInferiorConjunction": 4702521439983305810, + "LastMercuryProgradeToRetrograde": 4702521419810272902, + "LastMercuryRetrograde": 4702521463851076111, + "LastMercuryRetrogradeToPrograde": 4702521463851076111, + "LastMercurySuperiorConjunction": 4702521588476144056, + "NextMercuryConjunction": 4702521687955545146, + "NextMercuryGreatestElongation": 4702521745789891698, + "NextMercuryGreatestElongationEast": 4702521901774283382, + "NextMercuryGreatestElongationWest": 4702521745789891698, + "NextMercuryInferiorConjunction": 4702521687955545146, + "NextMercuryProgradeToRetrograde": 4702521665590479634, + "NextMercuryRetrograde": 4702521665590479634, + "NextMercuryRetrogradeToPrograde": 4702521714351390434, + "NextMercurySuperiorConjunction": 4702521814925424894 + } + }, + { + "input_utc": "2003-09-01T18:43:46.789Z", + "tt_jd_bits": 4702521940231623569, + "events": { + "LastMercuryConjunction": 4702521814925424939, + "LastMercuryGreatestElongation": 4702521901774283404, + "LastMercuryGreatestElongationEast": 4702521901774283404, + "LastMercuryGreatestElongationWest": 4702521745789891742, + "LastMercuryInferiorConjunction": 4702521687955545157, + "LastMercuryProgradeToRetrograde": 4702521930002335803, + "LastMercuryRetrograde": 4702521930002335803, + "LastMercuryRetrogradeToPrograde": 4702521714351390434, + "LastMercurySuperiorConjunction": 4702521814925424939, + "NextMercuryConjunction": 4702521960203324435, + "NextMercuryGreatestElongation": 4702521994365018574, + "NextMercuryGreatestElongationEast": 4702522151708956862, + "NextMercuryGreatestElongationWest": 4702521994365018574, + "NextMercuryInferiorConjunction": 4702521960203324435, + "NextMercuryProgradeToRetrograde": 4702522169466554988, + "NextMercuryRetrograde": 4702521978382136928, + "NextMercuryRetrogradeToPrograde": 4702521978382136928, + "NextMercurySuperiorConjunction": 4702522055409762529 + } + }, + { + "input_utc": "2004-01-16T21:50:57.789Z", + "tt_jd_bits": 4702522234716031364, + "events": { + "LastMercuryConjunction": 4702522189915637790, + "LastMercuryGreatestElongation": 4702522151708956928, + "LastMercuryGreatestElongationEast": 4702522151708956928, + "LastMercuryGreatestElongationWest": 4702521994365018514, + "LastMercuryInferiorConjunction": 4702522189915637790, + "LastMercuryProgradeToRetrograde": 4702522169466554988, + "LastMercuryRetrograde": 4702522212551158753, + "LastMercuryRetrogradeToPrograde": 4702522212551158753, + "LastMercurySuperiorConjunction": 4702522055409762616, + "NextMercuryConjunction": 4702522335992120776, + "NextMercuryGreatestElongation": 4702522235743580448, + "NextMercuryGreatestElongationEast": 4702522390640058198, + "NextMercuryGreatestElongationWest": 4702522235743580448, + "NextMercuryInferiorConjunction": 4702522430424477366, + "NextMercuryProgradeToRetrograde": 4702522408586812276, + "NextMercuryRetrograde": 4702522408586812276, + "NextMercuryRetrogradeToPrograde": 4702522456977718422, + "NextMercurySuperiorConjunction": 4702522335992120776 + } + }, + { + "input_utc": "2004-06-02T00:58:08.789Z", + "tt_jd_bits": 4702522529200439159, + "events": { + "LastMercuryConjunction": 4702522430424477201, + "LastMercuryGreatestElongation": 4702522490153670360, + "LastMercuryGreatestElongationEast": 4702522390640058160, + "LastMercuryGreatestElongationWest": 4702522490153670360, + "LastMercuryInferiorConjunction": 4702522430424477201, + "LastMercuryProgradeToRetrograde": 4702522408586812276, + "LastMercuryRetrograde": 4702522456977718422, + "LastMercuryRetrogradeToPrograde": 4702522456977718422, + "LastMercurySuperiorConjunction": 4702522335992120619, + "NextMercuryConjunction": 4702522565386837428, + "NextMercuryGreatestElongation": 4702522647535765244, + "NextMercuryGreatestElongationEast": 4702522647535765244, + "NextMercuryGreatestElongationWest": 4702522742967635430, + "NextMercuryInferiorConjunction": 4702522707070714012, + "NextMercuryProgradeToRetrograde": 4702522675597456406, + "NextMercuryRetrograde": 4702522675597456406, + "NextMercuryRetrogradeToPrograde": 4702522726137505141, + "NextMercurySuperiorConjunction": 4702522565386837428 + } + }, + { + "input_utc": "2004-10-17T04:02:56.789Z", + "tt_jd_bits": 4702522823681292670, + "events": { + "LastMercuryConjunction": 4702522799201589272, + "LastMercuryGreatestElongation": 4702522742967635428, + "LastMercuryGreatestElongationEast": 4702522647535765210, + "LastMercuryGreatestElongationWest": 4702522742967635428, + "LastMercuryInferiorConjunction": 4702522707070713906, + "LastMercuryProgradeToRetrograde": 4702522675597456406, + "LastMercuryRetrograde": 4702522726137505141, + "LastMercuryRetrogradeToPrograde": 4702522726137505141, + "LastMercurySuperiorConjunction": 4702522799201589272, + "NextMercuryConjunction": 4702522940029241629, + "NextMercuryGreatestElongation": 4702522898592893664, + "NextMercuryGreatestElongationEast": 4702522898592893664, + "NextMercuryGreatestElongationWest": 4702522981882253766, + "NextMercuryInferiorConjunction": 4702522940029241629, + "NextMercuryProgradeToRetrograde": 4702522918977809667, + "NextMercuryRetrograde": 4702522918977809667, + "NextMercuryRetrogradeToPrograde": 4702522961395196926, + "NextMercurySuperiorConjunction": 4702523081984211833 + } + }, + { + "input_utc": "2005-03-03T07:10:07.789Z", + "tt_jd_bits": 4702523118165700465, + "events": { + "LastMercuryConjunction": 4702523081984211723, + "LastMercuryGreatestElongation": 4702522981882253784, + "LastMercuryGreatestElongationEast": 4702522898592893660, + "LastMercuryGreatestElongationWest": 4702522981882253784, + "LastMercuryInferiorConjunction": 4702522940029241509, + "LastMercuryProgradeToRetrograde": 4702522918977809667, + "LastMercuryRetrograde": 4702522961395196926, + "LastMercuryRetrogradeToPrograde": 4702522961395196926, + "LastMercurySuperiorConjunction": 4702523081984211723, + "NextMercuryConjunction": 4702523174805183932, + "NextMercuryGreatestElongation": 4702523138491350646, + "NextMercuryGreatestElongationEast": 4702523138491350646, + "NextMercuryGreatestElongationWest": 4702523234953642388, + "NextMercuryInferiorConjunction": 4702523174805183932, + "NextMercuryProgradeToRetrograde": 4702523153306081141, + "NextMercuryRetrograde": 4702523153306081141, + "NextMercuryRetrogradeToPrograde": 4702523201486035315, + "NextMercurySuperiorConjunction": 4702523315913890392 + } + }, + { + "input_utc": "2005-07-17T13:17:18.789Z", + "tt_jd_bits": 4702523410771060068, + "events": { + "LastMercuryConjunction": 4702523315913890302, + "LastMercuryGreatestElongation": 4702523392698136404, + "LastMercuryGreatestElongationEast": 4702523392698136404, + "LastMercuryGreatestElongationWest": 4702523234953642298, + "LastMercuryInferiorConjunction": 4702523174805183896, + "LastMercuryProgradeToRetrograde": 4702523153306081141, + "LastMercuryRetrograde": 4702523201486035315, + "LastMercuryRetrogradeToPrograde": 4702523201486035315, + "LastMercurySuperiorConjunction": 4702523315913890302, + "NextMercuryConjunction": 4702523452494063669, + "NextMercuryGreatestElongation": 4702523491122241464, + "NextMercuryGreatestElongationEast": 4702523645074145618, + "NextMercuryGreatestElongationWest": 4702523491122241464, + "NextMercuryInferiorConjunction": 4702523452494063669, + "NextMercuryProgradeToRetrograde": 4702523420799060905, + "NextMercuryRetrograde": 4702523420799060905, + "NextMercuryRetrogradeToPrograde": 4702523472995667966, + "NextMercurySuperiorConjunction": 4702523545107501456 + } + }, + { + "input_utc": "2005-12-01T16:24:29.789Z", + "tt_jd_bits": 4702523705255467863, + "events": { + "LastMercuryConjunction": 4702523690159185905, + "LastMercuryGreatestElongation": 4702523645074145542, + "LastMercuryGreatestElongationEast": 4702523645074145542, + "LastMercuryGreatestElongationWest": 4702523491122241514, + "LastMercuryInferiorConjunction": 4702523690159185905, + "LastMercuryProgradeToRetrograde": 4702523668134652166, + "LastMercuryRetrograde": 4702523668134652166, + "LastMercuryRetrogradeToPrograde": 4702523472995667966, + "LastMercurySuperiorConjunction": 4702523545107501579, + "NextMercuryConjunction": 4702523825973990989, + "NextMercuryGreatestElongation": 4702523728544263694, + "NextMercuryGreatestElongationEast": 4702523886775128504, + "NextMercuryGreatestElongationWest": 4702523728544263694, + "NextMercuryInferiorConjunction": 4702523920925563273, + "NextMercuryProgradeToRetrograde": 4702523899843419575, + "NextMercuryRetrograde": 4702523710311192770, + "NextMercuryRetrogradeToPrograde": 4702523710311192770, + "NextMercurySuperiorConjunction": 4702523825973990989 + } + }, + { + "input_utc": "2006-04-17T19:31:40.789Z", + "tt_jd_bits": 4702523999739900513, + "events": { + "LastMercuryConjunction": 4702523920925563364, + "LastMercuryGreatestElongation": 4702523980332486672, + "LastMercuryGreatestElongationEast": 4702523886775128468, + "LastMercuryGreatestElongationWest": 4702523980332486672, + "LastMercuryInferiorConjunction": 4702523920925563364, + "LastMercuryProgradeToRetrograde": 4702523899843419575, + "LastMercuryRetrograde": 4702523947551534429, + "LastMercuryRetrogradeToPrograde": 4702523947551534429, + "LastMercurySuperiorConjunction": 4702523825973990887, + "NextMercuryConjunction": 4702524066355577587, + "NextMercuryGreatestElongation": 4702524137235703298, + "NextMercuryGreatestElongationEast": 4702524137235703298, + "NextMercuryGreatestElongationWest": 4702524238557878240, + "NextMercuryInferiorConjunction": 4702524196196698187, + "NextMercuryProgradeToRetrograde": 4702524165658348239, + "NextMercuryRetrograde": 4702524165658348239, + "NextMercuryRetrogradeToPrograde": 4702524218537863389, + "NextMercurySuperiorConjunction": 4702524066355577587 + } + }, + { + "input_utc": "2006-09-01T22:38:51.789Z", + "tt_jd_bits": 4702524294224308308, + "events": { + "LastMercuryConjunction": 4702524292627635287, + "LastMercuryGreatestElongation": 4702524238557878168, + "LastMercuryGreatestElongationEast": 4702524137235703316, + "LastMercuryGreatestElongationWest": 4702524238557878168, + "LastMercuryInferiorConjunction": 4702524196196698330, + "LastMercuryProgradeToRetrograde": 4702524165658348239, + "LastMercuryRetrograde": 4702524218537863389, + "LastMercuryRetrogradeToPrograde": 4702524218537863389, + "LastMercurySuperiorConjunction": 4702524292627635287, + "NextMercuryConjunction": 4702524440149509375, + "NextMercuryGreatestElongation": 4702524391350451612, + "NextMercuryGreatestElongationEast": 4702524391350451612, + "NextMercuryGreatestElongationWest": 4702524475890356746, + "NextMercuryInferiorConjunction": 4702524440149509375, + "NextMercuryProgradeToRetrograde": 4702524416759440289, + "NextMercuryRetrograde": 4702524416759440289, + "NextMercuryRetrogradeToPrograde": 4702524459255770639, + "NextMercurySuperiorConjunction": 4702524567618736739 + } + }, + { + "input_utc": "2007-01-17T01:46:02.789Z", + "tt_jd_bits": 4702524588708716103, + "events": { + "LastMercuryConjunction": 4702524567618736714, + "LastMercuryGreatestElongation": 4702524475890356722, + "LastMercuryGreatestElongationEast": 4702524391350451678, + "LastMercuryGreatestElongationWest": 4702524475890356722, + "LastMercuryInferiorConjunction": 4702524440149509453, + "LastMercuryProgradeToRetrograde": 4702524416759440289, + "LastMercuryRetrograde": 4702524459255770639, + "LastMercuryRetrogradeToPrograde": 4702524459255770639, + "LastMercurySuperiorConjunction": 4702524567618736714, + "NextMercuryConjunction": 4702524668431534309, + "NextMercuryGreatestElongation": 4702524635226817630, + "NextMercuryGreatestElongationEast": 4702524635226817630, + "NextMercuryGreatestElongationWest": 4702524726148367300, + "NextMercuryInferiorConjunction": 4702524668431534309, + "NextMercuryProgradeToRetrograde": 4702524647783088692, + "NextMercuryRetrograde": 4702524647783088692, + "NextMercuryRetrogradeToPrograde": 4702524694684655854, + "NextMercurySuperiorConjunction": 4702524816548110487 + } + }, + { + "input_utc": "2007-06-03T03:36:13.789Z", + "tt_jd_bits": 4702524883078293176, + "events": { + "LastMercuryConjunction": 4702524816548110523, + "LastMercuryGreatestElongation": 4702524881497338232, + "LastMercuryGreatestElongationEast": 4702524881497338232, + "LastMercuryGreatestElongationWest": 4702524726148367336, + "LastMercuryInferiorConjunction": 4702524668431534398, + "LastMercuryProgradeToRetrograde": 4702524647783088692, + "LastMercuryRetrograde": 4702524694684655854, + "LastMercuryRetrogradeToPrograde": 4702524694684655854, + "LastMercurySuperiorConjunction": 4702524816548110523, + "NextMercuryConjunction": 4702524938110863118, + "NextMercuryGreatestElongation": 4702524985027469384, + "NextMercuryGreatestElongationEast": 4702525137601393522, + "NextMercuryGreatestElongationWest": 4702524985027469384, + "NextMercuryInferiorConjunction": 4702524938110863118, + "NextMercuryProgradeToRetrograde": 4702524909970925319, + "NextMercuryRetrograde": 4702524909970925319, + "NextMercuryRetrogradeToPrograde": 4702524962358219072, + "NextMercurySuperiorConjunction": 4702525041304459597 + } + }, + { + "input_utc": "2007-10-18T06:43:24.789Z", + "tt_jd_bits": 4702525177562700971, + "events": { + "LastMercuryConjunction": 4702525041304459566, + "LastMercuryGreatestElongation": 4702525137601393580, + "LastMercuryGreatestElongationEast": 4702525137601393580, + "LastMercuryGreatestElongationWest": 4702524985027469352, + "LastMercuryInferiorConjunction": 4702524938110863153, + "LastMercuryProgradeToRetrograde": 4702525164696705562, + "LastMercuryRetrograde": 4702525164696705562, + "LastMercuryRetrogradeToPrograde": 4702524962358219072, + "LastMercurySuperiorConjunction": 4702525041304459566, + "NextMercuryConjunction": 4702525189836619831, + "NextMercuryGreatestElongation": 4702525223892403996, + "NextMercuryGreatestElongationEast": 4702525383602854410, + "NextMercuryGreatestElongationWest": 4702525223892403996, + "NextMercuryInferiorConjunction": 4702525189836619831, + "NextMercuryProgradeToRetrograde": 4702525396656640204, + "NextMercuryRetrograde": 4702525208161792832, + "NextMercuryRetrogradeToPrograde": 4702525208161792832, + "NextMercurySuperiorConjunction": 4702525307190200599 + } + }, + { + "input_utc": "2008-03-02T12:50:35.789Z", + "tt_jd_bits": 4702525470168060574, + "events": { + "LastMercuryConjunction": 4702525416968858913, + "LastMercuryGreatestElongation": 4702525383602854456, + "LastMercuryGreatestElongationEast": 4702525383602854456, + "LastMercuryGreatestElongationWest": 4702525223892404034, + "LastMercuryInferiorConjunction": 4702525416968858913, + "LastMercuryProgradeToRetrograde": 4702525396656640204, + "LastMercuryRetrograde": 4702525442500524947, + "LastMercuryRetrogradeToPrograde": 4702525442500524947, + "LastMercurySuperiorConjunction": 4702525307190200484, + "NextMercuryConjunction": 4702525566316197471, + "NextMercuryGreatestElongation": 4702525472169600784, + "NextMercuryGreatestElongationEast": 4702525626127513724, + "NextMercuryGreatestElongationWest": 4702525472169600784, + "NextMercuryInferiorConjunction": 4702525678705186067, + "NextMercuryProgradeToRetrograde": 4702525653419718635, + "NextMercuryRetrograde": 4702525653419718635, + "NextMercuryRetrogradeToPrograde": 4702525704399179537, + "NextMercurySuperiorConjunction": 4702525566316197471 + } + }, + { + "input_utc": "2008-07-17T15:57:46.789Z", + "tt_jd_bits": 4702525764652468369, + "events": { + "LastMercuryConjunction": 4702525678705186196, + "LastMercuryGreatestElongation": 4702525730464896658, + "LastMercuryGreatestElongationEast": 4702525626127513724, + "LastMercuryGreatestElongationWest": 4702525730464896658, + "LastMercuryInferiorConjunction": 4702525678705186196, + "LastMercuryProgradeToRetrograde": 4702525653419718635, + "LastMercuryRetrograde": 4702525704399179537, + "LastMercuryRetrogradeToPrograde": 4702525704399179537, + "LastMercurySuperiorConjunction": 4702525566316197399, + "NextMercuryConjunction": 4702525790788458111, + "NextMercuryGreatestElongation": 4702525883886341820, + "NextMercuryGreatestElongationEast": 4702525883886341820, + "NextMercuryGreatestElongationWest": 4702525972383390586, + "NextMercuryInferiorConjunction": 4702525939037242083, + "NextMercuryProgradeToRetrograde": 4702525911875406517, + "NextMercuryRetrograde": 4702525911875406517, + "NextMercuryRetrogradeToPrograde": 4702525956945661466, + "NextMercurySuperiorConjunction": 4702525790788458111 + } + }, + { + "input_utc": "2008-12-01T19:04:57.789Z", + "tt_jd_bits": 4702526059136876164, + "events": { + "LastMercuryConjunction": 4702526046051976071, + "LastMercuryGreatestElongation": 4702525972383390592, + "LastMercuryGreatestElongationEast": 4702525883886341766, + "LastMercuryGreatestElongationWest": 4702525972383390592, + "LastMercuryInferiorConjunction": 4702525939037242122, + "LastMercuryProgradeToRetrograde": 4702525911875406517, + "LastMercuryRetrograde": 4702525956945661466, + "LastMercuryRetrogradeToPrograde": 4702525956945661466, + "LastMercurySuperiorConjunction": 4702526046051976071, + "NextMercuryConjunction": 4702526166232662970, + "NextMercuryGreatestElongation": 4702526131692116786, + "NextMercuryGreatestElongationEast": 4702526131692116786, + "NextMercuryGreatestElongationWest": 4702526218194832060, + "NextMercuryInferiorConjunction": 4702526166232662970, + "NextMercuryProgradeToRetrograde": 4702526146072065473, + "NextMercuryRetrograde": 4702526146072065473, + "NextMercuryRetrogradeToPrograde": 4702526190759393611, + "NextMercurySuperiorConjunction": 4702526315438081732 + } + }, + { + "input_utc": "2009-04-17T22:12:08.789Z", + "tt_jd_bits": 4702526353621308814, + "events": { + "LastMercuryConjunction": 4702526315438081786, + "LastMercuryGreatestElongation": 4702526218194832114, + "LastMercuryGreatestElongationEast": 4702526131692116810, + "LastMercuryGreatestElongationWest": 4702526218194832114, + "LastMercuryInferiorConjunction": 4702526166232663014, + "LastMercuryProgradeToRetrograde": 4702526146072065473, + "LastMercuryRetrograde": 4702526190759393611, + "LastMercuryRetrogradeToPrograde": 4702526190759393611, + "LastMercurySuperiorConjunction": 4702526315438081786, + "NextMercuryConjunction": 4702526419102708598, + "NextMercuryGreatestElongation": 4702526371658784768, + "NextMercuryGreatestElongationEast": 4702526371658784768, + "NextMercuryGreatestElongationWest": 4702526475101781014, + "NextMercuryInferiorConjunction": 4702526419102708598, + "NextMercuryProgradeToRetrograde": 4702526396022363766, + "NextMercuryRetrograde": 4702526396022363766, + "NextMercuryRetrogradeToPrograde": 4702526445391787488, + "NextMercurySuperiorConjunction": 4702526540813645208 + } + }, + { + "input_utc": "2009-09-02T01:16:56.789Z", + "tt_jd_bits": 4702526648102162325, + "events": { + "LastMercuryConjunction": 4702526540813645185, + "LastMercuryGreatestElongation": 4702526630112169634, + "LastMercuryGreatestElongationEast": 4702526630112169634, + "LastMercuryGreatestElongationWest": 4702526475101780990, + "LastMercuryInferiorConjunction": 4702526419102708589, + "LastMercuryProgradeToRetrograde": 4702526396022363766, + "LastMercuryRetrograde": 4702526445391787488, + "LastMercuryRetrogradeToPrograde": 4702526445391787488, + "LastMercurySuperiorConjunction": 4702526540813645185, + "NextMercuryConjunction": 4702526687542180098, + "NextMercuryGreatestElongation": 4702526721135883516, + "NextMercuryGreatestElongationEast": 4702526879345179484, + "NextMercuryGreatestElongationWest": 4702526721135883516, + "NextMercuryInferiorConjunction": 4702526687542180098, + "NextMercuryProgradeToRetrograde": 4702526658343876801, + "NextMercuryRetrograde": 4702526658343876801, + "NextMercuryRetrogradeToPrograde": 4702526705478169688, + "NextMercurySuperiorConjunction": 4702526786144042224 + } + }, + { + "input_utc": "2010-01-17T04:24:07.789Z", + "tt_jd_bits": 4702526942586570120, + "events": { + "LastMercuryConjunction": 4702526915982848372, + "LastMercuryGreatestElongation": 4702526879345179422, + "LastMercuryGreatestElongationEast": 4702526879345179422, + "LastMercuryGreatestElongationWest": 4702526721135883608, + "LastMercuryInferiorConjunction": 4702526915982848372, + "LastMercuryProgradeToRetrograde": 4702526895725404952, + "LastMercuryRetrograde": 4702526939325940048, + "LastMercuryRetrogradeToPrograde": 4702526939325940048, + "LastMercurySuperiorConjunction": 4702526786144042208, + "NextMercuryConjunction": 4702527063637705840, + "NextMercuryGreatestElongation": 4702526964148379832, + "NextMercuryGreatestElongationEast": 4702527118239772186, + "NextMercuryGreatestElongationWest": 4702526964148379832, + "NextMercuryInferiorConjunction": 4702527160583580414, + "NextMercuryProgradeToRetrograde": 4702527138531036733, + "NextMercuryRetrograde": 4702527138531036733, + "NextMercuryRetrogradeToPrograde": 4702527187015427263, + "NextMercurySuperiorConjunction": 4702527063637705840 + } + }, + { + "input_utc": "2010-06-03T07:31:18.789Z", + "tt_jd_bits": 4702527237070977915, + "events": { + "LastMercuryConjunction": 4702527160583580391, + "LastMercuryGreatestElongation": 4702527219428773438, + "LastMercuryGreatestElongationEast": 4702527118239772290, + "LastMercuryGreatestElongationWest": 4702527219428773438, + "LastMercuryInferiorConjunction": 4702527160583580391, + "LastMercuryProgradeToRetrograde": 4702527138531036733, + "LastMercuryRetrograde": 4702527187015427263, + "LastMercuryRetrogradeToPrograde": 4702527187015427263, + "LastMercurySuperiorConjunction": 4702527063637705811, + "NextMercuryConjunction": 4702527291167237184, + "NextMercuryGreatestElongation": 4702527376086982756, + "NextMercuryGreatestElongationEast": 4702527376086982756, + "NextMercuryGreatestElongationWest": 4702527469876011792, + "NextMercuryInferiorConjunction": 4702527435090442448, + "NextMercuryProgradeToRetrograde": 4702527404240277982, + "NextMercuryRetrograde": 4702527404240277982, + "NextMercuryRetrogradeToPrograde": 4702527453574758402, + "NextMercurySuperiorConjunction": 4702527291167237184 + } + }, + { + "input_utc": "2010-10-17T13:38:29.789Z", + "tt_jd_bits": 4702527529676337518, + "events": { + "LastMercuryConjunction": 4702527528550304090, + "LastMercuryGreatestElongation": 4702527469876011766, + "LastMercuryGreatestElongationEast": 4702527376086982740, + "LastMercuryGreatestElongationWest": 4702527469876011766, + "LastMercuryInferiorConjunction": 4702527435090442438, + "LastMercuryProgradeToRetrograde": 4702527404240277980, + "LastMercuryRetrograde": 4702527453574758402, + "LastMercuryRetrogradeToPrograde": 4702527453574758402, + "LastMercurySuperiorConjunction": 4702527528550304090, + "NextMercuryConjunction": 4702527666016855268, + "NextMercuryGreatestElongation": 4702527626495736944, + "NextMercuryGreatestElongationEast": 4702527626495736944, + "NextMercuryGreatestElongationWest": 4702527710134277650, + "NextMercuryInferiorConjunction": 4702527666016855268, + "NextMercuryProgradeToRetrograde": 4702527645359991035, + "NextMercuryRetrograde": 4702527645359991035, + "NextMercuryRetrogradeToPrograde": 4702527688098504589, + "NextMercurySuperiorConjunction": 4702527810561261163 + } + }, + { + "input_utc": "2011-03-03T16:45:40.789Z", + "tt_jd_bits": 4702527824160745313, + "events": { + "LastMercuryConjunction": 4702527810561261005, + "LastMercuryGreatestElongation": 4702527710134277748, + "LastMercuryGreatestElongationEast": 4702527626495737054, + "LastMercuryGreatestElongationWest": 4702527710134277748, + "LastMercuryInferiorConjunction": 4702527666016855225, + "LastMercuryProgradeToRetrograde": 4702527645359991035, + "LastMercuryRetrograde": 4702527688098504589, + "LastMercuryRetrogradeToPrograde": 4702527688098504589, + "LastMercurySuperiorConjunction": 4702527810561261005, + "NextMercuryConjunction": 4702527903870414582, + "NextMercuryGreatestElongation": 4702527865713728062, + "NextMercuryGreatestElongationEast": 4702527865713728062, + "NextMercuryGreatestElongationWest": 4702527963953474814, + "NextMercuryInferiorConjunction": 4702527903870414582, + "NextMercuryProgradeToRetrograde": 4702527882183883518, + "NextMercuryRetrograde": 4702527882183883518, + "NextMercuryRetrogradeToPrograde": 4702527930487710479, + "NextMercurySuperiorConjunction": 4702528041679346542 + } + }, + { + "input_utc": "2011-07-18T18:35:51.789Z", + "tt_jd_bits": 4702528118530322386, + "events": { + "LastMercuryConjunction": 4702528041679346494, + "LastMercuryGreatestElongation": 4702527963953474766, + "LastMercuryGreatestElongationEast": 4702527865713727992, + "LastMercuryGreatestElongationWest": 4702527963953474766, + "LastMercuryInferiorConjunction": 4702527903870414634, + "LastMercuryProgradeToRetrograde": 4702527882183883518, + "LastMercuryRetrograde": 4702527930487710477, + "LastMercuryRetrogradeToPrograde": 4702527930487710477, + "LastMercurySuperiorConjunction": 4702528041679346494, + "NextMercuryConjunction": 4702528181384529234, + "NextMercuryGreatestElongation": 4702528121609348740, + "NextMercuryGreatestElongationEast": 4702528121609348740, + "NextMercuryGreatestElongationWest": 4702528218326898158, + "NextMercuryInferiorConjunction": 4702528181384529234, + "NextMercuryProgradeToRetrograde": 4702528149689072976, + "NextMercuryRetrograde": 4702528149689072976, + "NextMercuryRetrogradeToPrograde": 4702528200981589293, + "NextMercurySuperiorConjunction": 4702528273296391160 + } + }, + { + "input_utc": "2011-12-02T21:43:02.789Z", + "tt_jd_bits": 4702528413014730181, + "events": { + "LastMercuryConjunction": 4702528273296391217, + "LastMercuryGreatestElongation": 4702528373190780564, + "LastMercuryGreatestElongationEast": 4702528373190780564, + "LastMercuryGreatestElongationWest": 4702528218326898214, + "LastMercuryInferiorConjunction": 4702528181384529181, + "LastMercuryProgradeToRetrograde": 4702528394748842015, + "LastMercuryRetrograde": 4702528394748842015, + "LastMercuryRetrogradeToPrograde": 4702528200981589293, + "LastMercurySuperiorConjunction": 4702528273296391217, + "NextMercuryConjunction": 4702528416158541796, + "NextMercuryGreatestElongation": 4702528456448243382, + "NextMercuryGreatestElongationEast": 4702528613790249490, + "NextMercuryGreatestElongationWest": 4702528456448243382, + "NextMercuryInferiorConjunction": 4702528416158541796, + "NextMercuryProgradeToRetrograde": 4702528627681255402, + "NextMercuryRetrograde": 4702528436994958001, + "NextMercuryRetrogradeToPrograde": 4702528436994958001, + "NextMercurySuperiorConjunction": 4702528555760247200 + } + }, + { + "input_utc": "2012-04-18T00:50:13.789Z", + "tt_jd_bits": 4702528707499137976, + "events": { + "LastMercuryConjunction": 4702528649023769508, + "LastMercuryGreatestElongation": 4702528613790249442, + "LastMercuryGreatestElongationEast": 4702528613790249442, + "LastMercuryGreatestElongationWest": 4702528456448243356, + "LastMercuryInferiorConjunction": 4702528649023769508, + "LastMercuryProgradeToRetrograde": 4702528627681255402, + "LastMercuryRetrograde": 4702528675741661908, + "LastMercuryRetrogradeToPrograde": 4702528675741661908, + "LastMercurySuperiorConjunction": 4702528555760247184, + "NextMercuryConjunction": 4702528792187537320, + "NextMercuryGreatestElongation": 4702528708978357446, + "NextMercuryGreatestElongationEast": 4702528866511356392, + "NextMercuryGreatestElongationWest": 4702528708978357446, + "NextMercuryInferiorConjunction": 4702528926103907896, + "NextMercuryProgradeToRetrograde": 4702528894735641805, + "NextMercuryRetrograde": 4702528894735641805, + "NextMercuryRetrogradeToPrograde": 4702528947344851189, + "NextMercurySuperiorConjunction": 4702528792187537320 + } + }, + { + "input_utc": "2012-09-02T03:57:24.789Z", + "tt_jd_bits": 4702529001983570626, + "events": { + "LastMercuryConjunction": 4702528926103907863, + "LastMercuryGreatestElongation": 4702528966199947472, + "LastMercuryGreatestElongationEast": 4702528866511356360, + "LastMercuryGreatestElongationWest": 4702528966199947472, + "LastMercuryInferiorConjunction": 4702528926103907863, + "LastMercuryProgradeToRetrograde": 4702528894735641804, + "LastMercuryRetrograde": 4702528947344851189, + "LastMercuryRetrogradeToPrograde": 4702528947344851189, + "LastMercurySuperiorConjunction": 4702528792187537283, + "NextMercuryConjunction": 4702529019946761805, + "NextMercuryGreatestElongation": 4702529119577765600, + "NextMercuryGreatestElongationEast": 4702529119577765600, + "NextMercuryGreatestElongationWest": 4702529203384710484, + "NextMercuryInferiorConjunction": 4702529166248306028, + "NextMercuryProgradeToRetrograde": 4702529143687207331, + "NextMercuryRetrograde": 4702529143687207331, + "NextMercuryRetrogradeToPrograde": 4702529185940516121, + "NextMercurySuperiorConjunction": 4702529019946761805 + } + }, + { + "input_utc": "1995-02-25T12:34:56.789Z", + "tt_jd_bits": 4702515261007359590, + "events": { + "LastMercuryConjunction": 4702515214693455685, + "LastMercuryGreatestElongation": 4702515181210033306, + "LastMercuryGreatestElongationEast": 4702515181210033306, + "LastMercuryGreatestElongationWest": 4702515021517794702, + "LastMercuryInferiorConjunction": 4702515214693455685, + "LastMercuryProgradeToRetrograde": 4702515194423148411, + "LastMercuryRetrograde": 4702515240096207209, + "LastMercuryRetrogradeToPrograde": 4702515240096207209, + "LastMercurySuperiorConjunction": 4702515103405453348, + "NextMercuryConjunction": 4702515364105430859, + "NextMercuryGreatestElongation": 4702515269464472554, + "NextMercuryGreatestElongationEast": 4702515423269902574, + "NextMercuryGreatestElongationWest": 4702515269464472554, + "NextMercuryInferiorConjunction": 4702515475132306816, + "NextMercuryProgradeToRetrograde": 4702515450268316245, + "NextMercuryRetrograde": 4702515450268316245, + "NextMercuryRetrogradeToPrograde": 4702515500954237132, + "NextMercurySuperiorConjunction": 4702515364105430859 + } + }, + { + "input_utc": "1995-05-25T19:40:15.789Z", + "tt_jd_bits": 4702515452767682448, + "events": { + "LastMercuryConjunction": 4702515364105430895, + "LastMercuryGreatestElongation": 4702515423269902576, + "LastMercuryGreatestElongationEast": 4702515423269902576, + "LastMercuryGreatestElongationWest": 4702515269464472590, + "LastMercuryInferiorConjunction": 4702515214693455694, + "LastMercuryProgradeToRetrograde": 4702515450268316245, + "LastMercuryRetrograde": 4702515450268316245, + "LastMercuryRetrogradeToPrograde": 4702515240096207209, + "LastMercurySuperiorConjunction": 4702515364105430895, + "NextMercuryConjunction": 4702515475132306817, + "NextMercuryGreatestElongation": 4702515527588870252, + "NextMercuryGreatestElongationEast": 4702515681197673902, + "NextMercuryGreatestElongationWest": 4702515527588870252, + "NextMercuryInferiorConjunction": 4702515475132306817, + "NextMercuryProgradeToRetrograde": 4702515709270598966, + "NextMercuryRetrograde": 4702515500954237132, + "NextMercuryRetrogradeToPrograde": 4702515500954237132, + "NextMercurySuperiorConjunction": 4702515588636630993 + } + }, + { + "input_utc": "1995-08-23T02:45:34.789Z", + "tt_jd_bits": 4702515644528005305, + "events": { + "LastMercuryConjunction": 4702515588636631064, + "LastMercuryGreatestElongation": 4702515527588870196, + "LastMercuryGreatestElongationEast": 4702515423269902624, + "LastMercuryGreatestElongationWest": 4702515527588870196, + "LastMercuryInferiorConjunction": 4702515475132306807, + "LastMercuryProgradeToRetrograde": 4702515450268316245, + "LastMercuryRetrograde": 4702515500954237132, + "LastMercuryRetrogradeToPrograde": 4702515500954237132, + "LastMercurySuperiorConjunction": 4702515588636631064, + "NextMercuryConjunction": 4702515736735711189, + "NextMercuryGreatestElongation": 4702515681197673904, + "NextMercuryGreatestElongationEast": 4702515681197673904, + "NextMercuryGreatestElongationWest": 4702515770061198758, + "NextMercuryInferiorConjunction": 4702515736735711189, + "NextMercuryProgradeToRetrograde": 4702515709270598966, + "NextMercuryRetrograde": 4702515709270598966, + "NextMercuryRetrogradeToPrograde": 4702515754612402123, + "NextMercurySuperiorConjunction": 4702515842333660725 + } + }, + { + "input_utc": "1995-11-20T09:50:53.789Z", + "tt_jd_bits": 4702515836288328162, + "events": { + "LastMercuryConjunction": 4702515736735711297, + "LastMercuryGreatestElongation": 4702515770061198782, + "LastMercuryGreatestElongationEast": 4702515681197673884, + "LastMercuryGreatestElongationWest": 4702515770061198782, + "LastMercuryInferiorConjunction": 4702515736735711297, + "LastMercuryProgradeToRetrograde": 4702515709270598966, + "LastMercuryRetrograde": 4702515754612402123, + "LastMercuryRetrogradeToPrograde": 4702515754612402123, + "LastMercurySuperiorConjunction": 4702515588636631072, + "NextMercuryConjunction": 4702515842333660749, + "NextMercuryGreatestElongation": 4702515929246795582, + "NextMercuryGreatestElongationEast": 4702515929246795582, + "NextMercuryGreatestElongationWest": 4702516015471090580, + "NextMercuryInferiorConjunction": 4702515964045090023, + "NextMercuryProgradeToRetrograde": 4702515943890285850, + "NextMercuryRetrograde": 4702515943890285850, + "NextMercuryRetrogradeToPrograde": 4702515988415305582, + "NextMercurySuperiorConjunction": 4702515842333660749 + } + }, + { + "input_utc": "1996-02-17T16:56:12.789Z", + "tt_jd_bits": 4702516028048675875, + "events": { + "LastMercuryConjunction": 4702515964045090027, + "LastMercuryGreatestElongation": 4702516015471090478, + "LastMercuryGreatestElongationEast": 4702515929246795586, + "LastMercuryGreatestElongationWest": 4702516015471090478, + "LastMercuryInferiorConjunction": 4702515964045090027, + "LastMercuryProgradeToRetrograde": 4702515943890285850, + "LastMercuryRetrograde": 4702515988415305582, + "LastMercuryRetrogradeToPrograde": 4702515988415305582, + "LastMercurySuperiorConjunction": 4702515842333660874, + "NextMercuryConjunction": 4702516113112506788, + "NextMercuryGreatestElongation": 4702516168964293794, + "NextMercuryGreatestElongationEast": 4702516168964293794, + "NextMercuryGreatestElongationWest": 4702516272138534776, + "NextMercuryInferiorConjunction": 4702516215618182008, + "NextMercuryProgradeToRetrograde": 4702516192756674343, + "NextMercuryRetrograde": 4702516192756674343, + "NextMercuryRetrogradeToPrograde": 4702516241948935106, + "NextMercurySuperiorConjunction": 4702516113112506788 + } + }, + { + "input_utc": "1996-05-15T13:01:31.789Z", + "tt_jd_bits": 4702516216677251745, + "events": { + "LastMercuryConjunction": 4702516215618181993, + "LastMercuryGreatestElongation": 4702516168964293780, + "LastMercuryGreatestElongationEast": 4702516168964293780, + "LastMercuryGreatestElongationWest": 4702516015471090532, + "LastMercuryInferiorConjunction": 4702516215618181993, + "LastMercuryProgradeToRetrograde": 4702516192756674343, + "LastMercuryRetrograde": 4702516192756674343, + "LastMercuryRetrogradeToPrograde": 4702515988415305582, + "LastMercurySuperiorConjunction": 4702516113112506673, + "NextMercuryConjunction": 4702516338722821374, + "NextMercuryGreatestElongation": 4702516272138534892, + "NextMercuryGreatestElongationEast": 4702516427408912868, + "NextMercuryGreatestElongationWest": 4702516272138534892, + "NextMercuryInferiorConjunction": 4702516485116611819, + "NextMercuryProgradeToRetrograde": 4702516455638852666, + "NextMercuryRetrograde": 4702516241948935106, + "NextMercuryRetrogradeToPrograde": 4702516241948935106, + "NextMercurySuperiorConjunction": 4702516338722821374 + } + }, + { + "input_utc": "1996-08-12T20:06:50.789Z", + "tt_jd_bits": 4702516408437574603, + "events": { + "LastMercuryConjunction": 4702516338722821523, + "LastMercuryGreatestElongation": 4702516272138534784, + "LastMercuryGreatestElongationEast": 4702516168964293846, + "LastMercuryGreatestElongationWest": 4702516272138534784, + "LastMercuryInferiorConjunction": 4702516215618181979, + "LastMercuryProgradeToRetrograde": 4702516192756674343, + "LastMercuryRetrograde": 4702516241948935106, + "LastMercuryRetrogradeToPrograde": 4702516241948935106, + "LastMercurySuperiorConjunction": 4702516338722821523, + "NextMercuryConjunction": 4702516485116611918, + "NextMercuryGreatestElongation": 4702516427408912966, + "NextMercuryGreatestElongationEast": 4702516427408912966, + "NextMercuryGreatestElongationWest": 4702516518824249742, + "NextMercuryInferiorConjunction": 4702516485116611918, + "NextMercuryProgradeToRetrograde": 4702516455638852666, + "NextMercuryRetrograde": 4702516455638852666, + "NextMercuryRetrogradeToPrograde": 4702516503104069980, + "NextMercurySuperiorConjunction": 4702516582722765862 + } + }, + { + "input_utc": "1996-11-10T03:12:09.789Z", + "tt_jd_bits": 4702516600197897460, + "events": { + "LastMercuryConjunction": 4702516582722766012, + "LastMercuryGreatestElongation": 4702516518824249764, + "LastMercuryGreatestElongationEast": 4702516427408912950, + "LastMercuryGreatestElongationWest": 4702516518824249764, + "LastMercuryInferiorConjunction": 4702516485116611844, + "LastMercuryProgradeToRetrograde": 4702516455638852666, + "LastMercuryRetrograde": 4702516503104069980, + "LastMercuryRetrogradeToPrograde": 4702516503104069980, + "LastMercurySuperiorConjunction": 4702516582722766012, + "NextMercuryConjunction": 4702516713848025185, + "NextMercuryGreatestElongation": 4702516676832042946, + "NextMercuryGreatestElongationEast": 4702516676832042946, + "NextMercuryGreatestElongationWest": 4702516761408453046, + "NextMercuryInferiorConjunction": 4702516713848025185, + "NextMercuryProgradeToRetrograde": 4702516693552924059, + "NextMercuryRetrograde": 4702516693552924059, + "NextMercuryRetrogradeToPrograde": 4702516737013192068, + "NextMercurySuperiorConjunction": 4702516861148405908 + } + }, + { + "input_utc": "1997-02-07T10:17:28.789Z", + "tt_jd_bits": 4702516791958220317, + "events": { + "LastMercuryConjunction": 4702516713848025061, + "LastMercuryGreatestElongation": 4702516761408453150, + "LastMercuryGreatestElongationEast": 4702516676832042950, + "LastMercuryGreatestElongationWest": 4702516761408453150, + "LastMercuryInferiorConjunction": 4702516713848025061, + "LastMercuryProgradeToRetrograde": 4702516693552924059, + "LastMercuryRetrograde": 4702516737013192068, + "LastMercuryRetrogradeToPrograde": 4702516737013192068, + "LastMercurySuperiorConjunction": 4702516582722765997, + "NextMercuryConjunction": 4702516861148405884, + "NextMercuryGreatestElongation": 4702516915691034414, + "NextMercuryGreatestElongationEast": 4702516915691034414, + "NextMercuryGreatestElongationWest": 4702517016458023750, + "NextMercuryInferiorConjunction": 4702516957334635863, + "NextMercuryProgradeToRetrograde": 4702516935346191870, + "NextMercuryRetrograde": 4702516935346191870, + "NextMercuryRetrogradeToPrograde": 4702516983785722589, + "NextMercurySuperiorConjunction": 4702516861148405884 + } + }, + { + "input_utc": "1997-05-07T17:22:47.789Z", + "tt_jd_bits": 4702516983718543175, + "events": { + "LastMercuryConjunction": 4702516957334635919, + "LastMercuryGreatestElongation": 4702516915691034470, + "LastMercuryGreatestElongationEast": 4702516915691034470, + "LastMercuryGreatestElongationWest": 4702516761408453122, + "LastMercuryInferiorConjunction": 4702516957334635919, + "LastMercuryProgradeToRetrograde": 4702516935346191870, + "LastMercuryRetrograde": 4702516935346191870, + "LastMercuryRetrogradeToPrograde": 4702516737013192068, + "LastMercurySuperiorConjunction": 4702516861148405847, + "NextMercuryConjunction": 4702517089109299204, + "NextMercuryGreatestElongation": 4702517016458023672, + "NextMercuryGreatestElongationEast": 4702517173332815718, + "NextMercuryGreatestElongationWest": 4702517016458023672, + "NextMercuryInferiorConjunction": 4702517232496649901, + "NextMercuryProgradeToRetrograde": 4702517201445547580, + "NextMercuryRetrograde": 4702516983785722589, + "NextMercuryRetrogradeToPrograde": 4702516983785722589, + "NextMercurySuperiorConjunction": 4702517089109299204 + } + }, + { + "input_utc": "1997-08-03T13:28:06.789Z", + "tt_jd_bits": 4702517172347143900, + "events": { + "LastMercuryConjunction": 4702517089109299236, + "LastMercuryGreatestElongation": 4702517016458023704, + "LastMercuryGreatestElongationEast": 4702516915691034472, + "LastMercuryGreatestElongationWest": 4702517016458023704, + "LastMercuryInferiorConjunction": 4702516957334635884, + "LastMercuryProgradeToRetrograde": 4702516935346191870, + "LastMercuryRetrograde": 4702516983785722589, + "LastMercuryRetrogradeToPrograde": 4702516983785722589, + "LastMercurySuperiorConjunction": 4702517089109299236, + "NextMercuryConjunction": 4702517232496650013, + "NextMercuryGreatestElongation": 4702517173332815702, + "NextMercuryGreatestElongationEast": 4702517173332815702, + "NextMercuryGreatestElongationWest": 4702517267537726312, + "NextMercuryInferiorConjunction": 4702517232496650013, + "NextMercuryProgradeToRetrograde": 4702517201445547579, + "NextMercuryRetrograde": 4702517201445547579, + "NextMercuryRetrogradeToPrograde": 4702517251119941877, + "NextMercurySuperiorConjunction": 4702517325494165633 + } + }, + { + "input_utc": "1997-10-31T20:33:25.789Z", + "tt_jd_bits": 4702517364107466758, + "events": { + "LastMercuryConjunction": 4702517325494165687, + "LastMercuryGreatestElongation": 4702517267537726238, + "LastMercuryGreatestElongationEast": 4702517173332815618, + "LastMercuryGreatestElongationWest": 4702517267537726238, + "LastMercuryInferiorConjunction": 4702517232496649960, + "LastMercuryProgradeToRetrograde": 4702517201445547579, + "LastMercuryRetrograde": 4702517251119941877, + "LastMercuryRetrogradeToPrograde": 4702517251119941877, + "LastMercurySuperiorConjunction": 4702517325494165687, + "NextMercuryConjunction": 4702517463904041964, + "NextMercuryGreatestElongation": 4702517423902486608, + "NextMercuryGreatestElongationEast": 4702517423902486608, + "NextMercuryGreatestElongationWest": 4702517507425936188, + "NextMercuryInferiorConjunction": 4702517463904041964, + "NextMercuryProgradeToRetrograde": 4702517443160895144, + "NextMercuryRetrograde": 4702517443160895144, + "NextMercuryRetrogradeToPrograde": 4702517485795666430, + "NextMercurySuperiorConjunction": 4702517607838368469 + } + }, + { + "input_utc": "1998-01-29T03:38:44.789Z", + "tt_jd_bits": 4702517555867789615, + "events": { + "LastMercuryConjunction": 4702517463904042080, + "LastMercuryGreatestElongation": 4702517507425936266, + "LastMercuryGreatestElongationEast": 4702517423902486596, + "LastMercuryGreatestElongationWest": 4702517507425936266, + "LastMercuryInferiorConjunction": 4702517463904042080, + "LastMercuryProgradeToRetrograde": 4702517443160895144, + "LastMercuryRetrograde": 4702517485795666430, + "LastMercuryRetrogradeToPrograde": 4702517485795666430, + "LastMercurySuperiorConjunction": 4702517325494165777, + "NextMercuryConjunction": 4702517607838368419, + "NextMercuryGreatestElongation": 4702517663264508198, + "NextMercuryGreatestElongationEast": 4702517663264508198, + "NextMercuryGreatestElongationWest": 4702517761050820392, + "NextMercuryInferiorConjunction": 4702517700904711919, + "NextMercuryProgradeToRetrograde": 4702517679268486973, + "NextMercuryRetrograde": 4702517679268486973, + "NextMercuryRetrogradeToPrograde": 4702517727533323045, + "NextMercurySuperiorConjunction": 4702517607838368419 + } + }, + { + "input_utc": "1998-04-28T09:39:03.789Z", + "tt_jd_bits": 4702517747531177447, + "events": { + "LastMercuryConjunction": 4702517700904711832, + "LastMercuryGreatestElongation": 4702517663264508240, + "LastMercuryGreatestElongationEast": 4702517663264508240, + "LastMercuryGreatestElongationWest": 4702517507425936256, + "LastMercuryInferiorConjunction": 4702517700904711832, + "LastMercuryProgradeToRetrograde": 4702517679268486973, + "LastMercuryRetrograde": 4702517727533323045, + "LastMercuryRetrogradeToPrograde": 4702517727533323045, + "LastMercurySuperiorConjunction": 4702517607838368329, + "NextMercuryConjunction": 4702517839629279419, + "NextMercuryGreatestElongation": 4702517761050820404, + "NextMercuryGreatestElongationEast": 4702517918758347212, + "NextMercuryGreatestElongationWest": 4702517761050820404, + "NextMercuryInferiorConjunction": 4702517978575629873, + "NextMercuryProgradeToRetrograde": 4702517946845516795, + "NextMercuryRetrograde": 4702517946845516795, + "NextMercuryRetrogradeToPrograde": 4702517998394273966, + "NextMercurySuperiorConjunction": 4702517839629279419 + } + }, + { + "input_utc": "1998-07-26T16:44:22.789Z", + "tt_jd_bits": 4702517939291500304, + "events": { + "LastMercuryConjunction": 4702517839629279553, + "LastMercuryGreatestElongation": 4702517918758347208, + "LastMercuryGreatestElongationEast": 4702517918758347208, + "LastMercuryGreatestElongationWest": 4702517761050820410, + "LastMercuryInferiorConjunction": 4702517700904711794, + "LastMercuryProgradeToRetrograde": 4702517679268486973, + "LastMercuryRetrograde": 4702517727533323045, + "LastMercuryRetrogradeToPrograde": 4702517727533323045, + "LastMercurySuperiorConjunction": 4702517839629279553, + "NextMercuryConjunction": 4702517978575629869, + "NextMercuryGreatestElongation": 4702518015925175572, + "NextMercuryGreatestElongationEast": 4702518170532141288, + "NextMercuryGreatestElongationWest": 4702518015925175572, + "NextMercuryInferiorConjunction": 4702517978575629869, + "NextMercuryProgradeToRetrograde": 4702517946845516795, + "NextMercuryRetrograde": 4702517946845516795, + "NextMercuryRetrogradeToPrograde": 4702517998394273966, + "NextMercurySuperiorConjunction": 4702518070563236850 + } + }, + { + "input_utc": "1998-10-22T12:49:41.789Z", + "tt_jd_bits": 4702518127920076175, + "events": { + "LastMercuryConjunction": 4702518070563236928, + "LastMercuryGreatestElongation": 4702518015925175522, + "LastMercuryGreatestElongationEast": 4702517918758347318, + "LastMercuryGreatestElongationWest": 4702518015925175522, + "LastMercuryInferiorConjunction": 4702517978575629937, + "LastMercuryProgradeToRetrograde": 4702517946845516795, + "LastMercuryRetrograde": 4702517998394273966, + "LastMercuryRetrogradeToPrograde": 4702517998394273966, + "LastMercurySuperiorConjunction": 4702518070563236928, + "NextMercuryConjunction": 4702518214046535103, + "NextMercuryGreatestElongation": 4702518170532141378, + "NextMercuryGreatestElongationEast": 4702518170532141378, + "NextMercuryGreatestElongationWest": 4702518253821597554, + "NextMercuryInferiorConjunction": 4702518214046535103, + "NextMercuryProgradeToRetrograde": 4702518192496706607, + "NextMercuryRetrograde": 4702518192496706607, + "NextMercuryRetrogradeToPrograde": 4702518234697963901, + "NextMercurySuperiorConjunction": 4702518352733449492 + } + }, + { + "input_utc": "1999-01-19T19:55:00.789Z", + "tt_jd_bits": 4702518319680423887, + "events": { + "LastMercuryConjunction": 4702518214046535040, + "LastMercuryGreatestElongation": 4702518253821597550, + "LastMercuryGreatestElongationEast": 4702518170532141314, + "LastMercuryGreatestElongationWest": 4702518253821597550, + "LastMercuryInferiorConjunction": 4702518214046535040, + "LastMercuryProgradeToRetrograde": 4702518192496706607, + "LastMercuryRetrograde": 4702518234697963901, + "LastMercuryRetrogradeToPrograde": 4702518234697963901, + "LastMercurySuperiorConjunction": 4702518070563236913, + "NextMercuryConjunction": 4702518352733449488, + "NextMercuryGreatestElongation": 4702518411403125562, + "NextMercuryGreatestElongationEast": 4702518411403125562, + "NextMercuryGreatestElongationWest": 4702518506164242048, + "NextMercuryInferiorConjunction": 4702518446318833284, + "NextMercuryProgradeToRetrograde": 4702518425040582798, + "NextMercuryRetrograde": 4702518425040582798, + "NextMercuryRetrogradeToPrograde": 4702518473028257806, + "NextMercurySuperiorConjunction": 4702518352733449488 + } + }, + { + "input_utc": "1999-04-19T02:54:56.789Z", + "tt_jd_bits": 4702518511432718536, + "events": { + "LastMercuryConjunction": 4702518446318833378, + "LastMercuryGreatestElongation": 4702518506164242046, + "LastMercuryGreatestElongationEast": 4702518411403125528, + "LastMercuryGreatestElongationWest": 4702518506164242046, + "LastMercuryInferiorConjunction": 4702518446318833378, + "LastMercuryProgradeToRetrograde": 4702518425040582798, + "LastMercuryRetrograde": 4702518473028257806, + "LastMercuryRetrogradeToPrograde": 4702518473028257806, + "LastMercurySuperiorConjunction": 4702518352733449468, + "NextMercuryConjunction": 4702518590122894572, + "NextMercuryGreatestElongation": 4702518663561353252, + "NextMercuryGreatestElongationEast": 4702518663561353252, + "NextMercuryGreatestElongationWest": 4702518763692083092, + "NextMercuryInferiorConjunction": 4702518723037141660, + "NextMercuryProgradeToRetrograde": 4702518691858419375, + "NextMercuryRetrograde": 4702518691858419375, + "NextMercuryRetrogradeToPrograde": 4702518744556183974, + "NextMercurySuperiorConjunction": 4702518590122894572 + } + }, + { + "input_utc": "1999-07-17T10:00:15.789Z", + "tt_jd_bits": 4702518703193041393, + "events": { + "LastMercuryConjunction": 4702518590122894603, + "LastMercuryGreatestElongation": 4702518663561353312, + "LastMercuryGreatestElongationEast": 4702518663561353312, + "LastMercuryGreatestElongationWest": 4702518506164242078, + "LastMercuryInferiorConjunction": 4702518446318833317, + "LastMercuryProgradeToRetrograde": 4702518691858419375, + "LastMercuryRetrograde": 4702518691858419375, + "LastMercuryRetrogradeToPrograde": 4702518473028257806, + "LastMercurySuperiorConjunction": 4702518590122894603, + "NextMercuryConjunction": 4702518723037141592, + "NextMercuryGreatestElongation": 4702518763692083092, + "NextMercuryGreatestElongationEast": 4702518916888231954, + "NextMercuryGreatestElongationWest": 4702518763692083092, + "NextMercuryInferiorConjunction": 4702518723037141592, + "NextMercuryProgradeToRetrograde": 4702518941359740699, + "NextMercuryRetrograde": 4702518744556183974, + "NextMercuryRetrogradeToPrograde": 4702518744556183974, + "NextMercurySuperiorConjunction": 4702518817451684848 + } + }, + { + "input_utc": "1999-10-14T17:05:34.789Z", + "tt_jd_bits": 4702518894953364250, + "events": { + "LastMercuryConjunction": 4702518817451684916, + "LastMercuryGreatestElongation": 4702518763692083160, + "LastMercuryGreatestElongationEast": 4702518663561353266, + "LastMercuryGreatestElongationWest": 4702518763692083160, + "LastMercuryInferiorConjunction": 4702518723037141582, + "LastMercuryProgradeToRetrograde": 4702518691858419375, + "LastMercuryRetrograde": 4702518744556183974, + "LastMercuryRetrogradeToPrograde": 4702518744556183974, + "LastMercurySuperiorConjunction": 4702518817451684916, + "NextMercuryConjunction": 4702518964116923212, + "NextMercuryGreatestElongation": 4702518916888231966, + "NextMercuryGreatestElongationEast": 4702518916888231966, + "NextMercuryGreatestElongationWest": 4702519000861134744, + "NextMercuryInferiorConjunction": 4702518964116923212, + "NextMercuryProgradeToRetrograde": 4702518941359740699, + "NextMercuryRetrograde": 4702518941359740699, + "NextMercuryRetrogradeToPrograde": 4702518983648921716, + "NextMercurySuperiorConjunction": 4702519095403440173 + } + }, + { + "input_utc": "2000-01-10T13:10:53.789Z", + "tt_jd_bits": 4702519083581940121, + "events": { + "LastMercuryConjunction": 4702518964116923315, + "LastMercuryGreatestElongation": 4702519000861134740, + "LastMercuryGreatestElongationEast": 4702518916888231942, + "LastMercuryGreatestElongationWest": 4702519000861134740, + "LastMercuryInferiorConjunction": 4702518964116923315, + "LastMercuryProgradeToRetrograde": 4702518941359740699, + "LastMercuryRetrograde": 4702518983648921716, + "LastMercuryRetrogradeToPrograde": 4702518983648921716, + "LastMercurySuperiorConjunction": 4702518817451684941, + "NextMercuryConjunction": 4702519095403440170, + "NextMercuryGreatestElongation": 4702519159822379194, + "NextMercuryGreatestElongationEast": 4702519159822379194, + "NextMercuryGreatestElongationWest": 4702519251803219458, + "NextMercuryInferiorConjunction": 4702519193280296364, + "NextMercuryProgradeToRetrograde": 4702519172442321242, + "NextMercuryRetrograde": 4702519172442321242, + "NextMercuryRetrogradeToPrograde": 4702519219748409294, + "NextMercurySuperiorConjunction": 4702519095403440170 + } + }, + { + "input_utc": "2000-04-08T20:16:12.789Z", + "tt_jd_bits": 4702519275342262978, + "events": { + "LastMercuryConjunction": 4702519193280296412, + "LastMercuryGreatestElongation": 4702519251803219504, + "LastMercuryGreatestElongationEast": 4702519159822379114, + "LastMercuryGreatestElongationWest": 4702519251803219504, + "LastMercuryInferiorConjunction": 4702519193280296412, + "LastMercuryProgradeToRetrograde": 4702519172442321242, + "LastMercuryRetrograde": 4702519219748409294, + "LastMercuryRetrogradeToPrograde": 4702519219748409294, + "LastMercurySuperiorConjunction": 4702519095403440118, + "NextMercuryConjunction": 4702519340440977932, + "NextMercuryGreatestElongation": 4702519407882301248, + "NextMercuryGreatestElongationEast": 4702519407882301248, + "NextMercuryGreatestElongationWest": 4702519510579408246, + "NextMercuryInferiorConjunction": 4702519465689295655, + "NextMercuryProgradeToRetrograde": 4702519436384939955, + "NextMercuryRetrograde": 4702519436384939955, + "NextMercuryRetrogradeToPrograde": 4702519489175360650, + "NextMercurySuperiorConjunction": 4702519340440977932 + } + }, + { + "input_utc": "2000-07-07T03:21:31.789Z", + "tt_jd_bits": 4702519467102585836, + "events": { + "LastMercuryConjunction": 4702519465689295750, + "LastMercuryGreatestElongation": 4702519407882301342, + "LastMercuryGreatestElongationEast": 4702519407882301342, + "LastMercuryGreatestElongationWest": 4702519251803219420, + "LastMercuryInferiorConjunction": 4702519465689295750, + "LastMercuryProgradeToRetrograde": 4702519436384939955, + "LastMercuryRetrograde": 4702519436384939955, + "LastMercuryRetrogradeToPrograde": 4702519219748409294, + "LastMercurySuperiorConjunction": 4702519340440977952, + "NextMercuryConjunction": 4702519565682295644, + "NextMercuryGreatestElongation": 4702519510579408226, + "NextMercuryGreatestElongationEast": 4702519663143563514, + "NextMercuryGreatestElongationWest": 4702519510579408226, + "NextMercuryInferiorConjunction": 4702519713953422008, + "NextMercuryProgradeToRetrograde": 4702519689585860799, + "NextMercuryRetrograde": 4702519489175360650, + "NextMercuryRetrogradeToPrograde": 4702519489175360650, + "NextMercurySuperiorConjunction": 4702519565682295644 + } + }, + { + "input_utc": "2000-10-04T10:26:50.789Z", + "tt_jd_bits": 4702519658862908693, + "events": { + "LastMercuryConjunction": 4702519565682295727, + "LastMercuryGreatestElongation": 4702519510579408180, + "LastMercuryGreatestElongationEast": 4702519407882301310, + "LastMercuryGreatestElongationWest": 4702519510579408180, + "LastMercuryInferiorConjunction": 4702519465689295689, + "LastMercuryProgradeToRetrograde": 4702519436384939955, + "LastMercuryRetrograde": 4702519489175360650, + "LastMercuryRetrogradeToPrograde": 4702519489175360650, + "LastMercurySuperiorConjunction": 4702519565682295727, + "NextMercuryConjunction": 4702519713953421960, + "NextMercuryGreatestElongation": 4702519663143563466, + "NextMercuryGreatestElongationEast": 4702519663143563466, + "NextMercuryGreatestElongationWest": 4702519748601407724, + "NextMercuryInferiorConjunction": 4702519713953421960, + "NextMercuryProgradeToRetrograde": 4702519689585860799, + "NextMercuryRetrograde": 4702519689585860799, + "NextMercuryRetrogradeToPrograde": 4702519732580126465, + "NextMercurySuperiorConjunction": 4702519835754627438 + } + }, + { + "input_utc": "2001-01-01T17:32:09.789Z", + "tt_jd_bits": 4702519850623231550, + "events": { + "LastMercuryConjunction": 4702519835754627416, + "LastMercuryGreatestElongation": 4702519748601407702, + "LastMercuryGreatestElongationEast": 4702519663143563512, + "LastMercuryGreatestElongationWest": 4702519748601407702, + "LastMercuryInferiorConjunction": 4702519713953421938, + "LastMercuryProgradeToRetrograde": 4702519689585860799, + "LastMercuryRetrograde": 4702519732580126465, + "LastMercuryRetrogradeToPrograde": 4702519732580126465, + "LastMercurySuperiorConjunction": 4702519835754627416, + "NextMercuryConjunction": 4702519941419676376, + "NextMercuryGreatestElongation": 4702519908254178580, + "NextMercuryGreatestElongationEast": 4702519908254178580, + "NextMercuryGreatestElongationWest": 4702519997780906794, + "NextMercuryInferiorConjunction": 4702519941419676376, + "NextMercuryProgradeToRetrograde": 4702519920986224858, + "NextMercuryRetrograde": 4702519920986224858, + "NextMercuryRetrogradeToPrograde": 4702519967286591545, + "NextMercurySuperiorConjunction": 4702520090412487082 + } + }, + { + "input_utc": "2001-03-30T13:37:28.789Z", + "tt_jd_bits": 4702520039251807421, + "events": { + "LastMercuryConjunction": 4702519941419676247, + "LastMercuryGreatestElongation": 4702519997780906810, + "LastMercuryGreatestElongationEast": 4702519908254178580, + "LastMercuryGreatestElongationWest": 4702519997780906810, + "LastMercuryInferiorConjunction": 4702519941419676247, + "LastMercuryProgradeToRetrograde": 4702519920986224858, + "LastMercuryRetrograde": 4702519967286591545, + "LastMercuryRetrogradeToPrograde": 4702519967286591545, + "LastMercurySuperiorConjunction": 4702519835754627313, + "NextMercuryConjunction": 4702520090412487098, + "NextMercuryGreatestElongation": 4702520152254564760, + "NextMercuryGreatestElongationEast": 4702520152254564760, + "NextMercuryGreatestElongationWest": 4702520256453370960, + "NextMercuryInferiorConjunction": 4702520206736692052, + "NextMercuryProgradeToRetrograde": 4702520180247003822, + "NextMercuryRetrograde": 4702520180247003822, + "NextMercuryRetrogradeToPrograde": 4702520231917627674, + "NextMercurySuperiorConjunction": 4702520090412487098 + } + }, + { + "input_utc": "2001-06-27T19:37:47.789Z", + "tt_jd_bits": 4702520230915195253, + "events": { + "LastMercuryConjunction": 4702520206736692111, + "LastMercuryGreatestElongation": 4702520152254564692, + "LastMercuryGreatestElongationEast": 4702520152254564692, + "LastMercuryGreatestElongationWest": 4702519997780906864, + "LastMercuryInferiorConjunction": 4702520206736692111, + "LastMercuryProgradeToRetrograde": 4702520180247003822, + "LastMercuryRetrograde": 4702520180247003822, + "LastMercuryRetrogradeToPrograde": 4702519967286591545, + "LastMercurySuperiorConjunction": 4702520090412486953, + "NextMercuryConjunction": 4702520314863960852, + "NextMercuryGreatestElongation": 4702520256453370936, + "NextMercuryGreatestElongationEast": 4702520409403191654, + "NextMercuryGreatestElongationWest": 4702520256453370936, + "NextMercuryInferiorConjunction": 4702520463385815190, + "NextMercuryProgradeToRetrograde": 4702520437091209095, + "NextMercuryRetrograde": 4702520231917627674, + "NextMercuryRetrogradeToPrograde": 4702520231917627674, + "NextMercurySuperiorConjunction": 4702520314863960852 + } + }, + { + "input_utc": "2001-09-25T02:43:06.789Z", + "tt_jd_bits": 4702520422675518110, + "events": { + "LastMercuryConjunction": 4702520314863961016, + "LastMercuryGreatestElongation": 4702520409403191666, + "LastMercuryGreatestElongationEast": 4702520409403191666, + "LastMercuryGreatestElongationWest": 4702520256453370972, + "LastMercuryInferiorConjunction": 4702520206736692178, + "LastMercuryProgradeToRetrograde": 4702520180247003822, + "LastMercuryRetrograde": 4702520231917627674, + "LastMercuryRetrogradeToPrograde": 4702520231917627674, + "LastMercurySuperiorConjunction": 4702520314863961016, + "NextMercuryConjunction": 4702520463385815202, + "NextMercuryGreatestElongation": 4702520496921898298, + "NextMercuryGreatestElongationEast": 4702520656483145714, + "NextMercuryGreatestElongationWest": 4702520496921898298, + "NextMercuryInferiorConjunction": 4702520463385815202, + "NextMercuryProgradeToRetrograde": 4702520437091209095, + "NextMercuryRetrograde": 4702520437091209095, + "NextMercuryRetrogradeToPrograde": 4702520481419358818, + "NextMercurySuperiorConjunction": 4702520574687095389 + } + }, + { + "input_utc": "2001-12-23T09:48:25.789Z", + "tt_jd_bits": 4702520614435840967, + "events": { + "LastMercuryConjunction": 4702520574687095521, + "LastMercuryGreatestElongation": 4702520496921898302, + "LastMercuryGreatestElongationEast": 4702520409403191670, + "LastMercuryGreatestElongationWest": 4702520496921898302, + "LastMercuryInferiorConjunction": 4702520463385815291, + "LastMercuryProgradeToRetrograde": 4702520437091209095, + "LastMercuryRetrograde": 4702520481419358818, + "LastMercuryRetrogradeToPrograde": 4702520481419358818, + "LastMercurySuperiorConjunction": 4702520574687095521, + "NextMercuryConjunction": 4702520690411081545, + "NextMercuryGreatestElongation": 4702520656483145778, + "NextMercuryGreatestElongationEast": 4702520656483145778, + "NextMercuryGreatestElongationWest": 4702520743848930254, + "NextMercuryInferiorConjunction": 4702520690411081545, + "NextMercuryProgradeToRetrograde": 4702520670223798773, + "NextMercuryRetrograde": 4702520670223798773, + "NextMercuryRetrogradeToPrograde": 4702520715381633727, + "NextMercurySuperiorConjunction": 4702520839839903284 + } + }, + { + "input_utc": "2002-03-22T16:53:44.789Z", + "tt_jd_bits": 4702520806196163825, + "events": { + "LastMercuryConjunction": 4702520690411081611, + "LastMercuryGreatestElongation": 4702520743848930268, + "LastMercuryGreatestElongationEast": 4702520656483145716, + "LastMercuryGreatestElongationWest": 4702520743848930268, + "LastMercuryInferiorConjunction": 4702520690411081611, + "LastMercuryProgradeToRetrograde": 4702520670223798773, + "LastMercuryRetrograde": 4702520715381633727, + "LastMercuryRetrogradeToPrograde": 4702520715381633727, + "LastMercurySuperiorConjunction": 4702520574687095461, + "NextMercuryConjunction": 4702520839839903298, + "NextMercuryGreatestElongation": 4702520897355592188, + "NextMercuryGreatestElongationEast": 4702520897355592188, + "NextMercuryGreatestElongationWest": 4702521001401327410, + "NextMercuryInferiorConjunction": 4702520947056957228, + "NextMercuryProgradeToRetrograde": 4702520923220166780, + "NextMercuryRetrograde": 4702520923220166780, + "NextMercuryRetrogradeToPrograde": 4702520973149683216, + "NextMercurySuperiorConjunction": 4702520839839903298 + } + }, + { + "input_utc": "2002-06-18T12:59:03.789Z", + "tt_jd_bits": 4702520994824739695, + "events": { + "LastMercuryConjunction": 4702520947056957193, + "LastMercuryGreatestElongation": 4702520897355592154, + "LastMercuryGreatestElongationEast": 4702520897355592154, + "LastMercuryGreatestElongationWest": 4702520743848930290, + "LastMercuryInferiorConjunction": 4702520947056957193, + "LastMercuryProgradeToRetrograde": 4702520923220166780, + "LastMercuryRetrograde": 4702520973149683216, + "LastMercuryRetrogradeToPrograde": 4702520973149683216, + "LastMercurySuperiorConjunction": 4702520839839903267, + "NextMercuryConjunction": 4702521064688048507, + "NextMercuryGreatestElongation": 4702521001401327412, + "NextMercuryGreatestElongationEast": 4702521155652682328, + "NextMercuryGreatestElongationWest": 4702521001401327412, + "NextMercuryInferiorConjunction": 4702521212214220604, + "NextMercuryProgradeToRetrograde": 4702521183874806444, + "NextMercuryRetrograde": 4702521183874806444, + "NextMercuryRetrogradeToPrograde": 4702521230067431482, + "NextMercurySuperiorConjunction": 4702521064688048507 + } + }, + { + "input_utc": "2002-09-15T20:04:22.789Z", + "tt_jd_bits": 4702521186585062553, + "events": { + "LastMercuryConjunction": 4702521064688048563, + "LastMercuryGreatestElongation": 4702521155652682330, + "LastMercuryGreatestElongationEast": 4702521155652682330, + "LastMercuryGreatestElongationWest": 4702521001401327340, + "LastMercuryInferiorConjunction": 4702520947056957261, + "LastMercuryProgradeToRetrograde": 4702521183874806444, + "LastMercuryRetrograde": 4702521183874806444, + "LastMercuryRetrogradeToPrograde": 4702520973149683216, + "LastMercurySuperiorConjunction": 4702521064688048563, + "NextMercuryConjunction": 4702521212214220607, + "NextMercuryGreatestElongation": 4702521245595004408, + "NextMercuryGreatestElongationEast": 4702521404334668772, + "NextMercuryGreatestElongationWest": 4702521245595004408, + "NextMercuryInferiorConjunction": 4702521212214220607, + "NextMercuryProgradeToRetrograde": 4702521419810272902, + "NextMercuryRetrograde": 4702521230067431482, + "NextMercuryRetrogradeToPrograde": 4702521230067431482, + "NextMercurySuperiorConjunction": 4702521314052929330 + } + }, + { + "input_utc": "2002-12-14T03:09:41.789Z", + "tt_jd_bits": 4702521378345385410, + "events": { + "LastMercuryConjunction": 4702521314052929372, + "LastMercuryGreatestElongation": 4702521245595004450, + "LastMercuryGreatestElongationEast": 4702521155652682264, + "LastMercuryGreatestElongationWest": 4702521245595004450, + "LastMercuryInferiorConjunction": 4702521212214220662, + "LastMercuryProgradeToRetrograde": 4702521183874806444, + "LastMercuryRetrograde": 4702521230067431482, + "LastMercuryRetrogradeToPrograde": 4702521230067431482, + "LastMercurySuperiorConjunction": 4702521314052929372, + "NextMercuryConjunction": 4702521439983305760, + "NextMercuryGreatestElongation": 4702521404334668710, + "NextMercuryGreatestElongationEast": 4702521404334668710, + "NextMercuryGreatestElongationWest": 4702521489825413960, + "NextMercuryInferiorConjunction": 4702521439983305760, + "NextMercuryProgradeToRetrograde": 4702521419810272902, + "NextMercuryRetrograde": 4702521419810272902, + "NextMercuryRetrogradeToPrograde": 4702521463851076111, + "NextMercurySuperiorConjunction": 4702521588476144098 + } + }, + { + "input_utc": "2003-03-13T10:15:00.789Z", + "tt_jd_bits": 4702521570105708267, + "events": { + "LastMercuryConjunction": 4702521439983305861, + "LastMercuryGreatestElongation": 4702521489825413872, + "LastMercuryGreatestElongationEast": 4702521404334668810, + "LastMercuryGreatestElongationWest": 4702521489825413872, + "LastMercuryInferiorConjunction": 4702521439983305861, + "LastMercuryProgradeToRetrograde": 4702521419810272902, + "LastMercuryRetrograde": 4702521463851076111, + "LastMercuryRetrogradeToPrograde": 4702521463851076111, + "LastMercurySuperiorConjunction": 4702521314052929439, + "NextMercuryConjunction": 4702521588476144139, + "NextMercuryGreatestElongation": 4702521643511939716, + "NextMercuryGreatestElongationEast": 4702521643511939716, + "NextMercuryGreatestElongationWest": 4702521745789891698, + "NextMercuryInferiorConjunction": 4702521687955545124, + "NextMercuryProgradeToRetrograde": 4702521665590479634, + "NextMercuryRetrograde": 4702521665590479634, + "NextMercuryRetrogradeToPrograde": 4702521714351390434, + "NextMercurySuperiorConjunction": 4702521588476144139 + } + }, + { + "input_utc": "2003-06-10T17:14:56.789Z", + "tt_jd_bits": 4702521761858002916, + "events": { + "LastMercuryConjunction": 4702521687955545214, + "LastMercuryGreatestElongation": 4702521745789891680, + "LastMercuryGreatestElongationEast": 4702521643511939806, + "LastMercuryGreatestElongationWest": 4702521745789891680, + "LastMercuryInferiorConjunction": 4702521687955545214, + "LastMercuryProgradeToRetrograde": 4702521665590479634, + "LastMercuryRetrograde": 4702521714351390434, + "LastMercuryRetrogradeToPrograde": 4702521714351390434, + "LastMercurySuperiorConjunction": 4702521588476144024, + "NextMercuryConjunction": 4702521814925424876, + "NextMercuryGreatestElongation": 4702521901774283382, + "NextMercuryGreatestElongationEast": 4702521901774283382, + "NextMercuryGreatestElongationWest": 4702521994365018574, + "NextMercuryInferiorConjunction": 4702521960203324412, + "NextMercuryProgradeToRetrograde": 4702521930002335803, + "NextMercuryRetrograde": 4702521930002335803, + "NextMercuryRetrogradeToPrograde": 4702521978382136928, + "NextMercurySuperiorConjunction": 4702521814925424876 + } + }, + { + "input_utc": "2003-09-06T13:20:15.789Z", + "tt_jd_bits": 4702521950486578787, + "events": { + "LastMercuryConjunction": 4702521814925425021, + "LastMercuryGreatestElongation": 4702521901774283392, + "LastMercuryGreatestElongationEast": 4702521901774283392, + "LastMercuryGreatestElongationWest": 4702521745789891696, + "LastMercuryInferiorConjunction": 4702521687955545111, + "LastMercuryProgradeToRetrograde": 4702521930002335803, + "LastMercuryRetrograde": 4702521930002335803, + "LastMercuryRetrogradeToPrograde": 4702521714351390434, + "LastMercurySuperiorConjunction": 4702521814925425021, + "NextMercuryConjunction": 4702521960203324422, + "NextMercuryGreatestElongation": 4702521994365018574, + "NextMercuryGreatestElongationEast": 4702522151708956862, + "NextMercuryGreatestElongationWest": 4702521994365018574, + "NextMercuryInferiorConjunction": 4702521960203324422, + "NextMercuryProgradeToRetrograde": 4702522169466554988, + "NextMercuryRetrograde": 4702521978382136928, + "NextMercuryRetrogradeToPrograde": 4702521978382136928, + "NextMercurySuperiorConjunction": 4702522055409762529 + } + }, + { + "input_utc": "2003-12-04T20:25:34.789Z", + "tt_jd_bits": 4702522142246901644, + "events": { + "LastMercuryConjunction": 4702522055409762598, + "LastMercuryGreatestElongation": 4702521994365018516, + "LastMercuryGreatestElongationEast": 4702521901774283436, + "LastMercuryGreatestElongationWest": 4702521994365018516, + "LastMercuryInferiorConjunction": 4702521960203324480, + "LastMercuryProgradeToRetrograde": 4702521930002335803, + "LastMercuryRetrograde": 4702521978382136928, + "LastMercuryRetrogradeToPrograde": 4702521978382136928, + "LastMercurySuperiorConjunction": 4702522055409762598, + "NextMercuryConjunction": 4702522189915637846, + "NextMercuryGreatestElongation": 4702522151708956856, + "NextMercuryGreatestElongationEast": 4702522151708956856, + "NextMercuryGreatestElongationWest": 4702522235743580436, + "NextMercuryInferiorConjunction": 4702522189915637846, + "NextMercuryProgradeToRetrograde": 4702522169466554988, + "NextMercuryRetrograde": 4702522169466554988, + "NextMercuryRetrogradeToPrograde": 4702522212551158753, + "NextMercurySuperiorConjunction": 4702522335992120764 + } + }, + { + "input_utc": "2004-03-03T03:30:53.789Z", + "tt_jd_bits": 4702522334007224502, + "events": { + "LastMercuryConjunction": 4702522189915637712, + "LastMercuryGreatestElongation": 4702522235743580454, + "LastMercuryGreatestElongationEast": 4702522151708956850, + "LastMercuryGreatestElongationWest": 4702522235743580454, + "LastMercuryInferiorConjunction": 4702522189915637712, + "LastMercuryProgradeToRetrograde": 4702522169466554988, + "LastMercuryRetrograde": 4702522212551158753, + "LastMercuryRetrogradeToPrograde": 4702522212551158753, + "LastMercurySuperiorConjunction": 4702522055409762666, + "NextMercuryConjunction": 4702522335992120782, + "NextMercuryGreatestElongation": 4702522390640058198, + "NextMercuryGreatestElongationEast": 4702522390640058198, + "NextMercuryGreatestElongationWest": 4702522490153670352, + "NextMercuryInferiorConjunction": 4702522430424477366, + "NextMercuryProgradeToRetrograde": 4702522408586812276, + "NextMercuryRetrograde": 4702522408586812276, + "NextMercuryRetrogradeToPrograde": 4702522456977718422, + "NextMercurySuperiorConjunction": 4702522335992120782 + } + }, + { + "input_utc": "2004-05-31T10:36:12.789Z", + "tt_jd_bits": 4702522525767547359, + "events": { + "LastMercuryConjunction": 4702522430424477305, + "LastMercuryGreatestElongation": 4702522490153670354, + "LastMercuryGreatestElongationEast": 4702522390640058136, + "LastMercuryGreatestElongationWest": 4702522490153670354, + "LastMercuryInferiorConjunction": 4702522430424477305, + "LastMercuryProgradeToRetrograde": 4702522408586812276, + "LastMercuryRetrograde": 4702522456977718422, + "LastMercuryRetrogradeToPrograde": 4702522456977718422, + "LastMercurySuperiorConjunction": 4702522335992120723, + "NextMercuryConjunction": 4702522565386837421, + "NextMercuryGreatestElongation": 4702522647535765244, + "NextMercuryGreatestElongationEast": 4702522647535765244, + "NextMercuryGreatestElongationWest": 4702522742967635430, + "NextMercuryInferiorConjunction": 4702522707070714012, + "NextMercuryProgradeToRetrograde": 4702522675597456406, + "NextMercuryRetrograde": 4702522675597456406, + "NextMercuryRetrogradeToPrograde": 4702522726137505141, + "NextMercurySuperiorConjunction": 4702522565386837421 + } + }, + { + "input_utc": "2004-08-28T16:36:31.789Z", + "tt_jd_bits": 4702522717430935190, + "events": { + "LastMercuryConjunction": 4702522707070713904, + "LastMercuryGreatestElongation": 4702522647535765136, + "LastMercuryGreatestElongationEast": 4702522647535765136, + "LastMercuryGreatestElongationWest": 4702522490153670318, + "LastMercuryInferiorConjunction": 4702522707070713904, + "LastMercuryProgradeToRetrograde": 4702522675597456406, + "LastMercuryRetrograde": 4702522675597456406, + "LastMercuryRetrogradeToPrograde": 4702522456977718422, + "LastMercurySuperiorConjunction": 4702522565386837450, + "NextMercuryConjunction": 4702522799201589164, + "NextMercuryGreatestElongation": 4702522742967635448, + "NextMercuryGreatestElongationEast": 4702522898592893762, + "NextMercuryGreatestElongationWest": 4702522742967635448, + "NextMercuryInferiorConjunction": 4702522940029241600, + "NextMercuryProgradeToRetrograde": 4702522918977809667, + "NextMercuryRetrograde": 4702522726137505141, + "NextMercuryRetrogradeToPrograde": 4702522726137505141, + "NextMercurySuperiorConjunction": 4702522799201589164 + } + }, + { + "input_utc": "2004-11-24T12:41:50.789Z", + "tt_jd_bits": 4702522906059511061, + "events": { + "LastMercuryConjunction": 4702522799201589295, + "LastMercuryGreatestElongation": 4702522898592893654, + "LastMercuryGreatestElongationEast": 4702522898592893654, + "LastMercuryGreatestElongationWest": 4702522742967635450, + "LastMercuryInferiorConjunction": 4702522707070713929, + "LastMercuryProgradeToRetrograde": 4702522675597456406, + "LastMercuryRetrograde": 4702522726137505141, + "LastMercuryRetrogradeToPrograde": 4702522726137505141, + "LastMercurySuperiorConjunction": 4702522799201589295, + "NextMercuryConjunction": 4702522940029241620, + "NextMercuryGreatestElongation": 4702522981882253766, + "NextMercuryGreatestElongationEast": 4702523138491350754, + "NextMercuryGreatestElongationWest": 4702522981882253766, + "NextMercuryInferiorConjunction": 4702522940029241620, + "NextMercuryProgradeToRetrograde": 4702522918977809667, + "NextMercuryRetrograde": 4702522918977809667, + "NextMercuryRetrogradeToPrograde": 4702522961395196926, + "NextMercurySuperiorConjunction": 4702523081984211833 + } + }, + { + "input_utc": "2005-02-21T19:47:09.789Z", + "tt_jd_bits": 4702523097819833918, + "events": { + "LastMercuryConjunction": 4702523081984211800, + "LastMercuryGreatestElongation": 4702522981882253732, + "LastMercuryGreatestElongationEast": 4702522898592893770, + "LastMercuryGreatestElongationWest": 4702522981882253732, + "LastMercuryInferiorConjunction": 4702522940029241586, + "LastMercuryProgradeToRetrograde": 4702522918977809667, + "LastMercuryRetrograde": 4702522961395196926, + "LastMercuryRetrogradeToPrograde": 4702522961395196926, + "LastMercurySuperiorConjunction": 4702523081984211800, + "NextMercuryConjunction": 4702523174805183914, + "NextMercuryGreatestElongation": 4702523138491350756, + "NextMercuryGreatestElongationEast": 4702523138491350756, + "NextMercuryGreatestElongationWest": 4702523234953642388, + "NextMercuryInferiorConjunction": 4702523174805183914, + "NextMercuryProgradeToRetrograde": 4702523153306081141, + "NextMercuryRetrograde": 4702523153306081141, + "NextMercuryRetrogradeToPrograde": 4702523201486035315, + "NextMercurySuperiorConjunction": 4702523315913890392 + } + }, + { + "input_utc": "2005-05-22T02:52:28.789Z", + "tt_jd_bits": 4702523289580156776, + "events": { + "LastMercuryConjunction": 4702523174805183874, + "LastMercuryGreatestElongation": 4702523234953642286, + "LastMercuryGreatestElongationEast": 4702523138491350716, + "LastMercuryGreatestElongationWest": 4702523234953642286, + "LastMercuryInferiorConjunction": 4702523174805183874, + "LastMercuryProgradeToRetrograde": 4702523153306081141, + "LastMercuryRetrograde": 4702523201486035314, + "LastMercuryRetrogradeToPrograde": 4702523201486035314, + "LastMercurySuperiorConjunction": 4702523081984211740, + "NextMercuryConjunction": 4702523315913890418, + "NextMercuryGreatestElongation": 4702523392698136440, + "NextMercuryGreatestElongationEast": 4702523392698136440, + "NextMercuryGreatestElongationWest": 4702523491122241464, + "NextMercuryInferiorConjunction": 4702523452494063578, + "NextMercuryProgradeToRetrograde": 4702523420799060905, + "NextMercuryRetrograde": 4702523420799060905, + "NextMercuryRetrogradeToPrograde": 4702523472995667966, + "NextMercurySuperiorConjunction": 4702523315913890418 + } + }, + { + "input_utc": "2005-08-19T09:57:47.789Z", + "tt_jd_bits": 4702523481340479633, + "events": { + "LastMercuryConjunction": 4702523452494063531, + "LastMercuryGreatestElongation": 4702523392698136394, + "LastMercuryGreatestElongationEast": 4702523392698136394, + "LastMercuryGreatestElongationWest": 4702523234953642390, + "LastMercuryInferiorConjunction": 4702523452494063531, + "LastMercuryProgradeToRetrograde": 4702523420799060905, + "LastMercuryRetrograde": 4702523472995667966, + "LastMercuryRetrogradeToPrograde": 4702523472995667966, + "LastMercurySuperiorConjunction": 4702523315913890373, + "NextMercuryConjunction": 4702523545107501438, + "NextMercuryGreatestElongation": 4702523491122241446, + "NextMercuryGreatestElongationEast": 4702523645074145618, + "NextMercuryGreatestElongationWest": 4702523491122241446, + "NextMercuryInferiorConjunction": 4702523690159185982, + "NextMercuryProgradeToRetrograde": 4702523668134652166, + "NextMercuryRetrograde": 4702523668134652166, + "NextMercuryRetrogradeToPrograde": 4702523710311192770, + "NextMercurySuperiorConjunction": 4702523545107501438 + } + }, + { + "input_utc": "2005-11-16T17:03:06.789Z", + "tt_jd_bits": 4702523673100802490, + "events": { + "LastMercuryConjunction": 4702523545107501524, + "LastMercuryGreatestElongation": 4702523645074145526, + "LastMercuryGreatestElongationEast": 4702523645074145526, + "LastMercuryGreatestElongationWest": 4702523491122241404, + "LastMercuryInferiorConjunction": 4702523452494063598, + "LastMercuryProgradeToRetrograde": 4702523668134652166, + "LastMercuryRetrograde": 4702523668134652166, + "LastMercuryRetrogradeToPrograde": 4702523472995667966, + "LastMercurySuperiorConjunction": 4702523545107501524, + "NextMercuryConjunction": 4702523690159186017, + "NextMercuryGreatestElongation": 4702523728544263720, + "NextMercuryGreatestElongationEast": 4702523886775128504, + "NextMercuryGreatestElongationWest": 4702523728544263720, + "NextMercuryInferiorConjunction": 4702523690159186017, + "NextMercuryProgradeToRetrograde": 4702523899843419575, + "NextMercuryRetrograde": 4702523710311192770, + "NextMercuryRetrogradeToPrograde": 4702523710311192770, + "NextMercurySuperiorConjunction": 4702523825973991015 + } + }, + { + "input_utc": "2006-02-12T13:08:25.789Z", + "tt_jd_bits": 4702523861729403216, + "events": { + "LastMercuryConjunction": 4702523825973990867, + "LastMercuryGreatestElongation": 4702523728544263700, + "LastMercuryGreatestElongationEast": 4702523645074145636, + "LastMercuryGreatestElongationWest": 4702523728544263700, + "LastMercuryInferiorConjunction": 4702523690159185901, + "LastMercuryProgradeToRetrograde": 4702523668134652166, + "LastMercuryRetrograde": 4702523710311192770, + "LastMercuryRetrogradeToPrograde": 4702523710311192770, + "LastMercurySuperiorConjunction": 4702523825973990867, + "NextMercuryConjunction": 4702523920925563227, + "NextMercuryGreatestElongation": 4702523886775128458, + "NextMercuryGreatestElongationEast": 4702523886775128458, + "NextMercuryGreatestElongationWest": 4702523980332486672, + "NextMercuryInferiorConjunction": 4702523920925563227, + "NextMercuryProgradeToRetrograde": 4702523899843419575, + "NextMercuryRetrograde": 4702523899843419575, + "NextMercuryRetrogradeToPrograde": 4702523947551534429, + "NextMercurySuperiorConjunction": 4702524066355577587 + } + }, + { + "input_utc": "2006-05-12T20:13:44.789Z", + "tt_jd_bits": 4702524053489726073, + "events": { + "LastMercuryConjunction": 4702523920925563260, + "LastMercuryGreatestElongation": 4702523980332486672, + "LastMercuryGreatestElongationEast": 4702523886775128492, + "LastMercuryGreatestElongationWest": 4702523980332486672, + "LastMercuryInferiorConjunction": 4702523920925563260, + "LastMercuryProgradeToRetrograde": 4702523899843419575, + "LastMercuryRetrograde": 4702523947551534429, + "LastMercuryRetrogradeToPrograde": 4702523947551534429, + "LastMercurySuperiorConjunction": 4702523825973990911, + "NextMercuryConjunction": 4702524066355577587, + "NextMercuryGreatestElongation": 4702524137235703298, + "NextMercuryGreatestElongationEast": 4702524137235703298, + "NextMercuryGreatestElongationWest": 4702524238557878240, + "NextMercuryInferiorConjunction": 4702524196196698187, + "NextMercuryProgradeToRetrograde": 4702524165658348239, + "NextMercuryRetrograde": 4702524165658348239, + "NextMercuryRetrogradeToPrograde": 4702524218537863389, + "NextMercurySuperiorConjunction": 4702524066355577587 + } + }, + { + "input_utc": "2006-08-10T03:19:03.789Z", + "tt_jd_bits": 4702524245250048931, + "events": { + "LastMercuryConjunction": 4702524196196698278, + "LastMercuryGreatestElongation": 4702524238557878236, + "LastMercuryGreatestElongationEast": 4702524137235703262, + "LastMercuryGreatestElongationWest": 4702524238557878236, + "LastMercuryInferiorConjunction": 4702524196196698278, + "LastMercuryProgradeToRetrograde": 4702524165658348239, + "LastMercuryRetrograde": 4702524218537863389, + "LastMercuryRetrogradeToPrograde": 4702524218537863389, + "LastMercurySuperiorConjunction": 4702524066355577641, + "NextMercuryConjunction": 4702524292627635227, + "NextMercuryGreatestElongation": 4702524391350451652, + "NextMercuryGreatestElongationEast": 4702524391350451652, + "NextMercuryGreatestElongationWest": 4702524475890356746, + "NextMercuryInferiorConjunction": 4702524440149509415, + "NextMercuryProgradeToRetrograde": 4702524416759440289, + "NextMercuryRetrograde": 4702524416759440289, + "NextMercuryRetrogradeToPrograde": 4702524459255770639, + "NextMercurySuperiorConjunction": 4702524292627635227 + } + } + ] +} diff --git a/basic/testdata/semianalytic/neptune_event_baseline.json b/basic/testdata/semianalytic/neptune_event_baseline.json new file mode 100644 index 0000000..2fa165d --- /dev/null +++ b/basic/testdata/semianalytic/neptune_event_baseline.json @@ -0,0 +1,1732 @@ +{ + "samples": [ + { + "input_utc": "1996-04-17T03:14:15.926Z", + "tt_jd_bits": 4702516155671917476, + "events": { + "LastNeptuneConjunction": 4702515958071380307, + "LastNeptuneEasternQuadrature": 4702515761670859909, + "LastNeptuneOpposition": 4702515565243596299, + "LastNeptuneProgradeToRetrograde": 4702515392761860989, + "LastNeptuneRetrogradeToPrograde": 4702515736655826340, + "LastNeptuneWesternQuadrature": 4702515367174517548, + "NextNeptuneConjunction": 4702516747062911378, + "NextNeptuneEasternQuadrature": 4702516550781582515, + "NextNeptuneOpposition": 4702516354552986371, + "NextNeptuneProgradeToRetrograde": 4702516181956450581, + "NextNeptuneRetrogradeToPrograde": 4702516525855821720, + "NextNeptuneWesternQuadrature": 4702516156366194663 + } + }, + { + "input_utc": "1996-12-14T06:31:22.926Z", + "tt_jd_bits": 4702516673509438324, + "events": { + "LastNeptuneConjunction": 4702515958071380307, + "LastNeptuneEasternQuadrature": 4702516550781582515, + "LastNeptuneOpposition": 4702516354552986371, + "LastNeptuneProgradeToRetrograde": 4702516181956450581, + "LastNeptuneRetrogradeToPrograde": 4702516525855821720, + "LastNeptuneWesternQuadrature": 4702516156366194663, + "NextNeptuneConjunction": 4702516747062911378, + "NextNeptuneEasternQuadrature": 4702517339906805250, + "NextNeptuneOpposition": 4702517143871817437, + "NextNeptuneProgradeToRetrograde": 4702516971321732963, + "NextNeptuneRetrogradeToPrograde": 4702517314847535212, + "NextNeptuneWesternQuadrature": 4702516945567756320 + } + }, + { + "input_utc": "1997-08-12T09:48:29.926Z", + "tt_jd_bits": 4702517191346984026, + "events": { + "LastNeptuneConjunction": 4702516747062911378, + "LastNeptuneEasternQuadrature": 4702516550781582515, + "LastNeptuneOpposition": 4702517143871817437, + "LastNeptuneProgradeToRetrograde": 4702516971321732963, + "LastNeptuneRetrogradeToPrograde": 4702516525855821720, + "LastNeptuneWesternQuadrature": 4702516945567756320, + "NextNeptuneConjunction": 4702517536084450596, + "NextNeptuneEasternQuadrature": 4702517339906805250, + "NextNeptuneOpposition": 4702517933168137145, + "NextNeptuneProgradeToRetrograde": 4702517760492452126, + "NextNeptuneRetrogradeToPrograde": 4702517314847535212, + "NextNeptuneWesternQuadrature": 4702517734779825816 + } + }, + { + "input_utc": "1998-04-10T13:05:36.926Z", + "tt_jd_bits": 4702517709184504873, + "events": { + "LastNeptuneConjunction": 4702517536084450596, + "LastNeptuneEasternQuadrature": 4702517339906805250, + "LastNeptuneOpposition": 4702517143871817437, + "LastNeptuneProgradeToRetrograde": 4702516971321732963, + "LastNeptuneRetrogradeToPrograde": 4702517314847535212, + "LastNeptuneWesternQuadrature": 4702516945567756320, + "NextNeptuneConjunction": 4702518325087993501, + "NextNeptuneEasternQuadrature": 4702518129008816070, + "NextNeptuneOpposition": 4702517933168137145, + "NextNeptuneProgradeToRetrograde": 4702517760492452126, + "NextNeptuneRetrogradeToPrograde": 4702518104106338544, + "NextNeptuneWesternQuadrature": 4702517734779825816 + } + }, + { + "input_utc": "1998-12-07T16:22:43.926Z", + "tt_jd_bits": 4702518227022025721, + "events": { + "LastNeptuneConjunction": 4702517536084450596, + "LastNeptuneEasternQuadrature": 4702518129008816070, + "LastNeptuneOpposition": 4702517933168137145, + "LastNeptuneProgradeToRetrograde": 4702517760492452126, + "LastNeptuneRetrogradeToPrograde": 4702518104106338544, + "LastNeptuneWesternQuadrature": 4702517734779825816, + "NextNeptuneConjunction": 4702518325087993501, + "NextNeptuneEasternQuadrature": 4702518918119524345, + "NextNeptuneOpposition": 4702518722476969087, + "NextNeptuneProgradeToRetrograde": 4702518549926145855, + "NextNeptuneRetrogradeToPrograde": 4702518893288503608, + "NextNeptuneWesternQuadrature": 4702518523991065624 + } + }, + { + "input_utc": "1999-08-05T19:39:50.926Z", + "tt_jd_bits": 4702518744859571423, + "events": { + "LastNeptuneConjunction": 4702518325087993501, + "LastNeptuneEasternQuadrature": 4702518129008816070, + "LastNeptuneOpposition": 4702518722476969087, + "LastNeptuneProgradeToRetrograde": 4702518549926145855, + "LastNeptuneRetrogradeToPrograde": 4702518104106338552, + "LastNeptuneWesternQuadrature": 4702518523991065624, + "NextNeptuneConjunction": 4702519114088694186, + "NextNeptuneEasternQuadrature": 4702518918119524345, + "NextNeptuneOpposition": 4702519511791041747, + "NextNeptuneProgradeToRetrograde": 4702519339132037758, + "NextNeptuneRetrogradeToPrograde": 4702518893288503608, + "NextNeptuneWesternQuadrature": 4702519313217627433 + } + }, + { + "input_utc": "2000-04-02T22:56:57.926Z", + "tt_jd_bits": 4702519262697092271, + "events": { + "LastNeptuneConjunction": 4702519114088694186, + "LastNeptuneEasternQuadrature": 4702518918119524345, + "LastNeptuneOpposition": 4702518722476969087, + "LastNeptuneProgradeToRetrograde": 4702518549926145855, + "LastNeptuneRetrogradeToPrograde": 4702518893288503608, + "LastNeptuneWesternQuadrature": 4702518523991065624, + "NextNeptuneConjunction": 4702519903089547967, + "NextNeptuneEasternQuadrature": 4702519707219408360, + "NextNeptuneOpposition": 4702519511791041747, + "NextNeptuneProgradeToRetrograde": 4702519339132037758, + "NextNeptuneRetrogradeToPrograde": 4702519682565794317, + "NextNeptuneWesternQuadrature": 4702519313217627433 + } + }, + { + "input_utc": "2000-11-30T02:14:04.926Z", + "tt_jd_bits": 4702519780534613118, + "events": { + "LastNeptuneConjunction": 4702519114088694186, + "LastNeptuneEasternQuadrature": 4702519707219408360, + "LastNeptuneOpposition": 4702519511791041747, + "LastNeptuneProgradeToRetrograde": 4702519339132037758, + "LastNeptuneRetrogradeToPrograde": 4702519682565794317, + "LastNeptuneWesternQuadrature": 4702519313217627433, + "NextNeptuneConjunction": 4702519903089547967, + "NextNeptuneEasternQuadrature": 4702520496296693610, + "NextNeptuneOpposition": 4702520301080043651, + "NextNeptuneProgradeToRetrograde": 4702520128437936923, + "NextNeptuneRetrogradeToPrograde": 4702520471756344834, + "NextNeptuneWesternQuadrature": 4702520102439370105 + } + }, + { + "input_utc": "2001-07-29T03:15:11.926Z", + "tt_jd_bits": 4702520298169316066, + "events": { + "LastNeptuneConjunction": 4702519903089547967, + "LastNeptuneEasternQuadrature": 4702519707219408360, + "LastNeptuneOpposition": 4702519511791041747, + "LastNeptuneProgradeToRetrograde": 4702520128437936923, + "LastNeptuneRetrogradeToPrograde": 4702519682565794317, + "LastNeptuneWesternQuadrature": 4702520102439370105, + "NextNeptuneConjunction": 4702520692096778935, + "NextNeptuneEasternQuadrature": 4702520496296693610, + "NextNeptuneOpposition": 4702520301080043651, + "NextNeptuneProgradeToRetrograde": 4702520917586086820, + "NextNeptuneRetrogradeToPrograde": 4702520471756344834, + "NextNeptuneWesternQuadrature": 4702520891671301874 + } + }, + { + "input_utc": "2002-03-27T06:32:18.926Z", + "tt_jd_bits": 4702520816006836913, + "events": { + "LastNeptuneConjunction": 4702520692096778935, + "LastNeptuneEasternQuadrature": 4702520496296693610, + "LastNeptuneOpposition": 4702520301080043651, + "LastNeptuneProgradeToRetrograde": 4702520128437936923, + "LastNeptuneRetrogradeToPrograde": 4702520471756344834, + "LastNeptuneWesternQuadrature": 4702520102439370105, + "NextNeptuneConjunction": 4702521481101967327, + "NextNeptuneEasternQuadrature": 4702521285385325603, + "NextNeptuneOpposition": 4702521090382580029, + "NextNeptuneProgradeToRetrograde": 4702520917586086820, + "NextNeptuneRetrogradeToPrograde": 4702521260977684764, + "NextNeptuneWesternQuadrature": 4702520891671301874 + } + }, + { + "input_utc": "2002-11-22T03:49:25.926Z", + "tt_jd_bits": 4702521331160003201, + "events": { + "LastNeptuneConjunction": 4702520692096778935, + "LastNeptuneEasternQuadrature": 4702521285385325603, + "LastNeptuneOpposition": 4702521090382580029, + "LastNeptuneProgradeToRetrograde": 4702520917586086820, + "LastNeptuneRetrogradeToPrograde": 4702521260977684764, + "LastNeptuneWesternQuadrature": 4702520891671301874, + "NextNeptuneConjunction": 4702521481101967327, + "NextNeptuneEasternQuadrature": 4702522074463753269, + "NextNeptuneOpposition": 4702521879668940838, + "NextNeptuneProgradeToRetrograde": 4702521706894567270, + "NextNeptuneRetrogradeToPrograde": 4702522050196817960, + "NextNeptuneWesternQuadrature": 4702521680882756032 + } + }, + { + "input_utc": "2003-07-21T07:06:32.926Z", + "tt_jd_bits": 4702521848997524048, + "events": { + "LastNeptuneConjunction": 4702521481101967327, + "LastNeptuneEasternQuadrature": 4702521285385325603, + "LastNeptuneOpposition": 4702521090382580029, + "LastNeptuneProgradeToRetrograde": 4702521706894567270, + "LastNeptuneRetrogradeToPrograde": 4702521260977684764, + "LastNeptuneWesternQuadrature": 4702521680882756032, + "NextNeptuneConjunction": 4702522270114869139, + "NextNeptuneEasternQuadrature": 4702522074463753269, + "NextNeptuneOpposition": 4702521879668940838, + "NextNeptuneProgradeToRetrograde": 4702522496094650577, + "NextNeptuneRetrogradeToPrograde": 4702522050196817960, + "NextNeptuneWesternQuadrature": 4702522470129484093 + } + }, + { + "input_utc": "2004-03-18T10:23:39.926Z", + "tt_jd_bits": 4702522366835044895, + "events": { + "LastNeptuneConjunction": 4702522270114869139, + "LastNeptuneEasternQuadrature": 4702522074463753269, + "LastNeptuneOpposition": 4702521879668940838, + "LastNeptuneProgradeToRetrograde": 4702521706894567270, + "LastNeptuneRetrogradeToPrograde": 4702522050196817960, + "LastNeptuneWesternQuadrature": 4702521680882756032, + "NextNeptuneConjunction": 4702523059136438537, + "NextNeptuneEasternQuadrature": 4702522863544502714, + "NextNeptuneOpposition": 4702522668978216343, + "NextNeptuneProgradeToRetrograde": 4702522496094650577, + "NextNeptuneRetrogradeToPrograde": 4702522839255752990, + "NextNeptuneWesternQuadrature": 4702522470129484093 + } + }, + { + "input_utc": "2004-11-14T13:40:46.926Z", + "tt_jd_bits": 4702522884672565743, + "events": { + "LastNeptuneConjunction": 4702522270114869139, + "LastNeptuneEasternQuadrature": 4702522863544502714, + "LastNeptuneOpposition": 4702522668978216343, + "LastNeptuneProgradeToRetrograde": 4702522496094650577, + "LastNeptuneRetrogradeToPrograde": 4702522839255752990, + "LastNeptuneWesternQuadrature": 4702522470129484093, + "NextNeptuneConjunction": 4702523059136438537, + "NextNeptuneEasternQuadrature": 4702523652620616787, + "NextNeptuneOpposition": 4702523458272842065, + "NextNeptuneProgradeToRetrograde": 4702523285307963998, + "NextNeptuneRetrogradeToPrograde": 4702523628444177261, + "NextNeptuneWesternQuadrature": 4702523259384907442 + } + }, + { + "input_utc": "2005-07-13T16:57:53.926Z", + "tt_jd_bits": 4702523402510086590, + "events": { + "LastNeptuneConjunction": 4702523059136438537, + "LastNeptuneEasternQuadrature": 4702522863544502714, + "LastNeptuneOpposition": 4702522668978216343, + "LastNeptuneProgradeToRetrograde": 4702523285307963998, + "LastNeptuneRetrogradeToPrograde": 4702522839255752990, + "LastNeptuneWesternQuadrature": 4702523259384907442, + "NextNeptuneConjunction": 4702523848163442015, + "NextNeptuneEasternQuadrature": 4702523652620616787, + "NextNeptuneOpposition": 4702523458272842065, + "NextNeptuneProgradeToRetrograde": 4702524074698240946, + "NextNeptuneRetrogradeToPrograde": 4702523628444177261, + "NextNeptuneWesternQuadrature": 4702524048639023502 + } + }, + { + "input_utc": "2006-03-11T20:13:15.926Z", + "tt_jd_bits": 4702523920345022504, + "events": { + "LastNeptuneConjunction": 4702523848163442015, + "LastNeptuneEasternQuadrature": 4702523652620616787, + "LastNeptuneOpposition": 4702523458272842065, + "LastNeptuneProgradeToRetrograde": 4702523285307963998, + "LastNeptuneRetrogradeToPrograde": 4702523628444177261, + "LastNeptuneWesternQuadrature": 4702523259384907442, + "NextNeptuneConjunction": 4702524637213807171, + "NextNeptuneEasternQuadrature": 4702524441713748114, + "NextNeptuneOpposition": 4702524247567102982, + "NextNeptuneProgradeToRetrograde": 4702524074698240946, + "NextNeptuneRetrogradeToPrograde": 4702524417377850737, + "NextNeptuneWesternQuadrature": 4702524048639023502 + } + }, + { + "input_utc": "2006-11-07T21:14:22.926Z", + "tt_jd_bits": 4702524437979725451, + "events": { + "LastNeptuneConjunction": 4702523848163442015, + "LastNeptuneEasternQuadrature": 4702523652620616787, + "LastNeptuneOpposition": 4702524247567102982, + "LastNeptuneProgradeToRetrograde": 4702524074698240946, + "LastNeptuneRetrogradeToPrograde": 4702524417377850737, + "LastNeptuneWesternQuadrature": 4702524048639023502, + "NextNeptuneConjunction": 4702524637213807171, + "NextNeptuneEasternQuadrature": 4702524441713748114, + "NextNeptuneOpposition": 4702525036874120471, + "NextNeptuneProgradeToRetrograde": 4702524863967835003, + "NextNeptuneRetrogradeToPrograde": 4702525206640080377, + "NextNeptuneWesternQuadrature": 4702524837919357850 + } + }, + { + "input_utc": "2007-07-07T00:31:29.926Z", + "tt_jd_bits": 4702524955817246299, + "events": { + "LastNeptuneConjunction": 4702524637213807171, + "LastNeptuneEasternQuadrature": 4702524441713748114, + "LastNeptuneOpposition": 4702524247567102982, + "LastNeptuneProgradeToRetrograde": 4702524863967835003, + "LastNeptuneRetrogradeToPrograde": 4702524417377850737, + "LastNeptuneWesternQuadrature": 4702524837919357850, + "NextNeptuneConjunction": 4702525426251567580, + "NextNeptuneEasternQuadrature": 4702525230798332194, + "NextNeptuneOpposition": 4702525036874120471, + "NextNeptuneProgradeToRetrograde": 4702525653506879782, + "NextNeptuneRetrogradeToPrograde": 4702525206640080377, + "NextNeptuneWesternQuadrature": 4702525627200952187 + } + }, + { + "input_utc": "2008-03-04T03:48:36.926Z", + "tt_jd_bits": 4702525473654767146, + "events": { + "LastNeptuneConjunction": 4702525426251567580, + "LastNeptuneEasternQuadrature": 4702525230798332194, + "LastNeptuneOpposition": 4702525036874120471, + "LastNeptuneProgradeToRetrograde": 4702524863967835003, + "LastNeptuneRetrogradeToPrograde": 4702525206640080377, + "LastNeptuneWesternQuadrature": 4702524837919357850, + "NextNeptuneConjunction": 4702526215328445892, + "NextNeptuneEasternQuadrature": 4702526019893565391, + "NextNeptuneOpposition": 4702525826189598326, + "NextNeptuneProgradeToRetrograde": 4702525653506879782, + "NextNeptuneRetrogradeToPrograde": 4702525995775305543, + "NextNeptuneWesternQuadrature": 4702525627200952187 + } + }, + { + "input_utc": "2008-10-31T07:05:43.926Z", + "tt_jd_bits": 4702525991492287993, + "events": { + "LastNeptuneConjunction": 4702525426251567580, + "LastNeptuneEasternQuadrature": 4702525230798332194, + "LastNeptuneOpposition": 4702525826189598326, + "LastNeptuneProgradeToRetrograde": 4702525653506879782, + "LastNeptuneRetrogradeToPrograde": 4702525206640080377, + "LastNeptuneWesternQuadrature": 4702525627200952187, + "NextNeptuneConjunction": 4702526215328445892, + "NextNeptuneEasternQuadrature": 4702526019893565391, + "NextNeptuneOpposition": 4702526615497239269, + "NextNeptuneProgradeToRetrograde": 4702526442792282718, + "NextNeptuneRetrogradeToPrograde": 4702525995775305543, + "NextNeptuneWesternQuadrature": 4702526416513055336 + } + }, + { + "input_utc": "2009-06-28T04:22:50.926Z", + "tt_jd_bits": 4702526506645479136, + "events": { + "LastNeptuneConjunction": 4702526215328445892, + "LastNeptuneEasternQuadrature": 4702526019893565391, + "LastNeptuneOpposition": 4702525826189598326, + "LastNeptuneProgradeToRetrograde": 4702526442792282718, + "LastNeptuneRetrogradeToPrograde": 4702525995775305543, + "LastNeptuneWesternQuadrature": 4702526416513055336, + "NextNeptuneConjunction": 4702527004406607440, + "NextNeptuneEasternQuadrature": 4702526808973808598, + "NextNeptuneOpposition": 4702526615497239269, + "NextNeptuneProgradeToRetrograde": 4702527232266243818, + "NextNeptuneRetrogradeToPrograde": 4702526784989348563, + "NextNeptuneWesternQuadrature": 4702527205815798819 + } + }, + { + "input_utc": "2010-02-24T07:39:57.926Z", + "tt_jd_bits": 4702527024482999983, + "events": { + "LastNeptuneConjunction": 4702527004406607440, + "LastNeptuneEasternQuadrature": 4702526808973808598, + "LastNeptuneOpposition": 4702526615497239269, + "LastNeptuneProgradeToRetrograde": 4702526442792282718, + "LastNeptuneRetrogradeToPrograde": 4702526784989348563, + "LastNeptuneWesternQuadrature": 4702526416513055336, + "NextNeptuneConjunction": 4702527793483315868, + "NextNeptuneEasternQuadrature": 4702527598059113614, + "NextNeptuneOpposition": 4702527404804653731, + "NextNeptuneProgradeToRetrograde": 4702527232266243818, + "NextNeptuneRetrogradeToPrograde": 4702527574238083539, + "NextNeptuneWesternQuadrature": 4702527205815798819 + } + }, + { + "input_utc": "2010-10-23T10:57:04.926Z", + "tt_jd_bits": 4702527542320520831, + "events": { + "LastNeptuneConjunction": 4702527004406607440, + "LastNeptuneEasternQuadrature": 4702526808973808598, + "LastNeptuneOpposition": 4702527404804653731, + "LastNeptuneProgradeToRetrograde": 4702527232266243818, + "LastNeptuneRetrogradeToPrograde": 4702526784989348563, + "LastNeptuneWesternQuadrature": 4702527205815798819, + "NextNeptuneConjunction": 4702527793483315868, + "NextNeptuneEasternQuadrature": 4702527598059113614, + "NextNeptuneOpposition": 4702528194123267306, + "NextNeptuneProgradeToRetrograde": 4702528021597321662, + "NextNeptuneRetrogradeToPrograde": 4702527574238083539, + "NextNeptuneWesternQuadrature": 4702527995131216666 + } + }, + { + "input_utc": "2011-06-21T14:14:11.926Z", + "tt_jd_bits": 4702528060158041678, + "events": { + "LastNeptuneConjunction": 4702527793483315868, + "LastNeptuneEasternQuadrature": 4702527598059113614, + "LastNeptuneOpposition": 4702527404804653731, + "LastNeptuneProgradeToRetrograde": 4702528021597321662, + "LastNeptuneRetrogradeToPrograde": 4702527574238083539, + "LastNeptuneWesternQuadrature": 4702527995131216666, + "NextNeptuneConjunction": 4702528582571314390, + "NextNeptuneEasternQuadrature": 4702528387152357001, + "NextNeptuneOpposition": 4702528194123267306, + "NextNeptuneProgradeToRetrograde": 4702528811012343032, + "NextNeptuneRetrogradeToPrograde": 4702528363569912197, + "NextNeptuneWesternQuadrature": 4702528784456508121 + } + }, + { + "input_utc": "2012-02-17T15:15:18.926Z", + "tt_jd_bits": 4702528577792744626, + "events": { + "LastNeptuneConjunction": 4702527793483315868, + "LastNeptuneEasternQuadrature": 4702528387152357001, + "LastNeptuneOpposition": 4702528194123267306, + "LastNeptuneProgradeToRetrograde": 4702528021597321662, + "LastNeptuneRetrogradeToPrograde": 4702528363569912197, + "LastNeptuneWesternQuadrature": 4702527995131216666, + "NextNeptuneConjunction": 4702528582571314390, + "NextNeptuneEasternQuadrature": 4702529176217047087, + "NextNeptuneOpposition": 4702528983421437177, + "NextNeptuneProgradeToRetrograde": 4702528811012343032, + "NextNeptuneRetrogradeToPrograde": 4702529152916422918, + "NextNeptuneWesternQuadrature": 4702528784456508121 + } + }, + { + "input_utc": "2012-10-15T18:32:25.926Z", + "tt_jd_bits": 4702529095630290328, + "events": { + "LastNeptuneConjunction": 4702528582571314390, + "LastNeptuneEasternQuadrature": 4702528387152357001, + "LastNeptuneOpposition": 4702528983421437177, + "LastNeptuneProgradeToRetrograde": 4702528811012343032, + "LastNeptuneRetrogradeToPrograde": 4702528363569912197, + "LastNeptuneWesternQuadrature": 4702528784456508121, + "NextNeptuneConjunction": 4702529371649031405, + "NextNeptuneEasternQuadrature": 4702529176217047087, + "NextNeptuneOpposition": 4702529772727858401, + "NextNeptuneProgradeToRetrograde": 4702529600221816082, + "NextNeptuneRetrogradeToPrograde": 4702529152916422918, + "NextNeptuneWesternQuadrature": 4702529573792022542 + } + }, + { + "input_utc": "2013-06-13T21:49:32.926Z", + "tt_jd_bits": 4702529613467811176, + "events": { + "LastNeptuneConjunction": 4702529371649031405, + "LastNeptuneEasternQuadrature": 4702529176217047087, + "LastNeptuneOpposition": 4702528983421437177, + "LastNeptuneProgradeToRetrograde": 4702529600221816082, + "LastNeptuneRetrogradeToPrograde": 4702529152916422918, + "LastNeptuneWesternQuadrature": 4702529573792022542, + "NextNeptuneConjunction": 4702530160748173751, + "NextNeptuneEasternQuadrature": 4702529965299810834, + "NextNeptuneOpposition": 4702529772727858401, + "NextNeptuneProgradeToRetrograde": 4702530389449176949, + "NextNeptuneRetrogradeToPrograde": 4702529942069222453, + "NextNeptuneWesternQuadrature": 4702530363107102452 + } + }, + { + "input_utc": "2014-02-10T01:06:39.926Z", + "tt_jd_bits": 4702530131305332023, + "events": { + "LastNeptuneConjunction": 4702529371649031405, + "LastNeptuneEasternQuadrature": 4702529965299810834, + "LastNeptuneOpposition": 4702529772727858401, + "LastNeptuneProgradeToRetrograde": 4702529600221816082, + "LastNeptuneRetrogradeToPrograde": 4702529942069222453, + "LastNeptuneWesternQuadrature": 4702529573792022542, + "NextNeptuneConjunction": 4702530160748173751, + "NextNeptuneEasternQuadrature": 4702530754360961588, + "NextNeptuneOpposition": 4702530562002436201, + "NextNeptuneProgradeToRetrograde": 4702530389449176949, + "NextNeptuneRetrogradeToPrograde": 4702530731369828764, + "NextNeptuneWesternQuadrature": 4702530363107102452 + } + }, + { + "input_utc": "2014-10-09T04:23:46.926Z", + "tt_jd_bits": 4702530649142852870, + "events": { + "LastNeptuneConjunction": 4702530160748173751, + "LastNeptuneEasternQuadrature": 4702529965299810834, + "LastNeptuneOpposition": 4702530562002436201, + "LastNeptuneProgradeToRetrograde": 4702530389449176949, + "LastNeptuneRetrogradeToPrograde": 4702529942069222453, + "LastNeptuneWesternQuadrature": 4702530363107102452, + "NextNeptuneConjunction": 4702530949834918424, + "NextNeptuneEasternQuadrature": 4702530754360961588, + "NextNeptuneOpposition": 4702531351300044432, + "NextNeptuneProgradeToRetrograde": 4702531178827541690, + "NextNeptuneRetrogradeToPrograde": 4702530731369828764, + "NextNeptuneWesternQuadrature": 4702531152433685546 + } + }, + { + "input_utc": "2015-06-07T07:40:53.926Z", + "tt_jd_bits": 4702531166980373718, + "events": { + "LastNeptuneConjunction": 4702530949834918424, + "LastNeptuneEasternQuadrature": 4702530754360961588, + "LastNeptuneOpposition": 4702530562002436201, + "LastNeptuneProgradeToRetrograde": 4702530389449176949, + "LastNeptuneRetrogradeToPrograde": 4702530731369828764, + "LastNeptuneWesternQuadrature": 4702531152433685546, + "NextNeptuneConjunction": 4702531738934209180, + "NextNeptuneEasternQuadrature": 4702531543427424177, + "NextNeptuneOpposition": 4702531351300044432, + "NextNeptuneProgradeToRetrograde": 4702531178827541690, + "NextNeptuneRetrogradeToPrograde": 4702531520401833977, + "NextNeptuneWesternQuadrature": 4702531941774595281 + } + }, + { + "input_utc": "2016-02-02T04:56:15.926Z", + "tt_jd_bits": 4702531682130955072, + "events": { + "LastNeptuneConjunction": 4702530949834918424, + "LastNeptuneEasternQuadrature": 4702531543427424177, + "LastNeptuneOpposition": 4702531351300044432, + "LastNeptuneProgradeToRetrograde": 4702531178827541690, + "LastNeptuneRetrogradeToPrograde": 4702531520401833977, + "LastNeptuneWesternQuadrature": 4702531152433685546, + "NextNeptuneConjunction": 4702531738934209180, + "NextNeptuneEasternQuadrature": 4702532332489429640, + "NextNeptuneOpposition": 4702532140589249872, + "NextNeptuneProgradeToRetrograde": 4702531968048009077, + "NextNeptuneRetrogradeToPrograde": 4702532309683865260, + "NextNeptuneWesternQuadrature": 4702531941774595281 + } + }, + { + "input_utc": "2016-09-30T08:13:22.926Z", + "tt_jd_bits": 4702532199968475920, + "events": { + "LastNeptuneConjunction": 4702531738934209180, + "LastNeptuneEasternQuadrature": 4702531543427424177, + "LastNeptuneOpposition": 4702532140589249872, + "LastNeptuneProgradeToRetrograde": 4702531968048009077, + "LastNeptuneRetrogradeToPrograde": 4702531520401833977, + "LastNeptuneWesternQuadrature": 4702531941774595281, + "NextNeptuneConjunction": 4702532528040555695, + "NextNeptuneEasternQuadrature": 4702532332489429640, + "NextNeptuneOpposition": 4702532929864005523, + "NextNeptuneProgradeToRetrograde": 4702532757517133111, + "NextNeptuneRetrogradeToPrograde": 4702532309683865260, + "NextNeptuneWesternQuadrature": 4702532731110749057 + } + }, + { + "input_utc": "2017-05-29T09:14:29.926Z", + "tt_jd_bits": 4702532717603203722, + "events": { + "LastNeptuneConjunction": 4702532528040555695, + "LastNeptuneEasternQuadrature": 4702532332489429640, + "LastNeptuneOpposition": 4702532140589249872, + "LastNeptuneProgradeToRetrograde": 4702531968048009077, + "LastNeptuneRetrogradeToPrograde": 4702532309683865260, + "LastNeptuneWesternQuadrature": 4702531941774595281, + "NextNeptuneConjunction": 4702533317166107290, + "NextNeptuneEasternQuadrature": 4702533121551116508, + "NextNeptuneOpposition": 4702532929864005523, + "NextNeptuneProgradeToRetrograde": 4702532757517133111, + "NextNeptuneRetrogradeToPrograde": 4702533098732565043, + "NextNeptuneWesternQuadrature": 4702532731110749057 + } + }, + { + "input_utc": "2018-01-25T12:31:36.926Z", + "tt_jd_bits": 4702533235440724569, + "events": { + "LastNeptuneConjunction": 4702532528040555695, + "LastNeptuneEasternQuadrature": 4702533121551116508, + "LastNeptuneOpposition": 4702532929864005523, + "LastNeptuneProgradeToRetrograde": 4702532757517133111, + "LastNeptuneRetrogradeToPrograde": 4702533098732565043, + "LastNeptuneWesternQuadrature": 4702532731110749057, + "NextNeptuneConjunction": 4702533317166107290, + "NextNeptuneEasternQuadrature": 4702533910628975131, + "NextNeptuneOpposition": 4702533719152363502, + "NextNeptuneProgradeToRetrograde": 4702533546787825059, + "NextNeptuneRetrogradeToPrograde": 4702533887892789084, + "NextNeptuneWesternQuadrature": 4702533520467115858 + } + }, + { + "input_utc": "2018-09-23T15:48:43.926Z", + "tt_jd_bits": 4702533753278245417, + "events": { + "LastNeptuneConjunction": 4702533317166107290, + "LastNeptuneEasternQuadrature": 4702533121551116508, + "LastNeptuneOpposition": 4702533719152363502, + "LastNeptuneProgradeToRetrograde": 4702533546787825059, + "LastNeptuneRetrogradeToPrograde": 4702533098732565043, + "LastNeptuneWesternQuadrature": 4702533520467115858, + "NextNeptuneConjunction": 4702534106285796124, + "NextNeptuneEasternQuadrature": 4702533910628975131, + "NextNeptuneOpposition": 4702534508437767074, + "NextNeptuneProgradeToRetrograde": 4702534336335789232, + "NextNeptuneRetrogradeToPrograde": 4702533887892789084, + "NextNeptuneWesternQuadrature": 4702534309810193370 + } + }, + { + "input_utc": "2019-05-22T19:05:50.926Z", + "tt_jd_bits": 4702534271115766264, + "events": { + "LastNeptuneConjunction": 4702534106285796124, + "LastNeptuneEasternQuadrature": 4702533910628975131, + "LastNeptuneOpposition": 4702533719152363502, + "LastNeptuneProgradeToRetrograde": 4702533546787825059, + "LastNeptuneRetrogradeToPrograde": 4702533887892789084, + "LastNeptuneWesternQuadrature": 4702533520467115858, + "NextNeptuneConjunction": 4702534895431147401, + "NextNeptuneEasternQuadrature": 4702534699707390012, + "NextNeptuneOpposition": 4702534508437767074, + "NextNeptuneProgradeToRetrograde": 4702534336335789232, + "NextNeptuneRetrogradeToPrograde": 4702534677092143404, + "NextNeptuneWesternQuadrature": 4702534309810193370 + } + }, + { + "input_utc": "2020-01-18T22:22:57.926Z", + "tt_jd_bits": 4702534788953287112, + "events": { + "LastNeptuneConjunction": 4702534106285796124, + "LastNeptuneEasternQuadrature": 4702534699707390012, + "LastNeptuneOpposition": 4702534508437767074, + "LastNeptuneProgradeToRetrograde": 4702534336335789232, + "LastNeptuneRetrogradeToPrograde": 4702534677092143404, + "LastNeptuneWesternQuadrature": 4702534309810193370, + "NextNeptuneConjunction": 4702534895431147401, + "NextNeptuneEasternQuadrature": 4702535488789188864, + "NextNeptuneOpposition": 4702535297730403092, + "NextNeptuneProgradeToRetrograde": 4702535125750151419, + "NextNeptuneRetrogradeToPrograde": 4702535466364615639, + "NextNeptuneWesternQuadrature": 4702535099195205784 + } + }, + { + "input_utc": "2020-09-16T01:40:04.926Z", + "tt_jd_bits": 4702535306790807959, + "events": { + "LastNeptuneConjunction": 4702534895431147401, + "LastNeptuneEasternQuadrature": 4702534699707390012, + "LastNeptuneOpposition": 4702535297730403092, + "LastNeptuneProgradeToRetrograde": 4702535125750151419, + "LastNeptuneRetrogradeToPrograde": 4702534677092143404, + "LastNeptuneWesternQuadrature": 4702535099195205784, + "NextNeptuneConjunction": 4702535684598369676, + "NextNeptuneEasternQuadrature": 4702535488789188864, + "NextNeptuneOpposition": 4702536087013022621, + "NextNeptuneProgradeToRetrograde": 4702535915257787802, + "NextNeptuneRetrogradeToPrograde": 4702535466364615617, + "NextNeptuneWesternQuadrature": 4702535888577468012 + } + }, + { + "input_utc": "2021-05-15T04:57:11.926Z", + "tt_jd_bits": 4702535824628328807, + "events": { + "LastNeptuneConjunction": 4702535684598369676, + "LastNeptuneEasternQuadrature": 4702535488789188864, + "LastNeptuneOpposition": 4702535297730403092, + "LastNeptuneProgradeToRetrograde": 4702535125750151419, + "LastNeptuneRetrogradeToPrograde": 4702535466364615639, + "LastNeptuneWesternQuadrature": 4702535099195205784, + "NextNeptuneConjunction": 4702536473771990730, + "NextNeptuneEasternQuadrature": 4702536277870829190, + "NextNeptuneOpposition": 4702536087013022621, + "NextNeptuneProgradeToRetrograde": 4702535915257787802, + "NextNeptuneRetrogradeToPrograde": 4702536255677683031, + "NextNeptuneWesternQuadrature": 4702535888577468012 + } + }, + { + "input_utc": "2022-01-11T08:14:18.926Z", + "tt_jd_bits": 4702536342465849654, + "events": { + "LastNeptuneConjunction": 4702535684598369676, + "LastNeptuneEasternQuadrature": 4702536277870829190, + "LastNeptuneOpposition": 4702536087013022621, + "LastNeptuneProgradeToRetrograde": 4702535915257787802, + "LastNeptuneRetrogradeToPrograde": 4702536255677683031, + "LastNeptuneWesternQuadrature": 4702535888577468012, + "NextNeptuneConjunction": 4702536473771990730, + "NextNeptuneEasternQuadrature": 4702537066965095909, + "NextNeptuneOpposition": 4702536876302606645, + "NextNeptuneProgradeToRetrograde": 4702536704570503091, + "NextNeptuneRetrogradeToPrograde": 4702537044852784896, + "NextNeptuneWesternQuadrature": 4702536677959493271 + } + }, + { + "input_utc": "2022-09-08T03:15:25.926Z", + "tt_jd_bits": 4702536857416198042, + "events": { + "LastNeptuneConjunction": 4702536473771990730, + "LastNeptuneEasternQuadrature": 4702536277870829190, + "LastNeptuneOpposition": 4702536087013022621, + "LastNeptuneProgradeToRetrograde": 4702536704570503091, + "LastNeptuneRetrogradeToPrograde": 4702536255677683031, + "LastNeptuneWesternQuadrature": 4702536677959493271, + "NextNeptuneConjunction": 4702537262966218524, + "NextNeptuneEasternQuadrature": 4702537066965095909, + "NextNeptuneOpposition": 4702536876302606645, + "NextNeptuneProgradeToRetrograde": 4702537493979511170, + "NextNeptuneRetrogradeToPrograde": 4702537044852784896, + "NextNeptuneWesternQuadrature": 4702537467357055951 + } + }, + { + "input_utc": "2023-05-07T06:32:32.926Z", + "tt_jd_bits": 4702537375253718889, + "events": { + "LastNeptuneConjunction": 4702537262966218524, + "LastNeptuneEasternQuadrature": 4702537066965095909, + "LastNeptuneOpposition": 4702536876302606645, + "LastNeptuneProgradeToRetrograde": 4702536704570503091, + "LastNeptuneRetrogradeToPrograde": 4702537044852784896, + "LastNeptuneWesternQuadrature": 4702536677959493271, + "NextNeptuneConjunction": 4702538052141677193, + "NextNeptuneEasternQuadrature": 4702537856035827931, + "NextNeptuneOpposition": 4702537665587273193, + "NextNeptuneProgradeToRetrograde": 4702537493979511170, + "NextNeptuneRetrogradeToPrograde": 4702537834217174403, + "NextNeptuneWesternQuadrature": 4702537467357055951 + } + }, + { + "input_utc": "2024-01-03T09:49:39.926Z", + "tt_jd_bits": 4702537893091239736, + "events": { + "LastNeptuneConjunction": 4702537262966218524, + "LastNeptuneEasternQuadrature": 4702537856035827931, + "LastNeptuneOpposition": 4702537665587273193, + "LastNeptuneProgradeToRetrograde": 4702537493979511170, + "LastNeptuneRetrogradeToPrograde": 4702537834217174403, + "LastNeptuneWesternQuadrature": 4702537467357055951, + "NextNeptuneConjunction": 4702538052141677193, + "NextNeptuneEasternQuadrature": 4702538645124895333, + "NextNeptuneOpposition": 4702538454875905510, + "NextNeptuneProgradeToRetrograde": 4702538283331445142, + "NextNeptuneRetrogradeToPrograde": 4702538623345147828, + "NextNeptuneWesternQuadrature": 4702538256763909153 + } + }, + { + "input_utc": "2024-08-31T13:06:46.926Z", + "tt_jd_bits": 4702538410928760584, + "events": { + "LastNeptuneConjunction": 4702538052141677193, + "LastNeptuneEasternQuadrature": 4702537856035827931, + "LastNeptuneOpposition": 4702537665587273193, + "LastNeptuneProgradeToRetrograde": 4702538283331445142, + "LastNeptuneRetrogradeToPrograde": 4702537834217174403, + "LastNeptuneWesternQuadrature": 4702538256763909153, + "NextNeptuneConjunction": 4702538841345839901, + "NextNeptuneEasternQuadrature": 4702538645124895333, + "NextNeptuneOpposition": 4702538454875905510, + "NextNeptuneProgradeToRetrograde": 4702539072476536470, + "NextNeptuneRetrogradeToPrograde": 4702538623345147828, + "NextNeptuneWesternQuadrature": 4702539046168144690 + } + }, + { + "input_utc": "2025-04-29T16:23:53.926Z", + "tt_jd_bits": 4702538928766281431, + "events": { + "LastNeptuneConjunction": 4702538841345839901, + "LastNeptuneEasternQuadrature": 4702538645124895333, + "LastNeptuneOpposition": 4702538454875905510, + "LastNeptuneProgradeToRetrograde": 4702538283331445142, + "LastNeptuneRetrogradeToPrograde": 4702538623345147828, + "LastNeptuneWesternQuadrature": 4702538256763909153, + "NextNeptuneConjunction": 4702539630536449202, + "NextNeptuneEasternQuadrature": 4702539434195835754, + "NextNeptuneOpposition": 4702539244131033031, + "NextNeptuneProgradeToRetrograde": 4702539072476536470, + "NextNeptuneRetrogradeToPrograde": 4702539412661169055, + "NextNeptuneWesternQuadrature": 4702539046168144690 + } + }, + { + "input_utc": "2025-12-26T19:39:15.926Z", + "tt_jd_bits": 4702539446601192490, + "events": { + "LastNeptuneConjunction": 4702538841345839901, + "LastNeptuneEasternQuadrature": 4702539434195835754, + "LastNeptuneOpposition": 4702539244131033031, + "LastNeptuneProgradeToRetrograde": 4702539072476536470, + "LastNeptuneRetrogradeToPrograde": 4702539412661169055, + "LastNeptuneWesternQuadrature": 4702539046168144690, + "NextNeptuneConjunction": 4702539630536449202, + "NextNeptuneEasternQuadrature": 4702540223269717166, + "NextNeptuneOpposition": 4702540033395067440, + "NextNeptuneProgradeToRetrograde": 4702539861814668024, + "NextNeptuneRetrogradeToPrograde": 4702540201719221684, + "NextNeptuneWesternQuadrature": 4702539835561179972 + } + }, + { + "input_utc": "2026-08-24T22:56:22.926Z", + "tt_jd_bits": 4702539964438714830, + "events": { + "LastNeptuneConjunction": 4702539630536449202, + "LastNeptuneEasternQuadrature": 4702539434195835754, + "LastNeptuneOpposition": 4702539244131033031, + "LastNeptuneProgradeToRetrograde": 4702539861814668024, + "LastNeptuneRetrogradeToPrograde": 4702539412661169055, + "LastNeptuneWesternQuadrature": 4702539835561179972, + "NextNeptuneConjunction": 4702540419727866785, + "NextNeptuneEasternQuadrature": 4702540223269717166, + "NextNeptuneOpposition": 4702540033395067440, + "NextNeptuneProgradeToRetrograde": 4702540651018964133, + "NextNeptuneRetrogradeToPrograde": 4702540201719221684, + "NextNeptuneWesternQuadrature": 4702540624958844632 + } + }, + { + "input_utc": "2027-04-23T02:13:29.926Z", + "tt_jd_bits": 4702540482276236639, + "events": { + "LastNeptuneConjunction": 4702540419727866785, + "LastNeptuneEasternQuadrature": 4702540223269717166, + "LastNeptuneOpposition": 4702540033395067440, + "LastNeptuneProgradeToRetrograde": 4702539861814668024, + "LastNeptuneRetrogradeToPrograde": 4702540201719221684, + "LastNeptuneWesternQuadrature": 4702539835561179972, + "NextNeptuneConjunction": 4702541208932634537, + "NextNeptuneEasternQuadrature": 4702541012343202398, + "NextNeptuneOpposition": 4702540822658641413, + "NextNeptuneProgradeToRetrograde": 4702540651018964133, + "NextNeptuneRetrogradeToPrograde": 4702540990872654140, + "NextNeptuneWesternQuadrature": 4702540624958844632 + } + }, + { + "input_utc": "1996-06-23T03:14:15.926Z", + "tt_jd_bits": 4702516299553321892, + "events": { + "LastNeptuneConjunction": 4702515958071380307, + "LastNeptuneEasternQuadrature": 4702515761670859909, + "LastNeptuneOpposition": 4702515565243596299, + "LastNeptuneProgradeToRetrograde": 4702516181956450581, + "LastNeptuneRetrogradeToPrograde": 4702515736655826340, + "LastNeptuneWesternQuadrature": 4702516156366194663, + "NextNeptuneConjunction": 4702516747062911378, + "NextNeptuneEasternQuadrature": 4702516550781582515, + "NextNeptuneOpposition": 4702516354552986371, + "NextNeptuneProgradeToRetrograde": 4702516971321732963, + "NextNeptuneRetrogradeToPrograde": 4702516525855821720, + "NextNeptuneWesternQuadrature": 4702516945567756320 + } + }, + { + "input_utc": "1996-12-07T07:33:42.926Z", + "tt_jd_bits": 4702516658570010992, + "events": { + "LastNeptuneConjunction": 4702515958071380307, + "LastNeptuneEasternQuadrature": 4702516550781582515, + "LastNeptuneOpposition": 4702516354552986371, + "LastNeptuneProgradeToRetrograde": 4702516181956450581, + "LastNeptuneRetrogradeToPrograde": 4702516525855821720, + "LastNeptuneWesternQuadrature": 4702516156366194663, + "NextNeptuneConjunction": 4702516747062911378, + "NextNeptuneEasternQuadrature": 4702517339906805250, + "NextNeptuneOpposition": 4702517143871817437, + "NextNeptuneProgradeToRetrograde": 4702516971321732963, + "NextNeptuneRetrogradeToPrograde": 4702517314847535212, + "NextNeptuneWesternQuadrature": 4702516945567756320 + } + }, + { + "input_utc": "1997-05-23T11:53:09.926Z", + "tt_jd_bits": 4702517017586700091, + "events": { + "LastNeptuneConjunction": 4702516747062911378, + "LastNeptuneEasternQuadrature": 4702516550781582515, + "LastNeptuneOpposition": 4702516354552986371, + "LastNeptuneProgradeToRetrograde": 4702516971321732963, + "LastNeptuneRetrogradeToPrograde": 4702516525855821720, + "LastNeptuneWesternQuadrature": 4702516945567756320, + "NextNeptuneConjunction": 4702517536084450596, + "NextNeptuneEasternQuadrature": 4702517339906805250, + "NextNeptuneOpposition": 4702517143871817437, + "NextNeptuneProgradeToRetrograde": 4702517760492452126, + "NextNeptuneRetrogradeToPrograde": 4702517314847535212, + "NextNeptuneWesternQuadrature": 4702517734779825816 + } + }, + { + "input_utc": "1997-11-06T16:12:36.926Z", + "tt_jd_bits": 4702517376603414046, + "events": { + "LastNeptuneConjunction": 4702516747062911378, + "LastNeptuneEasternQuadrature": 4702517339906805250, + "LastNeptuneOpposition": 4702517143871817437, + "LastNeptuneProgradeToRetrograde": 4702516971321732963, + "LastNeptuneRetrogradeToPrograde": 4702517314847535212, + "LastNeptuneWesternQuadrature": 4702516945567756320, + "NextNeptuneConjunction": 4702517536084450596, + "NextNeptuneEasternQuadrature": 4702518129008816070, + "NextNeptuneOpposition": 4702517933168137145, + "NextNeptuneProgradeToRetrograde": 4702517760492452126, + "NextNeptuneRetrogradeToPrograde": 4702518104106338544, + "NextNeptuneWesternQuadrature": 4702517734779825816 + } + }, + { + "input_utc": "1998-04-22T20:32:03.926Z", + "tt_jd_bits": 4702517735620103146, + "events": { + "LastNeptuneConjunction": 4702517536084450596, + "LastNeptuneEasternQuadrature": 4702517339906805250, + "LastNeptuneOpposition": 4702517143871817437, + "LastNeptuneProgradeToRetrograde": 4702516971321732963, + "LastNeptuneRetrogradeToPrograde": 4702517314847535212, + "LastNeptuneWesternQuadrature": 4702517734779825816, + "NextNeptuneConjunction": 4702518325087993501, + "NextNeptuneEasternQuadrature": 4702518129008816070, + "NextNeptuneOpposition": 4702517933168137145, + "NextNeptuneProgradeToRetrograde": 4702517760492452126, + "NextNeptuneRetrogradeToPrograde": 4702518104106338544, + "NextNeptuneWesternQuadrature": 4702518523991065624 + } + }, + { + "input_utc": "1998-10-07T00:51:30.926Z", + "tt_jd_bits": 4702518094636792245, + "events": { + "LastNeptuneConjunction": 4702517536084450596, + "LastNeptuneEasternQuadrature": 4702517339906805250, + "LastNeptuneOpposition": 4702517933168137145, + "LastNeptuneProgradeToRetrograde": 4702517760492452126, + "LastNeptuneRetrogradeToPrograde": 4702517314847535234, + "LastNeptuneWesternQuadrature": 4702517734779825816, + "NextNeptuneConjunction": 4702518325087993501, + "NextNeptuneEasternQuadrature": 4702518129008816070, + "NextNeptuneOpposition": 4702518722476969087, + "NextNeptuneProgradeToRetrograde": 4702518549926145855, + "NextNeptuneRetrogradeToPrograde": 4702518104106338544, + "NextNeptuneWesternQuadrature": 4702518523991065624 + } + }, + { + "input_utc": "1999-03-23T05:10:57.926Z", + "tt_jd_bits": 4702518453653506200, + "events": { + "LastNeptuneConjunction": 4702518325087993501, + "LastNeptuneEasternQuadrature": 4702518129008816070, + "LastNeptuneOpposition": 4702517933168137145, + "LastNeptuneProgradeToRetrograde": 4702517760492452126, + "LastNeptuneRetrogradeToPrograde": 4702518104106338544, + "LastNeptuneWesternQuadrature": 4702517734779825816, + "NextNeptuneConjunction": 4702519114088694186, + "NextNeptuneEasternQuadrature": 4702518918119524345, + "NextNeptuneOpposition": 4702518722476969087, + "NextNeptuneProgradeToRetrograde": 4702518549926145855, + "NextNeptuneRetrogradeToPrograde": 4702518893288503608, + "NextNeptuneWesternQuadrature": 4702518523991065624 + } + }, + { + "input_utc": "1999-09-06T09:30:24.926Z", + "tt_jd_bits": 4702518812670195300, + "events": { + "LastNeptuneConjunction": 4702518325087993501, + "LastNeptuneEasternQuadrature": 4702518129008816070, + "LastNeptuneOpposition": 4702518722476969087, + "LastNeptuneProgradeToRetrograde": 4702518549926145855, + "LastNeptuneRetrogradeToPrograde": 4702518104106338552, + "LastNeptuneWesternQuadrature": 4702518523991065624, + "NextNeptuneConjunction": 4702519114088694186, + "NextNeptuneEasternQuadrature": 4702518918119524345, + "NextNeptuneOpposition": 4702519511791041747, + "NextNeptuneProgradeToRetrograde": 4702519339132037758, + "NextNeptuneRetrogradeToPrograde": 4702518893288503608, + "NextNeptuneWesternQuadrature": 4702519313217627433 + } + }, + { + "input_utc": "2000-02-20T13:49:51.926Z", + "tt_jd_bits": 4702519171686884399, + "events": { + "LastNeptuneConjunction": 4702519114088694186, + "LastNeptuneEasternQuadrature": 4702518918119524345, + "LastNeptuneOpposition": 4702518722476969087, + "LastNeptuneProgradeToRetrograde": 4702518549926145855, + "LastNeptuneRetrogradeToPrograde": 4702518893288503608, + "LastNeptuneWesternQuadrature": 4702518523991065624, + "NextNeptuneConjunction": 4702519903089547967, + "NextNeptuneEasternQuadrature": 4702519707219408360, + "NextNeptuneOpposition": 4702519511791041747, + "NextNeptuneProgradeToRetrograde": 4702519339132037758, + "NextNeptuneRetrogradeToPrograde": 4702519682565794317, + "NextNeptuneWesternQuadrature": 4702519313217627433 + } + }, + { + "input_utc": "2000-08-04T06:09:18.926Z", + "tt_jd_bits": 4702519527482348027, + "events": { + "LastNeptuneConjunction": 4702519114088694186, + "LastNeptuneEasternQuadrature": 4702518918119524345, + "LastNeptuneOpposition": 4702519511791041747, + "LastNeptuneProgradeToRetrograde": 4702519339132037758, + "LastNeptuneRetrogradeToPrograde": 4702518893288503608, + "LastNeptuneWesternQuadrature": 4702519313217627433, + "NextNeptuneConjunction": 4702519903089547967, + "NextNeptuneEasternQuadrature": 4702519707219408360, + "NextNeptuneOpposition": 4702520301080043651, + "NextNeptuneProgradeToRetrograde": 4702520128437936923, + "NextNeptuneRetrogradeToPrograde": 4702519682565794317, + "NextNeptuneWesternQuadrature": 4702520102439370105 + } + }, + { + "input_utc": "2001-01-18T10:28:45.926Z", + "tt_jd_bits": 4702519886499037127, + "events": { + "LastNeptuneConjunction": 4702519114088694186, + "LastNeptuneEasternQuadrature": 4702519707219408360, + "LastNeptuneOpposition": 4702519511791041747, + "LastNeptuneProgradeToRetrograde": 4702519339132037758, + "LastNeptuneRetrogradeToPrograde": 4702519682565794317, + "LastNeptuneWesternQuadrature": 4702519313217627433, + "NextNeptuneConjunction": 4702519903089547967, + "NextNeptuneEasternQuadrature": 4702520496296693610, + "NextNeptuneOpposition": 4702520301080043651, + "NextNeptuneProgradeToRetrograde": 4702520128437936923, + "NextNeptuneRetrogradeToPrograde": 4702520471756344834, + "NextNeptuneWesternQuadrature": 4702520102439370105 + } + }, + { + "input_utc": "2001-07-04T11:19:12.926Z", + "tt_jd_bits": 4702520245204042836, + "events": { + "LastNeptuneConjunction": 4702519903089547967, + "LastNeptuneEasternQuadrature": 4702519707219408360, + "LastNeptuneOpposition": 4702519511791041747, + "LastNeptuneProgradeToRetrograde": 4702520128437936923, + "LastNeptuneRetrogradeToPrograde": 4702519682565794317, + "LastNeptuneWesternQuadrature": 4702520102439370105, + "NextNeptuneConjunction": 4702520692096778935, + "NextNeptuneEasternQuadrature": 4702520496296693610, + "NextNeptuneOpposition": 4702520301080043651, + "NextNeptuneProgradeToRetrograde": 4702520917586086820, + "NextNeptuneRetrogradeToPrograde": 4702520471756344834, + "NextNeptuneWesternQuadrature": 4702520891671301874 + } + }, + { + "input_utc": "2001-12-18T15:38:39.926Z", + "tt_jd_bits": 4702520604220731935, + "events": { + "LastNeptuneConjunction": 4702519903089547967, + "LastNeptuneEasternQuadrature": 4702520496296693610, + "LastNeptuneOpposition": 4702520301080043651, + "LastNeptuneProgradeToRetrograde": 4702520128437936923, + "LastNeptuneRetrogradeToPrograde": 4702520471756344834, + "LastNeptuneWesternQuadrature": 4702520102439370105, + "NextNeptuneConjunction": 4702520692096778935, + "NextNeptuneEasternQuadrature": 4702521285385325603, + "NextNeptuneOpposition": 4702521090382580029, + "NextNeptuneProgradeToRetrograde": 4702520917586086820, + "NextNeptuneRetrogradeToPrograde": 4702521260977684764, + "NextNeptuneWesternQuadrature": 4702520891671301874 + } + }, + { + "input_utc": "2002-06-03T19:58:06.926Z", + "tt_jd_bits": 4702520963237421035, + "events": { + "LastNeptuneConjunction": 4702520692096778935, + "LastNeptuneEasternQuadrature": 4702520496296693610, + "LastNeptuneOpposition": 4702520301080043651, + "LastNeptuneProgradeToRetrograde": 4702520917586086820, + "LastNeptuneRetrogradeToPrograde": 4702520471756344834, + "LastNeptuneWesternQuadrature": 4702520891671301874, + "NextNeptuneConjunction": 4702521481101967327, + "NextNeptuneEasternQuadrature": 4702521285385325603, + "NextNeptuneOpposition": 4702521090382580029, + "NextNeptuneProgradeToRetrograde": 4702521706894567270, + "NextNeptuneRetrogradeToPrograde": 4702521260977684764, + "NextNeptuneWesternQuadrature": 4702521680882756032 + } + }, + { + "input_utc": "2002-11-18T00:11:15.926Z", + "tt_jd_bits": 4702521322244714894, + "events": { + "LastNeptuneConjunction": 4702520692096778935, + "LastNeptuneEasternQuadrature": 4702521285385325603, + "LastNeptuneOpposition": 4702521090382580029, + "LastNeptuneProgradeToRetrograde": 4702520917586086820, + "LastNeptuneRetrogradeToPrograde": 4702521260977684764, + "LastNeptuneWesternQuadrature": 4702520891671301874, + "NextNeptuneConjunction": 4702521481101967327, + "NextNeptuneEasternQuadrature": 4702522074463753269, + "NextNeptuneOpposition": 4702521879668940838, + "NextNeptuneProgradeToRetrograde": 4702521706894567270, + "NextNeptuneRetrogradeToPrograde": 4702522050196817960, + "NextNeptuneWesternQuadrature": 4702521680882756032 + } + }, + { + "input_utc": "2003-05-04T04:30:42.926Z", + "tt_jd_bits": 4702521681261403994, + "events": { + "LastNeptuneConjunction": 4702521481101967327, + "LastNeptuneEasternQuadrature": 4702521285385325603, + "LastNeptuneOpposition": 4702521090382580029, + "LastNeptuneProgradeToRetrograde": 4702520917586086820, + "LastNeptuneRetrogradeToPrograde": 4702521260977684764, + "LastNeptuneWesternQuadrature": 4702521680882756032, + "NextNeptuneConjunction": 4702522270114869139, + "NextNeptuneEasternQuadrature": 4702522074463753269, + "NextNeptuneOpposition": 4702521879668940838, + "NextNeptuneProgradeToRetrograde": 4702521706894567270, + "NextNeptuneRetrogradeToPrograde": 4702522050196817960, + "NextNeptuneWesternQuadrature": 4702522470129484093 + } + }, + { + "input_utc": "2003-10-18T08:50:09.926Z", + "tt_jd_bits": 4702522040278093093, + "events": { + "LastNeptuneConjunction": 4702521481101967327, + "LastNeptuneEasternQuadrature": 4702521285385325603, + "LastNeptuneOpposition": 4702521879668940838, + "LastNeptuneProgradeToRetrograde": 4702521706894567270, + "LastNeptuneRetrogradeToPrograde": 4702521260977684764, + "LastNeptuneWesternQuadrature": 4702521680882756032, + "NextNeptuneConjunction": 4702522270114869139, + "NextNeptuneEasternQuadrature": 4702522074463753269, + "NextNeptuneOpposition": 4702522668978216343, + "NextNeptuneProgradeToRetrograde": 4702522496094650577, + "NextNeptuneRetrogradeToPrograde": 4702522050196817960, + "NextNeptuneWesternQuadrature": 4702522470129484093 + } + }, + { + "input_utc": "2004-04-02T13:09:36.926Z", + "tt_jd_bits": 4702522399294782193, + "events": { + "LastNeptuneConjunction": 4702522270114869139, + "LastNeptuneEasternQuadrature": 4702522074463753269, + "LastNeptuneOpposition": 4702521879668940838, + "LastNeptuneProgradeToRetrograde": 4702521706894567270, + "LastNeptuneRetrogradeToPrograde": 4702522050196817960, + "LastNeptuneWesternQuadrature": 4702521680882756032, + "NextNeptuneConjunction": 4702523059136438537, + "NextNeptuneEasternQuadrature": 4702522863544502714, + "NextNeptuneOpposition": 4702522668978216343, + "NextNeptuneProgradeToRetrograde": 4702522496094650577, + "NextNeptuneRetrogradeToPrograde": 4702522839255752990, + "NextNeptuneWesternQuadrature": 4702522470129484093 + } + }, + { + "input_utc": "2004-09-15T05:29:03.926Z", + "tt_jd_bits": 4702522755090245821, + "events": { + "LastNeptuneConjunction": 4702522270114869139, + "LastNeptuneEasternQuadrature": 4702522074463753269, + "LastNeptuneOpposition": 4702522668978216343, + "LastNeptuneProgradeToRetrograde": 4702522496094650577, + "LastNeptuneRetrogradeToPrograde": 4702522050196817960, + "LastNeptuneWesternQuadrature": 4702522470129484093, + "NextNeptuneConjunction": 4702523059136438537, + "NextNeptuneEasternQuadrature": 4702522863544502714, + "NextNeptuneOpposition": 4702523458272842065, + "NextNeptuneProgradeToRetrograde": 4702523285307963998, + "NextNeptuneRetrogradeToPrograde": 4702522839255752990, + "NextNeptuneWesternQuadrature": 4702523259384907442 + } + }, + { + "input_utc": "2005-03-01T09:48:30.926Z", + "tt_jd_bits": 4702523114106934920, + "events": { + "LastNeptuneConjunction": 4702523059136438537, + "LastNeptuneEasternQuadrature": 4702522863544502714, + "LastNeptuneOpposition": 4702522668978216343, + "LastNeptuneProgradeToRetrograde": 4702522496094650577, + "LastNeptuneRetrogradeToPrograde": 4702522839255752990, + "LastNeptuneWesternQuadrature": 4702522470129484093, + "NextNeptuneConjunction": 4702523848163442015, + "NextNeptuneEasternQuadrature": 4702523652620616787, + "NextNeptuneOpposition": 4702523458272842065, + "NextNeptuneProgradeToRetrograde": 4702523285307963998, + "NextNeptuneRetrogradeToPrograde": 4702523628444177261, + "NextNeptuneWesternQuadrature": 4702523259384907442 + } + }, + { + "input_utc": "2005-08-15T14:07:57.926Z", + "tt_jd_bits": 4702523473123624020, + "events": { + "LastNeptuneConjunction": 4702523059136438537, + "LastNeptuneEasternQuadrature": 4702522863544502714, + "LastNeptuneOpposition": 4702523458272842065, + "LastNeptuneProgradeToRetrograde": 4702523285307963998, + "LastNeptuneRetrogradeToPrograde": 4702522839255752990, + "LastNeptuneWesternQuadrature": 4702523259384907442, + "NextNeptuneConjunction": 4702523848163442015, + "NextNeptuneEasternQuadrature": 4702523652620616787, + "NextNeptuneOpposition": 4702524247567102982, + "NextNeptuneProgradeToRetrograde": 4702524074698240946, + "NextNeptuneRetrogradeToPrograde": 4702523628444177264, + "NextNeptuneWesternQuadrature": 4702524048639023502 + } + }, + { + "input_utc": "2006-01-29T18:27:24.926Z", + "tt_jd_bits": 4702523832140337974, + "events": { + "LastNeptuneConjunction": 4702523059136438537, + "LastNeptuneEasternQuadrature": 4702523652620616787, + "LastNeptuneOpposition": 4702523458272842065, + "LastNeptuneProgradeToRetrograde": 4702523285307963998, + "LastNeptuneRetrogradeToPrograde": 4702523628444177261, + "LastNeptuneWesternQuadrature": 4702523259384907442, + "NextNeptuneConjunction": 4702523848163442015, + "NextNeptuneEasternQuadrature": 4702524441713748114, + "NextNeptuneOpposition": 4702524247567102982, + "NextNeptuneProgradeToRetrograde": 4702524074698240946, + "NextNeptuneRetrogradeToPrograde": 4702524417377850737, + "NextNeptuneWesternQuadrature": 4702524048639023502 + } + }, + { + "input_utc": "2006-07-15T19:17:51.926Z", + "tt_jd_bits": 4702524190845343684, + "events": { + "LastNeptuneConjunction": 4702523848163442015, + "LastNeptuneEasternQuadrature": 4702523652620616787, + "LastNeptuneOpposition": 4702523458272842065, + "LastNeptuneProgradeToRetrograde": 4702524074698240946, + "LastNeptuneRetrogradeToPrograde": 4702523628444177261, + "LastNeptuneWesternQuadrature": 4702524048639023502, + "NextNeptuneConjunction": 4702524637213807171, + "NextNeptuneEasternQuadrature": 4702524441713748114, + "NextNeptuneOpposition": 4702524247567102982, + "NextNeptuneProgradeToRetrograde": 4702524863967835003, + "NextNeptuneRetrogradeToPrograde": 4702524417377850737, + "NextNeptuneWesternQuadrature": 4702524837919357850 + } + }, + { + "input_utc": "2006-12-29T23:37:18.926Z", + "tt_jd_bits": 4702524549862032783, + "events": { + "LastNeptuneConjunction": 4702523848163442015, + "LastNeptuneEasternQuadrature": 4702524441713748114, + "LastNeptuneOpposition": 4702524247567102982, + "LastNeptuneProgradeToRetrograde": 4702524074698240946, + "LastNeptuneRetrogradeToPrograde": 4702524417377850737, + "LastNeptuneWesternQuadrature": 4702524048639023502, + "NextNeptuneConjunction": 4702524637213807171, + "NextNeptuneEasternQuadrature": 4702525230798332194, + "NextNeptuneOpposition": 4702525036874120471, + "NextNeptuneProgradeToRetrograde": 4702524863967835003, + "NextNeptuneRetrogradeToPrograde": 4702525206640080377, + "NextNeptuneWesternQuadrature": 4702524837919357850 + } + }, + { + "input_utc": "2007-06-15T03:56:45.926Z", + "tt_jd_bits": 4702524908878721883, + "events": { + "LastNeptuneConjunction": 4702524637213807171, + "LastNeptuneEasternQuadrature": 4702524441713748114, + "LastNeptuneOpposition": 4702524247567102982, + "LastNeptuneProgradeToRetrograde": 4702524863967835003, + "LastNeptuneRetrogradeToPrograde": 4702524417377850714, + "LastNeptuneWesternQuadrature": 4702524837919357850, + "NextNeptuneConjunction": 4702525426251567580, + "NextNeptuneEasternQuadrature": 4702525230798332194, + "NextNeptuneOpposition": 4702525036874120471, + "NextNeptuneProgradeToRetrograde": 4702525653506879782, + "NextNeptuneRetrogradeToPrograde": 4702525206640080377, + "NextNeptuneWesternQuadrature": 4702525627200952187 + } + }, + { + "input_utc": "2007-11-29T08:16:12.926Z", + "tt_jd_bits": 4702525267895410983, + "events": { + "LastNeptuneConjunction": 4702524637213807171, + "LastNeptuneEasternQuadrature": 4702525230798332194, + "LastNeptuneOpposition": 4702525036874120471, + "LastNeptuneProgradeToRetrograde": 4702524863967835003, + "LastNeptuneRetrogradeToPrograde": 4702525206640080377, + "LastNeptuneWesternQuadrature": 4702524837919357850, + "NextNeptuneConjunction": 4702525426251567580, + "NextNeptuneEasternQuadrature": 4702526019893565391, + "NextNeptuneOpposition": 4702525826189598326, + "NextNeptuneProgradeToRetrograde": 4702525653506879782, + "NextNeptuneRetrogradeToPrograde": 4702525995775305543, + "NextNeptuneWesternQuadrature": 4702525627200952187 + } + }, + { + "input_utc": "2008-05-14T12:35:39.926Z", + "tt_jd_bits": 4702525626912100082, + "events": { + "LastNeptuneConjunction": 4702525426251567580, + "LastNeptuneEasternQuadrature": 4702525230798332194, + "LastNeptuneOpposition": 4702525036874120471, + "LastNeptuneProgradeToRetrograde": 4702524863967835003, + "LastNeptuneRetrogradeToPrograde": 4702525206640080377, + "LastNeptuneWesternQuadrature": 4702524837919357850, + "NextNeptuneConjunction": 4702526215328445892, + "NextNeptuneEasternQuadrature": 4702526019893565391, + "NextNeptuneOpposition": 4702525826189598326, + "NextNeptuneProgradeToRetrograde": 4702525653506879782, + "NextNeptuneRetrogradeToPrograde": 4702525995775305525, + "NextNeptuneWesternQuadrature": 4702525627200952187 + } + }, + { + "input_utc": "2008-10-27T04:55:06.926Z", + "tt_jd_bits": 4702525982707563710, + "events": { + "LastNeptuneConjunction": 4702525426251567580, + "LastNeptuneEasternQuadrature": 4702525230798332194, + "LastNeptuneOpposition": 4702525826189598326, + "LastNeptuneProgradeToRetrograde": 4702525653506879782, + "LastNeptuneRetrogradeToPrograde": 4702525206640080377, + "LastNeptuneWesternQuadrature": 4702525627200952187, + "NextNeptuneConjunction": 4702526215328445892, + "NextNeptuneEasternQuadrature": 4702526019893565391, + "NextNeptuneOpposition": 4702526615497239269, + "NextNeptuneProgradeToRetrograde": 4702526442792282718, + "NextNeptuneRetrogradeToPrograde": 4702525995775305543, + "NextNeptuneWesternQuadrature": 4702526416513055336 + } + }, + { + "input_utc": "2009-04-12T09:08:15.926Z", + "tt_jd_bits": 4702526341714882424, + "events": { + "LastNeptuneConjunction": 4702526215328445892, + "LastNeptuneEasternQuadrature": 4702526019893565391, + "LastNeptuneOpposition": 4702525826189598326, + "LastNeptuneProgradeToRetrograde": 4702525653506879782, + "LastNeptuneRetrogradeToPrograde": 4702525995775305543, + "LastNeptuneWesternQuadrature": 4702525627200952187, + "NextNeptuneConjunction": 4702527004406607440, + "NextNeptuneEasternQuadrature": 4702526808973808598, + "NextNeptuneOpposition": 4702526615497239269, + "NextNeptuneProgradeToRetrograde": 4702526442792282718, + "NextNeptuneRetrogradeToPrograde": 4702526784989348563, + "NextNeptuneWesternQuadrature": 4702526416513055336 + } + }, + { + "input_utc": "2009-09-26T13:27:42.926Z", + "tt_jd_bits": 4702526700731571523, + "events": { + "LastNeptuneConjunction": 4702526215328445892, + "LastNeptuneEasternQuadrature": 4702526019893565391, + "LastNeptuneOpposition": 4702526615497239269, + "LastNeptuneProgradeToRetrograde": 4702526442792282718, + "LastNeptuneRetrogradeToPrograde": 4702525995775305543, + "LastNeptuneWesternQuadrature": 4702526416513055336, + "NextNeptuneConjunction": 4702527004406607440, + "NextNeptuneEasternQuadrature": 4702526808973808598, + "NextNeptuneOpposition": 4702527404804653731, + "NextNeptuneProgradeToRetrograde": 4702527232266243818, + "NextNeptuneRetrogradeToPrograde": 4702526784989348563, + "NextNeptuneWesternQuadrature": 4702527205815798819 + } + }, + { + "input_utc": "2010-03-12T17:47:09.926Z", + "tt_jd_bits": 4702527059748260623, + "events": { + "LastNeptuneConjunction": 4702527004406607440, + "LastNeptuneEasternQuadrature": 4702526808973808598, + "LastNeptuneOpposition": 4702526615497239269, + "LastNeptuneProgradeToRetrograde": 4702526442792282718, + "LastNeptuneRetrogradeToPrograde": 4702526784989348563, + "LastNeptuneWesternQuadrature": 4702526416513055336, + "NextNeptuneConjunction": 4702527793483315868, + "NextNeptuneEasternQuadrature": 4702527598059113614, + "NextNeptuneOpposition": 4702527404804653731, + "NextNeptuneProgradeToRetrograde": 4702527232266243818, + "NextNeptuneRetrogradeToPrograde": 4702527574238083539, + "NextNeptuneWesternQuadrature": 4702527205815798819 + } + }, + { + "input_utc": "2010-08-26T22:06:36.926Z", + "tt_jd_bits": 4702527418764949723, + "events": { + "LastNeptuneConjunction": 4702527004406607440, + "LastNeptuneEasternQuadrature": 4702526808973808598, + "LastNeptuneOpposition": 4702527404804653731, + "LastNeptuneProgradeToRetrograde": 4702527232266243818, + "LastNeptuneRetrogradeToPrograde": 4702526784989348563, + "LastNeptuneWesternQuadrature": 4702527205815798819, + "NextNeptuneConjunction": 4702527793483315868, + "NextNeptuneEasternQuadrature": 4702527598059113614, + "NextNeptuneOpposition": 4702528194123267306, + "NextNeptuneProgradeToRetrograde": 4702528021597321662, + "NextNeptuneRetrogradeToPrograde": 4702527574238083539, + "NextNeptuneWesternQuadrature": 4702527995131216666 + } + }, + { + "input_utc": "2011-02-10T02:26:03.926Z", + "tt_jd_bits": 4702527777781638822, + "events": { + "LastNeptuneConjunction": 4702527004406607440, + "LastNeptuneEasternQuadrature": 4702527598059113614, + "LastNeptuneOpposition": 4702527404804653731, + "LastNeptuneProgradeToRetrograde": 4702527232266243818, + "LastNeptuneRetrogradeToPrograde": 4702527574238083539, + "LastNeptuneWesternQuadrature": 4702527205815798819, + "NextNeptuneConjunction": 4702527793483315868, + "NextNeptuneEasternQuadrature": 4702528387152357001, + "NextNeptuneOpposition": 4702528194123267306, + "NextNeptuneProgradeToRetrograde": 4702528021597321662, + "NextNeptuneRetrogradeToPrograde": 4702528363569912197, + "NextNeptuneWesternQuadrature": 4702527995131216666 + } + }, + { + "input_utc": "2011-07-27T03:16:30.926Z", + "tt_jd_bits": 4702528136486644531, + "events": { + "LastNeptuneConjunction": 4702527793483315868, + "LastNeptuneEasternQuadrature": 4702527598059113614, + "LastNeptuneOpposition": 4702527404804653731, + "LastNeptuneProgradeToRetrograde": 4702528021597321662, + "LastNeptuneRetrogradeToPrograde": 4702527574238083539, + "LastNeptuneWesternQuadrature": 4702527995131216666, + "NextNeptuneConjunction": 4702528582571314390, + "NextNeptuneEasternQuadrature": 4702528387152357001, + "NextNeptuneOpposition": 4702528194123267306, + "NextNeptuneProgradeToRetrograde": 4702528811012343032, + "NextNeptuneRetrogradeToPrograde": 4702528363569912197, + "NextNeptuneWesternQuadrature": 4702528784456508121 + } + }, + { + "input_utc": "2012-01-10T07:35:57.926Z", + "tt_jd_bits": 4702528495503333631, + "events": { + "LastNeptuneConjunction": 4702527793483315868, + "LastNeptuneEasternQuadrature": 4702528387152357001, + "LastNeptuneOpposition": 4702528194123267306, + "LastNeptuneProgradeToRetrograde": 4702528021597321662, + "LastNeptuneRetrogradeToPrograde": 4702528363569912197, + "LastNeptuneWesternQuadrature": 4702527995131216666, + "NextNeptuneConjunction": 4702528582571314390, + "NextNeptuneEasternQuadrature": 4702529176217047087, + "NextNeptuneOpposition": 4702528983421437177, + "NextNeptuneProgradeToRetrograde": 4702528811012343032, + "NextNeptuneRetrogradeToPrograde": 4702529152916422918, + "NextNeptuneWesternQuadrature": 4702528784456508121 + } + }, + { + "input_utc": "2012-06-25T11:55:24.926Z", + "tt_jd_bits": 4702528854520022731, + "events": { + "LastNeptuneConjunction": 4702528582571314390, + "LastNeptuneEasternQuadrature": 4702528387152357001, + "LastNeptuneOpposition": 4702528194123267306, + "LastNeptuneProgradeToRetrograde": 4702528811012343032, + "LastNeptuneRetrogradeToPrograde": 4702528363569912197, + "LastNeptuneWesternQuadrature": 4702528784456508121, + "NextNeptuneConjunction": 4702529371649031405, + "NextNeptuneEasternQuadrature": 4702529176217047087, + "NextNeptuneOpposition": 4702528983421437177, + "NextNeptuneProgradeToRetrograde": 4702529600221816082, + "NextNeptuneRetrogradeToPrograde": 4702529152916422918, + "NextNeptuneWesternQuadrature": 4702529573792022542 + } + }, + { + "input_utc": "2012-12-08T04:14:51.926Z", + "tt_jd_bits": 4702529210315511213, + "events": { + "LastNeptuneConjunction": 4702528582571314390, + "LastNeptuneEasternQuadrature": 4702529176217047087, + "LastNeptuneOpposition": 4702528983421437177, + "LastNeptuneProgradeToRetrograde": 4702528811012343032, + "LastNeptuneRetrogradeToPrograde": 4702529152916422918, + "LastNeptuneWesternQuadrature": 4702528784456508121, + "NextNeptuneConjunction": 4702529371649031405, + "NextNeptuneEasternQuadrature": 4702529965299810834, + "NextNeptuneOpposition": 4702529772727858401, + "NextNeptuneProgradeToRetrograde": 4702529600221816082, + "NextNeptuneRetrogradeToPrograde": 4702529942069222453, + "NextNeptuneWesternQuadrature": 4702529573792022542 + } + }, + { + "input_utc": "2013-05-24T08:34:18.926Z", + "tt_jd_bits": 4702529569332200313, + "events": { + "LastNeptuneConjunction": 4702529371649031405, + "LastNeptuneEasternQuadrature": 4702529176217047087, + "LastNeptuneOpposition": 4702528983421437177, + "LastNeptuneProgradeToRetrograde": 4702528811012343032, + "LastNeptuneRetrogradeToPrograde": 4702529152916422918, + "LastNeptuneWesternQuadrature": 4702528784456508121, + "NextNeptuneConjunction": 4702530160748173751, + "NextNeptuneEasternQuadrature": 4702529965299810834, + "NextNeptuneOpposition": 4702529772727858401, + "NextNeptuneProgradeToRetrograde": 4702529600221816082, + "NextNeptuneRetrogradeToPrograde": 4702529942069222453, + "NextNeptuneWesternQuadrature": 4702529573792022542 + } + }, + { + "input_utc": "2013-11-07T12:53:45.926Z", + "tt_jd_bits": 4702529928348889413, + "events": { + "LastNeptuneConjunction": 4702529371649031405, + "LastNeptuneEasternQuadrature": 4702529176217047087, + "LastNeptuneOpposition": 4702529772727858401, + "LastNeptuneProgradeToRetrograde": 4702529600221816082, + "LastNeptuneRetrogradeToPrograde": 4702529152916422918, + "LastNeptuneWesternQuadrature": 4702529573792022542, + "NextNeptuneConjunction": 4702530160748173751, + "NextNeptuneEasternQuadrature": 4702529965299810834, + "NextNeptuneOpposition": 4702530562002436201, + "NextNeptuneProgradeToRetrograde": 4702530389449176949, + "NextNeptuneRetrogradeToPrograde": 4702529942069222453, + "NextNeptuneWesternQuadrature": 4702530363107102452 + } + }, + { + "input_utc": "2014-04-23T17:13:12.926Z", + "tt_jd_bits": 4702530287365578512, + "events": { + "LastNeptuneConjunction": 4702530160748173751, + "LastNeptuneEasternQuadrature": 4702529965299810834, + "LastNeptuneOpposition": 4702529772727858401, + "LastNeptuneProgradeToRetrograde": 4702529600221816082, + "LastNeptuneRetrogradeToPrograde": 4702529942069222453, + "LastNeptuneWesternQuadrature": 4702529573792022542, + "NextNeptuneConjunction": 4702530949834918424, + "NextNeptuneEasternQuadrature": 4702530754360961588, + "NextNeptuneOpposition": 4702530562002436201, + "NextNeptuneProgradeToRetrograde": 4702530389449176949, + "NextNeptuneRetrogradeToPrograde": 4702530731369828764, + "NextNeptuneWesternQuadrature": 4702530363107102452 + } + }, + { + "input_utc": "2014-10-07T21:32:39.926Z", + "tt_jd_bits": 4702530646382267612, + "events": { + "LastNeptuneConjunction": 4702530160748173751, + "LastNeptuneEasternQuadrature": 4702529965299810834, + "LastNeptuneOpposition": 4702530562002436201, + "LastNeptuneProgradeToRetrograde": 4702530389449176949, + "LastNeptuneRetrogradeToPrograde": 4702529942069222453, + "LastNeptuneWesternQuadrature": 4702530363107102452, + "NextNeptuneConjunction": 4702530949834918424, + "NextNeptuneEasternQuadrature": 4702530754360961588, + "NextNeptuneOpposition": 4702531351300044432, + "NextNeptuneProgradeToRetrograde": 4702531178827541690, + "NextNeptuneRetrogradeToPrograde": 4702530731369828764, + "NextNeptuneWesternQuadrature": 4702531152433685546 + } + }, + { + "input_utc": "2015-03-24T01:52:06.926Z", + "tt_jd_bits": 4702531005398956712, + "events": { + "LastNeptuneConjunction": 4702530949834918424, + "LastNeptuneEasternQuadrature": 4702530754360961588, + "LastNeptuneOpposition": 4702530562002436201, + "LastNeptuneProgradeToRetrograde": 4702530389449176949, + "LastNeptuneRetrogradeToPrograde": 4702530731369828764, + "LastNeptuneWesternQuadrature": 4702530363107102452, + "NextNeptuneConjunction": 4702531738934209180, + "NextNeptuneEasternQuadrature": 4702531543427424177, + "NextNeptuneOpposition": 4702531351300044432, + "NextNeptuneProgradeToRetrograde": 4702531178827541690, + "NextNeptuneRetrogradeToPrograde": 4702531520401833977, + "NextNeptuneWesternQuadrature": 4702531152433685546 + } + }, + { + "input_utc": "2015-09-07T06:05:15.926Z", + "tt_jd_bits": 4702531364406275426, + "events": { + "LastNeptuneConjunction": 4702530949834918424, + "LastNeptuneEasternQuadrature": 4702530754360961588, + "LastNeptuneOpposition": 4702531351300044432, + "LastNeptuneProgradeToRetrograde": 4702531178827541690, + "LastNeptuneRetrogradeToPrograde": 4702530731369828764, + "LastNeptuneWesternQuadrature": 4702531152433685546, + "NextNeptuneConjunction": 4702531738934209180, + "NextNeptuneEasternQuadrature": 4702531543427424177, + "NextNeptuneOpposition": 4702532140589249872, + "NextNeptuneProgradeToRetrograde": 4702531968048009077, + "NextNeptuneRetrogradeToPrograde": 4702531520401833977, + "NextNeptuneWesternQuadrature": 4702531941774595281 + } + }, + { + "input_utc": "2016-02-21T10:24:42.926Z", + "tt_jd_bits": 4702531723422964526, + "events": { + "LastNeptuneConjunction": 4702530949834918424, + "LastNeptuneEasternQuadrature": 4702531543427424177, + "LastNeptuneOpposition": 4702531351300044432, + "LastNeptuneProgradeToRetrograde": 4702531178827541690, + "LastNeptuneRetrogradeToPrograde": 4702531520401833977, + "LastNeptuneWesternQuadrature": 4702531152433685546, + "NextNeptuneConjunction": 4702531738934209180, + "NextNeptuneEasternQuadrature": 4702532332489429640, + "NextNeptuneOpposition": 4702532140589249872, + "NextNeptuneProgradeToRetrograde": 4702531968048009077, + "NextNeptuneRetrogradeToPrograde": 4702532309683865260, + "NextNeptuneWesternQuadrature": 4702531941774595281 + } + }, + { + "input_utc": "2016-08-06T11:15:09.926Z", + "tt_jd_bits": 4702532082127970235, + "events": { + "LastNeptuneConjunction": 4702531738934209180, + "LastNeptuneEasternQuadrature": 4702531543427424177, + "LastNeptuneOpposition": 4702531351300044432, + "LastNeptuneProgradeToRetrograde": 4702531968048009077, + "LastNeptuneRetrogradeToPrograde": 4702531520401833977, + "LastNeptuneWesternQuadrature": 4702531941774595281, + "NextNeptuneConjunction": 4702532528040555695, + "NextNeptuneEasternQuadrature": 4702532332489429640, + "NextNeptuneOpposition": 4702532140589249872, + "NextNeptuneProgradeToRetrograde": 4702532757517133111, + "NextNeptuneRetrogradeToPrograde": 4702532309683865224, + "NextNeptuneWesternQuadrature": 4702532731110749057 + } + }, + { + "input_utc": "2017-01-19T03:34:36.926Z", + "tt_jd_bits": 4702532437923458718, + "events": { + "LastNeptuneConjunction": 4702531738934209180, + "LastNeptuneEasternQuadrature": 4702532332489429640, + "LastNeptuneOpposition": 4702532140589249872, + "LastNeptuneProgradeToRetrograde": 4702531968048009077, + "LastNeptuneRetrogradeToPrograde": 4702532309683865260, + "LastNeptuneWesternQuadrature": 4702531941774595281, + "NextNeptuneConjunction": 4702532528040555695, + "NextNeptuneEasternQuadrature": 4702533121551116508, + "NextNeptuneOpposition": 4702532929864005523, + "NextNeptuneProgradeToRetrograde": 4702532757517133111, + "NextNeptuneRetrogradeToPrograde": 4702533098732565043, + "NextNeptuneWesternQuadrature": 4702532731110749057 + } + }, + { + "input_utc": "2017-07-05T07:54:03.926Z", + "tt_jd_bits": 4702532796940147817, + "events": { + "LastNeptuneConjunction": 4702532528040555695, + "LastNeptuneEasternQuadrature": 4702532332489429640, + "LastNeptuneOpposition": 4702532140589249872, + "LastNeptuneProgradeToRetrograde": 4702532757517133111, + "LastNeptuneRetrogradeToPrograde": 4702532309683865260, + "LastNeptuneWesternQuadrature": 4702532731110749057, + "NextNeptuneConjunction": 4702533317166107290, + "NextNeptuneEasternQuadrature": 4702533121551116508, + "NextNeptuneOpposition": 4702532929864005523, + "NextNeptuneProgradeToRetrograde": 4702533546787825059, + "NextNeptuneRetrogradeToPrograde": 4702533098732565043, + "NextNeptuneWesternQuadrature": 4702533520467115858 + } + }, + { + "input_utc": "2017-12-19T12:13:30.926Z", + "tt_jd_bits": 4702533155956836917, + "events": { + "LastNeptuneConjunction": 4702532528040555695, + "LastNeptuneEasternQuadrature": 4702533121551116508, + "LastNeptuneOpposition": 4702532929864005523, + "LastNeptuneProgradeToRetrograde": 4702532757517133111, + "LastNeptuneRetrogradeToPrograde": 4702533098732565043, + "LastNeptuneWesternQuadrature": 4702532731110749057, + "NextNeptuneConjunction": 4702533317166107290, + "NextNeptuneEasternQuadrature": 4702533910628975131, + "NextNeptuneOpposition": 4702533719152363502, + "NextNeptuneProgradeToRetrograde": 4702533546787825059, + "NextNeptuneRetrogradeToPrograde": 4702533887892789084, + "NextNeptuneWesternQuadrature": 4702533520467115858 + } + } + ] +} diff --git a/basic/testdata/semianalytic/occultation_limit_anchors.json b/basic/testdata/semianalytic/occultation_limit_anchors.json new file mode 100644 index 0000000..fe6518a --- /dev/null +++ b/basic/testdata/semianalytic/occultation_limit_anchors.json @@ -0,0 +1,10 @@ +{ + "Jupiter 1962-10-10/greatest limit separation": 3593.2109969295625, + "Jupiter 1962-10-10/greatest total width": 3385.16335804256, + "Jupiter 1962-10-10/greatest width": 5319.126309677706, + "Jupiter 1962-10-10/limit ratio": 0.6755265409644466, + "Mars 2025-07-29/greatest limit separation": 1818.6083653164637, + "Mars 2025-07-29/greatest total width": 1787.8335304125978, + "Mars 2025-07-29/greatest width": 2189.861629221675, + "Mars 2025-07-29/limit ratio": 0.8304672500987368 +} diff --git a/basic/testdata/semianalytic/planet_apparent_de441.csv b/basic/testdata/semianalytic/planet_apparent_de441.csv new file mode 100644 index 0000000..5b12427 --- /dev/null +++ b/basic/testdata/semianalytic/planet_apparent_de441.csv @@ -0,0 +1,420 @@ +1,2442852.598320,341.576701247986,-10.220169244744 +1,2443032.631368,185.335687388556,-6.813356235888 +1,2443800.324081,214.660479431796,-14.539591705924 +1,2446609.036940,123.081472751385,20.098377515469 +1,2447580.508890,310.870383694663,-18.665990307590 +1,2449380.722868,326.478181632146,-14.619661502007 +1,2453172.193713,80.092163541065,23.677211387284 +1,2460075.035542,34.880051355688,12.151286139876 +1,2460576.115139,174.400851246083,4.383712132577 +1,2461252.352425,109.855021204234,19.710256380220 +1,2465037.490905,279.961414589097,-24.836164012653 +1,2466002.242853,112.103368678974,19.249956616840 +1,2467731.003899,11.720445794900,2.905603481859 +1,2468894.937849,82.636123985354,19.087650288203 +1,2471518.037136,189.789472505386,-6.271067821244 +1,2472382.850203,278.830931576002,-22.942098167939 +1,2473879.699426,311.754638626693,-17.852909734497 +1,2475152.732450,134.407725943792,14.683137859924 +1,2477709.152944,131.409192150415,17.138193302474 +1,2478046.173972,108.404779679976,17.998672928286 +1,2480398.374847,271.342207636805,-24.372344768996 +1,2484893.456907,5.628695410821,-0.426348063089 +1,2485866.166155,276.467633004557,-25.281387139330 +1,2487031.436974,349.790533816267,-5.482101524519 +1,2487993.960597,216.802184388760,-17.741111463506 +1,2494748.687580,1.197725248301,-2.285446768909 +1,2497233.267900,329.223036397127,-14.012991463569 +1,2497436.567300,156.704069378264,11.569797338477 +1,2500839.356648,288.683624363046,-24.167007579537 +1,2501741.816126,90.628433251178,25.296181792182 +1,2502120.913950,114.195901261900,20.891372987687 +1,2502358.271960,330.513924439160,-8.270944964097 +1,2502867.051711,88.556065833609,22.481605728965 +1,2502957.731709,181.959059058073,-1.505751174425 +1,2506044.978524,15.369275585240,8.001327052403 +1,2507497.593138,330.532455215758,-13.884057186632 +1,2511788.853206,243.323514177556,-20.433534419623 +1,2512752.169911,149.289610871315,13.824952849083 +1,2514440.194448,329.997382296821,-13.333568754744 +1,2515573.689052,24.153563017819,11.170182998663 +1,2515916.600632,8.873183461338,7.157710850784 +1,2516402.327587,113.711114943131,17.473978985428 +1,2517635.922509,282.294331235972,-25.209570532946 +1,2523787.480998,225.550872551923,-20.431960482980 +1,2529636.120387,198.799351502242,-6.151040289821 +1,2533624.053259,201.606621069221,-10.912681516078 +1,2535620.237095,342.804949855479,-3.817835472761 +1,2536138.502919,141.863000793180,16.796336663288 +1,2538404.248614,217.406372238129,-17.078292747595 +1,2538979.286032,62.560639894664,20.386504327908 +1,2540016.322404,7.449139965338,7.000005570855 +1,2542027.356019,168.815724262853,6.592842165867 +1,2544871.683287,136.137132856207,16.574860885308 +1,2545132.028984,344.939870835646,-5.666734847345 +1,2545929.116990,80.397090950171,22.433774875756 +1,2547914.179215,213.901277204312,-12.784341273427 +1,2550260.506946,359.466287785558,-2.951078377206 +1,2557501.527484,337.165885492433,-9.277632154320 +1,2558702.356885,34.872276879908,10.287222250270 +1,2559471.391828,125.900490606972,19.825120324622 +2,2442852.598320,332.529040338110,-12.416229079316 +2,2443032.631368,190.941961310501,-3.783482476309 +2,2443800.324081,228.510157774356,-24.994422886448 +2,2446609.036940,136.772075333130,18.502945736375 +2,2447580.508890,326.844756227653,-14.597462155340 +2,2449380.722868,313.528925566070,-18.720318751184 +2,2453172.193713,72.399815105492,20.600536849854 +2,2460075.035542,93.571022650360,26.084093333078 +2,2460576.115139,207.389627288135,-11.119923204149 +2,2461252.352425,173.478854736293,2.904358629219 +2,2465037.490905,232.176845196689,-17.480428029826 +2,2466002.242853,140.694472172576,8.838177991323 +2,2467731.003899,71.066907460838,27.494515049306 +2,2468894.937849,141.647879510083,14.737110296015 +2,2471518.037136,157.928331609431,10.654967987550 +2,2472382.850203,346.361937703585,-4.985975177034 +2,2473879.699426,334.194144568619,-12.125080629121 +2,2475152.732450,172.987202854346,4.391657155531 +2,2477709.152944,119.824254585539,13.427518148091 +2,2478046.173972,140.438971436051,16.995173349230 +2,2480398.374847,303.153693052525,-21.494646152136 +2,2484893.456907,7.220460402257,1.396096096921 +2,2485866.166155,264.821776183222,-21.387909315836 +2,2487031.436974,341.236360302253,1.482582743215 +2,2487993.960597,224.968128901423,-17.096937955705 +2,2494748.687580,346.975331988775,-6.587977722417 +2,2497233.267900,311.717338524437,-18.973871481798 +2,2497436.567300,194.001263066247,-6.510391358667 +2,2500839.356648,290.703293423880,-23.493281025693 +2,2501741.816126,33.910587533523,11.169224945055 +2,2502120.913950,138.206157864301,18.157983500346 +2,2502358.271960,290.331580201177,-21.280958021345 +2,2502867.051711,58.858091067185,16.881748880184 +2,2502957.731709,163.130271444483,8.319826634094 +2,2506044.978524,8.029779147509,2.114518631666 +2,2507497.593138,318.360953420760,-9.485707979255 +2,2511788.853206,242.165126535432,-20.083565658259 +2,2512752.169911,94.309213624873,17.506256978108 +2,2514440.194448,31.266167210447,18.221897951551 +2,2515573.689052,76.222853374436,26.272351392911 +2,2515916.600632,4.229443844285,0.271285415678 +2,2516402.327587,97.597553870474,22.506563319552 +2,2517635.922509,247.613594083593,-21.142001401135 +2,2523787.480998,236.830784458281,-26.200898696270 +2,2529636.120387,233.354166711124,-25.964089469355 +2,2533624.053259,218.186667585478,-16.148882194995 +2,2535620.237095,311.874716790442,-17.315306245275 +2,2536138.502919,94.093027123903,19.272711016950 +2,2538404.248614,223.757092489497,-23.853178162048 +2,2538979.286032,90.661647236696,27.024822095278 +2,2540016.322404,34.987505155376,14.236442381011 +2,2542027.356019,141.982271600770,14.490535365761 +2,2544871.683287,70.519137801655,17.674538872183 +2,2545132.028984,5.211099863142,0.741825363243 +2,2545929.116990,118.771335554184,23.501955129681 +2,2547914.179215,191.096475991104,-2.941590064633 +2,2550260.506946,349.994594813859,-5.645149671557 +2,2557501.527484,345.681149406427,-7.662235174318 +2,2558702.356885,95.389866325611,25.048137691930 +2,2559471.391828,59.766502230101,16.698219375040 +3,2442852.598320,88.347698644069,25.809014344084 +3,2443032.631368,190.821055171554,-4.132837122247 +3,2443800.324081,227.349790399485,-18.038345586569 +3,2446609.036940,293.544277823324,-26.520436999467 +3,2447580.508890,47.335345502769,18.948739769448 +3,2449380.722868,302.449006575037,-21.112629599007 +3,2453172.193713,117.022260762345,22.376801859523 +3,2460075.035542,116.506417429728,22.903790204651 +3,2460576.115139,101.258370216269,23.373723817128 +3,2461252.352425,81.603176884873,23.291204743365 +3,2465037.490905,230.268130613362,-18.151449215602 +3,2466002.242853,58.925563088169,19.402068922939 +3,2467731.003899,163.949501661010,9.278965008472 +3,2468894.937849,99.951137819229,23.953680299301 +3,2471518.037136,74.655400195447,21.839723925337 +3,2472382.850203,129.989997579822,22.920026351933 +3,2473879.699426,201.536828290242,-5.764172496925 +3,2475152.732450,141.116691024759,16.425626110680 +3,2477709.152944,76.489240746959,22.392174167857 +3,2478046.173972,170.192437782985,5.071510290624 +3,2480398.374847,330.962376207407,-13.024062085171 +3,2484893.456907,159.321374238669,11.341705006816 +3,2485866.166155,315.924576438806,-18.067764734463 +3,2487031.436974,260.125769705333,-22.855695307682 +3,2487993.960597,24.677467272361,7.379428765784 +3,2494748.687580,344.443258501769,-8.053319995644 +3,2497233.267900,210.018971571032,-9.886032764993 +3,2497436.567300,230.238173174017,-20.079019769365 +3,2500839.356648,261.171869641844,-23.702961377541 +3,2501741.816126,47.357502253435,17.248837072599 +3,2502120.913950,173.762666646739,3.441823189084 +3,2502358.271960,339.938445795536,-9.515157485575 +3,2502867.051711,197.888636422420,-8.095187581105 +3,2502957.731709,252.474033558012,-23.884093542574 +3,2506044.978524,86.007989685038,25.492365990600 +3,2507497.593138,144.524096216458,18.329339525581 +3,2511788.853206,243.458690592757,-21.300006165801 +3,2512752.169911,74.322321450270,22.393726374848 +3,2514440.194448,211.827292943399,-10.081228518494 +3,2515573.689052,60.181196102250,21.235143821287 +3,2515916.600632,281.615947598493,-23.373350307475 +3,2516402.327587,142.337328017356,16.042943582009 +3,2517635.922509,82.183545608765,25.886617061729 +3,2523787.480998,109.111761755515,22.975396515183 +3,2529636.120387,219.628371497136,-15.422130170950 +3,2533624.053259,173.946866045148,3.729914186354 +3,2535620.237095,110.817841252760,25.327033288912 +3,2536138.502919,89.837576903227,23.594631764650 +3,2538404.248614,177.302798508215,2.524086564289 +3,2538979.286032,88.963340236638,24.399921870005 +3,2540016.322404,308.666749108116,-19.701718823939 +3,2542027.356019,217.625997218225,-15.097518605916 +3,2544871.683287,327.144014694151,-18.802899667264 +3,2545132.028984,58.288522590984,21.214317729956 +3,2545929.116990,119.243918846085,22.110933869308 +3,2547914.179215,148.843841829038,14.425254932802 +3,2550260.506946,278.733350504231,-23.527908666378 +3,2557501.527484,53.140034137959,20.970452220548 +3,2558702.356885,21.632213988007,7.941336976659 +3,2559471.391828,65.306810417102,21.335592813945 +4,2442852.598320,25.851360702246,9.602469344121 +4,2443032.631368,59.184616116583,19.311040367575 +4,2443800.324081,129.297213102485,18.908760508922 +4,2446609.036940,353.581181039116,-4.102011087786 +4,2447580.508890,55.856982471027,19.051036318455 +4,2449380.722868,221.085027554622,-14.637702429695 +4,2453172.193713,163.287863333676,8.407715142570 +4,2460075.035542,26.934910057244,9.970321965656 +4,2460576.115139,80.126201476353,22.416180779306 +4,2461252.352425,129.224402914209,19.055425288594 +4,2465037.490905,80.801458419350,22.612546405206 +4,2466002.242853,157.026348854695,10.626767822577 +4,2467731.003899,308.094642458773,-19.155420280936 +4,2468894.937849,45.883706389628,16.201702643107 +4,2471518.037136,237.537588795222,-19.421191741542 +4,2472382.850203,321.145247870925,-15.969596807495 +4,2473879.699426,85.554570054514,23.252001420048 +4,2475152.732450,189.225486084757,-2.730878512988 +4,2477709.152944,62.340153453008,20.004305395812 +4,2478046.173972,88.308224583008,23.055950271759 +4,2480398.374847,269.272188898948,-23.166095207193 +4,2484893.456907,293.636693056775,-21.637239929867 +4,2485866.166155,355.951503598594,-3.172925983388 +4,2487031.436974,101.262423775420,23.234911969829 +4,2487993.960597,187.960595863041,-2.238351085529 +4,2494748.687580,24.867213789823,9.207339708956 +4,2497233.267900,237.176416029955,-18.978777488762 +4,2497436.567300,231.836864395345,-18.009204468501 +4,2500839.356648,184.585311989477,-0.650331215794 +4,2501741.816126,237.441796332589,-19.037899798478 +4,2502120.913950,273.797418429390,-23.128183524258 +4,2502358.271960,299.928004957757,-20.790425201494 +4,2502867.051711,348.848845509881,-6.090849878430 +4,2502957.731709,340.844476967813,-9.598193609658 +4,2506044.978524,249.337563653289,-21.125998113525 +4,2507497.593138,2.450181829288,-0.108129151245 +4,2511788.853206,350.488433886863,-5.500601389560 +4,2512752.169911,94.065145884934,23.074148202694 +4,2514440.194448,226.773823623611,-16.170057953456 +4,2515573.689052,324.965516307458,-14.603096961008 +4,2515916.600632,348.076904528612,-6.141671094691 +4,2516402.327587,36.273927470728,13.028184957668 +4,2517635.922509,145.105431634935,14.696004538235 +4,2523787.480998,264.052278706141,-23.124041593051 +4,2529636.120387,51.221179914456,17.325922267790 +4,2533624.053259,20.402633251570,6.868510559062 +4,2535620.237095,187.542374153264,-1.567433538929 +4,2536138.502919,211.572767533323,-11.634128329131 +4,2538404.248614,62.459162027765,19.876687188954 +4,2538979.286032,101.714443025049,23.066532892188 +4,2540016.322404,190.439239686431,-2.777081684241 +4,2542027.356019,352.541770661414,-4.866157612849 +4,2544871.683287,217.172871409712,-13.457730641927 +4,2545132.028984,258.833708142824,-22.236836130777 +4,2545929.116990,328.834604134851,-13.406944364139 +4,2547914.179215,143.716165098184,14.988168334676 +4,2550260.506946,327.291859012169,-13.827548247150 +4,2557501.527484,210.232107770198,-10.846858376753 +4,2558702.356885,309.273992476196,-18.851371927827 +4,2559471.391828,15.456012136818,5.213567885259 +5,2442852.598320,118.253839397897,21.281380188772 +5,2443032.631368,134.558950090671,17.771922711659 +5,2443800.324081,162.287860113834,9.200872575622 +5,2446609.036940,242.721314825782,-19.074584409753 +5,2447580.508890,282.148627663078,-22.287650503124 +5,2449380.722868,332.514521110757,-12.828239313171 +5,2453172.193713,105.039900157300,22.417597211006 +5,2460075.035542,338.355955394862,-10.638980308175 +5,2460576.115139,346.991141218424,-7.956471454267 +5,2461252.352425,14.543278676217,3.481596584689 +5,2465037.490905,155.343658332939,11.751638673109 +5,2466002.242853,177.302353212743,3.490933106154 +5,2467731.003899,236.789895308237,-17.557935598069 +5,2468894.937849,270.580905012619,-22.379409926276 +5,2471518.037136,355.624116185688,-4.486754997401 +5,2472382.850203,16.398097916159,4.337446017062 +5,2473879.699426,69.333060758421,20.534910343450 +5,2475152.732450,129.161456436185,18.986200341804 +5,2477709.152944,206.152516825591,-8.300436582537 +5,2478046.173972,215.604084106246,-11.628884261645 +5,2480398.374847,295.035983552632,-21.446430496139 +5,2484893.456907,81.223335681073,22.096361629957 +5,2485866.166155,129.350725720601,18.940326877528 +5,2487031.436974,167.604533067443,7.636498152550 +5,2487993.960597,197.598565263793,-5.049598009398 +5,2494748.687580,49.498749762834,16.197595798405 +5,2497233.267900,150.112873470812,13.713926137414 +5,2497436.567300,155.558429860428,11.657716053352 +5,2500839.356648,262.949143000758,-22.077138114505 +5,2501741.816126,295.532683637599,-21.134975340668 +5,2502120.913950,307.480969698416,-19.310975557324 +5,2502358.271960,313.113815061881,-18.127025893953 +5,2502867.051711,331.432888157521,-13.136682841957 +5,2502957.731709,325.980388718497,-15.151051723723 +5,2506044.978524,66.797032887188,20.120392806010 +5,2507497.593138,126.045978633411,19.937680748731 +5,2511788.853206,268.321316437613,-22.425518765983 +5,2512752.169911,299.422188537626,-20.832515202606 +5,2514440.194448,353.192254230242,-4.978769291855 +5,2515573.689052,29.005319835888,9.481657459018 +5,2515916.600632,37.635883149084,12.530204450943 +5,2516402.327587,64.086791936121,19.315107181285 +5,2517635.922509,110.275236243275,21.661508387382 +5,2523787.480998,303.980171754825,-20.288202791221 +5,2529636.120387,158.870960391827,10.357049983716 +5,2533624.053259,274.492615968508,-22.664241599449 +5,2535620.237095,344.209011271853,-8.467061201361 +5,2536138.502919,3.228328291590,-1.210728357379 +5,2538404.248614,83.356584567189,21.661895628811 +5,2538979.286032,97.123291510355,22.638255209391 +5,2540016.322404,136.717489063526,17.622874160171 +5,2542027.356019,204.576114925581,-7.776778904650 +5,2544871.683287,297.848367319648,-20.901641172466 +5,2545132.028984,310.249139452338,-18.581109464663 +5,2545929.116990,335.679883272790,-11.525351632279 +5,2547914.179215,30.806821554401,9.579086864250 +5,2550260.506946,115.404665760522,21.526322540035 +5,2557501.527484,352.182090074661,-5.474549907853 +5,2558702.356885,35.823370826717,11.856501573903 +5,2559471.391828,64.594373927862,19.498870162663 +6,2442852.598320,214.502089018755,-13.258431741958 +6,2443032.631368,212.500793488804,-12.637213675936 +6,2443800.324081,223.027134751047,-16.125026308782 +6,2446609.036940,258.828959178186,-23.139436511762 +6,2447580.508890,274.872393711238,-23.599932413600 +6,2449380.722868,295.104365052052,-21.899060439617 +6,2453172.193713,338.810953639826,-9.739668410203 +6,2460075.035542,46.611421569667,17.162047076090 +6,2460576.115139,54.850268311535,19.245343309820 +6,2461252.352425,63.058597212730,20.972846529271 +6,2465037.490905,110.355262776252,22.560136425714 +6,2466002.242853,122.003130300362,20.713208010376 +6,2467731.003899,140.686183187066,16.114285939972 +6,2468894.937849,155.706859883807,10.905003734752 +6,2471518.037136,188.264693991693,-2.843366185376 +6,2472382.850203,202.585868243224,-8.780983527778 +6,2473879.699426,220.562017496249,-15.274500020072 +6,2475152.732450,230.485704227956,-18.236509045882 +6,2477709.152944,263.374235959522,-23.456468045862 +6,2478046.173972,268.883427083422,-23.649414281411 +6,2480398.374847,298.655126942362,-21.334392242556 +6,2484893.456907,349.564359430205,-5.298165506131 +6,2485866.166155,354.791422674385,-3.073277697207 +6,2487031.436974,7.578487926189,2.538901700870 +6,2487993.960597,18.236716346342,7.005363954992 +6,2494748.687580,94.105525257604,23.657389540392 +6,2497233.267900,129.508347961680,19.152435667206 +6,2497436.567300,133.611450542120,18.042766868950 +6,2500839.356648,177.243770612509,2.028167898597 +6,2501741.816126,182.313229626054,-0.199664749704 +6,2502120.913950,186.720635588457,-2.136059047176 +6,2502358.271960,194.646074426176,-5.487471374336 +6,2502867.051711,195.543609884981,-5.934298313176 +6,2502957.731709,199.197636244327,-7.468117744352 +6,2506044.978524,240.353637529261,-20.417949817531 +6,2507497.593138,259.394025554188,-23.098154053463 +6,2511788.853206,306.894989968212,-19.707390367569 +6,2512752.169911,320.184356162937,-16.256951490145 +6,2514440.194448,338.261343522371,-9.898951164763 +6,2515573.689052,350.598501469233,-4.858832925469 +6,2515916.600632,353.013840770468,-3.815859579244 +6,2516402.327587,359.139720423153,-1.226738080394 +6,2517635.922509,6.668593406217,2.102704538970 +6,2523787.480998,79.830813180074,23.129596672031 +6,2529636.120387,155.142351244151,11.097262845133 +6,2533624.053259,199.426286664022,-7.556846046480 +6,2535620.237095,227.519175210849,-17.322244913025 +6,2536138.502919,228.228122693473,-17.601002723575 +6,2538404.248614,258.007993294181,-23.028232189247 +6,2538979.286032,269.346080370625,-23.584769911251 +6,2540016.322404,284.064324972048,-23.102558520546 +6,2542027.356019,302.526416029786,-20.654801094312 +6,2544871.683287,338.105742169300,-10.015637164919 +6,2545132.028984,339.935873416305,-9.238445846117 +6,2545929.116990,349.358869373119,-5.408287493756 +6,2547914.179215,4.623335987485,1.178644774740 +6,2550260.506946,29.407556301808,11.456636857484 +6,2557501.527484,116.787702249958,21.661903962034 +6,2558702.356885,130.626563084981,18.830300518359 +6,2559471.391828,141.263932343989,15.861930145048 +7,2442852.598320,252.808807498552,-20.953583162890 +7,2443032.631368,249.965945224595,-20.645442922121 +7,2443800.324081,255.225177025732,-21.335004357441 +7,2446609.036940,274.754280571348,-22.262319890885 +7,2447580.508890,282.646100559638,-22.023992587984 +7,2449380.722868,293.117240823133,-21.122372581852 +7,2453172.193713,317.653335731149,-16.342321012341 +7,2460075.035542,357.715868588160,-2.288231247381 +7,2460576.115139,359.124495153381,-1.825001254715 +7,2461252.352425,4.474250094304,0.420802291032 +7,2465037.490905,23.084498359997,7.787230495581 +7,2466002.242853,31.985143694669,11.079194328739 +7,2467731.003899,40.135650170548,13.817930770609 +7,2468894.937849,48.932378204496,16.319235362665 +7,2471518.037136,65.843476954943,19.919851143448 +7,2472382.850203,67.840486841698,20.263196980475 +7,2473879.699426,77.125709983983,21.488839695044 +7,2475152.732450,89.271626520838,22.208134683487 +7,2477709.152944,105.675694203809,21.796273129980 +7,2478046.173972,106.990000397375,21.719569126626 +7,2480398.374847,122.267732683534,19.701619251056 +7,2484893.456907,147.239984391904,13.645641613407 +7,2485866.166155,156.198196677611,10.560959574198 +7,2487031.436974,161.254895848387,8.805291260152 +7,2487993.960597,167.735837202925,6.253155419132 +7,2494748.687580,204.029822083780,-8.147442728835 +7,2497233.267900,219.957466498588,-13.700015127656 +7,2497436.567300,217.583797772322,-12.970221687857 +7,2500839.356648,240.334671638082,-18.972891092753 +7,2501741.816126,244.732536125749,-19.701965826695 +7,2502120.913950,246.707175266747,-20.042354623622 +7,2502358.271960,250.849320987782,-20.691386014325 +7,2502867.051711,251.033679468255,-20.698724859527 +7,2502957.731709,250.995624792276,-20.771777154251 +7,2506044.978524,274.399256570252,-22.201174793260 +7,2507497.593138,283.603077851374,-21.892239385596 +7,2511788.853206,307.431919723570,-18.757908041067 +7,2512752.169911,314.864579093147,-17.019087665662 +7,2514440.194448,325.817310738583,-13.951900006877 +7,2515573.689052,333.160088160401,-11.560444534008 +7,2515916.600632,334.665335866798,-11.054284556267 +7,2516402.327587,337.298400527856,-10.170951775552 +7,2517635.922509,341.766598503948,-8.598681668482 +7,2523787.480998,18.204490008696,5.926622180639 +7,2529636.120387,54.045057915627,17.494548794607 +7,2533624.053259,80.683416196080,21.692971786363 +7,2535620.237095,89.739148565933,22.218653135959 +7,2536138.502919,96.598263751987,22.184509029799 +7,2538404.248614,111.966625384296,21.096182689801 +7,2538979.286032,112.142766452157,21.188259537793 +7,2540016.322404,118.504514030485,20.325916566393 +7,2542027.356019,134.366181786928,17.071694376453 +7,2544871.683287,148.742744712440,13.064174402465 +7,2545132.028984,150.314999821881,12.577554764489 +7,2545929.116990,154.211423455435,11.277876171003 +7,2547914.179215,168.707570978195,5.806688667755 +7,2550260.506946,179.641591108388,1.542288530368 +7,2557501.527484,222.625939354062,-14.510131394612 +7,2558702.356885,227.476738655745,-15.824391070342 +7,2559471.391828,231.059461909055,-16.781216250039 diff --git a/basic/testdata/semianalytic/saturn_event_baseline.json b/basic/testdata/semianalytic/saturn_event_baseline.json new file mode 100644 index 0000000..7ef17fa --- /dev/null +++ b/basic/testdata/semianalytic/saturn_event_baseline.json @@ -0,0 +1,1732 @@ +{ + "samples": [ + { + "input_utc": "1990-07-21T08:12:34.567Z", + "tt_jd_bits": 4702511652843456479, + "events": { + "LastSaturnConjunction": 4702511233082799871, + "LastSaturnEasternQuadrature": 4702511022491997719, + "LastSaturnOpposition": 4702511638661451685, + "LastSaturnProgradeToRetrograde": 4702511486901854564, + "LastSaturnRetrogradeToPrograde": 4702510980431897393, + "LastSaturnWesternQuadrature": 4702511444359747148, + "NextSaturnConjunction": 4702512041495019844, + "NextSaturnEasternQuadrature": 4702511831665430466, + "NextSaturnOpposition": 4702512448855511897, + "NextSaturnProgradeToRetrograde": 4702512297376154580, + "NextSaturnRetrogradeToPrograde": 4702511789774836955, + "NextSaturnWesternQuadrature": 4702512254003900109 + } + }, + { + "input_utc": "1991-03-11T13:19:53.567Z", + "tt_jd_bits": 4702512153665475149, + "events": { + "LastSaturnConjunction": 4702512041495019844, + "LastSaturnEasternQuadrature": 4702511831665430466, + "LastSaturnOpposition": 4702511638661451685, + "LastSaturnProgradeToRetrograde": 4702511486901854564, + "LastSaturnRetrogradeToPrograde": 4702511789774836955, + "LastSaturnWesternQuadrature": 4702511444359747148, + "NextSaturnConjunction": 4702512850183523623, + "NextSaturnEasternQuadrature": 4702512640867054255, + "NextSaturnOpposition": 4702512448855511897, + "NextSaturnProgradeToRetrograde": 4702512297376154580, + "NextSaturnRetrogradeToPrograde": 4702512599370335510, + "NextSaturnWesternQuadrature": 4702512254003900109 + } + }, + { + "input_utc": "1991-10-30T18:27:12.567Z", + "tt_jd_bits": 4702512654487468964, + "events": { + "LastSaturnConjunction": 4702512041495019844, + "LastSaturnEasternQuadrature": 4702512640867054255, + "LastSaturnOpposition": 4702512448855511897, + "LastSaturnProgradeToRetrograde": 4702512297376154580, + "LastSaturnRetrogradeToPrograde": 4702512599370335510, + "LastSaturnWesternQuadrature": 4702512254003900109, + "NextSaturnConjunction": 4702512850183523623, + "NextSaturnEasternQuadrature": 4702513450206973184, + "NextSaturnOpposition": 4702513259309054642, + "NextSaturnProgradeToRetrograde": 4702513108332257840, + "NextSaturnRetrogradeToPrograde": 4702513409105122572, + "NextSaturnWesternQuadrature": 4702513064124092900 + } + }, + { + "input_utc": "1992-06-19T23:34:31.567Z", + "tt_jd_bits": 4702513155309462779, + "events": { + "LastSaturnConjunction": 4702512850183523623, + "LastSaturnEasternQuadrature": 4702512640867054255, + "LastSaturnOpposition": 4702512448855511897, + "LastSaturnProgradeToRetrograde": 4702513108332257840, + "LastSaturnRetrogradeToPrograde": 4702512599370335510, + "LastSaturnWesternQuadrature": 4702513064124092900, + "NextSaturnConjunction": 4702513659304170755, + "NextSaturnEasternQuadrature": 4702513450206973184, + "NextSaturnOpposition": 4702513259309054642, + "NextSaturnProgradeToRetrograde": 4702513919872067788, + "NextSaturnRetrogradeToPrograde": 4702513409105122572, + "NextSaturnWesternQuadrature": 4702513874832681280 + } + }, + { + "input_utc": "1993-02-08T04:41:50.567Z", + "tt_jd_bits": 4702513656131481449, + "events": { + "LastSaturnConjunction": 4702512850183523623, + "LastSaturnEasternQuadrature": 4702513450206973184, + "LastSaturnOpposition": 4702513259309054642, + "LastSaturnProgradeToRetrograde": 4702513108332257840, + "LastSaturnRetrogradeToPrograde": 4702513409105122572, + "LastSaturnWesternQuadrature": 4702513064124092900, + "NextSaturnConjunction": 4702513659304170755, + "NextSaturnEasternQuadrature": 4702514259798480917, + "NextSaturnOpposition": 4702514070085390511, + "NextSaturnProgradeToRetrograde": 4702513919872067788, + "NextSaturnRetrogradeToPrograde": 4702514219285494636, + "NextSaturnWesternQuadrature": 4702513874832681280 + } + }, + { + "input_utc": "1993-09-29T09:49:09.567Z", + "tt_jd_bits": 4702514156953500118, + "events": { + "LastSaturnConjunction": 4702513659304170755, + "LastSaturnEasternQuadrature": 4702513450206973184, + "LastSaturnOpposition": 4702514070085390511, + "LastSaturnProgradeToRetrograde": 4702513919872067788, + "LastSaturnRetrogradeToPrograde": 4702513409105122572, + "LastSaturnWesternQuadrature": 4702513874832681280, + "NextSaturnConjunction": 4702514468983832337, + "NextSaturnEasternQuadrature": 4702514259798480917, + "NextSaturnOpposition": 4702514881266945183, + "NextSaturnProgradeToRetrograde": 4702514731950956278, + "NextSaturnRetrogradeToPrograde": 4702514219285494636, + "NextSaturnWesternQuadrature": 4702514686189564609 + } + }, + { + "input_utc": "1994-05-20T14:56:28.567Z", + "tt_jd_bits": 4702514657775493933, + "events": { + "LastSaturnConjunction": 4702514468983832337, + "LastSaturnEasternQuadrature": 4702514259798480917, + "LastSaturnOpposition": 4702514070085390511, + "LastSaturnProgradeToRetrograde": 4702513919872067788, + "LastSaturnRetrogradeToPrograde": 4702514219285494636, + "LastSaturnWesternQuadrature": 4702513874832681280, + "NextSaturnConjunction": 4702515279344904012, + "NextSaturnEasternQuadrature": 4702515069775059105, + "NextSaturnOpposition": 4702514881266945183, + "NextSaturnProgradeToRetrograde": 4702514731950956278, + "NextSaturnRetrogradeToPrograde": 4702515029844380521, + "NextSaturnWesternQuadrature": 4702514686189564609 + } + }, + { + "input_utc": "1995-01-08T20:03:47.567Z", + "tt_jd_bits": 4702515158597512604, + "events": { + "LastSaturnConjunction": 4702514468983832337, + "LastSaturnEasternQuadrature": 4702515069775059105, + "LastSaturnOpposition": 4702514881266945183, + "LastSaturnProgradeToRetrograde": 4702514731950956278, + "LastSaturnRetrogradeToPrograde": 4702515029844380521, + "LastSaturnWesternQuadrature": 4702514686189564609, + "NextSaturnConjunction": 4702515279344904012, + "NextSaturnEasternQuadrature": 4702515880214499666, + "NextSaturnOpposition": 4702515692894887718, + "NextSaturnProgradeToRetrograde": 4702515544354790793, + "NextSaturnRetrogradeToPrograde": 4702515840919678262, + "NextSaturnWesternQuadrature": 4702515498244737324 + } + }, + { + "input_utc": "1995-08-30T01:11:06.567Z", + "tt_jd_bits": 4702515659419506419, + "events": { + "LastSaturnConjunction": 4702515279344904012, + "LastSaturnEasternQuadrature": 4702515069775059105, + "LastSaturnOpposition": 4702514881266945183, + "LastSaturnProgradeToRetrograde": 4702515544354790793, + "LastSaturnRetrogradeToPrograde": 4702515029844380521, + "LastSaturnWesternQuadrature": 4702515498244737324, + "NextSaturnConjunction": 4702516090514403465, + "NextSaturnEasternQuadrature": 4702515880214499666, + "NextSaturnOpposition": 4702515692894887718, + "NextSaturnProgradeToRetrograde": 4702516357268860063, + "NextSaturnRetrogradeToPrograde": 4702515840919678262, + "NextSaturnWesternQuadrature": 4702516311055707275 + } + }, + { + "input_utc": "1996-04-17T09:18:25.567Z", + "tt_jd_bits": 4702516156214993249, + "events": { + "LastSaturnConjunction": 4702516090514403465, + "LastSaturnEasternQuadrature": 4702515880214499666, + "LastSaturnOpposition": 4702515692894887718, + "LastSaturnProgradeToRetrograde": 4702515544354790793, + "LastSaturnRetrogradeToPrograde": 4702515840919678262, + "LastSaturnWesternQuadrature": 4702515498244737324, + "NextSaturnConjunction": 4702516902555213289, + "NextSaturnEasternQuadrature": 4702516691190423829, + "NextSaturnOpposition": 4702516504989133571, + "NextSaturnProgradeToRetrograde": 4702516357268860063, + "NextSaturnRetrogradeToPrograde": 4702516652440739701, + "NextSaturnWesternQuadrature": 4702516311055707275 + } + }, + { + "input_utc": "1996-12-06T13:15:44.567Z", + "tt_jd_bits": 4702516656932595497, + "events": { + "LastSaturnConjunction": 4702516090514403465, + "LastSaturnEasternQuadrature": 4702515880214499666, + "LastSaturnOpposition": 4702516504989133571, + "LastSaturnProgradeToRetrograde": 4702516357268860063, + "LastSaturnRetrogradeToPrograde": 4702516652440739701, + "LastSaturnWesternQuadrature": 4702516311055707275, + "NextSaturnConjunction": 4702516902555213289, + "NextSaturnEasternQuadrature": 4702516691190423829, + "NextSaturnOpposition": 4702517317565699910, + "NextSaturnProgradeToRetrograde": 4702517170653779998, + "NextSaturnRetrogradeToPrograde": 4702517464207914117, + "NextSaturnWesternQuadrature": 4702517124594159184 + } + }, + { + "input_utc": "1997-07-27T18:23:03.567Z", + "tt_jd_bits": 4702517157754614167, + "events": { + "LastSaturnConjunction": 4702516902555213289, + "LastSaturnEasternQuadrature": 4702516691190423829, + "LastSaturnOpposition": 4702516504989133571, + "LastSaturnProgradeToRetrograde": 4702516357268860063, + "LastSaturnRetrogradeToPrograde": 4702516652440739701, + "LastSaturnWesternQuadrature": 4702517124594159184, + "NextSaturnConjunction": 4702517715498679309, + "NextSaturnEasternQuadrature": 4702517502797519439, + "NextSaturnOpposition": 4702517317565699910, + "NextSaturnProgradeToRetrograde": 4702517170653779998, + "NextSaturnRetrogradeToPrograde": 4702517464207914117, + "NextSaturnWesternQuadrature": 4702517938784445531 + } + }, + { + "input_utc": "1998-03-17T23:26:34.567Z", + "tt_jd_bits": 4702517658570941011, + "events": { + "LastSaturnConjunction": 4702516902555213289, + "LastSaturnEasternQuadrature": 4702517502797519439, + "LastSaturnOpposition": 4702517317565699910, + "LastSaturnProgradeToRetrograde": 4702517170653779998, + "LastSaturnRetrogradeToPrograde": 4702517464207914117, + "LastSaturnWesternQuadrature": 4702517124594159184, + "NextSaturnConjunction": 4702517715498679309, + "NextSaturnEasternQuadrature": 4702518315061530241, + "NextSaturnOpposition": 4702518130601901153, + "NextSaturnProgradeToRetrograde": 4702517984360493277, + "NextSaturnRetrogradeToPrograde": 4702518276343167865, + "NextSaturnWesternQuadrature": 4702517938784445531 + } + }, + { + "input_utc": "1998-11-06T04:33:53.567Z", + "tt_jd_bits": 4702518159392934826, + "events": { + "LastSaturnConjunction": 4702517715498679309, + "LastSaturnEasternQuadrature": 4702517502797519439, + "LastSaturnOpposition": 4702518130601901153, + "LastSaturnProgradeToRetrograde": 4702517984360493277, + "LastSaturnRetrogradeToPrograde": 4702517464207914117, + "LastSaturnWesternQuadrature": 4702517938784445531, + "NextSaturnConjunction": 4702518529340025963, + "NextSaturnEasternQuadrature": 4702518315061530241, + "NextSaturnOpposition": 4702518944056058944, + "NextSaturnProgradeToRetrograde": 4702518798260631044, + "NextSaturnRetrogradeToPrograde": 4702518276343167865, + "NextSaturnWesternQuadrature": 4702518753554402094 + } + }, + { + "input_utc": "1999-06-27T09:41:12.567Z", + "tt_jd_bits": 4702518660214953496, + "events": { + "LastSaturnConjunction": 4702518529340025963, + "LastSaturnEasternQuadrature": 4702518315061530241, + "LastSaturnOpposition": 4702518130601901153, + "LastSaturnProgradeToRetrograde": 4702517984360493277, + "LastSaturnRetrogradeToPrograde": 4702518276343167865, + "LastSaturnWesternQuadrature": 4702517938784445531, + "NextSaturnConjunction": 4702519344013453296, + "NextSaturnEasternQuadrature": 4702519127967829206, + "NextSaturnOpposition": 4702518944056058944, + "NextSaturnProgradeToRetrograde": 4702518798260631044, + "NextSaturnRetrogradeToPrograde": 4702519088992051071, + "NextSaturnWesternQuadrature": 4702518753554402094 + } + }, + { + "input_utc": "2000-02-15T14:48:31.567Z", + "tt_jd_bits": 4702519161036947311, + "events": { + "LastSaturnConjunction": 4702518529340025963, + "LastSaturnEasternQuadrature": 4702519127967829206, + "LastSaturnOpposition": 4702518944056058944, + "LastSaturnProgradeToRetrograde": 4702518798260631044, + "LastSaturnRetrogradeToPrograde": 4702519088992051071, + "LastSaturnWesternQuadrature": 4702518753554402094, + "NextSaturnConjunction": 4702519344013453296, + "NextSaturnEasternQuadrature": 4702519941514994119, + "NextSaturnOpposition": 4702519757846345849, + "NextSaturnProgradeToRetrograde": 4702519612466313577, + "NextSaturnRetrogradeToPrograde": 4702519901997171652, + "NextSaturnWesternQuadrature": 4702519568739629632 + } + }, + { + "input_utc": "2000-10-05T19:55:50.567Z", + "tt_jd_bits": 4702519661858941126, + "events": { + "LastSaturnConjunction": 4702519344013453296, + "LastSaturnEasternQuadrature": 4702519127967829206, + "LastSaturnOpposition": 4702518944056058944, + "LastSaturnProgradeToRetrograde": 4702519612466313577, + "LastSaturnRetrogradeToPrograde": 4702519088992051071, + "LastSaturnWesternQuadrature": 4702519568739629632, + "NextSaturnConjunction": 4702520159413504881, + "NextSaturnEasternQuadrature": 4702519941514994119, + "NextSaturnOpposition": 4702519757846345849, + "NextSaturnProgradeToRetrograde": 4702520426999456595, + "NextSaturnRetrogradeToPrograde": 4702519901997171652, + "NextSaturnWesternQuadrature": 4702520384150966971 + } + }, + { + "input_utc": "2001-05-27T01:03:09.567Z", + "tt_jd_bits": 4702520162680934941, + "events": { + "LastSaturnConjunction": 4702520159413504881, + "LastSaturnEasternQuadrature": 4702519941514994119, + "LastSaturnOpposition": 4702519757846345849, + "LastSaturnProgradeToRetrograde": 4702519612466313577, + "LastSaturnRetrogradeToPrograde": 4702519901997171652, + "LastSaturnWesternQuadrature": 4702519568739629632, + "NextSaturnConjunction": 4702520975353468347, + "NextSaturnEasternQuadrature": 4702520755639430340, + "NextSaturnOpposition": 4702520571879633151, + "NextSaturnProgradeToRetrograde": 4702520426999456595, + "NextSaturnRetrogradeToPrograde": 4702520715408413592, + "NextSaturnWesternQuadrature": 4702520384150966971 + } + }, + { + "input_utc": "2002-01-13T09:10:28.567Z", + "tt_jd_bits": 4702520659476396916, + "events": { + "LastSaturnConjunction": 4702520159413504881, + "LastSaturnEasternQuadrature": 4702519941514994119, + "LastSaturnOpposition": 4702520571879633151, + "LastSaturnProgradeToRetrograde": 4702520426999456595, + "LastSaturnRetrogradeToPrograde": 4702519901997171652, + "LastSaturnWesternQuadrature": 4702520384150966971, + "NextSaturnConjunction": 4702520975353468347, + "NextSaturnEasternQuadrature": 4702520755639430340, + "NextSaturnOpposition": 4702521386065954738, + "NextSaturnProgradeToRetrograde": 4702521241794457001, + "NextSaturnRetrogradeToPrograde": 4702520715408413592, + "NextSaturnWesternQuadrature": 4702521199621573005 + } + }, + { + "input_utc": "2002-09-03T14:17:47.567Z", + "tt_jd_bits": 4702521160298390730, + "events": { + "LastSaturnConjunction": 4702520975353468347, + "LastSaturnEasternQuadrature": 4702520755639430340, + "LastSaturnOpposition": 4702520571879633151, + "LastSaturnProgradeToRetrograde": 4702520426999456595, + "LastSaturnRetrogradeToPrograde": 4702520715408413592, + "LastSaturnWesternQuadrature": 4702520384150966971, + "NextSaturnConjunction": 4702521791599604601, + "NextSaturnEasternQuadrature": 4702521570228101699, + "NextSaturnOpposition": 4702521386065954738, + "NextSaturnProgradeToRetrograde": 4702521241794457001, + "NextSaturnRetrogradeToPrograde": 4702521529307759912, + "NextSaturnWesternQuadrature": 4702521199621573005 + } + }, + { + "input_utc": "2003-04-24T18:15:06.567Z", + "tt_jd_bits": 4702521661015992979, + "events": { + "LastSaturnConjunction": 4702520975353468347, + "LastSaturnEasternQuadrature": 4702521570228101699, + "LastSaturnOpposition": 4702521386065954738, + "LastSaturnProgradeToRetrograde": 4702521241794457001, + "LastSaturnRetrogradeToPrograde": 4702521529307759912, + "LastSaturnWesternQuadrature": 4702521199621573005, + "NextSaturnConjunction": 4702521791599604601, + "NextSaturnEasternQuadrature": 4702522385163266546, + "NextSaturnOpposition": 4702522200274787837, + "NextSaturnProgradeToRetrograde": 4702522056670159268, + "NextSaturnRetrogradeToPrograde": 4702522343647597446, + "NextSaturnWesternQuadrature": 4702522014939346053 + } + }, + { + "input_utc": "2003-12-13T23:22:25.567Z", + "tt_jd_bits": 4702522161837986794, + "events": { + "LastSaturnConjunction": 4702521791599604601, + "LastSaturnEasternQuadrature": 4702521570228101699, + "LastSaturnOpposition": 4702521386065954738, + "LastSaturnProgradeToRetrograde": 4702522056670159268, + "LastSaturnRetrogradeToPrograde": 4702521529307759912, + "LastSaturnWesternQuadrature": 4702522014939346053, + "NextSaturnConjunction": 4702522607909662837, + "NextSaturnEasternQuadrature": 4702522385163266546, + "NextSaturnOpposition": 4702522200274787837, + "NextSaturnProgradeToRetrograde": 4702522871515639267, + "NextSaturnRetrogradeToPrograde": 4702522343647597446, + "NextSaturnWesternQuadrature": 4702522829887210388 + } + }, + { + "input_utc": "2004-08-03T04:29:44.567Z", + "tt_jd_bits": 4702522662659980609, + "events": { + "LastSaturnConjunction": 4702522607909662837, + "LastSaturnEasternQuadrature": 4702522385163266546, + "LastSaturnOpposition": 4702522200274787837, + "LastSaturnProgradeToRetrograde": 4702522056670159268, + "LastSaturnRetrogradeToPrograde": 4702522343647597446, + "LastSaturnWesternQuadrature": 4702522014939346053, + "NextSaturnConjunction": 4702523423987531694, + "NextSaturnEasternQuadrature": 4702523200254560201, + "NextSaturnOpposition": 4702523014362657594, + "NextSaturnProgradeToRetrograde": 4702522871515639267, + "NextSaturnRetrogradeToPrograde": 4702523158325400874, + "NextSaturnWesternQuadrature": 4702522829887210388 + } + }, + { + "input_utc": "2005-03-24T09:37:03.567Z", + "tt_jd_bits": 4702523163481974424, + "events": { + "LastSaturnConjunction": 4702522607909662837, + "LastSaturnEasternQuadrature": 4702522385163266546, + "LastSaturnOpposition": 4702523014362657594, + "LastSaturnProgradeToRetrograde": 4702522871515639267, + "LastSaturnRetrogradeToPrograde": 4702523158325400874, + "LastSaturnWesternQuadrature": 4702522829887210388, + "NextSaturnConjunction": 4702523423987531694, + "NextSaturnEasternQuadrature": 4702523200254560201, + "NextSaturnOpposition": 4702523828232851436, + "NextSaturnProgradeToRetrograde": 4702523686106865906, + "NextSaturnRetrogradeToPrograde": 4702523973299354559, + "NextSaturnWesternQuadrature": 4702523644313450934 + } + }, + { + "input_utc": "2005-11-12T14:40:34.567Z", + "tt_jd_bits": 4702523664298301268, + "events": { + "LastSaturnConjunction": 4702523423987531694, + "LastSaturnEasternQuadrature": 4702523200254560201, + "LastSaturnOpposition": 4702523014362657594, + "LastSaturnProgradeToRetrograde": 4702522871515639267, + "LastSaturnRetrogradeToPrograde": 4702523158325400874, + "LastSaturnWesternQuadrature": 4702523644313450934, + "NextSaturnConjunction": 4702524239574238653, + "NextSaturnEasternQuadrature": 4702524015309486401, + "NextSaturnOpposition": 4702523828232851436, + "NextSaturnProgradeToRetrograde": 4702523686106865906, + "NextSaturnRetrogradeToPrograde": 4702523973299354559, + "NextSaturnWesternQuadrature": 4702524458086858917 + } + }, + { + "input_utc": "2006-07-03T19:47:53.567Z", + "tt_jd_bits": 4702524165120319938, + "events": { + "LastSaturnConjunction": 4702523423987531694, + "LastSaturnEasternQuadrature": 4702524015309486401, + "LastSaturnOpposition": 4702523828232851436, + "LastSaturnProgradeToRetrograde": 4702523686106865906, + "LastSaturnRetrogradeToPrograde": 4702523973299354559, + "LastSaturnWesternQuadrature": 4702523644313450934, + "NextSaturnConjunction": 4702524239574238653, + "NextSaturnEasternQuadrature": 4702524830148407467, + "NextSaturnOpposition": 4702524641761931974, + "NextSaturnProgradeToRetrograde": 4702524500152336126, + "NextSaturnRetrogradeToPrograde": 4702524788384548335, + "NextSaturnWesternQuadrature": 4702524458086858917 + } + }, + { + "input_utc": "2007-02-22T00:55:12.567Z", + "tt_jd_bits": 4702524665942313752, + "events": { + "LastSaturnConjunction": 4702524239574238653, + "LastSaturnEasternQuadrature": 4702524015309486401, + "LastSaturnOpposition": 4702524641761931974, + "LastSaturnProgradeToRetrograde": 4702524500152336126, + "LastSaturnRetrogradeToPrograde": 4702523973299354559, + "LastSaturnWesternQuadrature": 4702524458086858917, + "NextSaturnConjunction": 4702525054505781758, + "NextSaturnEasternQuadrature": 4702524830148407467, + "NextSaturnOpposition": 4702525454861509683, + "NextSaturnProgradeToRetrograde": 4702525313344307587, + "NextSaturnRetrogradeToPrograde": 4702524788384548335, + "NextSaturnWesternQuadrature": 4702525271124627365 + } + }, + { + "input_utc": "2007-10-11T09:02:31.567Z", + "tt_jd_bits": 4702525162737775727, + "events": { + "LastSaturnConjunction": 4702525054505781758, + "LastSaturnEasternQuadrature": 4702524830148407467, + "LastSaturnOpposition": 4702524641761931974, + "LastSaturnProgradeToRetrograde": 4702524500152336126, + "LastSaturnRetrogradeToPrograde": 4702524788384548335, + "LastSaturnWesternQuadrature": 4702524458086858917, + "NextSaturnConjunction": 4702525868627604694, + "NextSaturnEasternQuadrature": 4702525644611446357, + "NextSaturnOpposition": 4702525454861509683, + "NextSaturnProgradeToRetrograde": 4702525313344307587, + "NextSaturnRetrogradeToPrograde": 4702525603357423262, + "NextSaturnWesternQuadrature": 4702525271124627365 + } + }, + { + "input_utc": "2008-05-31T14:09:50.567Z", + "tt_jd_bits": 4702525663559769542, + "events": { + "LastSaturnConjunction": 4702525054505781758, + "LastSaturnEasternQuadrature": 4702525644611446357, + "LastSaturnOpposition": 4702525454861509683, + "LastSaturnProgradeToRetrograde": 4702525313344307587, + "LastSaturnRetrogradeToPrograde": 4702525603357423262, + "LastSaturnWesternQuadrature": 4702525271124627365, + "NextSaturnConjunction": 4702525868627604694, + "NextSaturnEasternQuadrature": 4702526458575522693, + "NextSaturnOpposition": 4702526267512973295, + "NextSaturnProgradeToRetrograde": 4702526125760943366, + "NextSaturnRetrogradeToPrograde": 4702526417756436584, + "NextSaturnWesternQuadrature": 4702526083406105005 + } + }, + { + "input_utc": "2009-01-19T19:17:09.567Z", + "tt_jd_bits": 4702526164381788212, + "events": { + "LastSaturnConjunction": 4702525868627604694, + "LastSaturnEasternQuadrature": 4702525644611446357, + "LastSaturnOpposition": 4702525454861509683, + "LastSaturnProgradeToRetrograde": 4702526125760943366, + "LastSaturnRetrogradeToPrograde": 4702525603357423262, + "LastSaturnWesternQuadrature": 4702526083406105005, + "NextSaturnConjunction": 4702526681841740281, + "NextSaturnEasternQuadrature": 4702526458575522693, + "NextSaturnOpposition": 4702526267512973295, + "NextSaturnProgradeToRetrograde": 4702526937433024155, + "NextSaturnRetrogradeToPrograde": 4702526417756436584, + "NextSaturnWesternQuadrature": 4702526894942936055 + } + }, + { + "input_utc": "2009-09-09T23:14:28.567Z", + "tt_jd_bits": 4702526665099390461, + "events": { + "LastSaturnConjunction": 4702525868627604694, + "LastSaturnEasternQuadrature": 4702526458575522693, + "LastSaturnOpposition": 4702526267512973295, + "LastSaturnProgradeToRetrograde": 4702526125760943366, + "LastSaturnRetrogradeToPrograde": 4702526417756436584, + "LastSaturnWesternQuadrature": 4702526083406105005, + "NextSaturnConjunction": 4702526681841740281, + "NextSaturnEasternQuadrature": 4702527271945670476, + "NextSaturnOpposition": 4702527079685422092, + "NextSaturnProgradeToRetrograde": 4702526937433024155, + "NextSaturnRetrogradeToPrograde": 4702527231412694346, + "NextSaturnWesternQuadrature": 4702526894942936055 + } + }, + { + "input_utc": "2010-05-01T04:21:47.567Z", + "tt_jd_bits": 4702527165921384275, + "events": { + "LastSaturnConjunction": 4702526681841740281, + "LastSaturnEasternQuadrature": 4702526458575522693, + "LastSaturnOpposition": 4702527079685422092, + "LastSaturnProgradeToRetrograde": 4702526937433024155, + "LastSaturnRetrogradeToPrograde": 4702526417756436584, + "LastSaturnWesternQuadrature": 4702526894942936055, + "NextSaturnConjunction": 4702527494156787303, + "NextSaturnEasternQuadrature": 4702527271945670476, + "NextSaturnOpposition": 4702527891373299407, + "NextSaturnProgradeToRetrograde": 4702527748232075775, + "NextSaturnRetrogradeToPrograde": 4702527231412694346, + "NextSaturnWesternQuadrature": 4702527705800705664 + } + }, + { + "input_utc": "2010-12-20T09:29:06.567Z", + "tt_jd_bits": 4702527666743378090, + "events": { + "LastSaturnConjunction": 4702527494156787303, + "LastSaturnEasternQuadrature": 4702527271945670476, + "LastSaturnOpposition": 4702527079685422092, + "LastSaturnProgradeToRetrograde": 4702526937433024155, + "LastSaturnRetrogradeToPrograde": 4702527231412694346, + "LastSaturnWesternQuadrature": 4702526894942936055, + "NextSaturnConjunction": 4702528305594093674, + "NextSaturnEasternQuadrature": 4702528084677575789, + "NextSaturnOpposition": 4702527891373299407, + "NextSaturnProgradeToRetrograde": 4702527748232075775, + "NextSaturnRetrogradeToPrograde": 4702528044263283830, + "NextSaturnWesternQuadrature": 4702527705800705664 + } + }, + { + "input_utc": "2011-08-10T14:36:25.567Z", + "tt_jd_bits": 4702528167565371905, + "events": { + "LastSaturnConjunction": 4702527494156787303, + "LastSaturnEasternQuadrature": 4702528084677575789, + "LastSaturnOpposition": 4702527891373299407, + "LastSaturnProgradeToRetrograde": 4702527748232075775, + "LastSaturnRetrogradeToPrograde": 4702528044263283830, + "LastSaturnWesternQuadrature": 4702527705800705664, + "NextSaturnConjunction": 4702528305594093674, + "NextSaturnEasternQuadrature": 4702528896780133962, + "NextSaturnOpposition": 4702528702630001142, + "NextSaturnProgradeToRetrograde": 4702528558209759303, + "NextSaturnRetrogradeToPrograde": 4702528856420696262, + "NextSaturnWesternQuadrature": 4702528516055783475 + } + }, + { + "input_utc": "2012-03-30T19:43:44.567Z", + "tt_jd_bits": 4702528668387365720, + "events": { + "LastSaturnConjunction": 4702528305594093674, + "LastSaturnEasternQuadrature": 4702528084677575789, + "LastSaturnOpposition": 4702527891373299407, + "LastSaturnProgradeToRetrograde": 4702528558209759303, + "LastSaturnRetrogradeToPrograde": 4702528044263283830, + "LastSaturnWesternQuadrature": 4702528516055783475, + "NextSaturnConjunction": 4702529116208229368, + "NextSaturnEasternQuadrature": 4702528896780133962, + "NextSaturnOpposition": 4702528702630001142, + "NextSaturnProgradeToRetrograde": 4702529367654657338, + "NextSaturnRetrogradeToPrograde": 4702528856420696262, + "NextSaturnWesternQuadrature": 4702529325791487376 + } + }, + { + "input_utc": "2012-11-19T00:51:03.567Z", + "tt_jd_bits": 4702529169209384390, + "events": { + "LastSaturnConjunction": 4702529116208229368, + "LastSaturnEasternQuadrature": 4702528896780133962, + "LastSaturnOpposition": 4702528702630001142, + "LastSaturnProgradeToRetrograde": 4702528558209759303, + "LastSaturnRetrogradeToPrograde": 4702528856420696262, + "LastSaturnWesternQuadrature": 4702528516055783475, + "NextSaturnConjunction": 4702529926120420489, + "NextSaturnEasternQuadrature": 4702529708247587876, + "NextSaturnOpposition": 4702529513484519769, + "NextSaturnProgradeToRetrograde": 4702529367654657338, + "NextSaturnRetrogradeToPrograde": 4702529667729377306, + "NextSaturnWesternQuadrature": 4702529325791487376 + } + }, + { + "input_utc": "2013-07-08T08:54:34.567Z", + "tt_jd_bits": 4702529665999179394, + "events": { + "LastSaturnConjunction": 4702529116208229368, + "LastSaturnEasternQuadrature": 4702528896780133962, + "LastSaturnOpposition": 4702529513484519769, + "LastSaturnProgradeToRetrograde": 4702529367654657338, + "LastSaturnRetrogradeToPrograde": 4702528856420696262, + "LastSaturnWesternQuadrature": 4702529325791487376, + "NextSaturnConjunction": 4702529926120420489, + "NextSaturnEasternQuadrature": 4702529708247587876, + "NextSaturnOpposition": 4702530323982720975, + "NextSaturnProgradeToRetrograde": 4702530176692036995, + "NextSaturnRetrogradeToPrograde": 4702529667729377306, + "NextSaturnWesternQuadrature": 4702530135136541283 + } + }, + { + "input_utc": "2014-02-26T14:01:53.567Z", + "tt_jd_bits": 4702530166821173209, + "events": { + "LastSaturnConjunction": 4702529926120420489, + "LastSaturnEasternQuadrature": 4702529708247587876, + "LastSaturnOpposition": 4702529513484519769, + "LastSaturnProgradeToRetrograde": 4702529367654657338, + "LastSaturnRetrogradeToPrograde": 4702529667729377306, + "LastSaturnWesternQuadrature": 4702530135136541283, + "NextSaturnConjunction": 4702530735437812401, + "NextSaturnEasternQuadrature": 4702530519108891970, + "NextSaturnOpposition": 4702530323982720975, + "NextSaturnProgradeToRetrograde": 4702530176692036995, + "NextSaturnRetrogradeToPrograde": 4702530478329069381, + "NextSaturnWesternQuadrature": 4702530944199773918 + } + }, + { + "input_utc": "2014-10-17T19:09:12.567Z", + "tt_jd_bits": 4702530667643167024, + "events": { + "LastSaturnConjunction": 4702529926120420489, + "LastSaturnEasternQuadrature": 4702530519108891970, + "LastSaturnOpposition": 4702530323982720975, + "LastSaturnProgradeToRetrograde": 4702530176692036995, + "LastSaturnRetrogradeToPrograde": 4702530478329069381, + "LastSaturnWesternQuadrature": 4702530135136541283, + "NextSaturnConjunction": 4702530735437812401, + "NextSaturnEasternQuadrature": 4702531329470659021, + "NextSaturnOpposition": 4702531134220738613, + "NextSaturnProgradeToRetrograde": 4702530985713401495, + "NextSaturnRetrogradeToPrograde": 4702531288318358975, + "NextSaturnWesternQuadrature": 4702530944199773918 + } + }, + { + "input_utc": "2015-06-08T00:16:31.567Z", + "tt_jd_bits": 4702531168465160838, + "events": { + "LastSaturnConjunction": 4702530735437812401, + "LastSaturnEasternQuadrature": 4702530519108891970, + "LastSaturnOpposition": 4702531134220738613, + "LastSaturnProgradeToRetrograde": 4702530985713401495, + "LastSaturnRetrogradeToPrograde": 4702530478329069381, + "LastSaturnWesternQuadrature": 4702530944199773918, + "NextSaturnConjunction": 4702531544272060514, + "NextSaturnEasternQuadrature": 4702531329470659021, + "NextSaturnOpposition": 4702531944272912979, + "NextSaturnProgradeToRetrograde": 4702531794499633949, + "NextSaturnRetrogradeToPrograde": 4702531288318358975, + "NextSaturnWesternQuadrature": 4702531753096564607 + } + }, + { + "input_utc": "2016-01-27T04:13:50.567Z", + "tt_jd_bits": 4702531669182787943, + "events": { + "LastSaturnConjunction": 4702531544272060514, + "LastSaturnEasternQuadrature": 4702531329470659021, + "LastSaturnOpposition": 4702531134220738613, + "LastSaturnProgradeToRetrograde": 4702530985713401495, + "LastSaturnRetrogradeToPrograde": 4702531288318358975, + "LastSaturnWesternQuadrature": 4702530944199773918, + "NextSaturnConjunction": 4702532352763015780, + "NextSaturnEasternQuadrature": 4702532139402591988, + "NextSaturnOpposition": 4702531944272912979, + "NextSaturnProgradeToRetrograde": 4702531794499633949, + "NextSaturnRetrogradeToPrograde": 4702532097752752136, + "NextSaturnWesternQuadrature": 4702531753096564607 + } + }, + { + "input_utc": "2016-09-16T09:21:09.567Z", + "tt_jd_bits": 4702532170004781758, + "events": { + "LastSaturnConjunction": 4702531544272060514, + "LastSaturnEasternQuadrature": 4702532139402591988, + "LastSaturnOpposition": 4702531944272912979, + "LastSaturnProgradeToRetrograde": 4702531794499633949, + "LastSaturnRetrogradeToPrograde": 4702532097752752136, + "LastSaturnWesternQuadrature": 4702531753096564607, + "NextSaturnConjunction": 4702532352763015780, + "NextSaturnEasternQuadrature": 4702532948968819840, + "NextSaturnOpposition": 4702532754203200526, + "NextSaturnProgradeToRetrograde": 4702532603436176255, + "NextSaturnRetrogradeToPrograde": 4702532907108663923, + "NextSaturnWesternQuadrature": 4702532561958283869 + } + }, + { + "input_utc": "2017-05-07T14:28:28.567Z", + "tt_jd_bits": 4702532670826800428, + "events": { + "LastSaturnConjunction": 4702532352763015780, + "LastSaturnEasternQuadrature": 4702532139402591988, + "LastSaturnOpposition": 4702531944272912979, + "LastSaturnProgradeToRetrograde": 4702532603436176255, + "LastSaturnRetrogradeToPrograde": 4702532097752752136, + "LastSaturnWesternQuadrature": 4702532561958283869, + "NextSaturnConjunction": 4702533161047908186, + "NextSaturnEasternQuadrature": 4702532948968819840, + "NextSaturnOpposition": 4702532754203200526, + "NextSaturnProgradeToRetrograde": 4702533412701135064, + "NextSaturnRetrogradeToPrograde": 4702532907108663923, + "NextSaturnWesternQuadrature": 4702533370886391830 + } + }, + { + "input_utc": "2017-12-26T19:35:47.567Z", + "tt_jd_bits": 4702533171648794243, + "events": { + "LastSaturnConjunction": 4702533161047908186, + "LastSaturnEasternQuadrature": 4702532948968819840, + "LastSaturnOpposition": 4702532754203200526, + "LastSaturnProgradeToRetrograde": 4702532603436176255, + "LastSaturnRetrogradeToPrograde": 4702532907108663923, + "LastSaturnWesternQuadrature": 4702532561958283869, + "NextSaturnConjunction": 4702533969278751510, + "NextSaturnEasternQuadrature": 4702533758289903395, + "NextSaturnOpposition": 4702533564088052716, + "NextSaturnProgradeToRetrograde": 4702533412701135064, + "NextSaturnRetrogradeToPrograde": 4702533716289075423, + "NextSaturnWesternQuadrature": 4702533370886391830 + } + }, + { + "input_utc": "2018-08-17T00:43:06.567Z", + "tt_jd_bits": 4702533672470788057, + "events": { + "LastSaturnConjunction": 4702533161047908186, + "LastSaturnEasternQuadrature": 4702532948968819840, + "LastSaturnOpposition": 4702533564088052716, + "LastSaturnProgradeToRetrograde": 4702533412701135064, + "LastSaturnRetrogradeToPrograde": 4702532907108663926, + "LastSaturnWesternQuadrature": 4702533370886391830, + "NextSaturnConjunction": 4702533969278751510, + "NextSaturnEasternQuadrature": 4702533758289903395, + "NextSaturnOpposition": 4702534374016295984, + "NextSaturnProgradeToRetrograde": 4702534222381736418, + "NextSaturnRetrogradeToPrograde": 4702533716289075423, + "NextSaturnWesternQuadrature": 4702534179996995450 + } + }, + { + "input_utc": "2019-04-05T08:50:25.567Z", + "tt_jd_bits": 4702534169266250032, + "events": { + "LastSaturnConjunction": 4702533969278751510, + "LastSaturnEasternQuadrature": 4702533758289903395, + "LastSaturnOpposition": 4702533564088052716, + "LastSaturnProgradeToRetrograde": 4702533412701135064, + "LastSaturnRetrogradeToPrograde": 4702533716289075423, + "LastSaturnWesternQuadrature": 4702533370886391830, + "NextSaturnConjunction": 4702534777577021322, + "NextSaturnEasternQuadrature": 4702534567468424843, + "NextSaturnOpposition": 4702534374016295984, + "NextSaturnProgradeToRetrograde": 4702534222381736418, + "NextSaturnRetrogradeToPrograde": 4702534525513487502, + "NextSaturnWesternQuadrature": 4702534179996995450 + } + }, + { + "input_utc": "2019-11-24T13:57:44.567Z", + "tt_jd_bits": 4702534670088243847, + "events": { + "LastSaturnConjunction": 4702533969278751510, + "LastSaturnEasternQuadrature": 4702534567468424843, + "LastSaturnOpposition": 4702534374016295984, + "LastSaturnProgradeToRetrograde": 4702534222381736418, + "LastSaturnRetrogradeToPrograde": 4702534525513487502, + "LastSaturnWesternQuadrature": 4702534179996995450, + "NextSaturnConjunction": 4702534777577021322, + "NextSaturnEasternQuadrature": 4702535376608536290, + "NextSaturnOpposition": 4702535184095658237, + "NextSaturnProgradeToRetrograde": 4702535032607208659, + "NextSaturnRetrogradeToPrograde": 4702535334812479926, + "NextSaturnWesternQuadrature": 4702534989438734845 + } + }, + { + "input_utc": "2020-07-14T19:05:03.567Z", + "tt_jd_bits": 4702535170910237662, + "events": { + "LastSaturnConjunction": 4702534777577021322, + "LastSaturnEasternQuadrature": 4702534567468424843, + "LastSaturnOpposition": 4702534374016295984, + "LastSaturnProgradeToRetrograde": 4702535032607208659, + "LastSaturnRetrogradeToPrograde": 4702534525513487502, + "LastSaturnWesternQuadrature": 4702534989438734845, + "NextSaturnConjunction": 4702535586083147644, + "NextSaturnEasternQuadrature": 4702535376608536290, + "NextSaturnOpposition": 4702535184095658237, + "NextSaturnProgradeToRetrograde": 4702535843143424572, + "NextSaturnRetrogradeToPrograde": 4702535334812479928, + "NextSaturnWesternQuadrature": 4702535799310672678 + } + }, + { + "input_utc": "1990-09-02T08:12:34.567Z", + "tt_jd_bits": 4702511745185253343, + "events": { + "LastSaturnConjunction": 4702511233082799871, + "LastSaturnEasternQuadrature": 4702511022491997719, + "LastSaturnOpposition": 4702511638661451685, + "LastSaturnProgradeToRetrograde": 4702511486901854564, + "LastSaturnRetrogradeToPrograde": 4702510980431897393, + "LastSaturnWesternQuadrature": 4702511444359747148, + "NextSaturnConjunction": 4702512041495019844, + "NextSaturnEasternQuadrature": 4702511831665430466, + "NextSaturnOpposition": 4702512448855511897, + "NextSaturnProgradeToRetrograde": 4702512297376154580, + "NextSaturnRetrogradeToPrograde": 4702511789774836955, + "NextSaturnWesternQuadrature": 4702512254003900109 + } + }, + { + "input_utc": "1991-02-06T14:24:03.567Z", + "tt_jd_bits": 4702512082894207034, + "events": { + "LastSaturnConjunction": 4702512041495019844, + "LastSaturnEasternQuadrature": 4702511831665430466, + "LastSaturnOpposition": 4702511638661451685, + "LastSaturnProgradeToRetrograde": 4702511486901854564, + "LastSaturnRetrogradeToPrograde": 4702511789774836955, + "LastSaturnWesternQuadrature": 4702511444359747148, + "NextSaturnConjunction": 4702512850183523623, + "NextSaturnEasternQuadrature": 4702512640867054255, + "NextSaturnOpposition": 4702512448855511897, + "NextSaturnProgradeToRetrograde": 4702512297376154580, + "NextSaturnRetrogradeToPrograde": 4702512599370335510, + "NextSaturnWesternQuadrature": 4702512254003900109 + } + }, + { + "input_utc": "1991-07-13T20:35:32.567Z", + "tt_jd_bits": 4702512420603135870, + "events": { + "LastSaturnConjunction": 4702512041495019844, + "LastSaturnEasternQuadrature": 4702511831665430466, + "LastSaturnOpposition": 4702511638661451685, + "LastSaturnProgradeToRetrograde": 4702512297376154580, + "LastSaturnRetrogradeToPrograde": 4702511789774836947, + "LastSaturnWesternQuadrature": 4702512254003900109, + "NextSaturnConjunction": 4702512850183523623, + "NextSaturnEasternQuadrature": 4702512640867054255, + "NextSaturnOpposition": 4702512448855511897, + "NextSaturnProgradeToRetrograde": 4702513108332257840, + "NextSaturnRetrogradeToPrograde": 4702512599370335510, + "NextSaturnWesternQuadrature": 4702513064124092900 + } + }, + { + "input_utc": "1991-12-18T02:47:01.567Z", + "tt_jd_bits": 4702512758312064706, + "events": { + "LastSaturnConjunction": 4702512041495019844, + "LastSaturnEasternQuadrature": 4702512640867054255, + "LastSaturnOpposition": 4702512448855511897, + "LastSaturnProgradeToRetrograde": 4702512297376154580, + "LastSaturnRetrogradeToPrograde": 4702512599370335510, + "LastSaturnWesternQuadrature": 4702512254003900109, + "NextSaturnConjunction": 4702512850183523623, + "NextSaturnEasternQuadrature": 4702513450206973184, + "NextSaturnOpposition": 4702513259309054642, + "NextSaturnProgradeToRetrograde": 4702513108332257840, + "NextSaturnRetrogradeToPrograde": 4702513409105122572, + "NextSaturnWesternQuadrature": 4702513064124092900 + } + }, + { + "input_utc": "1992-05-23T08:58:30.567Z", + "tt_jd_bits": 4702513096020993542, + "events": { + "LastSaturnConjunction": 4702512850183523623, + "LastSaturnEasternQuadrature": 4702512640867054255, + "LastSaturnOpposition": 4702512448855511897, + "LastSaturnProgradeToRetrograde": 4702512297376154580, + "LastSaturnRetrogradeToPrograde": 4702512599370335510, + "LastSaturnWesternQuadrature": 4702513064124092900, + "NextSaturnConjunction": 4702513659304170755, + "NextSaturnEasternQuadrature": 4702513450206973184, + "NextSaturnOpposition": 4702513259309054642, + "NextSaturnProgradeToRetrograde": 4702513108332257840, + "NextSaturnRetrogradeToPrograde": 4702513409105122572, + "NextSaturnWesternQuadrature": 4702513874832681280 + } + }, + { + "input_utc": "1992-10-27T15:09:59.567Z", + "tt_jd_bits": 4702513433729947233, + "events": { + "LastSaturnConjunction": 4702512850183523623, + "LastSaturnEasternQuadrature": 4702512640867054255, + "LastSaturnOpposition": 4702513259309054642, + "LastSaturnProgradeToRetrograde": 4702513108332257840, + "LastSaturnRetrogradeToPrograde": 4702513409105122572, + "LastSaturnWesternQuadrature": 4702513064124092900, + "NextSaturnConjunction": 4702513659304170755, + "NextSaturnEasternQuadrature": 4702513450206973184, + "NextSaturnOpposition": 4702514070085390511, + "NextSaturnProgradeToRetrograde": 4702513919872067788, + "NextSaturnRetrogradeToPrograde": 4702514219285494636, + "NextSaturnWesternQuadrature": 4702513874832681280 + } + }, + { + "input_utc": "1993-04-02T21:21:28.567Z", + "tt_jd_bits": 4702513771438876069, + "events": { + "LastSaturnConjunction": 4702513659304170755, + "LastSaturnEasternQuadrature": 4702513450206973184, + "LastSaturnOpposition": 4702513259309054642, + "LastSaturnProgradeToRetrograde": 4702513108332257840, + "LastSaturnRetrogradeToPrograde": 4702513409105122572, + "LastSaturnWesternQuadrature": 4702513064124092900, + "NextSaturnConjunction": 4702514468983832337, + "NextSaturnEasternQuadrature": 4702514259798480917, + "NextSaturnOpposition": 4702514070085390511, + "NextSaturnProgradeToRetrograde": 4702513919872067788, + "NextSaturnRetrogradeToPrograde": 4702514219285494636, + "NextSaturnWesternQuadrature": 4702513874832681280 + } + }, + { + "input_utc": "1993-09-05T09:32:57.567Z", + "tt_jd_bits": 4702514105389733375, + "events": { + "LastSaturnConjunction": 4702513659304170755, + "LastSaturnEasternQuadrature": 4702513450206973184, + "LastSaturnOpposition": 4702514070085390511, + "LastSaturnProgradeToRetrograde": 4702513919872067788, + "LastSaturnRetrogradeToPrograde": 4702513409105122572, + "LastSaturnWesternQuadrature": 4702513874832681280, + "NextSaturnConjunction": 4702514468983832337, + "NextSaturnEasternQuadrature": 4702514259798480917, + "NextSaturnOpposition": 4702514881266945183, + "NextSaturnProgradeToRetrograde": 4702514731950956278, + "NextSaturnRetrogradeToPrograde": 4702514219285494636, + "NextSaturnWesternQuadrature": 4702514686189564609 + } + }, + { + "input_utc": "1994-02-09T15:44:26.567Z", + "tt_jd_bits": 4702514443098662211, + "events": { + "LastSaturnConjunction": 4702513659304170755, + "LastSaturnEasternQuadrature": 4702514259798480917, + "LastSaturnOpposition": 4702514070085390511, + "LastSaturnProgradeToRetrograde": 4702513919872067788, + "LastSaturnRetrogradeToPrograde": 4702514219285494636, + "LastSaturnWesternQuadrature": 4702513874832681280, + "NextSaturnConjunction": 4702514468983832337, + "NextSaturnEasternQuadrature": 4702515069775059105, + "NextSaturnOpposition": 4702514881266945183, + "NextSaturnProgradeToRetrograde": 4702514731950956278, + "NextSaturnRetrogradeToPrograde": 4702515029844380521, + "NextSaturnWesternQuadrature": 4702514686189564609 + } + }, + { + "input_utc": "1994-07-16T20:16:55.567Z", + "tt_jd_bits": 4702514780659976402, + "events": { + "LastSaturnConjunction": 4702514468983832337, + "LastSaturnEasternQuadrature": 4702514259798480917, + "LastSaturnOpposition": 4702514070085390511, + "LastSaturnProgradeToRetrograde": 4702514731950956278, + "LastSaturnRetrogradeToPrograde": 4702514219285494636, + "LastSaturnWesternQuadrature": 4702514686189564609, + "NextSaturnConjunction": 4702515279344904012, + "NextSaturnEasternQuadrature": 4702515069775059105, + "NextSaturnOpposition": 4702514881266945183, + "NextSaturnProgradeToRetrograde": 4702515544354790793, + "NextSaturnRetrogradeToPrograde": 4702515029844380521, + "NextSaturnWesternQuadrature": 4702515498244737324 + } + }, + { + "input_utc": "1994-12-21T02:28:24.567Z", + "tt_jd_bits": 4702515118368905238, + "events": { + "LastSaturnConjunction": 4702514468983832337, + "LastSaturnEasternQuadrature": 4702515069775059105, + "LastSaturnOpposition": 4702514881266945183, + "LastSaturnProgradeToRetrograde": 4702514731950956278, + "LastSaturnRetrogradeToPrograde": 4702515029844380521, + "LastSaturnWesternQuadrature": 4702514686189564609, + "NextSaturnConjunction": 4702515279344904012, + "NextSaturnEasternQuadrature": 4702515880214499666, + "NextSaturnOpposition": 4702515692894887718, + "NextSaturnProgradeToRetrograde": 4702515544354790793, + "NextSaturnRetrogradeToPrograde": 4702515840919678262, + "NextSaturnWesternQuadrature": 4702515498244737324 + } + }, + { + "input_utc": "1995-05-27T08:39:53.567Z", + "tt_jd_bits": 4702515456077834074, + "events": { + "LastSaturnConjunction": 4702515279344904012, + "LastSaturnEasternQuadrature": 4702515069775059105, + "LastSaturnOpposition": 4702514881266945183, + "LastSaturnProgradeToRetrograde": 4702514731950956278, + "LastSaturnRetrogradeToPrograde": 4702515029844380521, + "LastSaturnWesternQuadrature": 4702514686189564609, + "NextSaturnConjunction": 4702516090514403465, + "NextSaturnEasternQuadrature": 4702515880214499666, + "NextSaturnOpposition": 4702515692894887718, + "NextSaturnProgradeToRetrograde": 4702515544354790793, + "NextSaturnRetrogradeToPrograde": 4702515840919678262, + "NextSaturnWesternQuadrature": 4702515498244737324 + } + }, + { + "input_utc": "1995-10-31T14:51:22.567Z", + "tt_jd_bits": 4702515793786762910, + "events": { + "LastSaturnConjunction": 4702515279344904012, + "LastSaturnEasternQuadrature": 4702515069775059105, + "LastSaturnOpposition": 4702515692894887718, + "LastSaturnProgradeToRetrograde": 4702515544354790793, + "LastSaturnRetrogradeToPrograde": 4702515029844380521, + "LastSaturnWesternQuadrature": 4702515498244737324, + "NextSaturnConjunction": 4702516090514403465, + "NextSaturnEasternQuadrature": 4702515880214499666, + "NextSaturnOpposition": 4702516504989133571, + "NextSaturnProgradeToRetrograde": 4702516357268860063, + "NextSaturnRetrogradeToPrograde": 4702515840919678262, + "NextSaturnWesternQuadrature": 4702516311055707275 + } + }, + { + "input_utc": "1996-04-05T21:02:51.567Z", + "tt_jd_bits": 4702516131495716601, + "events": { + "LastSaturnConjunction": 4702516090514403465, + "LastSaturnEasternQuadrature": 4702515880214499666, + "LastSaturnOpposition": 4702515692894887718, + "LastSaturnProgradeToRetrograde": 4702515544354790793, + "LastSaturnRetrogradeToPrograde": 4702515840919678262, + "LastSaturnWesternQuadrature": 4702515498244737324, + "NextSaturnConjunction": 4702516902555213289, + "NextSaturnEasternQuadrature": 4702516691190423829, + "NextSaturnOpposition": 4702516504989133571, + "NextSaturnProgradeToRetrograde": 4702516357268860063, + "NextSaturnRetrogradeToPrograde": 4702516652440739701, + "NextSaturnWesternQuadrature": 4702516311055707275 + } + }, + { + "input_utc": "1996-09-08T09:07:34.567Z", + "tt_jd_bits": 4702516465436457868, + "events": { + "LastSaturnConjunction": 4702516090514403465, + "LastSaturnEasternQuadrature": 4702515880214499666, + "LastSaturnOpposition": 4702515692894887718, + "LastSaturnProgradeToRetrograde": 4702516357268860063, + "LastSaturnRetrogradeToPrograde": 4702515840919678262, + "LastSaturnWesternQuadrature": 4702516311055707275, + "NextSaturnConjunction": 4702516902555213289, + "NextSaturnEasternQuadrature": 4702516691190423829, + "NextSaturnOpposition": 4702516504989133571, + "NextSaturnProgradeToRetrograde": 4702517170653779998, + "NextSaturnRetrogradeToPrograde": 4702516652440739701, + "NextSaturnWesternQuadrature": 4702517124594159184 + } + }, + { + "input_utc": "1997-02-12T15:19:03.567Z", + "tt_jd_bits": 4702516803145386704, + "events": { + "LastSaturnConjunction": 4702516090514403465, + "LastSaturnEasternQuadrature": 4702516691190423829, + "LastSaturnOpposition": 4702516504989133571, + "LastSaturnProgradeToRetrograde": 4702516357268860063, + "LastSaturnRetrogradeToPrograde": 4702516652440739701, + "LastSaturnWesternQuadrature": 4702516311055707275, + "NextSaturnConjunction": 4702516902555213289, + "NextSaturnEasternQuadrature": 4702517502797519439, + "NextSaturnOpposition": 4702517317565699910, + "NextSaturnProgradeToRetrograde": 4702517170653779998, + "NextSaturnRetrogradeToPrograde": 4702517464207914117, + "NextSaturnWesternQuadrature": 4702517124594159184 + } + }, + { + "input_utc": "1997-07-19T21:30:32.567Z", + "tt_jd_bits": 4702517140854340395, + "events": { + "LastSaturnConjunction": 4702516902555213289, + "LastSaturnEasternQuadrature": 4702516691190423829, + "LastSaturnOpposition": 4702516504989133571, + "LastSaturnProgradeToRetrograde": 4702516357268860063, + "LastSaturnRetrogradeToPrograde": 4702516652440739701, + "LastSaturnWesternQuadrature": 4702517124594159184, + "NextSaturnConjunction": 4702517715498679309, + "NextSaturnEasternQuadrature": 4702517502797519439, + "NextSaturnOpposition": 4702517317565699910, + "NextSaturnProgradeToRetrograde": 4702517170653779998, + "NextSaturnRetrogradeToPrograde": 4702517464207914117, + "NextSaturnWesternQuadrature": 4702517938784445531 + } + }, + { + "input_utc": "1997-12-24T03:42:01.567Z", + "tt_jd_bits": 4702517478563269231, + "events": { + "LastSaturnConjunction": 4702516902555213289, + "LastSaturnEasternQuadrature": 4702516691190423829, + "LastSaturnOpposition": 4702517317565699910, + "LastSaturnProgradeToRetrograde": 4702517170653779998, + "LastSaturnRetrogradeToPrograde": 4702517464207914117, + "LastSaturnWesternQuadrature": 4702517124594159184, + "NextSaturnConjunction": 4702517715498679309, + "NextSaturnEasternQuadrature": 4702517502797519439, + "NextSaturnOpposition": 4702518130601901153, + "NextSaturnProgradeToRetrograde": 4702517984360493277, + "NextSaturnRetrogradeToPrograde": 4702518276343167865, + "NextSaturnWesternQuadrature": 4702517938784445531 + } + }, + { + "input_utc": "1998-05-30T08:14:30.567Z", + "tt_jd_bits": 4702517816124558566, + "events": { + "LastSaturnConjunction": 4702517715498679309, + "LastSaturnEasternQuadrature": 4702517502797519439, + "LastSaturnOpposition": 4702517317565699910, + "LastSaturnProgradeToRetrograde": 4702517170653779998, + "LastSaturnRetrogradeToPrograde": 4702517464207914117, + "LastSaturnWesternQuadrature": 4702517124594159184, + "NextSaturnConjunction": 4702518529340025963, + "NextSaturnEasternQuadrature": 4702518315061530241, + "NextSaturnOpposition": 4702518130601901153, + "NextSaturnProgradeToRetrograde": 4702517984360493277, + "NextSaturnRetrogradeToPrograde": 4702518276343167865, + "NextSaturnWesternQuadrature": 4702517938784445531 + } + }, + { + "input_utc": "1998-11-03T14:25:59.567Z", + "tt_jd_bits": 4702518153833487402, + "events": { + "LastSaturnConjunction": 4702517715498679309, + "LastSaturnEasternQuadrature": 4702517502797519439, + "LastSaturnOpposition": 4702518130601901153, + "LastSaturnProgradeToRetrograde": 4702517984360493277, + "LastSaturnRetrogradeToPrograde": 4702517464207914117, + "LastSaturnWesternQuadrature": 4702517938784445531, + "NextSaturnConjunction": 4702518529340025963, + "NextSaturnEasternQuadrature": 4702518315061530241, + "NextSaturnOpposition": 4702518944056058944, + "NextSaturnProgradeToRetrograde": 4702518798260631044, + "NextSaturnRetrogradeToPrograde": 4702518276343167865, + "NextSaturnWesternQuadrature": 4702518753554402094 + } + }, + { + "input_utc": "1999-04-09T20:37:28.567Z", + "tt_jd_bits": 4702518491542441093, + "events": { + "LastSaturnConjunction": 4702517715498679309, + "LastSaturnEasternQuadrature": 4702518315061530241, + "LastSaturnOpposition": 4702518130601901153, + "LastSaturnProgradeToRetrograde": 4702517984360493277, + "LastSaturnRetrogradeToPrograde": 4702518276343167865, + "LastSaturnWesternQuadrature": 4702517938784445531, + "NextSaturnConjunction": 4702518529340025963, + "NextSaturnEasternQuadrature": 4702519127967829206, + "NextSaturnOpposition": 4702518944056058944, + "NextSaturnProgradeToRetrograde": 4702518798260631044, + "NextSaturnRetrogradeToPrograde": 4702519088992051071, + "NextSaturnWesternQuadrature": 4702518753554402094 + } + }, + { + "input_utc": "1999-09-12T08:48:57.567Z", + "tt_jd_bits": 4702518825493273544, + "events": { + "LastSaturnConjunction": 4702518529340025963, + "LastSaturnEasternQuadrature": 4702518315061530241, + "LastSaturnOpposition": 4702518130601901153, + "LastSaturnProgradeToRetrograde": 4702518798260631044, + "LastSaturnRetrogradeToPrograde": 4702518276343167865, + "LastSaturnWesternQuadrature": 4702518753554402094, + "NextSaturnConjunction": 4702519344013453296, + "NextSaturnEasternQuadrature": 4702519127967829206, + "NextSaturnOpposition": 4702518944056058944, + "NextSaturnProgradeToRetrograde": 4702519612466313577, + "NextSaturnRetrogradeToPrograde": 4702519088992051071, + "NextSaturnWesternQuadrature": 4702519568739629632 + } + }, + { + "input_utc": "2000-02-16T15:00:26.567Z", + "tt_jd_bits": 4702519163202202380, + "events": { + "LastSaturnConjunction": 4702518529340025963, + "LastSaturnEasternQuadrature": 4702519127967829206, + "LastSaturnOpposition": 4702518944056058944, + "LastSaturnProgradeToRetrograde": 4702518798260631044, + "LastSaturnRetrogradeToPrograde": 4702519088992051071, + "LastSaturnWesternQuadrature": 4702518753554402094, + "NextSaturnConjunction": 4702519344013453296, + "NextSaturnEasternQuadrature": 4702519941514994119, + "NextSaturnOpposition": 4702519757846345849, + "NextSaturnProgradeToRetrograde": 4702519612466313577, + "NextSaturnRetrogradeToPrograde": 4702519901997171652, + "NextSaturnWesternQuadrature": 4702519568739629632 + } + }, + { + "input_utc": "2000-07-22T21:11:55.567Z", + "tt_jd_bits": 4702519500911131216, + "events": { + "LastSaturnConjunction": 4702519344013453296, + "LastSaturnEasternQuadrature": 4702519127967829206, + "LastSaturnOpposition": 4702518944056058944, + "LastSaturnProgradeToRetrograde": 4702518798260631044, + "LastSaturnRetrogradeToPrograde": 4702519088992051071, + "LastSaturnWesternQuadrature": 4702518753554402094, + "NextSaturnConjunction": 4702520159413504881, + "NextSaturnEasternQuadrature": 4702519941514994119, + "NextSaturnOpposition": 4702519757846345849, + "NextSaturnProgradeToRetrograde": 4702519612466313577, + "NextSaturnRetrogradeToPrograde": 4702519901997171652, + "NextSaturnWesternQuadrature": 4702519568739629632 + } + }, + { + "input_utc": "2000-12-27T03:23:24.567Z", + "tt_jd_bits": 4702519838620060052, + "events": { + "LastSaturnConjunction": 4702519344013453296, + "LastSaturnEasternQuadrature": 4702519127967829206, + "LastSaturnOpposition": 4702519757846345849, + "LastSaturnProgradeToRetrograde": 4702519612466313577, + "LastSaturnRetrogradeToPrograde": 4702519088992051071, + "LastSaturnWesternQuadrature": 4702519568739629632, + "NextSaturnConjunction": 4702520159413504881, + "NextSaturnEasternQuadrature": 4702519941514994119, + "NextSaturnOpposition": 4702520571879633151, + "NextSaturnProgradeToRetrograde": 4702520426999456595, + "NextSaturnRetrogradeToPrograde": 4702519901997171652, + "NextSaturnWesternQuadrature": 4702520384150966971 + } + }, + { + "input_utc": "2001-06-02T09:34:53.567Z", + "tt_jd_bits": 4702520176328988888, + "events": { + "LastSaturnConjunction": 4702520159413504881, + "LastSaturnEasternQuadrature": 4702519941514994119, + "LastSaturnOpposition": 4702519757846345849, + "LastSaturnProgradeToRetrograde": 4702519612466313577, + "LastSaturnRetrogradeToPrograde": 4702519901997171652, + "LastSaturnWesternQuadrature": 4702519568739629632, + "NextSaturnConjunction": 4702520975353468347, + "NextSaturnEasternQuadrature": 4702520755639430340, + "NextSaturnOpposition": 4702520571879633151, + "NextSaturnProgradeToRetrograde": 4702520426999456595, + "NextSaturnRetrogradeToPrograde": 4702520715408413592, + "NextSaturnWesternQuadrature": 4702520384150966971 + } + }, + { + "input_utc": "2001-11-06T15:46:22.567Z", + "tt_jd_bits": 4702520514037917724, + "events": { + "LastSaturnConjunction": 4702520159413504881, + "LastSaturnEasternQuadrature": 4702519941514994119, + "LastSaturnOpposition": 4702519757846345849, + "LastSaturnProgradeToRetrograde": 4702520426999456595, + "LastSaturnRetrogradeToPrograde": 4702519901997171652, + "LastSaturnWesternQuadrature": 4702520384150966971, + "NextSaturnConjunction": 4702520975353468347, + "NextSaturnEasternQuadrature": 4702520755639430340, + "NextSaturnOpposition": 4702520571879633151, + "NextSaturnProgradeToRetrograde": 4702521241794457001, + "NextSaturnRetrogradeToPrograde": 4702520715408413592, + "NextSaturnWesternQuadrature": 4702521199621573005 + } + }, + { + "input_utc": "2002-04-12T20:18:51.567Z", + "tt_jd_bits": 4702520851599207059, + "events": { + "LastSaturnConjunction": 4702520159413504881, + "LastSaturnEasternQuadrature": 4702520755639430340, + "LastSaturnOpposition": 4702520571879633151, + "LastSaturnProgradeToRetrograde": 4702520426999456595, + "LastSaturnRetrogradeToPrograde": 4702520715408413592, + "LastSaturnWesternQuadrature": 4702520384150966971, + "NextSaturnConjunction": 4702520975353468347, + "NextSaturnEasternQuadrature": 4702521570228101699, + "NextSaturnOpposition": 4702521386065954738, + "NextSaturnProgradeToRetrograde": 4702521241794457001, + "NextSaturnRetrogradeToPrograde": 4702521529307759912, + "NextSaturnWesternQuadrature": 4702521199621573005 + } + }, + { + "input_utc": "2002-09-15T08:23:34.567Z", + "tt_jd_bits": 4702521185539948326, + "events": { + "LastSaturnConjunction": 4702520975353468347, + "LastSaturnEasternQuadrature": 4702520755639430340, + "LastSaturnOpposition": 4702520571879633151, + "LastSaturnProgradeToRetrograde": 4702520426999456595, + "LastSaturnRetrogradeToPrograde": 4702520715408413592, + "LastSaturnWesternQuadrature": 4702520384150966971, + "NextSaturnConjunction": 4702521791599604601, + "NextSaturnEasternQuadrature": 4702521570228101699, + "NextSaturnOpposition": 4702521386065954738, + "NextSaturnProgradeToRetrograde": 4702521241794457001, + "NextSaturnRetrogradeToPrograde": 4702521529307759912, + "NextSaturnWesternQuadrature": 4702521199621573005 + } + }, + { + "input_utc": "2003-02-19T14:35:03.567Z", + "tt_jd_bits": 4702521523248877162, + "events": { + "LastSaturnConjunction": 4702520975353468347, + "LastSaturnEasternQuadrature": 4702520755639430340, + "LastSaturnOpposition": 4702521386065954738, + "LastSaturnProgradeToRetrograde": 4702521241794457001, + "LastSaturnRetrogradeToPrograde": 4702520715408413592, + "LastSaturnWesternQuadrature": 4702521199621573005, + "NextSaturnConjunction": 4702521791599604601, + "NextSaturnEasternQuadrature": 4702521570228101699, + "NextSaturnOpposition": 4702522200274787837, + "NextSaturnProgradeToRetrograde": 4702522056670159268, + "NextSaturnRetrogradeToPrograde": 4702521529307759912, + "NextSaturnWesternQuadrature": 4702522014939346053 + } + }, + { + "input_utc": "2003-07-26T20:46:32.567Z", + "tt_jd_bits": 4702521860957805998, + "events": { + "LastSaturnConjunction": 4702521791599604601, + "LastSaturnEasternQuadrature": 4702521570228101699, + "LastSaturnOpposition": 4702521386065954738, + "LastSaturnProgradeToRetrograde": 4702521241794457001, + "LastSaturnRetrogradeToPrograde": 4702521529307759912, + "LastSaturnWesternQuadrature": 4702521199621573005, + "NextSaturnConjunction": 4702522607909662837, + "NextSaturnEasternQuadrature": 4702522385163266546, + "NextSaturnOpposition": 4702522200274787837, + "NextSaturnProgradeToRetrograde": 4702522056670159268, + "NextSaturnRetrogradeToPrograde": 4702522343647597446, + "NextSaturnWesternQuadrature": 4702522014939346053 + } + }, + { + "input_utc": "2003-12-31T02:58:01.567Z", + "tt_jd_bits": 4702522198666734834, + "events": { + "LastSaturnConjunction": 4702521791599604601, + "LastSaturnEasternQuadrature": 4702521570228101699, + "LastSaturnOpposition": 4702521386065954738, + "LastSaturnProgradeToRetrograde": 4702522056670159268, + "LastSaturnRetrogradeToPrograde": 4702521529307759912, + "LastSaturnWesternQuadrature": 4702522014939346053, + "NextSaturnConjunction": 4702522607909662837, + "NextSaturnEasternQuadrature": 4702522385163266546, + "NextSaturnOpposition": 4702522200274787837, + "NextSaturnProgradeToRetrograde": 4702522871515639267, + "NextSaturnRetrogradeToPrograde": 4702522343647597447, + "NextSaturnWesternQuadrature": 4702522829887210388 + } + }, + { + "input_utc": "2004-06-05T09:09:30.567Z", + "tt_jd_bits": 4702522536375663670, + "events": { + "LastSaturnConjunction": 4702521791599604601, + "LastSaturnEasternQuadrature": 4702522385163266546, + "LastSaturnOpposition": 4702522200274787837, + "LastSaturnProgradeToRetrograde": 4702522056670159268, + "LastSaturnRetrogradeToPrograde": 4702522343647597446, + "LastSaturnWesternQuadrature": 4702522014939346053, + "NextSaturnConjunction": 4702522607909662837, + "NextSaturnEasternQuadrature": 4702523200254560201, + "NextSaturnOpposition": 4702523014362657594, + "NextSaturnProgradeToRetrograde": 4702522871515639267, + "NextSaturnRetrogradeToPrograde": 4702523158325400874, + "NextSaturnWesternQuadrature": 4702522829887210388 + } + }, + { + "input_utc": "2004-11-09T15:20:59.567Z", + "tt_jd_bits": 4702522874084592506, + "events": { + "LastSaturnConjunction": 4702522607909662837, + "LastSaturnEasternQuadrature": 4702522385163266546, + "LastSaturnOpposition": 4702522200274787837, + "LastSaturnProgradeToRetrograde": 4702522871515639267, + "LastSaturnRetrogradeToPrograde": 4702522343647597446, + "LastSaturnWesternQuadrature": 4702522829887210388, + "NextSaturnConjunction": 4702523423987531694, + "NextSaturnEasternQuadrature": 4702523200254560201, + "NextSaturnOpposition": 4702523014362657594, + "NextSaturnProgradeToRetrograde": 4702523686106865906, + "NextSaturnRetrogradeToPrograde": 4702523158325400874, + "NextSaturnWesternQuadrature": 4702523644313450934 + } + }, + { + "input_utc": "2005-04-15T21:32:28.567Z", + "tt_jd_bits": 4702523211793521341, + "events": { + "LastSaturnConjunction": 4702522607909662837, + "LastSaturnEasternQuadrature": 4702523200254560201, + "LastSaturnOpposition": 4702523014362657594, + "LastSaturnProgradeToRetrograde": 4702522871515639267, + "LastSaturnRetrogradeToPrograde": 4702523158325400874, + "LastSaturnWesternQuadrature": 4702522829887210388, + "NextSaturnConjunction": 4702523423987531694, + "NextSaturnEasternQuadrature": 4702524015309486401, + "NextSaturnOpposition": 4702523828232851436, + "NextSaturnProgradeToRetrograde": 4702523686106865906, + "NextSaturnRetrogradeToPrograde": 4702523973299354559, + "NextSaturnWesternQuadrature": 4702523644313450934 + } + }, + { + "input_utc": "2005-09-18T09:43:57.567Z", + "tt_jd_bits": 4702523545744353793, + "events": { + "LastSaturnConjunction": 4702523423987531694, + "LastSaturnEasternQuadrature": 4702523200254560201, + "LastSaturnOpposition": 4702523014362657594, + "LastSaturnProgradeToRetrograde": 4702522871515639267, + "LastSaturnRetrogradeToPrograde": 4702523158325400874, + "LastSaturnWesternQuadrature": 4702522829887210388, + "NextSaturnConjunction": 4702524239574238653, + "NextSaturnEasternQuadrature": 4702524015309486401, + "NextSaturnOpposition": 4702523828232851436, + "NextSaturnProgradeToRetrograde": 4702523686106865906, + "NextSaturnRetrogradeToPrograde": 4702523973299354559, + "NextSaturnWesternQuadrature": 4702523644313450934 + } + }, + { + "input_utc": "2006-02-22T14:16:26.567Z", + "tt_jd_bits": 4702523883305667983, + "events": { + "LastSaturnConjunction": 4702523423987531694, + "LastSaturnEasternQuadrature": 4702523200254560201, + "LastSaturnOpposition": 4702523828232851436, + "LastSaturnProgradeToRetrograde": 4702523686106865906, + "LastSaturnRetrogradeToPrograde": 4702523158325400874, + "LastSaturnWesternQuadrature": 4702523644313450934, + "NextSaturnConjunction": 4702524239574238653, + "NextSaturnEasternQuadrature": 4702524015309486401, + "NextSaturnOpposition": 4702524641761931974, + "NextSaturnProgradeToRetrograde": 4702524500152336126, + "NextSaturnRetrogradeToPrograde": 4702523973299354559, + "NextSaturnWesternQuadrature": 4702524458086858917 + } + }, + { + "input_utc": "2006-07-29T20:27:55.567Z", + "tt_jd_bits": 4702524221014596819, + "events": { + "LastSaturnConjunction": 4702523423987531694, + "LastSaturnEasternQuadrature": 4702524015309486401, + "LastSaturnOpposition": 4702523828232851436, + "LastSaturnProgradeToRetrograde": 4702523686106865906, + "LastSaturnRetrogradeToPrograde": 4702523973299354559, + "LastSaturnWesternQuadrature": 4702523644313450934, + "NextSaturnConjunction": 4702524239574238653, + "NextSaturnEasternQuadrature": 4702524830148407467, + "NextSaturnOpposition": 4702524641761931974, + "NextSaturnProgradeToRetrograde": 4702524500152336126, + "NextSaturnRetrogradeToPrograde": 4702524788384548335, + "NextSaturnWesternQuadrature": 4702524458086858917 + } + }, + { + "input_utc": "2007-01-03T02:39:24.567Z", + "tt_jd_bits": 4702524558723525655, + "events": { + "LastSaturnConjunction": 4702524239574238653, + "LastSaturnEasternQuadrature": 4702524015309486401, + "LastSaturnOpposition": 4702523828232851436, + "LastSaturnProgradeToRetrograde": 4702524500152336126, + "LastSaturnRetrogradeToPrograde": 4702523973299354559, + "LastSaturnWesternQuadrature": 4702524458086858917, + "NextSaturnConjunction": 4702525054505781758, + "NextSaturnEasternQuadrature": 4702524830148407467, + "NextSaturnOpposition": 4702524641761931974, + "NextSaturnProgradeToRetrograde": 4702525313344307587, + "NextSaturnRetrogradeToPrograde": 4702524788384548335, + "NextSaturnWesternQuadrature": 4702525271124627365 + } + }, + { + "input_utc": "2007-06-09T08:50:53.567Z", + "tt_jd_bits": 4702524896432454491, + "events": { + "LastSaturnConjunction": 4702524239574238653, + "LastSaturnEasternQuadrature": 4702524830148407467, + "LastSaturnOpposition": 4702524641761931974, + "LastSaturnProgradeToRetrograde": 4702524500152336126, + "LastSaturnRetrogradeToPrograde": 4702524788384548335, + "LastSaturnWesternQuadrature": 4702524458086858917, + "NextSaturnConjunction": 4702525054505781758, + "NextSaturnEasternQuadrature": 4702525644611446357, + "NextSaturnOpposition": 4702525454861509683, + "NextSaturnProgradeToRetrograde": 4702525313344307587, + "NextSaturnRetrogradeToPrograde": 4702525603357423262, + "NextSaturnWesternQuadrature": 4702525271124627365 + } + }, + { + "input_utc": "2007-11-13T15:02:22.567Z", + "tt_jd_bits": 4702525234141383327, + "events": { + "LastSaturnConjunction": 4702525054505781758, + "LastSaturnEasternQuadrature": 4702524830148407467, + "LastSaturnOpposition": 4702524641761931974, + "LastSaturnProgradeToRetrograde": 4702524500152336126, + "LastSaturnRetrogradeToPrograde": 4702524788384548335, + "LastSaturnWesternQuadrature": 4702524458086858917, + "NextSaturnConjunction": 4702525868627604694, + "NextSaturnEasternQuadrature": 4702525644611446357, + "NextSaturnOpposition": 4702525454861509683, + "NextSaturnProgradeToRetrograde": 4702525313344307587, + "NextSaturnRetrogradeToPrograde": 4702525603357423262, + "NextSaturnWesternQuadrature": 4702525271124627365 + } + }, + { + "input_utc": "2008-04-18T21:13:51.567Z", + "tt_jd_bits": 4702525571850312163, + "events": { + "LastSaturnConjunction": 4702525054505781758, + "LastSaturnEasternQuadrature": 4702524830148407467, + "LastSaturnOpposition": 4702525454861509683, + "LastSaturnProgradeToRetrograde": 4702525313344307587, + "LastSaturnRetrogradeToPrograde": 4702524788384548335, + "LastSaturnWesternQuadrature": 4702525271124627365, + "NextSaturnConjunction": 4702525868627604694, + "NextSaturnEasternQuadrature": 4702525644611446357, + "NextSaturnOpposition": 4702526267512973295, + "NextSaturnProgradeToRetrograde": 4702526125760943366, + "NextSaturnRetrogradeToPrograde": 4702525603357423262, + "NextSaturnWesternQuadrature": 4702526083406105005 + } + }, + { + "input_utc": "2008-09-21T09:18:34.567Z", + "tt_jd_bits": 4702525905791053430, + "events": { + "LastSaturnConjunction": 4702525868627604694, + "LastSaturnEasternQuadrature": 4702525644611446357, + "LastSaturnOpposition": 4702525454861509683, + "LastSaturnProgradeToRetrograde": 4702525313344307587, + "LastSaturnRetrogradeToPrograde": 4702525603357423262, + "LastSaturnWesternQuadrature": 4702525271124627365, + "NextSaturnConjunction": 4702526681841740281, + "NextSaturnEasternQuadrature": 4702526458575522693, + "NextSaturnOpposition": 4702526267512973295, + "NextSaturnProgradeToRetrograde": 4702526125760943366, + "NextSaturnRetrogradeToPrograde": 4702526417756436584, + "NextSaturnWesternQuadrature": 4702526083406105005 + } + }, + { + "input_utc": "2009-02-25T15:30:03.567Z", + "tt_jd_bits": 4702526243500007121, + "events": { + "LastSaturnConjunction": 4702525868627604694, + "LastSaturnEasternQuadrature": 4702525644611446357, + "LastSaturnOpposition": 4702525454861509683, + "LastSaturnProgradeToRetrograde": 4702526125760943366, + "LastSaturnRetrogradeToPrograde": 4702525603357423262, + "LastSaturnWesternQuadrature": 4702526083406105005, + "NextSaturnConjunction": 4702526681841740281, + "NextSaturnEasternQuadrature": 4702526458575522693, + "NextSaturnOpposition": 4702526267512973295, + "NextSaturnProgradeToRetrograde": 4702526937433024155, + "NextSaturnRetrogradeToPrograde": 4702526417756436584, + "NextSaturnWesternQuadrature": 4702526894942936055 + } + }, + { + "input_utc": "2009-08-01T21:41:32.567Z", + "tt_jd_bits": 4702526581208935957, + "events": { + "LastSaturnConjunction": 4702525868627604694, + "LastSaturnEasternQuadrature": 4702526458575522693, + "LastSaturnOpposition": 4702526267512973295, + "LastSaturnProgradeToRetrograde": 4702526125760943366, + "LastSaturnRetrogradeToPrograde": 4702526417756436584, + "LastSaturnWesternQuadrature": 4702526083406105005, + "NextSaturnConjunction": 4702526681841740281, + "NextSaturnEasternQuadrature": 4702527271945670476, + "NextSaturnOpposition": 4702527079685422092, + "NextSaturnProgradeToRetrograde": 4702526937433024155, + "NextSaturnRetrogradeToPrograde": 4702527231412694346, + "NextSaturnWesternQuadrature": 4702526894942936055 + } + }, + { + "input_utc": "2010-01-06T02:14:01.567Z", + "tt_jd_bits": 4702526918770225292, + "events": { + "LastSaturnConjunction": 4702526681841740281, + "LastSaturnEasternQuadrature": 4702526458575522693, + "LastSaturnOpposition": 4702526267512973295, + "LastSaturnProgradeToRetrograde": 4702526125760943366, + "LastSaturnRetrogradeToPrograde": 4702526417756436584, + "LastSaturnWesternQuadrature": 4702526894942936055, + "NextSaturnConjunction": 4702527494156787303, + "NextSaturnEasternQuadrature": 4702527271945670476, + "NextSaturnOpposition": 4702527079685422092, + "NextSaturnProgradeToRetrograde": 4702526937433024155, + "NextSaturnRetrogradeToPrograde": 4702527231412694346, + "NextSaturnWesternQuadrature": 4702527705800705664 + } + }, + { + "input_utc": "2010-06-12T08:25:30.567Z", + "tt_jd_bits": 4702527256479154128, + "events": { + "LastSaturnConjunction": 4702526681841740281, + "LastSaturnEasternQuadrature": 4702526458575522693, + "LastSaturnOpposition": 4702527079685422092, + "LastSaturnProgradeToRetrograde": 4702526937433024155, + "LastSaturnRetrogradeToPrograde": 4702527231412694346, + "LastSaturnWesternQuadrature": 4702526894942936055, + "NextSaturnConjunction": 4702527494156787303, + "NextSaturnEasternQuadrature": 4702527271945670476, + "NextSaturnOpposition": 4702527891373299407, + "NextSaturnProgradeToRetrograde": 4702527748232075775, + "NextSaturnRetrogradeToPrograde": 4702528044263283830, + "NextSaturnWesternQuadrature": 4702527705800705664 + } + }, + { + "input_utc": "2010-11-16T14:36:59.567Z", + "tt_jd_bits": 4702527594188082964, + "events": { + "LastSaturnConjunction": 4702527494156787303, + "LastSaturnEasternQuadrature": 4702527271945670476, + "LastSaturnOpposition": 4702527079685422092, + "LastSaturnProgradeToRetrograde": 4702526937433024155, + "LastSaturnRetrogradeToPrograde": 4702527231412694346, + "LastSaturnWesternQuadrature": 4702526894942936055, + "NextSaturnConjunction": 4702528305594093674, + "NextSaturnEasternQuadrature": 4702528084677575789, + "NextSaturnOpposition": 4702527891373299407, + "NextSaturnProgradeToRetrograde": 4702527748232075775, + "NextSaturnRetrogradeToPrograde": 4702528044263283830, + "NextSaturnWesternQuadrature": 4702527705800705664 + } + } + ] +} diff --git a/basic/testdata/semianalytic/uranus_event_baseline.json b/basic/testdata/semianalytic/uranus_event_baseline.json new file mode 100644 index 0000000..59facd0 --- /dev/null +++ b/basic/testdata/semianalytic/uranus_event_baseline.json @@ -0,0 +1,1732 @@ +{ + "samples": [ + { + "input_utc": "1993-11-05T10:22:33.444Z", + "tt_jd_bits": 4702514236460201727, + "events": { + "LastUranusConjunction": 4702513589956428580, + "LastUranusEasternQuadrature": 4702514183194391354, + "LastUranusOpposition": 4702513987644203074, + "LastUranusProgradeToRetrograde": 4702513822178743697, + "LastUranusRetrogradeToPrograde": 4702514152991374876, + "LastUranusWesternQuadrature": 4702513791016334939, + "NextUranusConjunction": 4702514383078903865, + "NextUranusEasternQuadrature": 4702514976566485365, + "NextUranusOpposition": 4702514781339796483, + "NextUranusProgradeToRetrograde": 4702514615746391813, + "NextUranusRetrogradeToPrograde": 4702514946642709608, + "NextUranusWesternQuadrature": 4702514584468421060 + } + }, + { + "input_utc": "1994-07-02T16:35:44.444Z", + "tt_jd_bits": 4702514750265349779, + "events": { + "LastUranusConjunction": 4702514383078903865, + "LastUranusEasternQuadrature": 4702514183194391354, + "LastUranusOpposition": 4702513987644203074, + "LastUranusProgradeToRetrograde": 4702514615746391813, + "LastUranusRetrogradeToPrograde": 4702514152991374876, + "LastUranusWesternQuadrature": 4702514584468421060, + "NextUranusConjunction": 4702515176160775820, + "NextUranusEasternQuadrature": 4702514976566485365, + "NextUranusOpposition": 4702514781339796483, + "NextUranusProgradeToRetrograde": 4702515409073909647, + "NextUranusRetrogradeToPrograde": 4702514946642709608, + "NextUranusWesternQuadrature": 4702515377892657586 + } + }, + { + "input_utc": "1995-02-26T22:48:55.444Z", + "tt_jd_bits": 4702515264070472975, + "events": { + "LastUranusConjunction": 4702515176160775820, + "LastUranusEasternQuadrature": 4702514976566485365, + "LastUranusOpposition": 4702514781339796483, + "LastUranusProgradeToRetrograde": 4702514615746391813, + "LastUranusRetrogradeToPrograde": 4702514946642709608, + "LastUranusWesternQuadrature": 4702514584468421060, + "NextUranusConjunction": 4702515969206953483, + "NextUranusEasternQuadrature": 4702515769884036293, + "NextUranusOpposition": 4702515574993977078, + "NextUranusProgradeToRetrograde": 4702515409073909647, + "NextUranusRetrogradeToPrograde": 4702515740111440990, + "NextUranusWesternQuadrature": 4702515377892657586 + } + }, + { + "input_utc": "1995-10-24T05:02:06.444Z", + "tt_jd_bits": 4702515777875596170, + "events": { + "LastUranusConjunction": 4702515176160775820, + "LastUranusEasternQuadrature": 4702515769884036293, + "LastUranusOpposition": 4702515574993977078, + "LastUranusProgradeToRetrograde": 4702515409073909647, + "LastUranusRetrogradeToPrograde": 4702515740111440990, + "LastUranusWesternQuadrature": 4702515377892657586, + "NextUranusConjunction": 4702515969206953483, + "NextUranusEasternQuadrature": 4702516563112430999, + "NextUranusOpposition": 4702516368590855028, + "NextUranusProgradeToRetrograde": 4702516202604953141, + "NextUranusRetrogradeToPrograde": 4702516533650105909, + "NextUranusWesternQuadrature": 4702516171294419118 + } + }, + { + "input_utc": "1996-06-19T11:15:17.444Z", + "tt_jd_bits": 4702516291680744221, + "events": { + "LastUranusConjunction": 4702515969206953483, + "LastUranusEasternQuadrature": 4702515769884036293, + "LastUranusOpposition": 4702515574993977078, + "LastUranusProgradeToRetrograde": 4702516202604953141, + "LastUranusRetrogradeToPrograde": 4702515740111440988, + "LastUranusWesternQuadrature": 4702516171294419118, + "NextUranusConjunction": 4702516762213654130, + "NextUranusEasternQuadrature": 4702516563112430999, + "NextUranusOpposition": 4702516368590855028, + "NextUranusProgradeToRetrograde": 4702516995840821966, + "NextUranusRetrogradeToPrograde": 4702516533650105909, + "NextUranusWesternQuadrature": 4702516964690052376 + } + }, + { + "input_utc": "1997-02-13T17:28:28.444Z", + "tt_jd_bits": 4702516805485867417, + "events": { + "LastUranusConjunction": 4702516762213654130, + "LastUranusEasternQuadrature": 4702516563112430999, + "LastUranusOpposition": 4702516368590855028, + "LastUranusProgradeToRetrograde": 4702516202604953141, + "LastUranusRetrogradeToPrograde": 4702516533650105909, + "LastUranusWesternQuadrature": 4702516171294419118, + "NextUranusConjunction": 4702517555194019146, + "NextUranusEasternQuadrature": 4702517356287426305, + "NextUranusOpposition": 4702517162146131694, + "NextUranusProgradeToRetrograde": 4702516995840821966, + "NextUranusRetrogradeToPrograde": 4702517327006920678, + "NextUranusWesternQuadrature": 4702516964690052376 + } + }, + { + "input_utc": "1997-10-10T23:41:39.444Z", + "tt_jd_bits": 4702517319291015467, + "events": { + "LastUranusConjunction": 4702516762213654130, + "LastUranusEasternQuadrature": 4702516563112430999, + "LastUranusOpposition": 4702517162146131694, + "LastUranusProgradeToRetrograde": 4702516995840821966, + "LastUranusRetrogradeToPrograde": 4702516533650105909, + "LastUranusWesternQuadrature": 4702516964690052376, + "NextUranusConjunction": 4702517555194019146, + "NextUranusEasternQuadrature": 4702517356287426305, + "NextUranusOpposition": 4702517955616420944, + "NextUranusProgradeToRetrograde": 4702517789294606652, + "NextUranusRetrogradeToPrograde": 4702517327006920678, + "NextUranusWesternQuadrature": 4702517758036980436 + } + }, + { + "input_utc": "1998-06-05T11:54:50.444Z", + "tt_jd_bits": 4702517829338042279, + "events": { + "LastUranusConjunction": 4702517555194019146, + "LastUranusEasternQuadrature": 4702517356287426305, + "LastUranusOpposition": 4702517162146131694, + "LastUranusProgradeToRetrograde": 4702517789294606652, + "LastUranusRetrogradeToPrograde": 4702517327006920678, + "LastUranusWesternQuadrature": 4702517758036980436, + "NextUranusConjunction": 4702518348138289026, + "NextUranusEasternQuadrature": 4702518149384397935, + "NextUranusOpposition": 4702517955616420944, + "NextUranusProgradeToRetrograde": 4702518582421556755, + "NextUranusRetrogradeToPrograde": 4702518120430235048, + "NextUranusWesternQuadrature": 4702518551382001488 + } + }, + { + "input_utc": "1999-01-30T18:08:01.444Z", + "tt_jd_bits": 4702518343143190330, + "events": { + "LastUranusConjunction": 4702517555194019146, + "LastUranusEasternQuadrature": 4702518149384397935, + "LastUranusOpposition": 4702517955616420944, + "LastUranusProgradeToRetrograde": 4702517789294606652, + "LastUranusRetrogradeToPrograde": 4702518120430235048, + "LastUranusWesternQuadrature": 4702517758036980436, + "NextUranusConjunction": 4702518348138289026, + "NextUranusEasternQuadrature": 4702518942431004829, + "NextUranusOpposition": 4702518749060402679, + "NextUranusProgradeToRetrograde": 4702518582421556755, + "NextUranusRetrogradeToPrograde": 4702518913694121891, + "NextUranusWesternQuadrature": 4702518551382001488 + } + }, + { + "input_utc": "1999-09-26T22:24:12.444Z", + "tt_jd_bits": 4702518856773830479, + "events": { + "LastUranusConjunction": 4702518348138289026, + "LastUranusEasternQuadrature": 4702518149384397935, + "LastUranusOpposition": 4702518749060402679, + "LastUranusProgradeToRetrograde": 4702518582421556755, + "LastUranusRetrogradeToPrograde": 4702518120430235051, + "LastUranusWesternQuadrature": 4702518551382001488, + "NextUranusConjunction": 4702519141030238372, + "NextUranusEasternQuadrature": 4702518942431004829, + "NextUranusOpposition": 4702519542439181950, + "NextUranusProgradeToRetrograde": 4702519375780275139, + "NextUranusRetrogradeToPrograde": 4702518913694121891, + "NextUranusWesternQuadrature": 4702519344685930594 + } + }, + { + "input_utc": "2000-05-23T04:37:23.444Z", + "tt_jd_bits": 4702519370578953674, + "events": { + "LastUranusConjunction": 4702519141030238372, + "LastUranusEasternQuadrature": 4702518942431004829, + "LastUranusOpposition": 4702518749060402679, + "LastUranusProgradeToRetrograde": 4702518582421556755, + "LastUranusRetrogradeToPrograde": 4702518913694121891, + "LastUranusWesternQuadrature": 4702519344685930594, + "NextUranusConjunction": 4702519933907074315, + "NextUranusEasternQuadrature": 4702519735411013532, + "NextUranusOpposition": 4702519542439181950, + "NextUranusProgradeToRetrograde": 4702519375780275139, + "NextUranusRetrogradeToPrograde": 4702519706987378249, + "NextUranusWesternQuadrature": 4702520137987082394 + } + }, + { + "input_utc": "2001-01-17T10:50:34.444Z", + "tt_jd_bits": 4702519884384076870, + "events": { + "LastUranusConjunction": 4702519141030238372, + "LastUranusEasternQuadrature": 4702519735411013532, + "LastUranusOpposition": 4702519542439181950, + "LastUranusProgradeToRetrograde": 4702519375780275139, + "LastUranusRetrogradeToPrograde": 4702519706987378249, + "LastUranusWesternQuadrature": 4702519344685930594, + "NextUranusConjunction": 4702519933907074315, + "NextUranusEasternQuadrature": 4702520528330939778, + "NextUranusOpposition": 4702520335763063109, + "NextUranusProgradeToRetrograde": 4702520168850222885, + "NextUranusRetrogradeToPrograde": 4702520500136246098, + "NextUranusWesternQuadrature": 4702520137987082394 + } + }, + { + "input_utc": "2001-09-13T17:03:45.444Z", + "tt_jd_bits": 4702520398189200066, + "events": { + "LastUranusConjunction": 4702519933907074315, + "LastUranusEasternQuadrature": 4702519735411013532, + "LastUranusOpposition": 4702520335763063109, + "LastUranusProgradeToRetrograde": 4702520168850222885, + "LastUranusRetrogradeToPrograde": 4702519706987378249, + "LastUranusWesternQuadrature": 4702520137987082394, + "NextUranusConjunction": 4702520726756236739, + "NextUranusEasternQuadrature": 4702520528330939778, + "NextUranusOpposition": 4702521129032783644, + "NextUranusProgradeToRetrograde": 4702520962113326063, + "NextUranusRetrogradeToPrograde": 4702520500136246098, + "NextUranusWesternQuadrature": 4702520931243294219 + } + }, + { + "input_utc": "2002-05-10T23:16:56.444Z", + "tt_jd_bits": 4702520911994323261, + "events": { + "LastUranusConjunction": 4702520726756236739, + "LastUranusEasternQuadrature": 4702520528330939778, + "LastUranusOpposition": 4702520335763063109, + "LastUranusProgradeToRetrograde": 4702520168850222885, + "LastUranusRetrogradeToPrograde": 4702520500136246098, + "LastUranusWesternQuadrature": 4702520137987082394, + "NextUranusConjunction": 4702521519582393976, + "NextUranusEasternQuadrature": 4702521321203119144, + "NextUranusOpposition": 4702521129032783644, + "NextUranusProgradeToRetrograde": 4702520962113326063, + "NextUranusRetrogradeToPrograde": 4702521293269182258, + "NextUranusWesternQuadrature": 4702520931243294219 + } + }, + { + "input_utc": "2003-01-03T11:27:33.444Z", + "tt_jd_bits": 4702521422037522382, + "events": { + "LastUranusConjunction": 4702520726756236739, + "LastUranusEasternQuadrature": 4702521321203119144, + "LastUranusOpposition": 4702521129032783644, + "LastUranusProgradeToRetrograde": 4702520962113326063, + "LastUranusRetrogradeToPrograde": 4702521293269182258, + "LastUranusWesternQuadrature": 4702520931243294219, + "NextUranusConjunction": 4702521519582393976, + "NextUranusEasternQuadrature": 4702522114042826215, + "NextUranusOpposition": 4702521922272506938, + "NextUranusProgradeToRetrograde": 4702521755169979229, + "NextUranusRetrogradeToPrograde": 4702522086307185797, + "NextUranusWesternQuadrature": 4702521724486826620 + } + }, + { + "input_utc": "2003-08-30T17:40:44.444Z", + "tt_jd_bits": 4702521935842645578, + "events": { + "LastUranusConjunction": 4702521519582393976, + "LastUranusEasternQuadrature": 4702521321203119144, + "LastUranusOpposition": 4702521922272506938, + "LastUranusProgradeToRetrograde": 4702521755169979229, + "LastUranusRetrogradeToPrograde": 4702521293269182258, + "LastUranusWesternQuadrature": 4702521724486826620, + "NextUranusConjunction": 4702522312405238255, + "NextUranusEasternQuadrature": 4702522114042826215, + "NextUranusOpposition": 4702522715466869692, + "NextUranusProgradeToRetrograde": 4702522548401002404, + "NextUranusRetrogradeToPrograde": 4702522086307185797, + "NextUranusWesternQuadrature": 4702522517723695592 + } + }, + { + "input_utc": "2004-04-25T23:53:55.444Z", + "tt_jd_bits": 4702522449647768773, + "events": { + "LastUranusConjunction": 4702522312405238255, + "LastUranusEasternQuadrature": 4702522114042826215, + "LastUranusOpposition": 4702521922272506938, + "LastUranusProgradeToRetrograde": 4702521755169979229, + "LastUranusRetrogradeToPrograde": 4702522086307185797, + "LastUranusWesternQuadrature": 4702521724486826620, + "NextUranusConjunction": 4702523105223175500, + "NextUranusEasternQuadrature": 4702522906829070915, + "NextUranusOpposition": 4702522715466869692, + "NextUranusProgradeToRetrograde": 4702522548401002404, + "NextUranusRetrogradeToPrograde": 4702522879332705681, + "NextUranusWesternQuadrature": 4702522517723695592 + } + }, + { + "input_utc": "2004-12-21T06:07:06.444Z", + "tt_jd_bits": 4702522963452891969, + "events": { + "LastUranusConjunction": 4702522312405238255, + "LastUranusEasternQuadrature": 4702522906829070915, + "LastUranusOpposition": 4702522715466869692, + "LastUranusProgradeToRetrograde": 4702522548401002404, + "LastUranusRetrogradeToPrograde": 4702522879332705681, + "LastUranusWesternQuadrature": 4702522517723695592, + "NextUranusConjunction": 4702523105223175500, + "NextUranusEasternQuadrature": 4702523699606141280, + "NextUranusOpposition": 4702523508636855545, + "NextUranusProgradeToRetrograde": 4702523341453750306, + "NextUranusRetrogradeToPrograde": 4702523672241865101, + "NextUranusWesternQuadrature": 4702523310971611093 + } + }, + { + "input_utc": "2005-08-17T10:23:17.444Z", + "tt_jd_bits": 4702523477083532118, + "events": { + "LastUranusConjunction": 4702523105223175500, + "LastUranusEasternQuadrature": 4702522906829070915, + "LastUranusOpposition": 4702522715466869692, + "LastUranusProgradeToRetrograde": 4702523341453750306, + "LastUranusRetrogradeToPrograde": 4702522879332705681, + "LastUranusWesternQuadrature": 4702523310971611093, + "NextUranusConjunction": 4702523898046578325, + "NextUranusEasternQuadrature": 4702523699606141280, + "NextUranusOpposition": 4702523508636855545, + "NextUranusProgradeToRetrograde": 4702524134690960296, + "NextUranusRetrogradeToPrograde": 4702523672241865101, + "NextUranusWesternQuadrature": 4702524104190314585 + } + }, + { + "input_utc": "2006-04-13T16:36:28.444Z", + "tt_jd_bits": 4702523990888680169, + "events": { + "LastUranusConjunction": 4702523898046578325, + "LastUranusEasternQuadrature": 4702523699606141280, + "LastUranusOpposition": 4702523508636855545, + "LastUranusProgradeToRetrograde": 4702523341453750306, + "LastUranusRetrogradeToPrograde": 4702523672241865101, + "LastUranusWesternQuadrature": 4702523310971611093, + "NextUranusConjunction": 4702524690881620298, + "NextUranusEasternQuadrature": 4702524492359515575, + "NextUranusOpposition": 4702524301761604170, + "NextUranusProgradeToRetrograde": 4702524134690960296, + "NextUranusRetrogradeToPrograde": 4702524465219979788, + "NextUranusWesternQuadrature": 4702524104190314585 + } + }, + { + "input_utc": "2006-12-08T22:49:39.444Z", + "tt_jd_bits": 4702524504693803365, + "events": { + "LastUranusConjunction": 4702523898046578325, + "LastUranusEasternQuadrature": 4702524492359515575, + "LastUranusOpposition": 4702524301761604170, + "LastUranusProgradeToRetrograde": 4702524134690960296, + "LastUranusRetrogradeToPrograde": 4702524465219979788, + "LastUranusWesternQuadrature": 4702524104190314585, + "NextUranusConjunction": 4702524690881620298, + "NextUranusEasternQuadrature": 4702525285100838117, + "NextUranusOpposition": 4702525094886362025, + "NextUranusProgradeToRetrograde": 4702524927750286211, + "NextUranusRetrogradeToPrograde": 4702525258035592462, + "NextUranusWesternQuadrature": 4702524897441072246 + } + }, + { + "input_utc": "2007-08-03T11:02:50.444Z", + "tt_jd_bits": 4702525014740830176, + "events": { + "LastUranusConjunction": 4702524690881620298, + "LastUranusEasternQuadrature": 4702524492359515575, + "LastUranusOpposition": 4702524301761604170, + "LastUranusProgradeToRetrograde": 4702524927750286211, + "LastUranusRetrogradeToPrograde": 4702524465219979788, + "LastUranusWesternQuadrature": 4702524897441072246, + "NextUranusConjunction": 4702525483719950761, + "NextUranusEasternQuadrature": 4702525285100838117, + "NextUranusOpposition": 4702525094886362025, + "NextUranusProgradeToRetrograde": 4702525720994139735, + "NextUranusRetrogradeToPrograde": 4702525258035592462, + "NextUranusWesternQuadrature": 4702525690691498122 + } + }, + { + "input_utc": "2008-03-29T17:16:01.444Z", + "tt_jd_bits": 4702525528545953372, + "events": { + "LastUranusConjunction": 4702525483719950761, + "LastUranusEasternQuadrature": 4702525285100838117, + "LastUranusOpposition": 4702525094886362025, + "LastUranusProgradeToRetrograde": 4702524927750286211, + "LastUranusRetrogradeToPrograde": 4702525258035592462, + "LastUranusWesternQuadrature": 4702524897441072246, + "NextUranusConjunction": 4702526276601511849, + "NextUranusEasternQuadrature": 4702526077833344551, + "NextUranusOpposition": 4702525887987449575, + "NextUranusProgradeToRetrograde": 4702525720994139735, + "NextUranusRetrogradeToPrograde": 4702526051000597259, + "NextUranusWesternQuadrature": 4702525690691498122 + } + }, + { + "input_utc": "2008-11-23T23:29:12.444Z", + "tt_jd_bits": 4702526042351076568, + "events": { + "LastUranusConjunction": 4702525483719950761, + "LastUranusEasternQuadrature": 4702525285100838117, + "LastUranusOpposition": 4702525887987449575, + "LastUranusProgradeToRetrograde": 4702525720994139735, + "LastUranusRetrogradeToPrograde": 4702525258035592462, + "LastUranusWesternQuadrature": 4702525690691498122, + "NextUranusConjunction": 4702526276601511849, + "NextUranusEasternQuadrature": 4702526077833344551, + "NextUranusOpposition": 4702526681064911535, + "NextUranusProgradeToRetrograde": 4702526514089112545, + "NextUranusRetrogradeToPrograde": 4702526051000597259, + "NextUranusWesternQuadrature": 4702526483966388917 + } + }, + { + "input_utc": "2009-07-21T05:42:23.444Z", + "tt_jd_bits": 4702526556156224618, + "events": { + "LastUranusConjunction": 4702526276601511849, + "LastUranusEasternQuadrature": 4702526077833344551, + "LastUranusOpposition": 4702525887987449575, + "LastUranusProgradeToRetrograde": 4702526514089112545, + "LastUranusRetrogradeToPrograde": 4702526051000597259, + "LastUranusWesternQuadrature": 4702526483966388917, + "NextUranusConjunction": 4702527069503259834, + "NextUranusEasternQuadrature": 4702526870558749696, + "NextUranusOpposition": 4702526681064911535, + "NextUranusProgradeToRetrograde": 4702527307309226397, + "NextUranusRetrogradeToPrograde": 4702526843816138715, + "NextUranusWesternQuadrature": 4702527277238975987 + } + }, + { + "input_utc": "2010-03-17T11:55:34.444Z", + "tt_jd_bits": 4702527069961347814, + "events": { + "LastUranusConjunction": 4702527069503259834, + "LastUranusEasternQuadrature": 4702526870558749696, + "LastUranusOpposition": 4702526681064911535, + "LastUranusProgradeToRetrograde": 4702526514089112545, + "LastUranusRetrogradeToPrograde": 4702526843816138715, + "LastUranusWesternQuadrature": 4702526483966388917, + "NextUranusConjunction": 4702527862423691121, + "NextUranusEasternQuadrature": 4702527663280100351, + "NextUranusOpposition": 4702527474137866588, + "NextUranusProgradeToRetrograde": 4702527307309226397, + "NextUranusRetrogradeToPrograde": 4702527636725903637, + "NextUranusWesternQuadrature": 4702527277238975987 + } + }, + { + "input_utc": "2010-11-11T18:08:45.444Z", + "tt_jd_bits": 4702527583766471009, + "events": { + "LastUranusConjunction": 4702527069503259834, + "LastUranusEasternQuadrature": 4702526870558749696, + "LastUranusOpposition": 4702527474137866588, + "LastUranusProgradeToRetrograde": 4702527307309226397, + "LastUranusRetrogradeToPrograde": 4702526843816138715, + "LastUranusWesternQuadrature": 4702527277238975987, + "NextUranusConjunction": 4702527862423691121, + "NextUranusEasternQuadrature": 4702527663280100351, + "NextUranusOpposition": 4702528267210529259, + "NextUranusProgradeToRetrograde": 4702528100404291856, + "NextUranusRetrogradeToPrograde": 4702527636725903637, + "NextUranusWesternQuadrature": 4702528070534670244 + } + }, + { + "input_utc": "2011-07-08T22:24:56.444Z", + "tt_jd_bits": 4702528097397111159, + "events": { + "LastUranusConjunction": 4702527862423691121, + "LastUranusEasternQuadrature": 4702527663280100351, + "LastUranusOpposition": 4702527474137866588, + "LastUranusProgradeToRetrograde": 4702527307309226397, + "LastUranusRetrogradeToPrograde": 4702527636725903637, + "LastUranusWesternQuadrature": 4702528070534670244, + "NextUranusConjunction": 4702528655376073975, + "NextUranusEasternQuadrature": 4702528456008718924, + "NextUranusOpposition": 4702528267210529259, + "NextUranusProgradeToRetrograde": 4702528100404291856, + "NextUranusRetrogradeToPrograde": 4702528429612614754, + "NextUranusWesternQuadrature": 4702528863845796288 + } + }, + { + "input_utc": "2012-03-02T10:35:33.444Z", + "tt_jd_bits": 4702528607440310280, + "events": { + "LastUranusConjunction": 4702527862423691121, + "LastUranusEasternQuadrature": 4702528456008718924, + "LastUranusOpposition": 4702528267210529259, + "LastUranusProgradeToRetrograde": 4702528100404291856, + "LastUranusRetrogradeToPrograde": 4702528429612614754, + "LastUranusWesternQuadrature": 4702528070534670244, + "NextUranusConjunction": 4702528655376073975, + "NextUranusEasternQuadrature": 4702529248724111355, + "NextUranusOpposition": 4702529060258334373, + "NextUranusProgradeToRetrograde": 4702528893611508415, + "NextUranusRetrogradeToPrograde": 4702529222468708699, + "NextUranusWesternQuadrature": 4702528863845796288 + } + }, + { + "input_utc": "2012-10-27T16:48:44.444Z", + "tt_jd_bits": 4702529121245458330, + "events": { + "LastUranusConjunction": 4702528655376073975, + "LastUranusEasternQuadrature": 4702528456008718924, + "LastUranusOpposition": 4702529060258334373, + "LastUranusProgradeToRetrograde": 4702528893611508415, + "LastUranusRetrogradeToPrograde": 4702528429612614754, + "LastUranusWesternQuadrature": 4702528863845796288, + "NextUranusConjunction": 4702529448360653771, + "NextUranusEasternQuadrature": 4702529248724111355, + "NextUranusOpposition": 4702529853301602080, + "NextUranusProgradeToRetrograde": 4702529686670872585, + "NextUranusRetrogradeToPrograde": 4702529222468708699, + "NextUranusWesternQuadrature": 4702529657180522929 + } + }, + { + "input_utc": "2013-06-23T23:01:55.444Z", + "tt_jd_bits": 4702529635050581526, + "events": { + "LastUranusConjunction": 4702529448360653771, + "LastUranusEasternQuadrature": 4702529248724111355, + "LastUranusOpposition": 4702529060258334373, + "LastUranusProgradeToRetrograde": 4702528893611508415, + "LastUranusRetrogradeToPrograde": 4702529222468708699, + "LastUranusWesternQuadrature": 4702528863845796288, + "NextUranusConjunction": 4702530241364888538, + "NextUranusEasternQuadrature": 4702530041464612877, + "NextUranusOpposition": 4702529853301602080, + "NextUranusProgradeToRetrograde": 4702529686670872585, + "NextUranusRetrogradeToPrograde": 4702530015380220086, + "NextUranusWesternQuadrature": 4702529657180522929 + } + }, + { + "input_utc": "2014-02-18T05:15:06.444Z", + "tt_jd_bits": 4702530148855704721, + "events": { + "LastUranusConjunction": 4702529448360653771, + "LastUranusEasternQuadrature": 4702530041464612877, + "LastUranusOpposition": 4702529853301602080, + "LastUranusProgradeToRetrograde": 4702529686670872585, + "LastUranusRetrogradeToPrograde": 4702530015380220086, + "LastUranusWesternQuadrature": 4702529657180522929, + "NextUranusConjunction": 4702530241364888538, + "NextUranusEasternQuadrature": 4702530834206943163, + "NextUranusOpposition": 4702530646328225616, + "NextUranusProgradeToRetrograde": 4702530479893828936, + "NextUranusRetrogradeToPrograde": 4702530808225899488, + "NextUranusWesternQuadrature": 4702530450497844286 + } + }, + { + "input_utc": "2014-10-15T11:28:17.444Z", + "tt_jd_bits": 4702530662660827917, + "events": { + "LastUranusConjunction": 4702530241364888538, + "LastUranusEasternQuadrature": 4702530041464612877, + "LastUranusOpposition": 4702530646328225616, + "LastUranusProgradeToRetrograde": 4702530479893828936, + "LastUranusRetrogradeToPrograde": 4702530015380220086, + "LastUranusWesternQuadrature": 4702530450497844286, + "NextUranusConjunction": 4702531034411469999, + "NextUranusEasternQuadrature": 4702530834206943163, + "NextUranusOpposition": 4702531439363828252, + "NextUranusProgradeToRetrograde": 4702531272965735516, + "NextUranusRetrogradeToPrograde": 4702530808225899488, + "NextUranusWesternQuadrature": 4702531243858264878 + } + }, + { + "input_utc": "2015-06-11T17:41:28.444Z", + "tt_jd_bits": 4702531176465951113, + "events": { + "LastUranusConjunction": 4702531034411469999, + "LastUranusEasternQuadrature": 4702530834206943163, + "LastUranusOpposition": 4702530646328225616, + "LastUranusProgradeToRetrograde": 4702530479893828936, + "LastUranusRetrogradeToPrograde": 4702530808225899488, + "LastUranusWesternQuadrature": 4702530450497844286, + "NextUranusConjunction": 4702531827487948290, + "NextUranusEasternQuadrature": 4702531626959545443, + "NextUranusOpposition": 4702531439363828252, + "NextUranusProgradeToRetrograde": 4702531272965735516, + "NextUranusRetrogradeToPrograde": 4702531601089681573, + "NextUranusWesternQuadrature": 4702531243858264878 + } + }, + { + "input_utc": "2016-02-05T23:54:39.444Z", + "tt_jd_bits": 4702531690271099164, + "events": { + "LastUranusConjunction": 4702531034411469999, + "LastUranusEasternQuadrature": 4702531626959545443, + "LastUranusOpposition": 4702531439363828252, + "LastUranusProgradeToRetrograde": 4702531272965735516, + "LastUranusRetrogradeToPrograde": 4702531601089681573, + "LastUranusWesternQuadrature": 4702531243858264878, + "NextUranusConjunction": 4702531827487948290, + "NextUranusEasternQuadrature": 4702532419738683036, + "NextUranusOpposition": 4702532232402244312, + "NextUranusProgradeToRetrograde": 4702532066267714925, + "NextUranusRetrogradeToPrograde": 4702532393965398838, + "NextUranusWesternQuadrature": 4702532037232665279 + } + }, + { + "input_utc": "2016-09-30T12:07:50.444Z", + "tt_jd_bits": 4702532200318125976, + "events": { + "LastUranusConjunction": 4702531827487948290, + "LastUranusEasternQuadrature": 4702531626959545443, + "LastUranusOpposition": 4702531439363828252, + "LastUranusProgradeToRetrograde": 4702532066267714925, + "LastUranusRetrogradeToPrograde": 4702531601089681573, + "LastUranusWesternQuadrature": 4702532037232665279, + "NextUranusConjunction": 4702532620630138545, + "NextUranusEasternQuadrature": 4702532419738683036, + "NextUranusOpposition": 4702532232402244312, + "NextUranusProgradeToRetrograde": 4702532859390306795, + "NextUranusRetrogradeToPrograde": 4702532393965398838, + "NextUranusWesternQuadrature": 4702532830629185486 + } + }, + { + "input_utc": "2017-05-27T16:24:01.444Z", + "tt_jd_bits": 4702532713948790980, + "events": { + "LastUranusConjunction": 4702532620630138545, + "LastUranusEasternQuadrature": 4702532419738683036, + "LastUranusOpposition": 4702532232402244312, + "LastUranusProgradeToRetrograde": 4702532066267714925, + "LastUranusRetrogradeToPrograde": 4702532393965398838, + "LastUranusWesternQuadrature": 4702532037232665279, + "NextUranusConjunction": 4702533413812192416, + "NextUranusEasternQuadrature": 4702533212551818972, + "NextUranusOpposition": 4702533025437862363, + "NextUranusProgradeToRetrograde": 4702532859390306795, + "NextUranusRetrogradeToPrograde": 4702533186786558146, + "NextUranusWesternQuadrature": 4702532830629185486 + } + }, + { + "input_utc": "2018-01-21T22:37:12.444Z", + "tt_jd_bits": 4702533227753914176, + "events": { + "LastUranusConjunction": 4702532620630138545, + "LastUranusEasternQuadrature": 4702533212551818972, + "LastUranusOpposition": 4702533025437862363, + "LastUranusProgradeToRetrograde": 4702532859390306795, + "LastUranusRetrogradeToPrograde": 4702533186786558146, + "LastUranusWesternQuadrature": 4702532830629185486, + "NextUranusConjunction": 4702533413812192416, + "NextUranusEasternQuadrature": 4702534005399872750, + "NextUranusOpposition": 4702533818503388168, + "NextUranusProgradeToRetrograde": 4702533652766469976, + "NextUranusRetrogradeToPrograde": 4702533979710565569, + "NextUranusWesternQuadrature": 4702533624049837175 + } + }, + { + "input_utc": "2018-09-18T04:50:23.444Z", + "tt_jd_bits": 4702533741559037372, + "events": { + "LastUranusConjunction": 4702533413812192416, + "LastUranusEasternQuadrature": 4702533212551818972, + "LastUranusOpposition": 4702533025437862363, + "LastUranusProgradeToRetrograde": 4702533652766469976, + "LastUranusRetrogradeToPrograde": 4702533186786558146, + "LastUranusWesternQuadrature": 4702533624049837175, + "NextUranusConjunction": 4702534207048816179, + "NextUranusEasternQuadrature": 4702534005399872750, + "NextUranusOpposition": 4702533818503388168, + "NextUranusProgradeToRetrograde": 4702534445993130482, + "NextUranusRetrogradeToPrograde": 4702533979710565561, + "NextUranusWesternQuadrature": 4702534417513101105 + } + }, + { + "input_utc": "2019-05-15T11:03:34.444Z", + "tt_jd_bits": 4702534255364160567, + "events": { + "LastUranusConjunction": 4702534207048816179, + "LastUranusEasternQuadrature": 4702534005399872750, + "LastUranusOpposition": 4702533818503388168, + "LastUranusProgradeToRetrograde": 4702533652766469976, + "LastUranusRetrogradeToPrograde": 4702533979710565561, + "LastUranusWesternQuadrature": 4702533624049837175, + "NextUranusConjunction": 4702535000356338498, + "NextUranusEasternQuadrature": 4702534798304044725, + "NextUranusOpposition": 4702534611593638755, + "NextUranusProgradeToRetrograde": 4702534445993130482, + "NextUranusRetrogradeToPrograde": 4702534772576049736, + "NextUranusWesternQuadrature": 4702534417513101105 + } + }, + { + "input_utc": "2020-01-09T17:16:45.444Z", + "tt_jd_bits": 4702534769169283763, + "events": { + "LastUranusConjunction": 4702534207048816179, + "LastUranusEasternQuadrature": 4702534005399872750, + "LastUranusOpposition": 4702534611593638755, + "LastUranusProgradeToRetrograde": 4702534445993130482, + "LastUranusRetrogradeToPrograde": 4702533979710565561, + "LastUranusWesternQuadrature": 4702534417513101105, + "NextUranusConjunction": 4702535000356338498, + "NextUranusEasternQuadrature": 4702534798304044725, + "NextUranusOpposition": 4702535404697963125, + "NextUranusProgradeToRetrograde": 4702535239432645966, + "NextUranusRetrogradeToPrograde": 4702534772576049736, + "NextUranusWesternQuadrature": 4702535211015555292 + } + }, + { + "input_utc": "2020-09-04T23:29:56.444Z", + "tt_jd_bits": 4702535282974406958, + "events": { + "LastUranusConjunction": 4702535000356338498, + "LastUranusEasternQuadrature": 4702534798304044725, + "LastUranusOpposition": 4702534611593638755, + "LastUranusProgradeToRetrograde": 4702535239432645966, + "LastUranusRetrogradeToPrograde": 4702534772576049736, + "LastUranusWesternQuadrature": 4702535211015555292, + "NextUranusConjunction": 4702535793751519758, + "NextUranusEasternQuadrature": 4702535591253165383, + "NextUranusOpposition": 4702535404697963125, + "NextUranusProgradeToRetrograde": 4702536032804456627, + "NextUranusRetrogradeToPrograde": 4702535565553197828, + "NextUranusWesternQuadrature": 4702536004567918135 + } + }, + { + "input_utc": "2021-04-30T11:40:33.444Z", + "tt_jd_bits": 4702535793017606079, + "events": { + "LastUranusConjunction": 4702535000356338498, + "LastUranusEasternQuadrature": 4702535591253165383, + "LastUranusOpposition": 4702535404697963125, + "LastUranusProgradeToRetrograde": 4702535239432645966, + "LastUranusRetrogradeToPrograde": 4702535565553197828, + "LastUranusWesternQuadrature": 4702535211015555292, + "NextUranusConjunction": 4702535793751519758, + "NextUranusEasternQuadrature": 4702536384276838739, + "NextUranusOpposition": 4702536197842049078, + "NextUranusProgradeToRetrograde": 4702536032804456627, + "NextUranusRetrogradeToPrograde": 4702536358536016431, + "NextUranusWesternQuadrature": 4702536004567918135 + } + }, + { + "input_utc": "2021-12-25T17:53:44.444Z", + "tt_jd_bits": 4702536306822729275, + "events": { + "LastUranusConjunction": 4702535793751519758, + "LastUranusEasternQuadrature": 4702535591253165383, + "LastUranusOpposition": 4702536197842049078, + "LastUranusProgradeToRetrograde": 4702536032804456627, + "LastUranusRetrogradeToPrograde": 4702535565553197828, + "LastUranusWesternQuadrature": 4702536004567918135, + "NextUranusConjunction": 4702536587198423208, + "NextUranusEasternQuadrature": 4702536384276838739, + "NextUranusOpposition": 4702536991021956071, + "NextUranusProgradeToRetrograde": 4702536826271810208, + "NextUranusRetrogradeToPrograde": 4702536358536016431, + "NextUranusWesternQuadrature": 4702536798146701399 + } + }, + { + "input_utc": "2022-08-22T00:06:55.444Z", + "tt_jd_bits": 4702536820627852471, + "events": { + "LastUranusConjunction": 4702536587198423208, + "LastUranusEasternQuadrature": 4702536384276838739, + "LastUranusOpposition": 4702536197842049078, + "LastUranusProgradeToRetrograde": 4702536032804456627, + "LastUranusRetrogradeToPrograde": 4702536358536016431, + "LastUranusWesternQuadrature": 4702536798146701399, + "NextUranusConjunction": 4702537380744983364, + "NextUranusEasternQuadrature": 4702537177352222887, + "NextUranusOpposition": 4702536991021956071, + "NextUranusProgradeToRetrograde": 4702536826271810208, + "NextUranusRetrogradeToPrograde": 4702537151584992294, + "NextUranusWesternQuadrature": 4702537591792961682 + } + }, + { + "input_utc": "2023-04-18T04:23:06.444Z", + "tt_jd_bits": 4702537334258492620, + "events": { + "LastUranusConjunction": 4702536587198423208, + "LastUranusEasternQuadrature": 4702537177352222887, + "LastUranusOpposition": 4702536991021956071, + "LastUranusProgradeToRetrograde": 4702536826271810208, + "LastUranusRetrogradeToPrograde": 4702537151584992294, + "LastUranusWesternQuadrature": 4702536798146701399, + "NextUranusConjunction": 4702537380744983364, + "NextUranusEasternQuadrature": 4702537970482566333, + "NextUranusOpposition": 4702537784240294046, + "NextUranusProgradeToRetrograde": 4702537619787920719, + "NextUranusRetrogradeToPrograde": 4702537944718283737, + "NextUranusWesternQuadrature": 4702537591792961682 + } + }, + { + "input_utc": "2023-12-13T10:36:17.444Z", + "tt_jd_bits": 4702537848063615816, + "events": { + "LastUranusConjunction": 4702537380744983364, + "LastUranusEasternQuadrature": 4702537177352222887, + "LastUranusOpposition": 4702537784240294046, + "LastUranusProgradeToRetrograde": 4702537619787920719, + "LastUranusRetrogradeToPrograde": 4702537151584992294, + "LastUranusWesternQuadrature": 4702537591792961682, + "NextUranusConjunction": 4702538174355860156, + "NextUranusEasternQuadrature": 4702537970482566333, + "NextUranusOpposition": 4702538577503037915, + "NextUranusProgradeToRetrograde": 4702538413308559996, + "NextUranusRetrogradeToPrograde": 4702537944718283737, + "NextUranusWesternQuadrature": 4702538385483267720 + } + }, + { + "input_utc": "2024-08-08T16:49:28.444Z", + "tt_jd_bits": 4702538361868739011, + "events": { + "LastUranusConjunction": 4702538174355860156, + "LastUranusEasternQuadrature": 4702537970482566333, + "LastUranusOpposition": 4702537784240294046, + "LastUranusProgradeToRetrograde": 4702537619787920719, + "LastUranusRetrogradeToPrograde": 4702537944718283737, + "LastUranusWesternQuadrature": 4702537591792961682, + "NextUranusConjunction": 4702538968058039212, + "NextUranusEasternQuadrature": 4702538763686120784, + "NextUranusOpposition": 4702538577503037915, + "NextUranusProgradeToRetrograde": 4702538413308559996, + "NextUranusRetrogradeToPrograde": 4702538737876233455, + "NextUranusWesternQuadrature": 4702538385483267720 + } + }, + { + "input_utc": "1994-01-03T10:22:33.444Z", + "tt_jd_bits": 4702514363161736959, + "events": { + "LastUranusConjunction": 4702513589956428580, + "LastUranusEasternQuadrature": 4702514183194391354, + "LastUranusOpposition": 4702513987644203074, + "LastUranusProgradeToRetrograde": 4702513822178743697, + "LastUranusRetrogradeToPrograde": 4702514152991374876, + "LastUranusWesternQuadrature": 4702513791016334939, + "NextUranusConjunction": 4702514383078903865, + "NextUranusEasternQuadrature": 4702514976566485365, + "NextUranusOpposition": 4702514781339796483, + "NextUranusProgradeToRetrograde": 4702514615746391813, + "NextUranusRetrogradeToPrograde": 4702514946642709608, + "NextUranusWesternQuadrature": 4702514584468421060 + } + }, + { + "input_utc": "1994-06-15T15:40:04.444Z", + "tt_jd_bits": 4702514713675086757, + "events": { + "LastUranusConjunction": 4702514383078903865, + "LastUranusEasternQuadrature": 4702514183194391354, + "LastUranusOpposition": 4702513987644203074, + "LastUranusProgradeToRetrograde": 4702514615746391813, + "LastUranusRetrogradeToPrograde": 4702514152991374876, + "LastUranusWesternQuadrature": 4702514584468421060, + "NextUranusConjunction": 4702515176160775820, + "NextUranusEasternQuadrature": 4702514976566485365, + "NextUranusOpposition": 4702514781339796483, + "NextUranusProgradeToRetrograde": 4702515409073909647, + "NextUranusRetrogradeToPrograde": 4702514946642709608, + "NextUranusWesternQuadrature": 4702515377892657586 + } + }, + { + "input_utc": "1994-11-25T20:57:35.444Z", + "tt_jd_bits": 4702515064188461410, + "events": { + "LastUranusConjunction": 4702514383078903865, + "LastUranusEasternQuadrature": 4702514976566485365, + "LastUranusOpposition": 4702514781339796483, + "LastUranusProgradeToRetrograde": 4702514615746391813, + "LastUranusRetrogradeToPrograde": 4702514946642709608, + "LastUranusWesternQuadrature": 4702514584468421060, + "NextUranusConjunction": 4702515176160775820, + "NextUranusEasternQuadrature": 4702515769884036293, + "NextUranusOpposition": 4702515574993977078, + "NextUranusProgradeToRetrograde": 4702515409073909647, + "NextUranusRetrogradeToPrograde": 4702515740111440990, + "NextUranusWesternQuadrature": 4702515377892657586 + } + }, + { + "input_utc": "1995-05-08T02:15:06.444Z", + "tt_jd_bits": 4702515414701811207, + "events": { + "LastUranusConjunction": 4702515176160775820, + "LastUranusEasternQuadrature": 4702514976566485365, + "LastUranusOpposition": 4702514781339796483, + "LastUranusProgradeToRetrograde": 4702515409073909647, + "LastUranusRetrogradeToPrograde": 4702514946642709608, + "LastUranusWesternQuadrature": 4702515377892657586, + "NextUranusConjunction": 4702515969206953483, + "NextUranusEasternQuadrature": 4702515769884036293, + "NextUranusOpposition": 4702515574993977078, + "NextUranusProgradeToRetrograde": 4702516202604953141, + "NextUranusRetrogradeToPrograde": 4702515740111440990, + "NextUranusWesternQuadrature": 4702516171294419118 + } + }, + { + "input_utc": "1995-10-18T07:32:37.444Z", + "tt_jd_bits": 4702515765215161005, + "events": { + "LastUranusConjunction": 4702515176160775820, + "LastUranusEasternQuadrature": 4702514976566485365, + "LastUranusOpposition": 4702515574993977078, + "LastUranusProgradeToRetrograde": 4702515409073909647, + "LastUranusRetrogradeToPrograde": 4702515740111440990, + "LastUranusWesternQuadrature": 4702515377892657586, + "NextUranusConjunction": 4702515969206953483, + "NextUranusEasternQuadrature": 4702515769884036293, + "NextUranusOpposition": 4702516368590855028, + "NextUranusProgradeToRetrograde": 4702516202604953141, + "NextUranusRetrogradeToPrograde": 4702516533650105909, + "NextUranusWesternQuadrature": 4702516171294419118 + } + }, + { + "input_utc": "1996-03-29T12:50:08.444Z", + "tt_jd_bits": 4702516115728535657, + "events": { + "LastUranusConjunction": 4702515969206953483, + "LastUranusEasternQuadrature": 4702515769884036293, + "LastUranusOpposition": 4702515574993977078, + "LastUranusProgradeToRetrograde": 4702515409073909647, + "LastUranusRetrogradeToPrograde": 4702515740111440990, + "LastUranusWesternQuadrature": 4702515377892657586, + "NextUranusConjunction": 4702516762213654130, + "NextUranusEasternQuadrature": 4702516563112430999, + "NextUranusOpposition": 4702516368590855028, + "NextUranusProgradeToRetrograde": 4702516202604953141, + "NextUranusRetrogradeToPrograde": 4702516533650105909, + "NextUranusWesternQuadrature": 4702516171294419118 + } + }, + { + "input_utc": "1996-09-08T18:07:39.444Z", + "tt_jd_bits": 4702516466241885455, + "events": { + "LastUranusConjunction": 4702515969206953483, + "LastUranusEasternQuadrature": 4702515769884036293, + "LastUranusOpposition": 4702516368590855028, + "LastUranusProgradeToRetrograde": 4702516202604953141, + "LastUranusRetrogradeToPrograde": 4702515740111440990, + "LastUranusWesternQuadrature": 4702516171294419118, + "NextUranusConjunction": 4702516762213654130, + "NextUranusEasternQuadrature": 4702516563112430999, + "NextUranusOpposition": 4702517162146131694, + "NextUranusProgradeToRetrograde": 4702516995840821966, + "NextUranusRetrogradeToPrograde": 4702516533650105909, + "NextUranusWesternQuadrature": 4702516964690052376 + } + }, + { + "input_utc": "1997-02-18T23:25:10.444Z", + "tt_jd_bits": 4702516816755235252, + "events": { + "LastUranusConjunction": 4702516762213654130, + "LastUranusEasternQuadrature": 4702516563112430999, + "LastUranusOpposition": 4702516368590855028, + "LastUranusProgradeToRetrograde": 4702516202604953141, + "LastUranusRetrogradeToPrograde": 4702516533650105909, + "LastUranusWesternQuadrature": 4702516171294419118, + "NextUranusConjunction": 4702517555194019146, + "NextUranusEasternQuadrature": 4702517356287426305, + "NextUranusOpposition": 4702517162146131694, + "NextUranusProgradeToRetrograde": 4702516995840821966, + "NextUranusRetrogradeToPrograde": 4702517327006920678, + "NextUranusWesternQuadrature": 4702516964690052376 + } + }, + { + "input_utc": "1997-07-30T12:42:41.444Z", + "tt_jd_bits": 4702517163689470491, + "events": { + "LastUranusConjunction": 4702516762213654130, + "LastUranusEasternQuadrature": 4702516563112430999, + "LastUranusOpposition": 4702517162146131694, + "LastUranusProgradeToRetrograde": 4702516995840821966, + "LastUranusRetrogradeToPrograde": 4702516533650105909, + "LastUranusWesternQuadrature": 4702516964690052376, + "NextUranusConjunction": 4702517555194019146, + "NextUranusEasternQuadrature": 4702517356287426305, + "NextUranusOpposition": 4702517955616420944, + "NextUranusProgradeToRetrograde": 4702517789294606652, + "NextUranusRetrogradeToPrograde": 4702517327006920678, + "NextUranusWesternQuadrature": 4702517758036980436 + } + }, + { + "input_utc": "1998-01-09T18:00:12.444Z", + "tt_jd_bits": 4702517514202820288, + "events": { + "LastUranusConjunction": 4702516762213654130, + "LastUranusEasternQuadrature": 4702517356287426305, + "LastUranusOpposition": 4702517162146131694, + "LastUranusProgradeToRetrograde": 4702516995840821966, + "LastUranusRetrogradeToPrograde": 4702517327006920678, + "LastUranusWesternQuadrature": 4702516964690052376, + "NextUranusConjunction": 4702517555194019146, + "NextUranusEasternQuadrature": 4702518149384397935, + "NextUranusOpposition": 4702517955616420944, + "NextUranusProgradeToRetrograde": 4702517789294606652, + "NextUranusRetrogradeToPrograde": 4702518120430235048, + "NextUranusWesternQuadrature": 4702517758036980436 + } + }, + { + "input_utc": "1998-06-21T23:17:43.444Z", + "tt_jd_bits": 4702517864716170086, + "events": { + "LastUranusConjunction": 4702517555194019146, + "LastUranusEasternQuadrature": 4702517356287426305, + "LastUranusOpposition": 4702517162146131694, + "LastUranusProgradeToRetrograde": 4702517789294606652, + "LastUranusRetrogradeToPrograde": 4702517327006920678, + "LastUranusWesternQuadrature": 4702517758036980436, + "NextUranusConjunction": 4702518348138289026, + "NextUranusEasternQuadrature": 4702518149384397935, + "NextUranusOpposition": 4702517955616420944, + "NextUranusProgradeToRetrograde": 4702518582421556755, + "NextUranusRetrogradeToPrograde": 4702518120430235048, + "NextUranusWesternQuadrature": 4702518551382001488 + } + }, + { + "input_utc": "1998-12-02T04:35:14.444Z", + "tt_jd_bits": 4702518215229519883, + "events": { + "LastUranusConjunction": 4702517555194019146, + "LastUranusEasternQuadrature": 4702518149384397935, + "LastUranusOpposition": 4702517955616420944, + "LastUranusProgradeToRetrograde": 4702517789294606652, + "LastUranusRetrogradeToPrograde": 4702518120430235048, + "LastUranusWesternQuadrature": 4702517758036980436, + "NextUranusConjunction": 4702518348138289026, + "NextUranusEasternQuadrature": 4702518942431004829, + "NextUranusOpposition": 4702518749060402679, + "NextUranusProgradeToRetrograde": 4702518582421556755, + "NextUranusRetrogradeToPrograde": 4702518913694121891, + "NextUranusWesternQuadrature": 4702518551382001488 + } + }, + { + "input_utc": "1999-05-14T06:28:45.444Z", + "tt_jd_bits": 4702518565438667685, + "events": { + "LastUranusConjunction": 4702518348138289026, + "LastUranusEasternQuadrature": 4702518149384397935, + "LastUranusOpposition": 4702517955616420944, + "LastUranusProgradeToRetrograde": 4702517789294606652, + "LastUranusRetrogradeToPrograde": 4702518120430235048, + "LastUranusWesternQuadrature": 4702518551382001488, + "NextUranusConjunction": 4702519141030238372, + "NextUranusEasternQuadrature": 4702518942431004829, + "NextUranusOpposition": 4702518749060402679, + "NextUranusProgradeToRetrograde": 4702518582421556755, + "NextUranusRetrogradeToPrograde": 4702518913694121891, + "NextUranusWesternQuadrature": 4702519344685930594 + } + }, + { + "input_utc": "1999-10-24T11:39:33.444Z", + "tt_jd_bits": 4702518915942000863, + "events": { + "LastUranusConjunction": 4702518348138289026, + "LastUranusEasternQuadrature": 4702518149384397935, + "LastUranusOpposition": 4702518749060402679, + "LastUranusProgradeToRetrograde": 4702518582421556755, + "LastUranusRetrogradeToPrograde": 4702518913694121891, + "LastUranusWesternQuadrature": 4702518551382001488, + "NextUranusConjunction": 4702519141030238372, + "NextUranusEasternQuadrature": 4702518942431004829, + "NextUranusOpposition": 4702519542439181950, + "NextUranusProgradeToRetrograde": 4702519375780275139, + "NextUranusRetrogradeToPrograde": 4702519706987378249, + "NextUranusWesternQuadrature": 4702519344685930594 + } + }, + { + "input_utc": "2000-04-04T16:57:04.444Z", + "tt_jd_bits": 4702519266455350661, + "events": { + "LastUranusConjunction": 4702519141030238372, + "LastUranusEasternQuadrature": 4702518942431004829, + "LastUranusOpposition": 4702518749060402679, + "LastUranusProgradeToRetrograde": 4702518582421556755, + "LastUranusRetrogradeToPrograde": 4702518913694121891, + "LastUranusWesternQuadrature": 4702518551382001488, + "NextUranusConjunction": 4702519933907074315, + "NextUranusEasternQuadrature": 4702519735411013532, + "NextUranusOpposition": 4702519542439181950, + "NextUranusProgradeToRetrograde": 4702519375780275139, + "NextUranusRetrogradeToPrograde": 4702519706987378249, + "NextUranusWesternQuadrature": 4702519344685930594 + } + }, + { + "input_utc": "2000-09-14T22:14:35.444Z", + "tt_jd_bits": 4702519616968700458, + "events": { + "LastUranusConjunction": 4702519141030238372, + "LastUranusEasternQuadrature": 4702518942431004829, + "LastUranusOpposition": 4702519542439181950, + "LastUranusProgradeToRetrograde": 4702519375780275139, + "LastUranusRetrogradeToPrograde": 4702518913694121891, + "LastUranusWesternQuadrature": 4702519344685930594, + "NextUranusConjunction": 4702519933907074315, + "NextUranusEasternQuadrature": 4702519735411013532, + "NextUranusOpposition": 4702520335763063109, + "NextUranusProgradeToRetrograde": 4702520168850222885, + "NextUranusRetrogradeToPrograde": 4702519706987378249, + "NextUranusWesternQuadrature": 4702520137987082394 + } + }, + { + "input_utc": "2001-02-23T11:32:06.444Z", + "tt_jd_bits": 4702519963902910842, + "events": { + "LastUranusConjunction": 4702519933907074315, + "LastUranusEasternQuadrature": 4702519735411013532, + "LastUranusOpposition": 4702519542439181950, + "LastUranusProgradeToRetrograde": 4702519375780275139, + "LastUranusRetrogradeToPrograde": 4702519706987378249, + "LastUranusWesternQuadrature": 4702519344685930594, + "NextUranusConjunction": 4702520726756236739, + "NextUranusEasternQuadrature": 4702520528330939778, + "NextUranusOpposition": 4702520335763063109, + "NextUranusProgradeToRetrograde": 4702520168850222885, + "NextUranusRetrogradeToPrograde": 4702520500136246098, + "NextUranusWesternQuadrature": 4702520137987082394 + } + }, + { + "input_utc": "2001-08-05T16:49:37.444Z", + "tt_jd_bits": 4702520314416260639, + "events": { + "LastUranusConjunction": 4702519933907074315, + "LastUranusEasternQuadrature": 4702519735411013532, + "LastUranusOpposition": 4702519542439181950, + "LastUranusProgradeToRetrograde": 4702520168850222885, + "LastUranusRetrogradeToPrograde": 4702519706987378249, + "LastUranusWesternQuadrature": 4702520137987082394, + "NextUranusConjunction": 4702520726756236739, + "NextUranusEasternQuadrature": 4702520528330939778, + "NextUranusOpposition": 4702520335763063109, + "NextUranusProgradeToRetrograde": 4702520962113326063, + "NextUranusRetrogradeToPrograde": 4702520500136246098, + "NextUranusWesternQuadrature": 4702520931243294219 + } + }, + { + "input_utc": "2002-01-15T22:07:08.444Z", + "tt_jd_bits": 4702520664929610437, + "events": { + "LastUranusConjunction": 4702519933907074315, + "LastUranusEasternQuadrature": 4702520528330939778, + "LastUranusOpposition": 4702520335763063109, + "LastUranusProgradeToRetrograde": 4702520168850222885, + "LastUranusRetrogradeToPrograde": 4702520500136246098, + "LastUranusWesternQuadrature": 4702520137987082394, + "NextUranusConjunction": 4702520726756236739, + "NextUranusEasternQuadrature": 4702521321203119144, + "NextUranusOpposition": 4702521129032783644, + "NextUranusProgradeToRetrograde": 4702520962113326063, + "NextUranusRetrogradeToPrograde": 4702521293269182258, + "NextUranusWesternQuadrature": 4702520931243294219 + } + }, + { + "input_utc": "2002-06-28T03:24:39.444Z", + "tt_jd_bits": 4702521015442960234, + "events": { + "LastUranusConjunction": 4702520726756236739, + "LastUranusEasternQuadrature": 4702520528330939778, + "LastUranusOpposition": 4702520335763063109, + "LastUranusProgradeToRetrograde": 4702520962113326063, + "LastUranusRetrogradeToPrograde": 4702520500136246098, + "LastUranusWesternQuadrature": 4702520931243294219, + "NextUranusConjunction": 4702521519582393976, + "NextUranusEasternQuadrature": 4702521321203119144, + "NextUranusOpposition": 4702521129032783644, + "NextUranusProgradeToRetrograde": 4702521755169979229, + "NextUranusRetrogradeToPrograde": 4702521293269182258, + "NextUranusWesternQuadrature": 4702521724486826620 + } + }, + { + "input_utc": "2002-12-08T08:42:10.444Z", + "tt_jd_bits": 4702521365956310031, + "events": { + "LastUranusConjunction": 4702520726756236739, + "LastUranusEasternQuadrature": 4702521321203119144, + "LastUranusOpposition": 4702521129032783644, + "LastUranusProgradeToRetrograde": 4702520962113326063, + "LastUranusRetrogradeToPrograde": 4702521293269182258, + "LastUranusWesternQuadrature": 4702520931243294219, + "NextUranusConjunction": 4702521519582393976, + "NextUranusEasternQuadrature": 4702522114042826215, + "NextUranusOpposition": 4702521922272506938, + "NextUranusProgradeToRetrograde": 4702521755169979229, + "NextUranusRetrogradeToPrograde": 4702522086307185797, + "NextUranusWesternQuadrature": 4702521724486826620 + } + }, + { + "input_utc": "2003-05-20T13:59:41.444Z", + "tt_jd_bits": 4702521716469659829, + "events": { + "LastUranusConjunction": 4702521519582393976, + "LastUranusEasternQuadrature": 4702521321203119144, + "LastUranusOpposition": 4702521129032783644, + "LastUranusProgradeToRetrograde": 4702520962113326063, + "LastUranusRetrogradeToPrograde": 4702521293269182258, + "LastUranusWesternQuadrature": 4702520931243294219, + "NextUranusConjunction": 4702522312405238255, + "NextUranusEasternQuadrature": 4702522114042826215, + "NextUranusOpposition": 4702521922272506938, + "NextUranusProgradeToRetrograde": 4702521755169979229, + "NextUranusRetrogradeToPrograde": 4702522086307185797, + "NextUranusWesternQuadrature": 4702521724486826620 + } + }, + { + "input_utc": "2003-10-30T19:17:12.444Z", + "tt_jd_bits": 4702522066983009626, + "events": { + "LastUranusConjunction": 4702521519582393976, + "LastUranusEasternQuadrature": 4702521321203119144, + "LastUranusOpposition": 4702521922272506938, + "LastUranusProgradeToRetrograde": 4702521755169979229, + "LastUranusRetrogradeToPrograde": 4702521293269182258, + "LastUranusWesternQuadrature": 4702521724486826620, + "NextUranusConjunction": 4702522312405238255, + "NextUranusEasternQuadrature": 4702522114042826215, + "NextUranusOpposition": 4702522715466869692, + "NextUranusProgradeToRetrograde": 4702522548401002404, + "NextUranusRetrogradeToPrograde": 4702522086307185797, + "NextUranusWesternQuadrature": 4702522517723695592 + } + }, + { + "input_utc": "2004-04-11T00:34:43.444Z", + "tt_jd_bits": 4702522417496359423, + "events": { + "LastUranusConjunction": 4702522312405238255, + "LastUranusEasternQuadrature": 4702522114042826215, + "LastUranusOpposition": 4702521922272506938, + "LastUranusProgradeToRetrograde": 4702521755169979229, + "LastUranusRetrogradeToPrograde": 4702522086307185797, + "LastUranusWesternQuadrature": 4702521724486826620, + "NextUranusConjunction": 4702523105223175500, + "NextUranusEasternQuadrature": 4702522906829070915, + "NextUranusOpposition": 4702522715466869692, + "NextUranusProgradeToRetrograde": 4702522548401002404, + "NextUranusRetrogradeToPrograde": 4702522879332705681, + "NextUranusWesternQuadrature": 4702522517723695592 + } + }, + { + "input_utc": "2004-09-19T10:28:14.444Z", + "tt_jd_bits": 4702522764126342957, + "events": { + "LastUranusConjunction": 4702522312405238255, + "LastUranusEasternQuadrature": 4702522114042826215, + "LastUranusOpposition": 4702522715466869692, + "LastUranusProgradeToRetrograde": 4702522548401002404, + "LastUranusRetrogradeToPrograde": 4702522086307185797, + "LastUranusWesternQuadrature": 4702522517723695592, + "NextUranusConjunction": 4702523105223175500, + "NextUranusEasternQuadrature": 4702522906829070915, + "NextUranusOpposition": 4702523508636855545, + "NextUranusProgradeToRetrograde": 4702523341453750306, + "NextUranusRetrogradeToPrograde": 4702522879332705681, + "NextUranusWesternQuadrature": 4702523310971611093 + } + }, + { + "input_utc": "2005-03-01T15:45:45.444Z", + "tt_jd_bits": 4702523114639692755, + "events": { + "LastUranusConjunction": 4702523105223175500, + "LastUranusEasternQuadrature": 4702522906829070915, + "LastUranusOpposition": 4702522715466869692, + "LastUranusProgradeToRetrograde": 4702522548401002404, + "LastUranusRetrogradeToPrograde": 4702522879332705681, + "LastUranusWesternQuadrature": 4702522517723695592, + "NextUranusConjunction": 4702523898046578325, + "NextUranusEasternQuadrature": 4702523699606141280, + "NextUranusOpposition": 4702523508636855545, + "NextUranusProgradeToRetrograde": 4702523341453750306, + "NextUranusRetrogradeToPrograde": 4702523672241865101, + "NextUranusWesternQuadrature": 4702523310971611093 + } + }, + { + "input_utc": "2005-08-11T20:56:33.444Z", + "tt_jd_bits": 4702523465143025933, + "events": { + "LastUranusConjunction": 4702523105223175500, + "LastUranusEasternQuadrature": 4702522906829070915, + "LastUranusOpposition": 4702522715466869692, + "LastUranusProgradeToRetrograde": 4702523341453750306, + "LastUranusRetrogradeToPrograde": 4702522879332705676, + "LastUranusWesternQuadrature": 4702523310971611093, + "NextUranusConjunction": 4702523898046578325, + "NextUranusEasternQuadrature": 4702523699606141280, + "NextUranusOpposition": 4702523508636855545, + "NextUranusProgradeToRetrograde": 4702524134690960296, + "NextUranusRetrogradeToPrograde": 4702523672241865101, + "NextUranusWesternQuadrature": 4702524104190314585 + } + }, + { + "input_utc": "2006-01-22T02:14:04.444Z", + "tt_jd_bits": 4702523815656400585, + "events": { + "LastUranusConjunction": 4702523105223175500, + "LastUranusEasternQuadrature": 4702523699606141280, + "LastUranusOpposition": 4702523508636855545, + "LastUranusProgradeToRetrograde": 4702523341453750306, + "LastUranusRetrogradeToPrograde": 4702523672241865101, + "LastUranusWesternQuadrature": 4702523310971611093, + "NextUranusConjunction": 4702523898046578325, + "NextUranusEasternQuadrature": 4702524492359515575, + "NextUranusOpposition": 4702524301761604170, + "NextUranusProgradeToRetrograde": 4702524134690960296, + "NextUranusRetrogradeToPrograde": 4702524465219979788, + "NextUranusWesternQuadrature": 4702524104190314585 + } + }, + { + "input_utc": "2006-07-04T07:31:35.444Z", + "tt_jd_bits": 4702524166169750382, + "events": { + "LastUranusConjunction": 4702523898046578325, + "LastUranusEasternQuadrature": 4702523699606141280, + "LastUranusOpposition": 4702523508636855545, + "LastUranusProgradeToRetrograde": 4702524134690960296, + "LastUranusRetrogradeToPrograde": 4702523672241865101, + "LastUranusWesternQuadrature": 4702524104190314585, + "NextUranusConjunction": 4702524690881620298, + "NextUranusEasternQuadrature": 4702524492359515575, + "NextUranusOpposition": 4702524301761604170, + "NextUranusProgradeToRetrograde": 4702524927750286211, + "NextUranusRetrogradeToPrograde": 4702524465219979788, + "NextUranusWesternQuadrature": 4702524897441072246 + } + }, + { + "input_utc": "2006-12-14T12:49:06.444Z", + "tt_jd_bits": 4702524516683100180, + "events": { + "LastUranusConjunction": 4702523898046578325, + "LastUranusEasternQuadrature": 4702524492359515575, + "LastUranusOpposition": 4702524301761604170, + "LastUranusProgradeToRetrograde": 4702524134690960296, + "LastUranusRetrogradeToPrograde": 4702524465219979788, + "LastUranusWesternQuadrature": 4702524104190314585, + "NextUranusConjunction": 4702524690881620298, + "NextUranusEasternQuadrature": 4702525285100838117, + "NextUranusOpposition": 4702525094886362025, + "NextUranusProgradeToRetrograde": 4702524927750286211, + "NextUranusRetrogradeToPrograde": 4702525258035592462, + "NextUranusWesternQuadrature": 4702524897441072246 + } + }, + { + "input_utc": "2007-05-26T18:06:37.444Z", + "tt_jd_bits": 4702524867196449977, + "events": { + "LastUranusConjunction": 4702524690881620298, + "LastUranusEasternQuadrature": 4702524492359515575, + "LastUranusOpposition": 4702524301761604170, + "LastUranusProgradeToRetrograde": 4702524134690960296, + "LastUranusRetrogradeToPrograde": 4702524465219979788, + "LastUranusWesternQuadrature": 4702524104190314585, + "NextUranusConjunction": 4702525483719950761, + "NextUranusEasternQuadrature": 4702525285100838117, + "NextUranusOpposition": 4702525094886362025, + "NextUranusProgradeToRetrograde": 4702524927750286211, + "NextUranusRetrogradeToPrograde": 4702525258035592462, + "NextUranusWesternQuadrature": 4702524897441072246 + } + }, + { + "input_utc": "2007-11-05T23:24:08.444Z", + "tt_jd_bits": 4702525217709799775, + "events": { + "LastUranusConjunction": 4702524690881620298, + "LastUranusEasternQuadrature": 4702524492359515575, + "LastUranusOpposition": 4702525094886362025, + "LastUranusProgradeToRetrograde": 4702524927750286211, + "LastUranusRetrogradeToPrograde": 4702524465219979788, + "LastUranusWesternQuadrature": 4702524897441072246, + "NextUranusConjunction": 4702525483719950761, + "NextUranusEasternQuadrature": 4702525285100838117, + "NextUranusOpposition": 4702525887987449575, + "NextUranusProgradeToRetrograde": 4702525720994139735, + "NextUranusRetrogradeToPrograde": 4702525258035592462, + "NextUranusWesternQuadrature": 4702525690691498122 + } + }, + { + "input_utc": "2008-04-15T12:41:39.444Z", + "tt_jd_bits": 4702525564644010159, + "events": { + "LastUranusConjunction": 4702525483719950761, + "LastUranusEasternQuadrature": 4702525285100838117, + "LastUranusOpposition": 4702525094886362025, + "LastUranusProgradeToRetrograde": 4702524927750286211, + "LastUranusRetrogradeToPrograde": 4702525258035592462, + "LastUranusWesternQuadrature": 4702524897441072246, + "NextUranusConjunction": 4702526276601511849, + "NextUranusEasternQuadrature": 4702526077833344551, + "NextUranusOpposition": 4702525887987449575, + "NextUranusProgradeToRetrograde": 4702525720994139735, + "NextUranusRetrogradeToPrograde": 4702526051000597259, + "NextUranusWesternQuadrature": 4702525690691498122 + } + }, + { + "input_utc": "2008-09-25T17:59:10.444Z", + "tt_jd_bits": 4702525915157359956, + "events": { + "LastUranusConjunction": 4702525483719950761, + "LastUranusEasternQuadrature": 4702525285100838117, + "LastUranusOpposition": 4702525887987449575, + "LastUranusProgradeToRetrograde": 4702525720994139735, + "LastUranusRetrogradeToPrograde": 4702525258035592462, + "LastUranusWesternQuadrature": 4702525690691498122, + "NextUranusConjunction": 4702526276601511849, + "NextUranusEasternQuadrature": 4702526077833344551, + "NextUranusOpposition": 4702526681064911535, + "NextUranusProgradeToRetrograde": 4702526514089112545, + "NextUranusRetrogradeToPrograde": 4702526051000597259, + "NextUranusWesternQuadrature": 4702526483966388917 + } + }, + { + "input_utc": "2009-03-07T23:16:41.444Z", + "tt_jd_bits": 4702526265670734608, + "events": { + "LastUranusConjunction": 4702525483719950761, + "LastUranusEasternQuadrature": 4702526077833344551, + "LastUranusOpposition": 4702525887987449575, + "LastUranusProgradeToRetrograde": 4702525720994139735, + "LastUranusRetrogradeToPrograde": 4702526051000597259, + "LastUranusWesternQuadrature": 4702525690691498122, + "NextUranusConjunction": 4702526276601511849, + "NextUranusEasternQuadrature": 4702526870558749696, + "NextUranusOpposition": 4702526681064911535, + "NextUranusProgradeToRetrograde": 4702526514089112545, + "NextUranusRetrogradeToPrograde": 4702526843816138715, + "NextUranusWesternQuadrature": 4702526483966388917 + } + }, + { + "input_utc": "2009-08-18T04:34:12.444Z", + "tt_jd_bits": 4702526616184084406, + "events": { + "LastUranusConjunction": 4702526276601511849, + "LastUranusEasternQuadrature": 4702526077833344551, + "LastUranusOpposition": 4702525887987449575, + "LastUranusProgradeToRetrograde": 4702526514089112545, + "LastUranusRetrogradeToPrograde": 4702526051000597259, + "LastUranusWesternQuadrature": 4702526483966388917, + "NextUranusConjunction": 4702527069503259834, + "NextUranusEasternQuadrature": 4702526870558749696, + "NextUranusOpposition": 4702526681064911535, + "NextUranusProgradeToRetrograde": 4702527307309226397, + "NextUranusRetrogradeToPrograde": 4702526843816138715, + "NextUranusWesternQuadrature": 4702527277238975987 + } + }, + { + "input_utc": "2010-01-28T06:27:43.444Z", + "tt_jd_bits": 4702526966393207353, + "events": { + "LastUranusConjunction": 4702526276601511849, + "LastUranusEasternQuadrature": 4702526870558749696, + "LastUranusOpposition": 4702526681064911535, + "LastUranusProgradeToRetrograde": 4702526514089112545, + "LastUranusRetrogradeToPrograde": 4702526843816138715, + "LastUranusWesternQuadrature": 4702526483966388917, + "NextUranusConjunction": 4702527069503259834, + "NextUranusEasternQuadrature": 4702527663280100351, + "NextUranusOpposition": 4702527474137866588, + "NextUranusProgradeToRetrograde": 4702527307309226397, + "NextUranusRetrogradeToPrograde": 4702527636725903637, + "NextUranusWesternQuadrature": 4702527277238975987 + } + }, + { + "input_utc": "2010-07-10T11:45:14.444Z", + "tt_jd_bits": 4702527316906557150, + "events": { + "LastUranusConjunction": 4702527069503259834, + "LastUranusEasternQuadrature": 4702526870558749696, + "LastUranusOpposition": 4702526681064911535, + "LastUranusProgradeToRetrograde": 4702527307309226397, + "LastUranusRetrogradeToPrograde": 4702526843816138715, + "LastUranusWesternQuadrature": 4702527277238975987, + "NextUranusConjunction": 4702527862423691121, + "NextUranusEasternQuadrature": 4702527663280100351, + "NextUranusOpposition": 4702527474137866588, + "NextUranusProgradeToRetrograde": 4702528100404291856, + "NextUranusRetrogradeToPrograde": 4702527636725903637, + "NextUranusWesternQuadrature": 4702528070534670244 + } + }, + { + "input_utc": "2010-12-20T17:02:45.444Z", + "tt_jd_bits": 4702527667419906948, + "events": { + "LastUranusConjunction": 4702527069503259834, + "LastUranusEasternQuadrature": 4702527663280100351, + "LastUranusOpposition": 4702527474137866588, + "LastUranusProgradeToRetrograde": 4702527307309226397, + "LastUranusRetrogradeToPrograde": 4702527636725903637, + "LastUranusWesternQuadrature": 4702527277238975987, + "NextUranusConjunction": 4702527862423691121, + "NextUranusEasternQuadrature": 4702528456008718924, + "NextUranusOpposition": 4702528267210529259, + "NextUranusProgradeToRetrograde": 4702528100404291856, + "NextUranusRetrogradeToPrograde": 4702528429612614754, + "NextUranusWesternQuadrature": 4702528070534670244 + } + }, + { + "input_utc": "2011-06-01T22:13:33.444Z", + "tt_jd_bits": 4702528017923240126, + "events": { + "LastUranusConjunction": 4702527862423691121, + "LastUranusEasternQuadrature": 4702527663280100351, + "LastUranusOpposition": 4702527474137866588, + "LastUranusProgradeToRetrograde": 4702527307309226397, + "LastUranusRetrogradeToPrograde": 4702527636725903637, + "LastUranusWesternQuadrature": 4702527277238975987, + "NextUranusConjunction": 4702528655376073975, + "NextUranusEasternQuadrature": 4702528456008718924, + "NextUranusOpposition": 4702528267210529259, + "NextUranusProgradeToRetrograde": 4702528100404291856, + "NextUranusRetrogradeToPrograde": 4702528429612614754, + "NextUranusWesternQuadrature": 4702528070534670244 + } + }, + { + "input_utc": "2011-11-10T11:31:04.444Z", + "tt_jd_bits": 4702528364857450510, + "events": { + "LastUranusConjunction": 4702527862423691121, + "LastUranusEasternQuadrature": 4702527663280100351, + "LastUranusOpposition": 4702528267210529259, + "LastUranusProgradeToRetrograde": 4702528100404291856, + "LastUranusRetrogradeToPrograde": 4702527636725903637, + "LastUranusWesternQuadrature": 4702528070534670244, + "NextUranusConjunction": 4702528655376073975, + "NextUranusEasternQuadrature": 4702528456008718924, + "NextUranusOpposition": 4702529060258334373, + "NextUranusProgradeToRetrograde": 4702528893611508415, + "NextUranusRetrogradeToPrograde": 4702528429612614754, + "NextUranusWesternQuadrature": 4702528863845796288 + } + }, + { + "input_utc": "2012-04-21T16:48:35.444Z", + "tt_jd_bits": 4702528715370800307, + "events": { + "LastUranusConjunction": 4702528655376073975, + "LastUranusEasternQuadrature": 4702528456008718924, + "LastUranusOpposition": 4702528267210529259, + "LastUranusProgradeToRetrograde": 4702528100404291856, + "LastUranusRetrogradeToPrograde": 4702528429612614754, + "LastUranusWesternQuadrature": 4702528070534670244, + "NextUranusConjunction": 4702529448360653771, + "NextUranusEasternQuadrature": 4702529248724111355, + "NextUranusOpposition": 4702529060258334373, + "NextUranusProgradeToRetrograde": 4702528893611508415, + "NextUranusRetrogradeToPrograde": 4702529222468708699, + "NextUranusWesternQuadrature": 4702528863845796288 + } + }, + { + "input_utc": "2012-10-01T22:06:06.444Z", + "tt_jd_bits": 4702529065884174959, + "events": { + "LastUranusConjunction": 4702528655376073975, + "LastUranusEasternQuadrature": 4702528456008718924, + "LastUranusOpposition": 4702529060258334373, + "LastUranusProgradeToRetrograde": 4702528893611508415, + "LastUranusRetrogradeToPrograde": 4702528429612614754, + "LastUranusWesternQuadrature": 4702528863845796288, + "NextUranusConjunction": 4702529448360653771, + "NextUranusEasternQuadrature": 4702529248724111355, + "NextUranusOpposition": 4702529853301602080, + "NextUranusProgradeToRetrograde": 4702529686670872585, + "NextUranusRetrogradeToPrograde": 4702529222468708699, + "NextUranusWesternQuadrature": 4702529657180522929 + } + }, + { + "input_utc": "2013-03-14T03:23:37.444Z", + "tt_jd_bits": 4702529416397524757, + "events": { + "LastUranusConjunction": 4702528655376073975, + "LastUranusEasternQuadrature": 4702529248724111355, + "LastUranusOpposition": 4702529060258334373, + "LastUranusProgradeToRetrograde": 4702528893611508415, + "LastUranusRetrogradeToPrograde": 4702529222468708699, + "LastUranusWesternQuadrature": 4702528863845796288, + "NextUranusConjunction": 4702529448360653771, + "NextUranusEasternQuadrature": 4702530041464612877, + "NextUranusOpposition": 4702529853301602080, + "NextUranusProgradeToRetrograde": 4702529686670872585, + "NextUranusRetrogradeToPrograde": 4702530015380220086, + "NextUranusWesternQuadrature": 4702529657180522929 + } + }, + { + "input_utc": "2013-08-24T08:41:08.444Z", + "tt_jd_bits": 4702529766910874554, + "events": { + "LastUranusConjunction": 4702529448360653771, + "LastUranusEasternQuadrature": 4702529248724111355, + "LastUranusOpposition": 4702529060258334373, + "LastUranusProgradeToRetrograde": 4702529686670872585, + "LastUranusRetrogradeToPrograde": 4702529222468708699, + "LastUranusWesternQuadrature": 4702529657180522929, + "NextUranusConjunction": 4702530241364888538, + "NextUranusEasternQuadrature": 4702530041464612877, + "NextUranusOpposition": 4702529853301602080, + "NextUranusProgradeToRetrograde": 4702530479893828936, + "NextUranusRetrogradeToPrograde": 4702530015380220086, + "NextUranusWesternQuadrature": 4702530450497844286 + } + }, + { + "input_utc": "2014-02-03T13:58:39.444Z", + "tt_jd_bits": 4702530117424224351, + "events": { + "LastUranusConjunction": 4702529448360653771, + "LastUranusEasternQuadrature": 4702530041464612877, + "LastUranusOpposition": 4702529853301602080, + "LastUranusProgradeToRetrograde": 4702529686670872585, + "LastUranusRetrogradeToPrograde": 4702530015380220086, + "LastUranusWesternQuadrature": 4702529657180522929, + "NextUranusConjunction": 4702530241364888538, + "NextUranusEasternQuadrature": 4702530834206943163, + "NextUranusOpposition": 4702530646328225616, + "NextUranusProgradeToRetrograde": 4702530479893828936, + "NextUranusRetrogradeToPrograde": 4702530808225899488, + "NextUranusWesternQuadrature": 4702530450497844286 + } + }, + { + "input_utc": "2014-07-16T19:16:10.444Z", + "tt_jd_bits": 4702530467937574149, + "events": { + "LastUranusConjunction": 4702530241364888538, + "LastUranusEasternQuadrature": 4702530041464612877, + "LastUranusOpposition": 4702529853301602080, + "LastUranusProgradeToRetrograde": 4702529686670872585, + "LastUranusRetrogradeToPrograde": 4702530015380220086, + "LastUranusWesternQuadrature": 4702530450497844286, + "NextUranusConjunction": 4702531034411469999, + "NextUranusEasternQuadrature": 4702530834206943163, + "NextUranusOpposition": 4702530646328225616, + "NextUranusProgradeToRetrograde": 4702530479893828936, + "NextUranusRetrogradeToPrograde": 4702530808225899488, + "NextUranusWesternQuadrature": 4702531243858264878 + } + }, + { + "input_utc": "2014-12-27T00:33:41.444Z", + "tt_jd_bits": 4702530818450923946, + "events": { + "LastUranusConjunction": 4702530241364888538, + "LastUranusEasternQuadrature": 4702530041464612877, + "LastUranusOpposition": 4702530646328225616, + "LastUranusProgradeToRetrograde": 4702530479893828936, + "LastUranusRetrogradeToPrograde": 4702530808225899488, + "LastUranusWesternQuadrature": 4702530450497844286, + "NextUranusConjunction": 4702531034411469999, + "NextUranusEasternQuadrature": 4702530834206943163, + "NextUranusOpposition": 4702531439363828252, + "NextUranusProgradeToRetrograde": 4702531272965735516, + "NextUranusRetrogradeToPrograde": 4702531601089681573, + "NextUranusWesternQuadrature": 4702531243858264878 + } + } + ] +} diff --git a/basic/testdata/semianalytic/venus_event_baseline.json b/basic/testdata/semianalytic/venus_event_baseline.json new file mode 100644 index 0000000..238896a --- /dev/null +++ b/basic/testdata/semianalytic/venus_event_baseline.json @@ -0,0 +1,2308 @@ +{ + "samples": [ + { + "input_utc": "1994-02-03T04:56:12.345Z", + "tt_jd_bits": 4702514429247039192, + "events": { + "LastVenusConjunction": 4702514392481032636, + "LastVenusGreatestElongation": 4702513918856277452, + "LastVenusGreatestElongationEast": 4702513614199357220, + "LastVenusGreatestElongationWest": 4702513918856277452, + "LastVenusInferiorConjunction": 4702513768559154323, + "LastVenusProgradeToRetrograde": 4702513719865138858, + "LastVenusRetrograde": 4702513808332618238, + "LastVenusRetrogradeToPrograde": 4702513808332618238, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515014994844544, + "NextVenusGreatestElongation": 4702514864641653804, + "NextVenusGreatestElongationEast": 4702514864641653804, + "NextVenusGreatestElongationWest": 4702515168605106448, + "NextVenusInferiorConjunction": 4702515014994844544, + "NextVenusProgradeToRetrograde": 4702514969849063197, + "NextVenusRetrograde": 4702514969849063197, + "NextVenusRetrogradeToPrograde": 4702515055133824128, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1994-07-26T10:07:25.345Z", + "tt_jd_bits": 4702514801225855084, + "events": { + "LastVenusConjunction": 4702514392481032636, + "LastVenusGreatestElongation": 4702513918856277452, + "LastVenusGreatestElongationEast": 4702513614199357220, + "LastVenusGreatestElongationWest": 4702513918856277452, + "LastVenusInferiorConjunction": 4702513768559154323, + "LastVenusProgradeToRetrograde": 4702513719865138858, + "LastVenusRetrograde": 4702513808332618238, + "LastVenusRetrogradeToPrograde": 4702513808332618238, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515014994844544, + "NextVenusGreatestElongation": 4702514864641653804, + "NextVenusGreatestElongationEast": 4702514864641653804, + "NextVenusGreatestElongationWest": 4702515168605106448, + "NextVenusInferiorConjunction": 4702515014994844544, + "NextVenusProgradeToRetrograde": 4702514969849063197, + "NextVenusRetrograde": 4702514969849063197, + "NextVenusRetrogradeToPrograde": 4702515055133824128, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-01-15T15:18:38.345Z", + "tt_jd_bits": 4702515173204646121, + "events": { + "LastVenusConjunction": 4702515014994844544, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515639990446509, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-07-07T20:29:51.345Z", + "tt_jd_bits": 4702515545183437157, + "events": { + "LastVenusConjunction": 4702515014994844544, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515639990446509, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-12-28T01:41:04.345Z", + "tt_jd_bits": 4702515917162228194, + "events": { + "LastVenusConjunction": 4702515639990446509, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516272804148034, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1996-06-18T06:52:17.345Z", + "tt_jd_bits": 4702516289141044085, + "events": { + "LastVenusConjunction": 4702516272804148034, + "LastVenusGreatestElongation": 4702516121090930520, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516226878400921, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516908230248195, + "NextVenusGreatestElongation": 4702516424127004108, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702516318547117858, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1996-12-08T12:03:30.345Z", + "tt_jd_bits": 4702516661119835121, + "events": { + "LastVenusConjunction": 4702516272804148034, + "LastVenusGreatestElongation": 4702516424127004108, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516908230248195, + "NextVenusGreatestElongation": 4702517375745288074, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1997-05-30T15:57:43.345Z", + "tt_jd_bits": 4702517032983795435, + "events": { + "LastVenusConjunction": 4702516908230248195, + "LastVenusGreatestElongation": 4702516424127004108, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702517528633992505, + "NextVenusGreatestElongation": 4702517375745288074, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1997-11-19T21:08:56.345Z", + "tt_jd_bits": 4702517404962611326, + "events": { + "LastVenusConjunction": 4702516908230248195, + "LastVenusGreatestElongation": 4702517375745288074, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702517528633992505, + "NextVenusGreatestElongation": 4702517679616047386, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1998-05-10T05:20:09.345Z", + "tt_jd_bits": 4702517772914870523, + "events": { + "LastVenusConjunction": 4702517528633992505, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702518144341918960, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1998-10-30T10:31:22.345Z", + "tt_jd_bits": 4702518144893661559, + "events": { + "LastVenusConjunction": 4702518144341918960, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702518776380939323, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "1999-04-21T15:42:35.345Z", + "tt_jd_bits": 4702518516872477450, + "events": { + "LastVenusConjunction": 4702518144341918960, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702518776380939323, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "1999-10-11T20:53:48.345Z", + "tt_jd_bits": 4702518888851268487, + "events": { + "LastVenusConjunction": 4702518776380939323, + "LastVenusGreatestElongation": 4702518626051128940, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702519411906295324, + "NextVenusGreatestElongation": 4702518929890649762, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "2000-04-02T02:02:12.345Z", + "tt_jd_bits": 4702519260825859005, + "events": { + "LastVenusConjunction": 4702518776380939323, + "LastVenusGreatestElongation": 4702518929890649762, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702519411906295324, + "NextVenusGreatestElongation": 4702519883963900672, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "2000-09-22T05:56:25.345Z", + "tt_jd_bits": 4702519632689819319, + "events": { + "LastVenusConjunction": 4702519411906295324, + "LastVenusGreatestElongation": 4702518929890649762, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520038413653359, + "NextVenusGreatestElongation": 4702519883963900672, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2001-03-14T11:07:38.345Z", + "tt_jd_bits": 4702520004668610355, + "events": { + "LastVenusConjunction": 4702519411906295324, + "LastVenusGreatestElongation": 4702519883963900672, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702519989583753004, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520038413653359, + "NextVenusGreatestElongation": 4702520188775143046, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702520078167311162, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2001-09-03T16:18:51.345Z", + "tt_jd_bits": 4702520376647401392, + "events": { + "LastVenusConjunction": 4702520038413653359, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520661833733673, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2002-02-23T21:30:04.345Z", + "tt_jd_bits": 4702520748626192428, + "events": { + "LastVenusConjunction": 4702520661833733673, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521284654012721, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2002-08-14T05:41:17.345Z", + "tt_jd_bits": 4702521116578451625, + "events": { + "LastVenusConjunction": 4702520661833733673, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521284654012721, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2003-02-03T10:52:30.345Z", + "tt_jd_bits": 4702521488557242661, + "events": { + "LastVenusConjunction": 4702521284654012721, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521910106751845, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2003-07-26T16:03:43.345Z", + "tt_jd_bits": 4702521860536033697, + "events": { + "LastVenusConjunction": 4702521284654012721, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521910106751845, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2004-01-15T19:57:56.345Z", + "tt_jd_bits": 4702522232399994011, + "events": { + "LastVenusConjunction": 4702521910106751845, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702522542777144799, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2004-07-07T01:09:09.345Z", + "tt_jd_bits": 4702522604378785047, + "events": { + "LastVenusConjunction": 4702522542777144799, + "LastVenusGreatestElongation": 4702522391017602520, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702523177964903487, + "NextVenusGreatestElongation": 4702522693977973000, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2004-12-27T06:20:22.345Z", + "tt_jd_bits": 4702522976357576084, + "events": { + "LastVenusConjunction": 4702522542777144799, + "LastVenusGreatestElongation": 4702522693977973000, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702523177964903487, + "NextVenusGreatestElongation": 4702523645405413500, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2005-06-18T11:31:35.345Z", + "tt_jd_bits": 4702523348336367120, + "events": { + "LastVenusConjunction": 4702523177964903487, + "LastVenusGreatestElongation": 4702522693977973000, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702523798272971317, + "NextVenusGreatestElongation": 4702523645405413500, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2005-12-08T16:42:48.345Z", + "tt_jd_bits": 4702523720315158157, + "events": { + "LastVenusConjunction": 4702523177964903487, + "LastVenusGreatestElongation": 4702523645405413500, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702523798272971317, + "NextVenusGreatestElongation": 4702523949202163128, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2006-05-30T21:51:12.345Z", + "tt_jd_bits": 4702524092289773530, + "events": { + "LastVenusConjunction": 4702523798272971317, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702524414051142647, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2006-11-18T06:02:25.345Z", + "tt_jd_bits": 4702524460242032727, + "events": { + "LastVenusConjunction": 4702524414051142647, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525046292053779, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2007-05-10T09:56:38.345Z", + "tt_jd_bits": 4702524832105993040, + "events": { + "LastVenusConjunction": 4702524414051142647, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525046292053779, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2007-10-30T15:07:51.345Z", + "tt_jd_bits": 4702525204084784077, + "events": { + "LastVenusConjunction": 4702525046292053779, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525682003400406, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2008-04-20T20:19:04.345Z", + "tt_jd_bits": 4702525576063575113, + "events": { + "LastVenusConjunction": 4702525046292053779, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525682003400406, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2008-10-11T01:30:17.345Z", + "tt_jd_bits": 4702525948042366149, + "events": { + "LastVenusConjunction": 4702525682003400406, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526308271303356, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2009-04-02T06:41:30.345Z", + "tt_jd_bits": 4702526320021182041, + "events": { + "LastVenusConjunction": 4702526308271303356, + "LastVenusGreatestElongation": 4702526153832455514, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526259348625163, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526931193901496, + "NextVenusGreatestElongation": 4702526458723921452, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702526348061495207, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2009-09-22T11:52:43.345Z", + "tt_jd_bits": 4702526691999973077, + "events": { + "LastVenusConjunction": 4702526308271303356, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526931193901496, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2010-03-14T17:03:56.345Z", + "tt_jd_bits": 4702527063978764114, + "events": { + "LastVenusConjunction": 4702526931193901496, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702527554328569648, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + }, + { + "input_utc": "2010-09-03T20:58:09.345Z", + "tt_jd_bits": 4702527435842724427, + "events": { + "LastVenusConjunction": 4702526931193901496, + "LastVenusGreatestElongation": 4702527404242007826, + "LastVenusGreatestElongationEast": 4702527404242007826, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702527554328569648, + "NextVenusGreatestElongation": 4702527708128036028, + "NextVenusGreatestElongationEast": 4702528660869828396, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + }, + { + "input_utc": "2011-02-22T05:09:22.345Z", + "tt_jd_bits": 4702527803794983623, + "events": { + "LastVenusConjunction": 4702527554328569648, + "LastVenusGreatestElongation": 4702527708128036028, + "LastVenusGreatestElongationEast": 4702527404242007826, + "LastVenusGreatestElongationWest": 4702527708128036028, + "LastVenusInferiorConjunction": 4702527554328569648, + "LastVenusProgradeToRetrograde": 4702527508682115377, + "LastVenusRetrograde": 4702527594279007280, + "LastVenusRetrogradeToPrograde": 4702527594279007280, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702528180226942902, + "NextVenusGreatestElongation": 4702528660869828396, + "NextVenusGreatestElongationEast": 4702528660869828396, + "NextVenusGreatestElongationWest": 4702528963788305374, + "NextVenusInferiorConjunction": 4702528812752153963, + "NextVenusProgradeToRetrograde": 4702528766958386310, + "NextVenusRetrograde": 4702528766958386310, + "NextVenusRetrogradeToPrograde": 4702528858135417331, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + }, + { + "input_utc": "2011-08-14T10:20:35.345Z", + "tt_jd_bits": 4702528175773774660, + "events": { + "LastVenusConjunction": 4702527554328569648, + "LastVenusGreatestElongation": 4702527708128036028, + "LastVenusGreatestElongationEast": 4702527404242007826, + "LastVenusGreatestElongationWest": 4702527708128036028, + "LastVenusInferiorConjunction": 4702527554328569648, + "LastVenusProgradeToRetrograde": 4702527508682115377, + "LastVenusRetrograde": 4702527594279007280, + "LastVenusRetrogradeToPrograde": 4702527594279007280, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702528180226942902, + "NextVenusGreatestElongation": 4702528660869828396, + "NextVenusGreatestElongationEast": 4702528660869828396, + "NextVenusGreatestElongationWest": 4702528963788305374, + "NextVenusInferiorConjunction": 4702528812752153963, + "NextVenusProgradeToRetrograde": 4702528766958386310, + "NextVenusRetrograde": 4702528766958386310, + "NextVenusRetrogradeToPrograde": 4702528858135417331, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + }, + { + "input_utc": "2012-02-03T15:31:48.345Z", + "tt_jd_bits": 4702528547752565696, + "events": { + "LastVenusConjunction": 4702528180226942902, + "LastVenusGreatestElongation": 4702527708128036028, + "LastVenusGreatestElongationEast": 4702527404242007826, + "LastVenusGreatestElongationWest": 4702527708128036028, + "LastVenusInferiorConjunction": 4702527554328569648, + "LastVenusProgradeToRetrograde": 4702527508682115377, + "LastVenusRetrograde": 4702527594279007280, + "LastVenusRetrogradeToPrograde": 4702527594279007280, + "LastVenusSuperiorConjunction": 4702528180226942902, + "NextVenusConjunction": 4702528812752153963, + "NextVenusGreatestElongation": 4702528660869828396, + "NextVenusGreatestElongationEast": 4702528660869828396, + "NextVenusGreatestElongationWest": 4702528963788305374, + "NextVenusInferiorConjunction": 4702528812752153963, + "NextVenusProgradeToRetrograde": 4702528766958386310, + "NextVenusRetrograde": 4702528766958386310, + "NextVenusRetrogradeToPrograde": 4702528858135417331, + "NextVenusSuperiorConjunction": 4702529447685429333 + } + }, + { + "input_utc": "2012-07-25T20:40:12.345Z", + "tt_jd_bits": 4702528919727181070, + "events": { + "LastVenusConjunction": 4702528812752153963, + "LastVenusGreatestElongation": 4702528660869828396, + "LastVenusGreatestElongationEast": 4702528660869828396, + "LastVenusGreatestElongationWest": 4702527708128036028, + "LastVenusInferiorConjunction": 4702528812752153963, + "LastVenusProgradeToRetrograde": 4702528766958386310, + "LastVenusRetrograde": 4702528858135417331, + "LastVenusRetrogradeToPrograde": 4702528858135417331, + "LastVenusSuperiorConjunction": 4702528180226942902, + "NextVenusConjunction": 4702529447685429333, + "NextVenusGreatestElongation": 4702528963788305374, + "NextVenusGreatestElongationEast": 4702529915022141490, + "NextVenusGreatestElongationWest": 4702528963788305374, + "NextVenusInferiorConjunction": 4702530067889910743, + "NextVenusProgradeToRetrograde": 4702530021291330338, + "NextVenusRetrograde": 4702530021291330338, + "NextVenusRetrogradeToPrograde": 4702530111451036794, + "NextVenusSuperiorConjunction": 4702529447685429333 + } + }, + { + "input_utc": "2013-01-15T01:51:25.345Z", + "tt_jd_bits": 4702529291705972106, + "events": { + "LastVenusConjunction": 4702528812752153963, + "LastVenusGreatestElongation": 4702528963788305374, + "LastVenusGreatestElongationEast": 4702528660869828396, + "LastVenusGreatestElongationWest": 4702528963788305374, + "LastVenusInferiorConjunction": 4702528812752153963, + "LastVenusProgradeToRetrograde": 4702528766958386310, + "LastVenusRetrograde": 4702528858135417331, + "LastVenusRetrogradeToPrograde": 4702528858135417331, + "LastVenusSuperiorConjunction": 4702528180226942902, + "NextVenusConjunction": 4702529447685429333, + "NextVenusGreatestElongation": 4702529915022141490, + "NextVenusGreatestElongationEast": 4702529915022141490, + "NextVenusGreatestElongationWest": 4702530218849021714, + "NextVenusInferiorConjunction": 4702530067889910743, + "NextVenusProgradeToRetrograde": 4702530021291330338, + "NextVenusRetrograde": 4702530021291330338, + "NextVenusRetrogradeToPrograde": 4702530111451036794, + "NextVenusSuperiorConjunction": 4702529447685429333 + } + }, + { + "input_utc": "2013-07-07T07:02:38.345Z", + "tt_jd_bits": 4702529663684763143, + "events": { + "LastVenusConjunction": 4702529447685429333, + "LastVenusGreatestElongation": 4702528963788305374, + "LastVenusGreatestElongationEast": 4702528660869828396, + "LastVenusGreatestElongationWest": 4702528963788305374, + "LastVenusInferiorConjunction": 4702528812752153963, + "LastVenusProgradeToRetrograde": 4702528766958386310, + "LastVenusRetrograde": 4702528858135417331, + "LastVenusRetrogradeToPrograde": 4702528858135417331, + "LastVenusSuperiorConjunction": 4702529447685429333, + "NextVenusConjunction": 4702530067889910743, + "NextVenusGreatestElongation": 4702529915022141490, + "NextVenusGreatestElongationEast": 4702529915022141490, + "NextVenusGreatestElongationWest": 4702530218849021714, + "NextVenusInferiorConjunction": 4702530067889910743, + "NextVenusProgradeToRetrograde": 4702530021291330338, + "NextVenusRetrograde": 4702530021291330338, + "NextVenusRetrogradeToPrograde": 4702530111451036794, + "NextVenusSuperiorConjunction": 4702530683779814224 + } + }, + { + "input_utc": "2013-12-27T10:56:51.345Z", + "tt_jd_bits": 4702530035548723456, + "events": { + "LastVenusConjunction": 4702529447685429333, + "LastVenusGreatestElongation": 4702529915022141490, + "LastVenusGreatestElongationEast": 4702529915022141490, + "LastVenusGreatestElongationWest": 4702528963788305374, + "LastVenusInferiorConjunction": 4702528812752153963, + "LastVenusProgradeToRetrograde": 4702530021291330338, + "LastVenusRetrograde": 4702530021291330338, + "LastVenusRetrogradeToPrograde": 4702528858135417331, + "LastVenusSuperiorConjunction": 4702529447685429333, + "NextVenusConjunction": 4702530067889910743, + "NextVenusGreatestElongation": 4702530218849021714, + "NextVenusGreatestElongationEast": 4702531165797403472, + "NextVenusGreatestElongationWest": 4702530218849021714, + "NextVenusInferiorConjunction": 4702530067889910743, + "NextVenusProgradeToRetrograde": 4702531265647600827, + "NextVenusRetrograde": 4702530111451036794, + "NextVenusRetrogradeToPrograde": 4702530111451036794, + "NextVenusSuperiorConjunction": 4702530683779814224 + } + }, + { + "input_utc": "2014-06-18T16:08:04.345Z", + "tt_jd_bits": 4702530407527514493, + "events": { + "LastVenusConjunction": 4702530067889910743, + "LastVenusGreatestElongation": 4702530218849021714, + "LastVenusGreatestElongationEast": 4702529915022141490, + "LastVenusGreatestElongationWest": 4702530218849021714, + "LastVenusInferiorConjunction": 4702530067889910743, + "LastVenusProgradeToRetrograde": 4702530021291330338, + "LastVenusRetrograde": 4702530111451036794, + "LastVenusRetrogradeToPrograde": 4702530111451036794, + "LastVenusSuperiorConjunction": 4702529447685429333, + "NextVenusConjunction": 4702530683779814224, + "NextVenusGreatestElongation": 4702531165797403472, + "NextVenusGreatestElongationEast": 4702531165797403472, + "NextVenusGreatestElongationWest": 4702531469730776558, + "NextVenusInferiorConjunction": 4702531316200089912, + "NextVenusProgradeToRetrograde": 4702531265647600827, + "NextVenusRetrograde": 4702531265647600827, + "NextVenusRetrogradeToPrograde": 4702531360334332031, + "NextVenusSuperiorConjunction": 4702530683779814224 + } + }, + { + "input_utc": "2014-12-08T21:19:17.345Z", + "tt_jd_bits": 4702530779506305529, + "events": { + "LastVenusConjunction": 4702530683779814224, + "LastVenusGreatestElongation": 4702530218849021714, + "LastVenusGreatestElongationEast": 4702529915022141490, + "LastVenusGreatestElongationWest": 4702530218849021714, + "LastVenusInferiorConjunction": 4702530067889910743, + "LastVenusProgradeToRetrograde": 4702530021291330338, + "LastVenusRetrograde": 4702530111451036794, + "LastVenusRetrogradeToPrograde": 4702530111451036794, + "LastVenusSuperiorConjunction": 4702530683779814224, + "NextVenusConjunction": 4702531316200089912, + "NextVenusGreatestElongation": 4702531165797403472, + "NextVenusGreatestElongationEast": 4702531165797403472, + "NextVenusGreatestElongationWest": 4702531469730776558, + "NextVenusInferiorConjunction": 4702531316200089912, + "NextVenusProgradeToRetrograde": 4702531265647600827, + "NextVenusRetrograde": 4702531265647600827, + "NextVenusRetrogradeToPrograde": 4702531360334332031, + "NextVenusSuperiorConjunction": 4702531952075682121 + } + }, + { + "input_utc": "2015-05-29T05:30:30.345Z", + "tt_jd_bits": 4702531147458564725, + "events": { + "LastVenusConjunction": 4702530683779814224, + "LastVenusGreatestElongation": 4702530218849021714, + "LastVenusGreatestElongationEast": 4702529915022141490, + "LastVenusGreatestElongationWest": 4702530218849021714, + "LastVenusInferiorConjunction": 4702530067889910743, + "LastVenusProgradeToRetrograde": 4702530021291330338, + "LastVenusRetrograde": 4702530111451036794, + "LastVenusRetrogradeToPrograde": 4702530111451036794, + "LastVenusSuperiorConjunction": 4702530683779814224, + "NextVenusConjunction": 4702531316200089912, + "NextVenusGreatestElongation": 4702531165797403472, + "NextVenusGreatestElongationEast": 4702531165797403472, + "NextVenusGreatestElongationWest": 4702531469730776558, + "NextVenusInferiorConjunction": 4702531316200089912, + "NextVenusProgradeToRetrograde": 4702531265647600827, + "NextVenusRetrograde": 4702531265647600827, + "NextVenusRetrogradeToPrograde": 4702531360334332031, + "NextVenusSuperiorConjunction": 4702531952075682121 + } + }, + { + "input_utc": "2015-11-18T10:41:43.345Z", + "tt_jd_bits": 4702531519437380618, + "events": { + "LastVenusConjunction": 4702531316200089912, + "LastVenusGreatestElongation": 4702531469730776558, + "LastVenusGreatestElongationEast": 4702531165797403472, + "LastVenusGreatestElongationWest": 4702531469730776558, + "LastVenusInferiorConjunction": 4702531316200089912, + "LastVenusProgradeToRetrograde": 4702531265647600827, + "LastVenusRetrograde": 4702531360334332031, + "LastVenusRetrogradeToPrograde": 4702531360334332031, + "LastVenusSuperiorConjunction": 4702530683779814224, + "NextVenusConjunction": 4702531952075682121, + "NextVenusGreatestElongation": 4702532423760435438, + "NextVenusGreatestElongationEast": 4702532423760435438, + "NextVenusGreatestElongationWest": 4702532728628603494, + "NextVenusInferiorConjunction": 4702532578108468622, + "NextVenusProgradeToRetrograde": 4702532529073542458, + "NextVenusRetrograde": 4702532529073542458, + "NextVenusRetrogradeToPrograde": 4702532617958468471, + "NextVenusSuperiorConjunction": 4702531952075682121 + } + }, + { + "input_utc": "2016-05-09T15:52:56.345Z", + "tt_jd_bits": 4702531891416171654, + "events": { + "LastVenusConjunction": 4702531316200089912, + "LastVenusGreatestElongation": 4702531469730776558, + "LastVenusGreatestElongationEast": 4702531165797403472, + "LastVenusGreatestElongationWest": 4702531469730776558, + "LastVenusInferiorConjunction": 4702531316200089912, + "LastVenusProgradeToRetrograde": 4702531265647600827, + "LastVenusRetrograde": 4702531360334332031, + "LastVenusRetrogradeToPrograde": 4702531360334332031, + "LastVenusSuperiorConjunction": 4702530683779814224, + "NextVenusConjunction": 4702531952075682121, + "NextVenusGreatestElongation": 4702532423760435438, + "NextVenusGreatestElongationEast": 4702532423760435438, + "NextVenusGreatestElongationWest": 4702532728628603494, + "NextVenusInferiorConjunction": 4702532578108468622, + "NextVenusProgradeToRetrograde": 4702532529073542458, + "NextVenusRetrograde": 4702532529073542458, + "NextVenusRetrogradeToPrograde": 4702532617958468471, + "NextVenusSuperiorConjunction": 4702531952075682121 + } + }, + { + "input_utc": "1994-03-04T04:56:12.345Z", + "tt_jd_bits": 4702514491524064984, + "events": { + "LastVenusConjunction": 4702514392481032636, + "LastVenusGreatestElongation": 4702513918856277452, + "LastVenusGreatestElongationEast": 4702513614199357220, + "LastVenusGreatestElongationWest": 4702513918856277452, + "LastVenusInferiorConjunction": 4702513768559154323, + "LastVenusProgradeToRetrograde": 4702513719865138858, + "LastVenusRetrograde": 4702513808332618238, + "LastVenusRetrogradeToPrograde": 4702513808332618238, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515014994844544, + "NextVenusGreatestElongation": 4702514864641653804, + "NextVenusGreatestElongationEast": 4702514864641653804, + "NextVenusGreatestElongationWest": 4702515168605106448, + "NextVenusInferiorConjunction": 4702515014994844544, + "NextVenusProgradeToRetrograde": 4702514969849063197, + "NextVenusRetrograde": 4702514969849063197, + "NextVenusRetrogradeToPrograde": 4702515055133824128, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1994-07-09T11:09:29.345Z", + "tt_jd_bits": 4702514764811193590, + "events": { + "LastVenusConjunction": 4702514392481032636, + "LastVenusGreatestElongation": 4702513918856277452, + "LastVenusGreatestElongationEast": 4702513614199357220, + "LastVenusGreatestElongationWest": 4702513918856277452, + "LastVenusInferiorConjunction": 4702513768559154323, + "LastVenusProgradeToRetrograde": 4702513719865138858, + "LastVenusRetrograde": 4702513808332618238, + "LastVenusRetrogradeToPrograde": 4702513808332618238, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515014994844544, + "NextVenusGreatestElongation": 4702514864641653804, + "NextVenusGreatestElongationEast": 4702514864641653804, + "NextVenusGreatestElongationWest": 4702515168605106448, + "NextVenusInferiorConjunction": 4702515014994844544, + "NextVenusProgradeToRetrograde": 4702514969849063197, + "NextVenusRetrograde": 4702514969849063197, + "NextVenusRetrogradeToPrograde": 4702515055133824128, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1994-11-13T17:22:46.345Z", + "tt_jd_bits": 4702515038098297341, + "events": { + "LastVenusConjunction": 4702515014994844544, + "LastVenusGreatestElongation": 4702514864641653804, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702513918856277452, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702514969849063197, + "LastVenusRetrogradeToPrograde": 4702513808332618238, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515639990446509, + "NextVenusGreatestElongation": 4702515168605106448, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702515168605106448, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702515055133824128, + "NextVenusRetrogradeToPrograde": 4702515055133824128, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-03-20T23:36:03.345Z", + "tt_jd_bits": 4702515311385401091, + "events": { + "LastVenusConjunction": 4702515014994844544, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515639990446509, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-07-26T05:49:20.345Z", + "tt_jd_bits": 4702515584672504842, + "events": { + "LastVenusConjunction": 4702515014994844544, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702514392481032636, + "NextVenusConjunction": 4702515639990446509, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702515639990446509 + } + }, + { + "input_utc": "1995-11-30T12:02:37.345Z", + "tt_jd_bits": 4702515857959608592, + "events": { + "LastVenusConjunction": 4702515639990446509, + "LastVenusGreatestElongation": 4702515168605106448, + "LastVenusGreatestElongationEast": 4702514864641653804, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516272804148034, + "NextVenusGreatestElongation": 4702516121090930520, + "NextVenusGreatestElongationEast": 4702516121090930520, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1996-04-05T18:15:54.345Z", + "tt_jd_bits": 4702516131246737198, + "events": { + "LastVenusConjunction": 4702515639990446509, + "LastVenusGreatestElongation": 4702516121090930520, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702515014994844544, + "LastVenusProgradeToRetrograde": 4702514969849063197, + "LastVenusRetrograde": 4702515055133824128, + "LastVenusRetrogradeToPrograde": 4702515055133824128, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516272804148034, + "NextVenusGreatestElongation": 4702516424127004108, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702516272804148034, + "NextVenusProgradeToRetrograde": 4702516226878400921, + "NextVenusRetrograde": 4702516226878400921, + "NextVenusRetrogradeToPrograde": 4702516318547117858, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1996-08-11T00:29:11.345Z", + "tt_jd_bits": 4702516404533840948, + "events": { + "LastVenusConjunction": 4702516272804148034, + "LastVenusGreatestElongation": 4702516121090930520, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702515168605106448, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516908230248195, + "NextVenusGreatestElongation": 4702516424127004108, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702516424127004108, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1996-12-14T06:42:28.345Z", + "tt_jd_bits": 4702516673525977403, + "events": { + "LastVenusConjunction": 4702516272804148034, + "LastVenusGreatestElongation": 4702516424127004108, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702515639990446509, + "NextVenusConjunction": 4702516908230248195, + "NextVenusGreatestElongation": 4702517375745288074, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702516908230248195 + } + }, + { + "input_utc": "1997-04-20T12:55:45.345Z", + "tt_jd_bits": 4702516946813081153, + "events": { + "LastVenusConjunction": 4702516908230248195, + "LastVenusGreatestElongation": 4702516424127004108, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702517528633992505, + "NextVenusGreatestElongation": 4702517375745288074, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1997-08-25T16:59:02.345Z", + "tt_jd_bits": 4702517219906339707, + "events": { + "LastVenusConjunction": 4702516908230248195, + "LastVenusGreatestElongation": 4702516424127004108, + "LastVenusGreatestElongationEast": 4702516121090930520, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702516226878400921, + "LastVenusRetrograde": 4702516318547117858, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702517528633992505, + "NextVenusGreatestElongation": 4702517375745288074, + "NextVenusGreatestElongationEast": 4702517375745288074, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702517481616362992, + "NextVenusRetrograde": 4702517481616362992, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1997-12-30T23:12:19.345Z", + "tt_jd_bits": 4702517493193443457, + "events": { + "LastVenusConjunction": 4702516908230248195, + "LastVenusGreatestElongation": 4702517375745288074, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702516424127004108, + "LastVenusInferiorConjunction": 4702516272804148034, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517481616362992, + "LastVenusRetrogradeToPrograde": 4702516318547117858, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702517528633992505, + "NextVenusGreatestElongation": 4702517679616047386, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702517679616047386, + "NextVenusInferiorConjunction": 4702517528633992505, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702517572154200632, + "NextVenusRetrogradeToPrograde": 4702517572154200632, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1998-05-07T05:25:36.345Z", + "tt_jd_bits": 4702517766480547208, + "events": { + "LastVenusConjunction": 4702517528633992505, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702518144341918960, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1998-09-11T11:38:53.345Z", + "tt_jd_bits": 4702518039767650958, + "events": { + "LastVenusConjunction": 4702517528633992505, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702516908230248195, + "NextVenusConjunction": 4702518144341918960, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702518144341918960 + } + }, + { + "input_utc": "1999-01-16T17:52:10.345Z", + "tt_jd_bits": 4702518313054779564, + "events": { + "LastVenusConjunction": 4702518144341918960, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702518776380939323, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "1999-05-24T00:01:12.345Z", + "tt_jd_bits": 4702518586335545255, + "events": { + "LastVenusConjunction": 4702518144341918960, + "LastVenusGreatestElongation": 4702517679616047386, + "LastVenusGreatestElongationEast": 4702517375745288074, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702517528633992505, + "LastVenusProgradeToRetrograde": 4702517481616362992, + "LastVenusRetrograde": 4702517572154200632, + "LastVenusRetrogradeToPrograde": 4702517572154200632, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702518776380939323, + "NextVenusGreatestElongation": 4702518626051128940, + "NextVenusGreatestElongationEast": 4702518626051128940, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702518776380939323, + "NextVenusProgradeToRetrograde": 4702518725593615746, + "NextVenusRetrograde": 4702518725593615746, + "NextVenusRetrogradeToPrograde": 4702518820037583595, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "1999-09-26T06:14:29.345Z", + "tt_jd_bits": 4702518855327681709, + "events": { + "LastVenusConjunction": 4702518776380939323, + "LastVenusGreatestElongation": 4702518626051128940, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702517679616047386, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702519411906295324, + "NextVenusGreatestElongation": 4702518929890649762, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702518929890649762, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "2000-01-31T12:27:46.345Z", + "tt_jd_bits": 4702519128614785460, + "events": { + "LastVenusConjunction": 4702518776380939323, + "LastVenusGreatestElongation": 4702518929890649762, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702519411906295324, + "NextVenusGreatestElongation": 4702519883963900672, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "2000-06-06T18:41:03.345Z", + "tt_jd_bits": 4702519401901889210, + "events": { + "LastVenusConjunction": 4702518776380939323, + "LastVenusGreatestElongation": 4702518929890649762, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702518144341918960, + "NextVenusConjunction": 4702519411906295324, + "NextVenusGreatestElongation": 4702519883963900672, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702519411906295324 + } + }, + { + "input_utc": "2000-10-12T00:54:20.345Z", + "tt_jd_bits": 4702519675188992961, + "events": { + "LastVenusConjunction": 4702519411906295324, + "LastVenusGreatestElongation": 4702518929890649762, + "LastVenusGreatestElongationEast": 4702518626051128940, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520038413653359, + "NextVenusGreatestElongation": 4702519883963900672, + "NextVenusGreatestElongationEast": 4702519883963900672, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2001-02-16T04:57:37.345Z", + "tt_jd_bits": 4702519948282226659, + "events": { + "LastVenusConjunction": 4702519411906295324, + "LastVenusGreatestElongation": 4702519883963900672, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702518929890649762, + "LastVenusInferiorConjunction": 4702518776380939323, + "LastVenusProgradeToRetrograde": 4702518725593615746, + "LastVenusRetrograde": 4702518820037583595, + "LastVenusRetrogradeToPrograde": 4702518820037583595, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520038413653359, + "NextVenusGreatestElongation": 4702520188775143046, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702520188775143046, + "NextVenusInferiorConjunction": 4702520038413653359, + "NextVenusProgradeToRetrograde": 4702519989583753004, + "NextVenusRetrograde": 4702519989583753004, + "NextVenusRetrogradeToPrograde": 4702520078167311162, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2001-06-23T11:10:54.345Z", + "tt_jd_bits": 4702520221569330410, + "events": { + "LastVenusConjunction": 4702520038413653359, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520661833733673, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2001-10-28T17:24:11.345Z", + "tt_jd_bits": 4702520494856434160, + "events": { + "LastVenusConjunction": 4702520038413653359, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702519411906295324, + "NextVenusConjunction": 4702520661833733673, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702520661833733673 + } + }, + { + "input_utc": "2002-03-04T23:37:28.345Z", + "tt_jd_bits": 4702520768143537911, + "events": { + "LastVenusConjunction": 4702520661833733673, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521284654012721, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2002-07-08T05:50:45.345Z", + "tt_jd_bits": 4702521037135674365, + "events": { + "LastVenusConjunction": 4702520661833733673, + "LastVenusGreatestElongation": 4702520188775143046, + "LastVenusGreatestElongationEast": 4702519883963900672, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702520038413653359, + "LastVenusProgradeToRetrograde": 4702519989583753004, + "LastVenusRetrograde": 4702520078167311162, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521284654012721, + "NextVenusGreatestElongation": 4702521134439713686, + "NextVenusGreatestElongationEast": 4702521134439713686, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702521284654012721, + "NextVenusProgradeToRetrograde": 4702521239289679716, + "NextVenusRetrograde": 4702521239289679716, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2002-11-12T12:04:02.345Z", + "tt_jd_bits": 4702521310422778116, + "events": { + "LastVenusConjunction": 4702521284654012721, + "LastVenusGreatestElongation": 4702521134439713686, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702520188775143046, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521239289679716, + "LastVenusRetrogradeToPrograde": 4702520078167311162, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521910106751845, + "NextVenusGreatestElongation": 4702521438408467918, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702521438408467918, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702521324710542095, + "NextVenusRetrogradeToPrograde": 4702521324710542095, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2003-03-19T18:17:19.345Z", + "tt_jd_bits": 4702521583709881866, + "events": { + "LastVenusConjunction": 4702521284654012721, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521910106751845, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2003-07-25T00:30:36.345Z", + "tt_jd_bits": 4702521856996985617, + "events": { + "LastVenusConjunction": 4702521284654012721, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702520661833733673, + "NextVenusConjunction": 4702521910106751845, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702521910106751845 + } + }, + { + "input_utc": "2003-11-29T06:43:53.345Z", + "tt_jd_bits": 4702522130284089367, + "events": { + "LastVenusConjunction": 4702521910106751845, + "LastVenusGreatestElongation": 4702521438408467918, + "LastVenusGreatestElongationEast": 4702521134439713686, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702522542777144799, + "NextVenusGreatestElongation": 4702522391017602520, + "NextVenusGreatestElongationEast": 4702522391017602520, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2004-04-04T12:57:10.345Z", + "tt_jd_bits": 4702522403571193117, + "events": { + "LastVenusConjunction": 4702521910106751845, + "LastVenusGreatestElongation": 4702522391017602520, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702521284654012721, + "LastVenusProgradeToRetrograde": 4702521239289679716, + "LastVenusRetrograde": 4702521324710542095, + "LastVenusRetrogradeToPrograde": 4702521324710542095, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702522542777144799, + "NextVenusGreatestElongation": 4702522693977973000, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702522542777144799, + "NextVenusProgradeToRetrograde": 4702522496937086012, + "NextVenusRetrograde": 4702522496937086012, + "NextVenusRetrogradeToPrograde": 4702522588371445290, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2004-08-09T16:56:12.345Z", + "tt_jd_bits": 4702522676658088757, + "events": { + "LastVenusConjunction": 4702522542777144799, + "LastVenusGreatestElongation": 4702522391017602520, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702521438408467918, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702523177964903487, + "NextVenusGreatestElongation": 4702522693977973000, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702522693977973000, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2004-12-14T23:09:29.345Z", + "tt_jd_bits": 4702522949945192507, + "events": { + "LastVenusConjunction": 4702522542777144799, + "LastVenusGreatestElongation": 4702522693977973000, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702521910106751845, + "NextVenusConjunction": 4702523177964903487, + "NextVenusGreatestElongation": 4702523645405413500, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702523177964903487 + } + }, + { + "input_utc": "2005-04-19T05:22:46.345Z", + "tt_jd_bits": 4702523218937328962, + "events": { + "LastVenusConjunction": 4702523177964903487, + "LastVenusGreatestElongation": 4702522693977973000, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702523798272971317, + "NextVenusGreatestElongation": 4702523645405413500, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2005-08-24T11:36:03.345Z", + "tt_jd_bits": 4702523492224432712, + "events": { + "LastVenusConjunction": 4702523177964903487, + "LastVenusGreatestElongation": 4702522693977973000, + "LastVenusGreatestElongationEast": 4702522391017602520, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702522496937086012, + "LastVenusRetrograde": 4702522588371445290, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702523798272971317, + "NextVenusGreatestElongation": 4702523645405413500, + "NextVenusGreatestElongationEast": 4702523645405413500, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702523751448597507, + "NextVenusRetrograde": 4702523751448597507, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2005-12-29T17:49:20.345Z", + "tt_jd_bits": 4702523765511536463, + "events": { + "LastVenusConjunction": 4702523177964903487, + "LastVenusGreatestElongation": 4702523645405413500, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702522693977973000, + "LastVenusInferiorConjunction": 4702522542777144799, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523751448597507, + "LastVenusRetrogradeToPrograde": 4702522588371445290, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702523798272971317, + "NextVenusGreatestElongation": 4702523949202163128, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702523949202163128, + "NextVenusInferiorConjunction": 4702523798272971317, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702523841828255446, + "NextVenusRetrogradeToPrograde": 4702523841828255446, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2006-05-06T00:02:37.345Z", + "tt_jd_bits": 4702524038798665068, + "events": { + "LastVenusConjunction": 4702523798272971317, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702524414051142647, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2006-09-10T06:15:54.345Z", + "tt_jd_bits": 4702524312085768819, + "events": { + "LastVenusConjunction": 4702523798272971317, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702523177964903487, + "NextVenusConjunction": 4702524414051142647, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702524414051142647 + } + }, + { + "input_utc": "2007-01-15T12:29:11.345Z", + "tt_jd_bits": 4702524585372872569, + "events": { + "LastVenusConjunction": 4702524414051142647, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525046292053779, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2007-05-22T18:42:28.345Z", + "tt_jd_bits": 4702524858659976320, + "events": { + "LastVenusConjunction": 4702524414051142647, + "LastVenusGreatestElongation": 4702523949202163128, + "LastVenusGreatestElongationEast": 4702523645405413500, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702523798272971317, + "LastVenusProgradeToRetrograde": 4702523751448597507, + "LastVenusRetrograde": 4702523841828255446, + "LastVenusRetrogradeToPrograde": 4702523841828255446, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525046292053779, + "NextVenusGreatestElongation": 4702524895885769978, + "NextVenusGreatestElongationEast": 4702524895885769978, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702525046292053779, + "NextVenusProgradeToRetrograde": 4702524995610960873, + "NextVenusRetrograde": 4702524995610960873, + "NextVenusRetrogradeToPrograde": 4702525090171470087, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2007-09-27T00:55:45.345Z", + "tt_jd_bits": 4702525131947080070, + "events": { + "LastVenusConjunction": 4702525046292053779, + "LastVenusGreatestElongation": 4702524895885769978, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702523949202163128, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525682003400406, + "NextVenusGreatestElongation": 4702525199784054900, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702525199784054900, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2008-01-30T04:59:02.345Z", + "tt_jd_bits": 4702525400745346473, + "events": { + "LastVenusConjunction": 4702525046292053779, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525682003400406, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2008-06-05T11:12:19.345Z", + "tt_jd_bits": 4702525674032450223, + "events": { + "LastVenusConjunction": 4702525046292053779, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702524414051142647, + "NextVenusConjunction": 4702525682003400406, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702525682003400406 + } + }, + { + "input_utc": "2008-10-10T17:25:36.345Z", + "tt_jd_bits": 4702525947319553974, + "events": { + "LastVenusConjunction": 4702525682003400406, + "LastVenusGreatestElongation": 4702525199784054900, + "LastVenusGreatestElongationEast": 4702524895885769978, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526308271303356, + "NextVenusGreatestElongation": 4702526153832455514, + "NextVenusGreatestElongationEast": 4702526153832455514, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2009-02-14T23:38:53.345Z", + "tt_jd_bits": 4702526220606682579, + "events": { + "LastVenusConjunction": 4702525682003400406, + "LastVenusGreatestElongation": 4702526153832455514, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702525199784054900, + "LastVenusInferiorConjunction": 4702525046292053779, + "LastVenusProgradeToRetrograde": 4702524995610960873, + "LastVenusRetrograde": 4702525090171470087, + "LastVenusRetrogradeToPrograde": 4702525090171470087, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526308271303356, + "NextVenusGreatestElongation": 4702526458723921452, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702526458723921452, + "NextVenusInferiorConjunction": 4702526308271303356, + "NextVenusProgradeToRetrograde": 4702526259348625163, + "NextVenusRetrograde": 4702526259348625163, + "NextVenusRetrogradeToPrograde": 4702526348061495207, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2009-06-22T05:52:10.345Z", + "tt_jd_bits": 4702526493893786330, + "events": { + "LastVenusConjunction": 4702526308271303356, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526931193901496, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2009-10-27T12:01:12.345Z", + "tt_jd_bits": 4702526767174552021, + "events": { + "LastVenusConjunction": 4702526308271303356, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702525682003400406, + "NextVenusConjunction": 4702526931193901496, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702526931193901496 + } + }, + { + "input_utc": "2010-03-03T18:14:29.345Z", + "tt_jd_bits": 4702527040461655771, + "events": { + "LastVenusConjunction": 4702526931193901496, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702527554328569648, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + }, + { + "input_utc": "2010-07-09T00:27:46.345Z", + "tt_jd_bits": 4702527313748759522, + "events": { + "LastVenusConjunction": 4702526931193901496, + "LastVenusGreatestElongation": 4702526458723921452, + "LastVenusGreatestElongationEast": 4702526153832455514, + "LastVenusGreatestElongationWest": 4702526458723921452, + "LastVenusInferiorConjunction": 4702526308271303356, + "LastVenusProgradeToRetrograde": 4702526259348625163, + "LastVenusRetrograde": 4702526348061495207, + "LastVenusRetrogradeToPrograde": 4702526348061495207, + "LastVenusSuperiorConjunction": 4702526931193901496, + "NextVenusConjunction": 4702527554328569648, + "NextVenusGreatestElongation": 4702527404242007826, + "NextVenusGreatestElongationEast": 4702527404242007826, + "NextVenusGreatestElongationWest": 4702527708128036028, + "NextVenusInferiorConjunction": 4702527554328569648, + "NextVenusProgradeToRetrograde": 4702527508682115377, + "NextVenusRetrograde": 4702527508682115377, + "NextVenusRetrogradeToPrograde": 4702527594279007280, + "NextVenusSuperiorConjunction": 4702528180226942902 + } + } + ] +} diff --git a/basic/time_labels.go b/basic/time_labels.go new file mode 100644 index 0000000..25e9b3c --- /dev/null +++ b/basic/time_labels.go @@ -0,0 +1,33 @@ +package basic + +import ( + "time" +) + +// TimeScale 导出时间标签的时标,零值为民用 UTC / output-label time scale, defaulting to civil UTC. +type TimeScale int + +const ( + // TimeScaleUTC 默认民用时标 / default civil time scale. + TimeScaleUTC TimeScale = iota + // TimeScaleUT1 UT1 读数,输出 Location 须为 nil 或 UTC / UT1 reading; output Location must be nil or UTC. + TimeScaleUT1 +) + +// LabelIn 把民用时刻换成该时标下的时刻值 / converts a civil instant to a value in one scale. +func LabelIn(scale TimeScale, date time.Time) time.Time { + if scale == TimeScaleUT1 { + return UT1FromUTC(date) + } + return date +} + +// UT1FromUTC 民用时刻转 UT1 读数,以 UTC Location 承载 / converts a civil instant to a UT1 reading in a UTC Location. +func UT1FromUTC(date time.Time) time.Time { + return JD2DateByZone(UTC2UT1(Date2JD(date.UTC())), time.UTC, false) +} + +// DUT1 返回民用时刻的 UT1−UTC,单位秒 / UT1−UTC in seconds. +func DUT1(date time.Time) float64 { + return DUT1Seconds(Date2JD(date.UTC())) +} diff --git a/basic/timescale.go b/basic/timescale.go index bf35f7c..562ee06 100644 --- a/basic/timescale.go +++ b/basic/timescale.go @@ -5,18 +5,18 @@ import ( "sync" ) -// TimeScaleFuturePolicy 民用时标换算政策,零值为闰秒情景外推 / civil-time conversion policy, defaulting to leap-second extrapolation. +// TimeScaleFuturePolicy 民用时标换算政策,零值为闰时 / civil-time conversion policy, leap hour by default. type TimeScaleFuturePolicy int const ( - // TimeScaleLeapSecond 默认按 ΔT 越限施加整数秒校正,不代表闰秒公告 / default integer-second corrections driven by extrapolated ΔT, not announcements. - TimeScaleLeapSecond TimeScaleFuturePolicy = iota + // TimeScaleLeapHour 按 ΔT 越限施加整小时校正(2027-05-20 起 CGPM 决议) / applies hour-sized corrections as adopted by CGPM from 2027. + TimeScaleLeapHour TimeScaleFuturePolicy = iota + // TimeScaleLeapSecond 按 ΔT 越限施加整数秒校正(1972–2026 确定值) / integer-second corrections (definitive 1972–2026). + TimeScaleLeapSecond // TimeScaleAssumeUT1Tracking 窗口外固定末端 DUT1,平滑跟随 UT1 / holds the last observed DUT1 beyond the window. TimeScaleAssumeUT1Tracking // TimeScaleFreezeUTCOffset 窗口外固定末端 TT−UTC / holds the last TT−UTC offset beyond the window. TimeScaleFreezeUTCOffset - // TimeScaleLeapHour 按 ΔT 越限施加整小时校正,仅作情景演算 / applies hour-sized corrections as a scenario assumption. - TimeScaleLeapHour // TimeScaleUT1Civil 全时轴民用时标等同 UT1,TT−UTC 覆盖仍优先 / uses UT1 as civil time everywhere unless TT−UTC is overridden. TimeScaleUT1Civil ) @@ -30,8 +30,8 @@ const utcDUT1ToleranceSeconds = 0.9 // utcEraStartJDE 是 UTC 按 SI 秒运行的起点(1972-01-01),此前民用时标即 UT1。 const utcEraStartJDE = 2441317.5 -// 实测窗口末端,必须与月度 ΔT 表末项同步。 -const timeScaleExactEndJDE = 2461284.5 +// 月度 ΔT 表覆盖窗口末端,必须与表末项同步。 +const timeScaleExactEndJDE = 2461314.5 // 闰秒生效的儒略日按降序排列,与 utcLeapOffsets 一一对应。 var utcLeapJDEs = [...]float64{ @@ -223,18 +223,21 @@ func utcToTTOffsetSeconds(jd float64) float64 { if GetTimeScaleFuturePolicy() == TimeScaleUT1Civil { return ut1ToTTOffsetSeconds(jd) } + // 内置实测窗口(截至 2026-10-01)一律取确定闰秒表值,窗口外才按政策外推。 if jd <= timeScaleExactEndJDE { return TTMinusUTCSeconds(jd) } switch GetTimeScaleFuturePolicy() { + case TimeScaleLeapHour: + return steppedUTCOffsetSeconds(jd, timeScaleLeapHourSeconds, timeScaleLeapHourSeconds) + case TimeScaleLeapSecond: + return steppedUTCOffsetSeconds(jd, 1, utcDUT1ToleranceSeconds) case TimeScaleAssumeUT1Tracking: return ut1ToTTOffsetSeconds(jd) + dut1AtTimeScaleExactEnd() case TimeScaleFreezeUTCOffset: return TTMinusUTCSeconds(timeScaleExactEndJDE) - case TimeScaleLeapHour: - return steppedUTCOffsetSeconds(jd, timeScaleLeapHourSeconds, timeScaleLeapHourSeconds) } - return steppedUTCOffsetSeconds(jd, 1, utcDUT1ToleranceSeconds) + return steppedUTCOffsetSeconds(jd, timeScaleLeapHourSeconds, timeScaleLeapHourSeconds) } // 相对窗口末端偏移施加最少整数步校正,使 DUT1 落回容限内。 diff --git a/basic/timescale_table.go b/basic/timescale_table.go index dd6371c..21ba48e 100644 --- a/basic/timescale_table.go +++ b/basic/timescale_table.go @@ -1,7 +1,7 @@ package basic -// 由月度实测 TT−UT1 序列生成,请勿手改;表尾同时是 UTC2TT 精确窗口的末端。 -// Generated from the monthly observed TT−UT1 series; the last entry also ends the exact window. +// 由月度 TT−UT1 序列生成,请勿手改;表尾同时是 UTC2TT 精确窗口的末端。 +// Generated from the monthly TT−UT1 series; the last entry also ends the exact window. // deltaTMonthlyJDE 是逐月实测 TT−UT1 的儒略日,与 deltaTMonthlyObserved 一一对应。 var deltaTMonthlyJDE = [...]float64{ @@ -165,7 +165,7 @@ var deltaTMonthlyJDE = [...]float64{ 2460827.5, 2460857.5, 2460888.5, 2460919.5, 2460949.5, 2460980.5, 2461010.5, 2461041.5, 2461072.5, 2461100.5, 2461131.5, 2461161.5, - 2461192.5, 2461222.5, 2461253.5, 2461284.5, + 2461192.5, 2461222.5, 2461253.5, 2461284.5, 2461314.5, } // deltaTMonthlyObserved 是对应月份的 TT−UT1(秒),来自权威逐月实测序列。 @@ -330,5 +330,5 @@ var deltaTMonthlyObserved = [...]float64{ 69.1550, 69.1406, 69.1219, 69.0994, 69.0909, 69.0909, 69.1042, 69.1099, 69.1133, 69.1168, 69.1330, 69.1511, - 69.1662, 69.1695, 69.1713, 69.1816, + 69.1662, 69.1695, 69.1713, 69.1815, 69.2065, } diff --git a/basic/timescale_test.go b/basic/timescale_test.go index d75a1c1..2afd1d8 100644 --- a/basic/timescale_test.go +++ b/basic/timescale_test.go @@ -10,11 +10,11 @@ import ( func resetTimeScaleState(t *testing.T) { t.Helper() SetTTMinusUTCFn(nil) - SetTimeScaleFuturePolicy(TimeScaleLeapSecond) + SetTimeScaleFuturePolicy(TimeScaleLeapHour) SetDeltaTFn(nil) t.Cleanup(func() { SetTTMinusUTCFn(nil) - SetTimeScaleFuturePolicy(TimeScaleLeapSecond) + SetTimeScaleFuturePolicy(TimeScaleLeapHour) SetDeltaTFn(nil) }) } @@ -23,8 +23,8 @@ func TestTimeScaleCalendarAnchors(t *testing.T) { if got := JDCalc(1972, 1, 1); got != 2441317.5 { t.Fatalf("JDCalc(1972,1,1)=%v, want 2441317.5", got) } - if got := JDCalc(2026, 9, 1); got != timeScaleExactEndJDE { - t.Fatalf("JDCalc(2026,9,1)=%v, want %v", got, timeScaleExactEndJDE) + if got := JDCalc(2026, 10, 1); got != timeScaleExactEndJDE { + t.Fatalf("JDCalc(2026,10,1)=%v, want %v", got, timeScaleExactEndJDE) } if got := deltaTMonthlyJDE[len(deltaTMonthlyJDE)-1]; got != timeScaleExactEndJDE { t.Fatalf("月度表末项=%v, want 精确窗口末端 %v", got, timeScaleExactEndJDE) @@ -77,24 +77,26 @@ func TestTimeScaleLeapHourPolicyCarriesFromDeltaT(t *testing.T) { } } -// 默认与零值均为整数秒外推,首次越限前保持末端偏移。 +// 默认与零值均为闰时(2027 起 CGPM 决议),按整小时施加校正。 func TestTimeScaleDefaultFuturePolicyIsLeapSecondRegime(t *testing.T) { resetTimeScaleState(t) - if got := GetTimeScaleFuturePolicy(); got != TimeScaleLeapSecond { - t.Fatalf("默认未来政策=%v, want TimeScaleLeapSecond", got) + if got := GetTimeScaleFuturePolicy(); got != TimeScaleLeapHour { + t.Fatalf("默认未来政策=%v, want TimeScaleLeapHour", got) } var zero TimeScaleFuturePolicy - if zero != TimeScaleLeapSecond { + if zero != TimeScaleLeapHour { t.Fatalf("零值政策=%v, 应即默认", zero) } base := TTMinusUTCSeconds(timeScaleExactEndJDE) for _, jd := range []float64{JDCalc(2027, 1, 1), JDCalc(2029, 7, 1), JDCalc(2035, 7, 1)} { - if got := utcToTTOffsetSeconds(jd); math.Abs(got-base) > 1e-9 { - t.Errorf("%v 年默认政策 TT−UTC=%v, want %v(首次触限前应与已宣告偏移相同)", - jd, got, base) + got := utcToTTOffsetSeconds(jd) + dut1 := math.Abs(DUT1Seconds(jd)) + if dut1 > timeScaleLeapHourSeconds+1e-9 { + t.Errorf("%v 年默认政策 |DUT1|=%v 越过闰时容限 3600 s", jd, dut1) } - if got := DUT1Seconds(jd); math.Abs(got) > utcDUT1ToleranceSeconds+1e-9 { - t.Errorf("%v 年默认政策 |DUT1|=%v 越过容限", jd, math.Abs(got)) + // 闰时政策下 TT−UTC 以小时为步阶跃 + if math.Mod(got-base, 3600) > 1e-6 { + t.Errorf("%v 年 TT−UTC=%v 相对基准 %v 的差值非整小时倍", jd, got, base) } } // 平滑跟随 UT1 的政策必须给出不同的值,否则这条守卫会与它混同。 @@ -183,14 +185,17 @@ func TestTimeScaleLeapTableAnchors(t *testing.T) { func TestTimeScaleMonthlyDeltaTAnchors(t *testing.T) { resetTimeScaleState(t) // 秒级偏移直接锚定;JDE 级取值受 jd≈2.5e6 的浮点抵消限制(约 2e-5 秒)。 - if got := DeltaT(timeScaleExactEndJDE, true); math.Abs(got-69.1816) > 1e-12 { - t.Errorf("2026-09 TT−UT1=%v, want 69.1816", got) + if got := DeltaT(JDCalc(2026, 9, 1), true); math.Abs(got-69.1815) > 1e-12 { + t.Errorf("2026-09 TT−UT1=%v, want 69.1815", got) + } + if got := DeltaT(timeScaleExactEndJDE, true); math.Abs(got-69.2065) > 1e-12 { + t.Errorf("2026-10 TT−UT1=%v, want 69.2065", got) } if got := DeltaT(JDCalc(1973, 2, 1), true); math.Abs(got-43.4724) > 1e-12 { t.Errorf("1973-02 TT−UT1=%v, want 43.4724", got) } - if got := (UT12TT(timeScaleExactEndJDE) - timeScaleExactEndJDE) * 86400; math.Abs(got-69.1816) > 1e-4 { - t.Errorf("2026-09 UT12TT 偏移=%v, want 69.1816", got) + if got := (UT12TT(timeScaleExactEndJDE) - timeScaleExactEndJDE) * 86400; math.Abs(got-69.2065) > 1e-4 { + t.Errorf("2026-10 UT12TT 偏移=%v, want 69.2065", got) } mid := (deltaTMonthlyJDE[10] + deltaTMonthlyJDE[11]) / 2 want := (deltaTMonthlyObserved[10] + deltaTMonthlyObserved[11]) / 2 @@ -240,8 +245,8 @@ func TestTimeScaleMonthlyDeltaTTableHeadAnchored(t *testing.T) { // 容限是整个窗口的历史事实:按 1e-3 天全窗扫描,抽样点会漏掉月度 ΔT 表首之前的越界。 func TestTimeScaleDUT1Identity(t *testing.T) { resetTimeScaleState(t) - if got := DUT1Seconds(timeScaleExactEndJDE); math.Abs(got-0.0024) > 1e-12 { - t.Errorf("DUT1(2026-09)=%v, want 0.0024", got) + if got := DUT1Seconds(timeScaleExactEndJDE); math.Abs(got+0.0225) > 1e-12 { + t.Errorf("DUT1(2026-10)=%v, want -0.0225", got) } worst, worstAt := 0.0, 0.0 for jd := utcEraStartJDE; jd <= timeScaleExactEndJDE; jd += 1e-3 { @@ -521,7 +526,7 @@ func TestTimeScaleLeapSecondPolicy(t *testing.T) { if isJd { year = 2026 + (jd-timeScaleExactEndJDE)/365.25 } - return 69.1816 - 0.2*(year-2026) + return 69.2065 - 0.2*(year-2026) }) jd := JDCalc(2032, 1, 1) if got := utcToTTOffsetSeconds(jd); math.Abs(got-(base-1)) > 1e-9 { diff --git a/basic/topocentric_horizontal.go b/basic/topocentric_horizontal.go new file mode 100644 index 0000000..5a4bc36 --- /dev/null +++ b/basic/topocentric_horizontal.go @@ -0,0 +1,90 @@ +package basic + +import ( + "math" + + "b612.me/astro/internal/ephem" + . "b612.me/astro/tools" +) + +// topocentricHourAngleDec 由地心视赤经、视赤纬(度)与地心距(AU)给出观测者处的站心时角与站心视赤纬(度)。 +// 恒星时只算一次,视差修正与时角共用;观测者取海拔 0,海拔对太阳系天体视差的影响可忽略。 +func topocentricHourAngleDec(utcJD, lon, lat, ra, dec, distanceAU float64) (hourAngle, topocentricDec float64) { + gast := ApparentSiderealTime(UTC2UT1(utcJD)) * 15 + topocentricRA, topocentricDec := topocentricRaDecWithSidereal(ra, dec, lat, lon, gast, distanceAU, 0) + return Limit360(gast + lon - topocentricRA), topocentricDec +} + +// horizontalAltitude 时角、赤纬、纬度(度)转高度角 / altitude from hour angle, declination and latitude. +func horizontalAltitude(hourAngle, dec, lat float64) float64 { + return ArcSin(Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle)) +} + +// horizontalAzimuth 时角、赤纬、纬度(度)转方位角,北起向东 / azimuth from north through east. +func horizontalAzimuth(hourAngle, dec, lat float64) float64 { + tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) + azimuth := ArcTan(tanAzimuth) + if azimuth < 0 { + if hourAngle/15 < 12 { + return azimuth + 360 + } + return azimuth + 180 + } + if hourAngle/15 < 12 { + return azimuth + 180 + } + return azimuth +} + +// planetApparentRaDecDistanceNWithInstance 行星地心视赤经、视赤纬与几何地心距,三者取自同一次求值。 +func planetApparentRaDecDistanceNWithInstance(index int, jde float64, n int, inst *Instance) (ra, dec, distanceAU float64) { + geo, _, distanceAU, ok := planetApparentPositionWithInstance(index, jde, n, inst) + if !ok || math.IsNaN(geo.lo) || math.IsNaN(geo.bo) { + return math.NaN(), math.NaN(), math.NaN() + } + ra, dec = planetApparentRaDecFromLoBo(jde, geo.lo, geo.bo) + return ra, dec, distanceAU +} + +func planetTopocentricHourAngleDecN(index int, localJD, lon, lat, timezone float64, n int, inst *Instance) (float64, float64) { + utcJD := localJD - timezone/24.0 + ra, dec, distanceAU := planetApparentRaDecDistanceNWithInstance(index, UTC2TT(utcJD), n, inst) + return topocentricHourAngleDec(utcJD, lon, lat, ra, dec, distanceAU) +} + +// planetHeightN 行星站心视高度角(度,不含折射)/ topocentric planet altitude without refraction. +func planetHeightN(index int, localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + hourAngle, dec := planetTopocentricHourAngleDecN(index, localJD, lon, lat, timezone, n, inst) + return horizontalAltitude(hourAngle, dec, lat) +} + +// planetAzimuthN 行星站心方位角(度)/ topocentric planet azimuth. +func planetAzimuthN(index int, localJD, lon, lat, timezone float64, n int, inst *Instance) float64 { + hourAngle, dec := planetTopocentricHourAngleDecN(index, localJD, lon, lat, timezone, n, inst) + return horizontalAzimuth(hourAngle, dec, lat) +} + +// sunTopocentricHourAngleDecN 太阳站心时角、站心视赤纬与地日距 / topocentric solar hour angle, declination and distance. +func sunTopocentricHourAngleDecN(jd, lon, lat, tz float64, n int, inst *Instance) (hourAngle, dec, distanceAU float64) { + utcJD := jd - tz/24.0 + ra, dec, distanceAU := hSunApparentRaDecDistanceN(UTC2TT(utcJD), n, inst) + hourAngle, dec = topocentricHourAngleDec(utcJD, lon, lat, ra, dec, distanceAU) + return hourAngle, dec, distanceAU +} + +// sunAzimuthN 地距只用于视差,矢量视位置可用时直接取其距离,省去一次地距求值。 +func sunAzimuthN(jd, lon, lat, tz float64, n int, inst *Instance) float64 { + utcJD := jd - tz/24.0 + tt := UTC2TT(utcJD) + var ra, dec, distanceAU float64 + if lo, bo, _, dist, ok, err := vectorApparentWithInstance(ephem.Sun, tt, n, inst); err != nil { + return math.NaN() + } else if ok { + ra, dec = LoBoToRaDec(tt, lo, bo) + distanceAU = dist + } else { + ra, dec, distanceAU = hSunApparentRaDecDistanceN(tt, n, inst) + } + hourAngle, topocentricDec := topocentricHourAngleDec(utcJD, lon, lat, ra, dec, distanceAU) + return horizontalAzimuth(hourAngle, topocentricDec, lat) +} diff --git a/basic/uranus.go b/basic/uranus.go index 01b2c29..14087a3 100644 --- a/basic/uranus.go +++ b/basic/uranus.go @@ -115,59 +115,34 @@ func UranusMag(jde float64) float64 { return FloatRound(mag, 2) } -func UranusHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := UranusApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// UranusHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(6, localJD, lon, lat, timezone, -1, inst) } -func UranusAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := UranusApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// UranusAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(6, localJD, lon, lat, timezone, -1, inst) } -func UranusHourAngle(jd, lon, timezone float64) float64 { +// UranusHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusHourAngleWithInstance(jd, lon, timezone float64, inst *Instance) float64 { siderealLongitude := Limit360(ApparentSiderealTime(UTC2UT1(jd-timezone/24))*15 + lon) - hourAngle := siderealLongitude - UranusApparentRa(UTC2TT(jd-timezone/24.0)) + hourAngle := siderealLongitude - UranusApparentRaWithInstance(UTC2TT(jd-timezone/24.0), inst) if hourAngle < 0 { hourAngle += 360 } return hourAngle } -func UranusCulminationTime(localJD, lon, timezone float64) float64 { +// UranusCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-UranusHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-UranusHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 normalizedHourAngle := func(localJD, lon, timezone float64) float64 { - currentHourAngle := UranusHourAngle(localJD, lon, timezone) + currentHourAngle := UranusHourAngleWithInstance(localJD, lon, timezone, inst) if currentHourAngle < 180 { currentHourAngle += 360 } @@ -185,14 +160,22 @@ func UranusCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func UranusRiseTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true) +// UranusRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusRiseTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, true, inst) } -func UranusSetTime(jd, lon, lat, timezone, aeroCorrection, observerHeight float64) (float64, error) { - return uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false) +// UranusSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func UranusSetTimeWithInstance(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, false, inst) } -func uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, UranusCulminationTime, UranusHeight, UranusApparentDec) +func uranusRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, timezone, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return UranusCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return UranusHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return UranusApparentDecWithInstance(jde, inst) }) } diff --git a/basic/uranus_events.go b/basic/uranus_events.go index 8e0345e..e795009 100644 --- a/basic/uranus_events.go +++ b/basic/uranus_events.go @@ -14,8 +14,10 @@ const ( uranusPhaseCoarseTolerance = 30.0 / 86400.0 ) -func uranusSunLongitudeDelta(jde, degree float64, filter bool) float64 { - sub := Limit360(Limit360(UranusApparentLo(jde)-HSunApparentLo(jde)) - degree) +func uranusSunLongitudeDelta(jde, degree float64, filter bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(UranusApparentLoWithInstance(jde, inst)-HSunApparentLoWithInstance(jde, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -27,8 +29,10 @@ func uranusSunLongitudeDelta(jde, degree float64, filter bool) float64 { return sub } -func uranusSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { - sub := Limit360(Limit360(UranusApparentLoN(jde, n)-HSunApparentLoN(jde, n)) - degree) +func uranusSunLongitudeDeltaN(jde, degree float64, filter bool, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(Limit360(UranusApparentLoNWithInstance(jde, n, inst)-HSunApparentLoNWithInstance(jde, n, inst)) - degree) if filter { if sub > 180 { sub -= 360 @@ -40,8 +44,10 @@ func uranusSunLongitudeDeltaN(jde, degree float64, filter bool, n int) float64 { return sub } -func uranusRADerivative(jde, delta float64) float64 { - sub := UranusApparentRa(jde+delta) - UranusApparentRa(jde-delta) +func uranusRADerivative(jde, delta float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := UranusApparentRaWithInstance(jde+delta, inst) - UranusApparentRaWithInstance(jde-delta, inst) if sub > 180 { sub -= 360 } @@ -51,8 +57,10 @@ func uranusRADerivative(jde, delta float64) float64 { return sub / (2 * delta) } -func uranusRADerivativeN(jde, delta float64, n int) float64 { - sub := UranusApparentRaN(jde+delta, n) - UranusApparentRaN(jde-delta, n) +func uranusRADerivativeN(jde, delta float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := UranusApparentRaNWithInstance(jde+delta, n, inst) - UranusApparentRaNWithInstance(jde-delta, n, inst) if sub > 180 { sub -= 360 } @@ -62,13 +70,15 @@ func uranusRADerivativeN(jde, delta float64, n int) float64 { return sub / (2 * delta) } -func uranusConjunctionFull(jde, degree float64, next uint8) float64 { +func uranusConjunctionFull(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := URANUS_S_PERIOD / 360 - currentDelta := uranusSunLongitudeDelta(jde, degree, false) + currentDelta := uranusSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -78,8 +88,8 @@ func uranusConjunctionFull(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := uranusSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (uranusSunLongitudeDelta(prevJDE+0.000005, degree, true) - uranusSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := uranusSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (uranusSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - uranusSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -93,13 +103,15 @@ func uranusConjunctionFull(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func uranusConjunction(jde, degree float64, next uint8) float64 { +func uranusConjunction(jde, degree float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + //0=last 1=next if !isFiniteFloat(jde) || !isFiniteFloat(degree) { return math.NaN() } daysPerDegree := URANUS_S_PERIOD / 360 - currentDelta := uranusSunLongitudeDelta(jde, degree, false) + currentDelta := uranusSunLongitudeDelta(jde, degree, false, inst) if next == 0 { jde -= (360 - currentDelta) * daysPerDegree } else { @@ -109,8 +121,8 @@ func uranusConjunction(jde, degree float64, next uint8) float64 { converged := false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := uranusSunLongitudeDeltaN(prevJDE, degree, true, uranusEventSearchN) - longitudeSlope := (uranusSunLongitudeDeltaN(prevJDE+0.000005, degree, true, uranusEventSearchN) - uranusSunLongitudeDeltaN(prevJDE-0.000005, degree, true, uranusEventSearchN)) / 0.00001 + longitudeDelta := uranusSunLongitudeDeltaN(prevJDE, degree, true, uranusEventSearchN, inst) + longitudeSlope := (uranusSunLongitudeDeltaN(prevJDE+0.000005, degree, true, uranusEventSearchN, inst) - uranusSunLongitudeDeltaN(prevJDE-0.000005, degree, true, uranusEventSearchN, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= uranusPhaseCoarseTolerance { @@ -124,8 +136,8 @@ func uranusConjunction(jde, degree float64, next uint8) float64 { converged = false for i := 0; i < eventNewtonMaxIterations; i++ { prevJDE := estimateJDE - longitudeDelta := uranusSunLongitudeDelta(prevJDE, degree, true) - longitudeSlope := (uranusSunLongitudeDelta(prevJDE+0.000005, degree, true) - uranusSunLongitudeDelta(prevJDE-0.000005, degree, true)) / 0.00001 + longitudeDelta := uranusSunLongitudeDelta(prevJDE, degree, true, inst) + longitudeSlope := (uranusSunLongitudeDelta(prevJDE+0.000005, degree, true, inst) - uranusSunLongitudeDelta(prevJDE-0.000005, degree, true, inst)) / 0.00001 nextJD := prevJDE - longitudeDelta/longitudeSlope estimateJDE = nextJD if math.Abs(nextJD-prevJDE) <= 0.00001 { @@ -139,39 +151,65 @@ func uranusConjunction(jde, degree float64, next uint8) float64 { return TT2UTC(estimateJDE) } -func LastUranusConjunction(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 0, uranusConjunction) +// LastUranusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func NextUranusConjunction(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 0, uranusConjunction) +// NextUranusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 0, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func LastUranusOpposition(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 180, uranusConjunction) +// LastUranusOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func NextUranusOpposition(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 180, uranusConjunction) +// NextUranusOppositionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusOppositionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 180, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func NextUranusEasternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 90, uranusConjunction) +// NextUranusEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func LastUranusEasternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 90, uranusConjunction) +// LastUranusEasternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusEasternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 90, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func NextUranusWesternQuadrature(jde float64) float64 { - return inclusiveNextPhaseEvent(jde, 270, uranusConjunction) +// NextUranusWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func LastUranusWesternQuadrature(jde float64) float64 { - return inclusiveLastPhaseEvent(jde, 270, uranusConjunction) +// LastUranusWesternQuadratureWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusWesternQuadratureWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastPhaseEvent(jde, 270, func(jde, degree float64, next uint8) float64 { + return uranusConjunction(jde, degree, next, inst) + }) } -func uranusRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool) float64 { +func uranusRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOpposition bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(oppositionJD) { return math.NaN() } @@ -179,103 +217,107 @@ func uranusRetrogradeAroundOpposition(oppositionJD float64, searchBeforeOppositi startTT := oppositionTT endTT := oppositionTT if searchBeforeOpposition { - easternQuadratureUT := uranusConjunction(oppositionTT, 90, 0) + easternQuadratureUT := uranusConjunction(oppositionTT, 90, 0, inst) startTT = UTC2TT(easternQuadratureUT) } else { - westernQuadratureUT := uranusConjunction(oppositionTT, 270, 1) + westernQuadratureUT := uranusConjunction(oppositionTT, 270, 1, inst) endTT = UTC2TT(westernQuadratureUT) } bestJDE := zeroEventInWindow(startTT, endTT, 2.0, 2.0, 30.0/86400.0, func(jd float64) float64 { - return uranusRADerivativeN(jd, stationDerivativeStepDay, uranusEventSearchN) + return uranusRADerivativeN(jd, stationDerivativeStepDay, uranusEventSearchN, inst) }, func(jd float64) float64 { - return uranusRADerivative(jd, stationDerivativeStepDay) + return uranusRADerivative(jd, stationDerivativeStepDay, inst) }) return TT2UTC(bestJDE) } -func NextUranusRetrogradeToPrograde(jde float64) float64 { +// NextUranusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := uranusConjunctionFull(jde, 180, 0) - date := uranusRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := uranusConjunctionFull(jde, 180, 0, inst) + date := uranusRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } - nextOppositionJD := uranusConjunctionFull(jde, 180, 1) + nextOppositionJD := uranusConjunctionFull(jde, 180, 1, inst) if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - date = uranusRetrogradeAroundOpposition(nextOppositionJD, false) + date = uranusRetrogradeAroundOpposition(nextOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastUranusRetrogradeToPrograde(jde float64) float64 { +// LastUranusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - lastOppositionJD := uranusConjunctionFull(jde, 180, 0) - date := uranusRetrogradeAroundOpposition(lastOppositionJD, false) + lastOppositionJD := uranusConjunctionFull(jde, 180, 0, inst) + date := uranusRetrogradeAroundOpposition(lastOppositionJD, false, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - previousOppositionJD := uranusConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0) + previousOppositionJD := uranusConjunctionFull(eventUTLastQueryTT(lastOppositionJD), 180, 0, inst) if !isFiniteFloat(previousOppositionJD) { return math.NaN() } - date = uranusRetrogradeAroundOpposition(previousOppositionJD, false) + date = uranusRetrogradeAroundOpposition(previousOppositionJD, false, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } return date } -func NextUranusProgradeToRetrograde(jde float64) float64 { +// NextUranusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextUranusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := uranusConjunctionFull(jde, 180, 1) - date := uranusRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := uranusConjunctionFull(jde, 180, 1, inst) + date := uranusRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryAfterOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - followingOppositionJD := uranusConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1) + followingOppositionJD := uranusConjunctionFull(eventUTNextQueryTT(nextOppositionJD), 180, 1, inst) if !isFiniteFloat(followingOppositionJD) { return math.NaN() } - date = uranusRetrogradeAroundOpposition(followingOppositionJD, true) + date = uranusRetrogradeAroundOpposition(followingOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryAfterOrEqual(date, jde) { return math.NaN() } return date } -func LastUranusProgradeToRetrograde(jde float64) float64 { +// LastUranusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastUranusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } - nextOppositionJD := uranusConjunctionFull(jde, 180, 1) - date := uranusRetrogradeAroundOpposition(nextOppositionJD, true) + nextOppositionJD := uranusConjunctionFull(jde, 180, 1, inst) + date := uranusRetrogradeAroundOpposition(nextOppositionJD, true, inst) if isFiniteFloat(date) && stationUTQueryBeforeOrEqual(date, jde) { return date } if !isFiniteFloat(nextOppositionJD) { return math.NaN() } - lastOppositionJD := uranusConjunctionFull(jde, 180, 0) + lastOppositionJD := uranusConjunctionFull(jde, 180, 0, inst) if !isFiniteFloat(lastOppositionJD) { return math.NaN() } - date = uranusRetrogradeAroundOpposition(lastOppositionJD, true) + date = uranusRetrogradeAroundOpposition(lastOppositionJD, true, inst) if !isFiniteFloat(date) || !stationUTQueryBeforeOrEqual(date, jde) { return math.NaN() } diff --git a/basic/venus.go b/basic/venus.go index 14ae0cf..fc2d37e 100644 --- a/basic/venus.go +++ b/basic/venus.go @@ -115,59 +115,34 @@ func VenusMag(jde float64) float64 { return FloatRound(mag, 2) } -func VenusHeight(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := VenusApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 高度角、时角与天球座标三角转换公式 - // sin(h)=sin(lat)*sin(dec)+cos(dec)*cos(lat)*cos(hourAngle) - sinHeight := Sin(lat)*Sin(dec) + Cos(dec)*Cos(lat)*Cos(hourAngle) - return ArcSin(sinHeight) +// VenusHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusHeightWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetHeightN(2, localJD, lon, lat, timezone, -1, inst) } -func VenusAzimuth(localJD, lon, lat, timezone float64) float64 { - // 转换为世界时 - utcJD := localJD - timezone/24.0 - // 计算视恒星时 - ra, dec := VenusApparentRaDec(UTC2TT(utcJD)) - st := Limit360(ApparentSiderealTime(UTC2UT1(utcJD))*15 + lon) - // 计算时角 - hourAngle := Limit360(st - ra) - // 三角转换公式 - tanAzimuth := Sin(hourAngle) / (Cos(hourAngle)*Sin(lat) - Tan(dec)*Cos(lat)) - azimuth := ArcTan(tanAzimuth) - if azimuth < 0 { - if hourAngle/15 < 12 { - return azimuth + 360 - } - return azimuth + 180 - } - if hourAngle/15 < 12 { - return azimuth + 180 - } - return azimuth +// VenusAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusAzimuthWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 { + return planetAzimuthN(2, localJD, lon, lat, timezone, -1, inst) } -func VenusHourAngle(jd, lon, tz float64) float64 { +// VenusHourAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusHourAngleWithInstance(jd, lon, tz float64, inst *Instance) float64 { startime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon) - timeangle := startime - VenusApparentRa(UTC2TT(jd-tz/24.0)) + timeangle := startime - VenusApparentRaWithInstance(UTC2TT(jd-tz/24.0), inst) if timeangle < 0 { timeangle += 360 } return timeangle } -func VenusCulminationTime(localJD, lon, timezone float64) float64 { +// VenusCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusCulminationTimeWithInstance(localJD, lon, timezone float64, inst *Instance) float64 { // localJD 是本地民用日锚点(当地 0 时),不是力学时。 //ra,dec 瞬时天球座标,非J2000等时间天球坐标 localJD = math.Floor(localJD) + 0.5 - estimateJD := localJD + Limit360(360-VenusHourAngle(localJD, lon, timezone))/15.0/24.0*0.99726851851851851851 + estimateJD := localJD + Limit360(360-VenusHourAngleWithInstance(localJD, lon, timezone, inst))/15.0/24.0*0.99726851851851851851 limitHA := func(localJD, lon, timezone float64) float64 { - ha := VenusHourAngle(localJD, lon, timezone) + ha := VenusHourAngleWithInstance(localJD, lon, timezone, inst) if ha < 180 { ha += 360 } @@ -185,14 +160,22 @@ func VenusCulminationTime(localJD, lon, timezone float64) float64 { return estimateJD } -func VenusRiseTime(jd, lon, lat, tz, aeroCorrection, observerHeight float64) (float64, error) { - return venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, true) +// VenusRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusRiseTimeWithInstance(jd, lon, lat, tz, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, true, inst) } -func VenusSetTime(jd, lon, lat, tz, aeroCorrection, observerHeight float64) (float64, error) { - return venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, false) +// VenusSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusSetTimeWithInstance(jd, lon, lat, tz, aeroCorrection, observerHeight float64, inst *Instance) (float64, error) { + return venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, false, inst) } -func venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight float64, isRise bool) (float64, error) { - return planetRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, isRise, VenusCulminationTime, VenusHeight, VenusApparentDec) +func venusRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight float64, isRise bool, instances ...*Instance) (float64, error) { + inst := instanceValue(instances) + + return planetRiseDown(jd, lon, lat, tz, aeroCorrection, observerHeight, isRise, func(localJD, lon, timezone float64) float64 { + return VenusCulminationTimeWithInstance(localJD, lon, timezone, inst) + }, func(localJD, lon, lat, timezone float64) float64 { + return VenusHeightWithInstance(localJD, lon, lat, timezone, inst) + }, func(jde float64) float64 { return VenusApparentDecWithInstance(jde, inst) }) } diff --git a/basic/venus_event_regression_test.go b/basic/venus_event_regression_test.go index 5e12603..031e532 100644 --- a/basic/venus_event_regression_test.go +++ b/basic/venus_event_regression_test.go @@ -3,7 +3,6 @@ package basic import ( "encoding/json" "math" - "os" "testing" "time" ) @@ -21,10 +20,11 @@ type venusEventBaseline struct { func loadVenusEventBaseline(t *testing.T) venusEventBaseline { t.Helper() - data, err := os.ReadFile("testdata/venus_event_baseline.json") - if err != nil { - t.Fatal(err) + var named []namedEventFunc + for _, c := range venusEventCases() { + named = append(named, namedEventFunc{c.name, c.fn}) } + data := eventBaselineData(t, "testdata/venus_event_baseline.json", named) var baseline venusEventBaseline if err := json.Unmarshal(data, &baseline); err != nil { diff --git a/basic/venus_events.go b/basic/venus_events.go index 31d5a4a..07ad89c 100644 --- a/basic/venus_events.go +++ b/basic/venus_events.go @@ -9,16 +9,16 @@ import ( const ( VENUS_S_PERIOD = 1 / ((1 / 224.701) - (1 / 365.256363004)) venusEventSearchN = 16 - // venusConjunctionSameInstantDegrees 「同刻」快速路径的视黄经差触发阈值(度)。 - // 实测截断级数(n=16)与全项级数的视黄经差最大 0.0031 度:若阈值小于它, - // 「查询落在合前几秒~几分钟」时截断级数在起点处符号相反,方向扫描会直接跨过这次合, - // 返回下一个会合周期的合(实测 1950–2055 年有 17~29 个查询点命中)。 - // 0.05 度约合 38 分钟,留有 16 倍余量;侧向判定仍用 0.1 秒口径,语义不变。 + // venusConjunctionSameInstantDegrees 「同刻」快速路径的视黄经差触发阈值(度):必须明显大于 + // 截断级数与全项级数的最大视黄经差,否则「查询落在合前几秒」时起点符号相反,方向扫描会跨过这次合。 + // 0.05 度约合 38 分钟;侧向判定仍用 0.1 秒口径,语义不变。 venusConjunctionSameInstantDegrees = 5.0e-2 ) -func venusSunLongitudeDelta(jde float64) float64 { - sub := Limit360(VenusApparentLo(jde) - HSunApparentLo(jde)) +func venusSunLongitudeDelta(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(VenusApparentLoWithInstance(jde, inst) - HSunApparentLoWithInstance(jde, inst)) if sub > 180 { sub -= 360 } @@ -28,8 +28,10 @@ func venusSunLongitudeDelta(jde float64) float64 { return sub } -func venusSunLongitudeDeltaN(jde float64, n int) float64 { - sub := Limit360(VenusApparentLoN(jde, n) - HSunApparentLoN(jde, n)) +func venusSunLongitudeDeltaN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := Limit360(VenusApparentLoNWithInstance(jde, n, inst) - HSunApparentLoNWithInstance(jde, n, inst)) if sub > 180 { sub -= 360 } @@ -39,8 +41,10 @@ func venusSunLongitudeDeltaN(jde float64, n int) float64 { return sub } -func venusRADerivative(jde, val float64) float64 { - sub := VenusApparentRa(jde+val) - VenusApparentRa(jde-val) +func venusRADerivative(jde, val float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + sub := VenusApparentRaWithInstance(jde+val, inst) - VenusApparentRaWithInstance(jde-val, inst) if sub > 180 { sub -= 360 } @@ -50,32 +54,40 @@ func venusRADerivative(jde, val float64) float64 { return sub / (2 * val) } -func venusRAContinuousForMax(jde float64) float64 { - ra := VenusApparentRa(jde) +func venusRAContinuousForMax(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + ra := VenusApparentRaWithInstance(jde, inst) if ra < 180 { return ra + 360 } return ra } -func venusRAContinuousForMaxN(jde float64, n int) float64 { - ra := VenusApparentRaN(jde, n) +func venusRAContinuousForMaxN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + ra := VenusApparentRaNWithInstance(jde, n, inst) if ra < 180 { return ra + 360 } return ra } -func venusRAContinuousForMin(jde float64) float64 { - ra := VenusApparentRa(jde) +func venusRAContinuousForMin(jde float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + ra := VenusApparentRaWithInstance(jde, inst) if ra > 180 { return ra - 360 } return ra } -func venusRAContinuousForMinN(jde float64, n int) float64 { - ra := VenusApparentRaN(jde, n) +func venusRAContinuousForMinN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + ra := VenusApparentRaNWithInstance(jde, n, inst) if ra > 180 { return ra - 360 } @@ -124,14 +136,18 @@ func venusRAExtremumRefine(seed, start, end, step float64, fn func(float64) floa return bestJD } -func venusSunElongationN(jde float64, n int) float64 { - lo1, bo1 := VenusApparentLoBoN(jde, n) - lo2 := HSunApparentLoN(jde, n) - bo2 := HSunTrueBoN(jde, n) +func venusSunElongationN(jde float64, n int, instances ...*Instance) float64 { + inst := instanceValue(instances) + + lo1, bo1 := VenusApparentLoBoNWithInstance(jde, n, inst) + lo2 := HSunApparentLoNWithInstance(jde, n, inst) + bo2 := HSunTrueBoNWithInstance(jde, n, inst) return StarAngularSeparation(lo1, bo1, lo2, bo2) } -func venusConjunction(jde float64, next uint8) float64 { +func venusConjunction(jde float64, next uint8, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(jde) { return math.NaN() } @@ -141,9 +157,9 @@ func venusConjunction(jde float64, next uint8) float64 { direction = 1 } left := queryTT - leftVal := venusSunLongitudeDeltaN(left, venusEventSearchN) - if math.Abs(venusSunLongitudeDelta(queryTT)) <= venusConjunctionSameInstantDegrees { - if exact, ok := venusConjunctionRefine(left, 1.0); ok { + leftVal := venusSunLongitudeDeltaN(left, venusEventSearchN, inst) + if math.Abs(venusSunLongitudeDelta(queryTT, inst)) <= venusConjunctionSameInstantDegrees { + if exact, ok := venusConjunctionRefine(left, 1.0, inst); ok { eventUT := TT2UTC(exact) if next == 0 && eventUTQueryBeforeOrEqual(eventUT, queryTT) { return eventUT @@ -156,16 +172,16 @@ func venusConjunction(jde float64, next uint8) float64 { const step = 8.0 for i := 0; i < 80; i++ { right := queryTT + direction*step*float64(i+1) - rightVal := venusSunLongitudeDeltaN(right, venusEventSearchN) + rightVal := venusSunLongitudeDeltaN(right, venusEventSearchN, inst) if leftVal == 0 || rightVal == 0 || leftVal*rightVal <= 0 { center := (left + right) / 2.0 halfWindow := math.Abs(right-left) / 2.0 - if exact, ok := venusConjunctionRefine(center, halfWindow); ok { + if exact, ok := venusConjunctionRefine(center, halfWindow, inst); ok { return TT2UTC(exact) } // 截断级数在根附近的符号可能与全项不一致(查询几乎正好落在合上), // 此时改用全项级数做方向扫描兜底,而不是直接有界失败。 - return venusConjunctionFullDirectionalScan(queryTT, direction) + return venusConjunctionFullDirectionalScan(queryTT, direction, inst) } left = right leftVal = rightVal @@ -179,25 +195,27 @@ func venusConjunction(jde float64, next uint8) float64 { } // venusConjunctionFullDirectionalScan 全项级数的方向扫描(截断括号未被确认时的兜底)。 -func venusConjunctionFullDirectionalScan(queryTT, direction float64) float64 { +func venusConjunctionFullDirectionalScan(queryTT, direction float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + const ( step = 8.0 maxSteps = 80 ) left := queryTT - leftVal := venusSunLongitudeDelta(left) + leftVal := venusSunLongitudeDelta(left, inst) if !isFiniteFloat(leftVal) { return math.NaN() } for i := 0; i < maxSteps; i++ { right := queryTT + direction*step*float64(i+1) - rightVal := venusSunLongitudeDelta(right) + rightVal := venusSunLongitudeDelta(right, inst) if !isFiniteFloat(rightVal) { return math.NaN() } if leftVal == 0 || rightVal == 0 || leftVal*rightVal < 0 { center := (left + right) / 2.0 - if exact, ok := venusConjunctionRefine(center, math.Abs(right-left)/2.0); ok { + if exact, ok := venusConjunctionRefine(center, math.Abs(right-left)/2.0, inst); ok { return TT2UTC(exact) } return math.NaN() @@ -208,121 +226,145 @@ func venusConjunctionFullDirectionalScan(queryTT, direction float64) float64 { return math.NaN() } -func venusConjunctionRefine(seed, halfWindow float64) (float64, bool) { +func venusConjunctionRefine(seed, halfWindow float64, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + leftJDE := seed - halfWindow centerJDE := seed rightJDE := seed + halfWindow - leftVal := venusSunLongitudeDelta(leftJDE) - centerVal := venusSunLongitudeDelta(centerJDE) - rightVal := venusSunLongitudeDelta(rightJDE) + leftVal := venusSunLongitudeDelta(leftJDE, inst) + centerVal := venusSunLongitudeDelta(centerJDE, inst) + rightVal := venusSunLongitudeDelta(rightJDE, inst) if !isFiniteFloat(leftVal) || !isFiniteFloat(centerVal) || !isFiniteFloat(rightVal) { return math.NaN(), false } if _, _, _, _, ok := eventZeroBracket(leftJDE, leftVal, centerJDE, centerVal, rightJDE, rightVal); !ok { return math.NaN(), false } - return eventZeroRefine(seed, halfWindow, 0.000005, venusSunLongitudeDelta), true + return eventZeroRefine(seed, halfWindow, 0.000005, func(jde float64) float64 { return venusSunLongitudeDelta(jde, inst) }), true } -func venusConjunctionTypeAt(eventUT float64) bool { - return EarthVenusAway(eventUT) <= EarthAway(eventUT) +func venusConjunctionTypeAt(eventUT float64, instances ...*Instance) bool { + inst := instanceValue(instances) + + return EarthVenusAwayWithInstance(eventUT, inst) <= EarthAwayWithInstance(eventUT, inst) } -func nextVenusTypedConjunctionFromEvent(jde float64, inferior bool) float64 { - date := NextVenusConjunctionStrict(jde) - if venusConjunctionTypeAt(date) == inferior { +func nextVenusTypedConjunctionFromEvent(jde float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + date := NextVenusConjunctionStrictWithInstance(jde, inst) + if venusConjunctionTypeAt(date, inst) == inferior { return date } - return NextVenusConjunctionStrict(eventUTNextQueryTT(date)) + return NextVenusConjunctionStrictWithInstance(eventUTNextQueryTT(date), inst) } -func lastVenusTypedConjunctionFromEvent(jde float64, inferior bool) float64 { - date := LastVenusConjunctionStrict(jde) - if venusConjunctionTypeAt(date) == inferior { +func lastVenusTypedConjunctionFromEvent(jde float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + date := LastVenusConjunctionStrictWithInstance(jde, inst) + if venusConjunctionTypeAt(date, inst) == inferior { return date } - return LastVenusConjunctionStrict(eventUTLastQueryTT(date)) + return LastVenusConjunctionStrictWithInstance(eventUTLastQueryTT(date), inst) } -func LastVenusConjunction(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusConjunctionStrict, NextVenusConjunctionStrict) +// LastVenusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusConjunctionStrictWithInstance(jde, inst) }) } -func NextVenusConjunction(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusConjunctionStrict, NextVenusConjunctionStrict) +// NextVenusConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusConjunctionWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusConjunctionStrictWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusConjunctionStrictWithInstance(jde, inst) }) } -func LastVenusConjunctionStrict(jde float64) float64 { - return venusConjunction(jde, 0) +// LastVenusConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusConjunctionStrictWithInstance(jde float64, inst *Instance) float64 { + return venusConjunction(jde, 0, inst) } -func NextVenusConjunctionStrict(jde float64) float64 { - return venusConjunction(jde, 1) +// NextVenusConjunctionStrictWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusConjunctionStrictWithInstance(jde float64, inst *Instance) float64 { + return venusConjunction(jde, 1, inst) } -func nextVenusTypedConjunction(jde float64, inferior bool) float64 { - return nextVenusTypedConjunctionFromEvent(jde, inferior) +func nextVenusTypedConjunction(jde float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return nextVenusTypedConjunctionFromEvent(jde, inferior, inst) } -func lastVenusTypedConjunction(jde float64, inferior bool) float64 { - return lastVenusTypedConjunctionFromEvent(jde, inferior) +func lastVenusTypedConjunction(jde float64, inferior bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return lastVenusTypedConjunctionFromEvent(jde, inferior, inst) } -func NextVenusInferiorConjunction(jde float64) float64 { - return nextVenusTypedConjunction(jde, true) +// NextVenusInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + return nextVenusTypedConjunction(jde, true, inst) } -func NextVenusSuperiorConjunction(jde float64) float64 { - return nextVenusTypedConjunction(jde, false) +// NextVenusSuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusSuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + return nextVenusTypedConjunction(jde, false, inst) } -func LastVenusInferiorConjunction(jde float64) float64 { - return lastVenusTypedConjunction(jde, true) +// LastVenusInferiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusInferiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + return lastVenusTypedConjunction(jde, true, inst) } -func LastVenusSuperiorConjunction(jde float64) float64 { - return lastVenusTypedConjunction(jde, false) +// LastVenusSuperiorConjunctionWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusSuperiorConjunctionWithInstance(jde float64, inst *Instance) float64 { + return lastVenusTypedConjunction(jde, false, inst) } -func NextVenusRetrograde(jde float64) float64 { - p2r := NextVenusProgradeToRetrograde(jde) - r2p := NextVenusRetrogradeToPrograde(jde) +// NextVenusRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusRetrogradeWithInstance(jde float64, inst *Instance) float64 { + p2r := NextVenusProgradeToRetrogradeWithInstance(jde, inst) + r2p := NextVenusRetrogradeToProgradeWithInstance(jde, inst) if sameEventJD(p2r, r2p) { return p2r } return earliestFiniteEventUT(p2r, r2p) } -func LastVenusRetrograde(jde float64) float64 { - p2r := LastVenusProgradeToRetrograde(jde) - r2p := LastVenusRetrogradeToPrograde(jde) +// LastVenusRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusRetrogradeWithInstance(jde float64, inst *Instance) float64 { + p2r := LastVenusProgradeToRetrogradeWithInstance(jde, inst) + r2p := LastVenusRetrogradeToProgradeWithInstance(jde, inst) if sameEventJD(p2r, r2p) { return p2r } return latestFiniteEventUT(p2r, r2p) } -func venusStationInWindow(start, end float64, progradeToRetrograde bool) float64 { +func venusStationInWindow(start, end float64, progradeToRetrograde bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + var best float64 if progradeToRetrograde { guess := scanWindowForMax(start, end, 2.0, func(jd float64) float64 { - return venusRAContinuousForMaxN(jd, venusEventSearchN) + return venusRAContinuousForMaxN(jd, venusEventSearchN, inst) }) best = venusRAExtremumRefine(guess, start, end, 1.0, func(jd float64) float64 { - return venusRAContinuousForMax(jd) + return venusRAContinuousForMax(jd, inst) }) } else { guess := scanWindowForMax(start, end, 2.0, func(jd float64) float64 { - return -venusRAContinuousForMinN(jd, venusEventSearchN) + return -venusRAContinuousForMinN(jd, venusEventSearchN, inst) }) best = venusRAExtremumRefine(guess, start, end, 1.0, func(jd float64) float64 { - return -venusRAContinuousForMin(jd) + return -venusRAContinuousForMin(jd, inst) }) } - // 抛物顶点法 8 次迭代后仍有秒级残差(实测最差 2.4 s),会让「查询落在站前 1 秒」 - // 这类边界查询跳到下一个会合周期。这里再用 RA 变化率的零点做一次割线抛光, - // 把站时刻精度压到亚秒级;抛光失败(括号内无异号)时保留顶点法结果。 - if polished, ok := venusStationPolish(best, progradeToRetrograde); ok { + // 抛物顶点法仍有秒级残差,会让「查询落在站前 1 秒」这类边界查询跳到下一个会合周期; + // 这里再用 RA 变化率的零点做一次割线抛光,把站时刻精度压到亚秒级, + // 抛光失败(括号内无异号)时保留顶点法结果。 + if polished, ok := venusStationPolish(best, progradeToRetrograde, inst); ok { best = polished } return TT2UTC(best) @@ -330,8 +372,10 @@ func venusStationInWindow(start, end float64, progradeToRetrograde bool) float64 // venusStationPolish 在抛物顶点结果附近求 RA 变化率的零点。 // progradeToRetrograde=true 时 RA 变化率由正转负(极大值),否则由负转正(极小值)。 -func venusStationPolish(seed float64, progradeToRetrograde bool) (float64, bool) { - rate := func(jd float64) float64 { return venusRADerivative(jd, stationDerivativeStepDay) } +func venusStationPolish(seed float64, progradeToRetrograde bool, instances ...*Instance) (float64, bool) { + inst := instanceValue(instances) + + rate := func(jd float64) float64 { return venusRADerivative(jd, stationDerivativeStepDay, inst) } for _, halfWindow := range []float64{120.0 / 86400.0, 600.0 / 86400.0} { leftJD := seed - halfWindow rightJD := seed + halfWindow @@ -361,319 +405,365 @@ func venusStationPolish(seed float64, progradeToRetrograde bool) (float64, bool) return math.NaN(), false } -func venusProgradeToRetrogradeAroundInferior(inferior float64) float64 { - return venusStationInWindow(inferior-30.0, inferior-14.0, true) +func venusProgradeToRetrogradeAroundInferior(inferior float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return venusStationInWindow(inferior-30.0, inferior-14.0, true, inst) } -func venusRetrogradeToProgradeAroundInferior(inferior float64) float64 { - return venusStationInWindow(inferior+14.0, inferior+24.0, false) +func venusRetrogradeToProgradeAroundInferior(inferior float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + + return venusStationInWindow(inferior+14.0, inferior+24.0, false, inst) } -func NextVenusProgradeToRetrograde(jde float64) float64 { - inferior := NextVenusInferiorConjunction(jde) +// NextVenusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { + inferior := NextVenusInferiorConjunctionWithInstance(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusProgradeToRetrogradeAroundInferior(inferior) + date := venusProgradeToRetrogradeAroundInferior(inferior, inst) if !isFiniteFloat(date) { return math.NaN() } if stationUTQueryAfterOrEqual(date, jde) { return date } - inferior = NextVenusInferiorConjunction(eventUTNextQueryTT(inferior)) + inferior = NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst) } return math.NaN() } -func NextVenusRetrogradeToPrograde(jde float64) float64 { - inferior := LastVenusInferiorConjunction(jde) +// NextVenusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { + inferior := LastVenusInferiorConjunctionWithInstance(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusRetrogradeToProgradeAroundInferior(inferior) + date := venusRetrogradeToProgradeAroundInferior(inferior, inst) if !isFiniteFloat(date) { return math.NaN() } if stationUTQueryAfterOrEqual(date, jde) { return date } - inferior = NextVenusInferiorConjunction(eventUTNextQueryTT(inferior)) + inferior = NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(inferior), inst) } return math.NaN() } -func LastVenusProgradeToRetrograde(jde float64) float64 { - inferior := NextVenusInferiorConjunction(jde) +// LastVenusProgradeToRetrogradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusProgradeToRetrogradeWithInstance(jde float64, inst *Instance) float64 { + inferior := NextVenusInferiorConjunctionWithInstance(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusProgradeToRetrogradeAroundInferior(inferior) + date := venusProgradeToRetrogradeAroundInferior(inferior, inst) if !isFiniteFloat(date) { return math.NaN() } if stationUTQueryBeforeOrEqual(date, jde) { return date } - inferior = LastVenusInferiorConjunction(eventUTLastQueryTT(inferior)) + inferior = LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) } return math.NaN() } -func LastVenusRetrogradeToPrograde(jde float64) float64 { - inferior := LastVenusInferiorConjunction(jde) +// LastVenusRetrogradeToProgradeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusRetrogradeToProgradeWithInstance(jde float64, inst *Instance) float64 { + inferior := LastVenusInferiorConjunctionWithInstance(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusRetrogradeToProgradeAroundInferior(inferior) + date := venusRetrogradeToProgradeAroundInferior(inferior, inst) if !isFiniteFloat(date) { return math.NaN() } if stationUTQueryBeforeOrEqual(date, jde) { return date } - inferior = LastVenusInferiorConjunction(eventUTLastQueryTT(inferior)) + inferior = LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) } return math.NaN() } -func VenusSunElongation(jde float64) float64 { - lo1, bo1 := VenusApparentLoBo(jde) - lo2 := HSunApparentLo(jde) - bo2 := HSunTrueBo(jde) +// VenusSunElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func VenusSunElongationWithInstance(jde float64, inst *Instance) float64 { + lo1, bo1 := VenusApparentLoBoWithInstance(jde, inst) + lo2 := HSunApparentLoWithInstance(jde, inst) + bo2 := HSunTrueBoWithInstance(jde, inst) return StarAngularSeparation(lo1, bo1, lo2, bo2) } // venusGreatestElongationInWindow 求窗口内大距:目标是公开 VenusSunElongation 的极大(视距角), // 而不是忽略光行差/视位置修正的真距角,否则返回的时刻不是调用方能量到的那个极值。 // 窗口两端是世界时,目标函数收力学时,因此逐次换算。 -func venusGreatestElongationInWindow(start, end float64) float64 { +func venusGreatestElongationInWindow(start, end float64, instances ...*Instance) float64 { + inst := instanceValue(instances) + return maximizeInWindow(start, end, 5.0, func(utJD float64) float64 { - return venusSunElongationN(UTC2TT(utJD), venusEventSearchN) + return venusSunElongationN(UTC2TT(utJD), venusEventSearchN, inst) }, func(utJD float64) float64 { - return VenusSunElongation(UTC2TT(utJD)) + return VenusSunElongationWithInstance(UTC2TT(utJD), inst) }) } -func venusEastElongationWindowEndingAt(inferior float64) (float64, float64) { - lastSuperior := LastVenusSuperiorConjunction(eventUTLastQueryTT(inferior)) +func venusEastElongationWindowEndingAt(inferior float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + lastSuperior := LastVenusSuperiorConjunctionWithInstance(eventUTLastQueryTT(inferior), inst) return lastSuperior + innerEventWindowPadding, inferior - innerEventWindowPadding } -func venusWestElongationWindowEndingAt(superior float64) (float64, float64) { - lastInferior := LastVenusInferiorConjunction(eventUTLastQueryTT(superior)) +func venusWestElongationWindowEndingAt(superior float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + lastInferior := LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(superior), inst) return lastInferior + innerEventWindowPadding, superior - innerEventWindowPadding } -func venusEastElongationWindowContaining(jde float64) (float64, float64) { - nextInferior := NextVenusInferiorConjunction(jde) - start, end := venusEastElongationWindowEndingAt(nextInferior) +func venusEastElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + nextInferior := NextVenusInferiorConjunctionWithInstance(jde, inst) + start, end := venusEastElongationWindowEndingAt(nextInferior, inst) if eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) { return start, end } - currentInferior := LastVenusInferiorConjunction(jde) - return venusEastElongationWindowEndingAt(currentInferior) + currentInferior := LastVenusInferiorConjunctionWithInstance(jde, inst) + return venusEastElongationWindowEndingAt(currentInferior, inst) } -func venusWestElongationWindowContaining(jde float64) (float64, float64) { - nextSuperior := NextVenusSuperiorConjunction(jde) - start, end := venusWestElongationWindowEndingAt(nextSuperior) +func venusWestElongationWindowContaining(jde float64, instances ...*Instance) (float64, float64) { + inst := instanceValue(instances) + + nextSuperior := NextVenusSuperiorConjunctionWithInstance(jde, inst) + start, end := venusWestElongationWindowEndingAt(nextSuperior, inst) if eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) { return start, end } - currentSuperior := LastVenusSuperiorConjunction(jde) - return venusWestElongationWindowEndingAt(currentSuperior) + currentSuperior := LastVenusSuperiorConjunctionWithInstance(jde, inst) + return venusWestElongationWindowEndingAt(currentSuperior, inst) } -func nextVenusGreatestElongationTyped(jde float64, east bool) float64 { +func nextVenusGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(jde) { return math.NaN() } if east { - start, windowEnd := venusEastElongationWindowContaining(jde) + start, windowEnd := venusEastElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusGreatestElongationInWindow(start, windowEnd) + date := venusGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } - nextInferior := NextVenusInferiorConjunction(eventUTNextQueryTT(windowEnd)) - start, windowEnd = venusEastElongationWindowEndingAt(nextInferior) + nextInferior := NextVenusInferiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst) + start, windowEnd = venusEastElongationWindowEndingAt(nextInferior, inst) } return math.NaN() } - start, windowEnd := venusWestElongationWindowContaining(jde) + start, windowEnd := venusWestElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusGreatestElongationInWindow(start, windowEnd) + date := venusGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryAfterOrEqual(date, jde) { return date } - nextSuperior := NextVenusSuperiorConjunction(eventUTNextQueryTT(windowEnd)) - start, windowEnd = venusWestElongationWindowEndingAt(nextSuperior) + nextSuperior := NextVenusSuperiorConjunctionWithInstance(eventUTNextQueryTT(windowEnd), inst) + start, windowEnd = venusWestElongationWindowEndingAt(nextSuperior, inst) } return math.NaN() } -func lastVenusGreatestElongationTyped(jde float64, east bool) float64 { +func lastVenusGreatestElongationTyped(jde float64, east bool, instances ...*Instance) float64 { + inst := instanceValue(instances) + if !isFiniteFloat(jde) { return math.NaN() } if east { - start, windowEnd := venusEastElongationWindowContaining(jde) + start, windowEnd := venusEastElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusGreatestElongationInWindow(start, windowEnd) + date := venusGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } - prevInferior := LastVenusInferiorConjunction(eventUTLastQueryTT(start)) - start, windowEnd = venusEastElongationWindowEndingAt(prevInferior) + prevInferior := LastVenusInferiorConjunctionWithInstance(eventUTLastQueryTT(start), inst) + start, windowEnd = venusEastElongationWindowEndingAt(prevInferior, inst) } return math.NaN() } - start, windowEnd := venusWestElongationWindowContaining(jde) + start, windowEnd := venusWestElongationWindowContaining(jde, inst) for i := 0; i < eventDirectionalSearchIterations; i++ { - date := venusGreatestElongationInWindow(start, windowEnd) + date := venusGreatestElongationInWindow(start, windowEnd, inst) if !isFiniteFloat(date) { return math.NaN() } if eventUTQueryBeforeOrEqual(date, jde) { return date } - prevSuperior := LastVenusSuperiorConjunction(eventUTLastQueryTT(start)) - start, windowEnd = venusWestElongationWindowEndingAt(prevSuperior) + prevSuperior := LastVenusSuperiorConjunctionWithInstance(eventUTLastQueryTT(start), inst) + start, windowEnd = venusWestElongationWindowEndingAt(prevSuperior, inst) } return math.NaN() } // venusElongationWindowAt 返回包含 jde 的该侧大距窗口;查询落在合的 4 秒内边距里时两侧都不包含。 -func venusElongationWindowAt(jde float64, east bool) (float64, float64, bool) { +func venusElongationWindowAt(jde float64, east bool, instances ...*Instance) (float64, float64, bool) { + inst := instanceValue(instances) + if east { - start, end := venusEastElongationWindowEndingAt(NextVenusInferiorConjunction(jde)) + start, end := venusEastElongationWindowEndingAt(NextVenusInferiorConjunctionWithInstance(jde, inst), inst) return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) } - start, end := venusWestElongationWindowEndingAt(NextVenusSuperiorConjunction(jde)) + start, end := venusWestElongationWindowEndingAt(NextVenusSuperiorConjunctionWithInstance(jde, inst), inst) return start, end, eventUTQueryBeforeOrEqual(start, jde) && eventUTQueryAfterOrEqual(end, jde) } -// 无东西侧参数的 Next/Last 先只看查询所在窗口那一侧:该侧大距未过就是答案,已过则另一侧紧接着的 -// 下一个才是答案,因此只有在查询贴住合(两侧窗口都不含)时才退化为两侧都算。赤经差在合附近会提前 -// 变号,不能用它定窗口。 -func NextVenusGreatestElongation(jde float64) float64 { +// NextVenusGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } + // 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。 for _, east := range [2]bool{true, false} { - start, end, ok := venusElongationWindowAt(jde, east) + start, end, ok := venusElongationWindowAt(jde, east, inst) if !ok { continue } - if date := venusGreatestElongationInWindow(start, end); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { + if date := venusGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { return date } - if date := nextVenusGreatestElongationTyped(jde, !east); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { + if date := nextVenusGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryAfterOrEqual(date, jde) { return date } break } - return earliestFiniteEventUT(nextVenusGreatestElongationTyped(jde, true), nextVenusGreatestElongationTyped(jde, false)) + return earliestFiniteEventUT(nextVenusGreatestElongationTyped(jde, true, inst), nextVenusGreatestElongationTyped(jde, false, inst)) } -func LastVenusGreatestElongation(jde float64) float64 { +// LastVenusGreatestElongationWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationWithInstance(jde float64, inst *Instance) float64 { if !isFiniteFloat(jde) { return math.NaN() } + // 先只看查询所在一侧的窗口,未过即答案,已过则取另一侧的下一个;赤经差在合附近会提前变号,不能用来定窗口。 for _, east := range [2]bool{true, false} { - start, end, ok := venusElongationWindowAt(jde, east) + start, end, ok := venusElongationWindowAt(jde, east, inst) if !ok { continue } - if date := venusGreatestElongationInWindow(start, end); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { + if date := venusGreatestElongationInWindow(start, end, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { return date } - if date := lastVenusGreatestElongationTyped(jde, !east); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { + if date := lastVenusGreatestElongationTyped(jde, !east, inst); isFiniteFloat(date) && eventUTQueryBeforeOrEqual(date, jde) { return date } break } - return latestFiniteEventUT(lastVenusGreatestElongationTyped(jde, true), lastVenusGreatestElongationTyped(jde, false)) + return latestFiniteEventUT(lastVenusGreatestElongationTyped(jde, true, inst), lastVenusGreatestElongationTyped(jde, false, inst)) } -func LastVenusInferiorConjunctionInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusInferiorConjunction, NextVenusInferiorConjunction) +// LastVenusInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusInferiorConjunctionWithInstance(jde, inst) }) } -func NextVenusInferiorConjunctionInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusInferiorConjunction, NextVenusInferiorConjunction) +// NextVenusInferiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusInferiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusInferiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusInferiorConjunctionWithInstance(jde, inst) }) } -func LastVenusSuperiorConjunctionInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusSuperiorConjunction, NextVenusSuperiorConjunction) +// LastVenusSuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusSuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusSuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusSuperiorConjunctionWithInstance(jde, inst) }) } -func NextVenusSuperiorConjunctionInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusSuperiorConjunction, NextVenusSuperiorConjunction) +// NextVenusSuperiorConjunctionInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusSuperiorConjunctionInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusSuperiorConjunctionWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusSuperiorConjunctionWithInstance(jde, inst) }) } -func LastVenusRetrogradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusRetrograde, NextVenusRetrograde) +// LastVenusRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeWithInstance(jde, inst) }) } -func NextVenusRetrogradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusRetrograde, NextVenusRetrograde) +// NextVenusRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeWithInstance(jde, inst) }) } -func LastVenusProgradeToRetrogradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusProgradeToRetrograde, NextVenusProgradeToRetrograde) +// LastVenusProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusProgradeToRetrogradeWithInstance(jde, inst) }) } -func NextVenusProgradeToRetrogradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusProgradeToRetrograde, NextVenusProgradeToRetrograde) +// NextVenusProgradeToRetrogradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusProgradeToRetrogradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusProgradeToRetrogradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusProgradeToRetrogradeWithInstance(jde, inst) }) } -func LastVenusRetrogradeToProgradeInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusRetrogradeToPrograde, NextVenusRetrogradeToPrograde) +// LastVenusRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeToProgradeWithInstance(jde, inst) }) } -func NextVenusRetrogradeToProgradeInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusRetrogradeToPrograde, NextVenusRetrogradeToPrograde) +// NextVenusRetrogradeToProgradeInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusRetrogradeToProgradeInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusRetrogradeToProgradeWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusRetrogradeToProgradeWithInstance(jde, inst) }) } -func LastVenusGreatestElongationInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusGreatestElongation, NextVenusGreatestElongation) +// LastVenusGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWithInstance(jde, inst) }) } -func NextVenusGreatestElongationInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusGreatestElongation, NextVenusGreatestElongation) +// NextVenusGreatestElongationInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWithInstance(jde, inst) }) } -func LastVenusGreatestElongationEastInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusGreatestElongationEast, NextVenusGreatestElongationEast) +// LastVenusGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationEastWithInstance(jde, inst) }) } -func NextVenusGreatestElongationEastInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusGreatestElongationEast, NextVenusGreatestElongationEast) +// NextVenusGreatestElongationEastInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationEastInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationEastWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationEastWithInstance(jde, inst) }) } -func LastVenusGreatestElongationWestInclusive(jde float64) float64 { - return inclusiveLastSimpleEvent(jde, LastVenusGreatestElongationWest, NextVenusGreatestElongationWest) +// LastVenusGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveLastSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWestWithInstance(jde, inst) }) } -func NextVenusGreatestElongationWestInclusive(jde float64) float64 { - return inclusiveNextSimpleEvent(jde, LastVenusGreatestElongationWest, NextVenusGreatestElongationWest) +// NextVenusGreatestElongationWestInclusiveWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationWestInclusiveWithInstance(jde float64, inst *Instance) float64 { + return inclusiveNextSimpleEvent(jde, func(jde float64) float64 { return LastVenusGreatestElongationWestWithInstance(jde, inst) }, func(jde float64) float64 { return NextVenusGreatestElongationWestWithInstance(jde, inst) }) } -func NextVenusGreatestElongationEast(jde float64) float64 { - return nextVenusGreatestElongationTyped(jde, true) +// NextVenusGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 { + return nextVenusGreatestElongationTyped(jde, true, inst) } -func NextVenusGreatestElongationWest(jde float64) float64 { - return nextVenusGreatestElongationTyped(jde, false) +// NextVenusGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func NextVenusGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 { + return nextVenusGreatestElongationTyped(jde, false, inst) } -func LastVenusGreatestElongationEast(jde float64) float64 { - return lastVenusGreatestElongationTyped(jde, true) +// LastVenusGreatestElongationEastWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationEastWithInstance(jde float64, inst *Instance) float64 { + return lastVenusGreatestElongationTyped(jde, true, inst) } -func LastVenusGreatestElongationWest(jde float64) float64 { - return lastVenusGreatestElongationTyped(jde, false) +// LastVenusGreatestElongationWestWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model. +func LastVenusGreatestElongationWestWithInstance(jde float64, inst *Instance) float64 { + return lastVenusGreatestElongationTyped(jde, false, inst) } diff --git a/calendar/chinese.go b/calendar/chinese.go index a1d742d..c10c1c2 100644 --- a/calendar/chinese.go +++ b/calendar/chinese.go @@ -14,8 +14,10 @@ import ( var tiangan = []string{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"} var dizhi = []string{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"} +var cstLocation = time.FixedZone("CST", 8*3600) + func getCst() *time.Location { - return time.FixedZone("CST", 8*3600) + return cstLocation } const ( @@ -616,8 +618,9 @@ func WuHou(year, term int) time.Time { } func rapidLunarModern(year, month, day int) (int, int, int, bool, string) { - var upper = []uint16{32274, 52242, 41001, 30036, 49204, 36918, 25882, 46101, 34854, 22674, 43026, 31145, 51241, 38964, 26997, 47149, 36885, 23717, 44069, 34258, 53266, 41001, 29036, 49178, 37915, 24875, 46090, 34853, 23698, 43026, 31129, 50229, 38970, 26971, 47126, 36874, 24804, 44068, 32242, 52274, 41013, 28086, 48173, 37909, 25898, 46089, 34852, 22706, 43050, 30189, 50203, 38957, 27989, 47123, 35881, 24788, 45076, 32298, 51258, 40986, 29099, 48170, 37906, 25897, 46121, 34836, 21754, 42038, 31190, 50197, 38949, 27986, 48146, 35881, 23860, 44084, 32309, 51245, 39981, 29093, 49189, 37906, 25897, 46121, 35500, 53274, 42011, 30123, 50218, 38949, 27986, 48146, 36889, 23770, 43066, 32282, 52246, 39978, 29028, 49188, 37938, 24885, 45109, 33846, 22678, 42005, 30186, 51209, 39972, 26994, 47146, 35885, 23853, 43051, 32341, 52242, 41001, 29076, 49172, 37930, 25885, 45082, 33835, 22675, 43026, 30121, 50217, 38964, 27002, 46133, 35862, 23770, 44069, 32466, 52242, 41001, 29108, 48180, 36917, 24950, 45101, 33813, 22674, 43026, 31209, 50217, 38954, 26989, 47131, 34859, 23765, 44068, 34322, 52242, 40985, 29082, 48186, 36890, 24874, 45098, 34852, 21746, 42034, 30197, 50229, 37942, 26966, 47125, 35881, 23828, 44052, 32298, 52266, 39981, 28077, 48171, 37909, 24873, 45097, 34836, 22762, 42010, 30172, 50202, 38955, 26963, 47122, 35881, 24852, 43060, 31290, 51253, 39990, 28058, 48149, 37906, 25897, 45097, 33844, 21686, 42037, 30198, 50221, 39957, 29010, 48146, 36905, 24884, 45098, 32365, 52251, 41003, 30101, 49188, 38930, 26905, 47125, 34842, 22747, 43034, 31210, 50218, 39956, 28018, 48170, 35893, 23866, 44086, 34518, 52245, 41001, 30100, 50196, 37930, 25973, 46124, 34861, 22677, 43029, 31209, 51241, 39956, 28010, 48154, 36892, 23853, 44058, 34507, 53266, 41001, 30100, 49204, 37946, 25946, 45110, 34838, 23754, 43018, 31208, 51240, 39988, 27061, 47149, 35893, 24854, 44053, 34442, 53265, 42024, 29098, 49194, 37933, 25965, 45099, 34837, 23753, 44049, 31192, 51220, 39962, 28059, 47126, 35882, 24852, 45074, 31785, 21652, 41012, 29114, 48181, 37910, 25962, 46121, 34834, 22761, 43049, 31220, 50220, 38957, 28053, 48139, 36901, 25874, 46098, 35433, 53273, 42010, 30125, 50202, 38922, 26917, 47140, 36882, 23769, 43065, 31226, 51254, 39958, 29002, 49162, 37924, 24882, 45106, 34421, 53293, 41005, 30165, 50197, 39945, 26980, 47140, 35882, 23797, 43053, 31277, 51243, 40981, 29001, 49161, 37908, 25898, 45082, 34523, 53270, 42026, 30098, 50194, 38953, 27988, 46132, 34874, 23770, 44054, 31210, 51237, 40978, 29097, 48169, 36916, 24950, 45101, 31797, 21605, 42021, 31186, 50194, 38953, 26988, 47130, 34859, 22765, 44042, 32293, 51236, 40978, 29081, 48185, 35898, 24859, 45078, 34826, 21669, 42020, 30130, 50226, 37941, 25974, 46125, 35861, 22762, 44041, 32228, 52260, 39978, 28077, 48157, 36909, 24853, 45075, 33833, 22676, 43028, 31146, 50234, 39962, 26987, 47146, 36882, 24809, 44073, 34260, 52276, 41014, 29082, 49173, 37926, 26898, 46098, 35497, 54313, 43060, 30197, 50221, 38957, 28005, 47141, 36882, 24809, 45097, 32300, 52250, 40987, 29101, 48170, 37925, 26898, 47122, 34841, 22746, 42042, 31195, 50198, 38954, 28004, 48164, 35890, 23861, 44085, 32310, 51245, 40981, 29098, 50185, 37924, 25970, 46122, 34861, 21614, 42029, 31189, 51218, 38953, 27988, 48148, 36906, 23837, 44058, 32299, 52266, 40978, 29097, 49193, 38932, 24954, 45110, 33846, 22682, 42021, 31186, 51218, 39977, 26996, 47156, 35893, 23862, 43053, 32405, 52261, 42002, 29097, 49193, 37932, 25901, 45083, 33835, 22677, 43044, 31122, 51218, 39961, 27994} - var lower = []uint8{218, 184, 92, 154, 152, 84, 170, 168, 180, 186, 184, 54, 52, 148, 82, 84, 168, 180, 108, 110, 108, 44, 150, 148, 80, 106, 216, 92, 94, 92, 44, 40, 148, 82, 180, 216, 220, 184, 90, 84, 40, 148, 84, 168, 182, 116, 180, 86, 84, 42, 40, 84, 106, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 152, 76, 84, 170, 168, 180, 186, 180, 52, 154, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 202, 176, 216, 218, 184, 88, 42, 40, 148, 170, 168, 182, 116, 180, 86, 84, 40, 84, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 106, 216, 92, 56, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 182, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148, 148, 200, 216, 184, 184, 92, 88, 42, 40, 148, 170, 168, 180, 186, 180, 86, 84, 40, 84, 84, 104, 116, 108, 172, 78, 76, 166, 168, 84, 106, 216, 92, 156, 88, 76, 72, 168, 212, 180, 184, 58, 52, 84, 74, 72, 164, 104, 116, 182, 108, 44, 150, 148, 74, 72, 88, 108, 220, 92, 46, 44, 148, 74, 168, 212, 180, 184, 92, 88, 40, 148, 84, 170, 168, 180, 186, 180, 52, 42, 168, 84, 170, 104, 116, 108, 172, 46, 44, 164, 84, 212, 106, 216, 92, 92, 88, 44, 164, 164, 212, 218, 184, 186, 180, 84, 42, 72, 164, 180, 108, 182, 108, 172, 86, 84, 40, 84, 84, 108, 110, 92, 174, 172, 84, 42, 168, 212, 218, 184, 92, 172, 168, 84, 84, 168, 212, 180, 184, 86, 52, 150, 164, 84, 170, 104, 116, 182, 108, 46, 44, 164, 82, 212, 216, 108, 220, 92, 44, 40, 148, 164, 212, 218, 184, 184, 90, 84, 42, 40, 164, 180, 108, 116, 182, 172, 84, 42, 40, 84, 84, 108, 110, 92, 172, 86, 84, 42, 168, 212, 218, 184, 92, 154, 152, 84, 170, 168, 180, 116, 184, 54, 52, 148, 74, 80, 168, 180, 108, 174, 108, 44, 150, 148, 80, 106, 216, 108, 220, 92, 44, 40, 148, 82, 180, 216, 92, 184, 90, 84, 40, 148, 148, 168, 182, 116, 182, 172, 84, 42, 40, 84, 170, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 154, 152, 84, 170, 168, 180, 186, 180, 54, 52, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 74, 176, 216, 218, 184, 88, 42, 40, 148, 84, 168, 182, 116, 180, 86, 84, 40, 148, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 212, 216, 92, 92, 152, 76, 72, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 108, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148} + // 1900~2026 为确定闰秒表结果,2027 起按现行时标政策生成。 + var upper = []uint16{32274, 52242, 41001, 30036, 49204, 36918, 25882, 46101, 34854, 22674, 43026, 31145, 51241, 38964, 26997, 47149, 36885, 23717, 44069, 34258, 53266, 41001, 29036, 49178, 37915, 24875, 46090, 34853, 23698, 43026, 31129, 50229, 38970, 26971, 47126, 36874, 24804, 44068, 32242, 52274, 41013, 28086, 48173, 37909, 25898, 46089, 34852, 22706, 43050, 30189, 50203, 38957, 27989, 47123, 35881, 24788, 45076, 32298, 51258, 40986, 29099, 48170, 37906, 25897, 46121, 34836, 21754, 42038, 31190, 50197, 38949, 27986, 48146, 35881, 23860, 44084, 32309, 51245, 39981, 29093, 49189, 37906, 25897, 46121, 35500, 53274, 42011, 30123, 50218, 38949, 27986, 48146, 36889, 23770, 43066, 32282, 52246, 39978, 29028, 49188, 37938, 24885, 45109, 33846, 22678, 42005, 30186, 51209, 39972, 26994, 47146, 35885, 23853, 43051, 32341, 52242, 41001, 29076, 49172, 37930, 25885, 45082, 33835, 22675, 43026, 30121, 50217, 38964, 27002, 46133, 35862, 23770, 44069, 32466, 52242, 41001, 29108, 48180, 36917, 24950, 45101, 33813, 22674, 43026, 31209, 50217, 38954, 26989, 47131, 34859, 23765, 44068, 34322, 52242, 40985, 29082, 48186, 36890, 24874, 45098, 34852, 21746, 42034, 30197, 50229, 37942, 26966, 47125, 35881, 23828, 44052, 32298, 52266, 39981, 28077, 48171, 37909, 24873, 45097, 34836, 22762, 42010, 30172, 50202, 38955, 26963, 47122, 35881, 24852, 43060, 31290, 51253, 39990, 28058, 48149, 37906, 25897, 45097, 33844, 21686, 42037, 30198, 50221, 39957, 29010, 48146, 36905, 24884, 45098, 32365, 52251, 41003, 30101, 49188, 38930, 26905, 47125, 34842, 22747, 43034, 31210, 50218, 39956, 28018, 48170, 35893, 23866, 44086, 34518, 52245, 41001, 30100, 50196, 37930, 25973, 46124, 34861, 22677, 43029, 31209, 51241, 39956, 28010, 48154, 36892, 23853, 44058, 34507, 53266, 41001, 30100, 49204, 37946, 25946, 45110, 34838, 23754, 43018, 31208, 51240, 39988, 27061, 47149, 35893, 24854, 44053, 34442, 53265, 42024, 29098, 49194, 37933, 25965, 45099, 34837, 23753, 44049, 31192, 51220, 39962, 28059, 47126, 35882, 24852, 45074, 31785, 21652, 41012, 29114, 48181, 37910, 25962, 46121, 34834, 22761, 43049, 31220, 50220, 38957, 28053, 48139, 36901, 25874, 46098, 35433, 53273, 42010, 30125, 50202, 38922, 26917, 47140, 36882, 23769, 43065, 31226, 51254, 39958, 29002, 49162, 37924, 24884, 45106, 34421, 53293, 41005, 30165, 50197, 39945, 26980, 47140, 35882, 23797, 43053, 31277, 51243, 40981, 29001, 49161, 37908, 25898, 45082, 34523, 53270, 42026, 30098, 50194, 38953, 27988, 46132, 34874, 23770, 44054, 31210, 51237, 40978, 29097, 48169, 36916, 24950, 45101, 33813, 21605, 42021, 31186, 50194, 38953, 26988, 47130, 34859, 22765, 44042, 32293, 51236, 40978, 29081, 48185, 35898, 24859, 45078, 34826, 21669, 42020, 30130, 50226, 37941, 25974, 46125, 35861, 22762, 44041, 32228, 52260, 39978, 28077, 48157, 36909, 24853, 45075, 33833, 22676, 43028, 31146, 50234, 39962, 26987, 47146, 36882, 24809, 44073, 34260, 52276, 41014, 29082, 49173, 37926, 26898, 46098, 35497, 54313, 43060, 30197, 50221, 38957, 28005, 47141, 36882, 24809, 45097, 32300, 52250, 40987, 29101, 48170, 37925, 26898, 47122, 34841, 22746, 42042, 31195, 50198, 38954, 28004, 48164, 35890, 23865, 44085, 32310, 51245, 40981, 29098, 50185, 37924, 25970, 46122, 34861, 21614, 42029, 31189, 51219, 38953, 27988, 48148, 36906, 23837, 44058, 32299, 52266, 40978, 29097, 49193, 38932, 24954, 45110, 34838, 22682, 42021, 31186, 51218, 39977, 26996, 47156, 35893, 23862, 43053, 32405, 52261, 42002, 29097, 49193, 37932, 25901, 45083, 33835, 22677, 43045, 31122, 51218, 39961, 27994} + var lower = []uint8{218, 184, 92, 154, 152, 84, 170, 168, 180, 186, 184, 54, 52, 148, 82, 84, 168, 180, 108, 110, 108, 44, 150, 148, 80, 106, 216, 92, 94, 92, 44, 40, 148, 82, 180, 216, 220, 184, 90, 84, 40, 148, 84, 168, 182, 116, 180, 86, 84, 42, 40, 84, 106, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 152, 76, 84, 170, 168, 180, 186, 180, 52, 154, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 202, 176, 216, 218, 184, 88, 42, 40, 148, 170, 168, 182, 116, 180, 86, 84, 40, 84, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 106, 216, 92, 56, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 182, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148, 148, 200, 216, 184, 184, 92, 88, 42, 40, 148, 170, 168, 180, 186, 180, 86, 84, 40, 84, 84, 104, 116, 108, 172, 78, 76, 166, 168, 84, 106, 216, 92, 156, 88, 76, 72, 168, 212, 180, 184, 58, 52, 84, 74, 72, 164, 104, 116, 182, 108, 44, 150, 148, 74, 72, 88, 108, 220, 92, 46, 44, 148, 74, 168, 212, 180, 184, 92, 88, 40, 148, 84, 170, 168, 180, 186, 180, 84, 42, 168, 84, 170, 104, 116, 108, 172, 46, 44, 164, 84, 212, 106, 216, 92, 92, 88, 44, 164, 164, 212, 218, 184, 186, 180, 84, 42, 72, 164, 180, 116, 182, 108, 172, 86, 84, 40, 100, 84, 108, 110, 92, 174, 172, 84, 42, 168, 212, 218, 184, 92, 172, 168, 84, 84, 168, 212, 180, 184, 86, 52, 150, 164, 84, 170, 104, 116, 182, 108, 46, 44, 164, 82, 212, 216, 108, 220, 92, 44, 40, 148, 164, 212, 218, 184, 184, 90, 84, 42, 40, 164, 180, 108, 116, 182, 172, 84, 42, 40, 84, 84, 108, 110, 92, 172, 86, 84, 42, 168, 212, 218, 184, 92, 154, 152, 84, 170, 168, 180, 180, 184, 54, 52, 148, 82, 84, 168, 180, 108, 174, 108, 44, 150, 148, 80, 106, 216, 108, 220, 92, 44, 40, 148, 82, 180, 216, 92, 184, 90, 84, 40, 148, 148, 168, 182, 116, 182, 172, 84, 42, 40, 84, 170, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 154, 152, 84, 170, 168, 180, 186, 180, 54, 52, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 110, 92, 44, 148, 148, 74, 176, 216, 218, 184, 88, 42, 40, 148, 84, 168, 182, 116, 180, 86, 84, 40, 148, 84, 106, 104, 108, 174, 172, 76, 42, 168, 84, 212, 216, 92, 92, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 168, 180, 108, 108, 46, 44, 148, 74, 72, 104, 108, 92, 94, 92, 44, 148} if year < 1900 || year > 2400 { return 0, 0, 0, false, "超过日期限制" } @@ -686,8 +689,9 @@ recalc: } func rapidSolarModern(year, month, day int, isLeap bool) time.Time { - var upper = []uint16{32274, 52242, 41001, 30036, 49204, 36918, 25882, 46101, 34854, 22674, 43026, 31145, 51241, 38964, 26997, 47149, 36885, 23717, 44069, 34258, 53266, 41001, 29036, 49178, 37915, 24875, 46090, 34853, 23698, 43026, 31129, 50229, 38970, 26971, 47126, 36874, 24804, 44068, 32242, 52274, 41013, 28086, 48173, 37909, 25898, 46089, 34852, 22706, 43050, 30189, 50203, 38957, 27989, 47123, 35881, 24788, 45076, 32298, 51258, 40986, 29099, 48170, 37906, 25897, 46121, 34836, 21754, 42038, 31190, 50197, 38949, 27986, 48146, 35881, 23860, 44084, 32309, 51245, 39981, 29093, 49189, 37906, 25897, 46121, 35500, 53274, 42011, 30123, 50218, 38949, 27986, 48146, 36889, 23770, 43066, 32282, 52246, 39978, 29028, 49188, 37938, 24885, 45109, 33846, 22678, 42005, 30186, 51209, 39972, 26994, 47146, 35885, 23853, 43051, 32341, 52242, 41001, 29076, 49172, 37930, 25885, 45082, 33835, 22675, 43026, 30121, 50217, 38964, 27002, 46133, 35862, 23770, 44069, 32466, 52242, 41001, 29108, 48180, 36917, 24950, 45101, 33813, 22674, 43026, 31209, 50217, 38954, 26989, 47131, 34859, 23765, 44068, 34322, 52242, 40985, 29082, 48186, 36890, 24874, 45098, 34852, 21746, 42034, 30197, 50229, 37942, 26966, 47125, 35881, 23828, 44052, 32298, 52266, 39981, 28077, 48171, 37909, 24873, 45097, 34836, 22762, 42010, 30172, 50202, 38955, 26963, 47122, 35881, 24852, 43060, 31290, 51253, 39990, 28058, 48149, 37906, 25897, 45097, 33844, 21686, 42037, 30198, 50221, 39957, 29010, 48146, 36905, 24884, 45098, 32365, 52251, 41003, 30101, 49188, 38930, 26905, 47125, 34842, 22747, 43034, 31210, 50218, 39956, 28018, 48170, 35893, 23866, 44086, 34518, 52245, 41001, 30100, 50196, 37930, 25973, 46124, 34861, 22677, 43029, 31209, 51241, 39956, 28010, 48154, 36892, 23853, 44058, 34507, 53266, 41001, 30100, 49204, 37946, 25946, 45110, 34838, 23754, 43018, 31208, 51240, 39988, 27061, 47149, 35893, 24854, 44053, 34442, 53265, 42024, 29098, 49194, 37933, 25965, 45099, 34837, 23753, 44049, 31192, 51220, 39962, 28059, 47126, 35882, 24852, 45074, 31785, 21652, 41012, 29114, 48181, 37910, 25962, 46121, 34834, 22761, 43049, 31220, 50220, 38957, 28053, 48139, 36901, 25874, 46098, 35433, 53273, 42010, 30125, 50202, 38922, 26917, 47140, 36882, 23769, 43065, 31226, 51254, 39958, 29002, 49162, 37924, 24882, 45106, 34421, 53293, 41005, 30165, 50197, 39945, 26980, 47140, 35882, 23797, 43053, 31277, 51243, 40981, 29001, 49161, 37908, 25898, 45082, 34523, 53270, 42026, 30098, 50194, 38953, 27988, 46132, 34874, 23770, 44054, 31210, 51237, 40978, 29097, 48169, 36916, 24950, 45101, 31797, 21605, 42021, 31186, 50194, 38953, 26988, 47130, 34859, 22765, 44042, 32293, 51236, 40978, 29081, 48185, 35898, 24859, 45078, 34826, 21669, 42020, 30130, 50226, 37941, 25974, 46125, 35861, 22762, 44041, 32228, 52260, 39978, 28077, 48157, 36909, 24853, 45075, 33833, 22676, 43028, 31146, 50234, 39962, 26987, 47146, 36882, 24809, 44073, 34260, 52276, 41014, 29082, 49173, 37926, 26898, 46098, 35497, 54313, 43060, 30197, 50221, 38957, 28005, 47141, 36882, 24809, 45097, 32300, 52250, 40987, 29101, 48170, 37925, 26898, 47122, 34841, 22746, 42042, 31195, 50198, 38954, 28004, 48164, 35890, 23861, 44085, 32310, 51245, 40981, 29098, 50185, 37924, 25970, 46122, 34861, 21614, 42029, 31189, 51218, 38953, 27988, 48148, 36906, 23837, 44058, 32299, 52266, 40978, 29097, 49193, 38932, 24954, 45110, 33846, 22682, 42021, 31186, 51218, 39977, 26996, 47156, 35893, 23862, 43053, 32405, 52261, 42002, 29097, 49193, 37932, 25901, 45083, 33835, 22677, 43044, 31122, 51218, 39961, 27994} - var lower = []uint8{218, 184, 92, 154, 152, 84, 170, 168, 180, 186, 184, 54, 52, 148, 82, 84, 168, 180, 108, 110, 108, 44, 150, 148, 80, 106, 216, 92, 94, 92, 44, 40, 148, 82, 180, 216, 220, 184, 90, 84, 40, 148, 84, 168, 182, 116, 180, 86, 84, 42, 40, 84, 106, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 152, 76, 84, 170, 168, 180, 186, 180, 52, 154, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 202, 176, 216, 218, 184, 88, 42, 40, 148, 170, 168, 182, 116, 180, 86, 84, 40, 84, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 106, 216, 92, 56, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 182, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148, 148, 200, 216, 184, 184, 92, 88, 42, 40, 148, 170, 168, 180, 186, 180, 86, 84, 40, 84, 84, 104, 116, 108, 172, 78, 76, 166, 168, 84, 106, 216, 92, 156, 88, 76, 72, 168, 212, 180, 184, 58, 52, 84, 74, 72, 164, 104, 116, 182, 108, 44, 150, 148, 74, 72, 88, 108, 220, 92, 46, 44, 148, 74, 168, 212, 180, 184, 92, 88, 40, 148, 84, 170, 168, 180, 186, 180, 52, 42, 168, 84, 170, 104, 116, 108, 172, 46, 44, 164, 84, 212, 106, 216, 92, 92, 88, 44, 164, 164, 212, 218, 184, 186, 180, 84, 42, 72, 164, 180, 108, 182, 108, 172, 86, 84, 40, 84, 84, 108, 110, 92, 174, 172, 84, 42, 168, 212, 218, 184, 92, 172, 168, 84, 84, 168, 212, 180, 184, 86, 52, 150, 164, 84, 170, 104, 116, 182, 108, 46, 44, 164, 82, 212, 216, 108, 220, 92, 44, 40, 148, 164, 212, 218, 184, 184, 90, 84, 42, 40, 164, 180, 108, 116, 182, 172, 84, 42, 40, 84, 84, 108, 110, 92, 172, 86, 84, 42, 168, 212, 218, 184, 92, 154, 152, 84, 170, 168, 180, 116, 184, 54, 52, 148, 74, 80, 168, 180, 108, 174, 108, 44, 150, 148, 80, 106, 216, 108, 220, 92, 44, 40, 148, 82, 180, 216, 92, 184, 90, 84, 40, 148, 148, 168, 182, 116, 182, 172, 84, 42, 40, 84, 170, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 154, 152, 84, 170, 168, 180, 186, 180, 54, 52, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 74, 176, 216, 218, 184, 88, 42, 40, 148, 84, 168, 182, 116, 180, 86, 84, 40, 148, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 212, 216, 92, 92, 152, 76, 72, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 108, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148} + // 1900~2026 为确定闰秒表结果,2027 起按现行时标政策生成。 + var upper = []uint16{32274, 52242, 41001, 30036, 49204, 36918, 25882, 46101, 34854, 22674, 43026, 31145, 51241, 38964, 26997, 47149, 36885, 23717, 44069, 34258, 53266, 41001, 29036, 49178, 37915, 24875, 46090, 34853, 23698, 43026, 31129, 50229, 38970, 26971, 47126, 36874, 24804, 44068, 32242, 52274, 41013, 28086, 48173, 37909, 25898, 46089, 34852, 22706, 43050, 30189, 50203, 38957, 27989, 47123, 35881, 24788, 45076, 32298, 51258, 40986, 29099, 48170, 37906, 25897, 46121, 34836, 21754, 42038, 31190, 50197, 38949, 27986, 48146, 35881, 23860, 44084, 32309, 51245, 39981, 29093, 49189, 37906, 25897, 46121, 35500, 53274, 42011, 30123, 50218, 38949, 27986, 48146, 36889, 23770, 43066, 32282, 52246, 39978, 29028, 49188, 37938, 24885, 45109, 33846, 22678, 42005, 30186, 51209, 39972, 26994, 47146, 35885, 23853, 43051, 32341, 52242, 41001, 29076, 49172, 37930, 25885, 45082, 33835, 22675, 43026, 30121, 50217, 38964, 27002, 46133, 35862, 23770, 44069, 32466, 52242, 41001, 29108, 48180, 36917, 24950, 45101, 33813, 22674, 43026, 31209, 50217, 38954, 26989, 47131, 34859, 23765, 44068, 34322, 52242, 40985, 29082, 48186, 36890, 24874, 45098, 34852, 21746, 42034, 30197, 50229, 37942, 26966, 47125, 35881, 23828, 44052, 32298, 52266, 39981, 28077, 48171, 37909, 24873, 45097, 34836, 22762, 42010, 30172, 50202, 38955, 26963, 47122, 35881, 24852, 43060, 31290, 51253, 39990, 28058, 48149, 37906, 25897, 45097, 33844, 21686, 42037, 30198, 50221, 39957, 29010, 48146, 36905, 24884, 45098, 32365, 52251, 41003, 30101, 49188, 38930, 26905, 47125, 34842, 22747, 43034, 31210, 50218, 39956, 28018, 48170, 35893, 23866, 44086, 34518, 52245, 41001, 30100, 50196, 37930, 25973, 46124, 34861, 22677, 43029, 31209, 51241, 39956, 28010, 48154, 36892, 23853, 44058, 34507, 53266, 41001, 30100, 49204, 37946, 25946, 45110, 34838, 23754, 43018, 31208, 51240, 39988, 27061, 47149, 35893, 24854, 44053, 34442, 53265, 42024, 29098, 49194, 37933, 25965, 45099, 34837, 23753, 44049, 31192, 51220, 39962, 28059, 47126, 35882, 24852, 45074, 31785, 21652, 41012, 29114, 48181, 37910, 25962, 46121, 34834, 22761, 43049, 31220, 50220, 38957, 28053, 48139, 36901, 25874, 46098, 35433, 53273, 42010, 30125, 50202, 38922, 26917, 47140, 36882, 23769, 43065, 31226, 51254, 39958, 29002, 49162, 37924, 24884, 45106, 34421, 53293, 41005, 30165, 50197, 39945, 26980, 47140, 35882, 23797, 43053, 31277, 51243, 40981, 29001, 49161, 37908, 25898, 45082, 34523, 53270, 42026, 30098, 50194, 38953, 27988, 46132, 34874, 23770, 44054, 31210, 51237, 40978, 29097, 48169, 36916, 24950, 45101, 33813, 21605, 42021, 31186, 50194, 38953, 26988, 47130, 34859, 22765, 44042, 32293, 51236, 40978, 29081, 48185, 35898, 24859, 45078, 34826, 21669, 42020, 30130, 50226, 37941, 25974, 46125, 35861, 22762, 44041, 32228, 52260, 39978, 28077, 48157, 36909, 24853, 45075, 33833, 22676, 43028, 31146, 50234, 39962, 26987, 47146, 36882, 24809, 44073, 34260, 52276, 41014, 29082, 49173, 37926, 26898, 46098, 35497, 54313, 43060, 30197, 50221, 38957, 28005, 47141, 36882, 24809, 45097, 32300, 52250, 40987, 29101, 48170, 37925, 26898, 47122, 34841, 22746, 42042, 31195, 50198, 38954, 28004, 48164, 35890, 23865, 44085, 32310, 51245, 40981, 29098, 50185, 37924, 25970, 46122, 34861, 21614, 42029, 31189, 51219, 38953, 27988, 48148, 36906, 23837, 44058, 32299, 52266, 40978, 29097, 49193, 38932, 24954, 45110, 34838, 22682, 42021, 31186, 51218, 39977, 26996, 47156, 35893, 23862, 43053, 32405, 52261, 42002, 29097, 49193, 37932, 25901, 45083, 33835, 22677, 43045, 31122, 51218, 39961, 27994} + var lower = []uint8{218, 184, 92, 154, 152, 84, 170, 168, 180, 186, 184, 54, 52, 148, 82, 84, 168, 180, 108, 110, 108, 44, 150, 148, 80, 106, 216, 92, 94, 92, 44, 40, 148, 82, 180, 216, 220, 184, 90, 84, 40, 148, 84, 168, 182, 116, 180, 86, 84, 42, 40, 84, 106, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 152, 76, 84, 170, 168, 180, 186, 180, 52, 154, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 94, 92, 44, 148, 148, 202, 176, 216, 218, 184, 88, 42, 40, 148, 170, 168, 182, 116, 180, 86, 84, 40, 84, 84, 106, 232, 108, 174, 172, 76, 42, 168, 84, 106, 216, 92, 56, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 104, 180, 182, 108, 46, 44, 148, 74, 72, 104, 108, 220, 94, 92, 44, 148, 148, 200, 216, 184, 184, 92, 88, 42, 40, 148, 170, 168, 180, 186, 180, 86, 84, 40, 84, 84, 104, 116, 108, 172, 78, 76, 166, 168, 84, 106, 216, 92, 156, 88, 76, 72, 168, 212, 180, 184, 58, 52, 84, 74, 72, 164, 104, 116, 182, 108, 44, 150, 148, 74, 72, 88, 108, 220, 92, 46, 44, 148, 74, 168, 212, 180, 184, 92, 88, 40, 148, 84, 170, 168, 180, 186, 180, 84, 42, 168, 84, 170, 104, 116, 108, 172, 46, 44, 164, 84, 212, 106, 216, 92, 92, 88, 44, 164, 164, 212, 218, 184, 186, 180, 84, 42, 72, 164, 180, 116, 182, 108, 172, 86, 84, 40, 100, 84, 108, 110, 92, 174, 172, 84, 42, 168, 212, 218, 184, 92, 172, 168, 84, 84, 168, 212, 180, 184, 86, 52, 150, 164, 84, 170, 104, 116, 182, 108, 46, 44, 164, 82, 212, 216, 108, 220, 92, 44, 40, 148, 164, 212, 218, 184, 184, 90, 84, 42, 40, 164, 180, 108, 116, 182, 172, 84, 42, 40, 84, 84, 108, 110, 92, 172, 86, 84, 42, 168, 212, 218, 184, 92, 154, 152, 84, 170, 168, 180, 180, 184, 54, 52, 148, 82, 84, 168, 180, 108, 174, 108, 44, 150, 148, 80, 106, 216, 108, 220, 92, 44, 40, 148, 82, 180, 216, 92, 184, 90, 84, 40, 148, 148, 168, 182, 116, 182, 172, 84, 42, 40, 84, 170, 104, 108, 174, 172, 84, 84, 168, 84, 212, 216, 92, 92, 154, 152, 84, 170, 168, 180, 186, 180, 54, 52, 148, 74, 80, 168, 180, 108, 108, 46, 44, 150, 148, 80, 104, 216, 92, 110, 92, 44, 148, 148, 74, 176, 216, 218, 184, 88, 42, 40, 148, 84, 168, 182, 116, 180, 86, 84, 40, 148, 84, 106, 104, 108, 174, 172, 76, 42, 168, 84, 212, 216, 92, 92, 152, 76, 76, 168, 212, 180, 186, 180, 52, 150, 148, 72, 168, 168, 180, 108, 108, 46, 44, 148, 74, 72, 104, 108, 92, 94, 92, 44, 148} if year < 1900 || year > 2400 { return time.Time{} } @@ -747,9 +751,10 @@ var chineseMonths = map[string]int{ "七": 7, "八": 8, "九": 9, "十": 10, } -// 两个正则提到包级:文本入口每次解析都会用到,每次重编译会白花几十微秒。 +// 正则提到包级:文本入口每次解析都会用到,每次重编译会白花几十微秒。 var ( chineseDatePrefixYearPattern = regexp.MustCompile(`^([-一二三四五六七八九十零〇\d]+?)年`) + digitPattern = regexp.MustCompile(`\d+`) chineseDatePattern = regexp.MustCompile(`^([\p{Han}]+?)([-负負一二三四五六七八九十零〇\d]*?元?)年([\p{Han}\d]+?)月([\p{Han}\d]+?)日?$`) ) @@ -772,7 +777,9 @@ func parseChineseDate(dateStr string) (LunarTime, error) { dateStr = originDateStr } } - dateStr = "公元" + dateStr + if !strings.HasPrefix(dateStr, "公元") { + dateStr = "公元" + dateStr + } // 正则表达式匹配日期格式 re := chineseDatePattern matches := re.FindStringSubmatch(dateStr) @@ -797,7 +804,7 @@ func parseChineseDate(dateStr string) (LunarTime, error) { sign = -1 yearStr = strings.TrimPrefix(strings.TrimPrefix(yearStr, "负"), "負") } - if m, _ := regexp.MatchString("\\d+", yearStr); m { + if digitPattern.MatchString(yearStr) { result.year, err = strconv.Atoi(yearStr) if err != nil { return result, fmt.Errorf("无效的年份: %s", matches[2]) @@ -826,7 +833,7 @@ func parseChineseDate(dateStr string) (LunarTime, error) { if month, ok := chineseMonths[monthStr]; ok { result.month = month } else { - if m, _ := regexp.MatchString("\\d+", monthStr); m { + if digitPattern.MatchString(monthStr) { result.month, err = strconv.Atoi(monthStr) } else { // 尝试将月份字符串转换为数字 @@ -852,7 +859,7 @@ func parseChineseDate(dateStr string) (LunarTime, error) { if day, ok := chineseDays[dayStr]; ok { result.day = day } else { - if m, _ := regexp.MatchString("\\d+", dayStr); m { + if digitPattern.MatchString(dayStr) { result.day, err = strconv.Atoi(dayStr) if err != nil { return result, fmt.Errorf("无效的日期: %s", dayStr) @@ -869,7 +876,7 @@ func parseChineseDate(dateStr string) (LunarTime, error) { } func parseDirectYear(yearStr string) (int, error) { - if m, _ := regexp.MatchString("\\d+", yearStr); m { + if digitPattern.MatchString(yearStr) { year, err := strconv.Atoi(yearStr) if err != nil { return 0, fmt.Errorf("无效的年份: %s", yearStr) @@ -980,6 +987,7 @@ func GanZhiOfYear(year int) string { // GanZhiOfDay 日干支 / sexagenary day name. func GanZhiOfDay(t time.Time) string { + t = t.In(getCst()) return ganZhiOfJD(Date2JD(time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, getCst()))) } diff --git a/calendar/chinese_test.go b/calendar/chinese_test.go index 027f010..a96fcbb 100644 --- a/calendar/chinese_test.go +++ b/calendar/chinese_test.go @@ -859,7 +859,7 @@ func TestRapidLunarAndLunar(t *testing.T) { t.Fatal(year, month, 24, a1, a2, a3, a4, b1, b2, b3, b4) } sol := JD2Date(basic.GetSolar(b1, b2, b3, b4, 8.0/24)) - if sol.Year() != year && int(sol.Month()) != month && sol.Day() != 24 { + if sol.Year() != year || int(sol.Month()) != month || sol.Day() != 24 { t.Fatal(year, month, sol, b1, b2, b3, b4) } } diff --git a/calendar/parse_zone_test.go b/calendar/parse_zone_test.go new file mode 100644 index 0000000..8a60e43 --- /dev/null +++ b/calendar/parse_zone_test.go @@ -0,0 +1,32 @@ +package calendar + +import ( + "testing" + "time" +) + +func TestGanZhiOfDayUsesCSTCivilDay(t *testing.T) { + instant := time.Date(2020, 1, 24, 17, 0, 0, 0, time.UTC) + want := GanZhiOfDay(instant.In(getCst())) + if got := GanZhiOfDay(instant); got != want { + t.Fatalf("GanZhiOfDay(UTC) = %s, want %s", got, want) + } + if want != "丁卯" { + t.Fatalf("GanZhiOfDay(2020-01-25 CST) = %s, want 丁卯", want) + } +} + +func TestParseChineseDateAcceptsExplicitGongYuan(t *testing.T) { + plain, err := parseChineseDate("1988年1月5日") + if err != nil { + t.Fatal(err) + } + prefixed, err := parseChineseDate("公元1988年1月5日") + if err != nil { + t.Fatal(err) + } + if prefixed.comment != plain.comment || prefixed.year != plain.year || + prefixed.month != plain.month || prefixed.day != plain.day { + t.Fatalf("公元 prefix parsed as %+v, want %+v", prefixed, plain) + } +} diff --git a/calendar/reform_roundtrip_test.go b/calendar/reform_roundtrip_test.go index f03a583..5b8f0f2 100644 --- a/calendar/reform_roundtrip_test.go +++ b/calendar/reform_roundtrip_test.go @@ -58,10 +58,7 @@ func TestGregorianReformReverseMatchesForward(t *testing.T) { } // 先秦岁首跨界:正向按公历年选默认古历、反向按农历年选,交界处必须自洽。 -// 农历 -479 年正月落在公历 -480 年 12 月(春秋历),此前反向按农历年选了周历,整体差 1 天。 -// The pre-Qin year-start crossing: the forward picks the default ancient calendar by civil -// year while the reverse picks by lunar year. Lunar year -479 month 1 falls in civil December -// -480 (Chunqiu calendar); the reverse used to pick Zhou and shift those 20 days by one day. +// 农历 -479 年正月落在公历 -480 年 12 月(春秋历),反向不得选成周历。 func TestAncientYearStartCrossingRoundTrips(t *testing.T) { cases := []struct { ly, lm, ld int diff --git a/calendar/time.go b/calendar/time.go index 7ba1732..48bc805 100644 --- a/calendar/time.go +++ b/calendar/time.go @@ -177,7 +177,11 @@ func (t Time) Add(d time.Duration) Time { jd := Date2JD(t.solarTime) + d.Seconds()/86400.0 newT = basic.JD2DateByZone(jd, t.solarTime.Location(), true) } - rT, _ := SolarToLunar(newT) + rT, err := SolarToLunar(newT) + if err != nil { + // 超出农历覆盖范围时仍保留公历时刻,农历部分为空。 + return Time{solarTime: newT} + } return rT } diff --git a/calendar/time_add_test.go b/calendar/time_add_test.go index aa063a8..fe8e3ac 100644 --- a/calendar/time_add_test.go +++ b/calendar/time_add_test.go @@ -19,9 +19,7 @@ func addBase(t *testing.T, y, m, d int) Time { return fwd } -// Add 必须与 time.Time 一样精确;此前 d≥1s 时经儒略日往返,普通日期会漂几微秒。 -// Add must be as exact as time.Time; it used to round-trip through the Julian day for d >= 1s, -// which drifted by a few microseconds on ordinary dates. +// Add 必须与 time.Time 一样精确,不得经儒略日往返引入微秒漂移。 func TestTimeAddIsExact(t *testing.T) { base := addBase(t, 2024, 3, 1) cases := []struct { @@ -67,3 +65,18 @@ func TestTimeAddSkipsGregorianReformGap(t *testing.T) { } } } + +func TestAddOutsideLunarRangeKeepsSolarTime(t *testing.T) { + base, err := SolarToLunarByYMD(-721, 1, 15) + if err != nil { + t.Fatal(err) + } + got := base.Add(-30 * 24 * time.Hour) + want := base.Solar().Add(-30 * 24 * time.Hour) + if !got.Solar().Equal(want) { + t.Fatalf("Solar() = %v, want %v", got.Solar(), want) + } + if len(got.Lunars()) != 0 { + t.Fatalf("Lunars() = %v, want empty", got.Lunars()) + } +} diff --git a/context.go b/context.go new file mode 100644 index 0000000..8016de3 --- /dev/null +++ b/context.go @@ -0,0 +1,237 @@ +package astro + +import ( + "sync" + + "b612.me/astro/basic" + "b612.me/astro/eclipse" + "b612.me/astro/geojson" + "b612.me/astro/internal/ephem" + "b612.me/astro/jupiter" + "b612.me/astro/mars" + "b612.me/astro/mercury" + "b612.me/astro/moon" + "b612.me/astro/neptune" + "b612.me/astro/phenomena" + "b612.me/astro/saturn" + "b612.me/astro/sun" + "b612.me/astro/uranus" + "b612.me/astro/venus" +) + +// Model 星历模型 / ephemeris model. +type Model = EphemerisModel + +const ( + // ModelLegacy VSOP87 与 ELP2000 截断级数 / truncated VSOP87 and ELP2000 series. + ModelLegacy = EphemerisLegacy + // ModelSemianalytic 半解析表模型 / semi-analytic table model. + ModelSemianalytic = EphemerisSemianalytic + // ModelCustom 自定义只读引擎 / custom read-only engine. + ModelCustom = EphemerisCustom +) + +// Context 不可变星历实例与延迟创建的高层模块,须通过 NewContext 创建且不得复制;零值与 nil 没有引擎, +// 标量结果为 NaN,带输入校验的接口(如升落)返回 basic.ErrInvalidObservationInput / +// immutable ephemeris context with lazy modules; construct with NewContext and do not copy. The zero value and nil +// have no engine: scalar results are NaN and validated entry points such as rise/set return basic.ErrInvalidObservationInput. +type Context struct { + inst *ephem.Instance + // lunarLimb 日月食与月掩的默认月缘剖面,nil 为圆月面。 + lunarLimb eclipse.LunarLimb + + moonOnce sync.Once + moonObj *moon.Moon + sunOnce sync.Once + sunObj *sun.Sun + mercuryOnce sync.Once + mercuryObj *mercury.Mercury + venusOnce sync.Once + venusObj *venus.Venus + marsOnce sync.Once + marsObj *mars.Mars + jupiterOnce sync.Once + jupiterObj *jupiter.Jupiter + saturnOnce sync.Once + saturnObj *saturn.Saturn + uranusOnce sync.Once + uranusObj *uranus.Uranus + neptuneOnce sync.Once + neptuneObj *neptune.Neptune + eclipseOnce sync.Once + eclipseObj *eclipse.Eclipse + phenomenaOnce sync.Once + phenomenaObj *phenomena.Phenomena + geojsonOnce sync.Once + geojsonObj *geojson.Generator +} + +// NewContext 创建独立模型上下文;内置模型不得带 Engine,ModelCustom 必须且只能带一个非 nil 只读 Engine, +// 违反时返回 ErrInvalidEngine,未知模型返回 ErrUnknownModel / +// create an independent context; built-in models take no Engine and ModelCustom takes exactly one non-nil Engine. +func NewContext(model Model, engines ...Engine) (*Context, error) { + switch model { + case ModelLegacy, ModelSemianalytic: + if len(engines) != 0 { + return nil, ErrInvalidEngine + } + return &Context{inst: ephem.NewModelInstance(model)}, nil + case ModelCustom: + if len(engines) != 1 || ephem.IsNilEngine(engines[0]) { + return nil, ErrInvalidEngine + } + return &Context{inst: ephem.NewInstance(engines[0])}, nil + default: + return nil, ErrUnknownModel + } +} + +// NewStackContext 创建使用多源星历的独立上下文,先传入的引擎优先;含 nil 引擎时返回 ErrInvalidEngine / +// creates a multi-source context; earlier engines have priority and nil engines return ErrInvalidEngine. +func NewStackContext(engines ...Engine) (*Context, error) { + for _, engine := range engines { + if ephem.IsNilEngine(engine) { + return nil, ErrInvalidEngine + } + } + return &Context{inst: ephem.NewInstance(ephem.NewStack(engines...))}, nil +} + +// Model 返回此上下文的固定模型 / the fixed model of this context. +func (context *Context) Model() Model { + return context.instance().Model() +} + +// emptyInstance 零值或 nil 上下文所用的无引擎实例。 +var emptyInstance = ephem.NewInstance(nil) + +// nilContext nil 接收者的访问器转到这个共享零值上下文。 +var nilContext = &Context{} + +func (context *Context) self() *Context { + if context == nil { + return nilContext + } + return context +} + +func (context *Context) instance() *ephem.Instance { + if context == nil || context.inst == nil { + return emptyInstance + } + return context.inst +} + +// Moon 返回月球模块 / the lunar module. +func (context *Context) Moon() *moon.Moon { + context = context.self() + context.moonOnce.Do(func() { context.moonObj = moon.NewWithInstance(context.instance()).WithLunarLimb(context.lunarLimb) }) + return context.moonObj +} + +// Sun 返回太阳模块 / the solar module. +func (context *Context) Sun() *sun.Sun { + context = context.self() + context.sunOnce.Do(func() { context.sunObj = sun.NewWithInstance(context.instance()) }) + return context.sunObj +} + +// Mercury 返回水星模块 / the Mercury module. +func (context *Context) Mercury() *mercury.Mercury { + context = context.self() + context.mercuryOnce.Do(func() { context.mercuryObj = mercury.NewWithInstance(context.instance()) }) + return context.mercuryObj +} + +// Venus 返回金星模块 / the Venus module. +func (context *Context) Venus() *venus.Venus { + context = context.self() + context.venusOnce.Do(func() { context.venusObj = venus.NewWithInstance(context.instance()) }) + return context.venusObj +} + +// Mars 返回火星模块 / the Mars module. +func (context *Context) Mars() *mars.Mars { + context = context.self() + context.marsOnce.Do(func() { context.marsObj = mars.NewWithInstance(context.instance()) }) + return context.marsObj +} + +// Jupiter 返回木星模块 / the Jupiter module. +func (context *Context) Jupiter() *jupiter.Jupiter { + context = context.self() + context.jupiterOnce.Do(func() { context.jupiterObj = jupiter.NewWithInstance(context.instance()) }) + return context.jupiterObj +} + +// Saturn 返回土星模块 / the Saturn module. +func (context *Context) Saturn() *saturn.Saturn { + context = context.self() + context.saturnOnce.Do(func() { context.saturnObj = saturn.NewWithInstance(context.instance()) }) + return context.saturnObj +} + +// Uranus 返回天王星模块 / the Uranus module. +func (context *Context) Uranus() *uranus.Uranus { + context = context.self() + context.uranusOnce.Do(func() { context.uranusObj = uranus.NewWithInstance(context.instance()) }) + return context.uranusObj +} + +// Neptune 返回海王星模块 / the Neptune module. +func (context *Context) Neptune() *neptune.Neptune { + context = context.self() + context.neptuneOnce.Do(func() { context.neptuneObj = neptune.NewWithInstance(context.instance()) }) + return context.neptuneObj +} + +// Eclipse 返回日月食模块 / the eclipse module. +func (context *Context) Eclipse() *eclipse.Eclipse { + context = context.self() + context.eclipseOnce.Do(func() { + context.eclipseObj = eclipse.NewWithInstance(context.instance()).WithLunarLimb(context.lunarLimb) + }) + return context.eclipseObj +} + +// WithLunarLimb 共享星历实例、日月食与月掩改用指定月缘剖面的新上下文,nil 为圆月面 / context using the given lunar limb. +func (context *Context) WithLunarLimb(limb eclipse.LunarLimb) *Context { + return &Context{inst: context.instance(), lunarLimb: limb} +} + +// GeoJSON 返回地理数据生成模块 / the geographic data generator. +func (context *Context) GeoJSON() *geojson.Generator { + context = context.self() + context.geojsonOnce.Do(func() { context.geojsonObj = geojson.NewWithInstance(context.instance()) }) + return context.geojsonObj +} + +var globalMu sync.RWMutex + +// SetModel 切换包级函数的全局内置模型,非内置模型返回 ErrUnknownModel 且不改动全局状态;已有显式上下文不受影响 / +// switch the global built-in model; non-built-in models return ErrUnknownModel and leave the global state unchanged. +func SetModel(model Model) error { + if model != ModelLegacy && model != ModelSemianalytic { + return ErrUnknownModel + } + inst := ephem.NewModelInstance(model) + globalMu.Lock() + defer globalMu.Unlock() + basic.SetEphemerisModel(basic.EphemerisModel(model)) + ephem.SetGlobal(inst) + return nil +} + +// GetModel 返回包级函数当前使用的模型 / the active model used by package-level functions. +func GetModel() Model { + globalMu.RLock() + defer globalMu.RUnlock() + return Model(basic.GetEphemerisModel()) +} + +// Phenomena 返回通用天象与偕日可见模块 / the generic sky event module. +func (context *Context) Phenomena() *phenomena.Phenomena { + context = context.self() + context.phenomenaOnce.Do(func() { context.phenomenaObj = phenomena.NewWithInstance(context.instance()) }) + return context.phenomenaObj +} diff --git a/context_example_test.go b/context_example_test.go new file mode 100644 index 0000000..e43e5e7 --- /dev/null +++ b/context_example_test.go @@ -0,0 +1,23 @@ +package astro_test + +import ( + "fmt" + "math" + "time" + + "b612.me/astro" +) + +func ExampleContext() { + context, err := astro.NewContext(astro.ModelSemianalytic) + if err != nil { + panic(err) + } + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + longitude := context.Moon().ApparentLo(date) + fmt.Println(context.Model() == astro.ModelSemianalytic) + fmt.Println(!math.IsNaN(longitude) && longitude >= 0 && longitude < 360) + // Output: + // true + // true +} diff --git a/context_internal_test.go b/context_internal_test.go new file mode 100644 index 0000000..d6d7ca0 --- /dev/null +++ b/context_internal_test.go @@ -0,0 +1,30 @@ +package astro + +import ( + "testing" + + "b612.me/astro/internal/ephem" +) + +func TestContextDefaultRefreshAndExistingModelSetter(t *testing.T) { + previousModel := GetModel() + previousInstance := ephem.Global() + t.Cleanup(func() { + SetModel(previousModel) + ephem.SetGlobal(previousInstance) + }) + SetModel(ModelLegacy) + first, err := NewContext(ModelLegacy) + if err != nil { + t.Fatal(err) + } + ephem.SetGlobal(ephem.NewInstance(nil)) + SetModel(ModelLegacy) + if ephem.Global().Model() != ephem.Legacy || GetModel() != ModelLegacy { + t.Fatal("repeated SetModel retained an incompatible global instance") + } + ephem.SetActive(ephem.Semianalytic) + if GetModel() != ModelSemianalytic || ephem.Global().Model() != ephem.Semianalytic || first.Model() != ModelLegacy { + t.Fatal("existing model setter broke global refresh or explicit isolation") + } +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..8852ef7 --- /dev/null +++ b/context_test.go @@ -0,0 +1,350 @@ +package astro_test + +import ( + "bytes" + "errors" + "math" + "reflect" + "sync" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/basic" + "b612.me/astro/eclipse" + "b612.me/astro/geojson" + "b612.me/astro/jupiter" + "b612.me/astro/mars" + "b612.me/astro/mercury" + "b612.me/astro/moon" + "b612.me/astro/neptune" + "b612.me/astro/saturn" + "b612.me/astro/sun" + "b612.me/astro/uranus" + "b612.me/astro/venus" +) + +func mustContext(tb testing.TB, model astro.Model, engines ...astro.Engine) *astro.Context { + tb.Helper() + context, err := astro.NewContext(model, engines...) + if err != nil { + tb.Fatalf("NewContext(%v): %v", model, err) + } + return context +} + +func mustInstance(tb testing.TB, model astro.EphemerisModel) *astro.Instance { + tb.Helper() + inst, err := astro.NewModelInstance(model) + if err != nil { + tb.Fatalf("NewModelInstance(%v): %v", model, err) + } + return inst +} + +type contextScalarCase struct { + name string + global func(time.Time) float64 + bound func(*astro.Context, time.Time) float64 +} + +func contextScalars() []contextScalarCase { + return []contextScalarCase{ + {"moon", moon.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Moon().ApparentLo(date) }}, + {"sun", sun.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Sun().ApparentLo(date) }}, + {"mercury", mercury.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Mercury().ApparentLo(date) }}, + {"venus", venus.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Venus().ApparentLo(date) }}, + {"mars", mars.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Mars().ApparentLo(date) }}, + {"jupiter", jupiter.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Jupiter().ApparentLo(date) }}, + {"saturn", saturn.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Saturn().ApparentLo(date) }}, + {"uranus", uranus.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Uranus().ApparentLo(date) }}, + {"neptune", neptune.ApparentLo, func(context *astro.Context, date time.Time) float64 { return context.Neptune().ApparentLo(date) }}, + {"sun-distance", sun.EarthDistance, func(context *astro.Context, date time.Time) float64 { return context.Sun().EarthDistance(date) }}, + {"moon-distance", moon.EarthDistance, func(context *astro.Context, date time.Time) float64 { return context.Moon().EarthDistance(date) }}, + {"venus-distance", venus.SunDistance, func(context *astro.Context, date time.Time) float64 { return context.Venus().SunDistance(date) }}, + } +} + +func TestContextIsolationAndGlobalModelSwitch(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + legacy := mustContext(t, astro.ModelLegacy) + modern := mustContext(t, astro.ModelSemianalytic) + for _, test := range contextScalars() { + t.Run(test.name, func(t *testing.T) { + astro.SetModel(astro.ModelLegacy) + wantLegacy := test.global(date) + gotLegacy := test.bound(legacy, date) + astro.SetModel(astro.ModelSemianalytic) + wantModern := test.global(date) + gotModern := test.bound(modern, date) + if math.IsNaN(gotLegacy) || math.IsNaN(gotModern) || math.Abs(gotLegacy-wantLegacy) > 1e-9 || math.Abs(gotModern-wantModern) > 1e-9 { + t.Fatalf("legacy %.12f/%.12f; modern %.12f/%.12f", gotLegacy, wantLegacy, gotModern, wantModern) + } + if math.Abs(gotLegacy-gotModern) < 1e-12 { + t.Fatalf("model switch did not change %s: %.12f", test.name, gotLegacy) + } + if test.bound(legacy, date) != gotLegacy || test.bound(modern, date) != gotModern { + t.Fatal("explicit context changed with the global model") + } + }) + } + if legacy.Model() != astro.ModelLegacy || modern.Model() != astro.ModelSemianalytic || astro.GetModel() != astro.ModelSemianalytic { + t.Fatal("incorrect model identity") + } + if !basic.SetEphemerisModel(basic.EphemerisLegacy) || astro.GetModel() != astro.ModelLegacy { + t.Fatal("existing basic model setter diverged from the root facade") + } +} + +func TestContextRisePhaseAndEclipseIsolation(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + zone := time.FixedZone("UTC+8", 8*3600) + date := time.Date(2024, 4, 8, 0, 0, 0, 0, zone) + for _, model := range []astro.Model{astro.ModelLegacy, astro.ModelSemianalytic} { + context := mustContext(t, model) + astro.SetModel(model) + wantRise, wantErr := moon.RiseTime(date, 116.4, 39.9, 0, true) + wantSunrise, wantSunErr := sun.RiseTime(date, 116.4, 39.9, 0, true) + wantPhase := moon.NextNewMoon(date) + solarDate := time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC) + wantSolar, wantSolarOK := eclipse.SolarEclipseOnDate(solarDate) + wantLunar, wantLunarOK := eclipse.LunarEclipseOnDateWithInstance(time.Date(2024, 3, 25, 0, 0, 0, 0, time.UTC), mustInstance(t, model)) + if !wantSolarOK || !wantLunarOK { + t.Fatal("known solar or lunar eclipse was not found") + } + wantJSON, wantJSONErr := geojson.MarshalLunarEclipse(wantLunar, 24) + astro.SetModel(astro.Model(1 - int(model))) + gotRise, gotErr := context.Moon().RiseTime(date, 116.4, 39.9, 0, true) + gotSunrise, gotSunErr := context.Sun().RiseTime(date, 116.4, 39.9, 0, true) + if gotRise != wantRise || gotErr != wantErr || gotSunrise != wantSunrise || gotSunErr != wantSunErr || context.Moon().NextNewMoon(date) != wantPhase { + t.Fatalf("model %d: rise or phase escaped context", model) + } + gotSolar, gotSolarOK := context.Eclipse().SolarEclipseOnDate(solarDate) + gotLunar, gotLunarOK := context.Eclipse().LunarEclipseOnDate(time.Date(2024, 3, 25, 0, 0, 0, 0, time.UTC)) + if gotSolarOK != wantSolarOK || !reflect.DeepEqual(gotSolar, wantSolar) || gotLunarOK != wantLunarOK || !reflect.DeepEqual(gotLunar, wantLunar) { + t.Fatalf("model %d: eclipse escaped context\nsolar got=%+v\nsolar want=%+v\nlunar got=%+v\nlunar want=%+v", model, gotSolar, wantSolar, gotLunar, wantLunar) + } + gotJSON, gotJSONErr := context.GeoJSON().MarshalLunarEclipse(wantLunar, 24) + if (gotJSONErr == nil) != (wantJSONErr == nil) || !bytes.Equal(gotJSON, wantJSON) { + t.Fatalf("model %d: lunar GeoJSON differs: %v/%v", model, gotJSONErr, wantJSONErr) + } + } +} + +func TestContextConcurrentReadAndLazyModules(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + context := mustContext(t, astro.ModelSemianalytic) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + want := mustContext(t, astro.ModelSemianalytic).Moon().ApparentLo(date) + var wait sync.WaitGroup + for worker := 0; worker < 12; worker++ { + wait.Add(1) + go func() { + defer wait.Done() + for iteration := 0; iteration < 12; iteration++ { + if context.Moon() != context.Moon() || context.Sun() != context.Sun() || context.Mercury() != context.Mercury() || context.Venus() != context.Venus() || context.Mars() != context.Mars() || context.Jupiter() != context.Jupiter() || context.Saturn() != context.Saturn() || context.Uranus() != context.Uranus() || context.Neptune() != context.Neptune() || context.Eclipse() != context.Eclipse() || context.GeoJSON() != context.GeoJSON() { + t.Error("lazy module identity changed") + } + if context.Moon().ApparentLo(date) != want { + t.Error("concurrent context calculation changed") + } + } + }() + } + for iteration := 0; iteration < 12; iteration++ { + astro.SetModel(astro.Model(iteration % 2)) + } + wait.Wait() +} + +func TestContextSolarShadowCacheAndGeoJSONIsolation(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + date := time.Date(2024, 4, 8, 18, 17, 0, 0, time.UTC) + options := eclipse.SolarEclipseShadowSolverOptions{BoundaryPoints: 24, TargetSpacingKM: -1} + for _, model := range []astro.Model{astro.ModelLegacy, astro.ModelSemianalytic} { + astro.SetModel(model) + wantSolver := eclipse.NewSolarEclipseShadowSolver(options) + want, wantOK := wantSolver.ShadowAt(date) + if !wantOK || want.Empty() { + t.Fatal("known solar eclipse has no shadow") + } + wantJSON, wantErr := geojson.MarshalSolarEclipseShadowInstant(want) + context := mustContext(t, model) + solver := context.Eclipse().NewSolarEclipseShadowSolver(options) + before, beforeOK := solver.ShadowAt(date) + astro.SetModel(astro.Model(1 - int(model))) + after, afterOK := solver.ShadowAt(date) + if !beforeOK || !afterOK || !reflect.DeepEqual(before, want) || !reflect.DeepEqual(after, want) { + t.Fatalf("model %d: shadow solver cache escaped instance", model) + } + gotJSON, gotErr := context.GeoJSON().MarshalSolarEclipseShadowInstant(after) + if wantErr != nil || gotErr != nil || !bytes.Equal(gotJSON, wantJSON) { + t.Fatalf("model %d: solar GeoJSON escaped context: %v/%v", model, gotErr, wantErr) + } + } +} + +func TestContextPackageCoordinateAllocations(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []astro.Model{astro.ModelLegacy, astro.ModelSemianalytic} { + astro.SetModel(model) + context := mustContext(t, model) + for _, test := range contextScalars() { + if count := testing.AllocsPerRun(10, func() { test.global(date) }); count != 0 { + t.Errorf("model %d %s package allocations=%g", model, test.name, count) + } + if count := testing.AllocsPerRun(10, func() { test.bound(context, date) }); count != 0 { + t.Errorf("model %d %s warm context allocations=%g", model, test.name, count) + } + } + } +} + +func TestContextPackageEventAllocations(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + astro.SetModel(astro.ModelLegacy) + date := time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC) + for _, test := range []struct { + name string + compute func() + maximum float64 + }{ + {"moon-rise", func() { moon.RiseTime(date, 116.4, 39.9, 0, true) }, 0}, + {"sun-rise", func() { sun.RiseTime(date, 116.4, 39.9, 0, true) }, 0}, + {"moon-phase", func() { moon.NextNewMoon(date) }, 0}, + {"venus-event", func() { venus.NextConjunction(date) }, 0}, + {"solar-eclipse", func() { eclipse.SolarEclipseOnDate(date) }, 27}, + } { + if allocations := testing.AllocsPerRun(5, test.compute); allocations > test.maximum { + t.Errorf("%s allocations=%g, maximum=%g", test.name, allocations, test.maximum) + } + } +} + +type contextRejectingEngine struct{} + +type contextRotatedEngine struct { + astro.Engine + apparent astro.ApparentEngine +} + +func (engine contextRotatedEngine) Apparent(target astro.Body, jdTT float64) ([3]float64, float64, float64, error) { + direction, distance, delay, err := engine.apparent.Apparent(target, jdTT) + if err != nil { + return direction, distance, delay, err + } + sine, cosine := math.Sincos(0.01) + direction = [3]float64{cosine*direction[0] - sine*direction[1], sine*direction[0] + cosine*direction[1], direction[2]} + return direction, distance, delay, nil +} + +func TestContextCustomApparentEngineIsolation(t *testing.T) { + previous := astro.GetModel() + t.Cleanup(func() { astro.SetModel(previous) }) + engine := mustInstance(t, astro.ModelSemianalytic).Engine() + apparent, ok := engine.(astro.ApparentEngine) + if !ok { + t.Fatal("built-in semi-analytic engine lacks apparent positions") + } + customEngine := contextRotatedEngine{Engine: engine, apparent: apparent} + context := mustContext(t, astro.ModelCustom, customEngine) + instance := astro.NewInstance(customEngine) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + jde := basic.UTC2TT(basic.Date2JD(date)) + want := basic.HMoonApparentLoWithInstance(jde, instance) + ordinary := mustContext(t, astro.ModelSemianalytic).Moon().ApparentLo(date) + for _, model := range []astro.Model{astro.ModelLegacy, astro.ModelSemianalytic} { + astro.SetModel(model) + if got := context.Moon().ApparentLo(date); got != want || math.Abs(got-ordinary) < 0.01 { + t.Fatalf("custom engine ignored or escaped context: got %.12f, want %.12f, ordinary %.12f", got, want, ordinary) + } + } +} + +func (*contextRejectingEngine) State(target, center astro.Body, jdTDB float64) ([3]float64, [3]float64, error) { + return [3]float64{}, [3]float64{}, astro.ErrEphemerisOutOfRange +} + +func (*contextRejectingEngine) Coverage(target astro.Body) (float64, float64, bool) { + return 0, 0, false +} + +func TestContextCustomEngineAndInvalidConstruction(t *testing.T) { + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + context := mustContext(t, astro.ModelCustom, &contextRejectingEngine{}) + if context.Model() != astro.ModelCustom || !math.IsNaN(context.Moon().ApparentLo(date)) || !math.IsNaN(context.Sun().ApparentLo(date)) || !math.IsNaN(context.Venus().SunDistance(date)) { + t.Fatal("failed custom engine silently fell back to a built-in model") + } + var typedNil *contextRejectingEngine + contextErr := func(model astro.Model, engines ...astro.Engine) func() error { + return func() error { + context, err := astro.NewContext(model, engines...) + if context != nil { + t.Error("failed construction returned a context") + } + return err + } + } + stackErr := func(engines ...astro.Engine) func() error { + return func() error { + context, err := astro.NewStackContext(engines...) + if context != nil { + t.Error("failed construction returned a context") + } + return err + } + } + for _, test := range []struct { + name string + construct func() error + want error + }{ + {"unknown", contextErr(astro.Model(99)), astro.ErrUnknownModel}, + {"missing-engine", contextErr(astro.ModelCustom), astro.ErrInvalidEngine}, + {"nil-engine", contextErr(astro.ModelCustom, nil), astro.ErrInvalidEngine}, + {"typed-nil-engine", contextErr(astro.ModelCustom, typedNil), astro.ErrInvalidEngine}, + {"extra-engine", contextErr(astro.ModelLegacy, &contextRejectingEngine{}), astro.ErrInvalidEngine}, + {"stack-nil-engine", stackErr(&contextRejectingEngine{}, nil), astro.ErrInvalidEngine}, + {"global-custom", func() error { return astro.SetModel(astro.ModelCustom) }, astro.ErrUnknownModel}, + } { + t.Run(test.name, func(t *testing.T) { + if err := test.construct(); !errors.Is(err, test.want) { + t.Errorf("err = %v, want %v", err, test.want) + } + }) + } + before := astro.GetModel() + if astro.SetModel(astro.ModelCustom) == nil || astro.GetModel() != before { + t.Error("rejected SetModel changed the global model") + } +} + +func TestZeroContextHasNoEngine(t *testing.T) { + var zero astro.Context + var nilContext *astro.Context + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for name, context := range map[string]*astro.Context{"zero": &zero, "nil": nilContext} { + if context.Model() != astro.ModelCustom || !math.IsNaN(context.Moon().ApparentLo(date)) || !math.IsNaN(context.Sun().ApparentLo(date)) || + !math.IsNaN(context.Venus().SunDistance(date)) { + t.Fatalf("%s context must have no engine", name) + } + if context.Mercury() == nil || context.Mars() == nil || context.Jupiter() == nil || context.Saturn() == nil || context.Uranus() == nil || + context.Neptune() == nil || context.Eclipse() == nil || context.GeoJSON() == nil || context.Phenomena() == nil { + t.Fatalf("%s context accessor returned nil", name) + } + if _, err := context.Sun().RiseTime(date, 116.4, 39.9, 0, true); !errors.Is(err, basic.ErrInvalidObservationInput) { + t.Fatalf("%s context rise/set err = %v", name, err) + } + if limited := context.WithLunarLimb(nil); limited == nil || limited.Model() != astro.ModelCustom { + t.Fatalf("%s context WithLunarLimb lost the empty instance", name) + } + } +} diff --git a/coord/coord.go b/coord/coord.go index fa7bd87..c2dce4b 100644 --- a/coord/coord.go +++ b/coord/coord.go @@ -67,6 +67,16 @@ func Nutation2000B(date time.Time) (longitude, obliquity float64) { return basic.Nutation2000B(jdUTC(date)) } +// Nutation2000A IAU 2000A 章动,度;仅显式调用时使用,默认链路仍为 2000B / IAU 2000A nutation, opt-in. +func Nutation2000A(date time.Time) (longitude, obliquity float64) { + return basic.Nutation2000A(jdUTC(date)) +} + +// MeanObliquity2006 IAU 2006 平黄赤交角,度 / IAU 2006 mean obliquity in degrees. +func MeanObliquity2006(date time.Time) float64 { + return basic.MeanObliquity2006(jdUTC(date)) +} + // Nutation1980 IAU 1980 章动 / IAU 1980 nutation. func Nutation1980(date time.Time) (longitude, obliquity float64) { return basic.Nutation1980(jdUTC(date)) diff --git a/doc/img/lunar-eclipse-2026-03-03-detailed-en.svg b/doc/img/lunar-eclipse-2026-03-03-detailed-en.svg index 8095580..616fb08 100644 --- a/doc/img/lunar-eclipse-2026-03-03-detailed-en.svg +++ b/doc/img/lunar-eclipse-2026-03-03-detailed-en.svg @@ -1,2 +1,2 @@ -Total Lunar Eclipse of 2026-03-03Greatest Eclipse = 19:33:42 (CST) | Umbral magnitude = 1.1506Penumbral magnitude = 2.1837 | Gamma = 0.3764P. Radius = 1.2361° | U. Radius = 0.6983° | Axis = 0.3596°Saros series = 133 | member 27 of 71All times are UTC (shown in CST, UTC+08:00)Sun at greatest eclipse赤经 R.A.22h56m57.4s赤纬 Dec.-06°42'58.0"视半径 S.D.00°16'08.0"H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.10h56m15.1s赤纬 Dec.+06°24'05.2"视半径 S.D.00°15'36.7"H.P.00°57'18.7"Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Eclipse durationsPenumbral338:40Partial207:10Total58:18Eclipse contactsP1 penumbral begins16:44:25U1 partial begins17:50:05U2 total begins19:04:32Greatest19:33:42U3 total ends20:02:50U4 partial ends21:17:16P4 penumbral ends22:23:050100角分Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleShadow-path diagram uses the Danjon shadow model; the lower map marks where the whole eclipse is visible, where the Moon rises orsets eclipsed, and where it is not visible. Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +Total Lunar Eclipse of 2026-03-03Greatest Eclipse = 19:33:42 (CST) | Umbral magnitude = 1.1506Penumbral magnitude = 2.1837 | Gamma = 0.3765P. Radius = 1.2361° | U. Radius = 0.6983° | Axis = 0.3596°Saros series = 133 | member 27 of 71All times are UTC (shown in CST, UTC+08:00). Circular limb.Sun at greatest eclipse赤经 R.A.22h56m57.4s赤纬 Dec.-06°42'58.0"视半径 S.D.00°16'08.0"H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.10h56m15.0s赤纬 Dec.+06°24'05.4"视半径 S.D.00°15'36.7"H.P.00°57'18.7"Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Eclipse durationsPenumbral338:40Partial207:10Total58:18Eclipse contactsP1 penumbral begins16:44:25U1 partial begins17:50:05U2 total begins19:04:32Greatest19:33:42U3 total ends20:02:50U4 partial ends21:17:16P4 penumbral ends22:23:050100角分Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleShadow-path diagram uses the Danjon shadow model; the lower map marks where the whole eclipse is visible, where the Moon rises orsets eclipsed, and where it is not visible. Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2026-03-03-detailed.svg b/doc/img/lunar-eclipse-2026-03-03-detailed.svg index 3961364..40f370f 100644 --- a/doc/img/lunar-eclipse-2026-03-03-detailed.svg +++ b/doc/img/lunar-eclipse-2026-03-03-detailed.svg @@ -1,2 +1,2 @@ -2026-03-03 月全食食甚 = 19:33:42(CST)| 本影食分 = 1.1506半影食分 = 2.1837 | 伽马 = 0.3764半影半径 = 1.2361° | 本影半径 = 0.6983° | 影轴角距 = 0.3596°沙罗序列 = 133 | 第 27 / 71 个成员图中时刻为 UTC(显示时区 CST,UTC+08:00)食甚时的太阳(地心坐标)赤经 R.A.22h56m57.4s赤纬 Dec.-06°42'58.0"视半径 S.D.00°16'08.0"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.10h56m15.1s赤纬 Dec.+06°24'05.2"视半径 S.D.00°15'36.7"地平视差 H.P.00°57'18.7"地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终月食历时半影食338:40偏食207:10全食58:18接触时刻P1 半影食始16:44:25U1 初亏17:50:05U2 食既19:04:32食甚19:33:42U3 生光20:02:50U4 复圆21:17:16P4 半影食终22:23:050100角分全程可见带食月出半影月出带食月落半影月落不可见穿影示意图使用 Danjon 影半径模型;下方底图区分全程可见、带食月出、带食月落与不可见四类区域。Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2026-03-03 月全食食甚 = 19:33:42(CST)| 本影食分 = 1.1506半影食分 = 2.1837 | 伽马 = 0.3765半影半径 = 1.2361° | 本影半径 = 0.6983° | 影轴角距 = 0.3596°沙罗序列 = 133 | 第 27 / 71 个成员图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。食甚时的太阳(地心坐标)赤经 R.A.22h56m57.4s赤纬 Dec.-06°42'58.0"视半径 S.D.00°16'08.0"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.10h56m15.0s赤纬 Dec.+06°24'05.4"视半径 S.D.00°15'36.7"地平视差 H.P.00°57'18.7"地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终月食历时半影食338:40偏食207:10全食58:18接触时刻P1 半影食始16:44:25U1 初亏17:50:05U2 食既19:04:32食甚19:33:42U3 生光20:02:50U4 复圆21:17:16P4 半影食终22:23:050100角分全程可见带食月出半影月出带食月落半影月落不可见穿影示意图使用 Danjon 影半径模型;下方底图区分全程可见、带食月出、带食月落与不可见四类区域。Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2026-03-03-en.svg b/doc/img/lunar-eclipse-2026-03-03-en.svg index 966156f..c7b10d4 100644 --- a/doc/img/lunar-eclipse-2026-03-03-en.svg +++ b/doc/img/lunar-eclipse-2026-03-03-en.svg @@ -1,2 +1,2 @@ -2026-03-03 Total Lunar Eclipsetype=Total Lunar Eclipse penumbral=2.1837 umbral=1.1506Maximum: 2026-03-03 19:33:42 CSTMoon: RA 10h56m15s Dec +06°24′05″ ecl.lon 162.8597 deg ecl.lat -0.3578 deg LeoPenumbral duration 05:38:40 Umbral duration 03:27:10 Total duration 00:58:18Lunar Saros 133 27/71Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Contacts (CST)P1 Penumbral begins 16:44:25 PA 104.3°U1 Partial begins 17:50:05 PA 96.2°U2 Total begins 19:04:32 PA 243.0°GE Greatest 19:33:42U3 Total ends 20:02:50 PA 173.4°U4 Partial ends 21:17:16 PA 320.3°P4 Penumbral ends 22:23:05 PA 312.1°North is up and east is left; the ecliptic is projected near greatest eclipse.Moon disks and shadow radii are drawn to the same relative angular-radius scale.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2026-03-03 Total Lunar Eclipsetype=Total Lunar Eclipse penumbral=2.1837 umbral=1.1506Maximum: 2026-03-03 19:33:42 CSTMoon: RA 10h56m15s Dec +06°24′05″ ecl.lon 162.8596 deg ecl.lat -0.3578 deg LeoPenumbral duration 05:38:40 Umbral duration 03:27:10 Total duration 00:58:18Lunar Saros 133 27/71Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Contacts (CST)P1 Penumbral begins 16:44:25 PA 104.3°U1 Partial begins 17:50:05 PA 96.2°U2 Total begins 19:04:32 PA 243.0°GE Greatest 19:33:42U3 Total ends 20:02:50 PA 173.4°U4 Partial ends 21:17:16 PA 320.3°P4 Penumbral ends 22:23:05 PA 312.1°North is up and east is left; the ecliptic is projected near greatest eclipse.Moon disks and shadow radii are drawn to the same relative angular-radius scale.All times are UTC (shown in CST, UTC+08:00). Circular limb. \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2026-03-03.svg b/doc/img/lunar-eclipse-2026-03-03.svg index 2325443..4374415 100644 --- a/doc/img/lunar-eclipse-2026-03-03.svg +++ b/doc/img/lunar-eclipse-2026-03-03.svg @@ -1,2 +1,2 @@ -2026-03-03 月全食食型=月全食 半影食分=2.1837 本影食分=1.1506食甚:2026-03-03 19:33:42 CST月球:赤经 10h56m15s 赤纬 +06°24′05″ 黄经 162.8597° 黄纬 -0.3578° 狮子座半影历时 05:38:40 本影历时 03:27:10 全食历时 00:58:18沙罗 133 第 27/71 个成员地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终接触时刻 (CST)P1 半影始 16:44:25 方位 104.3°U1 初亏 17:50:05 方位 96.2°U2 食既 19:04:32 方位 243.0°GE 食甚 19:33:42U3 生光 20:02:50 方位 173.4°U4 复圆 21:17:16 方位 320.3°P4 半影终 22:23:05 方位 312.1°上北下南,左东右西;黄道按食甚附近天球投影绘制。图中月面大小和影半径均按实际相对角半径缩放。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2026-03-03 月全食食型=月全食 半影食分=2.1837 本影食分=1.1506食甚:2026-03-03 19:33:42 CST月球:赤经 10h56m15s 赤纬 +06°24′05″ 黄经 162.8596° 黄纬 -0.3578° 狮子座半影历时 05:38:40 本影历时 03:27:10 全食历时 00:58:18沙罗 133 第 27/71 个成员地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终接触时刻 (CST)P1 半影始 16:44:25 方位 104.3°U1 初亏 17:50:05 方位 96.2°U2 食既 19:04:32 方位 243.0°GE 食甚 19:33:42U3 生光 20:02:50 方位 173.4°U4 复圆 21:17:16 方位 320.3°P4 半影终 22:23:05 方位 312.1°上北下南,左东右西;黄道按食甚附近天球投影绘制。图中月面大小和影半径均按实际相对角半径缩放。图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。 \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01-detailed-en.svg b/doc/img/lunar-eclipse-2029-01-01-detailed-en.svg index dd466b3..1be9c68 100644 --- a/doc/img/lunar-eclipse-2029-01-01-detailed-en.svg +++ b/doc/img/lunar-eclipse-2029-01-01-detailed-en.svg @@ -1,2 +1,2 @@ -Total Lunar Eclipse of 2029-01-01Greatest Eclipse = 00:52:05 (CST) | Umbral magnitude = 1.2461Penumbral magnitude = 2.2740 | Gamma = 0.3258P. Radius = 1.2511° | U. Radius = 0.7089° | Axis = 0.3153°Saros series = 125 | member 49 of 72All times are UTC (shown in CST, UTC+08:00)Sun at greatest eclipse赤经 R.A.18h45m53.3s赤纬 Dec.-23°01'01.1"视半径 S.D.00°16'15.9"H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.06h46m08.4s赤纬 Dec.+23°19'37.5"视半径 S.D.00°15'49.1"H.P.00°58'04.3"Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Eclipse durationsPenumbral336:17Partial208:50Total71:19Eclipse contactsP1 penumbral begins22:03:54U1 partial begins23:07:42U2 total begins00:16:27Greatest00:52:05U3 total ends01:27:46U4 partial ends02:36:32P4 penumbral ends03:40:110100角分Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleShadow-path diagram uses the Danjon shadow model; the lower map marks where the whole eclipse is visible, where the Moon rises orsets eclipsed, and where it is not visible. Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +Total Lunar Eclipse of 2029-01-01Greatest Eclipse = 00:52:05 (CST) | Umbral magnitude = 1.2461Penumbral magnitude = 2.2740 | Gamma = 0.3258P. Radius = 1.2511° | U. Radius = 0.7089° | Axis = 0.3153°Saros series = 125 | member 49 of 72All times are UTC (shown in CST, UTC+08:00). Circular limb.Sun at greatest eclipse赤经 R.A.18h45m53.3s赤纬 Dec.-23°01'01.1"视半径 S.D.00°16'15.9"H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.06h46m08.4s赤纬 Dec.+23°19'37.5"视半径 S.D.00°15'49.1"H.P.00°58'04.3"Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Eclipse durationsPenumbral336:17Partial208:50Total71:19Eclipse contactsP1 penumbral begins22:03:54U1 partial begins23:07:42U2 total begins00:16:27Greatest00:52:05U3 total ends01:27:46U4 partial ends02:36:32P4 penumbral ends03:40:110100角分Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleShadow-path diagram uses the Danjon shadow model; the lower map marks where the whole eclipse is visible, where the Moon rises orsets eclipsed, and where it is not visible. Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01-detailed.svg b/doc/img/lunar-eclipse-2029-01-01-detailed.svg index 7cf3b53..83566ef 100644 --- a/doc/img/lunar-eclipse-2029-01-01-detailed.svg +++ b/doc/img/lunar-eclipse-2029-01-01-detailed.svg @@ -1,2 +1,2 @@ -2029-01-01 月全食食甚 = 00:52:05(CST)| 本影食分 = 1.2461半影食分 = 2.2740 | 伽马 = 0.3258半影半径 = 1.2511° | 本影半径 = 0.7089° | 影轴角距 = 0.3153°沙罗序列 = 125 | 第 49 / 72 个成员图中时刻为 UTC(显示时区 CST,UTC+08:00)食甚时的太阳(地心坐标)赤经 R.A.18h45m53.3s赤纬 Dec.-23°01'01.1"视半径 S.D.00°16'15.9"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.06h46m08.4s赤纬 Dec.+23°19'37.5"视半径 S.D.00°15'49.1"地平视差 H.P.00°58'04.3"地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终月食历时半影食336:17偏食208:50全食71:19接触时刻P1 半影食始22:03:54U1 初亏23:07:42U2 食既00:16:27食甚00:52:05U3 生光01:27:46U4 复圆02:36:32P4 半影食终03:40:110100角分全程可见带食月出半影月出带食月落半影月落不可见穿影示意图使用 Danjon 影半径模型;下方底图区分全程可见、带食月出、带食月落与不可见四类区域。Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2029-01-01 月全食食甚 = 00:52:05(CST)| 本影食分 = 1.2461半影食分 = 2.2740 | 伽马 = 0.3258半影半径 = 1.2511° | 本影半径 = 0.7089° | 影轴角距 = 0.3153°沙罗序列 = 125 | 第 49 / 72 个成员图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。食甚时的太阳(地心坐标)赤经 R.A.18h45m53.3s赤纬 Dec.-23°01'01.1"视半径 S.D.00°16'15.9"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.06h46m08.4s赤纬 Dec.+23°19'37.5"视半径 S.D.00°15'49.1"地平视差 H.P.00°58'04.3"地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终月食历时半影食336:17偏食208:50全食71:19接触时刻P1 半影食始22:03:54U1 初亏23:07:42U2 食既00:16:27食甚00:52:05U3 生光01:27:46U4 复圆02:36:32P4 半影食终03:40:110100角分全程可见带食月出半影月出带食月落半影月落不可见穿影示意图使用 Danjon 影半径模型;下方底图区分全程可见、带食月出、带食月落与不可见四类区域。Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01-en.svg b/doc/img/lunar-eclipse-2029-01-01-en.svg index 7609c99..9ad98a9 100644 --- a/doc/img/lunar-eclipse-2029-01-01-en.svg +++ b/doc/img/lunar-eclipse-2029-01-01-en.svg @@ -1,2 +1,2 @@ -2029-01-01 Total Lunar Eclipsetype=Total Lunar Eclipse penumbral=2.2740 umbral=1.2461Maximum: 2029-01-01 00:52:05 CSTMoon: RA 06h46m08s Dec +23°19′37″ ecl.lon 100.5810 deg ecl.lat 0.3137 deg GeminiPenumbral duration 05:36:17 Umbral duration 03:28:50 Total duration 01:11:19Lunar Saros 125 49/72Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Contacts (CST)P1 Penumbral begins 22:03:54 PA 112.2°U1 Partial begins 23:07:42 PA 119.1°U2 Total begins 00:16:27 PA 325.4°GE Greatest 00:52:05U3 Total ends 01:27:46 PA 55.2°U4 Partial ends 02:36:32 PA 261.4°P4 Penumbral ends 03:40:11 PA 268.3°North is up and east is left; the ecliptic is projected near greatest eclipse.Moon disks and shadow radii are drawn to the same relative angular-radius scale.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2029-01-01 Total Lunar Eclipsetype=Total Lunar Eclipse penumbral=2.2740 umbral=1.2461Maximum: 2029-01-01 00:52:05 CSTMoon: RA 06h46m08s Dec +23°19′38″ ecl.lon 100.5808 deg ecl.lat 0.3137 deg GeminiPenumbral duration 05:36:17 Umbral duration 03:28:50 Total duration 01:11:19Lunar Saros 125 49/72Earth's PenumbraEarth's UmbraNEWSEclipticP1U1U2GreatestU3U4P4Contacts (CST)P1 Penumbral begins 22:03:54 PA 112.2°U1 Partial begins 23:07:42 PA 119.1°U2 Total begins 00:16:27 PA 325.3°GE Greatest 00:52:05U3 Total ends 01:27:46 PA 55.2°U4 Partial ends 02:36:32 PA 261.4°P4 Penumbral ends 03:40:11 PA 268.3°North is up and east is left; the ecliptic is projected near greatest eclipse.Moon disks and shadow radii are drawn to the same relative angular-radius scale.All times are UTC (shown in CST, UTC+08:00). Circular limb. \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01-global-en.svg b/doc/img/lunar-eclipse-2029-01-01-global-en.svg index 1f76a95..6c4b369 100644 --- a/doc/img/lunar-eclipse-2029-01-01-global-en.svg +++ b/doc/img/lunar-eclipse-2029-01-01-global-en.svg @@ -1 +1 @@ -2029-01-01 Total Lunar Eclipse Global VisibilityP1 22:03:54 | Greatest 00:52:05 | P4 03:40:11 (CST) | penumbral magnitude 2.274 | umbral magnitude 1.246Umbral phases U1 23:07:42 | U2 00:16:27 | U3 01:27:46 | U4 02:36:32Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleEquirectangular projection; P1/P4 Moon-visible hemispheres; Natural Earth 1:50m physical land, no administrative boundaries.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2029-01-01 Total Lunar Eclipse Global VisibilityP1 22:03:54 | Greatest 00:52:05 | P4 03:40:11 (CST) | penumbral magnitude 2.274 | umbral magnitude 1.246Umbral phases U1 23:07:42 | U2 00:16:27 | U3 01:27:46 | U4 02:36:32Entire eclipseMoonrise during eclipsePenumbra moonriseMoonset during eclipsePenumbra moonsetNot visibleEquirectangular projection; P1/P4 Moon-visible hemispheres; Natural Earth 1:50m physical land, no administrative boundaries.All times are UTC (shown in CST, UTC+08:00). Circular limb. \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01-global.svg b/doc/img/lunar-eclipse-2029-01-01-global.svg index 97af397..e7aa25b 100644 --- a/doc/img/lunar-eclipse-2029-01-01-global.svg +++ b/doc/img/lunar-eclipse-2029-01-01-global.svg @@ -1 +1 @@ -2029-01-01 月全食全球可见图P1 22:03:54 | 食甚 00:52:05 | P4 03:40:11 (CST) | 半影食分 2.274 | 本影食分 1.246本影阶段 U1 23:07:42 | U2 00:16:27 | U3 01:27:46 | U4 02:36:32全程可见带食月出半影月出带食月落半影月落不可见等经纬投影;按 P1/P4 月球可见半球分区;Natural Earth 1:50m 物理陆地底图,不含行政边界。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2029-01-01 月全食全球可见图P1 22:03:54 | 食甚 00:52:05 | P4 03:40:11 (CST) | 半影食分 2.274 | 本影食分 1.246本影阶段 U1 23:07:42 | U2 00:16:27 | U3 01:27:46 | U4 02:36:32全程可见带食月出半影月出带食月落半影月落不可见等经纬投影;按 P1/P4 月球可见半球分区;Natural Earth 1:50m 物理陆地底图,不含行政边界。图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。 \ No newline at end of file diff --git a/doc/img/lunar-eclipse-2029-01-01.svg b/doc/img/lunar-eclipse-2029-01-01.svg index ff892c9..c6ed621 100644 --- a/doc/img/lunar-eclipse-2029-01-01.svg +++ b/doc/img/lunar-eclipse-2029-01-01.svg @@ -1,2 +1,2 @@ -2029-01-01 月全食食型=月全食 半影食分=2.2740 本影食分=1.2461食甚:2029-01-01 00:52:05 CST月球:赤经 06h46m08s 赤纬 +23°19′37″ 黄经 100.5810° 黄纬 0.3137° 双子座半影历时 05:36:17 本影历时 03:28:50 全食历时 01:11:19沙罗 125 第 49/72 个成员地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终接触时刻 (CST)P1 半影始 22:03:54 方位 112.2°U1 初亏 23:07:42 方位 119.1°U2 食既 00:16:27 方位 325.4°GE 食甚 00:52:05U3 生光 01:27:46 方位 55.2°U4 复圆 02:36:32 方位 261.4°P4 半影终 03:40:11 方位 268.3°上北下南,左东右西;黄道按食甚附近天球投影绘制。图中月面大小和影半径均按实际相对角半径缩放。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2029-01-01 月全食食型=月全食 半影食分=2.2740 本影食分=1.2461食甚:2029-01-01 00:52:05 CST月球:赤经 06h46m08s 赤纬 +23°19′38″ 黄经 100.5808° 黄纬 0.3137° 双子座半影历时 05:36:17 本影历时 03:28:50 全食历时 01:11:19沙罗 125 第 49/72 个成员地球半影地球本影北东西南黄道P1 半影始U1 初亏U2 食既食甚U3 生光U4 复圆P4 半影终接触时刻 (CST)P1 半影始 22:03:54 方位 112.2°U1 初亏 23:07:42 方位 119.1°U2 食既 00:16:27 方位 325.3°GE 食甚 00:52:05U3 生光 01:27:46 方位 55.2°U4 复圆 02:36:32 方位 261.4°P4 半影终 03:40:11 方位 268.3°上北下南,左东右西;黄道按食甚附近天球投影绘制。图中月面大小和影半径均按实际相对角半径缩放。图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。 \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-detailed-en.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-detailed-en.svg index 6d03734..6bdcfad 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-detailed-en.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-detailed-en.svg @@ -1 +1 @@ -2025-06-05 Lunar Occultation of HR 4799Occultation begins 17:45:28 | Greatest 20:02:06 | Occultation ends 22:18:49 (CST)Greatest point 121.5660°E, 6.8071°N | Moon altitude +75.6°18:3019:0019:3020:0020:3021:0021:30StartGreatestEndVisible center lineGeometric center lineBand and limitsRise/set phase linesMoon (geocentric)R.A.12h38m32.1sDec.-05°46'28.0"S.D.00°14'48.0"H.P.00°54'19.9"Target bodyTargetHR 4799R.A.12h36m47.4sDec.-05°49'55.0"S.D.—Band path pointsPartial begins17:45:2863.9929°E, 39.0894°NGreatest20:02:06121.5660°E, 6.8071°NPartial ends22:18:49172.2910°E, 16.6450°SContactsPartial begins17:45:28Greatest20:02:06Partial ends22:18:49Ephemeris and constantsProjectionOrthographicΔT69.2 sMoon distance403584 kmPartial-band width3666.6 kmLibrationLibration l+3.53°Libration b+2.02°Axis position angle c21.62°Orthographic globe centred on the greatest occultation; Natural Earth 1:50m physical land, no administrative boundaries.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2025-06-05 Lunar Occultation of HR 4799Occultation begins 17:45:29 | Greatest 20:02:07 | Occultation ends 22:18:51 (CST)Greatest point 121.5611°E, 6.8082°N | Moon altitude +75.6°18:3019:0019:3020:0020:3021:0021:30StartGreatestEndVisible center lineGeometric center lineBand and limitsRise/set phase linesMoon (geocentric)R.A.12h38m32.1sDec.-05°46'27.9"S.D.00°14'48.0"H.P.00°54'19.9"Target bodyTargetHR 4799R.A.12h36m47.4sDec.-05°49'55.0"S.D.—Band path pointsPartial begins17:45:2964.0278°E, 39.0860°NGreatest20:02:07121.5611°E, 6.8082°NPartial ends22:18:51172.3100°E, 16.6414°SContactsPartial begins17:45:29Greatest20:02:07Partial ends22:18:51Ephemeris and constantsProjectionOrthographicΔT69.2 sMoon distance403584 kmPartial-band width3666.6 kmLibrationLibration l+3.53°Libration b+2.02°Axis position angle c21.62°Orthographic globe centred on the greatest occultation; Natural Earth 1:50m physical land, no administrative boundaries.All times are UTC (shown in CST, UTC+08:00). Circular lunar limb. \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-detailed.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-detailed.svg index 491a6bb..7001c32 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-detailed.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-detailed.svg @@ -1 +1 @@ -2025-06-05 月掩HR 4799全球掩带掩始 17:45:28 | 掩甚 20:02:06 | 掩终 22:18:49(CST)掩甚点 121.5660°E, 6.8071°N | 月球高度 +75.6°18:3019:0019:3020:0020:3021:0021:30掩始掩甚掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线月亮(地心坐标)赤经 R.A.12h38m32.1s赤纬 Dec.-05°46'28.0"视半径 S.D.00°14'48.0"地平视差 H.P.00°54'19.9"目标天体目标HR 4799赤经 R.A.12h36m47.4s赤纬 Dec.-05°49'55.0"视半径 S.D.—掩带路径点外掩始17:45:2863.9929°E, 39.0894°N掩甚20:02:06121.5660°E, 6.8071°N外掩终22:18:49172.2910°E, 16.6450°S接触时刻外掩始17:45:28掩甚20:02:06外掩终22:18:49历表与常数投影正射球面ΔT69.2 s月距403584 km部分掩带宽3666.6 km天平动经天平动 l+3.53°纬天平动 b+2.02°自转轴位置角 c21.62°正射球面投影,视点取掩甚点;Natural Earth 1:50m 物理陆地底图,不含行政边界。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2025-06-05 月掩HR 4799全球掩带掩始 17:45:29 | 掩甚 20:02:07 | 掩终 22:18:51(CST)掩甚点 121.5611°E, 6.8082°N | 月球高度 +75.6°18:3019:0019:3020:0020:3021:0021:30掩始掩甚掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线月亮(地心坐标)赤经 R.A.12h38m32.1s赤纬 Dec.-05°46'27.9"视半径 S.D.00°14'48.0"地平视差 H.P.00°54'19.9"目标天体目标HR 4799赤经 R.A.12h36m47.4s赤纬 Dec.-05°49'55.0"视半径 S.D.—掩带路径点外掩始17:45:2964.0278°E, 39.0860°N掩甚20:02:07121.5611°E, 6.8082°N外掩终22:18:51172.3100°E, 16.6414°S接触时刻外掩始17:45:29掩甚20:02:07外掩终22:18:51历表与常数投影正射球面ΔT69.2 s月距403584 km部分掩带宽3666.6 km天平动经天平动 l+3.53°纬天平动 b+2.02°自转轴位置角 c21.62°正射球面投影,视点取掩甚点;Natural Earth 1:50m 物理陆地底图,不含行政边界。图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。 \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-global-en.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-global-en.svg index fa03bd3..7fade63 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-global-en.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-global-en.svg @@ -1 +1 @@ -2025-06-05 Lunar Occultation of HR 4799Start 2025-06-05 17:45:28.4 | Greatest 20:02:06.3 | End 22:18:49.9 (CST)Greatest point 121.5660°E, 6.8071°N | path width 3666.6 km | Moon altitude +75.6°Global center line and occultation limits18:3019:0019:3020:0020:3021:0021:30StartGreatestEndVisible center lineGeometric center lineBand and limitsRise/set phase linesGlobal eventsStart17:45:28.463.9929°E, 39.0894°NMoon alt. +0.3°Greatest20:02:06.3121.5660°E, 6.8071°NMoon alt. +75.6°End22:18:49.9172.2910°E, 16.6450°SMoon alt. +0.3°Orthographic globe projection with Natural Earth 1:50m physical land and no administrative boundaries. Limits use the outer lunar limb on the Earth ellipsoid.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2025-06-05 Lunar Occultation of HR 4799Start 2025-06-05 17:45:29.8 | Greatest 20:02:07.6 | End 22:18:51.2 (CST)Greatest point 121.5611°E, 6.8082°N | path width 3666.6 km | Moon altitude +75.6°Global center line and occultation limits18:3019:0019:3020:0020:3021:0021:30StartGreatestEndVisible center lineGeometric center lineBand and limitsRise/set phase linesGlobal eventsStart17:45:29.864.0278°E, 39.0860°NMoon alt. +0.3°Greatest20:02:07.6121.5611°E, 6.8082°NMoon alt. +75.6°End22:18:51.2172.3100°E, 16.6414°SMoon alt. +0.3°Orthographic globe projection with Natural Earth 1:50m physical land and no administrative boundaries. Limits use the outer lunar limb on the Earth ellipsoid.All times are UTC (shown in CST, UTC+08:00). Circular lunar limb. \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-global.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-global.svg index c3e6772..8e9bb36 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-global.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-global.svg @@ -1 +1 @@ -2025-06-05 月掩HR 4799全球掩带掩始 2025-06-05 17:45:28.4 | 掩甚 20:02:06.3 | 掩终 22:18:49.9 (CST)掩甚点 121.5660°E, 6.8071°N | 掩带宽 3666.6 km | 月球高度 +75.6°全球中心线与掩带边界18:3019:0019:3020:0020:3021:0021:30掩始掩甚掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线全球事件掩始17:45:28.463.9929°E, 39.0894°N月球高度 +0.3°掩甚20:02:06.3121.5660°E, 6.8071°N月球高度 +75.6°掩终22:18:49.9172.2910°E, 16.6450°S月球高度 +0.3°正射球面投影;Natural Earth 1:50m 物理陆地底图,不含行政边界;掩带边界为地球椭球上的月球外缘投影。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2025-06-05 月掩HR 4799全球掩带掩始 2025-06-05 17:45:29.8 | 掩甚 20:02:07.6 | 掩终 22:18:51.2 (CST)掩甚点 121.5611°E, 6.8082°N | 掩带宽 3666.6 km | 月球高度 +75.6°全球中心线与掩带边界18:3019:0019:3020:0020:3021:0021:30掩始掩甚掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线全球事件掩始17:45:29.864.0278°E, 39.0860°N月球高度 +0.3°掩甚20:02:07.6121.5611°E, 6.8082°N月球高度 +75.6°掩终22:18:51.2172.3100°E, 16.6414°S月球高度 +0.3°正射球面投影;Natural Earth 1:50m 物理陆地底图,不含行政边界;掩带边界为地球椭球上的月球外缘投影。图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。 \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile-en.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile-en.svg new file mode 100644 index 0000000..c5db997 --- /dev/null +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile-en.svg @@ -0,0 +1,2 @@ + +2025-06-05 Limb Profile · Occultation of HR 4799Site 121.5660°E, 6.8071°N | elevation 0 m | 1 hidden interval(s) | step 200msThe star disappears at PA 138.9° (SE) and reappears at PA 317.4° (NW); panels show the two sides; PA measured from north…19:13:55138.9°19:14:00138.9°19:14:05138.9°19:14:10138.9°PA1horizontal ×5+7.9-5.420:50:10317.4°20:50:15317.4°20:50:20317.4°PA2horizontal ×5+7.9-5.4Disappearances / reappearances1 Disappears19:13:59.652 Reappears20:50:11.63Limb height toward the starStar above the mean limb (km)Star hiddenOn the limb (N up, E left)NEHeights are relative to the 1737.4 km reference sphere; the limb is interpolatedfrom the profile table and events shorter than the step may be missed.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile.svg new file mode 100644 index 0000000..49f7b93 --- /dev/null +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-limb-profile.svg @@ -0,0 +1,2 @@ + +2025-06-05 精密月掩月缘剖面 · HR 4799观测点 121.5660°E, 6.8071°N | 椭球高 0 米 | 遮挡 1 段 | 采样 200ms恒星于 PA 138.9°(东南)掩入、PA 317.4°(西北)复现;上下两栏分别为掩入侧与复现侧,PA 自天北向东量19:13:55138.9°19:14:00138.9°19:14:05138.9°19:14:10138.9°PA1横向放大约 5 倍+7.9-5.420:50:10317.4°20:50:15317.4°20:50:20317.4°PA2横向放大约 5 倍+7.9-5.4掩入 / 复现1 掩入19:13:59.652 复现20:50:11.63恒星方向月缘高度恒星距参考圆月面(km)恒星被遮挡月缘位置(上北左东)北东纵轴为月面上相对参考球 1737.4 km 的高度;月缘按剖面表插值,短于采样步长的遮挡可能漏检。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-local-en.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-local-en.svg index 593d745..5a84d84 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-local-en.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-local-en.svg @@ -1,2 +1,2 @@ -2025-06-05 Local Lunar Occultation of HR 4799Site 121.5660°E, 6.8071°N | elevation 0 m | total | duration 1h 36m 09.6sGreatest 2025-06-05 20:02:06.3 CST | minimum separation 0.01 arcsec | Moon altitude +75.6° azimuth 207.9°Topocentric star trackNEWSLunar pathImmersionGreatestEmersionLocal contactsImmersion19:14:01.0PA 138.9° | alt 76.4°az 157.7° | above horizonGreatest20:02:06.3PA 223.3° | alt 75.6°az 207.9° | above horizonEmersion20:50:10.7PA 317.4° | alt 67.3°az 235.5° | above horizonContact stagesImmersion19:14:01.0Greatest20:02:06.3Emersion20:50:10.7Moon fixed at center; east is left and north is up. The blue-gray dashedline is the local lunar path; the red dashed line is the star track.The star is a point source; immersion and emersion occur where it crosses the topocentricapparent lunar limb. Lunar texture is schematic. All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2025-06-05 Local Lunar Occultation of HR 4799Site 121.5660°E, 6.8071°N | elevation 0 m | total | duration 1h 36m 09.7sGreatest 2025-06-05 20:02:08.3 CST | minimum separation 0.16 arcsec | Moon altitude +75.6° azimuth 207.9°Topocentric star trackNEWSLunar pathImmersionGreatestEmersionLocal contactsImmersion19:14:03.1PA 138.9° | alt 76.4°az 157.7° | above horizonGreatest20:02:08.3PA 48.5° | alt 75.6°az 207.9° | above horizonEmersion20:50:12.7PA 317.4° | alt 67.3°az 235.5° | above horizonContact stagesImmersion19:14:03.1Greatest20:02:08.3Emersion20:50:12.7Moon fixed at center; east is left and north is up. The blue-gray dashedline is the local lunar path; the red dashed line is the star track.The star is a point source; immersion and emersion occur where it crosses the topocentric apparentlunar limb. Lunar texture is schematic. All times are UTC (shown in CST, UTC+08:00). Circular limb. \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-local.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-local.svg index 063c124..41a5f03 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-local.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-local.svg @@ -1,2 +1,2 @@ -2025-06-05 指定地点月掩HR 4799观测点 121.5660°E, 6.8071°N | 椭球高 0 米 | 全掩 | 掩星历时 1时36分09.6秒掩甚 2025-06-05 20:02:06.3 CST | 最小角距 0.01 角秒 | 月球高度 +75.6° 方位 207.9°站心恒星轨迹北东西南白道掩始掩甚掩终本地接触时刻掩始19:14:01.0PA 138.9° | 高度 76.4°方位 157.7° | 地平线上掩甚20:02:06.3PA 223.3° | 高度 75.6°方位 207.9° | 地平线上掩终20:50:10.7PA 317.4° | 高度 67.3°方位 235.5° | 地平线上掩始、掩甚与掩终视圆掩始19:14:01.0掩甚20:02:06.3掩终20:50:10.7月球固定在中心;图上左东右西,向上为北。灰蓝虚线为掩甚附近的站心白道,红色虚线为恒星相对月心轨迹。恒星按点光源绘制;掩始和掩终是恒星穿越站心月球视圆外缘的时刻,月面纹理仅作方向辅助。 图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2025-06-05 指定地点月掩HR 4799观测点 121.5660°E, 6.8071°N | 椭球高 0 米 | 全掩 | 掩星历时 1时36分09.7秒掩甚 2025-06-05 20:02:08.3 CST | 最小角距 0.16 角秒 | 月球高度 +75.6° 方位 207.9°站心恒星轨迹北东西南白道掩始掩甚掩终本地接触时刻掩始19:14:03.1PA 138.9° | 高度 76.4°方位 157.7° | 地平线上掩甚20:02:08.3PA 48.5° | 高度 75.6°方位 207.9° | 地平线上掩终20:50:12.7PA 317.4° | 高度 67.3°方位 235.5° | 地平线上掩始、掩甚与掩终视圆掩始19:14:03.1掩甚20:02:08.3掩终20:50:12.7月球固定在中心;图上左东右西,向上为北。灰蓝虚线为掩甚附近的站心白道,红色虚线为恒星相对月心轨迹。恒星按点光源绘制;掩始和掩终是恒星穿越站心月球视圆外缘的时刻,月面纹理仅作方向辅助。 图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。 \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar-en.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar-en.svg index 0bfcd9b..4554ad1 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar-en.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar-en.svg @@ -1 +1 @@ -2025-06-05 Lunar Occultation of HR 4799Start 2025-06-05 17:45:28.4 | Greatest 20:02:06.3 | End 22:18:49.9 (CST)Greatest point 121.5660°E, 6.8071°N | path width 3666.6 km | Moon altitude +75.6°Global center line and occultation limits21:0021:30EndVisible center lineGeometric center lineBand and limitsRise/set phase linesGlobal eventsStart17:45:28.463.9929°E, 39.0894°NMoon alt. +0.3°Greatest20:02:06.3121.5660°E, 6.8071°NMoon alt. +75.6°End22:18:49.9172.2910°E, 16.6450°SMoon alt. +0.3°South-polar azimuthal equidistant projection with Natural Earth 1:50m physical land and no administrative boundaries. Limits use the outer lunar limb on the Earth ellipsoid.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file +2025-06-05 Lunar Occultation of HR 4799Start 2025-06-05 17:45:29.8 | Greatest 20:02:07.6 | End 22:18:51.2 (CST)Greatest point 121.5611°E, 6.8082°N | path width 3666.6 km | Moon altitude +75.6°Global center line and occultation limits21:0021:30EndVisible center lineGeometric center lineBand and limitsRise/set phase linesGlobal eventsStart17:45:29.864.0278°E, 39.0860°NMoon alt. +0.3°Greatest20:02:07.6121.5611°E, 6.8082°NMoon alt. +75.6°End22:18:51.2172.3100°E, 16.6414°SMoon alt. +0.3°South-polar azimuthal equidistant projection with Natural Earth 1:50m physical land and no administrative boundaries. Limits use the outer lunar limb on the Earth ellipsoid.All times are UTC (shown in CST, UTC+08:00). Circular lunar limb. \ No newline at end of file diff --git a/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar.svg b/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar.svg index d7a6223..282178e 100644 --- a/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar.svg +++ b/doc/img/lunar-occultation-hr4799-2025-06-05-southpolar.svg @@ -1 +1 @@ -2025-06-05 月掩HR 4799全球掩带掩始 2025-06-05 17:45:28.4 | 掩甚 20:02:06.3 | 掩终 22:18:49.9 (CST)掩甚点 121.5660°E, 6.8071°N | 掩带宽 3666.6 km | 月球高度 +75.6°全球中心线与掩带边界21:0021:30掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线全球事件掩始17:45:28.463.9929°E, 39.0894°N月球高度 +0.3°掩甚20:02:06.3121.5660°E, 6.8071°N月球高度 +75.6°掩终22:18:49.9172.2910°E, 16.6450°S月球高度 +0.3°南极方位等距投影;Natural Earth 1:50m 物理陆地底图,不含行政边界;掩带边界为地球椭球上的月球外缘投影。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file +2025-06-05 月掩HR 4799全球掩带掩始 2025-06-05 17:45:29.8 | 掩甚 20:02:07.6 | 掩终 22:18:51.2 (CST)掩甚点 121.5611°E, 6.8082°N | 掩带宽 3666.6 km | 月球高度 +75.6°全球中心线与掩带边界21:0021:30掩终可见中心线几何中心线掩带范围与边界初掩/掩甚/终掩月升月落线全球事件掩始17:45:29.864.0278°E, 39.0860°N月球高度 +0.3°掩甚20:02:07.6121.5611°E, 6.8082°N月球高度 +75.6°掩终22:18:51.2172.3100°E, 16.6414°S月球高度 +0.3°南极方位等距投影;Natural Earth 1:50m 物理陆地底图,不含行政边界;掩带边界为地球椭球上的月球外缘投影。图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-arctic-2012-global-en.svg b/doc/img/solar-eclipse-arctic-2012-global-en.svg index 33ab32c..50ed5e9 100644 --- a/doc/img/solar-eclipse-arctic-2012-global-en.svg +++ b/doc/img/solar-eclipse-arctic-2012-global-en.svg @@ -1 +1 @@ -2012-05-21 Annular Solar Eclipse Global VisibilityPartial begins 04:56:08 | Greatest 07:52:47 | Partial ends 10:49:21 (CST) | magnitude 0.944 | Gamma 0.4828central path width 237.1 km | Saros series 128, member 58/73 | Sun alt 60.9° az 171.0° | central duration 05:46Geocentric conjunction (equal apparent right ascension) = 23:59:36.1 UT | J.D. = 2456068.499717All times are UTC (shown in CST, UTC+08:00)06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3009:0009:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityAnnular pathCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.03h52m30.8s赤纬 Dec.+20°39'06.6"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"Penumbral contactsP1 partial begins04:56:08P4 partial ends10:49:21Umbral contactsU1 umbra begins06:06:18U2 internal contact06:11:48U3 internal contact09:33:42U4 umbra ends09:39:10Local circumstances at greatestGreatest07:52:47Local magnitude0.9439Central path width237.1 kmCentral duration05:46Central begins06:09:02Central ends09:36:27Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-1.31°Libration b-0.56°Axis position angle c-13.68°Brown lunation1106020000 kmScaleNorth-polar azimuthal equidistant projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2012-05-21 Annular Solar Eclipse Global VisibilityPartial begins 04:56:07 | Greatest 07:52:47 | Partial ends 10:49:21 (CST) | magnitude 0.944 | Gamma 0.4828central path width 237.1 km | Saros series 128, member 58/73 | Sun alt 60.9° az 171.0° | central duration 05:46Geocentric conjunction (equal apparent right ascension) = 23:59:37.4 UT | J.D. = 2456068.499733All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3009:0009:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityAnnular pathCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.03h52m30.7s赤纬 Dec.+20°39'06.2"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"Penumbral contactsP1 partial begins04:56:07P4 partial ends10:49:21Umbral contactsU1 umbra begins06:06:17U2 internal contact06:11:47U3 internal contact09:33:42U4 umbra ends09:39:10Local circumstances at greatestGreatest07:52:47Local magnitude0.9439Central path width237.1 kmCentral duration05:46Central begins06:09:01Central ends09:36:28Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-1.31°Libration b-0.56°Axis position angle c-13.68°Brown lunation1106020000 kmScaleNorth-polar azimuthal equidistant projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-arctic-2012-global.svg b/doc/img/solar-eclipse-arctic-2012-global.svg index ca7eeb7..edea0ed 100644 --- a/doc/img/solar-eclipse-arctic-2012-global.svg +++ b/doc/img/solar-eclipse-arctic-2012-global.svg @@ -1 +1 @@ -2012-05-21 日环食全球见食图偏食始 04:56:08 | 食甚 07:52:47 | 偏食终 10:49:21 (CST) | 食分 0.944 | Gamma 0.4828中心食带宽 237.1 km | 沙罗序列 128,第 58/73 个成员 | 食甚点太阳高度 60.9° 方位 171.0° | 中心食持续 05:46地心合(视赤经相等) = 23:59:36.1 UT | J.D. = 2456068.499717图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区环食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.03h52m30.8s赤纬 Dec.+20°39'06.6"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"半影外切 / 内切接触P1 半影外切04:56:08P4 半影外切10:49:21本影外切 / 内切接触U1 本影外切06:06:18U2 本影内切06:11:48U3 本影内切09:33:42U4 本影外切09:39:10食甚点的地方情况食甚07:52:47站心食分0.9439中心食带宽237.1 km中心食时长05:46中心食始06:09:02中心食终09:36:27历表与常数历表NASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-1.31°纬天平动 b-0.56°自转轴位置角 c-13.68°布朗月序数1106020000 km比例尺北极方位等距投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2012-05-21 日环食全球见食图偏食始 04:56:07 | 食甚 07:52:47 | 偏食终 10:49:21 (CST) | 食分 0.944 | Gamma 0.4828中心食带宽 237.1 km | 沙罗序列 128,第 58/73 个成员 | 食甚点太阳高度 60.9° 方位 171.0° | 中心食持续 05:46地心合(视赤经相等) = 23:59:37.4 UT | J.D. = 2456068.499733图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区环食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.03h52m30.7s赤纬 Dec.+20°39'06.2"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"半影外切 / 内切接触P1 半影外切04:56:07P4 半影外切10:49:21本影外切 / 内切接触U1 本影外切06:06:17U2 本影内切06:11:47U3 本影内切09:33:42U4 本影外切09:39:10食甚点的地方情况食甚07:52:47站心食分0.9439中心食带宽237.1 km中心食时长05:46中心食始06:09:01中心食终09:36:28历表与常数历表NASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-1.31°纬天平动 b-0.56°自转轴位置角 c-13.68°布朗月序数1106020000 km比例尺北极方位等距投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-beijing-2035-en.svg b/doc/img/solar-eclipse-beijing-2035-en.svg index 2c14f77..3190713 100644 --- a/doc/img/solar-eclipse-beijing-2035-en.svg +++ b/doc/img/solar-eclipse-beijing-2035-en.svg @@ -1 +1 @@ -2035-09-02 Local Solar Eclipselon=116.4074 lat=39.9042 type=total magnitude=1.0255 obscuration=1.0000Greatest: 2035-09-02 08:33:38 CST Sun altitude 31.58 deg Sun in LeoSolar Saros 145 23/77 Totality 00:01:33Overview pathNEWSEclipticC1 287°C2 140°C3 256°C4 109°C1C2GEC3C4Phase disk panelsC107:24:29C2 Total begins08:32:52Greatest08:33:38C3 Total ends08:34:25C409:50:24Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAsare measured from celestial north toward east. All times are UTC (shown in CST, UTC+08:00)Contacts (CST)C1 First contact 07:24:29 PA 286.9°C2 Total begins 08:32:52 PA 139.8°GE Greatest 08:33:38C3 Total ends 08:34:25 PA 255.8°C4 Last contact 09:50:24 PA 108.8° \ No newline at end of file +2035-09-02 Local Solar Eclipselon=116.4074 lat=39.9042 type=total magnitude=1.0255 obscuration=1.0000Greatest: 2035-09-02 08:33:38 CST Sun altitude 31.58 deg Sun in LeoSolar Saros 145 23/77 Totality 00:01:33Overview pathNEWSEclipticC1 287°C2 140°C3 255°C4 109°C1C2GEC3C4Phase disk panelsC107:24:29C2 Total begins08:32:52Greatest08:33:38C3 Total ends08:34:25C409:50:24Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAs are measuredfrom celestial north toward east. All times are UTC (shown in CST, UTC+08:00). Circular limb.Contacts (CST)C1 First contact 07:24:29 PA 286.9°C2 Total begins 08:32:52 PA 140.1°GE Greatest 08:33:38C3 Total ends 08:34:25 PA 255.5°C4 Last contact 09:50:24 PA 108.8° \ No newline at end of file diff --git a/doc/img/solar-eclipse-beijing-2035-global-en.svg b/doc/img/solar-eclipse-beijing-2035-global-en.svg index 90cbc09..2850c39 100644 --- a/doc/img/solar-eclipse-beijing-2035-global-en.svg +++ b/doc/img/solar-eclipse-beijing-2035-global-en.svg @@ -1 +1 @@ -2035-09-02 Total Solar Eclipse Global VisibilityPartial begins 07:15:36 | Greatest 09:55:36 | Partial ends 12:35:48 (CST) | magnitude 1.032 | Gamma 0.3727central path width 116.6 km | Saros series 145, member 23/77 | Sun alt 67.9° az 198.5° | central duration 02:54Geocentric conjunction (equal apparent right ascension) = 01:43:59.8 UT | J.D. = 2464572.572213All times are UTC (shown in CST, UTC+08:00)Global visibility and central path08:3009:0009:3010:0010:3011:0011:3008:3009:3011:00Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.10h44m07.7s赤纬 Dec.+08°01'07.9"视半径 S.D.00°15'50.9"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.10h44m32.5s赤纬 Dec.+08°22'14.5"视半径 S.D.00°16'06.1"地平视差 H.P.00°59'06.9"Penumbral contactsP1 partial begins07:15:36P2 internal contact09:27:39P3 internal contact10:23:52P4 partial ends12:35:48Umbral contactsU1 umbra begins08:15:56U2 internal contact08:16:57U3 internal contact11:34:28U4 umbra ends11:35:24Local circumstances at greatestGreatest09:55:36Local magnitude1.0320Central path width116.6 kmCentral duration02:54Central begins08:16:26Central ends11:34:56Ephemeris and constantsEphemerisNASA bulletin Split-KΔT69.9 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+4.55°Libration b-0.48°Axis position angle c23.63°Brown lunation1394020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2035-09-02 Total Solar Eclipse Global VisibilityPartial begins 07:15:36 | Greatest 09:55:37 | Partial ends 12:35:48 (CST) | magnitude 1.032 | Gamma 0.3727central path width 116.6 km | Saros series 145, member 23/77 | Sun alt 67.9° az 198.5° | central duration 02:54Geocentric conjunction (equal apparent right ascension) = 01:44:01.2 UT | J.D. = 2464572.572229All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.Global visibility and central path08:3009:0009:3010:0010:3011:0011:3008:3009:3011:00Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.10h44m07.7s赤纬 Dec.+08°01'07.9"视半径 S.D.00°15'50.9"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.10h44m32.4s赤纬 Dec.+08°22'14.7"视半径 S.D.00°16'06.1"地平视差 H.P.00°59'06.9"Penumbral contactsP1 partial begins07:15:36P2 internal contact09:27:40P3 internal contact10:23:52P4 partial ends12:35:48Umbral contactsU1 umbra begins08:15:56U2 internal contact08:16:57U3 internal contact11:34:28U4 umbra ends11:35:24Local circumstances at greatestGreatest09:55:37Local magnitude1.0320Central path width116.6 kmCentral duration02:54Central begins08:16:26Central ends11:34:56Ephemeris and constantsEphemerisNASA bulletin Split-KΔT69.9 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+4.55°Libration b-0.48°Axis position angle c23.63°Brown lunation1394020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-beijing-2035-global.svg b/doc/img/solar-eclipse-beijing-2035-global.svg index 37ff7b1..d001a89 100644 --- a/doc/img/solar-eclipse-beijing-2035-global.svg +++ b/doc/img/solar-eclipse-beijing-2035-global.svg @@ -1 +1 @@ -2035-09-02 日全食全球见食图偏食始 07:15:36 | 食甚 09:55:36 | 偏食终 12:35:48 (CST) | 食分 1.032 | Gamma 0.3727中心食带宽 116.6 km | 沙罗序列 145,第 23/77 个成员 | 食甚点太阳高度 67.9° 方位 198.5° | 中心食持续 02:54地心合(视赤经相等) = 01:43:59.8 UT | J.D. = 2464572.572213图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带08:3009:0009:3010:0010:3011:0011:3008:3009:3011:00中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.10h44m07.7s赤纬 Dec.+08°01'07.9"视半径 S.D.00°15'50.9"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.10h44m32.5s赤纬 Dec.+08°22'14.5"视半径 S.D.00°16'06.1"地平视差 H.P.00°59'06.9"半影外切 / 内切接触P1 半影外切07:15:36P2 半影内切09:27:39P3 半影内切10:23:52P4 半影外切12:35:48本影外切 / 内切接触U1 本影外切08:15:56U2 本影内切08:16:57U3 本影内切11:34:28U4 本影外切11:35:24食甚点的地方情况食甚09:55:36站心食分1.0320中心食带宽116.6 km中心食时长02:54中心食始08:16:26中心食终11:34:56历表与常数历表NASA bulletin Split-KΔT69.9 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+4.55°纬天平动 b-0.48°自转轴位置角 c23.63°布朗月序数1394020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2035-09-02 日全食全球见食图偏食始 07:15:36 | 食甚 09:55:37 | 偏食终 12:35:48 (CST) | 食分 1.032 | Gamma 0.3727中心食带宽 116.6 km | 沙罗序列 145,第 23/77 个成员 | 食甚点太阳高度 67.9° 方位 198.5° | 中心食持续 02:54地心合(视赤经相等) = 01:44:01.2 UT | J.D. = 2464572.572229图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带08:3009:0009:3010:0010:3011:0011:3008:3009:3011:00中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.10h44m07.7s赤纬 Dec.+08°01'07.9"视半径 S.D.00°15'50.9"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.10h44m32.4s赤纬 Dec.+08°22'14.7"视半径 S.D.00°16'06.1"地平视差 H.P.00°59'06.9"半影外切 / 内切接触P1 半影外切07:15:36P2 半影内切09:27:40P3 半影内切10:23:52P4 半影外切12:35:48本影外切 / 内切接触U1 本影外切08:15:56U2 本影内切08:16:57U3 本影内切11:34:28U4 本影外切11:35:24食甚点的地方情况食甚09:55:37站心食分1.0320中心食带宽116.6 km中心食时长02:54中心食始08:16:26中心食终11:34:56历表与常数历表NASA bulletin Split-KΔT69.9 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+4.55°纬天平动 b-0.48°自转轴位置角 c23.63°布朗月序数1394020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-beijing-2035.svg b/doc/img/solar-eclipse-beijing-2035.svg index 1b89b88..a5f7f90 100644 --- a/doc/img/solar-eclipse-beijing-2035.svg +++ b/doc/img/solar-eclipse-beijing-2035.svg @@ -1 +1 @@ -2035-09-02 站心日全食经度=116.4074 纬度=39.9042 食型=日全食 食分=1.0255 掩食比=1.0000食甚:2035-09-02 08:33:38 CST 太阳高度 31.58 度 太阳位于狮子座沙罗 145 第 23/77 个成员 全食历时 00:01:33全局路径北东西南黄道C1 287°C2 140°C3 256°C4 109°C1C2食甚C3C4阶段视圆图C1 初亏07:24:29C2 食既08:32:52食甚08:33:38C3 生光08:34:25C4 复圆09:50:24太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00)接触时刻 (CST)C1 初亏 07:24:29 方位 286.9°C2 食既 08:32:52 方位 139.8°GE 食甚 08:33:38C3 生光 08:34:25 方位 255.8°C4 复圆 09:50:24 方位 108.8° \ No newline at end of file +2035-09-02 站心日全食经度=116.4074 纬度=39.9042 食型=日全食 食分=1.0255 掩食比=1.0000食甚:2035-09-02 08:33:38 CST 太阳高度 31.58 度 太阳位于狮子座沙罗 145 第 23/77 个成员 全食历时 00:01:33全局路径北东西南黄道C1 287°C2 140°C3 255°C4 109°C1C2食甚C3C4阶段视圆图C1 初亏07:24:29C2 食既08:32:52食甚08:33:38C3 生光08:34:25C4 复圆09:50:24太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。接触时刻 (CST)C1 初亏 07:24:29 方位 286.9°C2 食既 08:32:52 方位 140.1°GE 食甚 08:33:38C3 生光 08:34:25 方位 255.5°C4 复圆 09:50:24 方位 108.8° \ No newline at end of file diff --git a/doc/img/solar-eclipse-southpolar-2021-12-04-en.svg b/doc/img/solar-eclipse-southpolar-2021-12-04-en.svg index 2acb3e8..c44db97 100644 --- a/doc/img/solar-eclipse-southpolar-2021-12-04-en.svg +++ b/doc/img/solar-eclipse-southpolar-2021-12-04-en.svg @@ -1 +1 @@ -2021-12-04 Total Solar Eclipse Global VisibilityPartial begins 13:29:18 | Greatest 15:33:29 | Partial ends 17:37:28 (CST) | magnitude 1.037 | Gamma -0.9525central path width 417.4 km | Saros series 152, member 13/70 | Sun alt 17.2° az 114.7° | central duration 01:54Geocentric conjunction (equal apparent right ascension) = 07:56:41.8 UT | J.D. = 2459552.831039All times are UTC (shown in CST, UTC+08:00)14:0014:3015:0015:3016:0016:3015:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.16h43m33.8s赤纬 Dec.-22°16'31.4"视半径 S.D.00°16'13.6"地平视差 H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.16h42m35.1s赤纬 Dec.-23°13'22.2"视半径 S.D.00°16'44.4"地平视差 H.P.01°01'27.3"Penumbral contactsP1 partial begins13:29:18P4 partial ends17:37:28Umbral contactsU1 umbra begins15:00:12U2 internal contact15:06:01U3 internal contact16:00:38U4 umbra ends16:06:28Local circumstances at greatestGreatest15:33:29Local magnitude1.0367Central path width417.4 kmCentral duration01:54Central begins15:02:51Central ends16:03:51Ephemeris and constantsEphemerisNASA bulletin Split-KΔT69.3 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-0.22°Libration b+1.28°Axis position angle c6.09°Brown lunation1224020000 kmScaleSouth-polar azimuthal equidistant projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2021-12-04 Total Solar Eclipse Global VisibilityPartial begins 13:29:18 | Greatest 15:33:29 | Partial ends 17:37:28 (CST) | magnitude 1.037 | Gamma -0.9526central path width 417.8 km | Saros series 152, member 13/70 | Sun alt 17.2° az 114.8° | central duration 01:54Geocentric conjunction (equal apparent right ascension) = 07:56:43.0 UT | J.D. = 2459552.831052All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.14:0014:3015:0015:3016:0016:3015:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.16h43m33.8s赤纬 Dec.-22°16'31.4"视半径 S.D.00°16'13.6"地平视差 H.P.00°00'08.9"Moon at greatest eclipse赤经 R.A.16h42m35.1s赤纬 Dec.-23°13'22.3"视半径 S.D.00°16'44.4"地平视差 H.P.01°01'27.3"Penumbral contactsP1 partial begins13:29:18P4 partial ends17:37:28Umbral contactsU1 umbra begins15:00:14U2 internal contact15:06:04U3 internal contact16:00:35U4 umbra ends16:06:26Local circumstances at greatestGreatest15:33:29Local magnitude1.0367Central path width417.8 kmCentral duration01:54Central begins15:02:54Central ends16:03:49Ephemeris and constantsEphemerisNASA bulletin Split-KΔT69.3 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-0.22°Libration b+1.28°Axis position angle c6.09°Brown lunation1224020000 kmScaleSouth-polar azimuthal equidistant projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-southpolar-2021-12-04.svg b/doc/img/solar-eclipse-southpolar-2021-12-04.svg index 12d243d..74ac251 100644 --- a/doc/img/solar-eclipse-southpolar-2021-12-04.svg +++ b/doc/img/solar-eclipse-southpolar-2021-12-04.svg @@ -1 +1 @@ -2021-12-04 日全食全球见食图偏食始 13:29:18 | 食甚 15:33:29 | 偏食终 17:37:28 (CST) | 食分 1.037 | Gamma -0.9525中心食带宽 417.4 km | 沙罗序列 152,第 13/70 个成员 | 食甚点太阳高度 17.2° 方位 114.7° | 中心食持续 01:54地心合(视赤经相等) = 07:56:41.8 UT | J.D. = 2459552.831039图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带14:0014:3015:0015:3016:0016:3015:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.16h43m33.8s赤纬 Dec.-22°16'31.4"视半径 S.D.00°16'13.6"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.16h42m35.1s赤纬 Dec.-23°13'22.2"视半径 S.D.00°16'44.4"地平视差 H.P.01°01'27.3"半影外切 / 内切接触P1 半影外切13:29:18P4 半影外切17:37:28本影外切 / 内切接触U1 本影外切15:00:12U2 本影内切15:06:01U3 本影内切16:00:38U4 本影外切16:06:28食甚点的地方情况食甚15:33:29站心食分1.0367中心食带宽417.4 km中心食时长01:54中心食始15:02:51中心食终16:03:51历表与常数历表NASA bulletin Split-KΔT69.3 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-0.22°纬天平动 b+1.28°自转轴位置角 c6.09°布朗月序数1224020000 km比例尺南极方位等距投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2021-12-04 日全食全球见食图偏食始 13:29:18 | 食甚 15:33:29 | 偏食终 17:37:28 (CST) | 食分 1.037 | Gamma -0.9526中心食带宽 417.8 km | 沙罗序列 152,第 13/70 个成员 | 食甚点太阳高度 17.2° 方位 114.8° | 中心食持续 01:54地心合(视赤经相等) = 07:56:43.0 UT | J.D. = 2459552.831052图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带14:0014:3015:0015:3016:0016:3015:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.16h43m33.8s赤纬 Dec.-22°16'31.4"视半径 S.D.00°16'13.6"地平视差 H.P.00°00'08.9"食甚时的月亮(地心坐标)赤经 R.A.16h42m35.1s赤纬 Dec.-23°13'22.3"视半径 S.D.00°16'44.4"地平视差 H.P.01°01'27.3"半影外切 / 内切接触P1 半影外切13:29:18P4 半影外切17:37:28本影外切 / 内切接触U1 本影外切15:00:14U2 本影内切15:06:04U3 本影内切16:00:35U4 本影外切16:06:26食甚点的地方情况食甚15:33:29站心食分1.0367中心食带宽417.8 km中心食时长01:54中心食始15:02:54中心食终16:03:49历表与常数历表NASA bulletin Split-KΔT69.3 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-0.22°纬天平动 b+1.28°自转轴位置角 c6.09°布朗月序数1224020000 km比例尺南极方位等距投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-xiamen-2012-en.svg b/doc/img/solar-eclipse-xiamen-2012-en.svg index 6925519..68c6110 100644 --- a/doc/img/solar-eclipse-xiamen-2012-en.svg +++ b/doc/img/solar-eclipse-xiamen-2012-en.svg @@ -1 +1 @@ -2012-05-21 Local Solar Eclipselon=118.0894 lat=24.4798 type=annular magnitude=0.9333 obscuration=0.8724Greatest: 2012-05-21 06:10:25 CST Sun altitude 9.57 deg Sun in TaurusSolar Saros 128 58/73 Annularity 00:04:19Overview pathNEWSEclipticC1 256°C2 272°C3 56°C4 73°C1C2GEC3C4Phase disk panelsC105:08:12C2 Annularity begins06:08:15Greatest06:10:25C3 Annularity ends06:12:34C407:20:54Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAsare measured from celestial north toward east. All times are UTC (shown in CST, UTC+08:00)Contacts (CST)C1 First contact 05:08:12 PA 255.9°C2 Annularity begins 06:08:15 PA 272.3°GE Greatest 06:10:25C3 Annularity ends 06:12:34 PA 56.4°C4 Last contact 07:20:54 PA 72.8° \ No newline at end of file +2012-05-21 Local Solar Eclipselon=118.0894 lat=24.4798 type=annular magnitude=0.9333 obscuration=0.8724Greatest: 2012-05-21 06:10:25 CST Sun altitude 9.57 deg Sun in TaurusSolar Saros 128 58/73 Annularity 00:04:20Overview pathNEWSEclipticC1 256°C2 272°C3 57°C4 73°C1C2GEC3C4Phase disk panelsC105:08:12C2 Annularity begins06:08:15Greatest06:10:25C3 Annularity ends06:12:34C407:20:54Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAs are measuredfrom celestial north toward east. All times are UTC (shown in CST, UTC+08:00). Circular limb.Contacts (CST)C1 First contact 05:08:12 PA 255.9°C2 Annularity begins 06:08:15 PA 272.0°GE Greatest 06:10:25C3 Annularity ends 06:12:34 PA 56.7°C4 Last contact 07:20:54 PA 72.8° \ No newline at end of file diff --git a/doc/img/solar-eclipse-xiamen-2012-global-en.svg b/doc/img/solar-eclipse-xiamen-2012-global-en.svg index 0f9810c..b89c3ea 100644 --- a/doc/img/solar-eclipse-xiamen-2012-global-en.svg +++ b/doc/img/solar-eclipse-xiamen-2012-global-en.svg @@ -1 +1 @@ -2012-05-21 Annular Solar Eclipse Global VisibilityPartial begins 04:56:08 | Greatest 07:52:47 | Partial ends 10:49:21 (CST) | magnitude 0.944 | Gamma 0.4828central path width 237.1 km | Saros series 128, member 58/73 | Sun alt 60.9° az 171.0° | central duration 05:46Geocentric conjunction (equal apparent right ascension) = 23:59:36.1 UT | J.D. = 2456068.499717All times are UTC (shown in CST, UTC+08:00)Global visibility and central path06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityAnnular pathCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.03h52m30.8s赤纬 Dec.+20°39'06.6"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"Penumbral contactsP1 partial begins04:56:08P4 partial ends10:49:21Umbral contactsU1 umbra begins06:06:18U2 internal contact06:11:48U3 internal contact09:33:42U4 umbra ends09:39:10Local circumstances at greatestGreatest07:52:47Local magnitude0.9439Central path width237.1 kmCentral duration05:46Central begins06:09:02Central ends09:36:27Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-1.31°Libration b-0.56°Axis position angle c-13.68°Brown lunation1106020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2012-05-21 Annular Solar Eclipse Global VisibilityPartial begins 04:56:07 | Greatest 07:52:47 | Partial ends 10:49:21 (CST) | magnitude 0.944 | Gamma 0.4828central path width 237.1 km | Saros series 128, member 58/73 | Sun alt 60.9° az 171.0° | central duration 05:46Geocentric conjunction (equal apparent right ascension) = 23:59:37.4 UT | J.D. = 2456068.499733All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.Global visibility and central path06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30Axis entersAxis exitsP1P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityAnnular pathCenter lineRise/set phase linesGreatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.03h52m30.7s赤纬 Dec.+20°39'06.2"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"Penumbral contactsP1 partial begins04:56:07P4 partial ends10:49:21Umbral contactsU1 umbra begins06:06:17U2 internal contact06:11:47U3 internal contact09:33:42U4 umbra ends09:39:10Local circumstances at greatestGreatest07:52:47Local magnitude0.9439Central path width237.1 kmCentral duration05:46Central begins06:09:01Central ends09:36:28Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l-1.31°Libration b-0.56°Axis position angle c-13.68°Brown lunation1106020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-xiamen-2012-global.svg b/doc/img/solar-eclipse-xiamen-2012-global.svg index e8a2ecf..67b5eb7 100644 --- a/doc/img/solar-eclipse-xiamen-2012-global.svg +++ b/doc/img/solar-eclipse-xiamen-2012-global.svg @@ -1 +1 @@ -2012-05-21 日环食全球见食图偏食始 04:56:08 | 食甚 07:52:47 | 偏食终 10:49:21 (CST) | 食分 0.944 | Gamma 0.4828中心食带宽 237.1 km | 沙罗序列 128,第 58/73 个成员 | 食甚点太阳高度 60.9° 方位 171.0° | 中心食持续 05:46地心合(视赤经相等) = 23:59:36.1 UT | J.D. = 2456068.499717图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区环食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.03h52m30.8s赤纬 Dec.+20°39'06.6"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"半影外切 / 内切接触P1 半影外切04:56:08P4 半影外切10:49:21本影外切 / 内切接触U1 本影外切06:06:18U2 本影内切06:11:48U3 本影内切09:33:42U4 本影外切09:39:10食甚点的地方情况食甚07:52:47站心食分0.9439中心食带宽237.1 km中心食时长05:46中心食始06:09:02中心食终09:36:27历表与常数历表NASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-1.31°纬天平动 b-0.56°自转轴位置角 c-13.68°布朗月序数1106020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2012-05-21 日环食全球见食图偏食始 04:56:07 | 食甚 07:52:47 | 偏食终 10:49:21 (CST) | 食分 0.944 | Gamma 0.4828中心食带宽 237.1 km | 沙罗序列 128,第 58/73 个成员 | 食甚点太阳高度 60.9° 方位 171.0° | 中心食持续 05:46地心合(视赤经相等) = 23:59:37.4 UT | J.D. = 2456068.499733图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带06:0006:3007:0007:3008:0008:3009:0009:3010:0006:3007:3008:3009:30中心线始中心线终P1P4U1U2U3U4食甚太阳直射点偏食可见区环食带中心线初亏/食甚/复圆日升日落线食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.03h52m43.9s赤纬 Dec.+20°13'18.0"视半径 S.D.00°15'48.1"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.03h52m30.7s赤纬 Dec.+20°39'06.2"视半径 S.D.00°14'43.0"地平视差 H.P.00°54'01.7"半影外切 / 内切接触P1 半影外切04:56:07P4 半影外切10:49:21本影外切 / 内切接触U1 本影外切06:06:17U2 本影内切06:11:47U3 本影内切09:33:42U4 本影外切09:39:10食甚点的地方情况食甚07:52:47站心食分0.9439中心食带宽237.1 km中心食时长05:46中心食始06:09:01中心食终09:36:28历表与常数历表NASA bulletin Split-KΔT66.7 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l-1.31°纬天平动 b-0.56°自转轴位置角 c-13.68°布朗月序数1106020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-xiamen-2012.svg b/doc/img/solar-eclipse-xiamen-2012.svg index ac60bd6..fc9c293 100644 --- a/doc/img/solar-eclipse-xiamen-2012.svg +++ b/doc/img/solar-eclipse-xiamen-2012.svg @@ -1 +1 @@ -2012-05-21 站心日环食经度=118.0894 纬度=24.4798 食型=日环食 食分=0.9333 掩食比=0.8724食甚:2012-05-21 06:10:25 CST 太阳高度 9.57 度 太阳位于金牛座沙罗 128 第 58/73 个成员 环食历时 00:04:19全局路径北东西南黄道C1 256°C2 272°C3 56°C4 73°C1C2食甚C3C4阶段视圆图C1 初亏05:08:12C2 环食始06:08:15食甚06:10:25C3 环食终06:12:34C4 复圆07:20:54太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00)接触时刻 (CST)C1 初亏 05:08:12 方位 255.9°C2 环食始 06:08:15 方位 272.3°GE 食甚 06:10:25C3 环食终 06:12:34 方位 56.4°C4 复圆 07:20:54 方位 72.8° \ No newline at end of file +2012-05-21 站心日环食经度=118.0894 纬度=24.4798 食型=日环食 食分=0.9333 掩食比=0.8724食甚:2012-05-21 06:10:25 CST 太阳高度 9.57 度 太阳位于金牛座沙罗 128 第 58/73 个成员 环食历时 00:04:20全局路径北东西南黄道C1 256°C2 272°C3 57°C4 73°C1C2食甚C3C4阶段视圆图C1 初亏05:08:12C2 环食始06:08:15食甚06:10:25C3 环食终06:12:34C4 复圆07:20:54太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。接触时刻 (CST)C1 初亏 05:08:12 方位 255.9°C2 环食始 06:08:15 方位 272.0°GE 食甚 06:10:25C3 环食终 06:12:34 方位 56.7°C4 复圆 07:20:54 方位 72.8° \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-en.svg b/doc/img/solar-eclipse-yangshan-2009-en.svg index e1e8750..70732bb 100644 --- a/doc/img/solar-eclipse-yangshan-2009-en.svg +++ b/doc/img/solar-eclipse-yangshan-2009-en.svg @@ -1 +1 @@ -2009-07-22 Local Solar Eclipselon=121.9850 lat=30.6167 type=total magnitude=1.0770 obscuration=1.0000Greatest: 2009-07-22 09:40:20 CST Sun altitude 57.29 deg Sun in CancerSolar Saros 136 37/71 Totality 00:05:57Overview pathNEWSEclipticC1 287°C2 109°C3 291°C4 113°C1C2GEC3C4Phase disk panelsC108:23:55C2 Total begins09:37:23Greatest09:40:20C3 Total ends09:43:19C411:03:13Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAsare measured from celestial north toward east. All times are UTC (shown in CST, UTC+08:00)Contacts (CST)C1 First contact 08:23:55 PA 287.2°C2 Total begins 09:37:23 PA 108.6°GE Greatest 09:40:20C3 Total ends 09:43:19 PA 290.8°C4 Last contact 11:03:13 PA 112.5° \ No newline at end of file +2009-07-22 Local Solar Eclipselon=121.9850 lat=30.6167 type=total magnitude=1.0770 obscuration=1.0000Greatest: 2009-07-22 09:40:21 CST Sun altitude 57.29 deg Sun in CancerSolar Saros 136 37/71 Totality 00:05:57Overview pathNEWSEclipticC1 287°C2 109°C3 291°C4 113°C1C2GEC3C4Phase disk panelsC108:23:55C2 Total begins09:37:23Greatest09:40:21C3 Total ends09:43:20C411:03:14Sun is fixed at center; Moon path uses the local tangent plane. East is left, north is up.Overview omits C2/C3 Moon outlines; lower panels show each phase separately. Contact PAs are measuredfrom celestial north toward east. All times are UTC (shown in CST, UTC+08:00). Circular limb.Contacts (CST)C1 First contact 08:23:55 PA 287.2°C2 Total begins 09:37:23 PA 108.6°GE Greatest 09:40:21C3 Total ends 09:43:20 PA 290.8°C4 Last contact 11:03:14 PA 112.5° \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-global-en.svg b/doc/img/solar-eclipse-yangshan-2009-global-en.svg index 1a39dcb..ac90202 100644 --- a/doc/img/solar-eclipse-yangshan-2009-global-en.svg +++ b/doc/img/solar-eclipse-yangshan-2009-global-en.svg @@ -1 +1 @@ -2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 07:58:16 | Greatest 10:35:18 | Partial ends 13:12:21 (CST) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:04.2 UT | J.D. = 2455034.606302All times are UTC (shown in CST, UTC+08:00)Global visibility and central path09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:3011:30Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8Greatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"Penumbral contactsP1 partial begins07:58:16P2 internal contact09:47:39P3 internal contact11:23:00P4 partial ends13:12:21Umbral contactsU1 umbra begins08:51:14U2 internal contact08:54:28U3 internal contact12:16:10U4 umbra ends12:19:23Local circumstances at greatestGreatest10:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins08:52:50Central ends12:17:46Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 07:58:16 | Greatest 10:35:18 | Partial ends 13:12:22 (CST) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:05.7 UT | J.D. = 2455034.606319All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.Global visibility and central path09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:3011:30Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8Greatest-eclipse isochronesP/U shadow contactsSun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"Penumbral contactsP1 partial begins07:58:16P2 internal contact09:47:39P3 internal contact11:23:00P4 partial ends13:12:22Umbral contactsU1 umbra begins08:51:14U2 internal contact08:54:28U3 internal contact12:16:10U4 umbra ends12:19:23Local circumstances at greatestGreatest10:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins08:52:51Central ends12:17:47Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-global-ut1-en.svg b/doc/img/solar-eclipse-yangshan-2009-global-ut1-en.svg index 08772aa..047376e 100644 --- a/doc/img/solar-eclipse-yangshan-2009-global-ut1-en.svg +++ b/doc/img/solar-eclipse-yangshan-2009-global-ut1-en.svg @@ -1 +1 @@ -2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 23:58:16 | Greatest 02:35:18 | Partial ends 05:12:22 (UTC) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:04.4 UT | J.D. = 2455034.606302Times are UT1 (Universal Time 1); DUT1 = UT1−UTC = +0.23 s.Global visibility and central path0.20.40.60.801:0002:0003:30Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8P/U shadow contactsSun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"Penumbral contactsP1 partial begins23:58:16P2 internal contact01:47:39P3 internal contact03:23:00P4 partial ends05:12:22Umbral contactsU1 umbra begins00:51:14U2 internal contact00:54:28U3 internal contact04:16:10U4 umbra ends04:19:23Local circumstances at greatestGreatest02:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins00:52:51Central ends04:17:47Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 23:58:16 | Greatest 02:35:18 | Partial ends 05:12:22 (UTC) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:05.9 UT | J.D. = 2455034.606319Times are UT1 (Universal Time 1); DUT1 = UT1−UTC = +0.23 s. Circular lunar limb.0.20.40.60.801:0002:0003:30Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8P/U shadow contactsSun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"Penumbral contactsP1 partial begins23:58:16P2 internal contact01:47:40P3 internal contact03:23:00P4 partial ends05:12:22Umbral contactsU1 umbra begins00:51:14U2 internal contact00:54:28U3 internal contact04:16:10U4 umbra ends04:19:23Local circumstances at greatestGreatest02:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins00:52:51Central ends04:17:47Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071020000 km比例尺Equirectangular projection; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-global-ut1.svg b/doc/img/solar-eclipse-yangshan-2009-global-ut1.svg index 1aa7b15..fa8d846 100644 --- a/doc/img/solar-eclipse-yangshan-2009-global-ut1.svg +++ b/doc/img/solar-eclipse-yangshan-2009-global-ut1.svg @@ -1 +1 @@ -2009-07-22 日全食全球见食图偏食始 23:58:16 | 食甚 02:35:18 | 偏食终 05:12:22 (UTC) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:04.4 UT | J.D. = 2455034.606302图中时刻为 UT1(世界时),DUT1 = UT1−UTC = +0.23 s。全球见食范围与中心食带0.20.40.60.801:0002:0003:30中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"半影外切 / 内切接触P1 半影外切23:58:16P2 半影内切01:47:39P3 半影内切03:23:00P4 半影外切05:12:22本影外切 / 内切接触U1 本影外切00:51:14U2 本影内切00:54:28U3 本影内切04:16:10U4 本影外切04:19:23食甚点的地方情况食甚02:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始00:52:51中心食终04:17:47历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2009-07-22 日全食全球见食图偏食始 23:58:16 | 食甚 02:35:18 | 偏食终 05:12:22 (UTC) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:05.9 UT | J.D. = 2455034.606319图中时刻为 UT1(世界时),DUT1 = UT1−UTC = +0.23 s。月面按圆月面计算。全球见食范围与中心食带0.20.40.60.801:0002:0003:30中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"半影外切 / 内切接触P1 半影外切23:58:16P2 半影内切01:47:40P3 半影内切03:23:00P4 半影外切05:12:22本影外切 / 内切接触U1 本影外切00:51:14U2 本影内切00:54:28U3 本影内切04:16:10U4 本影外切04:19:23食甚点的地方情况食甚02:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始00:52:51中心食终04:17:47历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-global.svg b/doc/img/solar-eclipse-yangshan-2009-global.svg index 07756e6..b8097d6 100644 --- a/doc/img/solar-eclipse-yangshan-2009-global.svg +++ b/doc/img/solar-eclipse-yangshan-2009-global.svg @@ -1 +1 @@ -2009-07-22 日全食全球见食图偏食始 07:58:16 | 食甚 10:35:18 | 偏食终 13:12:21 (CST) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:04.2 UT | J.D. = 2455034.606302图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:3011:30中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"半影外切 / 内切接触P1 半影外切07:58:16P2 半影内切09:47:39P3 半影内切11:23:00P4 半影外切13:12:21本影外切 / 内切接触U1 本影外切08:51:14U2 本影内切08:54:28U3 本影内切12:16:10U4 本影外切12:19:23食甚点的地方情况食甚10:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始08:52:50中心食终12:17:46历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2009-07-22 日全食全球见食图偏食始 07:58:16 | 食甚 10:35:18 | 偏食终 13:12:22 (CST) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:05.7 UT | J.D. = 2455034.606319图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:3011:30中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8食甚时刻等时线P/U 影锥接触食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"半影外切 / 内切接触P1 半影外切07:58:16P2 半影内切09:47:39P3 半影内切11:23:00P4 半影外切13:12:22本影外切 / 内切接触U1 本影外切08:51:14U2 本影内切08:54:28U3 本影内切12:16:10U4 本影外切12:19:23食甚点的地方情况食甚10:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始08:52:51中心食终12:17:47历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071020000 km比例尺等经纬投影;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-globe-en.svg b/doc/img/solar-eclipse-yangshan-2009-globe-en.svg index a7dc419..6b5fe3f 100644 --- a/doc/img/solar-eclipse-yangshan-2009-globe-en.svg +++ b/doc/img/solar-eclipse-yangshan-2009-globe-en.svg @@ -1 +1 @@ -2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 07:58:16 | Greatest 10:35:18 | Partial ends 13:12:21 (CST) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:04.2 UT | J.D. = 2455034.606302All times are UTC (shown in CST, UTC+08:00)Global visibility and central path09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:0010:3011:0011:3012:00Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8Greatest-eclipse isochronesP/U shadow contactsNESW05000 kmScalePenumbral contactsP1 partial begins07:58:16P2 internal contact09:47:39P3 internal contact11:23:00P4 partial ends13:12:21Local circumstances at greatestGreatest10:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins08:52:50Central ends12:17:46Umbral contactsU1 umbra begins08:51:14U2 internal contact08:54:28U3 internal contact12:16:10U4 umbra ends12:19:23Sun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"H.P.01°01'19.8"Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071Orthographic globe projection, centred on the greatest eclipse; one hemisphere only; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file +2009-07-22 Total Solar Eclipse Global VisibilityPartial begins 07:58:16 | Greatest 10:35:18 | Partial ends 13:12:22 (CST) | magnitude 1.080 | Gamma 0.0698central path width 258.3 km | Saros series 136, member 37/71 | Sun alt 85.9° az 197.6° | central duration 06:39Geocentric conjunction (equal apparent right ascension) = 02:33:05.7 UT | J.D. = 2455034.606319All times are UTC (shown in CST, UTC+08:00). Circular lunar limb.Global visibility and central path09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:0010:3011:0011:3012:00Axis entersAxis exitsP1P2P3P4U1U2U3U4GreatestSubsolarPartial-eclipse visibilityPath of totalityCenter lineRise/set phase linesLocal magnitude 0.2-0.8Greatest-eclipse isochronesP/U shadow contactsNESW05000 kmScalePenumbral contactsP1 partial begins07:58:16P2 internal contact09:47:39P3 internal contact11:23:00P4 partial ends13:12:22Local circumstances at greatestGreatest10:35:18Local magnitude1.0799Central path width258.3 kmCentral duration06:39Central begins08:52:51Central ends12:17:47Umbral contactsU1 umbra begins08:51:14U2 internal contact08:54:28U3 internal contact12:16:10U4 umbra ends12:19:23Sun at greatest eclipse赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"H.P.00°00'08.7"Moon at greatest eclipse赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"H.P.01°01'19.8"Ephemeris and constantsEphemerisNASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"LibrationLibration l+0.67°Libration b-0.07°Axis position angle c10.52°Brown lunation1071Orthographic globe projection, centred on the greatest eclipse; one hemisphere only; sampled penumbral sweep and central path; Natural Earth 1:50m physical land, no administrative boundaries. \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009-globe.svg b/doc/img/solar-eclipse-yangshan-2009-globe.svg index 5503dee..19dfcc9 100644 --- a/doc/img/solar-eclipse-yangshan-2009-globe.svg +++ b/doc/img/solar-eclipse-yangshan-2009-globe.svg @@ -1 +1 @@ -2009-07-22 日全食全球见食图偏食始 07:58:16 | 食甚 10:35:18 | 偏食终 13:12:21 (CST) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:04.2 UT | J.D. = 2455034.606302图中时刻为 UTC(显示时区 CST,UTC+08:00)全球见食范围与中心食带09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:0010:3011:0011:3012:00中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8食甚时刻等时线P/U 影锥接触NESW05000 km比例尺半影外切 / 内切接触P1 半影外切07:58:16P2 半影内切09:47:39P3 半影内切11:23:00P4 半影外切13:12:21食甚点的地方情况食甚10:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始08:52:50中心食终12:17:46本影外切 / 内切接触U1 本影外切08:51:14U2 本影内切08:54:28U3 本影内切12:16:10U4 本影外切12:19:23食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.7s赤纬 Dec.+20°20'06.9"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071正射球面投影,视点取食甚点,只画朝向视点的半个地球;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file +2009-07-22 日全食全球见食图偏食始 07:58:16 | 食甚 10:35:18 | 偏食终 13:12:22 (CST) | 食分 1.080 | Gamma 0.0698中心食带宽 258.3 km | 沙罗序列 136,第 37/71 个成员 | 食甚点太阳高度 85.9° 方位 197.6° | 中心食持续 06:39地心合(视赤经相等) = 02:33:05.7 UT | J.D. = 2455034.606319图中时刻为 UTC(显示时区 CST,UTC+08:00);月面按圆月面计算。全球见食范围与中心食带09:0009:3010:0010:3011:0011:3012:000.20.40.60.809:0009:3010:0010:3011:0011:3012:00中心线始中心线终P1P2P3P4U1U2U3U4食甚太阳直射点偏食可见区全食带中心线初亏/食甚/复圆日升日落线地方食分 0.2–0.8食甚时刻等时线P/U 影锥接触NESW05000 km比例尺半影外切 / 内切接触P1 半影外切07:58:16P2 半影内切09:47:39P3 半影内切11:23:00P4 半影外切13:12:22食甚点的地方情况食甚10:35:18站心食分1.0799中心食带宽258.3 km中心食时长06:39中心食始08:52:51中心食终12:17:47本影外切 / 内切接触U1 本影外切08:51:14U2 本影内切08:54:28U3 本影内切12:16:10U4 本影外切12:19:23食甚时的太阳(地心坐标)赤经 R.A.08h06m24.3s赤纬 Dec.+20°16'02.4"视半径 S.D.00°15'44.5"地平视差 H.P.00°00'08.7"食甚时的月亮(地心坐标)赤经 R.A.08h06m29.6s赤纬 Dec.+20°20'07.0"视半径 S.D.00°16'42.3"地平视差 H.P.01°01'19.8"历表与常数历表NASA bulletin Split-KΔT66.0 sk10.2724880k20.2722810Δb+0.0"Δl+0.0"天平动经天平动 l+0.67°纬天平动 b-0.07°自转轴位置角 c10.52°布朗月序数1071正射球面投影,视点取食甚点,只画朝向视点的半个地球;偏食区为半影足迹时间扫掠,叠加中心食带;Natural Earth 1:50m 物理陆地底图,不含行政边界。 \ No newline at end of file diff --git a/doc/img/solar-eclipse-yangshan-2009.svg b/doc/img/solar-eclipse-yangshan-2009.svg index e46ef21..b04dbde 100644 --- a/doc/img/solar-eclipse-yangshan-2009.svg +++ b/doc/img/solar-eclipse-yangshan-2009.svg @@ -1 +1 @@ -2009-07-22 站心日全食经度=121.9850 纬度=30.6167 食型=日全食 食分=1.0770 掩食比=1.0000食甚:2009-07-22 09:40:20 CST 太阳高度 57.29 度 太阳位于巨蟹座沙罗 136 第 37/71 个成员 全食历时 00:05:57全局路径北东西南黄道C1 287°C2 109°C3 291°C4 113°C1C2食甚C3C4阶段视圆图C1 初亏08:23:55C2 食既09:37:23食甚09:40:20C3 生光09:43:19C4 复圆11:03:13太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00)接触时刻 (CST)C1 初亏 08:23:55 方位 287.2°C2 食既 09:37:23 方位 108.6°GE 食甚 09:40:20C3 生光 09:43:19 方位 290.8°C4 复圆 11:03:13 方位 112.5° \ No newline at end of file +2009-07-22 站心日全食经度=121.9850 纬度=30.6167 食型=日全食 食分=1.0770 掩食比=1.0000食甚:2009-07-22 09:40:21 CST 太阳高度 57.29 度 太阳位于巨蟹座沙罗 136 第 37/71 个成员 全食历时 00:05:57全局路径北东西南黄道C1 287°C2 109°C3 291°C4 113°C1C2食甚C3C4阶段视圆图C1 初亏08:23:55C2 食既09:37:23食甚09:40:21C3 生光09:43:20C4 复圆11:03:14太阳固定在中心;月球路径使用站心切平面。图上左东右西,向上为北。上方为全局路径,C2/C3 只标点位;下方为各阶段独立视圆图。接触点位置角从天球北点起向东量。 图中时刻为 UTC(显示时区 CST,UTC+08:00);圆月面。接触时刻 (CST)C1 初亏 08:23:55 方位 287.2°C2 食既 09:37:23 方位 108.6°GE 食甚 09:40:21C3 生光 09:43:20 方位 290.8°C4 复圆 11:03:14 方位 112.5° \ No newline at end of file diff --git a/doc/img/transit-mercury-beijing-2032-en.svg b/doc/img/transit-mercury-beijing-2032-en.svg new file mode 100644 index 0000000..0df19f4 --- /dev/null +++ b/doc/img/transit-mercury-beijing-2032-en.svg @@ -0,0 +1 @@ +2032-11-13 Transit of Mercury, Local CircumstancesSite 116.4074°E 39.9042°N, height 0 mTopocentric full transit, visible | minimum separation 568.3″ | Sun 969.4″ | planet 4.97″NSEWC1 14:40:40C2 14:42:44Greatest 16:53:54C3 19:05:16C4 19:07:20ContactsTimeSun alt.Az.C114:40:40+20.8°221.3°C214:42:44+20.5°221.8°Greatest16:53:54+0.2°245.9°C319:05:16-24.1°265.9°C419:07:20-24.5°266.3°Duration C1–C4 4h26m41sSunset during transit 16:58:19Sky view, north up and east left. Topocentric contacts; Sun altitudes are geometric centre altitudes, grey parts havethe Sun's centre below the apparent horizon (standard refraction).All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-mercury-beijing-2032-global-en.svg b/doc/img/transit-mercury-beijing-2032-global-en.svg new file mode 100644 index 0000000..8bbe5e9 --- /dev/null +++ b/doc/img/transit-mercury-beijing-2032-global-en.svg @@ -0,0 +1 @@ +2032-11-13 Transit of Mercury Global VisibilityC1 14:41:13 | C2 14:43:18 | Greatest 16:54:13 | C3 19:05:11 | C4 19:07:16 (CST)least separation 572.1″ | solar semidiameter 969.4″ | planet semidiameter 4.97″ (geocentric)Entire transitTransit at sunriseTransit at sunsetNot visibleEquirectangular projection; regions follow whether the Sun's centre is above the apparent horizon (standard refraction) at C1, greatest and C4.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-mercury-beijing-2032-global.svg b/doc/img/transit-mercury-beijing-2032-global.svg new file mode 100644 index 0000000..28af4dd --- /dev/null +++ b/doc/img/transit-mercury-beijing-2032-global.svg @@ -0,0 +1 @@ +2032-11-13 水星凌日全球可见图C1 14:41:13 | C2 14:43:18 | 凌甚 16:54:13 | C3 19:05:11 | C4 19:07:16 (CST)最小角距 572.1″ | 太阳视半径 969.4″ | 行星视半径 4.97″(地心)全程可见日出时已在凌日日落时仍在凌日不可见等经纬投影;按一触、凌甚、四触太阳中心是否在视地平上分区(计入标准大气折射)。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-mercury-beijing-2032.svg b/doc/img/transit-mercury-beijing-2032.svg new file mode 100644 index 0000000..3c762b9 --- /dev/null +++ b/doc/img/transit-mercury-beijing-2032.svg @@ -0,0 +1 @@ +2032-11-13 水星凌日站心图观测地 东经 116.4074° 北纬 39.9042°,海拔 0 米站心完整凌日,可见 | 最小角距 568.3″ | 太阳视半径 969.4″ | 行星视半径 4.97″北南东西一触 14:40:40二触 14:42:44凌甚 16:53:54三触 19:05:16四触 19:07:20接触时刻时刻太阳高度方位一触14:40:40+20.8°221.3°二触14:42:44+20.5°221.8°凌甚16:53:54+0.2°245.9°三触19:05:16-24.1°265.9°四触19:07:20-24.5°266.3°一触至四触历时 4h26m41s凌日中日落 16:58:19天空视向,上北左东;站心接触时刻,太阳高度为日心几何高度,灰色部分太阳中心在视地平下(计入标准大气折射)。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-venus-beijing-2012-en.svg b/doc/img/transit-venus-beijing-2012-en.svg new file mode 100644 index 0000000..532f534 --- /dev/null +++ b/doc/img/transit-venus-beijing-2012-en.svg @@ -0,0 +1 @@ +2012-06-06 Transit of Venus, Local CircumstancesSite 116.4074°E 39.9042°N, height 0 mTopocentric full transit, visible | minimum separation 544.0″ | Sun 945.3″ | planet 28.90″NSEWC1 06:10:13C2 06:27:52Greatest 09:30:40C3 12:31:56C4 12:49:17ContactsTimeSun alt.Az.C106:10:13+13.8°71.8°C206:27:52+17.0°74.4°Greatest09:30:40+51.7°104.4°C312:31:56+72.3°194.5°C412:49:17+71.2°206.7°Duration C1–C4 6h39m4sSky view, north up and east left. Topocentric contacts; Sun altitudes are geometric centre altitudes, grey parts havethe Sun's centre below the apparent horizon (standard refraction).All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-venus-beijing-2012-global-en.svg b/doc/img/transit-venus-beijing-2012-global-en.svg new file mode 100644 index 0000000..1dd0ed6 --- /dev/null +++ b/doc/img/transit-venus-beijing-2012-global-en.svg @@ -0,0 +1 @@ +2012-06-06 Transit of Venus Global VisibilityC1 06:09:48 | C2 06:27:37 | Greatest 09:29:36 | C3 12:31:35 | C4 12:49:24 (CST)least separation 554.4″ | solar semidiameter 945.3″ | planet semidiameter 28.90″ (geocentric)Entire transitTransit at sunriseTransit at sunsetNot visibleEquirectangular projection; regions follow whether the Sun's centre is above the apparent horizon (standard refraction) at C1, greatest and C4.All times are UTC (shown in CST, UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-venus-beijing-2012-global.svg b/doc/img/transit-venus-beijing-2012-global.svg new file mode 100644 index 0000000..7750fa1 --- /dev/null +++ b/doc/img/transit-venus-beijing-2012-global.svg @@ -0,0 +1 @@ +2012-06-06 金星凌日全球可见图C1 06:09:48 | C2 06:27:37 | 凌甚 09:29:36 | C3 12:31:35 | C4 12:49:24 (CST)最小角距 554.4″ | 太阳视半径 945.3″ | 行星视半径 28.90″(地心)全程可见日出时已在凌日日落时仍在凌日不可见等经纬投影;按一触、凌甚、四触太阳中心是否在视地平上分区(计入标准大气折射)。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file diff --git a/doc/img/transit-venus-beijing-2012.svg b/doc/img/transit-venus-beijing-2012.svg new file mode 100644 index 0000000..6f7c2ab --- /dev/null +++ b/doc/img/transit-venus-beijing-2012.svg @@ -0,0 +1 @@ +2012-06-06 金星凌日站心图观测地 东经 116.4074° 北纬 39.9042°,海拔 0 米站心完整凌日,可见 | 最小角距 544.0″ | 太阳视半径 945.3″ | 行星视半径 28.90″北南东西一触 06:10:13二触 06:27:52凌甚 09:30:40三触 12:31:56四触 12:49:17接触时刻时刻太阳高度方位一触06:10:13+13.8°71.8°二触06:27:52+17.0°74.4°凌甚09:30:40+51.7°104.4°三触12:31:56+72.3°194.5°四触12:49:17+71.2°206.7°一触至四触历时 6h39m4s天空视向,上北左东;站心接触时刻,太阳高度为日心几何高度,灰色部分太阳中心在视地平下(计入标准大气折射)。图中时刻为 UTC(显示时区 CST,UTC+08:00) \ No newline at end of file diff --git a/doc/manual/accuracy.md b/doc/manual/accuracy.md index 9b43e94..f2c502b 100644 --- a/doc/manual/accuracy.md +++ b/doc/manual/accuracy.md @@ -1,93 +1,158 @@ # 精度与性能 -[English](en/accuracy.md) | [README](../../README.md) +[English](en/accuracy.md) | [返回 README](../../README.md) -下表说明内置模型及已有样本的对照结果。样本最大误差不代表所有日期、地点的误差上限。与外部星历比较时,需要先统一时标、参考系、观测点高度和折射模型。 - -UTC、UT1 与 TT 的换算见[时标](timescale.md)。 +内置两套星历,误差随模型、年代与计算项目变化。表头注明的模型与区间之外,数值不可直接套用。 ## 目录 -- [太阳与行星](#太阳与行星) -- [月球](#月球) +- [星历模型](#星历模型) +- [模型选择](#模型选择) +- [测量口径](#测量口径) +- [地心视方向](#地心视方向) +- [固定 ICRF 几何方向](#固定-icrf-几何方向) +- [几何月地距离](#几何月地距离) +- [Legacy 截断误差](#legacy-截断误差) - [Lite 轻量链路](#lite-轻量链路) -- [精度校验参考](#精度校验参考) +- [性能](#性能) +- [换算与注意事项](#换算与注意事项) -## 太阳与行星 +## 星历模型 -太阳和行星使用内置 VSOP87 解析项,当前表项覆盖 **J2000 前后约 4000 年**。下表列出相对完整 VSOP87 的截断误差量级: +本库内置两套星历,默认是**半解析模型**(`ModelSemianalytic`):保留行星理论的幅角结构(整数乘子)和初始振幅,把振幅、基本幅角多项式与少量修正项重新拟合到 DE441,全部折叠进同一张级数表。运行时不再挂残差层,也不需要外部星历文件。 + +- **基准理论**:水星、金星、地球、火星用 VSOP2013;木星、土星、天王星、海王星用 TOP2013;月球用 ELP/MPP02。 +- **拟合真值**:长时段用 DE441;行星现代段用 DE440。 +- **可用区间**:TT JD `-1931455.0 … 5373545.0`(J2000 前后 12000、8000 儒略年),越界仍外推,精度不保证。 +- **现代段**(仅行星):J2000 ±1000 儒略年(TT JD `2086295.0 … 2816795.0`)按 DE440 单独拟合一张表,与长时段表在两端各 100 年内平滑过渡,近代精修优先使用它。 +- **求值规模**:`n < 0` 用完整级数,与不带 `N` 的接口一致;所有 `n >= 0` 共用同一张快速表,供粗搜索使用。只有 Legacy 的 `n` 才按项数截断。 +- **数据量**:行星表约 452 KiB、月球表约 42 KiB,随程序编译进去;即使只用到部分天体,行星表和月球表也都会链接,显式实例不减小二进制体积。 +- **坐标系**:表内是 J2000 平黄道几何位置,使用时旋转到当日平黄道,再走与 Legacy 相同的视位置链路(光行时、光行差、岁差与章动)。 + +旧模型 `ModelLegacy` 使用 VSOP87/ELP2000-82 截断级数,并在级数之外叠加订正项:行星为地球的三次多项式与其余天体的常数订正,月球为额外的多项式项、岁差项和公元 3000–5000 年的拟合项。`lite/sun`、`lite/moon` 使用的是另一套简化公式,两者不要混用。 + +## 模型选择 + +| 模型 | 内容 | 适用 | +| --- | --- | --- | +| `ModelSemianalytic` | 内置半解析表,默认构建(不加 `astro_legacy` 标签)的模型 | 日常位置、历法、观测和天象计算 | +| `ModelLegacy` | VSOP87/ELP2000-82 截断级数,叠加长期项与逐天体订正,`n` 按项数截断 | 复现旧数值和模型对照;`astro_legacy` 构建下为默认 | +| `ModelCustom` | SPK 或调用方提供的只读引擎 | 精度由外部核、其可用区间和上层转换共同决定 | +| `lite/sun`、`lite/moon` | 独立近似链路 | 资源受限环境;不随模型切换 | + +显式 `Context`/`Instance` 固定星历,但 ΔT、TT−UTC 和民用时标政策仍是进程级设置。签名不变、默认数值可变,这一点与旧版不同;需要旧模型数值时显式选择 Legacy。用法见[显式星历实例](ephemeris.md)。 + +## 测量口径 + +除另有说明,以下数据都是与 DE440/DE441 的 TT 格点对比,单位角秒: + +- 参考端用 Skyfield 1.55,只做 TT 到 TDB 的换算,不引入 UTC、UT1 或 ΔT 差异。表中的"半解析全量"指 `n < 0`,"半解析快速"指 `n >= 0`。 +- 现代区间:TT JD `2415020.5 … 2488434.25`,步长 0.25 日,共 `293656` 点,约 1900–2100 年。 +- 长区间:TT JD `990545.0 … 3912545.0`,J2000 ±4000 儒略年,共 `365252` 点;DE441 两段核在 JD `2440416.5` 切换。 +- 几何量用日期平黄道/平春分点,视赤经/视赤纬用日期真赤道/真春分点。 +- 表中 MAE 是平均绝对误差,RMS 是均方根误差,P95 是绝对误差的 95 分位,最大是采样最大值;角度是地心视方向夹角,不是单个坐标分量的误差。最大值只是样本最大值,不是连续时间上的误差上界。 + +## 地心视方向 + +太阳: + +| 区间/模型 | MAE | RMS | P95 | 最大 | +| --- | --- | --- | --- | --- | +| 现代 DE440/Legacy | `0.03895` | `0.04475` | `0.08013` | `0.1372` | +| 现代 DE440/半解析全量 | `0.00556` | `0.00682` | `0.01329` | `0.02680` | +| 现代 DE440/半解析快速 | `0.1783` | `0.2219` | `0.4415` | `0.9675` | +| 长区间 DE441/Legacy | `2.659` | `4.171` | `9.751` | `16.68` | +| 长区间 DE441/半解析全量 | `1.774` | `3.187` | `7.908` | `14.08` | +| 长区间 DE441/半解析快速 | `2.431` | `4.050` | `9.878` | `17.15` | + +月球: + +| 区间/模型 | MAE | RMS | P95 | 最大 | +| --- | --- | --- | --- | --- | +| 现代 DE440/Legacy | `0.7431` | `0.7499` | `0.9171` | `1.3297` | +| 现代 DE440/半解析全量 | `0.06302` | `0.07591` | `0.1437` | `0.4418` | +| 现代 DE440/半解析快速 | `3.455` | `3.991` | `7.092` | `18.18` | +| 长区间 DE441/Legacy | `42.16` | `67.10` | `156.35` | `233.58` | +| 长区间 DE441/半解析全量 | `1.788` | `3.182` | `7.920` | `14.08` | +| 长区间 DE441/半解析快速 | `6.098` | `7.359` | `14.30` | `39.74` | + +现代样本中半解析全量的视方向 RMS 是 `0.00682″`(太阳)与 `0.07591″`(月球),低于 Legacy 的 `0.04475″` 与 `0.7499″`;快速表是 `0.2219″` 与 `3.991″`,比 Legacy 更差,月球尤其明显。长区间的结果包含整条高层转换链和参考框架差异,不能全部归因于级数本身。 + +## 固定 ICRF 几何方向 + +下表单独比较半解析全量 `Instance.State(target, BodyEarth, jd)` 与 DE 的地心 ICRF 几何向量,不经过日期岁差/章动转换。库内以 TT 数值近似 TDB,参考端仍从同一 TT 转 TDB,所以这一组是近似 TDB 下的对比。单位角秒: + +| 区间/天体 | MAE | RMS | P95 | 样本最大值 | +| --- | --- | --- | --- | --- | +| 现代 DE440/太阳 | `0.00551` | `0.00677` | `0.01320` | `0.02655` | +| 现代 DE440/月球 | `0.06301` | `0.07591` | `0.1437` | `0.4414` | +| 长区间 DE441/太阳 | `0.04100` | `0.05754` | `0.1214` | `0.3613` | +| 长区间 DE441/月球 | `0.09163` | `0.1129` | `0.2170` | `0.8825` | + +长区间中固定框架误差远小于高层日期框架的视方向差异,说明整条计算链的误差不能用一个"模型精度"数字概括。Legacy 没有几何引擎,不列入此表。 + +## 几何月地距离 + +单位 km,取地心距: + +| 区间/模型 | MAE | P95 | 最大 | +| --- | --- | --- | --- | +| 现代 DE440/Legacy | `0.1434` | `0.3633` | `0.9143` | +| 现代 DE440/半解析全量 | `0.04674` | `0.1172` | `0.3312` | +| 现代 DE440/半解析快速 | `1.730` | `4.356` | `9.818` | +| 长区间 DE441/Legacy | `2.204` | `9.834` | `26.90` | +| 长区间 DE441/半解析全量 | `0.06496` | `0.1669` | `0.5576` | +| 长区间 DE441/半解析快速 | `2.005` | `5.160` | `16.10` | + +## Legacy 截断误差 + +下表是 Legacy 相对完整 VSOP87 的截断误差参考,不是半解析精度,也不是相对 DE440/DE441 的综合误差: | 目标 | 黄经/黄纬 | 距离 | | --- | --- | --- | -| 太阳/地球 | 约 `0.1"` | 约 `0.1 × 10^-6 AU` | -| 水星、金星 | 约 `0.2"` | 约 `0.2 × 10^-6 AU` | -| 火星 | 约 `0.5"` | 约 `1 × 10^-6 AU` | -| 木星 | 约 `0.5"` | 约 `3 × 10^-6 AU` | -| 土星 | 约 `0.5"` | 约 `5 × 10^-6 AU` | -| 天王星 | 约 `1"` | 约 `20 × 10^-6 AU` | -| 海王星 | 约 `1"` | 约 `40 × 10^-6 AU` | - -这类精度适合常规天文历法、观测辅助、科普展示和个人研究;航天导航、精确掩星预报和严格动力学积分不在该范围内,这类用途通常需要 JPL DE 等专业星历。 - -## 月球 - -月球使用内置的 ELP2000/82 风格截断解析级数,库体积轻,不需要外部星历文件。它适合农历定朔、月相、升落、月食、业余月掩预报和常规位置计算;极高精度月球测距、长期物理天平动和专业掩星超出该范围,这类用途以 JPL 星历或专门月球星历为准。 +| 太阳/地球 | 约 `0.1″` | 约 `0.1 × 10^-6 AU` | +| 水星、金星 | 约 `0.2″` | 约 `0.2 × 10^-6 AU` | +| 火星 | 约 `0.5″` | 约 `1 × 10^-6 AU` | +| 木星 | 约 `0.5″` | 约 `3 × 10^-6 AU` | +| 土星 | 约 `0.5″` | 约 `5 × 10^-6 AU` | +| 天王星 | 约 `1″` | 约 `20 × 10^-6 AU` | +| 海王星 | 约 `1″` | 约 `40 × 10^-6 AU` | ## Lite 轻量链路 -`lite/sun` 和 `lite/moon` 是独立于 `sun` / `moon` 的近似实现。不依赖 VSOP87 或主链的 ELP2000/82 级数,适合 CPU / 内存受限环境。 +`lite/sun` 是简化太阳公式,`lite/moon` 是含少量摄动项的近似月球链;升落搜索用固定步长扫描加二分,太阳 30 分钟、月球 15 分钟。两者不使用主链表,也不随模型切换。下表是 2026 全年、8 个站点相对 Legacy 主链的误差(统计量含义同[测量口径](#测量口径)): -- `lite/sun`:简化太阳真黄经 / 视黄经公式 + 轻量赤道坐标转换 -- `lite/moon`:Schlyter 风格月球近似(约 15 个摄动项)+ 轻量站心修正 -- 升落搜索:固定步长扫描 + 二分,不走主链的高精度章动迭代 -- 计算链路零堆分配(0 allocs/op);相对主链,位置与月相等纯求值接口约快 `7.5–27.1x`,升落接口约 `0.9–3.5x` - -能力边界: - -| 包 | 位置模型 | 升落搜索 | 主要用途 | +| 计算项 | 平均绝对误差 | P95 | 最大 | | --- | --- | --- | --- | -| `lite/sun` | 简化太阳真/视黄经 + 轻量赤道坐标转换 | `30` 分钟步长扫描 + 二分 | 日出日落、太阳高度角、表盘/前端周期刷新 | -| `lite/moon` | Schlyter / vFPS 月球近似 + 轻量站心修正 | `15` 分钟步长扫描 + 二分 | 月出月落、月相、月龄、轻量月球观测辅助 | +| 太阳日出/日落 | `0.02 / 0.02 min` | `0.04 / 0.06 min` | `0.31 / 0.35 min` | +| 月出/月落 | `0.28 / 0.36 min` | `0.57 / 0.86 min` | `1.44 / 1.24 min` | +| `Phase()` | `0.00089` | `0.00185` | `0.00243` | +| `PhaseAge()` | `0.003 d` | `0.010 d` | `0.014 d` | -与 `sun` / `moon` 包的误差(2026 全年,8 个站点;升落每 7 或 15 天取样,月相月龄每 6 小时): +这些样本并非全部事件语义一致:太阳日落有 2 个高纬跨午夜日期归属差异,月落有 1 个高纬"当天是否发生"的分歧。升落每 7 或 15 天采样,月相/月龄每 6 小时。表里的时间差只能说明已有事件的一致性,不能用来判断事件是否存在。日月食、物理天平动和高纬事件边界不走 lite 链路,由主链计算。 -| 能力 | 平均绝对误差 | P95 | 最大绝对误差 | 备注 | -| --- | --- | --- | --- |-----------------------------------| -| `lite/sun` 日出 | `0.02 min` | `0.04 min` | `0.31 min` | 样本中无事件存在性分歧 | -| `lite/sun` 日落 | `0.02 min` | `0.06 min` | `0.35 min` | `2` 个高纬样本在跨午夜日期归属上有语义差异 | -| `lite/moon` 月出 | `0.28 min` | `0.57 min` | `1.44 min` | 样本中无事件存在性分歧 | -| `lite/moon` 月落 | `0.36 min` | `0.86 min` | `1.24 min` | `1` 个高纬样本在“当天是否有月落”上与主链判断不同 | -| `lite/moon` `Phase()` | `0.00089` | `0.00185` | `0.00243` | 与 `moon.Phase` 对比的结果 | -| `lite/moon` `PhaseAge()` | `0.003 d` | `0.010 d` | `0.014 d` | 约平均 4.3 分钟、P95 14.4 分钟、最大 20.2 分钟 | -| `lite/moon` 地心黄经 | `2.41'` | `6.82'` | `9.91'` | 相对主链月球位置 | -| `lite/moon` 地心黄纬 | `0.87'` | `1.83'` | `2.92'` | 相对主链月球位置 | +## 性能 -`Go testing.Benchmark` 参考值(单机测量,仅供比较;绝对值因机器而异): +Linux/WSL、Intel i7-12700、Go 1.25.9,基准并行度 20、每项 300 ms。重复查询同一时刻和站点,属于热缓存测量,不含首次加载: -| 接口 | 主链 | `lite` | 加速倍数 | 主链分配 | `lite` 分配 | -| --- | --- | --- | --- | --- | --- | -| `Sun ApparentRaDec` | `6.031 µs/op` | `222.4 ns/op` | `27.1x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Sun Altitude` | `6.127 µs/op` | `672.3 ns/op` | `9.1x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Sun RiseTime` | `101.874 µs/op` | `29.168 µs/op` | `3.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon ApparentRaDec` | `16.897 µs/op` | `1.070 µs/op` | `15.8x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon Phase` | `15.441 µs/op` | `935.6 ns/op` | `16.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon Altitude` | `9.714 µs/op` | `1.294 µs/op` | `7.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon RiseTime` | `121.772 µs/op` | `132.312 µs/op` | `0.9x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | +| 入口 | 半解析全量 | Legacy 全量 | Lite | +| --- | --- | --- | --- | +| Sun `ApparentRaDec` | `0.606 µs` | `8.988 µs` | `0.213 µs` | +| Sun `Altitude` | `0.770 µs` | `9.134 µs` | `0.627 µs` | +| Sun `RiseTime` | `628.6 µs` | `144.8 µs` | `25.88 µs` | +| Moon `ApparentRaDec` | `0.855 µs` | `9.373 µs` | `1.003 µs` | +| Moon `Phase` | `1.171 µs` | `14.81 µs` | `0.914 µs` | +| Moon `Altitude` | `1.006 µs` | `9.520 µs` | `1.136 µs` | +| Moon `RiseTime` | `487.4 µs` | `135.0 µs` | `117.4 µs` | -主链与 `lite` 的差距随场景变化:位置、月相等纯求值接口约 `7.5–27.1x`;升落接口两边都要做时间搜索,差距缩小到 `0.9–3.5x`,其中 `Moon RiseTime` 接近持平。 +半解析的热缓存位置接口与 Lite 互有快慢;升落搜索每次都要在新历元求值,速度会下降,Legacy 在部分升落入口反而更快。以上是单次运行的绝对值,会随机器和调用形态变化。 -日月食、物理天平动或高纬边界判定使用主链 `sun` / `moon`。 +半解析的 Sun/Moon `RiseTime` 分别有 60/36 次分配,表中其余求值接口为 0 次分配。零分配只覆盖这些路径,其他接口不做保证。 -## 精度校验参考 +## 换算与注意事项 -下面这些函数曾与 JPL Horizons、NASA GSFC 等资料对照,可作为使用时判断结果量级的参考: - -- 太阳/行星/月亮视直径:各天体与外部基线的最大差异从 `0.000002"` 到 `0.194598"` 不等,月亮因视差和距离变化更敏感 -- 太阳物理星历 `P/B0/L0`:最大差异约 `0.003349° / 0.003986° / 0.047394°` -- 行星升/中天/落:已用 JPL Horizons 升中落事件做对比校验;该基线按 1 分钟步长生成,当前结果与 Horizons 事件时间在分钟级上对齐 -- 月出/月落:`aero=true` 按动态标准折射和实时月球视半径计算上缘过地平线。7 个地点、14 个海平面事件相对 JPL Horizons DE441 的平均/最大差异约 `0.30s / 0.75s` -- 月出/月落的其他口径:相对固定 `-0.8333°` 的 MET Norway(Skyfield 1.53 + DE440s)约 `38.77s / 76.22s`;相对未公开地平线口径的 IMCCE Miriade 平均约 `2m13.46s`,`61°N` 低仰角样本最大约 `6m41.82s` -- 地球近日点/远日点:时刻最大差异约 `1m28.84s`,距离最大差异约 `0.000000039837 AU` -- 月球主链位置:当前算法为 ELP2000/82 风格截断解析级数;在 `-2000` 年四个 JPL/Horizons `JDTT` 样本上,相对 JPL/Horizons 的最大差异约为黄经 `219.6"`、黄纬 `25.8"`、距离 `34.3 km` -- 月球近地点/远地点:时刻最大差异约 `15m53.45s`,距离最大差异约 `39.758 km` -- 月球最大赤纬:时刻最大差异约 `2.43s`,赤纬最大差异约 `0.00006431°` +- 升落差异要先统一折射模型、视半径口径、观测点高度和当地日归属,地平线定义的影响常大于星历差异。 +- 节气、朔望和食接触直接比较 TT 事件时刻;把位置角秒说成时间误差前,要先除以该处的局部角速度。 +- 民用日期还取决于时区和时标政策,接近子夜的事件尤其敏感。 +- 自定义 SPK 的几何状态使用 ICRF、km、km/日和 TDB;高层标量与视位置入口接收 TT,当前以 TT 近似 TDB。需要严格 TDB 时直接调用 `Instance.State`;换用更精确的核不会自动提升上层的坐标框架、大气与地形模型。 +- IAU 2006/2000A、极移、CPO 和月缘剖面都需要显式调用或显式设置,默认链路不会自动启用;见[坐标工具](coord.md)、[公式](formula.md)和[日月食与凌日](eclipse.md#月缘地形修正)。 diff --git a/doc/manual/calendar.md b/doc/manual/calendar.md index f0de1ce..db44ee5 100644 --- a/doc/manual/calendar.md +++ b/doc/manual/calendar.md @@ -336,7 +336,7 @@ zhuanxu 颛顼历 - **默认路由**:按年份自动选择,先秦段使用春秋/古六历重建,`-220..-104` 使用秦汉颛顼历,`-103..1912` 使用历表,`1913` 年后使用现代算法。 - **显式古历**:如果需要指定某一古历系统,请使用 `SolarToLunarWithCalendar` / `LunarToSolarWithCalendar` 这类 API。 -- **数据来源**:古历部分主要参考《寿星天文历》;使用 [ytliu0教授的网站数据](https://ytliu0.github.io/ChineseCalendar/index_simp.html)做验证校验;现代段依据GB/T 33661-2017编排,通过 VSOP87、ELP定气定朔 。 +- **数据来源**:古历部分主要参考《寿星天文历》;使用 [ytliu0 教授的网站数据](https://ytliu0.github.io/ChineseCalendar/index_simp.html) 交叉校验;现代段按 GB/T 33661-2017 编排,用 VSOP87 与 ELP 级数定朔定气。 - **节气**:`JieQi` 返回现代天文计算的节气时刻;`CalendricalJieQi` 返回历法相符节气日期。 --- @@ -362,7 +362,7 @@ zhuanxu 颛顼历 本package主要面向中国历法,因此定气和定朔的计算默认采用北京时间(UTC+8)。对于使用其他时区的地区,若直接套用中国农历的编排规则,可能会产生日期偏差。 -为方便探索与研究,本包 提供了底层方法 `Solar` 和 `Lunar`,它们支持在**自定义时区**下,按照**现行中国农历算法(GB/T 33661-2017)** 进行公历与农历的相互转换。 +`Solar` 与 `Lunar` 是底层入口,允许在**自定义时区**下按**现行中国农历算法(GB/T 33661-2017)**互转公历与农历。 如果只需北京时间下的标准转换,请直接使用封装好的 `SolarToLunar` 和 `LunarToSolar` 方法。 **示例**:农历规则要求冬至必须落在农历十一月。以1984年冬至为例,计算可得: @@ -377,7 +377,7 @@ fmt.Println(moon.ClosestShuoYue(ws)) | 冬至 | 1984-12-22 | 1984-12-21 | | 朔日 | 1984-12-22 | 1984-12-22 | -可见,对于东八区(中国),1984年12月22日既是冬至又是朔日,因此该日为农历十一月初一;而在东七区,冬至提前至12月21日,导致12月22日已成为腊月初一。 +对于东八区(中国),1984年12月22日既是冬至又是朔日,因此该日为农历十一月初一;而在东七区,冬至提前至12月21日,导致12月22日已成为腊月初一。 类似地,春节的公历日期也可能相差一个月。按本例的历法口径,1985 年正月初一在东八区对应 2 月 20 日,在东七区对应 1 月 21 日: ```go @@ -389,7 +389,7 @@ fmt.Println(calendar.Solar(1985, 1, 1, false, 7.0)) ⚠️ Go 标准库 `time.Time` 在历法处理上与本程序存在差异: -- Go 语言在1582年10月15日之前使用逆推格里高利历,而非儒略历。若不使用 `Add` 方法,一般可正常使用。 +- Go 语言在 1582-10-15 之前使用逆推格里高利历,而非儒略历;只要不调用 `time.Time` 的 `Add`/`AddDate`,其余用法不受影响。 - 因此,**在1582年10月15日之前,`time.Time.Weekday()` 返回结果与本程序计算结果不一致**。 例如:1582年10月4日,本程序为星期四,Go 语言判断为星期一。 @@ -510,9 +510,7 @@ func main() { 输出结果: ```text -// 同一公历时刻在三国并立时期会映射到多个政权各自的农历结果 -[魏明帝 景初三年腊月二十 蜀后主 延熙二年冬月十九 吴大帝 赤乌二年冬月二十] -// 结构化农历信息输出;每个对象对应一个政权口径下的结果 +[魏明帝 景初三年腊月二十 蜀后主 延熙二年冬月十九 吴大帝 赤乌二年冬月二十] // 同一公历时刻在三国并立时期会映射到多个政权各自的农历结果 [ { "solarDate": "0240-01-01T08:08:08.000000008+08:00", @@ -580,13 +578,10 @@ func main() { "lunarWithNianhaoDesc": "赤乌二年冬月二十", "chineseZodiac": "羊" } -] -// “元丰六年十月十二日”对应的公历日期 -1083-11-24 00:00:00 +0800 CST -// 同一天在并行政权下还会命中辽道宗大康九年十月十二 -[宋神宗 元丰六年十月十二 辽道宗 大康九年十月十二] -// 现代农历日期转换结果;2026 年正月初一对应 2026-02-17 -2026-02-17 00:00:00 +0800 CST +] // 结构化农历信息输出;每个对象对应一个政权口径下的结果 +1083-11-24 00:00:00 +0800 CST // “元丰六年十月十二日”对应的公历日期 +[宋神宗 元丰六年十月十二 辽道宗 大康九年十月十二] // 同一天在并行政权下还会命中辽道宗大康九年十月十二 +2026-02-17 00:00:00 +0800 CST // 现代农历日期转换结果;2026 年正月初一对应 2026-02-17 ``` ## 节气 @@ -616,12 +611,11 @@ func main() { 输出结果 -``` -2020-02-04 17:03:20.471614301 +0800 CST -2020-12-21 18:02:20.648710727 +0800 CST -2020-03-20 11:49:37.149532735 +0800 CST -2020-03-20 11:49:37.149532735 +0800 CST - +```text +2020-02-04 17:03:19.850052595 +0800 CST +2020-12-21 18:02:20.574118494 +0800 CST +2020-03-20 11:49:37.111029624 +0800 CST +2020-03-20 11:49:37.111029624 +0800 CST ``` 历法相符节气示例: @@ -636,7 +630,7 @@ fmt.Printf("%d-%02d-%02d %v\n", date.Year(), int(date.Month()), date.Day(), err) 输出结果 -``` +```text 1582-12-22 00:00:00 +0800 CST -202-12-25 ``` @@ -653,7 +647,7 @@ fmt.Printf("%d-%02d-%02d %v\n", date.Year(), int(date.Month()), date.Day(), err) ### 时标 -- 公开 API 的时刻一律按民用时刻处理,也就是把 `time.Time` 的字段直接当作 UTC 标签读取。库内把民用时刻换算到 TT 时,1972-01-01 之前按 UT1 处理,精确窗口内使用内置闰秒表;需要显式换算时用根包的 `astro.UT1FromUTC` / `astro.TTFromUTC` / `astro.DUT1`。 +- 公开 API 的时刻一律按民用时刻处理,也就是把 `time.Time` 的字段直接当作 UTC 标签读取。库内把民用时刻换算到 TT 时,1972-01-01 之前按 UT1 处理,此后使用内置闰秒表;需要显式换算时用根包的 `astro.UT1FromUTC` / `astro.TTFromUTC` / `astro.DUT1`。 - `JieQi` 与 `WuHou` 的返回值是北京时间下的民用时刻,小数秒保留完整精度;`CalendricalJieQi*` 固定返回北京时间当天 0 点。 - `JD`、`Date2JD`、`NowJD` 都是同一个民用口径的儒略日浮点数:`2026-02-17` 的北京民用日对应 `2461088.5`。 diff --git a/doc/manual/coord.md b/doc/manual/coord.md index 0528380..2be019f 100644 --- a/doc/manual/coord.md +++ b/doc/manual/coord.md @@ -4,7 +4,7 @@ `coord` 提供天球坐标转换与观测辅助计算。没有特殊说明时,角度单位为度;恒星时单位为小时;`time.Time` 按绝对时刻使用,内部转换为 UTC 后计算。 -高度角、天顶距与地平可见性的语义沿用[观测角语义](sun-moon.md#观测角语义);站心几何的完整应用见[月掩·站心事件图](occultation.md#站心事件图)与[日月位置](sun-moon.md#日月位置);站心日食的初亏、食甚、复圆见[日食](eclipse.md#日食)。时标、观测点高度与整体精度的约定见[时标约定](timescale.md)、[观测点高度约定](coord.md#观测点高度)与[适用范围与精度](accuracy.md)。 +相关约定:观测角语义见[观测角语义](sun-moon.md#观测角语义),站心几何的应用见[站心事件图](occultation.md#站心事件图)与[日月位置](sun-moon.md#日月位置),时标与观测点高度见[时标约定](timescale.md)与[观测点高度约定](coord.md#观测点高度),整体精度见[精度与性能](accuracy.md)。 ## 目录 @@ -148,12 +148,13 @@ fmt.Printf("HA=%.6f deg\n", coord.HourAngle(date, eq.RA, 115)) ### 岁差与章动 -| 名称 | 用途 | 单位与口径 | -| --- | --- | --- | -| `Precess` | 赤道坐标从一个日期岁差到另一个日期 | RA/Dec 单位度;两个日期都是民用时刻;只做岁差旋转,不含自行 | -| `EclipticObliquity` | 黄赤交角 | 度;`nutation=false` 给 IAU 1980 平交角,`true` 再叠加 IAU 2000B 交角章动 | -| `Nutation2000B` | IAU 2000B 章动 | 返回 `(黄经章动, 交角章动)`,度 | -| `Nutation1980` | IAU 1980 章动 | 返回 `(黄经章动, 交角章动)`,度 | +| 名称 | 用途 | 单位与口径 | +| --- | --- |----------------------------------------------------------------------------| +| `Precess` | 赤道坐标从一个日期岁差到另一个日期 | RA/Dec 单位度;两个日期都是民用时刻;只做岁差旋转,不含自行 | +| `EclipticObliquity` | 黄赤交角 | 度;`nutation=false` 给 IAU 1980 平交角,`true` 再叠加 IAU 2000B 交角章动 | +| `Nutation2000B` | IAU 2000B 章动 | 返回 `(黄经章动, 交角章动)`,度 | +| `Nutation1980` | IAU 1980 章动 | 返回 `(黄经章动, 交角章动)`,度 | +| `Nutation2000A` / `MeanObliquity2006` | IAU 2000A 章动、IAU 2006 平交角 | 度;仅供显式调用,默认链路不变;完整 IAU 2006/2000A 旋转矩阵见 `basic.BiasPrecessionNutation2006` | ```go // 把 J2000 的赤道坐标岁差到 date,并列出两套章动供对照。 @@ -386,11 +387,11 @@ fmt.Printf("q=%.6f airmass=%.6f\n", q, x) ### 精度与适用范围 - 黄赤交角:平均项取 IAU 1980 模型,`EclipticObliquity(date, true)` 在其上叠加 IAU 2000B 交角章动;`Nutation1980` 与 `Nutation2000B` 都可用,需要逐项对照时显式调用。 -- 恒星时:走 IAU 2006 的 ERA 路线,视恒星时叠加 IAU 2000B 黄经章动;同一儒略日的视恒星时做了有界记忆化,注入 ΔT 覆盖会按世代失效。 +- 恒星时:走 IAU 2006 的 ERA 路线,视恒星时叠加 IAU 2000B 黄经章动;视恒星时按儒略日缓存;调用 ΔT 覆盖接口后缓存立即失效,结果随之更新。 - 岁差:`Precess` 在赤道坐标上做长期岁差旋转(赤道极/黄极向量含周期项),不含自行、章动与光行差;它适合把一个历元的平坐标转到另一个历元,不能代替完整的“J2000 → 当日视位置”链路。 - 银道:使用固定的 ICRS ↔ Galactic 旋转矩阵,等价于 SOFA 的 `iauIcrs2g`/`iauG2icrs`,只适用于 ICRS 口径的输入。 - 站心改正:周日视差量级随 `distanceAU` 反比变化,月球最大(约 1°),较远天体是否可忽略应按精度要求判断;`height` 只影响视差项的幅度,量级为米级高程对应的角秒级差异。 -- 站心黄道坐标由站心赤道坐标换算(`basic.TopocentricLoBo` 一次求值给出黄经与黄纬):`TopocentricEcliptic.Lat` 保证落在 `[−90,90]`,并与 `EquatorialToEcliptic(TopocentricEquatorial(...))` 这条独立路径一致。 +- 站心黄道坐标由站心赤道坐标换算:`TopocentricEcliptic.Lat` 保证落在 `[−90,90]`,并与 `EquatorialToEcliptic(TopocentricEquatorial(...))` 这条独立路径一致。 - 折射:Saemundsson 公式,有效真高度窗口 `(−5°, 90°)`,窗口外不修正;低空(< 5°)折射变化剧烈,气压 ±1 hPa、气温 ±1 ℃ 都会带来可见误差。 - 大气质量:三种经验模型在中高空接近,低空差异最大;平行平板只是几何近似,接近地平线发散,低空精细估算用 Kasten-Young 或 Pickering,纯公式见 `formula` 的[大气质量模型](formula.md#大气质量模型)。 - 视差角:`ParallacticAngle` 用瞬时赤经赤纬与站点经纬度,`ParallacticAngleByHourAngle` 用同一几何的时角形式,两者对同一几何应给出一致结果。 diff --git a/doc/manual/eclipse.md b/doc/manual/eclipse.md index 3f2f894..9437645 100644 --- a/doc/manual/eclipse.md +++ b/doc/manual/eclipse.md @@ -1,9 +1,11 @@ -# 日食与月食 +# 日月食与凌日 [English](en/eclipse.md) | [返回 README](../../README.md) > 本手册的完整示例以仓库根目录为工作目录执行,生成的图片写入 `doc/img/`。 +本手册讲日食、月食的查询与出图,以及水星、金星凌日的两张 SVG 图。凌日的接触时刻与站心可见性由 `planets` 包的凌日接口给出,见[行星:水星与金星凌日](planets.md#水星与金星凌日)。 + ## 目录 - [简单示例:2009长江大日食,上海某地见食情况](#简单示例2009长江大日食上海某地见食情况) @@ -23,10 +25,14 @@ - [与 NASA 数据对照](#与-nasa-数据对照) - [月食 SVG](#月食-svg) - [参考资料](#参考资料) +- [月缘地形修正](#月缘地形修正) - [全球见食图与月食出图](#全球见食图与月食出图) - [全球见食图](#全球见食图) - [月食出图](#月食出图) - [时标口径与 UT1](#时标口径与-ut1) +- [凌日出图](#凌日出图) + - [水星:2032-11-13(北京)](#水星2032-11-13北京) + - [金星:2012-06-06(北京)](#金星2012-06-06北京) ## 简单示例:2009长江大日食,上海某地见食情况 @@ -63,7 +69,7 @@ SVG 片段使用导入别名 `eclipsesvg "b612.me/astro/eclipse/svg"`;月掩 | --- | --- | --- | | `LocalSolarEclipseOnDate` / `LocalSolarEclipseOnDateNASABulletinSplitK` | 站心日食查询 | 返回 `(info, bool)`;默认 NASA Split-K 口径 | | `LunarEclipseOnDate` / `LunarEclipseOnDateDanjon` / `LunarEclipseOnDateChauvenet` | 月食查询 | 同上;后缀强制影半径模型 | -| `SearchLocalCentralSolarEclipse` / `SolarEclipseCandidates` | 跨年搜索中心食 / 候选时刻表 | 前者带 `status.Exhausted` | +| `SearchLocalCentralSolarEclipse` / `SolarEclipseCandidates` | 跨年搜索中心食 / 候选时刻表 | 前者返回 `(info, SolarEclipseLocalSearchStatus)` | | `SolarEclipseCentralPath` / `SolarEclipsePartialFootprints` | 中心线和偏食足迹几何 | 选项见 `SolarEclipsePathOptions` / `SolarEclipsePartialFootprintOptions` | | `SolarEclipseBesselianElements` / `SolarEclipseBesselianMuForPublishedTable` | 贝塞尔根数与已发布表换算 | 用来对表 | | `eclipsesvg.SolarEclipseMapSVG` / `LunarEclipseMapSVG` | 全球见食图 / 月食可见区图 | 返回 `(string, bool)` | @@ -89,7 +95,7 @@ true total ``` - `LocalSolarEclipseOnDate` 一次给出食型、各阶段时刻、食分、遮掩比例与食甚太阳高度;只关心"有没有"时看第二个返回值。 -- 要跨年份找"下一次中心食",用 `SearchLocalCentralSolarEclipse`(返回 `status.Exhausted` 区分"跨度内确实没有");只要候选时刻表用 `SolarEclipseCandidates`。 +- 要跨年份找"下一次中心食",用 `SearchLocalCentralSolarEclipse`(第二个返回值 `SolarEclipseLocalSearchStatus` 的 `Exhausted` 区分"跨度内确实没有");只要候选时刻表用 `SolarEclipseCandidates`。 - 月食侧对应 `LunarEclipseOnDate` 与带 `Danjon` / `Chauvenet` 后缀的强制模型入口。 ### 全球见食图与月食图 @@ -152,9 +158,13 @@ true {136 37 71 true} 太阳半径有两个口径:**标准档**(1 AU 处 `959.639″`,与已发布星历表、目录一致,默认)与**边缘档**(1 AU 处 `959.95″`,由全食边缘的光变曲线实测得到的食半径),两者相差 `0.31″`。以 2024-04-08 全食为例,改用边缘档会让全食带每侧收窄约 0.6 千米、中心食时长缩短约 1.5 秒;2023-10-14 环食则带变宽约 1.3 千米、时长延长约 2.1 秒;偏食食分变化约 `1e-4`。 -可据此评估食限与中心时长对太阳半径的敏感性。 +两个口径都可以选: -两个口径都可以选:`eclipse.SolarEclipseOptions{SunRadiusModel: ...}` 配合 `SolarEclipseOnDateWithOptions`、`LocalSolarEclipseOnDateWithOptions`,以及搜索与面板的 `LastSolarEclipseWithOptions` / `NextSolarEclipseWithOptions` / `ClosestSolarEclipseWithOptions` / `SolarEclipseGeocentricPanelWithOptions`,或 `basic` 侧的 `SolarEclipseWithOptions` / `LocalSolarEclipseWithOptions` 与各 `...Options` 结构里的 `SunRadiusModel` 字段;结果里的 `SunRadiusModel` 记录实际使用的口径,`basic.SolarEclipseSunSemidiameter` 与面板里的“视半径 S.D.”都按同一口径计算。 +- 求值:`eclipse.SolarEclipseOptions{SunRadiusModel: ...}` 配合 `SolarEclipseOnDateWithOptions`、`LocalSolarEclipseOnDateWithOptions` +- 搜索与面板:`LastSolarEclipseWithOptions`、`NextSolarEclipseWithOptions`、`ClosestSolarEclipseWithOptions`、`SolarEclipseGeocentricPanelWithOptions` +- `basic` 侧:`SolarEclipseWithOptions`、`LocalSolarEclipseWithOptions`,以及各 `...Options` 结构里的 `SunRadiusModel` 字段 + +结果里的 `SunRadiusModel` 记录实际使用的口径,`basic.SolarEclipseSunSemidiameter` 与面板里的“视半径 S.D.”都按同一口径计算。 常用接口: @@ -222,7 +232,6 @@ if ok { - **全局日食**:关注整次日食的食甚 UT、食分、Gamma、食甚点经纬度和食带宽度。当前用 NASA GSFC 的日食搜索/贝塞尔根数资料对照了 `2023-04-20` 全环食、`2024-04-08` 日全食、`2024-10-02` 日环食、`2025-03-29` 日偏食。 - **站心日食**:关注某个观测点看到的初亏、食甚、复圆和中心食持续时间。当前用 NASA GSFC 的 local circumstances / Google map 日食资料对照了芝加哥偏食、2024 日全食食甚点、2024 日环食食甚点。 - | 时标类型 | 比较项 | 作用 | 已知量级 | | --- | --- | --- | --- | | **TT / TD 层**(力学时,去掉 ΔT) | 几何与星历:贝塞尔根数、影轴位置、接触时刻的 TD 值 | 星历与影子几何本身 | 4 个回归样例中位差约 `0.2 s`;1901–2100 与 NASA 配对 195 次的中位差 `0.40 s`、p99 `2.37 s`、max `2.51 s` | @@ -241,10 +250,11 @@ if ok { | 站心日食 | 3 个本地观测点 | 食甚、初亏、复圆 | UT 层,但公开值多为整分钟 | 与公开分钟值一致(分辨率限制,不按秒级解读) | | 站心日食 | 2 个中心食点 | 全食/环食持续时间 | **TT 层**:两端时刻相减,ΔT 自动抵消 | 秒级对齐,在 `5 s` 阈值内 | -- **日食持续时间反应模型层面精度**:它是两个接触时刻之差,ΔT 口径自动抵消,所以 `5 s` 阈值反映的是几何与星历(对食带边缘最敏感),与 ΔT 无关。 +- **日食持续时间反映几何与星历精度**:它是两个接触时刻之差,ΔT 口径自动抵消,所以 `5 s` 阈值反映的是几何与星历(对食带边缘最敏感),与 ΔT 无关。 - **UT 世界时时刻的 `8 s` 差异主要是口径差异**:不能当成几何精度,它的构成是 `4.8–5.9 s`(ΔT 口径)+ 不到 `1 s`(TD 层几何残差)+ NASA 整秒打印的舍入,所以是个宽松上界而不是精度指标。 - **更大范围的一致性对比结果**:1901–2100 与 NASA 五千年目录配对 195 次(NASA 页面缺 1986–2000、2088–2100 两段),类型普查 `A145/T139/H13/P155` 与 NASA 逐一相等、类型不一致 `0`;食甚 TD 差中位 `0.40 s`、p99 `2.37 s`、max `2.51 s`;gamma 差中位 `3.2e-5`、食分差中位 `4.2e-5`、带宽差中位 `0.5 km`(max `8.3 km`)、中心食时长差中位 `0.25 s`(max `0.59 s`)。 +- **两套内置模型的差异**:以上对照在 Legacy 为默认的时期完成。用 4 个回归样例实测,半解析与 Legacy 的食甚 TT 差只有 `0.008–0.198 s`,小于 TD 层残差,更远小于 UT 层的 `4.8–5.9 s` ΔT 口径差;`astro_legacy` 构建同样通过同一组 NASA 基线。因此这里不按模型分列。 - **复现NASA的 UT 值**:用 `astro.SetDeltaT` 注入NASA采用的 ΔT(例如 2024 年 `74 s`)后取 UT,就能把ΔT差值从比较里去掉;注入只影响换算,不改变 TT 几何。 - 此外,全局日食资料通常给到秒,适合直接做秒级对照;很多站心日食页面的初亏、复圆和本地食甚只公开到整分钟,因此这类资料只按分钟级核对,公开资料舍入造成的残差不按秒级误差解读。 - 下面的 2009 上海洋山港和 2012 厦门示例只展示接口调用与 SVG 输出,未承诺地方接触时刻的发布级精度;需要逐项核对时可与 NASA/IMCCE local circumstances 比对。 @@ -298,14 +308,14 @@ func main() { ```text true total // 洋山站点当天命中日食,食型为日全食 true {136 37 71 true} // Solar Saros 136,第 37/71 个成员,已核验 -2009-07-22 08:23:55.092397034 +0800 CST // 初亏 -2009-07-22 09:37:23.088684976 +0800 CST // 全食开始 -2009-07-22 09:40:20.87983489 +0800 CST // 食甚 -2009-07-22 09:43:19.723805487 +0800 CST // 全食结束 -2009-07-22 11:03:13.914820253 +0800 CST // 复圆 -5m56.635120511s // 全食持续时间 -magnitude=1.076997 obscuration=1.000000 altitude=57.293 // 食分、遮掩比例、食甚太阳高度 -greatest lon=144.1167 lat=24.2193 width=258.3km center=289 // 全局食甚点经纬度、食带宽度、中心线采样点数 +2009-07-22 08:23:55.491912066 +0800 CST // 初亏 +2009-07-22 09:37:23.525415658 +0800 CST // 全食开始 +2009-07-22 09:40:21.314272284 +0800 CST // 食甚 +2009-07-22 09:43:20.161139667 +0800 CST // 全食结束 +2009-07-22 11:03:14.368167221 +0800 CST // 复圆 +5m56.635724009s // 全食持续时间 +magnitude=1.076997 obscuration=1.000000 altitude=57.295 // 食分、遮掩比例、食甚太阳高度 +greatest lon=144.1154 lat=24.2190 width=258.3km center=289 // 全局食甚点经纬度、食带宽度、中心线采样点数 ``` ### 2012 年日环食:厦门示例 @@ -344,12 +354,12 @@ func main() { ```text true annular // 厦门站点当天命中日食,食型为日环食 true {128 58 73 true} // Solar Saros 128,第 58/73 个成员,已核验 -2012-05-21 05:08:12.878718674 +0800 CST // 初亏 -2012-05-21 06:08:15.561088621 +0800 CST // 环食开始 -2012-05-21 06:10:25.180663168 +0800 CST // 食甚 -2012-05-21 06:12:34.80941087 +0800 CST // 环食结束 -2012-05-21 07:20:54.806806147 +0800 CST // 复圆 -4m19.248322249s // 环食持续时间 +2012-05-21 05:08:12.807224392 +0800 CST // 初亏 +2012-05-21 06:08:15.319126546 +0800 CST // 环食开始 +2012-05-21 06:10:25.112628936 +0800 CST // 食甚 +2012-05-21 06:12:34.91297096 +0800 CST // 环食结束 +2012-05-21 07:20:54.736518859 +0800 CST // 复圆 +4m19.593844414s // 环食持续时间 magnitude=0.933289 obscuration=0.872354 altitude=9.565 // 食分、遮掩比例、食甚太阳高度 ``` @@ -438,12 +448,12 @@ func main() { 输出结果: ```text -true 13625 // 洋山日全食 SVG 生成成功,长度 13625 字节 -true 13542 // 厦门日环食 SVG 生成成功,长度 13542 字节 +true 13640 // 洋山日全食 SVG 生成成功,长度 13640 字节 +true 13557 // 厦门日环食 SVG 生成成功,长度 13557 字节 true total // 北京站点当天命中日食,食型为日全食 true {145 23 77 true} // Solar Saros 145,第 23/77 个成员,已核验 -1m33.329527974s // 北京市区近似坐标下的全食持续时间 -true 13589 // 北京日全食 SVG 生成成功,长度 13589 字节 +1m33.053206801s // 北京市区近似坐标下的全食持续时间 +true 13604 // 北京日全食 SVG 生成成功,长度 13604 字节 ``` 生成效果: @@ -550,26 +560,28 @@ func main() { 输出结果: ```text -total -true {125 49 72 true} -2028-12-31 16:52:05.603753328 +0000 UTC -2.2739938633996872 1.2461094682708755 -2028-12-31 14:03:54.239418804 +0000 UTC -2028-12-31 15:07:42.171904742 +0000 UTC -2028-12-31 16:16:27.306801974 +0000 UTC -2028-12-31 17:27:46.228030622 +0000 UTC -2028-12-31 18:36:32.270547151 +0000 UTC -2028-12-31 19:40:11.575520038 +0000 UTC -2.299608256177245 1.2511661731458574 -true -total -2029-01-01 00:52:05.603753328 +0800 CST +total // 食型 +true {125 49 72 true} // 沙罗 125 号第 49/72 次,已核对 +2028-12-31 16:52:05.797516107 +0000 UTC // 食甚 +2.2740102011228953 1.246124858428628 // 半影与影锥食分 +2028-12-31 14:03:54.428796172 +0000 UTC // P1 +2028-12-31 15:07:42.35383898 +0000 UTC // U1 +2028-12-31 16:16:27.44777888 +0000 UTC // U2 +2028-12-31 17:27:46.474579274 +0000 UTC // U3 +2028-12-31 18:36:32.476299405 +0000 UTC // U4 +2028-12-31 19:40:11.773990094 +0000 UTC // P4 +2.29962460346294 1.2511815539147877 // Chauvenet 口径的半影与影锥食分 +true // 当地日期落在月食当天 +total // 站心食型 +2029-01-01 00:52:05.797516107 +0800 CST // UTC+8 下的食甚 ``` ### 与 NASA 数据对照 对照值取自 NASA GSFC 的月食目录(食甚印为 **TD**、食分为目录值)。**UT 层的比较必须先扣掉时标口径**:目录与单次图页的 UT 时刻是用其出版时采用的 ΔT 从 TD 换算的(2026 年取 `75 s`、2024 年取 `74 s`),而实测 ΔT 只有约 `69.1–69.2 s`,两者相差 `4.8–5.9 s`。所以 UT 层的逐接触比较会整体带上这个时标差,它不是月球几何误差;TD 层(不含 ΔT)才是能单独检验星历与几何的一层。 +下表在 Legacy 为默认的时期测量。用半解析重测 `2026-03-03` 得食甚 TD `+0.083 s`,表内 Legacy 为 `+0.114 s`,两套模型相差 `0.031 s`;差异远小于表内 `0.1–1.0 s` 的口径残差,因此不按模型分列。 + | 样例 | 模型 | 半影食分误差 | 本影食分误差 | 食甚 TD 差(不含 ΔT) | 食甚 UT 差(含 ΔT 口径) | |------|------|--------------|--------------|----------------------|--------------------------| | 2026-03-03 月全食 | Danjon | -0.000067208 | -0.000069993 | +0.114 s | +5.99 s | @@ -643,7 +655,7 @@ func main() { 输出结果: ```text -true 19831 +true 19846 ``` 生成效果: @@ -658,6 +670,60 @@ true 19831 - NASA 2024-03-25 半影月食图页: - NASA 月食算法与历史说明: +## 月缘地形修正 + +默认月面是圆盘;站心日食、全球日食、月食和固定地点的月掩都可以改用 `moon/limb` 的月缘剖面。剖面给出月心指向目标方向上的实际月缘半径,用来精修接触时刻。 + +| 计算项 | 是否按月缘修正 | +| --- | --- | +| 站心日食的初亏、复圆、中心食始末与中心食时长 | 是 | +| 站心日食食甚时刻的食分与遮蔽率 | 是,食甚时刻本身不变 | +| 全球日食的偏食始末(P1/P4)与食甚点中心食时长 | 是 | +| 日食中心路径的全食/环食带南北界、逐点带宽、食甚带宽、中心带足迹、中心线最长时长 | 是,`SolarEclipsePathOptions.LunarLimb` 或模块设置 | +| 偏食可见域外边界(零食分包络与初亏、复圆的日出日落曲线) | 是,`SolarEclipsePartialFootprintOptions.LunarLimb` 或模块设置 | +| 月食的六个接触时刻 | 是,只能通过模块或上下文设置 | +| 月掩的掩始、掩终及行星内外切 | 是,见[月掩手册](occultation.md#月缘地形修正) | +| 站心与全球食甚时刻、全球食分、食型、中心线本身、食分线、食甚等时线与食甚日出日落曲线 | 否,仍按圆月面 | + +修正规则: + +- 环食内切与月全食内切取月缘背向接触中心的一侧。 +- 全球日食的 P1/P4 在圆月面切点两侧沿地球边缘扫描,取最早(最晚)的接触。 +- 中心路径南北界在中心方向两侧 ±45° 位置角内取最不利点:全食取月缘最低处仍盖住日面的界线,环食取月缘最高处仍在日面内的界线;地平线上的端点保留圆月面。 +- 偏食可见域外边界取月缘最高处刚好碰到日面近侧的界线;地平线上的端点与初亏、复圆日出日落曲线在同一时刻沿晨昏线移动,两者仍在同一点相接。太阳高度在 0.05°~0.5° 之间的点保留圆月面。结果的 `PartialBoundaryLimbApplied` 表示至少平移了一点;同一结果里的 `Eclipse`(偏食始末、食甚点中心食时长、`LunarLimbApplied`)与 `P1`/`P4` 也按月缘修正,与 `SolarEclipseOnDate` 一致。以 2024-04-08 等四场日食(含高纬偏食)实测:零等食分外包络的垂向平移中位数 1~4 km、最大约 7 km;初亏、复圆的升落曲线沿晨昏线滑动,个别点可移动 10 km 以上;默认步长下足迹计算耗时增加约 10%~20%。 + +```go +import "b612.me/astro/moon/limb" + +// 方式一:单次查询 +info, ok := eclipse.LocalSolarEclipseOnDateWithOptions(date, lon, lat, 0, + eclipse.SolarEclipseOptions{LunarLimb: limb.Default()}) + +// 方式二:整个上下文的站心日食都用月缘 +base, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +ctx := base.WithLunarLimb(limb.Default()) +info, ok = ctx.Eclipse().LocalSolarEclipseOnDate(date, lon, lat, 0) +``` + +出图同样可用:`eclipse/svg` 五个入口的选项都有 `LunarLimb` 字段。站心日食图修正接触时刻与食甚时的食分、遮蔽率;两种月食示意图修正接触时刻,日月盘仍画圆面、食甚时刻不变;月食世界图的各阶段地平圈随修正后的接触时刻移动;日食全球图修正中心带南北界、食甚带宽、偏食可见域外边界、全球 P1/P4 与食甚点站心面板(含中心食时长),食分线、等时线与地心面板仍按圆月面。 + +各层修正彼此独立,剖面只在部分位置有值时可能只修正其中几层;日食全球图页脚只列出实际修正的层,如“按月缘剖面修正可见域边界(食分线与等时线仍按圆月面)。”;中心路径结果的 `CentralLimitsLimbApplied` 记录南北界与带宽是否修正。 + +每张图都在时标声明后写明所用口径(站心图为“圆月面。”或“月缘剖面。”),自定义 `FooterNote` 不会覆盖它。 + +结果上的 `LunarLimbApplied` 记录月缘是否真正生效,图注按它而不是按选项是否为空来写。剖面在需要的位置无值时(`LimbRadiusKM` 返回 `false`),该处半径退回圆月面且不参与修正;整个结果没有任何量被月缘修正时字段为 `false`,数值与圆月面结果完全相同。 + +选项里的 `LunarLimb` 优先于模块设置;`eclipse.(*Eclipse).WithLunarLimb` 返回新模块,不改原模块。也可以实现 `eclipse.LunarLimb` 接口接入别的剖面;底层 `basic.LunarEclipseWithLunarLimb` 提供同一能力。 + +内置剖面在独立子包 `moon/limb`,由 ISAS/JAXA 的 SELENE(Kaguya)LALT 全球地形数据(DARTS 发布,16 像素/度栅格)加工而成,数据约 1.5 MB,只有导入该包才会链接;剖面按观测者月面经纬(±9° / ±8°,步长 1°)与位置角(步长 0.1°)制表,三线性插值对 DEM 直接求剖面的均方根误差约 90 m。 + +修正量是秒级:2024-04-08 达拉斯全食时长 `227.6 s` → `229.8 s`,托雷翁 `250.6 s` → `255.0 s`;2023-10-14 阿尔伯克基环食 `289.4 s` → `290.3 s`;2025-09-07 月全食各接触移动 `-4.4 s` 至 `+0.2 s`。全食带南北界平均移动约 `2.1 km` / `1.0 km`(最大约 `3.2 km`),食甚带宽 `198.6 km` → `196.8 km`,中心线最长时长 `268.0 s` → `271.7 s`,食甚点中心食时长 `268.0 s` → `271.2 s`。 + +需要逐项核对时,可与《中国天文年历》(紫金山天文台)的发布值比较:整秒精度下误差约 `-0.8 s` / `+0.2 s`;Chauvenet 口径、0.1 分钟精度下平均误差为圆月 `1.5 s`、月缘 `2.2 s`,都在发布舍入以内。 + ## 全球见食图与月食出图 `eclipse/svg` 的五个入口都返回 `(string, bool)`:第二个返回值是 `false` 表示这张图按当前参数画不出来(当天没有该事件、画布低于下限、或 UT1 口径配了非 UTC 时区),不是渲染错误。 @@ -917,3 +983,42 @@ ut1, ok := eclipsesvg.SolarEclipseMapSVG( ``` ![2009 长江大日食全球见食图(UT1 口径)](../img/solar-eclipse-yangshan-2009-global-ut1.svg) + +## 凌日出图 + +凌日的接触时刻、站心可见性和 `LocalTransit` 见[行星:水星与金星凌日](planets.md#水星与金星凌日);本节只讲 `eclipse/svg` 的两张图。两个凌日入口同样返回 `(string, bool)`,`date` 取距它最近的一次凌日(同日查询能找到当天凌日)。 + +| 入口 | 画面 | 画布建议 | +| --- | --- | --- | +| `TransitMapSVG` | 地心几何的全球可见图:全程可见、日出时已在凌日、日落时仍在凌日、不可见四区 | 960×640 起 | +| `TransitLocalSVG` | 站心日面图:上北左东,行星中心轨迹、各接触与凌甚的行星圆面、接触时刻表 | 960×680 起 | + +可见区按一触、凌甚、四触三刻太阳中心是否在视地平上划分,计入标准大气折射(地平圈半径 90°34′)。等时线默认不画,用 `Isochrones` 按位开启:`TransitIsochroneC1` … `TransitIsochroneC4`,或组合 `TransitIsochroneExternal`(一触、四触)、`TransitIsochroneAll`;掠凌没有内切接触时 C2/C3 自动略去。等时线是站心接触时刻,只画太阳在视地平上的部分;`IsochroneStep` 非正时水星取 20 秒、金星取 1 分钟。凌日可见范围接近半个地球,地图始终画全球,`Projection` 可选四种投影。 + +```go +venus := time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC) +world, ok := eclipsesvg.TransitMapSVG(eclipsesvg.TransitVenus, venus, eclipsesvg.TransitMapSVGOptions{ + Isochrones: eclipsesvg.TransitIsochroneExternal, + IsochroneStep: 10 * time.Minute, +}) +local, ok := eclipsesvg.TransitLocalSVG(eclipsesvg.TransitVenus, venus, 116.4, 39.9, 50, + eclipsesvg.TransitLocalSVGOptions{Location: time.FixedZone("UTC+8", 8*3600)}) +``` + +站心图中太阳中心在视地平下的轨迹段画成灰色虚线,时刻表补出凌日期间的日出、日落。该地几何上看不到凌日(行星圆面不进入站心日面)时 `TransitLocalSVG` 返回 `false`。站心接触的算法与口径见[行星:水星与金星凌日](planets.md#水星与金星凌日)。 + +### 水星:2032-11-13(北京) + +2032-11-13 是北京下一次可见的水星凌日。北京可见一触、二触与凌甚,凌甚时太阳高度只有 `+0.2°`;三触、四触发生在日落后(太阳高度约 `-24°`),站心图上画成灰色虚线。 + +![2032 水星凌日北京站心图](../img/transit-mercury-beijing-2032.svg) + +![2032 水星凌日全球可见图](../img/transit-mercury-beijing-2032-global.svg) + +### 金星:2012-06-06(北京) + +2012-06-06 金星凌日在北京全程可见,一触到四触历时 `6h39m4s`,各接触的太阳高度为 `+14°` 至 `+71°`。 + +![2012 金星凌日北京站心图](../img/transit-venus-beijing-2012.svg) + +![2012 金星凌日全球可见图](../img/transit-venus-beijing-2012-global.svg) diff --git a/doc/manual/en/accuracy.md b/doc/manual/en/accuracy.md index a3b8b26..4667d3e 100644 --- a/doc/manual/en/accuracy.md +++ b/doc/manual/en/accuracy.md @@ -1,99 +1,158 @@ -# Accuracy and performance +# Accuracy and Performance -[中文](../accuracy.md) | [README](../../../README.en.md) +[中文](../accuracy.md) | [Back to README](../../../README.en.md) -These tables describe the built-in models and previously measured comparisons. A sampled maximum is not an error bound for every date or site. - -External comparisons require matching the time scale, coordinate frame, observer height and refraction model. - -See [Time scales](timescale.md) for UTC, UT1 and TT. +Two ephemerides are built in, and their errors vary with model, epoch and calculated quantity. Values do not apply outside the model and range stated in each table header. ## Contents -- [Sun and planets](#sun-and-planets) -- [Moon](#moon) +- [Ephemeris models](#ephemeris-models) +- [Model selection](#model-selection) +- [Measurement conventions](#measurement-conventions) +- [Geocentric apparent direction](#geocentric-apparent-direction) +- [Fixed-ICRF geometric directions](#fixed-icrf-geometric-directions) +- [Geometric lunar distance](#geometric-lunar-distance) +- [Legacy truncation error](#legacy-truncation-error) - [Lite lightweight chains](#lite-lightweight-chains) -- [Accuracy references](#accuracy-references) +- [Performance](#performance) +- [Conversions and caveats](#conversions-and-caveats) -## Sun and planets +## Ephemeris models -The Sun and planets use built-in VSOP87 analytical terms. The current table entries cover roughly 4000 years around J2000. The table below lists truncation errors relative to the complete VSOP87 tables: +The library ships two ephemerides; the default is a **semianalytic model** (`ModelSemianalytic`): it keeps the argument structure (integer multipliers) and initial amplitudes of the planetary theories, then refits the amplitudes, fundamental-argument polynomials and a small number of correction terms to DE441, folding everything into a single series table. No residual layer is attached at runtime and no external ephemeris file is needed. + +- **Base theories**: VSOP2013 for Mercury, Venus, Earth and Mars; TOP2013 for Jupiter, Saturn, Uranus and Neptune; ELP/MPP02 for the Moon. +- **Fitting truth**: DE441 for the long range; DE440 for the planetary modern segment. +- **Coverage**: TT JD `-1931455.0 … 5373545.0` (12000 Julian years before and 8000 after J2000); values outside it are extrapolated with no accuracy guarantee. +- **Modern segment** (planets only): a separate table fitted over J2000 ±1000 Julian years (TT JD `2086295.0 … 2816795.0`) against DE440, blended with the long-range table over 100 years at each end; use it for refinement at recent epochs. +- **Evaluation scale**: `n < 0` uses the full series, matching the APIs without an `N`; every `n >= 0` shares one fast table intended for coarse searches. Only Legacy truncates its series according to `n`. +- **Data size**: the planetary table is about 452 KiB and the lunar table about 42 KiB, compiled into the program; both are linked even when only some bodies are used, and explicit instances do not reduce the binary size. +- **Frame**: the tables hold geometric positions in the J2000 mean ecliptic; at evaluation they are rotated to the mean ecliptic of date and then follow the same apparent-place chain as Legacy (light time, aberration, precession and nutation). + +`ModelLegacy` uses truncated VSOP87/ELP2000-82 series with correction terms added on top: a cubic polynomial for Earth and constant offsets for the other planets, and for the Moon an extra polynomial, a precession term and a fit for 3000–5000 CE. `lite/sun` and `lite/moon` use a different set of simplified formulas; do not mix the two. + +## Model selection + +| Model | Content | Use | +| --- | --- | --- | +| `ModelSemianalytic` | Built-in semianalytic tables, the model in default builds (without the `astro_legacy` tag) | Everyday positions, calendars, observing and event calculations | +| `ModelLegacy` | Truncated VSOP87/ELP2000-82 series with long-term and per-body corrections, `n` truncating by term count | Reproducing old values and comparing models; the default under an `astro_legacy` build | +| `ModelCustom` | SPK or a caller-supplied read-only engine | Accuracy determined jointly by the external kernel, its usable interval and the upper-level transforms | +| `lite/sun`, `lite/moon` | Independent approximation chains | Resource-constrained environments; do not follow model switches | + +An explicit `Context`/`Instance` fixes the ephemeris, but ΔT, TT−UTC and civil time-scale policy remain process-wide settings. Signatures are unchanged while default values may change, which differs from older versions; select Legacy explicitly when old model values are needed. See [Explicit ephemeris instances](ephemeris.md). + +## Measurement conventions + +Unless stated otherwise, the data below are comparisons against DE440/DE441 on TT grid points, in arcseconds: + +- The reference side uses Skyfield 1.55 and converts only TT to TDB, introducing no UTC, UT1 or ΔT differences. "Semianalytic full" in the tables means `n < 0`, and "semianalytic fast" means `n >= 0`. +- Modern range: TT JD `2415020.5 … 2488434.25`, step 0.25 day, `293656` points, approximately 1900–2100. +- Long range: TT JD `990545.0 … 3912545.0`, J2000 ±4000 Julian years, `365252` points; the two DE441 kernel segments switch at JD `2440416.5`. +- Geometric quantities use the mean ecliptic/equinox of date; apparent RA/Dec use the true equator/equinox of date. +- In the tables MAE is the mean absolute error, RMS the root-mean-square error, P95 the 95th percentile of the absolute error and Max the sampled maximum; angles are geocentric apparent direction angles, not errors in a single coordinate component. The maxima are sampled maxima, not continuous-time error bounds. + +## Geocentric apparent direction + +Sun: + +| Range / model | MAE | RMS | P95 | Max | +| --- | --- | --- | --- | --- | +| Modern DE440 / Legacy | `0.03895` | `0.04475` | `0.08013` | `0.1372` | +| Modern DE440 / semianalytic full | `0.00556` | `0.00682` | `0.01329` | `0.02680` | +| Modern DE440 / semianalytic fast | `0.1783` | `0.2219` | `0.4415` | `0.9675` | +| Long DE441 / Legacy | `2.659` | `4.171` | `9.751` | `16.68` | +| Long DE441 / semianalytic full | `1.774` | `3.187` | `7.908` | `14.08` | +| Long DE441 / semianalytic fast | `2.431` | `4.050` | `9.878` | `17.15` | + +Moon: + +| Range / model | MAE | RMS | P95 | Max | +| --- | --- | --- | --- | --- | +| Modern DE440 / Legacy | `0.7431` | `0.7499` | `0.9171` | `1.3297` | +| Modern DE440 / semianalytic full | `0.06302` | `0.07591` | `0.1437` | `0.4418` | +| Modern DE440 / semianalytic fast | `3.455` | `3.991` | `7.092` | `18.18` | +| Long DE441 / Legacy | `42.16` | `67.10` | `156.35` | `233.58` | +| Long DE441 / semianalytic full | `1.788` | `3.182` | `7.920` | `14.08` | +| Long DE441 / semianalytic fast | `6.098` | `7.359` | `14.30` | `39.74` | + +In the modern samples the semianalytic full series has an apparent-direction RMS of `0.00682″` (Sun) and `0.07591″` (Moon), below the Legacy `0.04475″` and `0.7499″`; the fast table gives `0.2219″` and `3.991″`, worse than Legacy, especially for the Moon. Long-range results include the whole upper-level transformation chain and reference-frame differences, so they cannot be attributed entirely to the series themselves. + +## Fixed-ICRF geometric directions + +The following table compares the semianalytic full `Instance.State(target, BodyEarth, jd)` against DE geocentric ICRF geometric vectors directly, without date precession/nutation transforms. The library approximates TDB numerically with TT, and the reference also converts the same TT to TDB, so this set is a comparison under approximate TDB. Values are arcseconds: + +| Range / body | MAE | RMS | P95 | Sample maximum | +| --- | --- | --- | --- | --- | +| Modern DE440 / Sun | `0.00551` | `0.00677` | `0.01320` | `0.02655` | +| Modern DE440 / Moon | `0.06301` | `0.07591` | `0.1437` | `0.4414` | +| Long DE441 / Sun | `0.04100` | `0.05754` | `0.1214` | `0.3613` | +| Long DE441 / Moon | `0.09163` | `0.1129` | `0.2170` | `0.8825` | + +In the long range the fixed-frame errors are much smaller than the upper-level apparent date-frame differences, showing that the error of the whole calculation chain cannot be summarized by a single "model accuracy" number. Legacy has no geometric engine and is not listed in this table. + +## Geometric lunar distance + +Values are km, as geocentric distances: + +| Range / model | MAE | P95 | Max | +| --- | --- | --- | --- | +| Modern DE440 / Legacy | `0.1434` | `0.3633` | `0.9143` | +| Modern DE440 / semianalytic full | `0.04674` | `0.1172` | `0.3312` | +| Modern DE440 / semianalytic fast | `1.730` | `4.356` | `9.818` | +| Long DE441 / Legacy | `2.204` | `9.834` | `26.90` | +| Long DE441 / semianalytic full | `0.06496` | `0.1669` | `0.5576` | +| Long DE441 / semianalytic fast | `2.005` | `5.160` | `16.10` | + +## Legacy truncation error + +The table below is a truncation-error reference for Legacy against complete VSOP87; it is neither semianalytic accuracy nor total error against DE440/DE441: | Target | Longitude / latitude | Distance | | --- | --- | --- | -| Sun / Earth | about `0.1"` | about `0.1 x 10^-6 AU` | -| Mercury, Venus | about `0.2"` | about `0.2 x 10^-6 AU` | -| Mars | about `0.5"` | about `1 x 10^-6 AU` | -| Jupiter | about `0.5"` | about `3 x 10^-6 AU` | -| Saturn | about `0.5"` | about `5 x 10^-6 AU` | -| Uranus | about `1"` | about `20 x 10^-6 AU` | -| Neptune | about `1"` | about `40 x 10^-6 AU` | - -This is suitable for ordinary calendrical work, observing support, outreach, and personal research; spacecraft navigation, precise occultation prediction, and strict dynamical integration fall outside that range and usually need a professional ephemeris such as JPL DE. - -## Moon - -The Moon uses a built-in truncated ELP2000/82-style analytical series. The package stays lightweight and does not require external ephemeris files. - -It is suitable for Chinese-calendar new moons, lunar phases, rise/set, lunar eclipses, amateur occultation prediction, and ordinary positional work; extremely high-precision lunar laser ranging, long-term physical libration, and professional occultation work fall outside that range and are best served by JPL or a dedicated lunar ephemeris. +| Sun / Earth | about `0.1″` | about `0.1 × 10^-6 AU` | +| Mercury, Venus | about `0.2″` | about `0.2 × 10^-6 AU` | +| Mars | about `0.5″` | about `1 × 10^-6 AU` | +| Jupiter | about `0.5″` | about `3 × 10^-6 AU` | +| Saturn | about `0.5″` | about `5 × 10^-6 AU` | +| Uranus | about `1″` | about `20 × 10^-6 AU` | +| Neptune | about `1″` | about `40 × 10^-6 AU` | ## Lite lightweight chains -`lite/sun` and `lite/moon` are independent approximation chains. They do not depend on the VSOP87 or ELP2000/82 series used by `sun` / `moon`, and are intended for CPU- or memory-constrained environments. +`lite/sun` uses simplified solar formulas and `lite/moon` an approximate lunar chain with a small number of perturbation terms; their rise/set searches use a fixed-step scan plus bisection, 30 minutes for the Sun and 15 minutes for the Moon. Neither uses the main-chain tables and neither follows model switches. The following are errors over all of 2026 at 8 sites relative to the Legacy main chain (statistics as in [Measurement conventions](#measurement-conventions)): -- `lite/sun`: simplified true/apparent solar longitude formulas plus lightweight equatorial conversion -- `lite/moon`: Schlyter-style lunar approximation with about 15 perturbation terms plus lightweight topocentric correction -- rise/set search: fixed-step scanning plus bisection, without the high-precision nutation iteration used by the main chain -- zero heap allocation in the computation path (0 allocs/op); against the main chain, pure evaluation entry points such as position and phase run about `7.5-27.1x` faster, and rise/set entry points about `0.9-3.5x` - -Capability boundaries: - -| Package | Position model | Rise/set search | Main use | +| Quantity | Mean absolute error | P95 | Max | | --- | --- | --- | --- | -| `lite/sun` | simplified true/apparent solar longitude plus lightweight equatorial conversion | `30 min` scan plus bisection | sunrise/sunset, solar altitude, watch faces, frontend refresh loops | -| `lite/moon` | Schlyter / vFPS lunar approximation plus lightweight topocentric correction | `15 min` scan plus bisection | moonrise/moonset, lunar phase, lunar age, lightweight lunar observing helpers | +| Sunrise / sunset | `0.02 / 0.02 min` | `0.04 / 0.06 min` | `0.31 / 0.35 min` | +| Moonrise / moonset | `0.28 / 0.36 min` | `0.57 / 0.86 min` | `1.44 / 1.24 min` | +| `Phase()` | `0.00089` | `0.00185` | `0.00243` | +| `PhaseAge()` | `0.003 d` | `0.010 d` | `0.014 d` | -Error against the `sun` / `moon` packages (year 2026, 8 observing sites; rise/set sampled every 7 or 15 days, phase/age every 6 hours): +These samples are not all semantically identical in their events: sunsets have 2 high-latitude date-attribution differences across midnight and moonset has 1 high-latitude disagreement about whether the event occurs that day. Rise/set are sampled every 7 or 15 days and phase/age every 6 hours. The time differences in the table only indicate agreement for events that do occur; they cannot be used to decide whether an event exists. Eclipses, physical libration and high-latitude event boundaries are computed by the main chain, not by the lite chains. -| Capability | Mean absolute error | P95 | Max absolute error | Notes | -| --- | --- | --- | --- | --- | -| `lite/sun` sunrise | `0.02 min` | `0.04 min` | `0.31 min` | no event-existence mismatch in the sample set | -| `lite/sun` sunset | `0.02 min` | `0.06 min` | `0.35 min` | `2` high-latitude samples differ only in day-attribution semantics across midnight | -| `lite/moon` moonrise | `0.28 min` | `0.57 min` | `1.44 min` | no event-existence mismatch in the sample set | -| `lite/moon` moonset | `0.36 min` | `0.86 min` | `1.24 min` | `1` high-latitude sample differs on whether the moonset belongs to the same civil day | -| `lite/moon` `Phase()` | `0.00089` | `0.00185` | `0.00243` | compared with `moon.Phase` | -| `lite/moon` `PhaseAge()` | `0.003 d` | `0.010 d` | `0.014 d` | about 4.3 min mean, 14.4 min P95, 20.2 min max | -| `lite/moon` geocentric longitude | `2.41'` | `6.82'` | `9.91'` | relative to the main lunar chain | -| `lite/moon` geocentric latitude | `0.87'` | `1.83'` | `2.92'` | relative to the main lunar chain | +## Performance -`Go testing.Benchmark` reference values (single-machine measurements for comparison; absolute values vary with hardware): +Linux/WSL, Intel i7-12700, Go 1.25.9, benchmark concurrency 20, 300 ms per benchmark. Calls repeat the same instant and site, so these are warm-cache measurements and exclude first-load cost: -| Entry point | Main chain | `lite` | Speedup | Main-chain allocation | `lite` allocation | -| --- | --- | --- | --- | --- | --- | -| `Sun ApparentRaDec` | `6.031 µs/op` | `222.4 ns/op` | `27.1x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Sun Altitude` | `6.127 µs/op` | `672.3 ns/op` | `9.1x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Sun RiseTime` | `101.874 µs/op` | `29.168 µs/op` | `3.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon ApparentRaDec` | `16.897 µs/op` | `1.070 µs/op` | `15.8x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon Phase` | `15.441 µs/op` | `935.6 ns/op` | `16.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon Altitude` | `9.714 µs/op` | `1.294 µs/op` | `7.5x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | -| `Moon RiseTime` | `121.772 µs/op` | `132.312 µs/op` | `0.9x` | `0 B/op, 0 allocs/op` | `0 B/op, 0 allocs/op` | +| Entry point | Semianalytic full | Legacy full | Lite | +| --- | --- | --- | --- | +| Sun `ApparentRaDec` | `0.606 µs` | `8.988 µs` | `0.213 µs` | +| Sun `Altitude` | `0.770 µs` | `9.134 µs` | `0.627 µs` | +| Sun `RiseTime` | `628.6 µs` | `144.8 µs` | `25.88 µs` | +| Moon `ApparentRaDec` | `0.855 µs` | `9.373 µs` | `1.003 µs` | +| Moon `Phase` | `1.171 µs` | `14.81 µs` | `0.914 µs` | +| Moon `Altitude` | `1.006 µs` | `9.520 µs` | `1.136 µs` | +| Moon `RiseTime` | `487.4 µs` | `135.0 µs` | `117.4 µs` | -The main-chain/`lite` gap depends on the scenario: pure evaluation entry points (position, phase) run about `7.5-27.1x` faster in `lite`, while rise/set entry points narrow to `0.9-3.5x` because both sides perform a time search; `Moon RiseTime` is close to parity. +The warm-cache semianalytic position APIs are faster than Lite in some cases and slower in others; rise/set searches evaluate at a new epoch each time and therefore run slower, while Legacy is in fact faster for some rise/set entry points. These are absolute single-run values and vary with machine and call shape. -Use the main `sun` / `moon` chains for eclipses, physical libration, or high-latitude edge cases. +Semianalytic Sun/Moon `RiseTime` allocate 60/36 times respectively, and the remaining evaluation APIs in the table allocate 0 times. Zero allocations cover only these paths and are not guaranteed for other APIs. -## Accuracy references +## Conversions and caveats -The following entry points have been checked against JPL Horizons, NASA GSFC, and other public references; use them to judge the order of magnitude to expect: - -- apparent diameters of the Sun, planets, and Moon: maximum differences from the external baseline range from `0.000002"` to `0.194598"` depending on the body; the Moon is the most sensitive because of parallax and distance changes -- solar physical ephemerides `P/B0/L0`: maximum differences are about `0.003349° / 0.003986° / 0.047394°` -- planetary rise, transit, and set: checked against JPL Horizons rise/transit/set events; that baseline is generated at a 1-minute step, and current results align with the Horizons event times at the minute level -- Moon rise/set: `aero=true` uses dynamic standard refraction and the instantaneous lunar semidiameter for an upper-limb crossing. - - Across 14 sea-level events at 7 sites, the current mean/maximum differences against JPL Horizons DE441 are about `0.30s / 0.75s`. -- Moon rise/set with other conventions: mean/maximum differences are about `38.77s / 76.22s` against MET Norway's fixed `-0.8333°` convention (Skyfield 1.53 + DE440s). Against IMCCE Miriade, whose horizon convention is not exposed, the mean is about `2m13.46s`; the low-elevation `61°N` sample reaches about `6m41.82s`. -- Earth perihelion and aphelion: maximum time difference about `1m28.84s`, maximum distance difference about `0.000000039837 AU` -- main-chain lunar position: the current algorithm is a truncated ELP2000/82-style analytical series; across four JPL/Horizons `JDTT` samples in year `-2000`, the maximum difference from JPL/Horizons is about `219.6"` in longitude, `25.8"` in latitude, and `34.3 km` in distance -- Moon perigee and apogee: maximum time difference about `15m53.45s`, maximum distance difference about `39.758 km` -- maximum lunar declination: maximum time difference about `2.43s`, maximum declination difference about `0.00006431°` +- Before comparing rise/set differences, align the refraction model, apparent-radius convention, observer height and local day attribution; the horizon definition often matters more than the ephemeris difference. +- Compare solar terms, syzygies and eclipse contacts directly as TT event instants; before turning positional arcseconds into a time error, divide by the local angular rate at that point. +- Civil dates also depend on the time zone and time-scale policy, and events near midnight are especially sensitive. +- Custom SPK geometric states use ICRF, km, km/day and TDB; high-level scalar and apparent-place entry points accept TT and currently approximate TDB with TT. Call `Instance.State` directly when strict TDB is required; switching to a more accurate kernel does not automatically improve the upper-level coordinate frame, atmosphere or terrain models. +- IAU 2006/2000A, polar motion, CPO and the lunar limb profile all require explicit calls or explicit settings and are not enabled automatically in the default chain; see [Coordinate tools](coord.md), [Formula helpers](formula.md) and [Eclipses and transits](eclipse.md#lunar-limb-correction). diff --git a/doc/manual/en/calendar.md b/doc/manual/en/calendar.md index 0399874..9c504d5 100644 --- a/doc/manual/en/calendar.md +++ b/doc/manual/en/calendar.md @@ -343,9 +343,9 @@ The explicit-calendar entry points work only for years that have data, and `Anci The pre-Qin range uses reconstructed Chunqiu and ancient-six-calendar systems, `-220..-104` uses the Qin/Han Zhuanxu calendar, `-103..1912` uses calendar tables, and `1913` onward uses the modern algorithm. - **Explicit ancient calendars**: use APIs such as `SolarToLunarWithCalendar` / `LunarToSolarWithCalendar` when a specific ancient calendar system is required. -- **Data sources**: ancient-calendar support mainly references 《寿星天文历》; [Professor ytliu0's ChineseCalendar data](https://ytliu0.github.io/ChineseCalendar/index_simp.html) is used for validation. +- **Data sources**: ancient-calendar support mainly references 《寿星天文历》; [Professor ytliu0's ChineseCalendar data](https://ytliu0.github.io/ChineseCalendar/index_simp.html) is used for cross-validation. - The modern range follows GB/T 33661-2017 and uses VSOP87/ELP computations for solar terms and new moons. + The modern range follows GB/T 33661-2017 and uses the VSOP87 and ELP series for new moons and solar terms. - **Solar terms**: `JieQi` returns modern astronomical solar-term instants; `CalendricalJieQi` returns calendar-compatible solar-term dates. ## Usage notes @@ -368,7 +368,7 @@ All computation goes through Julian Days. The Gregorian side follows these rules ### 4. Time zone The package targets the Chinese calendar, so solar terms and new moons are computed in Beijing time (UTC+8) by default. Applying Chinese lunisolar rules in another time zone can shift dates. -For exploration and research, the lower-level `Solar` and `Lunar` methods convert between the Gregorian and lunisolar calendars under a **custom time zone**, following the **current Chinese calendar algorithm (GB/T 33661-2017)**. +`Solar` and `Lunar` are the low-level entry points; they convert between the Gregorian and lunisolar calendars under a **custom time zone**, following the **current Chinese calendar algorithm (GB/T 33661-2017)**. For standard conversions in Beijing time, use the wrapped `SolarToLunar` and `LunarToSolar` methods. **Example**: the calendar rules require the winter solstice to fall in the eleventh lunisolar month. For the 1984 winter solstice: @@ -395,7 +395,7 @@ fmt.Println(calendar.Solar(1985, 1, 1, false, 7.0)) ⚠️ Go's standard-library `time.Time` differs from this package in calendar handling: -- Before 1582-10-15 Go uses the proleptic Gregorian calendar rather than the Julian calendar. Without `Add`, it generally works as expected. +- Before 1582-10-15 Go uses the proleptic Gregorian calendar rather than the Julian calendar; as long as you do not call `time.Time`'s `Add`/`AddDate`, every other use is unaffected. - So **before 1582-10-15, `time.Time.Weekday()` does not agree with this package**: for 1582-10-04 this package reports Thursday, Go reports Monday. @@ -412,7 +412,7 @@ For example, 700 is a leap year in the Julian calendar but not in Go's proleptic ### 6. Julian-only leap days (for example 700-02-29) Before 1582, years divisible by 100 but not 400 (such as 100, 700 or 1500) have a 29 February in the Julian calendar, -while Go's `time.Time` uses the proleptic Gregorian calendar and has no such day (`time.Date(700, 2, 29, ...)` normalises to 700-03-01). The library accepts 700-02-29 as an existing day, with these constraints: +while Go's `time.Time` uses the proleptic Gregorian calendar and has no such day (`time.Date(700, 2, 29, ...)` normalizes to 700-03-01). The library accepts 700-02-29 as an existing day, with these constraints: - `Time.Solar()` / `LunarTime.SolarDate`: the library's canonical output, always the **following day** (700-03-01 for 700-02-29), matching `basic.JD2DateByZone`; @@ -591,8 +591,8 @@ Output: } ] // structured lunisolar records, one object per matching historical result 1083-11-24 00:00:00 +0800 CST // Gregorian date corresponding to 元丰六年十月十二日 -[宋神宗 元丰六年十月十二 辽道宗 大康九年十月十二] // the same day also matches a Liao calendar result -2026-02-17 00:00:00 +0800 CST // Chinese New Year in 2026 +[宋神宗 元丰六年十月十二 辽道宗 大康九年十月十二] // the same day also matches the Liao calendar (大康九年十月十二) +2026-02-17 00:00:00 +0800 CST // modern lunisolar conversion; the 2026 Chinese New Year falls on 2026-02-17 ``` ## Solar terms @@ -627,10 +627,10 @@ func main() { Output: ```text -2020-02-04 17:03:20.471614301 +0800 CST // Beginning of Spring -2020-12-21 18:02:20.648710727 +0800 CST // Winter Solstice -2020-03-20 11:49:37.149532735 +0800 CST // March Equinox -2020-03-20 11:49:37.149532735 +0800 CST // same result from direct longitude input +2020-02-04 17:03:19.850052595 +0800 CST // Beginning of Spring +2020-12-21 18:02:20.574118494 +0800 CST // Winter Solstice +2020-03-20 11:49:37.111029624 +0800 CST // March Equinox +2020-03-20 11:49:37.111029624 +0800 CST // same result from direct longitude input ``` Calendrical solar-term example: @@ -664,13 +664,13 @@ Output: - Every public instant is treated as a civil value, meaning the fields of the `time.Time` are read directly as a UTC label. - When the library converts civil time to TT, values before 1972-01-01 are treated as UT1 and the exact window uses the built-in leap-second table; for explicit conversion use the root package's `astro.UT1FromUTC` / `astro.TTFromUTC` / `astro.DUT1`. + When the library converts civil time to TT, values before 1972-01-01 are treated as UT1 and from then on the built-in leap-second table is used; for explicit conversion use the root package's `astro.UT1FromUTC` / `astro.TTFromUTC` / `astro.DUT1`. - `JieQi` and `WuHou` return civil instants in Beijing time with full fractional-second precision; `CalendricalJieQi*` always returns 00:00 Beijing time on the matching day. - `JD`, `Date2JD`, and `NowJD` are all Julian-day floats on that same civil convention: the Beijing civil day `2026-02-17` corresponds to `2461088.5`. ### Zero Values and Out-of-Range Input -- The ten Gregorian days from 1582-10-05 through 1582-10-14 do not exist; the affected entry points report an error instead of silently normalising to a neighbouring date. +- The ten Gregorian days from 1582-10-05 through 1582-10-14 do not exist; the affected entry points report an error instead of silently normalizing to a neighbouring date. - `SolarToLunar` / `SolarToLunarByYMD` return an error outside `[-721,3000]`. `LunarToSolar*` is limited to the same Gregorian range, and the boundary lunisolar year `-722` is accepted while its result still falls inside that range. - `CalendricalJieQiWithCalendar` returns an error for the Chunqiu calendar and for years without calendrical solar-term data; `LunarToSolarWithCalendar` accepts an era-name description only when the ancient era table matches and errors otherwise. - `SolarCandidates()` returns a one-element slice holding `Solar()` when there is no second candidate, never an empty slice; `Lunar()` returns a zero-value `LunarTime` when there is no candidate, and its `JulianOnly()` then reports `false`. diff --git a/doc/manual/en/coord.md b/doc/manual/en/coord.md index 603697d..7f6157e 100644 --- a/doc/manual/en/coord.md +++ b/doc/manual/en/coord.md @@ -4,11 +4,7 @@ `coord` provides celestial coordinate conversion and observing helper calculations. Unless noted otherwise, angles are degrees, sidereal time is in hours, and `time.Time` is treated as an absolute instant and internally converted to UTC. -Altitude, zenith distance, and horizon-visibility semantics follow [Observing-angle semantics](sun-moon.md#observing-angle-semantics); full topocentric usage appears in [Occultation · Local event chart](occultation.md#fixed-site-charts) and [Sun and Moon positions](sun-moon.md#sun-and-moon-position); - -topocentric solar-eclipse contacts appear in [Solar eclipses](eclipse.md#solar-eclipse). - -Time scales, observer height, and overall accuracy are documented in [Time scale conventions](timescale.md), [Observer height conventions](coord.md#observer-height), and [Applicability and accuracy](accuracy.md). +Related conventions: observing-angle semantics in [Observing-angle semantics](sun-moon.md#observing-angle-semantics); topocentric geometry in [Fixed-site charts](occultation.md#fixed-site-charts) and [Sun and Moon positions](sun-moon.md#sun-and-moon-position); time scales and observer height in [Time scale conventions](timescale.md) and [Observer height conventions](coord.md#observer-height); overall accuracy in [Applicability and accuracy](accuracy.md). ## Contents @@ -158,6 +154,7 @@ fmt.Printf("HA=%.6f deg\n", coord.HourAngle(date, eq.RA, 115)) | `EclipticObliquity` | ecliptic obliquity | degrees; `nutation=false` gives the IAU 1980 mean obliquity, `true` adds the IAU 2000B obliquity nutation | | `Nutation2000B` | IAU 2000B nutation | returns `(longitude nutation, obliquity nutation)` in degrees | | `Nutation1980` | IAU 1980 nutation | returns `(longitude nutation, obliquity nutation)` in degrees | +| `Nutation2000A` / `MeanObliquity2006` | IAU 2000A nutation, IAU 2006 mean obliquity | degrees; opt-in only, the default chain is unchanged; the full IAU 2006/2000A rotation is `basic.BiasPrecessionNutation2006` | ```go // Precess J2000 equatorial coordinates to date and list both nutation series for comparison. @@ -404,11 +401,11 @@ The same observing helpers are also exposed in `sun`, `moon`, `star`, and the se ### Accuracy and Applicability - Ecliptic obliquity: the mean term uses the IAU 1980 model, and `EclipticObliquity(date, true)` adds the IAU 2000B obliquity nutation on top; both `Nutation1980` and `Nutation2000B` are available and can be called explicitly for term-by-term comparison. -- Sidereal time: the IAU 2006 ERA route, with apparent sidereal time adding the IAU 2000B longitude nutation; apparent sidereal time for the same Julian date is memoized in a bounded table, and an injected ΔT override invalidates the memo by generation. +- Sidereal time: the IAU 2006 ERA route, with apparent sidereal time adding the IAU 2000B longitude nutation; apparent sidereal time is cached per Julian day, and calling a ΔT override invalidates the cache immediately so results follow. - Precession: `Precess` applies a long-term precession rotation to equatorial coordinates (its equatorial/ecliptic pole vectors include periodic terms), without proper motion, nutation, or aberration; it moves mean coordinates from one epoch to another and cannot replace a full "J2000 to apparent-of-date" chain. - Galactic: a fixed ICRS ↔ Galactic rotation matrix, equivalent to SOFA `iauIcrs2g`/`iauG2icrs`, valid only for ICRS-convention input. - Topocentric correction: the diurnal parallax scales inversely with `distanceAU` and is largest for the Moon (about 1°), and whether it can be neglected for a more distant body depends on the required accuracy; `height` only scales the parallax term, with meter-level elevation producing arcsecond-level differences. -- Topocentric ecliptic coordinates come from the topocentric equatorial position (`basic.TopocentricLoBo` returns both from a single evaluation): `TopocentricEcliptic.Lat` is guaranteed to land in `[−90,90]` and agrees with the independent `EquatorialToEcliptic(TopocentricEquatorial(...))` route. +- Topocentric ecliptic coordinates come from the topocentric equatorial position: `TopocentricEcliptic.Lat` is guaranteed to land in `[−90,90]`. - Refraction: the Saemundsson formula, valid for true altitudes in `(−5°, 90°)`, with no correction outside that window; refraction changes rapidly at low altitude (< 5°), where ±1 hPa or ±1 °C already produces a visible error. - Airmass: the three empirical models agree at moderate altitude and differ most near the horizon; the plane-parallel model is a purely geometric approximation that diverges near the horizon, so use Kasten-Young or Pickering for careful low-altitude estimates, with the raw formulas in `formula` under [airmass models](formula.md#airmass-models). - Parallactic angle: `ParallacticAngle` uses apparent RA/Dec and the site longitude/latitude, while `ParallacticAngleByHourAngle` uses the hour-angle form of the same geometry; both must agree for the same geometry. diff --git a/doc/manual/en/eclipse.md b/doc/manual/en/eclipse.md index 8fa8ce6..e017d4b 100644 --- a/doc/manual/en/eclipse.md +++ b/doc/manual/en/eclipse.md @@ -1,9 +1,11 @@ -# Solar and Lunar Eclipses +# Eclipses and Transits [中文](../eclipse.md) | [Back to README](../../../README.en.md) > Full examples in this manual run from the repository root and write their figures to `doc/img/`. +This manual covers solar- and lunar-eclipse queries and charts, plus the two Mercury and Venus transit SVG charts. Transit contact times and topocentric visibility come from the `planets` transit entry points, see [Planets: Mercury and Venus transits](planets.md#mercury-and-venus-transits). + ## Contents - [Simple example: the 2009 Great Yangtze Eclipse at a site in Shanghai](#simple-example-the-2009-great-yangtze-eclipse-at-a-site-in-shanghai) @@ -23,10 +25,14 @@ - [Checks against NASA data](#checks-against-nasa-data) - [Lunar-eclipse SVG](#lunar-eclipse-svg) - [References](#references) +- [Lunar limb correction](#lunar-limb-correction) - [Solar and Lunar Eclipse Charts](#solar-and-lunar-eclipse-charts) - [Global visibility maps](#global-visibility-maps) - [Lunar eclipse charts](#lunar-eclipse-charts) - [Time scale and UT1](#time-scale-and-ut1) +- [Transit charts](#transit-charts) + - [Mercury: 2032-11-13 (Beijing)](#mercury-2032-11-13-beijing) + - [Venus: 2012-06-06 (Beijing)](#venus-2012-06-06-beijing) ## Simple example: the 2009 Great Yangtze Eclipse at a site in Shanghai @@ -63,7 +69,7 @@ SVG snippets use the import aliases `eclipsesvg "b612.me/astro/eclipse/svg"` and | --- | --- | --- | | `LocalSolarEclipseOnDate` / `LocalSolarEclipseOnDateNASABulletinSplitK` | Fixed-site solar eclipse query | Returns `(info, bool)`; NASA Split-K by default | | `LunarEclipseOnDate` / `LunarEclipseOnDateDanjon` / `LunarEclipseOnDateChauvenet` | Lunar eclipse query | Same; the suffix forces a shadow-radius model | -| `SearchLocalCentralSolarEclipse` / `SolarEclipseCandidates` | Cross-year central-eclipse search / candidate list | The former carries `status.Exhausted` | +| `SearchLocalCentralSolarEclipse` / `SolarEclipseCandidates` | Cross-year central-eclipse search / candidate list | The former returns `(info, SolarEclipseLocalSearchStatus)` | | `SolarEclipseCentralPath` / `SolarEclipsePartialFootprints` | Central path and partial-footprint geometry | Options are `SolarEclipsePathOptions` / `SolarEclipsePartialFootprintOptions` | | `SolarEclipseBesselianElements` / `SolarEclipseBesselianMuForPublishedTable` | Besselian elements and the published-table conversion | For table comparison | | `eclipsesvg.SolarEclipseMapSVG` / `LunarEclipseMapSVG` | Global visibility maps | Return `(string, bool)` | @@ -89,7 +95,7 @@ true total ``` - `LocalSolarEclipseOnDate` returns the type, all contact instants, magnitude, obscuration and the solar altitude at greatest eclipse in one call; when you only care whether something happens, read the second return value. -- To find "the next central eclipse" across years use `SearchLocalCentralSolarEclipse` (its `status.Exhausted` separates "nothing in the span" from "found"); for a bare candidate list use `SolarEclipseCandidates`. +- To find "the next central eclipse" across years use `SearchLocalCentralSolarEclipse` (the `Exhausted` field of the returned `SolarEclipseLocalSearchStatus` separates "nothing in the span" from "found"); for a bare candidate list use `SolarEclipseCandidates`. - On the lunar side the counterparts are `LunarEclipseOnDate` plus the `Danjon` / `Chauvenet` suffixed entry points that force a shadow-radius model. ### Global visibility maps and lunar charts @@ -154,9 +160,13 @@ There are two solar-radius conventions: the **standard** one (`959.639″` at 1 For the 2024-04-08 total eclipse the measured convention narrows the path by about 0.6 km per side and shortens totality by about 1.5 s; for the 2023-10-14 annular eclipse it widens the path by about 1.3 km and lengthens the annular phase by about 2.1 s; partial magnitudes change by about `1e-4`. -This lets you gauge how sensitive eclipse limits and central durations are to the solar radius. +Either convention can be selected: -Either convention can be selected through `eclipse.SolarEclipseOptions{SunRadiusModel: ...}` with `SolarEclipseOnDateWithOptions`, `LocalSolarEclipseOnDateWithOptions`, and the search/panel variants `LastSolarEclipseWithOptions` / `NextSolarEclipseWithOptions` / `ClosestSolarEclipseWithOptions` / `SolarEclipseGeocentricPanelWithOptions`, or through `basic.SolarEclipseWithOptions` / `basic.LocalSolarEclipseWithOptions` and the `SunRadiusModel` field of the various `...Options` structs; the returned `SunRadiusModel` records the convention used, and `basic.SolarEclipseSunSemidiameter` together with the "S.D." rows of the panels use that same convention. +- Evaluation: `eclipse.SolarEclipseOptions{SunRadiusModel: ...}` with `SolarEclipseOnDateWithOptions` and `LocalSolarEclipseOnDateWithOptions` +- Search and panels: `LastSolarEclipseWithOptions`, `NextSolarEclipseWithOptions`, `ClosestSolarEclipseWithOptions`, `SolarEclipseGeocentricPanelWithOptions` +- The `basic` layer: `SolarEclipseWithOptions`, `LocalSolarEclipseWithOptions`, and the `SunRadiusModel` field of the various `...Options` structs + +The returned `SunRadiusModel` records the convention used, and `basic.SolarEclipseSunSemidiameter` together with the "S.D." rows of the panels use that same convention. Common entry points: @@ -173,7 +183,7 @@ Common entry points: `SolarEclipseBesselianElements` returns the same shape of table that published element tables carry: `T0JDE` is the reference instant, `t = (jde - T0JDE) * 24` is the number of TT hours from it, and `X`/`Y`/`D`/`L1`/`L2`/`Mu` are cubics in `t` (`D` and `Mu` in degrees, the rest in equatorial Earth radii), with `TanF1`/`TanF2` constant over the eclipse. -`L1`/`L2` use the Explanatory Supplement form with its `1/cos f` factor, and a negative umbral `L2` means the Moon's centre has not yet passed the umbra's apex. +`L1`/`L2` use the Explanatory Supplement form with its `1/cos f` factor, and a negative umbral `L2` means the Moon's center has not yet passed the umbra's apex. By default `T0` is the whole TT hour below greatest eclipse, the window half-width is 3 hours, and five evenly spaced samples inside it are fitted by least squares, matching the fitting convention of published tables. @@ -181,7 +191,7 @@ The `Model`, `SunRadiusModel`, `PenumbralK`, `UmbralK` and `DeltaTSeconds` field **`Mu` uses a different time argument from published tables and must be shifted before comparison.** Published tables evaluate sidereal time at `T0` itself, while this library uses `UT = TT - ΔT`; the two differ by `DeltaT * 15.041067/3600` degrees. `Mu` stays continuous across the window and is not folded into `[0,360)`, and `SolarEclipseBesselianMuForPublishedTable` shifts only the constant term. -The library reports the true Greenwich hour angle so that `Mu` stays consistent with its own greatest-eclipse longitude, centre line and contact times; pairing a published table's `Mu` with a correct sidereal time yields a longitude error of about 0.3 degrees (roughly 30 km at the greatest-eclipse latitude of 2024-04-08). +The library reports the true Greenwich hour angle so that `Mu` stays consistent with its own greatest-eclipse longitude, center line and contact times; pairing a published table's `Mu` with a correct sidereal time yields a longitude error of about 0.3 degrees (roughly 30 km at the greatest-eclipse latitude of 2024-04-08). ```go // A Besselian element table; an explicit T0 and DeltaT make it directly comparable. @@ -277,6 +287,7 @@ That `4.8-5.9 s` gap enters every UT-level comparison. Convert it to a ground qu gamma differences median `3.2e-5`, magnitude differences median `4.2e-5`, path-width differences median `0.5 km` (max `8.3 km`) and central-duration differences median `0.25 s` (max `0.59 s`). These are the figures that describe geometry and ephemeris accuracy. +- **Difference between the two built-in models**: the comparisons above were made while Legacy was the default. On the four regression samples the greatest-eclipse TT difference between the semianalytic model and Legacy is only `0.008-0.198 s`, below the TD-layer residual and far below the `4.8-5.9 s` ΔT convention offset of the UT layer; an `astro_legacy` build passes the same NASA baselines. The tables are therefore not split by model. - **To reproduce a publisher's UT values**: inject their adopted ΔT with `astro.SetDeltaT` (for example `74 s` for 2024) and read UT, which removes the convention offset from the comparison; the injection only affects the conversion and never the TT geometry. - Global eclipse references often publish seconds, so second-level checks are meaningful there. @@ -333,14 +344,14 @@ Output: ```text true total // Yangshan site has a local total solar eclipse true {136 37 71 true} // Solar Saros 136, member 37/71, verified -2009-07-22 08:23:55.092397034 +0800 CST // first contact -2009-07-22 09:37:23.088684976 +0800 CST // totality begins -2009-07-22 09:40:20.87983489 +0800 CST // greatest eclipse -2009-07-22 09:43:19.723805487 +0800 CST // totality ends -2009-07-22 11:03:13.914820253 +0800 CST // last contact -5m56.635120511s // totality duration -magnitude=1.076997 obscuration=1.000000 altitude=57.293 // magnitude, obscuration, solar altitude at greatest eclipse -greatest lon=144.1167 lat=24.2193 width=258.3km center=289 // global greatest point, path width, center-line sample count +2009-07-22 08:23:55.491912066 +0800 CST // first contact +2009-07-22 09:37:23.525415658 +0800 CST // totality begins +2009-07-22 09:40:21.314272284 +0800 CST // greatest eclipse +2009-07-22 09:43:20.161139667 +0800 CST // totality ends +2009-07-22 11:03:14.368167221 +0800 CST // last contact +5m56.635724009s // totality duration +magnitude=1.076997 obscuration=1.000000 altitude=57.295 // magnitude, obscuration, solar altitude at greatest eclipse +greatest lon=144.1154 lat=24.2190 width=258.3km center=289 // global greatest point, path width, center-line sample count ``` ### 2012 Xiamen annular eclipse @@ -379,12 +390,12 @@ Output: ```text true annular // Xiamen site has a local annular solar eclipse true {128 58 73 true} // Solar Saros 128, member 58/73, verified -2012-05-21 05:08:12.878718674 +0800 CST // first contact -2012-05-21 06:08:15.561088621 +0800 CST // annularity begins -2012-05-21 06:10:25.180663168 +0800 CST // greatest eclipse -2012-05-21 06:12:34.80941087 +0800 CST // annularity ends -2012-05-21 07:20:54.806806147 +0800 CST // last contact -4m19.248322249s // annularity duration +2012-05-21 05:08:12.807224392 +0800 CST // first contact +2012-05-21 06:08:15.319126546 +0800 CST // annularity begins +2012-05-21 06:10:25.112628936 +0800 CST // greatest eclipse +2012-05-21 06:12:34.91297096 +0800 CST // annularity ends +2012-05-21 07:20:54.736518859 +0800 CST // last contact +4m19.593844414s // annularity duration magnitude=0.933289 obscuration=0.872354 altitude=9.565 // magnitude, obscuration, solar altitude at greatest eclipse ``` @@ -476,12 +487,12 @@ func main() { Output: ```text -true 13587 // Yangshan total-eclipse SVG generated, 13587 bytes -true 13516 // Xiamen annular-eclipse SVG generated, 13516 bytes +true 13603 // Yangshan total-eclipse SVG generated, 13603 bytes +true 13532 // Xiamen annular-eclipse SVG generated, 13532 bytes true total // Beijing site has a local total solar eclipse true {145 23 77 true} // Solar Saros 145, member 23/77, verified -1m33.329527974s // totality duration near downtown Beijing -true 13548 // Beijing total-eclipse SVG generated, 13548 bytes +1m33.053206801s // totality duration near downtown Beijing +true 13564 // Beijing total-eclipse SVG generated, 13564 bytes ``` Rendered examples: @@ -596,18 +607,18 @@ Output: ```text total // eclipse type true {125 49 72 true} // Lunar Saros 125, member 49/72, verified -2028-12-31 16:52:05.603753328 +0000 UTC // greatest eclipse -2.2739938633996872 1.2461094682708755 // penumbral and umbral magnitudes -2028-12-31 14:03:54.239418804 +0000 UTC // P1 -2028-12-31 15:07:42.171904742 +0000 UTC // U1 -2028-12-31 16:16:27.306801974 +0000 UTC // U2 -2028-12-31 17:27:46.228030622 +0000 UTC // U3 -2028-12-31 18:36:32.270547151 +0000 UTC // U4 -2028-12-31 19:40:11.575520038 +0000 UTC // P4 -2.299608256177245 1.2511661731458574 // Chauvenet penumbral and umbral magnitudes +2028-12-31 16:52:05.797516107 +0000 UTC // greatest eclipse +2.2740102011228953 1.246124858428628 // penumbral and umbral magnitudes +2028-12-31 14:03:54.428796172 +0000 UTC // P1 +2028-12-31 15:07:42.35383898 +0000 UTC // U1 +2028-12-31 16:16:27.44777888 +0000 UTC // U2 +2028-12-31 17:27:46.474579274 +0000 UTC // U3 +2028-12-31 18:36:32.476299405 +0000 UTC // U4 +2028-12-31 19:40:11.773990094 +0000 UTC // P4 +2.29962460346294 1.2511815539147877 // Chauvenet penumbral and umbral magnitudes true // local date overlaps an eclipse total // local eclipse type -2029-01-01 00:52:05.603753328 +0800 CST // greatest eclipse in UTC+8 +2029-01-01 00:52:05.797516107 +0800 CST // greatest eclipse in UTC+8 ``` ### Checks against NASA data @@ -618,6 +629,8 @@ The reference values come from NASA GSFC's lunar-eclipse catalog (greatest eclip That is a `4.8-5.9 s` offset, so any UT-level contact comparison carries it wholesale; it is not a lunar-geometry error. The TD level (no ΔT) is the layer that tests the ephemeris and the shadow geometry on its own. +The table below was measured while Legacy was the default. Re-measuring `2026-03-03` with the semianalytic model gives a greatest-eclipse TD of `+0.083 s` against the `+0.114 s` for Legacy in the table, a `0.031 s` model difference; that is far below the `0.1-1.0 s` convention residuals in the table, so the table is not split by model. + | Sample | Model | Penumbral magnitude error | Umbral magnitude error | Greatest-eclipse TD difference (no ΔT) | Greatest-eclipse UT difference (with the ΔT convention) | | --- | --- | --- | --- | --- | --- | | 2026-03-03 total lunar eclipse | Danjon | -0.000067208 | -0.000069993 | +0.114 s | +5.99 s | @@ -696,7 +709,7 @@ func main() { Output: ```text -true 19816 // lunar-eclipse SVG generated, 19816 bytes +true 19832 // lunar-eclipse SVG generated, 19832 bytes ``` Rendered example: @@ -711,6 +724,60 @@ Rendered example: - NASA 2024-03-25 penumbral lunar eclipse diagram: - NASA lunar eclipse algorithm and history notes: +## Lunar limb correction + +The Moon is a circular disk by default; local solar eclipses, global solar eclipses, lunar eclipses and fixed-site occultations can all use the lunar limb profile from `moon/limb`. The profile gives the actual limb radius toward the target and is used to refine contact times. + +| Quantity | Corrected with the limb? | +| --- | --- | +| Local solar eclipse first and last contacts, central begin/end and central duration | Yes | +| Local magnitude and obscuration at greatest eclipse | Yes; the instant of greatest eclipse is unchanged | +| Global solar eclipse partial begin/end (P1/P4) and the central duration at greatest eclipse | Yes | +| Solar central path northern/southern limits, per-point widths, width at greatest eclipse, central band footprint and longest central duration | Yes, via `SolarEclipsePathOptions.LunarLimb` or the module | +| Outer partial visibility boundary (zero-magnitude envelope and the start/end sunrise-sunset curves) | Yes, via `SolarEclipsePartialFootprintOptions.LunarLimb` or the module | +| The six lunar-eclipse contacts | Yes, only through the module or context | +| Occultation disappearance, reappearance and planetary inner/outer contacts | Yes, see the [occultation manual](occultation.md#lunar-limb-correction) | +| Local and global instant of greatest eclipse, global magnitude, type, the center line itself, magnitude isolines, greatest-eclipse isochrones and the greatest-eclipse sunrise-sunset curves | No, still circular | + +Correction rules: + +- Annular and total-lunar inner contacts use the limb on the far side from the contact center. +- P1/P4 scan observers along the Earth's limb on both sides of the circular tangent point and keep the earliest (latest) contact. +- Central-path limits take the worst point within ±45° of position angle around the central direction: a total limit is where the deepest limb valley still covers the Sun, an annular limit where the highest limb point still lies inside the Sun. Endpoints on the horizon stay circular. +- The outer partial boundary is where the highest limb point just touches the near edge of the Sun. Horizon endpoints and the start/end sunrise-sunset curves move along the terminator at the same instant, so the two still meet at one point. Points with solar altitude between 0.05° and 0.5° stay circular. `PartialBoundaryLimbApplied` reports that at least one point moved. Across four eclipses including 2024-04-08 and high-latitude partial ones, the zero-magnitude envelope shifts by a median of 1–4 km and a maximum of about 7 km; the start/end sunrise-sunset curves slide along the terminator and can move more than 10 km at individual points; with the default step, footprint computation takes about 10%–20% longer. + +```go +import "b612.me/astro/moon/limb" + +// Per query +info, ok := eclipse.LocalSolarEclipseOnDateWithOptions(date, lon, lat, 0, + eclipse.SolarEclipseOptions{LunarLimb: limb.Default()}) + +// For every local solar eclipse in a context +base, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +ctx := base.WithLunarLimb(limb.Default()) +info, ok = ctx.Eclipse().LocalSolarEclipseOnDate(date, lon, lat, 0) +``` + +Charts support it too: every eclipse-chart options struct in `eclipse/svg` carries a `LunarLimb` field. The local solar chart corrects contact times and the magnitude and obscuration at greatest eclipse; both lunar diagrams correct contact times, with the disks still drawn as circles and the instant of greatest eclipse unchanged; the lunar world map moves each phase horizon with the corrected contact; the global solar map corrects the central-band limits, the width at greatest eclipse, the outer partial visibility boundary, the global P1/P4 and the local panel at greatest eclipse (including the central duration), while magnitude isolines, isochrones and the geocentric panel stay circular. + +The layers are independent — a profile with values only at some position angles may correct just some of them — and the global solar map footer lists only the layers actually corrected, e.g. "Lunar limb profile applied to visibility boundary (isolines circular)."; `CentralLimitsLimbApplied` on the central path result records whether the limits and width were corrected. + +Each chart declares the convention after the time-scale note ("Circular limb." or "Limb profile." on local charts), and a custom `FooterNote` does not replace it. + +`LunarLimbApplied` on a result records whether the limb correction actually took effect, and charts use it rather than whether the option is set. Where the profile has no value (`LimbRadiusKM` returns `false`), the radius falls back to the circular limb and that sample does not take part in the correction; when nothing in a result was corrected the field is `false` and the numbers equal the circular result exactly. + +`LunarLimb` in the options takes precedence over the module setting. `eclipse.(*Eclipse).WithLunarLimb` returns a new module and leaves the original unchanged. Any type implementing `eclipse.LunarLimb` can supply another profile; the low-level `basic.LunarEclipseWithLunarLimb` offers the same capability. + +The built-in profile lives in the separate `moon/limb` package. It is a processed product of the ISAS/JAXA SELENE (Kaguya) LALT global topography data (published by DARTS, 16 pixels/degree) and is about 1.5 MB; the data is linked only when that package is imported. The table is indexed by the observer's selenographic longitude and latitude (±9° / ±8°, 1° steps) and position angle (0.1° steps); trilinear interpolation reproduces profiles taken directly from the DEM with an RMS error of about 90 m. + +The corrections are a few seconds: totality at Dallas on 2024-04-08 changes from `227.6 s` to `229.8 s`, Torreón `250.6 s` → `255.0 s`, annularity at Albuquerque on 2023-10-14 `289.4 s` → `290.3 s`, and the contacts of the 2025-09-07 total lunar eclipse move by `-4.4 s` to `+0.2 s`. The total-path limits move by about `2.1 km` / `1.0 km` on average (up to about `3.2 km`), the width at greatest eclipse from `198.6 km` to `196.8 km`, the longest central duration from `268.0 s` to `271.7 s` and the central duration at greatest eclipse from `268.0 s` to `271.2 s`. + +For item-by-item checks, compare with the published values of the *Chinese Astronomical Almanac* (Purple Mountain Observatory): about `-0.8 s` / `+0.2 s` at whole-second precision, and with the Chauvenet convention at 0.1-minute precision a mean error of `1.5 s` circular and `2.2 s` with the limb, both within the published rounding. + ## Solar and Lunar Eclipse Charts All five `eclipse/svg` entry points return `(string, bool)`. @@ -719,11 +786,11 @@ A `false` second value means the chart cannot be drawn with the current argument | Entry point | Chart | Suggested canvas | | --- | --- | --- | -| `LocalSolarEclipseSVG` | Fixed-site solar disk chart (see "Solar-eclipse SVG" above) | 920x720 and up | -| `SolarEclipseMapSVG` | Global visibility map, four selectable projections | 1200x800; orthographic globe 1000x1414 | -| `LunarEclipseSVG` | Lunar shadow-path diagram | 960x620 and up | -| `LunarEclipseMapSVG` | Lunar world visibility map | 1200x800 | -| `LunarEclipseDetailedSVG` | Lunar detailed layout (diagram and base map on one page) | 1000x1414 or 1414x1000 | +| `LocalSolarEclipseSVG` | Fixed-site solar disk chart (see "Solar-eclipse SVG" above) | 920×720 and up | +| `SolarEclipseMapSVG` | Global visibility map, four selectable projections | 1200×800; orthographic globe 1000×1414 | +| `LunarEclipseSVG` | Lunar shadow-path diagram | 960×620 and up | +| `LunarEclipseMapSVG` | Lunar world visibility map | 1200×800 | +| `LunarEclipseDetailedSVG` | Lunar detailed layout (diagram and base map on one page) | 1000×1414 or 1414×1000 | A solar global map draws the full partial-visibility region, the total/annular central band, the central line, global phase information and central-line time markers, together with the rise/set lines of first/greatest/last contact, the subsolar point, the shadow-axis entry and exit points, the `P1-P4/U1-U4` contacts, and the sampled penumbral and umbral/antumbral outlines that are off by default and requested on demand. @@ -771,7 +838,7 @@ fmt.Println(ok, len(annular), len(total)) `EclipseMapProjectionOrthographic` gives the NASA-style **orthographic globe**: the view point is the greatest-eclipse point, only the hemisphere facing it is drawn, and the projection boundary is the great circle of the visible hemisphere. -The orthographic projection uses the NASA-style centered-globe layout; a `1000x1414` canvas is recommended. It does not change the underlying geographic results. +The orthographic projection uses the NASA-style centered-globe layout; a `1000×1414` canvas is recommended. It does not change the underlying geographic results. ```go globe, ok := eclipsesvg.SolarEclipseMapSVG( @@ -786,11 +853,11 @@ globe, ok := eclipsesvg.SolarEclipseMapSVG( ![2009 Yangtze total solar eclipse orthographic globe](../../img/solar-eclipse-yangshan-2009-globe-en.svg) -The orthographic globe is most comfortable in portrait (e.g. `1000x1414`); landscape (`1200x800`) also renders, with a visibly smaller globe. +The orthographic globe is most comfortable in portrait (e.g. `1000×1414`); landscape (`1200×800`) also renders, with a visibly smaller globe. #### Polar azimuthal equidistant -`EclipseMapProjectionNorthPolar` and `EclipseMapProjectionSouthPolar` place the pole at the centre of the canvas, which suits events whose band lies entirely at high latitude. +`EclipseMapProjectionNorthPolar` and `EclipseMapProjectionSouthPolar` place the pole at the center of the canvas, which suits events whose band lies entirely at high latitude. `EclipseMapProjectionAuto` (the zero value) picks a polar map when that fits, so specify a projection explicitly only when the layout must be fixed; the projection only affects SVG presentation and never the underlying WGS84 geography. @@ -885,9 +952,9 @@ Degradable layers record their actual geometry source in `data-source`; the voca #### Canvas floors and fallbacks -- The solar-map floor is **800x560**: a width below 800 or a height below 560 falls back to 960x640. On narrower landscape canvases the map frame overlaps the right-hand data grid horizontally and the panel line spacing drops below 1 px. +- The solar-map floor is **800×560**: a width below 800 or a height below 560 falls back to 960×640. On narrower landscape canvases the map frame overlaps the right-hand data grid horizontally and the panel line spacing drops below 1 px. - The partial-region fill is a union of instantaneous footprints, so `PartialStep` values below two minutes are clamped to two minutes; a denser request does not improve the result. -- The lunar detailed layout derives its arrangement from `Height`: `640x420` and `800x600` cannot hold the diagram and base-map floors and return `false`, while `1000x1414` and `1414x1000` render normally. +- The lunar detailed layout derives its arrangement from `Height`: `640×420` and `800×600` cannot hold the diagram and base-map floors and return `false`, while `1000×1414` and `1414×1000` render normally. ### Lunar eclipse charts @@ -941,7 +1008,7 @@ penumbral, ok := eclipsesvg.LunarEclipseMapSVG( #### Detailed layout -The detailed layout combines both lunar charts on one page: a centred summary (greatest eclipse, penumbral/umbral magnitude, gamma, penumbral/umbral radii, Moon distance, Saros series), geocentric coordinate blocks for Sun and Moon on either side, the shadow-path diagram, three columns for duration, arc-minute scale and contact times, and the world visibility base map with its legend underneath. +The detailed layout combines both lunar charts on one page: a centered summary (greatest eclipse, penumbral/umbral magnitude, gamma, penumbral/umbral radii, Moon distance, Saros series), geocentric coordinate blocks for Sun and Moon on either side, the shadow-path diagram, three columns for duration, arc-minute scale and contact times, and the world visibility base map with its legend underneath. The shadow geometry comes from `basic.LunarEclipseShadowGeometryAt`, where **gamma uses the Earth equatorial radius while the penumbral/umbral radii are in degrees** - to convert them into Earth radii, multiply by the Earth parallax at the Moon. @@ -990,3 +1057,43 @@ ut1, ok := eclipsesvg.SolarEclipseMapSVG( ``` ![2009 Yangtze total solar eclipse global visibility map (UT1 scale)](../../img/solar-eclipse-yangshan-2009-global-ut1-en.svg) + +## Transit charts + +Transit contact times, topocentric visibility and `LocalTransit` live in [Planets: Mercury and Venus transits](planets.md#mercury-and-venus-transits); this section covers only the two `eclipse/svg` charts. `eclipse/svg` has two transit renderers, both returning `(string, bool)`; `date` selects the closest transit, and a same-day query finds that day's transit. + +| Entry | Chart | Suggested canvas | +| --- | --- | --- | +| `TransitMapSVG` | Geocentric global visibility: entire transit, transit at sunrise, transit at sunset, not visible | 960×640 and up | +| `TransitLocalSVG` | Topocentric solar disk: north up, east left, planet-center track, planet disk at each contact and at greatest, contact table | 960×680 and up | + +Regions follow whether the Sun's center is above the apparent horizon at C1, greatest and C4, with standard refraction (a horizon circle of 90°34′). Isochrones are off by default; enable them bitwise with `Isochrones`: `TransitIsochroneC1` … `TransitIsochroneC4`, or the combinations `TransitIsochroneExternal` (C1 and C4) and `TransitIsochroneAll`. C2/C3 are dropped for a grazing transit without internal contacts. Isochrones are topocentric contact times drawn only where the Sun is above the apparent horizon; a non-positive `IsochroneStep` uses 20 s for Mercury and 1 min for Venus. A transit is visible from roughly half the globe, so the map always shows the whole world; `Projection` selects one of the four projections. + +```go +venus := time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC) +world, ok := eclipsesvg.TransitMapSVG(eclipsesvg.TransitVenus, venus, eclipsesvg.TransitMapSVGOptions{ + Isochrones: eclipsesvg.TransitIsochroneExternal, + IsochroneStep: 10 * time.Minute, + Language: "en", +}) +local, ok := eclipsesvg.TransitLocalSVG(eclipsesvg.TransitVenus, venus, 116.4, 39.9, 50, + eclipsesvg.TransitLocalSVGOptions{Language: "en"}) +``` + +On the disk chart, track parts with the Sun's center below the apparent horizon are grey and dashed, and the table adds sunrise or sunset during the transit. `TransitLocalSVG` returns `false` when the site sees no geometric transit. The topocentric contact method is described in [Planets](planets.md#mercury-and-venus-transits). + +### Mercury: 2032-11-13 (Beijing) + +2032-11-13 is the next Mercury transit visible from Beijing. Beijing sees first contact, second contact and greatest, with the Sun only `+0.2°` high at greatest; third and fourth contact happen after sunset (Sun about `-24°`) and are drawn grey and dashed on the disk chart. + +![Mercury transit of 2032-11-13 from Beijing](../../img/transit-mercury-beijing-2032-en.svg) + +![Mercury transit of 2032-11-13 global visibility](../../img/transit-mercury-beijing-2032-global-en.svg) + +### Venus: 2012-06-06 (Beijing) + +The Venus transit of 2012-06-06 was visible in full from Beijing, with `6h39m4s` from first to fourth contact and solar altitudes of `+14°` to `+71°` at the contacts. + +![Venus transit of 2012-06-06 from Beijing](../../img/transit-venus-beijing-2012-en.svg) + +![Venus transit of 2012-06-06 global visibility](../../img/transit-venus-beijing-2012-global-en.svg) diff --git a/doc/manual/en/ephemeris.md b/doc/manual/en/ephemeris.md new file mode 100644 index 0000000..97298e8 --- /dev/null +++ b/doc/manual/en/ephemeris.md @@ -0,0 +1,219 @@ +# Explicit ephemeris instances + +[中文](../ephemeris.md) | [Back to README](../../../README.en.md) + +Package-level functions use the current ephemeris model of the process, which is the semianalytic model by default. To use several models side by side in one process, or to plug in an external ephemeris (SPK, a home-grown engine), pin the ephemeris down with `astro.Context`; when geometric state (position and velocity) has to be read directly, drop down to `astro.Instance`. + +## Contents + +- [Switching to another built-in model](#switching-to-another-built-in-model) +- [Coordinates, altitude and rise/set with a Context](#coordinates-altitude-and-riseset-with-a-context) +- [Querying events by date](#querying-events-by-date) +- [Plugging in an external ephemeris](#plugging-in-an-external-ephemeris) +- [Stacking multiple sources](#stacking-multiple-sources) +- [Engine contract](#engine-contract) +- [Limits](#limits) + +## Switching to another built-in model + +`astro.NewContext` creates an independent context, and the model is fixed when the context is created: + +```go +import ( + "fmt" + "time" + + "b612.me/astro" +) + +date := time.Date(2026, 2, 17, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + +semi, err := astro.NewContext(astro.ModelSemianalytic) // the model used by default builds +if err != nil { + panic(err) +} +legacy, err := astro.NewContext(astro.ModelLegacy) +if err != nil { + panic(err) +} + +ra1, dec1 := semi.Sun().ApparentRaDec(date) +ra2, dec2 := legacy.Sun().ApparentRaDec(date) +fmt.Printf("%.8f %.8f\n", ra1, dec1) +fmt.Printf("%.8f %.8f\n", ra2, dec2) +``` + +```text +330.64554489 -11.99819781 +330.64554785 -11.99820252 +``` + +Once a context is created its model is fixed, and a later `astro.SetModel` does not affect it. `SetModel` changes the default model of the package-level functions and accepts only the two built-in models; any other model returns `astro.ErrUnknownModel` and leaves the global state unchanged. A custom engine must be passed explicitly to a `Context` or an `Instance`. `NewContext` returns `ErrUnknownModel` for an unknown model and `ErrInvalidEngine` when the engines do not fit the model (an engine passed with a built-in model, or `ModelCustom` without exactly one non-nil engine); `NewModelInstance` accepts only built-in models. + +## Coordinates, altitude and rise/set with a Context + +The methods returned by the accessors take the same arguments and return the same values as the package-level functions of the same name, so `sun.Xxx` becomes `ctx.Sun().Xxx`: + +```go +ctx, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +date := time.Date(2026, 2, 17, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) +lon, lat := 116.4, 39.9 + +// The Sun: geocentric apparent right ascension/declination, topocentric altitude/azimuth +ra, dec := ctx.Sun().ApparentRaDec(date) +alt := ctx.Sun().Altitude(date, lon, lat) +az := ctx.Sun().Azimuth(date, lon, lat) +fmt.Printf("Sun ra=%.6f dec=%.6f alt=%.6f az=%.6f\n", ra, dec, alt, az) + +// The Moon: use GeocentricApparentRaDec for geocentric coordinates, topocentric quantities take the longitude and latitude directly +moonRa, moonDec := ctx.Moon().GeocentricApparentRaDec(date) +moonAlt := ctx.Moon().Altitude(date, lon, lat) +moonRise, err := ctx.Moon().RiseTime(date, lon, lat, 0, true) +fmt.Printf("Moon ra=%.6f dec=%.6f alt=%.6f rise=%s err=%v\n", + moonRa, moonDec, moonAlt, moonRise.Format("15:04:05"), err) + +// Planets are reached through accessors named after their packages +marsRa, marsDec := ctx.Mars().ApparentRaDec(date) +marsAlt := ctx.Mars().Altitude(date, lon, lat) +fmt.Printf("Mars ra=%.6f dec=%.6f alt=%.6f\n", marsRa, marsDec, marsAlt) +``` + +```text +Sun ra=330.645545 dec=-11.998198 alt=37.682918 az=171.221286 +Moon ra=327.197499 dec=-14.621205 alt=34.596340 rise=06:59:57 err= +Mars ra=322.193276 dec=-16.023967 alt=34.060823 +``` + +The instant is 2026-02-17 12:00 CST, and the site is 116.4° east and 39.9° north. `RiseTime` returns an error instead of a zero time when it finds no rise or set within the local civil day. + +| Entry point | Package | Purpose | +| --- | --- | --- | +| `Sun()` | `sun` | Geocentric coordinates, topocentric altitude/azimuth/zenith distance, rise and set and twilight, apparent diameter, physical ephemeris | +| `Moon()` | `moon` | Geocentric and topocentric coordinates, altitude/azimuth, rise and set, lunar phase, geocentric distance, libration | +| `Mercury()` … `Neptune()` | The planet packages | Apparent coordinates, altitude/azimuth, rise and set, phase and apparent magnitude, events such as opposition, conjunction and greatest elongation | +| `Eclipse()` | `eclipse` | Solar and lunar eclipse queries, local contact times, eclipse paths | +| `Phenomena()` | `phenomena` | Ecliptic longitude crossings, stations, closest approach, rise and set at any altitude, heliacal visibility | +| `GeoJSON()` | `geojson` | Map data for celestial events | + +The `...N` methods use the fast table, for example `ctx.Sun().AltitudeN(date, lon, lat, n)` and `ctx.Moon().TrueLoN(date, n)`; see [Accuracy and performance](accuracy.md) for the meaning of `n`. + +## Querying events by date + +Event queries also hang off the modules: + +```go +date := time.Date(2026, 3, 3, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + +info, ok := ctx.Eclipse().LunarEclipseOnDate(date) +next := ctx.Eclipse().NextLunarEclipse(date) +fmt.Println(ok, info.Maximum.Format("2006-01-02 15:04 MST"), info.Type) +fmt.Println(next.Maximum.Format("2006-01-02 15:04 MST"), next.Type) +``` + +```text +true 2026-03-03 19:33 CST total +2026-03-03 19:33 CST total +``` + +`ok` is `true` only when a lunar eclipse occurs on that date; the example date 2026-03-03 has one. Filtering and searching both use the instance of that context. The Saros tables of the built-in models are selected according to the instance model; a custom engine can describe an arbitrary trajectory, so no Saros number is inferred and `HasSaros` is `false` in the result. + +## Plugging in an external ephemeris + +`spk.Open` reads a JPL SPK kernel and passes it to `NewContext` as an `astro.Engine`; afterwards the calls are exactly the same as with a built-in model: + +```go +import "b612.me/astro/planet/spk" + +kernel, err := spk.Open("de440s.bsp") +if err != nil { + panic(err) +} +defer kernel.Close() + +custom, err := astro.NewContext(astro.ModelCustom, kernel) +if err != nil { + panic(err) +} + +// Reuse the date above (2026-02-17 12:00 CST) and the longitude and latitude +ra, dec := custom.Sun().ApparentRaDec(date) +alt := custom.Sun().Altitude(date, lon, lat) +marsRa, marsDec := custom.Mars().ApparentRaDec(date) +marsAlt := custom.Mars().Altitude(date, lon, lat) +moonAlt := custom.Moon().Altitude(date, lon, lat) +fmt.Printf("Sun ra=%.6f dec=%.6f alt=%.6f\n", ra, dec, alt) +fmt.Printf("Mars ra=%.6f dec=%.6f alt=%.6f\n", marsRa, marsDec, marsAlt) +fmt.Printf("Moon alt=%.6f\n", moonAlt) +``` + +```text +Sun ra=330.645545 dec=-11.998198 alt=37.682918 +Mars ra=322.193276 dec=-16.023967 alt=34.060823 +Moon alt=34.596325 +``` + +The results agree closely with the built-in semianalytic model at the same instant; here the lunar altitude differs by about `0.05″`. + +Use `Instance` instead when the raw geometric state (position and velocity) is needed: + +```go +inst := astro.NewInstance(kernel) +jdTDB := calendar.Date2JD(astro.TDBFromTT(date)) +pos, vel, err := inst.State(astro.BodyMoon, astro.BodyEarth, jdTDB) +start, end, ok := inst.Coverage(astro.BodyMoon) +fmt.Printf("pos=%.1f,%.1f,%.1f km\nvel=%.1f,%.1f,%.1f km/day\nerr=%v coverage=%.1f..%.1f ok=%v\n", + pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], err, start, end, ok) +``` + +```text +pos=312305.1,-204135.4,-98208.0 km +vel=48600.2,64310.3,36543.8 km/day +err= coverage=2396752.5..2506352.5 ok=true +``` + +`State` takes TDB epochs and returns the ICRF geometric position (km) and velocity (km/day). `Coverage` gives the usable epoch interval of that body, and going outside it returns `ErrEphemerisOutOfRange`. + +## Stacking multiple sources + +`astro.NewStackInstance` composes several engines in argument order, and the source passed first has priority. Small-body kernels often provide only the state of the target relative to the Sun, and Stack composes a state of the target relative to the solar-system barycenter across sources: + +```go +de440, _ := spk.Open("de440s.bsp") +ceres, _ := spk.Open("ceres.bsp") +defer de440.Close() +defer ceres.Close() + +stack := astro.NewStackInstance(de440, ceres) + +// A small-body kernel usually covers a narrow range, so the query epoch must fall inside its coverage +smallDate := time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC) +jdTT := calendar.Date2JD(astro.TTFromUTC(smallDate)) +dir, dist, tau, err := stack.Apparent(astro.Body(20000001), jdTT) +fmt.Printf("%.8f,%.8f,%.8f\ndist=%.0f km tau=%.5f day err=%v\n", dir[0], dir[1], dir[2], dist, tau, err) +``` + +```text +0.98097637,0.17021858,-0.09333272 +dist=417020638 km tau=0.01610 day err= +``` + +`astro.Body` is an `int`, so any NAIF identifier can be written directly; Ceres is `20000001`. The Ceres kernel in this example covers only 2026-01-01 to 01-03 (`stack.Coverage` returns `2461041.5..2461043.5`); going outside the coverage returns `ErrEphemerisOutOfRange` and never silently switches to another source. + +## Engine contract + +- `Engine.State(target, center, jdTDB)`: ICRF geometric position (km) and velocity (km/day), with TDB epochs. A custom engine must implement it. +- The optional `ApparentEngine.Apparent(target, jdTT)`: geocentric ICRF unit apparent direction, distance (km) and light time (days) at a TT epoch. An engine that implements only `State` (such as SPK) goes through the generic vector apparent-place chain: iterate the light time, then add solar gravitational deflection and relativistic aberration; this requires the engine to provide states of the Earth, the Sun and the target relative to the solar-system barycenter (`BodySSB`). +- Engines are called concurrently and must be read-only and reentrant; the library does not start goroutines. +- A failure of a custom engine does not fall back to the built-in tables: scalar entry points return `NaN`, the `Maximum` of a lunar eclipse core is `NaN` and each phase flag is `false`, and date queries report a miss. +- Scalar coordinates and the `basic` lunar eclipse entry points take TT epochs; the geometric engine currently approximates TDB with TT, so call `Instance.State` directly when strict TDB is required. + +## Limits + +- `EphemerisLegacy` instances have scalar entry points only and no geometric engine: `Instance.State` returns `astro.ErrEphemerisNoEngine`, and `Coverage` and `Apparent` report unavailable. Use the semianalytic model or a custom engine when positions and velocities are needed. +- An explicit instance only decides which ephemeris is used, and does not guarantee a smaller binary: both built-in tables are linked into the program. +- The usable range of the semianalytic model is TT JD `-1931455.0 … 5373545.0`, and evaluation still extrapolates outside it; the modern planetary segment is JD `2086295.0 … 2816795.0`. See [Accuracy and performance](accuracy.md) for the errors. +- `Stack.Coverage` is the union of the source intervals, may contain gaps in between, and does not guarantee that every body needed by a center chain (the path of centers Stack searches for between its sources to compose a state) is available; rely on what `State` returns at a specific epoch. +- Most `basic` entry points that support instances carry the `WithInstance` suffix, and the `n`-parameterised ones carry `NWithInstance`; passing `nil` means the current global model of the process. The exceptions are the shadow-radius entry `LunarEclipseWithLunarLimb` and the 18 atmospheric rise/set entries, which take `*Instance` as their last parameter. Direct calls to these entry points are normally unnecessary. diff --git a/doc/manual/en/formula.md b/doc/manual/en/formula.md index 99564e5..5529a82 100644 --- a/doc/manual/en/formula.md +++ b/doc/manual/en/formula.md @@ -2,15 +2,16 @@ [中文](../formula.md) | [Back to README](../../../README.en.md) -`formula` holds the common formulas that are unrelated to a specific date or ephemeris; they suit popular-science estimates, novel settings, and teaching demonstrations. +`formula` holds pure formulas that read no ephemeris tables, covering radiation, photometry, telescopes, the atmosphere and Earth orientation for estimates and observing aids. -It performs no time-scale conversion and reads no ephemeris tables: the inputs are only instantaneous or constant parameters such as temperature, wavelength, distance, aperture, and altitude. +It performs no time-scale conversion and reads no global ephemeris. Most inputs are physical quantities such as temperature, wavelength, distance, aperture and altitude; `TIOLocatorArcsec` is the exception that accepts a TT Julian date. -Altitude and zenith-distance conventions follow [Observing-angle semantics](sun-moon.md#observing-angle-semantics); when coordinate-layer refraction is needed, use `coord`'s [airmass](coord.md#airmass); overall accuracy and applicability are in the manual [Scope And Accuracy](accuracy.md). +Altitude and zenith-distance conventions follow [Observing-angle semantics](sun-moon.md#observing-angle-semantics); overall accuracy and applicability are in [Accuracy and performance](accuracy.md). ## Contents - [Magnitude, synodic period and blackbody radiation](#magnitude-synodic-period-and-blackbody-radiation) +- [Refraction, extinction and Earth orientation](#refraction-extinction-and-earth-orientation) - [API Reference](#api-reference) - [Blackbody and Radiation](#blackbody-and-radiation) - [Synodic Period](#synodic-period) @@ -31,6 +32,35 @@ Altitude and zenith-distance conventions follow [Observing-angle semantics](sun- - [Zero Values and Invalid Input](#zero-values-and-invalid-input) - [Accuracy and Applicability](#accuracy-and-applicability) +## Refraction, extinction and Earth orientation + +| Entry point | Input and output convention | +| --- | --- | +| `ApparentAltitude` / `TrueAltitude` | True/apparent altitude conversion in degrees; pressure hPa and temperature °C | +| `RefractionFromTrueAltitude` / `RefractionFromApparentAltitude` | Refraction in degrees, added to true altitude or subtracted from apparent altitude | +| `ExtinctionSchaefer` | V-band zenith extinction components and `Total`, in magnitudes per airmass | +| `TIOLocatorArcsec` | TT Julian date to the TIO locator s′ in arcseconds | +| `PolarMotionMatrix` | `xp`, `yp` and s′ in arcseconds; a matrix acting on column vectors from TIRS to ITRS | +| `CelestialPoleOffsetMatrix` | dX/dY in mas; a first-order small-angle correction matrix multiplying an existing celestial rotation on the left | + +Abbreviations in the table: `TIO` is the terrestrial intermediate origin, `TIRS` and `ITRS` are the terrestrial intermediate and terrestrial reference systems, `s′` is the TIO locator, and `CPO` is the celestial pole offset (`dX`, `dY`) in `mas` (milliarcseconds). + +```go +apparent := formula.ApparentAltitude(5, 1010, 10) +trueAltitude := formula.TrueAltitude(apparent, 1010, 10) +extinction := formula.ExtinctionSchaefer(100, 20, 50, 39.9, 120) +polarMotion := formula.PolarMotionMatrix(0.1, 0.2, formula.TIOLocatorArcsec(2460000.5)) +_ = trueAltitude +_ = extinction.Total +_ = polarMotion +``` + +The standard refraction constants are 1010 hPa and 10 °C. Pressure must be positive, temperature above absolute zero and inputs finite. Both directions use the same model; a missing inverse returns `NaN`. Refraction is zero at altitudes ≤−5° or ≥90°. These are empirical-model boundaries, not a guarantee for precision near-horizon observing. + +`ExtinctionSchaefer` uses site elevation in metres, unlike the ellipsoidal height of most observing APIs. Humidity is a percentage in `(0,100)`; latitude and solar right ascension are degrees. Assign `Total` to `phenomena.HeliacalOptions.ExtinctionCoefficient` to use it; when it is not assigned, the visibility calculation uses that option's own default extinction. Do not reuse a V-band coefficient for a different band without conversion. + +Callers supply polar-motion and CPO parameters. The package downloads no IERS (International Earth Rotation and Reference Systems Service) data and does not automatically apply these corrections to default rise/set, eclipse or occultation calculations. The arcseconds of s′ and xp/yp differ from the mas of dX/dY; matrix elements are dimensionless. + ## Magnitude, synodic period and blackbody radiation ```go @@ -299,11 +329,11 @@ Pressure- and temperature-corrected versions live in [Coordinate tools](coord.md - Telescope family: aperture millimeters; `DawesLimitArcsec` and `RayleighLimitArcsec` return **arcseconds**, not degrees; `LightGatheringPowerRatio` and `LimitingMagnitudeEmpirical` return a dimensionless ratio and a magnitude respectively. - Airmass family: altitude (or zenith distance) in degrees; the return value is the dimensionless relative airmass with 1 at the zenith. - Synodic period: the unit is chosen by the caller, both inputs must match, and the output matches them; the package does not assume days. -- This package produces no apparent diameter or apparent radius. Apparent-radius fields in the eclipse and occultation manuals are in arcseconds; here only the two Dawes/Rayleigh limits use arcseconds while all other angles are degrees, so do not interchange them. +- This package produces no apparent diameter or radius. Ordinary angles and refraction use degrees; Dawes/Rayleigh and the TIO locator use arcseconds, polar-motion inputs arcseconds and CPO inputs mas. ### Time Scales -- No entry point accepts an instant or performs any time-scale conversion: the formulas depend only on parameters such as temperature, wavelength, distance, aperture, and altitude, and are independent of UTC, UT1, and TT. +- The package performs no time-scale conversion. `TIOLocatorArcsec` accepts a TT Julian date; the other formulas use caller-supplied physical quantities or periods without reading global time-scale settings. - The synodic period is a length of time, not the instant of the next conjunction. Landing on a date requires the conjunction APIs in the planet packages plus civil time, whose convention is in the manual [Time Scale Conventions](timescale.md). @@ -311,10 +341,10 @@ Pressure- and temperature-corrected versions live in [Coordinate tools](coord.md ### Angle Quadrants and Degree/Radian Boundaries -- All angle parameters are degrees, are converted to radians internally, and come back as degrees or arcseconds; radians never leak to the caller. -- Altitude and zenith distance are both limited to `[0,90]`: the horizon is `0°`, the zenith is `+90°`, and zenith distance is the complement of altitude. +- Ordinary angles are degrees, polar-motion angles arcseconds and CPO mas. Follow each function's declared units; internal radians never leak to the caller. +- Airmass models limit altitude and zenith distance to `[0,90]`, with no quadrant folding. Refraction functions have their own low-altitude boundary; do not apply the airmass range to them. - The package performs no quadrant folding, so a negative altitude (below the horizon) is simply invalid and is never folded to the zenith or replaced by an absolute value. + Negative altitude is invalid for the airmass models rather than folded to the zenith or replaced by an absolute value. - Zenith distance `z` and altitude `h` satisfy `z = 90° − h`; `AirmassPlaneParallel` takes `h` and `AirmassPlaneParallelByZenithDistance` takes `z`, and both must agree for the same geometry. - Dawes/Rayleigh return arcseconds; divide by 3600 to compare with the degree-based angles in the other manuals. diff --git a/doc/manual/en/map-geojson.md b/doc/manual/en/map-geojson.md index 1d0a377..9f3054f 100644 --- a/doc/manual/en/map-geojson.md +++ b/doc/manual/en/map-geojson.md @@ -7,6 +7,7 @@ ## Contents - [Exporting solar-eclipse GeoJSON](#exporting-solar-eclipse-geojson) +- [Exporting transit GeoJSON](#exporting-transit-geojson) - [API Reference](#api-reference) - [Usage examples](#usage-examples) - [Choosing a projection](#choosing-a-projection) @@ -70,6 +71,25 @@ func main() { A partial eclipse may have no central path, so `centralPath` may be `nil`. This program writes GeoJSON to standard output for redirection to a file. SVG and KML examples follow. +## Exporting transit GeoJSON + +The transit entry points take a geocentric `TransitInfo` and partition the globe exactly like the transit visibility map: + +```go +m := mercury.NextTransit(time.Date(2026, 10, 12, 0, 0, 0, 0, cst)) +data, err := geojson.MarshalMercuryTransit(m, geojson.PlanetTransitOptions{ + BoundaryPoints: 360, + IsochroneStep: 5 * time.Minute, + TimeMarkers: &geojson.TimeMarkerOptions{Step: time.Hour, Location: cst}, +}) +if err != nil { + log.Fatal(err) +} +fmt.Println(string(data)) +``` + +For Venus, use `venus.NextTransit` with `MarshalVenusTransit`; the options are the same. `date` only has to fall before the transit: `NextTransit` returns the next one after it, and `ClosestTransit` returns the nearest in either direction. An invalid `TransitInfo` (`Valid` false) makes the export fail. + ## API Reference SVG snippets use the import aliases `eclipsesvg "b612.me/astro/eclipse/svg"` and, for occultations, `moonsvg "b612.me/astro/moon/svg"`. Dates and time zones reuse the first example. @@ -82,6 +102,7 @@ SVG snippets use the import aliases `eclipsesvg "b612.me/astro/eclipse/svg"` and | `StarOccultationPathSVG` / `PlanetOccultationPathSVG` | Occultation global path maps | Return `(string, error)` | | `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers` | Solar eclipse GeoJSON | Without and with time markers | | `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers` / `MarshalLunarEclipseWithOptions` | Lunar eclipse GeoJSON | Same three | +| `MarshalMercuryTransit` / `MarshalVenusTransit` | Planetary transit GeoJSON | Options in `PlanetTransitOptions` | | `MarshalStarOccultation` / `MarshalPlanetOccultation` (and `...WithTimeMarkers`) | Occultation GeoJSON | Same pair | | `NewSolarEclipseShadowSolver` / `MarshalSolarEclipseShadowInstant` | Single-instant footprint and its GeoJSON | For time-axis scrubbing | | `TimeMarkerOptions` | Marker `Step`, `Location` and `TimeScale` | `Step` defaults to 30 minutes, at most 1440 markers | @@ -134,7 +155,7 @@ fmt.Println(err, json.Valid(data), len(data)) - `WithTimeMarkers` appends Point features with `role=time-marker`: `label` is localised through `Location`, while `time` stays UTC RFC 3339. - `Step` defaults to 30 minutes, positive values are at least one minute, and one export carries at most 1440 markers; use the unsuffixed `MarshalSolarEclipse` when no markers are wanted. -- Lunar eclipses and occultations have symmetric entry points (`MarshalLunarEclipse*`, `MarshalStarOccultation*`, `MarshalPlanetOccultation*`); GeoJSON carries no basemap, boundary, style or projection, leaving Web Mercator and tile choices to the application. +- Lunar eclipses and occultations have symmetric entry points (`MarshalLunarEclipse*`, `MarshalStarOccultation*`, `MarshalPlanetOccultation*`). ### Declaring the scale and UT1 @@ -165,7 +186,15 @@ true true 4217 - The single-instant API computes only "this instant's umbral/penumbral footprint" and the station geometry; it produces no visibility bands, magnitude contours, rise/set boundaries or limits, and returns an empty value (not an error) when the umbra misses the Earth. - Compare `interp_signature` before interpolating: only identical neighbouring instants are safe to interpolate vertex by vertex; a flipped `closed`, a changed segment or vertex count, or an empty/non-empty transition (near U1/U4) all require exact geometry instead. -- Degradable layers record their actual geometry source in `data-source`; the vocabulary is under [Map Projections](#map-projections). +- Degradable layers record their actual geometry source in `data-source`; layers without a degraded branch carry no such attribute. The legal values are listed below. + +Grouped by figure family, the `data-source` values are listed below; the `eclipse/svg` package comment gives the full meaning of each: + +- Solar-eclipse boundaries and visibility bands: `partial-band-union`, `sampled-footprint-sweep`, `partial-band-contours`, `besselian-critical-envelope`, `paired-limit-chords`, `sampled-open-sweep`, `central-path-limits` +- Solar-eclipse instants and magnitude lines: `rise-set-phase-lines`, `magnitude-contours`, `greatest-time-isochrones` +- Instantaneous outlines: `penumbral-outlines`, `central-shadow-outlines` +- Lunar eclipses: `p1-p4-visibility-regions`, `p1-p4-horizon-boundaries` +- Transits: `c1-c4-visibility-regions`, `c1-c4-horizon-boundaries`, `transit-contact-isochrones` ## Map Projections @@ -177,14 +206,14 @@ The automatic projection (the zero value `...Auto`) picks a polar map for the so | Projection | Constant (solar/lunar - occultation) | When to use | Suggested canvas | | --- | --- | --- | --- | -| Automatic | `EclipseMapProjectionAuto` - `MapProjectionAuto` | Default; picks a polar map per event | 1200x800 | -| Equirectangular | `...Equirectangular` | Long bands crossing the antimeridian | 1200x800, 1414x1000 | -| North/south polar equidistant | `...NorthPolar` / `...SouthPolar` | Event lies entirely at high latitude | 1200x800, 1000x1414 | -| Orthographic globe | `...Orthographic` | NASA-style hemisphere view centred on the event | 1000x1414 | +| Automatic | `EclipseMapProjectionAuto` - `MapProjectionAuto` | Default; picks a polar map per event | 1200×800 | +| Equirectangular | `...Equirectangular` | Long bands crossing the antimeridian | 1200×800, 1414×1000 | +| North/south polar equidistant | `...NorthPolar` / `...SouthPolar` | Event lies entirely at high latitude | 1200×800, 1000×1414 | +| Orthographic globe | `...Orthographic` | NASA-style hemisphere view centered on the event | 1000×1414 | -`EclipseMapProjectionOrthographic` / `MapProjectionOrthographic` produce the orthographic globe: the view point is the greatest-eclipse point (the event centre for an occultation), only the hemisphere facing it is drawn, and the projection boundary is the great circle of the visible hemisphere. +`EclipseMapProjectionOrthographic` / `MapProjectionOrthographic` produce the orthographic globe: the view point is the greatest-eclipse point (the event center for an occultation), only the hemisphere facing it is drawn, and the projection boundary is the great circle of the visible hemisphere. -The orthographic projection uses the NASA-style centered-globe layout; a `1000x1414` canvas is recommended. It does not change the underlying geographic results. +The orthographic projection uses the NASA-style centered-globe layout; a `1000×1414` canvas is recommended. It does not change the underlying geographic results. Four projections of one event in a single pass: @@ -210,9 +239,7 @@ for _, spec := range []struct { } ``` -Per-family options, layer switches and illustrated examples live in the [solar and lunar eclipse manual](eclipse.md#solar-and-lunar-eclipse-charts) and the [lunar occultation manual](occultation.md#lunar-occultation-charts); the GeoJSON side carries geographic results only and leaves the projection to the application. - -Degradable layers record their actual geometry source in `data-source`; the vocabulary is listed in the `eclipse/svg` package comment: `partial-band-union`, `sampled-footprint-sweep`, `partial-band-contours`, `rise-set-phase-lines`, `magnitude-contours`, `greatest-time-isochrones`, `besselian-critical-envelope`, `paired-limit-chords`, `sampled-open-sweep`, `central-path-limits`, `penumbral-outlines`, `central-shadow-outlines`, `p1-p4-visibility-regions`, `p1-p4-horizon-boundaries`. +Per-family options, layer switches and illustrated examples live in the [solar, lunar and transit eclipse manual](eclipse.md#solar-and-lunar-eclipse-charts) and the [lunar occultation manual](occultation.md#lunar-occultation-charts); the GeoJSON side carries geographic results only and leaves the projection to the application. Transit visibility regions, C1/C4 apparent horizons and optional topocentric isochrones can also be exported as GeoJSON; see the transit export example above. ## Time Scale Declaration @@ -233,7 +260,7 @@ Time markers are placed at whole clock ticks of the output scale (a UT1 tick is `geojson` accepts an already computed solar-eclipse, lunar-eclipse, or lunar-occultation result and returns `[]byte`. -Those bytes are one complete UTF-8 RFC 7946 `FeatureCollection`, not an image or compressed payload: they can be written to `.geojson`, passed to `encoding/json`, or served directly to a map client. +Those bytes are one complete UTF-8 RFC 7946 `FeatureCollection`: they can be written directly to `.geojson`, or handed to `encoding/json` and a front-end map component. ```go package main @@ -282,9 +309,10 @@ Each event type has plain and time-marker variants: - `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers` - `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers` -- `MarshalLunarEclipseWithOptions`: one `LunarEclipseOptions` value carrying the time markers, `SkipRoles` and the time-envelope sampling. `SkipRoles` can omit envelopes and penumbra-only bands; skipping both envelopes also skips their sampling. `EnvelopeSweepSamples` defaults to 48 and is clamped to `[2, 192]`; `EnvelopeLongitudePoints` defaults to `max(360, boundaryPoints)` and is clamped to `[12, 720]`. +- `MarshalLunarEclipseWithOptions`: one `LunarEclipseOptions` value carrying the time markers, `SkipRoles` and the time-envelope sampling parameters. `SkipRoles` can omit envelopes and penumbra-only bands; skipping both envelopes also skips their sampling. `EnvelopeSweepSamples` defaults to 48 and is clamped to `[2, 192]`; `EnvelopeLongitudePoints` defaults to `max(360, boundaryPoints)` and is clamped to `[12, 720]`. - `MarshalStarOccultation` / `MarshalStarOccultationWithTimeMarkers` - `MarshalPlanetOccultation` / `MarshalPlanetOccultationWithTimeMarkers` +- `MarshalMercuryTransit` / `MarshalVenusTransit`: take `mercury.TransitInfo` / `venus.TransitInfo` and partition the globe like the transit SVG map (whether the Sun's center is above the apparent horizon, standard refraction, at C1, greatest and C4). `BoundaryPoints` is the horizon sample count and also sets the region longitude columns to `max(360, BoundaryPoints)`, capped at 720. Roles are the three regions `transit-entire`, `transit-at-sunrise` and `transit-at-sunset`, the visible hemispheres and horizons `visible-at-c1` / `visible-at-c4` and `c1-horizon` / `c4-horizon`, `greatest` (the subsolar point) and the time markers `time-marker`. A positive `IsochroneStep` (at least 1 s) adds topocentric `c1-isochrone` through `c4-isochrone` (no C2/C3 for a grazing transit; the times are topocentric contacts and parallax can place them outside the geocentric C1–C4 interval), and a contact whose isochrone does not reach the visible hemisphere omits that role; skipping all three region roles skips the region computation. - `MarshalSolarEclipseWithOptions`: one `SolarEclipseOptions` value carrying both the time markers and `SkipRoles`, equivalent to the solar entry points above plus layer trimming. The usual entry in `SkipRoles` is `partial-footprint` (the instantaneous penumbral outlines); `partial-band` is the edge of the eclipse band (the magnitude-0 limit) and is normally kept. @@ -352,7 +380,7 @@ Prior single-machine measurements put a 96-point footprint at about 64 µs and a Draw `central-shadow-boundary` for the physical edge and fill the footprint for the region. At U1/U4, an empty footprint is omitted rather than degraded to a line. `source_boundary_closed=true` means the physical boundary closes by itself. -Sampled penumbral footprints carry the same properties. A footprint describes one instant; a static band describes the event-wide envelope. They are not interchangeable. +Sampled penumbral footprints carry the same properties. A footprint covers only one instant and a static band covers the whole event, so do not mix them when taking geometry. Compare adjacent `interp_signature` values, such as `umbra-closed-seg1-pt97`, before interpolating vertices. Signatures, segment counts and vertex counts must match. This condition is not a strict interpolation error bound. @@ -380,7 +408,7 @@ Occultations use process-wide ΔT and report its actual value. `eclipse.SolarEclipseCandidates(start, end, options)` returns greatest times, eclipse types, central types, magnitudes, gamma and optional Saros information. It does not calculate geographic geometry. -For central eclipses at a fixed site, `SearchLocalCentralSolarEclipse` accepts `Kind`, `MaxYears`, `Backward`, `Geometric` and `Model` options and returns `(info, status)`. `status.Exhausted` reports that the search range was exhausted. +For central eclipses at a fixed site, `SearchLocalCentralSolarEclipse` accepts `Kind`, `MaxYears`, `Backward`, `Geometric` and `Model` options and returns `(info, SolarEclipseLocalSearchStatus)`, whose `Exhausted` field reports that the search range was exhausted. `MaxYears<=0` uses the default budget of 6000 candidate steps, about 992 years. Calculate paths, footprints or SVG only after selecting an event when full maps are not needed for every candidate. @@ -444,6 +472,7 @@ Features are grouped into Folders by `event` and `role`. Collections containing | Total and partial occultation bands | `total-band`, `partial-band`, `total-band-outline`, `occultation-band` | Yellow with translucent band fill | | Rise/set visibility edges, lunar time envelopes and solar partial-band edge | `visibility-boundary`, `p1-horizon`, `p4-horizon`, `visible-at-p1`, `visible-at-p4`, `visible-during-eclipse`, `visible-throughout-eclipse`, solar `partial-band` | Orange | | Penumbra-only moonrise/moonset bands | `penumbra-moonrise`, `penumbra-moonset` | Blue-violet for moonrise, violet for moonset, with a translucent fill | +| Transit visibility regions and contact lines | `transit-entire`, `transit-at-sunrise`, `transit-at-sunset`, `c1-horizon`, `c4-horizon`, `visible-at-c1`, `visible-at-c4`, `c1-isochrone` … `c4-isochrone` | Each of the three regions has its own fill; C1/C4 horizons and visible hemispheres are orange; the four contact isochrones each get a color | | Other limits, outlines, footprints and time markers | Other roles | Grey; polygons are outline-only by default | Zero-valued `Style` fields retain palette defaults: diff --git a/doc/manual/en/occultation.md b/doc/manual/en/occultation.md index 810996e..4cc22d7 100644 --- a/doc/manual/en/occultation.md +++ b/doc/manual/en/occultation.md @@ -31,10 +31,12 @@ Targets use two distinct contact models: - [Path algorithms and contours](#path-algorithms-and-contours) - [Greatest-time contours](#greatest-time-contours) - [Planetary occultations](#planetary-occultations) +- [Lunar limb correction](#lunar-limb-correction) - [Lunar Occultation Charts](#lunar-occultation-charts) - [Global paths](#global-paths) - [Detailed layout](#detailed-layout) - [Fixed-site charts](#fixed-site-charts) + - [Precise limb-profile chart](#precise-limb-profile-chart) - [Time scale and UT1](#time-scale-and-ut1) ## Searching for a stellar occultation at a fixed site @@ -92,6 +94,8 @@ The snippets use the target and search window from the first example. SVG calls | `moonsvg.FindLocalStarOccultationSVGs` / `FindLocalPlanetOccultationSVGs` | Search and render fixed-site charts | Same | | `moonsvg.StarOccultationPathSVG` / `PlanetOccultationPathSVG` | Render an existing global path | Return `(string, error)` | | `moonsvg.StarOccultationDetailedSVG` / `PlanetOccultationDetailedSVG` | One-page detailed layout | Fixed orthographic globe | +| `moon.StarOccultationLimbProfile` / `StarOccultationLimbProfileInUT1` | Precise solve along the star track with a limb profile, returning every hidden interval and the in-band samples | Needs a profile; result is a `StarOccultationLimbProfileResult` | +| `moonsvg.StarOccultationLimbProfileSVG` | Precise limb-profile chart | Options are `StarOccultationLimbProfileSVGOptions` | | `moonsvg.StarOccultationSVGOptions` / `moonsvg.OccultationDetailedSVGOptions` | Chart options (canvas, projection, scale, marker step) | Projections via `MapProjection*`, scales via `astro.TimeScale*` | | `moon.OccultationMercury` ... `moon.OccultationNeptune` | Planetary target constants | Passed to the planetary entry points | | `moon.OccultationSearchOptions` / `moon.OccultationPathOptions` | Search and path options | Path options include `Step`, `TargetSpacingKM` and `GreatestTimeStep` | @@ -183,7 +187,7 @@ fmt.Println(err, len(svg), detailErr, len(detailed)) - Path maps return `(string, error)` and accept the same four projections as the solar maps; the detailed layout is fixed to the orthographic globe, accepts no other projection, and derives its arrangement from the canvas aspect. - Use `StarOccultationPathSVG` when the path already exists, and `FindStarOccultationSVGs` for search-plus-render in one call (see the two chains above). -- Canvas floors: `640x480` for a standalone path map. The detailed layout accepts a width of `480` but in practice needs about `670x595` to hold the map, data blocks and footer; smaller canvases return an error. +- Canvas floors: `640×480` for a standalone path map. The detailed layout accepts a width of `480` but in practice needs about `670×595` to hold the map, data blocks and footer; smaller canvases return an error. ### Grazing events, isochrones and band widths @@ -309,10 +313,10 @@ Output: ```text 进贤增九 total -2025-06-05 19:14:01.076 CST 2025-06-05 20:02:06.311 CST 2025-06-05 20:50:10.721 CST -altitude=75.561 visible=true -2025-06-05 17:45:28.475 CST 2025-06-05 20:02:06.300 CST 2025-06-05 22:18:49.945 CST -greatest=121.566009 6.807079 width=3582.4km center=108 +2025-06-05 19:14:03.127 CST 2025-06-05 20:02:08.397 CST 2025-06-05 20:50:12.789 CST +altitude=75.556 visible=true +2025-06-05 17:45:29.822 CST 2025-06-05 20:02:07.622 CST 2025-06-05 22:18:51.246 CST +greatest=121.561060 6.808173 width=3582.4km center=108 ``` ### Search and path sampling options @@ -333,11 +337,7 @@ The result's `GreatestLimitSeparationKM` is the ground separation between northe ### Path algorithms and contours -`OccultationPathOptions.Algorithm` selects the stellar/planetary global-path ephemeris branch. - -Its zero value or `moon.OccultationPathAlgorithmOptimized` uses checked Cartesian interpolation at 30-minute nodes while retaining the station equations, continuous envelopes, and rise/set curves. - -`moon.OccultationPathAlgorithmExact` uses interpolated candidates with full-term ephemerides for final solving. Failed table checks fall back to full-term solving; evaluations outside the interpolation window use exact ephemerides. +`OccultationPathOptions.Algorithm` selects the stellar/planetary global-path ephemeris branch: its zero value or `moon.OccultationPathAlgorithmOptimized` approximates the ephemeris with vector interpolation at 30-minute nodes, and recomputes with the full ephemeris when the interpolation self-check fails or the instant falls outside the interpolation window. `moon.OccultationPathAlgorithmExact` interpolates only during candidate screening and always solves with the full ephemeris. Both branches keep the same station equations, band boundaries and rise/set curves. Checks are sampled safeguards, not a rigorous error bound at every instant. The branches share the geometric definition, but sample points and GeoJSON bytes need not be identical. @@ -406,17 +406,54 @@ Output: ```text Saturn total true -2025-02-01 11:29:09.710 CST -2025-02-01 11:29:40.069 CST -2025-02-01 12:00:48.747 CST -2025-02-01 12:32:46.415 CST -2025-02-01 12:33:18.312 CST +2025-02-01 11:29:10.984 CST +2025-02-01 11:29:41.333 CST +2025-02-01 12:00:50.058 CST +2025-02-01 12:32:47.753 CST +2025-02-01 12:33:19.650 CST ``` Global `FindPlanetOccultationPaths` results contain both the region where any part of the planetary disk overlaps the Moon and the region where the whole planet is hidden. `HasTotalBand` reports whether a total band exists, and `GreatestTotalWidthKM` is its width at greatest occultation; center lines, limits, and enabled instantaneous footprints all carry sample times. +## Lunar limb correction + +The Moon is a circular disk by default. Stellar and planetary occultation searches at a fixed site can use the ISAS/JAXA limb profile from the `moon/limb` package (a processed product of SELENE/Kaguya LALT data; see the [eclipse and transit manual](eclipse.md#lunar-limb-correction)): disappearance, reappearance and the planet's inner and outer contacts are refined with the actual limb radius toward the target. Greatest occultation, type and candidate screening still use the circular disk. + +```go +import "b612.me/astro/moon/limb" + +events, err := moon.FindStarOccultations(start, end, star, lon, lat, 0, + moon.OccultationSearchOptions{LunarLimb: limb.Default()}) + +// Or set it once on a context +base, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +ctx := base.WithLunarLimb(limb.Default()) +events, err = ctx.Moon().FindStarOccultations(start, end, star, lon, lat, 0, moon.OccultationSearchOptions{}) +``` + +Corrections are usually a few seconds; for the 2024-08-21 occultation of Saturn the contacts move by `-1.8 s` to `-0.6 s`. + +Accuracy limits: + +- The built-in profile is interpolated on a 1° libration grid; the limb margin at a single contact is good to about `±0.05″` (about 100 m on the Moon), set by the 16 pixel/degree terrain and the interpolation together. +- In a graze the star skims the limb with a margin of a few hundredths of an arcsecond, so the number of hidden intervals and the brief disappearances and reappearances can differ from observations; only the main interval and its magnitude are reliable. Example: the Regulus graze of 2017-09-18 (Greece, 5 intervals observed by IOTA) gives 1 interval here, whose disappearance and reappearance differ from the last observed interval by `+0.6 s` / `-0.4 s`. +- For finer graze profiles, build a table from higher-resolution terrain and load it with `limb.Parse`, or implement the `LunarLimb` interface. + +Global occultation paths (`FindStarOccultationPaths`, `FindPlanetOccultationPaths`, via `OccultationPathOptions.LunarLimb` or the module setting) correct only the outer-contact northern and southern limits: + +- The limits move onto the limb graze lines: the star track is treated as straight near the tangent point and the effective limb is the highest point within ±8° of position angle. +- Points with the Moon near the horizon stay circular. +- Planetary total-occultation limits, band fills, isochrones and rise/set curves stay circular. +- Multiple disappearances and reappearances inside the graze zone are not computed here. +- For HR 4799 on 2025-06-05 the graze lines move by up to about `4 km`. + +Charts need no extra switch: `LunarLimbApplied` on the result records whether the limb correction actually took effect, and `moon/svg` declares the convention after the time-scale note. A local result sets it only when every contact converged on the profile; if any contact fails, the whole result stays circular. A path sets it when at least one limit point was corrected. Converged contacts no longer sit on the circular limb, so local charts skip the circular contact check for them and render events from the built-in or a custom profile directly. + ## Lunar Occultation Charts `moon/svg` offers two groups of entry points: "search and render" and "render an existing result". @@ -428,6 +465,7 @@ Global `FindPlanetOccultationPaths` results contain both the region where any pa | `StarOccultationDetailedSVG` / `PlanetOccultationDetailedSVG` | One-page detailed layout | | `FindLocalStarOccultationSVGs` / `FindLocalPlanetOccultationSVGs` | Fixed-site topocentric lunar track, lunar path and contact-phase charts | | `LocalStarOccultationSVG` / `LocalPlanetOccultationSVG` | Render an existing fixed-site event | +| `StarOccultationLimbProfileSVG` | Precise limb-profile chart: limb relief along the track, every hidden interval and the immersion/reappearance numbering | `eclipse/svg` entry points return `(string, bool)` while `moon/svg` entry points return `(string, error)` or `([]string, error)`: on the occultation side the second value is a real error (UT1 with a non-UTC location, canvas below the floor, invalid path), not a "cannot draw this chart" flag. @@ -445,8 +483,8 @@ Greatest instants are measured from the Sun-Moon center separation for point sta | --- | --- | | `MapProjectionAuto` (zero value) | Chosen per event; high-latitude events may land on a polar map | | `MapProjectionEquirectangular` | Equirectangular, clearest for long paths crossing the antimeridian | -| `MapProjectionNorthPolar` / `MapProjectionSouthPolar` | Azimuthal equidistant with the pole at the centre | -| `MapProjectionOrthographic` | Orthographic globe viewed from the event centre, drawing only the hemisphere facing it | +| `MapProjectionNorthPolar` / `MapProjectionSouthPolar` | Azimuthal equidistant with the pole at the center | +| `MapProjectionOrthographic` | Orthographic globe viewed from the event center, drawing only the hemisphere facing it | The orthographic globe (`2025-06-05` occultation of HR 4799): @@ -493,15 +531,15 @@ svgs, err := moonsvg.FindStarOccultationSVGs(start, end, target, fmt.Println(err, len(svgs)) ``` -`TimeLabelStep` defaults to 30 minutes and a negative value disables the time markers along the center line. A standalone global path map needs at least `640x480`; smaller canvases return `ErrInvalidStarOccultationSVGOptions` (`ErrInvalidPlanetOccultationSVGOptions` for the planetary entry points). +`TimeLabelStep` defaults to 30 minutes and a negative value disables the time markers along the center line. A standalone global path map needs at least `640×480`; smaller canvases return `ErrInvalidStarOccultationSVGOptions` (`ErrInvalidPlanetOccultationSVGOptions` for the planetary entry points). -The legend, footer and graticule all reserve space by canvas height, so shorter canvases press against the footer more easily; the south-polar example in this manual works fine at `1200x800`. +The legend, footer and graticule all reserve space by canvas height, so shorter canvases press against the footer more easily; the south-polar example in this manual works fine at `1200×800`. ### Detailed layout -The detailed layout combines a whole occultation on one `1000x1414` page: a centred summary, geocentric/topocentric data blocks for Moon and target, an **orthographic globe** path map (northern and southern limits, visible/geometric center lines, the greatest point, immersion/greatest/emersion phase points and 30-minute time markers), and a footer note. +The detailed layout combines a whole occultation on one `1000×1414` page: a centered summary, geocentric/topocentric data blocks for Moon and target, an **orthographic globe** path map (northern and southern limits, visible/geometric center lines, the greatest point, immersion/greatest/emersion phase points and 30-minute time markers), and a footer note. -The globe is viewed from the event centre and this layout is fixed to the orthographic projection, accepting no other; use the `StarOccultationPathSVG` / `FindStarOccultationSVGs` above when only a standalone path map is needed. +The globe is viewed from the event center and this layout is fixed to the orthographic projection, accepting no other; use the `StarOccultationPathSVG` / `FindStarOccultationSVGs` above when only a standalone path map is needed. ```go detailed, err := moonsvg.StarOccultationDetailedSVG(paths[0], target, @@ -519,15 +557,15 @@ A landscape canvas puts the data blocks in two columns by three rows to the righ The globe path map uses the Natural Earth `1:50m` coastline without administrative boundaries. -The detailed layout derives its arrangement from the canvas and needs about `670x595` in practice (the `480` width floor is only an argument check), returning `ErrInvalidOccultationDetailedSVGOptions` when the map and data blocks do not fit (`800x600`, `1000x1414` and `1414x1000` all render; `660x600`, `800x590`, `640x420` and `900x400` are rejected). +The detailed layout derives its arrangement from the canvas and needs about `670×595` in practice (the `480` width floor is only an argument check), returning `ErrInvalidOccultationDetailedSVGOptions` when the map and data blocks do not fit (`800×600`, `1000×1414` and `1414×1000` all render; `660×600`, `800×590`, `640×420` and `900×400` are rejected). -The "band width" printed on the map is the ground separation of the northern and southern limits at greatest occultation, `GreatestLimitSeparationKM` (about `3666.6 km` for the HR 4799 sample), which is a different convention from the across-center-line width `Greatest.WidthKM` (about `3582.4 km`); the two are not interchangeable. +The "band width" printed on the map is the ground separation of the northern and southern limits at greatest occultation, `GreatestLimitSeparationKM`, not the across-center-line width `Greatest.WidthKM`; the two are not interchangeable. Greatest-time isochrones must be requested explicitly at the path layer through `moon.OccultationPathOptions.GreatestTimeStep`: `moon/svg` offers no switch of its own and only draws the `GreatestTimeContours` already present in the path result, so the request has to be made while computing the path and the line positions follow the core convention (`GreatestTimeStep` aligns to whole UTC marks; to align to the display time zone, convert the instants to dynamical-time Julian days first and pass them as `GreatestTimeValues`). A lunar occultation is visible worldwide for only a few hours (4 h 33 min for the HR 4799 sample), so the usual interval is denser than for solar eclipses, on the order of 15-30 minutes. -A compact band using `DisableFootprints` merges once on the first render and then caches for the same path. +A compact band using `DisableFootprints` is computed only once on the first render, and the same path reuses the cache afterwards. ### Fixed-site charts @@ -543,10 +581,48 @@ fmt.Println(err, len(localSVGs)) The local chart is drawn from the topocentric geometry of the given observer; the figure below reuses the `2025-06-05` occultation of Xianxianzengjiu (HR 4799). -The observer is at `121.56601 E, 6.80706 N`, close to the global geometric greatest point; the immersion, greatest and emersion instants in the figure are the topocentric contacts actually seen from that site, together with lunar orientation, the lunar path, Moon altitude, azimuth and horizon visibility. +The chart's observer is at `121.56601°E, 6.80706°N`, close to the global geometric greatest point; the immersion, greatest and emersion instants in the figure are the topocentric contacts actually seen from that site, together with lunar orientation, the lunar path, Moon altitude, azimuth and horizon visibility. ![2025 occultation of Xianxianzengjiu fixed-site chart](../../img/lunar-occultation-hr4799-2025-06-05-local-en.svg) +### Precise limb-profile chart + +The contact times above are solved on a circular limb. Feeding the limb relief in point by point gives the limb geometry at every instant along the star track and **every** hidden interval: a deep occultation has one disappearance and one reappearance segment, a grazing event usually just one, and a limb valley can split a single occultation into several separate intervals. + +```go +profile, err := moon.StarOccultationLimbProfile(events[0], target, limb.Default(), + moon.StarOccultationLimbProfileOptions{Step: 200 * time.Millisecond}) +fmt.Println(len(profile.Hidden), profile.Hidden[0].Start.UTC(), profile.Hidden[len(profile.Hidden)-1].End.UTC()) +samples := 0 +for _, segment := range profile.Segments { + samples += len(segment) +} +fmt.Println(profile.Step, len(profile.Segments), samples) + +profileSVG, err := moonsvg.StarOccultationLimbProfileSVG(profile, + moonsvg.StarOccultationLimbProfileSVGOptions{Width: 1000, Height: 620, Location: cst, Language: "en"}) +fmt.Println(err, len(profileSVG)) +``` + +```text +1 2025-06-05 11:13:59.65061903 +0000 UTC 2025-06-05 12:50:11.632025241 +0000 UTC +200ms 2 191 + 30702 +``` + +![Precise limb profile of the 2025 occultation of HR 4799](../../img/lunar-occultation-hr4799-2025-06-05-limb-profile-en.svg) + +The horizontal axis is time, each tick also labelled with the limb position angle at that instant, and the vertical axis is height above the reference circular limb in kilometres. The brown solid curve is the limb radius toward the star, the red dashed curve is the star's angular distance from the Moon's center, and their crossings are the hidden intervals, shaded and numbered; one panel is drawn per entry in `Segments`, with a sky-plane inset and the immersion/reappearance list on the right. The circular-limb result for the same event (`11:14:03.127` / `12:50:12.789 UTC`) differs by a few seconds — exactly the limb-relief correction. + +Conventions: + +- `info` contributes only the instant of greatest occultation and the observer; the remaining contacts are re-solved on the profile and returned in `profile.Occultation`. It must be a UTC-based result — never pass a `...InUT1` copy. +- A `nil` limb falls back to the module or context profile (`WithLunarLimb`); when neither is set the call returns `ErrInvalidOccultationInput`. +- `Step` defaults to 200 ms and is raised to a 10 ms minimum; each segment keeps at least 64 samples and the total is capped at 20000, with the step adjusted automatically. **Hidden intervals shorter than the step can be missed**, so shrink the step for grazing detail. +- `Segments` holds the samples inside the limb relief band (two for a deep occultation, usually one for a graze), `Hidden` holds every hidden interval, `ReliefMinKM` / `ReliefMaxKM` bound the limb radii used for gating, and `ReferenceRadiusKM` is the lunar radius behind `MeanRadiusArcsec`. +- The chart defaults to `920×620` and needs at least `640×480`; anything smaller returns `ErrInvalidStarOccultationLimbProfileSVGOptions`, and a track that never enters the relief band returns `ErrEmptyStarOccultationLimbProfile`. +- For UT1 labels take a copy with `moon.StarOccultationLimbProfileInUT1(profile)`, or pass `TimeScale: astro.TimeScaleUT1` to the chart options (then `Location` must be UTC). + ### Time scale and UT1 Occultation charts are drawn in the UTC scale by default and state it in the figure. diff --git a/doc/manual/en/orbit.md b/doc/manual/en/orbit.md index 222d915..39689ca 100644 --- a/doc/manual/en/orbit.md +++ b/doc/manual/en/orbit.md @@ -4,7 +4,22 @@ `orbit` propagates heliocentric two-body positions from orbital elements. It supports asteroids, comets, dwarf planets, and custom hypothetical orbits. -The seven major planets are still computed by their own packages using built-in VSOP87 analytical terms. +The seven major planets use the selected ephemeris through their own packages: default builds default to the semianalytic model, with Legacy or custom engines available explicitly. + +Use `StateWithOptions` to propagate to a target epoch with optional planetary perturbations: + +| `PropagationOptions` field | Meaning | +| --- | --- | +| `Perturbers` | Perturbing bodies; a non-empty value also requires an ephemeris source | +| `Engine` / `Context` | The perturbation ephemeris source. Both fields have the same type (`StateProvider`, i.e. `astro.Engine`) and `Engine` wins; pass an `*spk.File` or a custom engine | +| `Step` | Maximum step in days | +| `Tolerance` | Relative step-doubling error threshold | + +- The zero-value options struct follows the existing two-body path exactly, matching `State`. +- The integrator adds solar gravity plus direct and indirect perturbation terms. `Perturbers` accepts Mercury through Neptune, Earth, the Moon and the Earth-Moon barycenter (3); an unknown body, or the barycenter combined with Earth or the Moon, returns `ErrUnsupportedPerturber`. +- Integration starts at `EpochJD`; a perihelion-form set starts at `TpJD` only when `EpochJD` is 0. +- `NewEphemerisEngine` adapts the propagated result for `astro.NewStack`: elements and options are fixed at construction, and the engine's cached checkpoints never change results, so any query order matches a direct `StateWithOptions` call bit for bit. +- Accuracy depends on the supplied elements and propagation span; use SPK for long-term high-precision work. `orbit.Elements` supports two common forms: @@ -39,7 +54,7 @@ The positional and topocentric quantities here also work together with the inter - [Zero Values and Out-of-Range Input](#zero-values-and-out-of-range-input) - [Accuracy and Applicability](#accuracy-and-applicability) - [Common Pitfalls](#common-pitfalls) -- [Related Manuals](#related-manuals) +- [Related manuals](#related-manuals) ## Calculating the position of Ceres from orbital elements @@ -146,8 +161,8 @@ func main() { Output: ```text -ceres ra=7.739532 dec=-10.625981 distance=2.164391 -halley ra=312.112360 dec=-11.826451 distance=1.533936 +ceres ra=7.743516 dec=-10.625287 distance=2.164391 +halley ra=312.106763 dec=-11.828059 distance=1.533936 ``` Orbital elements have epochs. The farther the target date is from the epoch, the more static-element error can grow. @@ -215,7 +230,7 @@ fmt.Printf("r=%.6f delta=%.6f elong=%.6f phase=%.6f k=%.6f mag=%.3f q=%.6f\n", Near 180° elongation the phase angle approaches 0° and `IlluminatedFraction` approaches 1; all three quantities come from the same geocentric geometry. -`ParallacticAngle` does not solve for declination separately: it reuses the same topocentric solve as `HourAngle`, so that hour angle and declination never come from two Julian-day paths about 1 ULP apart and introduce sub-nanodegree drift. +`ParallacticAngle` does not solve for declination separately: it reuses the same topocentric solve as `HourAngle`, so the two are exactly self-consistent. All geometry helpers depend only on the absolute instant of `date`; `ParallacticAngle` is the only one that also takes observer parameters. @@ -338,7 +353,7 @@ fmt.Println(app.RA, app.Dec, app.Distance) 7.125008 -10.028726 2.262489 ``` -The four layers mean different things: `Heliocentric*` is relative to the Sun (the first number is the heliocentric distance), `Geocentric*J2000` is the J2000 geocentric position, `Astrometric*J2000` removes light-time and suits catalog comparison, and `Apparent*` is the apparent position of the day used for observing and charts. +The four layers mean different things: `Heliocentric*` is relative to the Sun (the first number is the heliocentric distance), `Geocentric*J2000` is the J2000 geocentric position, `Astrometric*J2000` adds light-time and suits catalog comparison, and `Apparent*` is the apparent position of the day used for observing and charts. For a topocentric apparent position use `ApparentTopocentricEquatorial(when, ceres, lon, lat, height)`. @@ -431,7 +446,7 @@ NaN NaN - The zero value of `Elements` is not a valid orbit. The classical elliptical form requires a finite positive `A`, `E` within `[0,1)`, and finite `EpochJD` and `M0`. Parabolic and hyperbolic orbits with `E >= 1` can only use the perihelion form, which requires a finite positive `Q`, a finite `TpJD`, `E >= 0`, and finite `I`/`Omega`/`W`. -- When `Q > 0` and `TpJD` is finite the perihelion form wins: `A`, `M0`, and `EpochJD` are ignored, `ADot`/`EDot`/`IDot`/`OmegaDot`/`WDot` have no effect, and only a non-zero `MDot` is used as the mean angular rate. +- When `Q > 0` and `TpJD` is finite the perihelion form wins: `A`, `M0`, and `EpochJD` are ignored, `ADot`/`EDot`/`IDot`/`OmegaDot`/`WDot` have no effect, and only a non-zero `MDot` is used as the mean angular rate. Perturbed propagation is the exception: when a perihelion-form set also gives `EpochJD`, that epoch is the osculating epoch where integration starts. - With invalid elements `MeanMotion` and `MeanAnomaly` return `NaN` and the positional interfaces return three `NaN`s. `AsteroidMagnitudeHG` returns `NaN` when its inputs are non-finite or when heliocentric distance, geocentric distance, or phase angle is non-positive, and `+Inf` when the H-G phase blend is zero. @@ -452,7 +467,7 @@ NaN NaN - Probing with a zero-value `Elements`: mean motion and the positional interfaces quietly return `NaN` instead of reporting an error. - Expecting `ADot`…`WDot` to take effect in the perihelion form: those rates only propagate in the classical elliptical form. -## Related Manuals +## Related manuals - Stars and catalogues: [star](star.md#stars) - Frame conversion and topocentric quantities: [coordinate tools](coord.md#coordinate-tools) diff --git a/doc/manual/en/phenomena.md b/doc/manual/en/phenomena.md new file mode 100644 index 0000000..171d2b4 --- /dev/null +++ b/doc/manual/en/phenomena.md @@ -0,0 +1,243 @@ +# Generic sky events and heliacal visibility + +[中文](../phenomena.md) | [Back to README](../../../README.en.md) + +The `phenomena` package searches events between any two bodies by geocentric apparent ecliptic longitude and latitude, computes morning and evening first and last visibility for planets and stars, and additionally provides rise and set at any altitude plus perihelion, aphelion and node passages. Package-level entry points use the process's current ephemeris model, and `ctx.Phenomena()` can fix an ephemeris instance; only a star's rise and set at any altitude is computed from the apparent coordinates passed in and does not read the ephemeris. + +## Contents + +- [Quick example](#quick-example) +- [Pairwise events](#pairwise-events) +- [Star coordinates](#star-coordinates) +- [Heliacal visibility](#heliacal-visibility) +- [Rise and set at any altitude](#rise-and-set-at-any-altitude) +- [Apsides and nodes](#apsides-and-nodes) +- [API reference](#api-reference) +- [Parameter and return conventions](#parameter-and-return-conventions) + +## Quick example + +```go +start := time.Date(2020, 10, 1, 0, 0, 0, 0, time.UTC) +end := time.Date(2021, 3, 1, 0, 0, 0, 0, time.UTC) + +events := phenomena.ClosestApproaches(phenomena.Jupiter, phenomena.Saturn, 1, start, end) +fmt.Println(events[0].Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.4f", events[0].Value)) +``` + +```text +2020-12-21 18:21 0.1018 +``` + +`ClosestApproaches` returns the **local minima** of the angular separation of the two bodies within the window, where `Value` is the separation at that instant in degrees. It returns an empty slice when the window contains no local minimum below `maxSeparation`. + +To fix the ephemeris instance, use the module method instead: + +```go +ctx, err := astro.NewContext(astro.ModelLegacy) +if err != nil { + panic(err) +} +events = ctx.Phenomena().ClosestApproaches(phenomena.Jupiter, phenomena.Saturn, 1, start, end) +``` + +## Pairwise events + +| Entry | Event | `Event.Value` | +| --- | --- | --- | +| `LongitudeCrossings(body, lon, start, end)` | apparent longitude passes through `lon` | apparent longitude at the event instant (degrees) | +| `Stations(body, start, end)` | stations of apparent longitude (planets) | apparent longitude at the station (degrees) | +| `AspectCrossings(a, b, angle, start, end)` | `lon(a) - lon(b) = angle`; `0` is conjunction in longitude, `180` opposition | the actual longitude difference (degrees) | +| `ClosestApproaches(a, b, max, start, end)` | local minima of the angular separation of two bodies | angular separation (degrees) | +| `StarApproaches(body, star, max, start, end)` | local minima of the angular separation from a star | angular separation (degrees) | + +Each entry accepts only the parameters listed in the table above, and results are returned in ascending time order. Stations defined by right ascension in the per-planet packages use a different convention from the longitude stations here, so the times can differ by several hours. + +```go +// first two Mercury stations of 2026 +stations := phenomena.Stations(phenomena.Mercury, + time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range stations { + fmt.Println(e.Time.UTC().Format("01-02 15:04"), fmt.Sprintf("%.3f", e.Value)) +} +``` + +```text +02-26 06:48 352.565 +03-20 19:32 338.491 +``` + +```go +// apparent solar longitude passing through 0° (vernal equinox) +crossings := phenomena.LongitudeCrossings(phenomena.Sun, 0, + time.Date(2026, 3, 18, 0, 0, 0, 0, time.UTC), time.Date(2026, 3, 24, 0, 0, 0, 0, time.UTC)) +fmt.Println(crossings[0].Time.UTC().Format("2006-01-02 15:04")) +``` + +```text +2026-03-20 14:45 +``` + +## Star coordinates + +Both the angular separation of a planet and a star and star heliacal visibility require a `StarCoordinate`; the embedded 9100-star catalog can be queried directly: + +| Entry | Input | +| --- | --- | +| `StarCoordinateByName(name)` | catalog Chinese name, for example `"天狼星"` | +| `StarCoordinateByHR(hr)` | HR number | +| `StarCoordinateFromStarData(star)` | a catalog record such as one obtained from `star.StarDataByHR` | + +```go +star, err := phenomena.StarCoordinateByName("天狼星") +fmt.Println(err, star.ID, fmt.Sprintf("%.6f %.6f %.2f %.2f", star.RA, star.Dec, + star.ProperMotionRACosDecMasPerYear, star.ProperMotionDecMasPerYear)) +``` + +```text + 天狼 101.287083 -16.716111 -553.00 -1205.00 +``` + +`RA` and `Dec` are in degrees, and `Epoch` and `Frame` are required; the proper-motion fields (`ProperMotionRACosDecMasPerYear` and friends) follow the catalog convention `dRA·cos(Dec)`, in milliarcseconds per year. `ParallaxMas` (or the equivalent `DistanceLightYear`) gives the distance, and `RadialVelocityKmPerSecond` gives the radial velocity; when the distance is missing the propagation is two-dimensional, and the radial velocity is included only when a distance is present. + +## Heliacal visibility + +`PlanetHeliacalEvents(body, start, end, lon, lat, options)` and `StarHeliacalEvents(star, magnitude, start, end, lon, lat, options)` scan the morning and evening intervals day by day while the solar altitude runs from -18° to 0°, derive the naked-eye limiting magnitude from a sky-brightness model, and compare it with the target apparent magnitude (Mallama & Hilton 2018 for planets). They return four kinds of event: + +| Constant | Meaning | +| --- | --- | +| `HeliacalMorningFirst` | morning first visibility (heliacal rising) | +| `HeliacalMorningLast` | morning last visibility | +| `HeliacalEveningFirst` | evening first visibility | +| `HeliacalEveningLast` | evening last visibility (heliacal setting) | + +In `HeliacalEvent`, `Time` is the best visible instant of that twilight, `TargetAltitude` and `SunAltitude` are the geometric altitudes of the target and the Sun at that instant (degrees), and `Margin` is the limiting magnitude minus the target magnitude. + +| `HeliacalOptions` | Default | Description | +| --- | --- | --- | +| `ExtinctionCoefficient` | `0.25` | atmospheric extinction, magnitudes per airmass | +| `MinTargetAltitude` | `0.5` | minimum geometric altitude of the target (degrees) | +| `Moonlight` | `false` | include scattered moonlight (Krisciunas-Schaefer) | + +```go +events := phenomena.PlanetHeliacalEvents(phenomena.Venus, + time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC), + 31.23, 30.04, phenomena.HeliacalOptions{}) +for _, e := range events { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02"), fmt.Sprintf("margin=%.2f", e.Margin)) +} +``` + +```text +3 2025-03-18 margin=0.10 +0 2025-03-25 margin=0.58 +1 2025-12-01 margin=0.10 +``` + +`Kind` prints as the constant's ordinal, so compare against the constants themselves when testing. `LimitingMagnitude(targetAltitude, sunAltitude, separation, extinction)` gives the limiting magnitude at a single instant on a moonless night, from the same source as the event test: + +```go +fmt.Println(fmt.Sprintf("%.3f", phenomena.LimitingMagnitude(20, -12, 60, 0.25))) +``` + +```text +3.408 +``` + +Results are sensitive to the extinction coefficient, and a spread of one or two days in the visible date is normal. + +## Rise and set at any altitude + +`AltitudeCrossing(body, date, lon, lat, height, altitude, options)` finds the rise and set times at which the Sun, Moon or a planet first crosses the apparent altitude `altitude` within the **local civil day** containing `date`. `StarAltitudeCrossing(date, ra, dec, lon, lat, altitude, options)` performs the same computation for a star's apparent coordinates; it does not read the ephemeris and has no module method. + +| `AltitudeOptions` | Default | Description | +| --- | --- | --- | +| `Limb` | `LimbUpper` | upper limb for the Sun and Moon, or `LimbCenter` and `LimbLower`; ignored for planets and stars | +| `PressureHPa` / `TemperatureC` | standard atmosphere (the `0` value) | specify measured weather | +| `NoRefraction` | `false` | when `true`, compare geometric altitudes | + +| `AltitudeCrossingResult` | Description | +| --- | --- | +| `Rise`, `Set` | rise and set times, zero when there is no event | +| `HasRise`, `HasSet` | whether a rise or set occurs that day | +| `State` | `AltitudeCrosses`, `AltitudeAlwaysAbove`, `AltitudeAlwaysBelow` or `AltitudeTangent` (the extremum differs from the target altitude by less than `1″`) | +| `MinResidual`, `MaxResidual` | minimum and maximum of the altitude minus the target altitude that day (degrees) | + +```go +cst := time.FixedZone("CST", 8*3600) +r, err := phenomena.AltitudeCrossing(phenomena.Sun, time.Date(2026, 3, 21, 12, 0, 0, 0, cst), + 116.4, 39.9, 0, -18, phenomena.AltitudeOptions{}) +fmt.Println(err, r.State, r.Rise.In(cst).Format("15:04:05"), r.Set.In(cst).Format("15:04:05"), + fmt.Sprintf("%.3f %.3f", r.MinResidual, r.MaxResidual)) +``` + +```text + 0 04:44:54 19:59:16 -31.806 68.602 +``` + +```go +// 89°N polar day: the Sun stays above -18° all day +polar, err := phenomena.AltitudeCrossing(phenomena.Sun, time.Date(2026, 6, 21, 12, 0, 0, 0, time.UTC), + 0, 89, 0, -18, phenomena.AltitudeOptions{}) +fmt.Println(err, polar.State, polar.HasRise, polar.HasSet, fmt.Sprintf("%.3f", polar.MinResidual)) +``` + +```text + 1 false false 40.737 +``` + +```go +// rise and set of Sirius through -0.833° +sirius, err := phenomena.StarAltitudeCrossing(time.Date(2026, 1, 1, 12, 0, 0, 0, cst), + star.RA, star.Dec, 116.4, 39.9, -0.833, phenomena.AltitudeOptions{Limb: phenomena.LimbCenter}) +fmt.Println(err, sirius.State, sirius.Rise.In(cst).Format("15:04:05"), sirius.Set.In(cst).Format("15:04:05")) +``` + +```text + 0 19:04:31 05:27:52 +``` + +`height` only affects lunar parallax. When a depressed horizon is required, the caller subtracts the horizon dip from `altitude`. When the body is not the Sun, Moon or a planet (for example `phenomena.Earth`), or a star's apparent coordinates are not finite, the function returns `basic.ErrInvalidObservationInput`. + +## Apsides and nodes + +| Constant | Event | +| --- | --- | +| `Perihelion`, `Aphelion` | perihelion, aphelion | +| `AscendingNode`, `DescendingNode` | ascending and descending node passages through the mean ecliptic of epoch | + +`Apsides(body, start, end)` accepts a planet or the Earth (`phenomena.Earth`), while `Nodes(body, start, end)` accepts only planets and returns an empty slice for the Earth. In `OrbitalEvent`, `Time` is the event instant, `Kind` is a constant from the table above, and `DistanceAU` is the heliocentric distance at that instant (AU). + +```go +apsides := phenomena.Apsides(phenomena.Earth, + time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range apsides { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.9f AU", e.DistanceAU)) +} +nodes := phenomena.Nodes(phenomena.Jupiter, + time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range nodes { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.6f AU", e.DistanceAU)) +} +``` + +```text +0 2026-01-03 09:15 0.983302049 AU +1 2026-07-06 09:30 1.016643962 AU +3 2020-02-25 18:09 5.207056 AU +2 2025-09-19 11:58 5.174312 AU +``` + +The extremum is taken on the heliocentric distance itself: the Sun's reflex motion about the barycenter makes the heliocentric distance of Uranus and Neptune show several small extrema near perihelion, so when several extrema appear in one span only the one with the smallest heliocentric distance within `0.3` orbital periods before and after is returned, which can differ from the osculating perihelion by several years. + +## API reference + +The methods of `ctx.Phenomena()` match the package-level functions of the same name (except `StarAltitudeCrossing`); `NewWithInstance` creates a module bound to a given ephemeris instance. Parameters and units are in the sections above. + +## Parameter and return conventions + +- Angles are always in degrees, `OrbitalEvent.DistanceAU` is in AU, and `magnitude` in `StarHeliacalEvents` is V magnitude. +- Time parameters are interpreted as absolute instants (their UTC fields are read), and returned instants are converted to the time zone of `start` (`date` for the rise and set entries). +- Event searches return an empty slice rather than an error when there is no result; only rise and set at any altitude returns `basic.ErrInvalidObservationInput` for invalid input. +- Event searches use the ephemeris, which a module method can fix; `StarAltitudeCrossing`, `StarCoordinate*` and `LimitingMagnitude` are independent of the ephemeris. +- For differences in convention such as planetary apparent magnitude and right-ascension stations see the [planet manual](planets.md); for Sun and Moon rise and set and observing-angle semantics see [Sun and Moon](sun-moon.md). diff --git a/doc/manual/en/planets.md b/doc/manual/en/planets.md index 7418c83..f57f5c1 100644 --- a/doc/manual/en/planets.md +++ b/doc/manual/en/planets.md @@ -6,14 +6,14 @@ The seven major planets each have a package of their own: `mercury`, `venus`, `m They share the same API shape: a civil instant is passed as `time.Time`, angles and distances come back as `float64`, and event searches return `time.Time` or a struct. -The inner planets (Mercury and Venus) add superior/inferior conjunction, greatest elongation, and geocentric transit; the outer planets (Mars through Neptune) add opposition and quadrature; Jupiter alone has the Galilean satellites and Saturn alone has ring parameters. The low-level VSOP87 series and the solar/lunar analytic series live in the `planet` package, shared by these seven packages and by `sun` / `moon`. +The inner planets (Mercury and Venus) add superior/inferior conjunction, greatest elongation, and geocentric and topocentric transits; the outer planets (Mars through Neptune) add opposition and quadrature; Jupiter alone has the Galilean satellites and Saturn alone has ring parameters. The `planet` package holds the built-in semianalytic model and the Legacy series, shared by these packages and by `sun` / `moon`. Default builds use to semianalytic; use an explicit Context to fix the model. - The function names of the common capabilities are identical across the seven packages (for example all of them provide `ApparentRa`, `ApparentDec`, and `ApparentRaDec`); only the extra families differ. Always qualify a call with its package name instead of mixing packages. - Position functions return a **geocentric apparent place**. - Topocentric quantities and horizontal coordinates are separate: `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle`, with formulas under [Coordinate Tools](coord.md). + Horizontal quantities are separate: `Altitude` / `Azimuth` / `Zenith` are topocentric, while `HourAngle` / `ParallacticAngle` are geocentric (no topocentric parallax), with formulas under [Coordinate Tools](coord.md). - Event-search families come as `Last...` / `Next...` pairs (some also `Closest...`) and always return the nearest event at or before/after the input instant, endpoints included. - The planets have no dedicated SVG chart entry point; occultation-related charts (including Saturn-ring occultations) are documented in [Lunar Occultations](occultation.md#lunar-occultation-charts). @@ -23,7 +23,7 @@ The inner planets (Mercury and Venus) add superior/inferior conjunction, greates - [API Reference](#api-reference) - [Cross-planet comparison](#cross-planet-comparison) - [Common capabilities and units](#common-capabilities-and-units) - - [The `...N` truncation family](#the-n-truncation-family) + - [The `...N` family](#the-n-family) - [Usage examples](#usage-examples) - [Which planet can I see tonight?](#which-planet-can-i-see-tonight) - [Oppositions, conjunctions, elongations, stations and retrogrades](#oppositions-conjunctions-elongations-stations-and-retrogrades) @@ -43,7 +43,7 @@ The inner planets (Mercury and Venus) add superior/inferior conjunction, greates - [Division of labour with other manuals](#division-of-labour-with-other-manuals) - [Physical ephemerides](#physical-ephemerides) - [Galilean satellites of Jupiter](#galilean-satellites-of-jupiter) - - [Shared types and constants from the `planet` package](#shared-types-and-constants-from-the-planet-package) + - [Shared low-level entry points in the `planet` package](#shared-low-level-entry-points-in-the-planet-package) - [Parameter and result conventions](#parameter-and-result-conventions) - [Time scale and civil time](#time-scale-and-civil-time) - [Units and conventions](#units-and-conventions) @@ -86,7 +86,7 @@ func main() { Rows list capabilities and columns list planetary packages. Qualify each function with its package, for example `mars.NextOpposition`. A slash separates function names; `—` means the package has no corresponding API. -| Capability | `mercury` | `venus` | `mars` | `jupiter` | `saturn` | `uranus` | `neptune` | +| Quantity | `mercury` | `venus` | `mars` | `jupiter` | `saturn` | `uranus` | `neptune` | | --- | --- | --- | --- | --- | --- | --- | --- | | Apparent ecliptic longitude / latitude | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | | Apparent right ascension / declination | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | @@ -95,7 +95,7 @@ Rows list capabilities and columns list planetary packages. Qualify each functio | Orbital ascending / descending node | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | | Apparent diameter / semidiameter | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | | Phase angle / illuminated fraction / bright-limb position angle | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | -| Topocentric horizontal quantities | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | +| Horizontal quantities | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | | Rise / set / culmination | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | | Physical ephemeris | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` / `CentralMeridians` / `CentralMeridiansN` | `Physical` / `PhysicalN` / `PhysicalSystemIII` / `Ring` | `Physical` / `PhysicalN` / `PhysicalSystemIII` | `Physical` / `PhysicalN` | | Conjunction | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | @@ -104,25 +104,27 @@ Rows list capabilities and columns list planetary packages. Qualify each functio | Opposition | — | — | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | | Quadrature | — | — | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | | Greatest elongation | `LastGreatestElongation` / `NextGreatestElongation` / `LastGreatestElongationEast` / `NextGreatestElongationEast` / `LastGreatestElongationWest` / `NextGreatestElongationWest` | `LastGreatestElongation` / `NextGreatestElongation` / `LastGreatestElongationEast` / `NextGreatestElongationEast` / `LastGreatestElongationWest` / `NextGreatestElongationWest` | — | — | — | — | — | -| Geocentric transit | `LastTransit` / `NextTransit` / `ClosestTransit` | `LastTransit` / `NextTransit` / `ClosestTransit` | — | — | — | — | — | +| Transit | `LastTransit` / `NextTransit` / `ClosestTransit` / `LocalTransit` | `LastTransit` / `NextTransit` / `ClosestTransit` / `LocalTransit` | — | — | — | — | — | | Galilean satellites | — | — | — | `Satellites` / `SatellitePhenomena` / `LastGalileanPhenomenonEvent` / `NextGalileanPhenomenonEvent` / `ClosestGalileanPhenomenonEvent` / `LastGalileanPhenomenonContactEvent` / `NextGalileanPhenomenonContactEvent` / `ClosestGalileanPhenomenonContactEvent` | — | — | — | -| Result types | `PhysicalInfo` / `TransitInfo` | `PhysicalInfo` / `TransitInfo` | `PhysicalInfo` | `PhysicalInfo` / `CentralMeridianInfo` / `GalileanSatellitesInfo` / `GalileanPhenomenaInfo` / `GalileanSatellitePosition` / `GalileanSatellitePhenomenon` / `GalileanPhenomenonEvent` / `GalileanPhenomenonContactEvent` | `PhysicalInfo` / `RingInfo` | `PhysicalInfo` | `PhysicalInfo` | +| Result types | `PhysicalInfo` / `TransitInfo` / `LocalTransitInfo` | `PhysicalInfo` / `TransitInfo` / `LocalTransitInfo` | `PhysicalInfo` | `PhysicalInfo` / `CentralMeridianInfo` / `GalileanSatellitesInfo` / `GalileanPhenomenaInfo` / `GalileanSatellitePosition` / `GalileanSatellitePhenomenon` / `GalileanPhenomenonEvent` / `GalileanPhenomenonContactEvent` | `PhysicalInfo` / `RingInfo` | `PhysicalInfo` | `PhysicalInfo` | -All seven packages spell the **truncation family** the same way: append `N` to any instantaneous evaluation function, where `n < 0` uses the full built-in series and `n >= 0` truncates it. +All seven packages spell the **`...N` family** the same way: append `N` to an instantaneous evaluation function. `n < 0` selects the full series; all nonnegative `n` select one fixed semianalytic fast table, while Legacy truncates according to `n`. -See [the `...N` truncation family](#the-n-truncation-family) for details. Event-search families (`Last...` / `Next...` / `Closest...`) have no `N` variants. +See [the `...N` family](#the-n-family) for details. Event-search families (`Last...` / `Next...` / `Closest...`) have no `N` variants. ### Common capabilities and units -| Capability | Purpose | Unit and convention | +| Quantity | Purpose | Unit and convention | | --- | --- | --- | | `ApparentLo` / `ApparentBo` | Geocentric apparent ecliptic longitude and latitude | degrees; true equinox of date, including light time, aberration, and nutation | | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | Geocentric apparent right ascension and declination | degrees; true equator of date; `ApparentRaDec` returns both at once | | `ApparentMagnitude` | Apparent visual magnitude | magnitudes (dimensionless) | -| `Altitude` / `Azimuth` / `Zenith` / `HourAngle` | Topocentric horizontal coordinates | degrees; azimuth runs from north toward east, and `Zenith` equals `90 - Altitude` | +| `Altitude` / `Azimuth` / `Zenith` | Topocentric horizontal coordinates | degrees; azimuth runs from north toward east, and `Zenith` equals `90 - Altitude` | +| `HourAngle` | Geocentric apparent hour angle, without topocentric parallax | degrees | | `RiseTime` / `SetTime` / `DownTime` | Rise and set within the local civil day | `time.Time` keeping the input time zone; `DownTime` is a compatibility alias for `SetTime` | +| `RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` | Rise and set with measured pressure (hPa) and temperature (°C) | `time.Time`; refraction always applied | | `CulminationTime` | Upper culmination instant | `time.Time` keeping the input time zone | -| `ParallacticAngle` | Parallactic angle (zenith direction angle) | degrees | +| `ParallacticAngle` | Geocentric parallactic angle (zenith direction angle), from the geocentric hour angle and declination | degrees | | `Diameter` / `Semidiameter` | Geocentric apparent diameter and semidiameter | arcseconds | | `PhaseAngle` | Sun-planet-Earth angle | degrees | | `IlluminatedFraction` / `Phase` | Illuminated fraction | `0-1`; `Phase` is an alias for `IlluminatedFraction` | @@ -130,18 +132,23 @@ See [the `...N` truncation family](#the-n-truncation-family) for details. Event- | `EarthDistance` / `SunDistance` | Earth distance and Sun distance | AU | | `AscendingNode` / `DescendingNode` | Ecliptic longitude of the orbital plane's intersections with the ecliptic | degrees; the two differ by about `180°` at the same instant | | `Physical` | Disk orientation, sub-Earth/sub-Solar coordinates, north-pole position angle | degrees; the positive longitude direction follows each body's IAU convention | -| `planet.WherePlanet` / `planet.WherePlanetN` | VSOP87 longitude, latitude, and heliocentric distance | degrees / AU; out-of-range input returns `NaN` instead of panicking | +| `planet.WherePlanet` / `planet.WherePlanetN` | Selected built-in model's longitude, latitude, and heliocentric distance | degrees / AU; out-of-range input returns `NaN` instead of panicking | -### The `...N` truncation family +### The `...N` family -Every instantaneous evaluation function has an `...N` variant that trades accuracy for cost: `n < 0` uses the full built-in series and is equivalent to the plain function, while `n >= 0` keeps roughly `n` principal terms and scales the higher orders proportionally. +Every instantaneous evaluation function has an `...N` variant that trades accuracy for cost. `n < 0` selects the full series and matches the plain function. All nonnegative `n` select one fixed semianalytic fast table, so increasing `n` does not improve accuracy; Legacy scales its principal and higher-order terms according to `n`. Event-search families (`Last...` / `Next...` / `Closest...`) have no `N` variant. -Functions with an `N` variant include `ApparentLoN`, `ApparentBoN`, `ApparentRaN`, `ApparentDecN`, `ApparentRaDecN`, `ApparentMagnitudeN`, `EarthDistanceN`, `SunDistanceN`, `AltitudeN`, `AzimuthN`, `ZenithN`, `HourAngleN`, `CulminationTimeN`, `RiseTimeN`, `SetTimeN`, `DownTimeN`, `ParallacticAngleN`, `DiameterN`, `SemidiameterN`, `PhaseAngleN`, `PhaseN`, `IlluminatedFractionN`, `BrightLimbPositionAngleN`, `AscendingNodeN`, `DescendingNodeN`, and `PhysicalN`, plus `CentralMeridiansN` for Jupiter, `RingN` and `PhysicalSystemIIIN` for Saturn, and `PhysicalSystemIIIN` for Uranus. +Functions with an `N` variant, grouped by purpose: + +- Position and distance: `ApparentLoN`, `ApparentBoN`, `ApparentRaN`, `ApparentDecN`, `ApparentRaDecN`, `EarthDistanceN`, `SunDistanceN` +- Horizon and instants: `AltitudeN`, `AzimuthN`, `ZenithN`, `HourAngleN`, `CulminationTimeN`, `RiseTimeN`, `SetTimeN`, `DownTimeN`, `ParallacticAngleN` +- Disc and phase: `ApparentMagnitudeN`, `DiameterN`, `SemidiameterN`, `PhaseAngleN`, `PhaseN`, `IlluminatedFractionN`, `BrightLimbPositionAngleN` +- Nodes and rotation: `AscendingNodeN`, `DescendingNodeN`, `PhysicalN`, plus `CentralMeridiansN` for Jupiter, `RingN` and `PhysicalSystemIIIN` for Saturn, and `PhysicalSystemIIIN` for Uranus ```go -fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // ecliptic longitude: full series / truncated to about 8 terms +fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // full series / fast table; Legacy truncates according to n fmt.Println(mars.SunDistance(date), mars.SunDistanceN(date, 8)) // heliocentric distance, AU ``` @@ -161,7 +168,7 @@ fmt.Println(mars.Altitude(date, lon, lat), mars.Azimuth(date, lon, lat)) // Mars 31.194578177219057 152.07031660415714 ``` -Above the horizon means `Altitude` greater than `0`, and azimuth increases from due north towards the east; `aero = true` lowers the geometric horizon to about `-0.5667°` for standard refraction. +Azimuth increases from due north towards the east; `aero = true` lowers the geometric horizon to about `-0.5667°` for standard refraction. Per-parameter conventions and the polar sentinel errors are in [Rise, set and culmination](#rise-set-and-culmination). @@ -196,7 +203,21 @@ true 2032-11-13 14:41:13.161198198 +0800 CST 2032-11-13 16:54:12.821315824 +0800 4h26m2.695272267s 572.0643215495325 ``` -`TransitInfo.Valid` false means no transit inside the search window and every other field is the zero value; a transit is a geocentric geometry test and does not check whether the Sun is above the horizon at a given site. +`TransitInfo.Valid` false means no transit inside the search window and every other field is the zero value; `TransitInfo` is the geocentric geometry. For the contact times seen from one site, `LocalTransit` returns the topocentric result: + +```go +transit := mercury.NextTransit(time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)) +local := mercury.LocalTransit(transit, -77.0, 38.9, 0) // Washington: longitude, latitude, height in metres +fmt.Println(local.Valid, local.HasInternal, local.Visible) // topocentric transit, internal contacts, Sun above the apparent horizon +fmt.Println(local.Start.Time, local.Start.SunAltitude) // topocentric C1 and the Sun's altitude then +``` + +```text +true true true +2019-11-11 12:36:09.037762284 +0000 UTC 7.933256722170642 +``` + +Topocentric contacts are re-solved after applying parallax to the Sun and the planet separately; Venus contacts can shift by about ±7 minutes from the geocentric ones across the globe, Mercury by about ±2 minutes. `SunAltitude` is the geometric altitude of the Sun's center; `Visible`, `Sunrise` and `Sunset` test whether the Sun's center is above the apparent horizon with standard refraction (1010 hPa, 10 °C), a geometric threshold of about −34′. A site may see only a grazing transit even when the geocentric one has internal contacts (the 1999-11-15 Mercury transit at high southern latitudes); then `HasInternal` is false and `InternalStart` / `InternalEnd` are zero. Global maps and topocentric disk charts are in [Eclipses and transits](eclipse.md#transit-charts). The four contacts, partial transits and the internal-contact fields are in [Greatest elongation and geocentric transits](#greatest-elongation-and-geocentric-transits). @@ -216,7 +237,7 @@ fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date)) // ascending/de `Phase` is an alias of `IlluminatedFraction` and runs from `0` to `1`; the two nodes are about `180°` apart at any instant. -Definitions, aliases and the truncated versions are in [Nodes, phase, magnitude, diameter and parallactic angle](#nodes-phase-magnitude-diameter-and-parallactic-angle). +Definitions, aliases and the `n` variants are in [Nodes, phase, magnitude, diameter and parallactic angle](#nodes-phase-magnitude-diameter-and-parallactic-angle). ### Physical ephemerides and the Galilean satellites @@ -237,7 +258,7 @@ Saturn's rings have their own `saturn.Ring` (`EarthLatitude`, `MinorAxis`, and s ### Comparison conventions ```go -fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // apparent longitude with every term / truncated to about 8 +fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // full series / fast table; Legacy truncates according to n _, err := mars.RiseTime(date, 0, 89, 0, true) // an observing site in the polar region fmt.Println(errors.Is(err, mars.ERR_MARS_NEVER_RISE), errors.Is(err, mars.ERR_MARS_NEVER_SET)) ``` @@ -247,9 +268,9 @@ fmt.Println(errors.Is(err, mars.ERR_MARS_NEVER_RISE), errors.Is(err, mars.ERR_MA true false ``` -`ApparentLo` and friends are geocentric apparent places referred to the **true equinox of date**; bring external J2000 or mean places to the same convention with `coord.Precess` before comparing. +`ApparentLo` and friends are geocentric apparent places referred to the **true equinox of date**. Comparing them with external J2000 or mean places requires matching the frame, nutation and apparent-place corrections; precession alone does not remove every difference. -In the truncation family `n < 0` uses every built-in term and `n >= 0` truncates. +For model-dependent `n` semantics, see [the `...N` family](#the-n-family). The convention gap between the Galilean contact events and JPL Horizons / IMCCE tables is documented under [External baselines](#external-baselines), and the overall boundaries under [Parameter and result conventions](#parameter-and-result-conventions). @@ -308,20 +329,20 @@ func main() { Output: ```text -2019-11-11 23:21:41.971051096 +0800 CST // previous inferior conjunction of Mercury -2021-03-26 14:57:42.052354216 +0800 CST // next superior conjunction of Venus -2019-11-01 04:31:49.749019145 +0800 CST // previous Mercury station from prograde to retrograde -2020-06-25 02:07:41.599749326 +0800 CST // next Venus station from retrograde to prograde -2019-10-20 12:01:37.740152478 +0800 CST // previous greatest eastern elongation of Mercury -2020-08-13 08:14:46.304587125 +0800 CST // next greatest western elongation of Venus -2020-01-01 10:02:34.172435402 +0800 CST // Venus rise time in Xi'an; no error -2020-01-01 20:25:37.36411482 +0800 CST // Venus set time in Xi'an; no error +2019-11-11 23:21:41.729008555 +0800 CST // previous inferior conjunction of Mercury +2021-03-26 14:57:43.352850079 +0800 CST // next superior conjunction of Venus +2019-11-01 04:31:49.712648391 +0800 CST // previous Mercury station from prograde to retrograde +2020-06-25 02:07:26.280500292 +0800 CST // next Venus station from retrograde to prograde +2019-10-20 12:01:37.925546765 +0800 CST // previous greatest eastern elongation of Mercury +2020-08-13 08:14:45.946914553 +0800 CST // next greatest western elongation of Venus +2020-01-01 10:02:34.94517982 +0800 CST // Venus rise time in Xi'an; no error +2020-01-01 20:25:36.944952607 +0800 CST // Venus set time in Xi'an; no error -4 // Venus apparent magnitude -49.98145049145023 // Venus phase angle, degrees -0.8215177914415865 // illuminated fraction of Venus -255.63802053541346 // bright-limb position angle of Venus, degrees -1.2778819631550336 // Earth-Venus distance, AU -0.7262651056423838 // Sun-Venus distance, AU +49.98142313573699 // Venus phase angle, degrees +0.8215179742647736 // illuminated fraction of Venus +255.63803631896667 // bright-limb position angle of Venus, degrees +1.2778823747747166 // Earth-Venus distance, AU +0.7262651318993368 // Sun-Venus distance, AU ``` Inner and outer planets also expose `Diameter` / `Semidiameter` and `N` variants, returning geocentric apparent diameter/semidiameter in arcseconds. @@ -346,7 +367,7 @@ For `date := 2020-01-01 08:08:08 CST`, the output is: 76.86008484515058 256.8600848451506 // Venus ascending-node and descending-node longitudes, degrees ``` -Mercury and Venus also expose `NextTransit` / `LastTransit` / `ClosestTransit` for geocentric planetary transits. "Geocentric" means the planet disk crosses the solar disk as seen from Earth's center; it does not test whether the Sun is above the horizon at a particular observing site. For observing plans, combine this with local solar altitude and weather. +Mercury and Venus also expose `NextTransit` / `LastTransit` / `ClosestTransit` for geocentric planetary transits. "Geocentric" means the planet disk crosses the solar disk as seen from Earth's center; it does not test whether the Sun is above the horizon at a particular observing site. For observing plans, use `LocalTransit` for topocentric contacts and solar altitudes (see above). ```go package main @@ -389,22 +410,22 @@ Output: ```text true // a valid geocentric Mercury transit was found -2019-11-11 12:35:31.567597389 +0000 UTC // first contact: Mercury externally enters the solar disk -2019-11-11 12:37:12.817581295 +0000 UTC // second contact: Mercury is fully inside the solar disk -2019-11-11 15:19:48.36056292 +0000 UTC // greatest transit: Mercury center is closest to the Sun center -2019-11-11 18:02:29.176982045 +0000 UTC // third contact: Mercury starts leaving the solar disk -2019-11-11 18:04:10.637948513 +0000 UTC // fourth contact: Mercury externally leaves the solar disk -5h28m39.070351124s // geocentric transit duration from first to fourth contact -75.92400059923187 // minimum Mercury-Sun center separation at greatest transit, arcseconds -968.8881519533047 // solar semidiameter at greatest transit, arcseconds -4.978442871670873 // Mercury semidiameter at greatest transit, arcseconds +2019-11-11 12:35:31.142292916 +0000 UTC // first contact: Mercury externally enters the solar disk +2019-11-11 12:37:12.603219151 +0000 UTC // second contact: Mercury is fully inside the solar disk +2019-11-11 15:19:48.146160542 +0000 UTC // greatest transit: Mercury center is closest to the Sun center +2019-11-11 18:02:28.751637339 +0000 UTC // third contact: Mercury starts leaving the solar disk +2019-11-11 18:04:10.212523341 +0000 UTC // fourth contact: Mercury externally leaves the solar disk +5h28m39.070230425s // geocentric transit duration from first to fourth contact +75.93592092696468 // minimum Mercury-Sun center separation at greatest transit, arcseconds +968.8881399120452 // solar semidiameter at greatest transit, arcseconds +4.978442587642365 // Mercury semidiameter at greatest transit, arcseconds true // a valid geocentric Venus transit was found -2012-06-05 22:09:47.466886639 +0000 UTC // first contact: Venus externally enters the solar disk -2012-06-05 22:27:35.865356326 +0000 UTC // second contact: Venus is fully inside the solar disk -2012-06-06 01:29:35.572371482 +0000 UTC // greatest transit: Venus center is closest to the Sun center -2012-06-06 04:31:35.068444311 +0000 UTC // third contact: Venus starts leaving the solar disk -2012-06-06 04:49:23.25597167 +0000 UTC // fourth contact: Venus externally leaves the solar disk -6h39m35.789085031s // geocentric transit duration from first to fourth contact +2012-06-05 22:09:48.690456748 +0000 UTC // first contact: Venus externally enters the solar disk +2012-06-05 22:27:37.088886201 +0000 UTC // second contact: Venus is fully inside the solar disk +2012-06-06 01:29:36.58495903 +0000 UTC // greatest transit: Venus center is closest to the Sun center +2012-06-06 04:31:35.87008953 +0000 UTC // third contact: Venus starts leaving the solar disk +2012-06-06 04:49:24.268559217 +0000 UTC // fourth contact: Venus externally leaves the solar disk +6h39m35.578102469s // geocentric transit duration from first to fourth contact ``` ### Outer planets @@ -467,18 +488,18 @@ func main() { Output: ```text -2020-10-14 07:25:50.441412627 +0800 CST // next opposition of Mars -2021-01-29 09:39:33.697994649 +0800 CST // next conjunction of Jupiter -2019-04-30 10:28:00.187439918 +0800 CST // previous Saturn station from prograde to retrograde -saturn B=23.577025 Bp=23.266930 P=6.629811 dU=1.171016 major=34.133852 minor=13.652911 // Saturn ring B, B', P, dU, major axis, minor axis -2020-01-11 15:23:23.360308706 +0800 CST // next Uranus station from retrograde to prograde -2019-12-08 17:00:15.517960488 +0800 CST // previous eastern quadrature of Neptune -2020-06-07 03:11:00.026179254 +0800 CST // next western quadrature of Mars -2020-01-01 04:41:29.621566236 +0800 CST // Mars rise time in Xi'an; no error -2020-01-01 14:55:32.963508367 +0800 CST // Mars set time in Xi'an; no error +2020-10-14 07:25:53.541214764 +0800 CST // next opposition of Mars +2021-01-29 09:39:32.556620836 +0800 CST // next conjunction of Jupiter +2019-04-30 10:28:17.254818677 +0800 CST // previous Saturn station from prograde to retrograde +saturn B=23.577010 Bp=23.266930 P=6.629810 dU=1.171003 major=34.133617 minor=13.652809 // Saturn ring B, B', P, dU, major axis, minor axis +2020-01-11 15:22:18.532456755 +0800 CST // next Uranus station from retrograde to prograde +2019-12-08 17:00:04.494322836 +0800 CST // previous eastern quadrature of Neptune +2020-06-07 03:10:55.059157311 +0800 CST // next western quadrature of Mars +2020-01-01 04:41:30.157055854 +0800 CST // Mars rise time in Xi'an; no error +2020-01-01 14:55:32.792302966 +0800 CST // Mars set time in Xi'an; no error 1.57 // Mars apparent magnitude -2.1844284956325937 // Earth-Mars distance, AU -1.5897860004265403 // Sun-Mars distance, AU +2.1844226019239925 // Earth-Mars distance, AU +1.5897858533008575 // Sun-Mars distance, AU ``` `saturn.Ring` returns `RingInfo`: `EarthLatitude` is ring opening angle B, `SunLatitude` is B', `PositionAngle` is the position angle of the northern semiminor axis, `DeltaU` is the Saturnicentric longitude difference between the Sun and Earth in the ring plane, and `MajorAxis` / `MinorAxis` are the apparent outer major/minor axes in arcseconds. @@ -505,9 +526,9 @@ fmt.Println(venus.EarthDistance(date), venus.SunDistance(date)) ### Rise, set and culmination -`RiseTime` / `SetTime` / `DownTime` work on the **local civil day** only: `date` selects the local date and the output time zone (when the local hour is greater than 12 it is first shifted back by 12 hours), `height` is the ellipsoidal height in meters, and `aero == true` adds standard atmospheric refraction (lowering the geometric horizon to about `-0.5667°`). +`RiseTime` / `SetTime` / `DownTime` work on the **local civil day** only: `date` selects the local date and the output time zone, and the returned events belong to that local civil day; `height` is the ellipsoidal height in meters, and `aero == true` adds standard atmospheric refraction (lowering the geometric horizon to about `-0.5667°`). -Polar day, polar night, or a day with no crossing returns a [sentinel error](#parameter-and-result-conventions) instead of an instant. `CulminationTime` gives upper culmination; `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` form the topocentric family, with geometry under [Coordinate Tools](coord.md). +Polar day, polar night, or a day with no crossing returns a [sentinel error](#parameter-and-result-conventions) instead of an instant. `CulminationTime` gives upper culmination; `Altitude` / `Azimuth` / `Zenith` are topocentric instantaneous quantities and `HourAngle` / `ParallacticAngle` geocentric ones, with geometry under [Coordinate Tools](coord.md). ```go // Rise, set, and culmination on the local civil day in Xi'an; aero=true adds refraction. @@ -525,7 +546,7 @@ fmt.Println(mars.ParallacticAngle(date, lon, lat)) ``` ```go -// DownTime is a compatibility alias for SetTime; the N variant evaluates a truncated series. +// DownTime is a compatibility alias for SetTime; the N variant evaluates the `n` variant. set, err := mars.DownTime(date, lon, lat, height, true) fmt.Println(set, err) fmt.Println(mars.CulminationTimeN(date, lon, 12)) @@ -597,7 +618,7 @@ fmt.Println(transit.SunSemidiameterArcsec, transit.PlanetSemidiameterArcsec) `PhaseAngle` is the Sun-planet-Earth angle in degrees; `IlluminatedFraction` (alias `Phase`) is the illuminated fraction from `0` to `1`; `BrightLimbPositionAngle` is the position angle of the bright-limb center in degrees. -`Diameter` / `Semidiameter` return the geocentric apparent diameter and semidiameter in arcseconds. `ParallacticAngle` returns the topocentric parallactic angle (zenith direction angle) in degrees; topocentric coordinates are under [Coordinate Tools](coord.md). +`Diameter` / `Semidiameter` return the geocentric apparent diameter and semidiameter in arcseconds. `ParallacticAngle` returns the geocentric parallactic angle (zenith direction angle) in degrees, computed from the geocentric hour angle and declination without topocentric parallax; topocentric coordinates are under [Coordinate Tools](coord.md). ```go fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date)) // node longitudes, degrees @@ -611,7 +632,7 @@ fmt.Println(mars.ParallacticAngle(date, lon, lat)) // parallactic angle, ``` ```go -// Nodes and apparent diameter also have truncated variants. +// Nodes and apparent diameter also have `n` variants. fmt.Println(mars.AscendingNodeN(date, 12), mars.DescendingNodeN(date, 12)) fmt.Println(mars.DiameterN(date, 12), mars.SemidiameterN(date, 12)) ``` @@ -669,8 +690,8 @@ func main() { Output: ```text -jupiter DS=54.342153 DE=1.436485 CMI=292.712909 CMII=276.309048 CMIII=147.241811 // Jupiter DS/DE and System I/II/III central meridians, degrees -saturn B=-0.608048 Bp=-2.675677 P=4.480276 major=42.709920 minor=0.453248 // Saturn ring B, B', minor-axis position angle, outer major/minor axes +jupiter DS=54.342201 DE=1.436449 CMI=292.712946 CMII=276.309085 CMIII=147.241848 // Jupiter DS/DE and System I/II/III central meridians, degrees +saturn B=-0.608032 Bp=-2.675667 P=4.480277 major=42.709152 minor=0.453228 // Saturn ring B, B', minor-axis position angle, outer major/minor axes ``` If only Jupiter central meridians are needed: @@ -796,8 +817,8 @@ func main() { Output: ```text -io x=-0.675026 y=-0.032798 front=true // Io X/Y offset from Jupiter center, in Jupiter radii; in front of Jupiter -europa ra=110.769133 dec=22.335828 // Europa apparent RA and Dec, degrees +io x=-0.675023 y=-0.032794 front=true // Io X/Y offset from Jupiter center, in Jupiter radii; in front of Jupiter +europa ra=110.769095 dec=22.335877 // Europa apparent RA and Dec, degrees io transit=true occultation=false eclipse=false shadow=true // Io is transiting, and its shadow is also transiting europa transit=false occultation=false eclipse=false shadow=false // Europa has no transit, occultation, eclipse, or shadow transit at this instant event valid=true sat=1 type=transit // next valid event is an Io transit @@ -829,15 +850,15 @@ Comparison results: - `GalileanPhenomenonContactEvent` differs from IMCCE 2026 D/F contact times by at most about `79 s`, and contact durations by about `17 s` in the compared cases. - `GalileanPhenomenonEvent` uses a different definition from IMCCE D/F contacts, so start and end times can differ by about `7 min`. -### Shared types and constants from the `planet` package +### Shared low-level entry points in the `planet` package -The `planet` package is the low-level analytic-series entry point, shared by the seven planet packages and by `sun` / `moon`. +The `planet` package provides low-level entry points to the built-in models and series, shared by the seven planet packages and by `sun` / `moon`. -It exports functions only and **no types**, so what the planet packages really share is one set of numerical conventions and truncation semantics rather than shared types: each package's `PhysicalInfo` mirrors `basic.PlanetPhysicalInfo` and `TransitInfo` mirrors `basic.PlanetTransitResult`, field for field, while the types themselves still belong to their own packages. +It exports functions only, not types. The planet packages share numerical conventions and model choices rather than result types: each package's `PhysicalInfo` mirrors `basic.PlanetPhysicalInfo` and `TransitInfo` mirrors `basic.PlanetTransitResult`, field for field, while the types themselves still belong to their own packages. External kernel-engine types live in the separate `planet/spk` subpackage. | Entry point | Purpose | Unit | | --- | --- | --- | -| `planet.WherePlanet` / `planet.WherePlanetN` | VSOP87 result: `xt` is `1..7` for Mercury through Neptune and `-1` or `0` for Earth, `zn` is `0` longitude, `1` latitude, `2` heliocentric distance; an out-of-range `xt` / `zn` returns `NaN` instead of panicking | degrees / AU | +| `planet.WherePlanet` / `planet.WherePlanetN` | Selected built-in model: `xt` is `1..7` for Mercury through Neptune and `-1` or `0` for Earth, `zn` is `0` longitude, `1` latitude, `2` heliocentric distance; an out-of-range `xt` / `zn` returns `NaN` instead of panicking | degrees / AU | | `planet.Distance` | Sun-Earth distance | AU | | `planet.SunLo` / `planet.SunM` / `planet.SunMidFun` / `planet.SunTrueLo` / `planet.SunApparentLo` | Solar geometric longitude, mean anomaly, equation of center, true longitude, apparent longitude | degrees | | `planet.Earthe` / `planet.EarthPI` | Earth's orbital eccentricity and longitude of perihelion | dimensionless / degrees | @@ -848,7 +869,7 @@ It exports functions only and **no types**, so what the planet packages really s ```go // xt=1..7 is Mercury..Neptune; zn=0 longitude, 1 latitude, 2 heliocentric distance (AU). fmt.Println(planet.WherePlanet(4, 2, 2460000.5)) // Jupiter heliocentric distance, AU -fmt.Println(planet.WherePlanetN(4, 2, 2460000.5, 12)) // truncated form, about 12 principal terms +fmt.Println(planet.WherePlanetN(4, 2, 2460000.5, 12)) // semianalytic fast table; Legacy truncates according to n fmt.Println(planet.WherePlanet(8, 0, 2460000.5)) // xt out of range: NaN ``` @@ -873,17 +894,17 @@ From 1972 on the built-in leap-second table is used, and beyond the exact window Angles are always in degrees; apparent diameter and semidiameter are in arcseconds; distances follow the function name -- `EarthDistance` / `SunDistance` and `planet.WherePlanet` with `zn=2` are in AU, while `planet.MoonAway` is in km. -`PhaseAngle` is in degrees, `IlluminatedFraction` and its alias `Phase` are `0-1`, and `ApparentMagnitude` is a magnitude (dimensionless); rise/set, culmination, and every event search return `time.Time` in the input time zone. +`PhaseAngle` is in degrees, `IlluminatedFraction` and its alias `Phase` are `0-1`, and `ApparentMagnitude` is a magnitude (dimensionless, classic phase polynomials); `ApparentMagnitude2018` uses Mallama & Hilton 2018, better for Mercury/Venus at large phase and Saturn's rings, without the Mars rotation and season terms; rise/set, culmination, and every event search return `time.Time` in the input time zone. Duration fields inside event structs (`TransitInfo.Duration`, `TransitInfo.InternalDuration`, `GalileanPhenomenonEvent.Duration`, `GalileanPhenomenonContact.Duration`) are Go `time.Duration` values, not Julian days or day counts; the `Start` / `Greatest` / `End` / `InternalStart` / `InternalEnd` fields of `TransitInfo` keep the caller's time zone. -Geocentric, topocentric, and distance are three different conventions: `ApparentLo` / `ApparentBo` / `ApparentRa` / `ApparentDec` / `ApparentRaDec` are geocentric apparent places, `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` are topocentric quantities, and `EarthDistance` / `SunDistance` are geometric geocentric distances. +Geocentric, topocentric, and distance are three different conventions: `ApparentLo` / `ApparentBo` / `ApparentRa` / `ApparentDec` / `ApparentRaDec` are geocentric apparent places, `Altitude` / `Azimuth` / `Zenith` are topocentric quantities, `HourAngle` / `ParallacticAngle` are geocentric quantities without topocentric parallax, and `EarthDistance` / `SunDistance` are geometric geocentric distances. ### Zero values, out-of-range input and sentinel errors When an event search finds nothing inside its window it returns zero values or a struct with `Valid == false` (`TransitInfo.Valid`, `GalileanPhenomenonEvent.Valid`, `GalileanPhenomenonContactEvent.Valid`) instead of an error; when `TransitInfo.HasInternal` is false, `InternalStart` / `InternalEnd` are zero values because a partial transit has no internal contacts. -`planet.WherePlanet` / `planet.WherePlanetN` return `NaN` instead of panicking when `xt` / `zn` is out of range; in the `...N` truncation family, `n < 0` uses the full built-in series and `n >= 0` truncates it. +`planet.WherePlanet` / `planet.WherePlanetN` return `NaN` instead of panicking when `xt` / `zn` is out of range. `n < 0` selects the full series; all nonnegative `n` select one semianalytic fast table, while Legacy truncates according to `n`. `RiseTime` / `SetTime` / `DownTime` (including their `N` forms) are the only family that returns an `error`: when the body has no geometric rise or set on that day they return the sentinel error below with a zero instant. @@ -916,12 +937,12 @@ case errors.Is(err, mercury.ERR_MERCURY_NEVER_SET): `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` use `date`'s time zone for local-time computation, with east-positive longitude, north-positive latitude, and `height` as the ellipsoidal height in meters (not orthometric elevation). -They are a different convention from the geocentric apparent place of `ApparentRa` / `ApparentDec`; convert through [Coordinate Tools](coord.md) before mixing them. +`Altitude` / `Azimuth` / `Zenith` include topocentric parallax and are a different convention from the geocentric apparent place of `ApparentRa` / `ApparentDec`; `HourAngle` / `ParallacticAngle` come straight from the geocentric apparent place without topocentric parallax. Convert through [Coordinate Tools](coord.md) before mixing them. ### Accuracy and scope -The planet packages use the built-in truncated VSOP87 series, covering about 4000 years around J2000; the truncation magnitudes relative to full VSOP87 are in [Sun and planets](accuracy.md#sun-and-planets) and the overall scope is in [Scope And Accuracy](accuracy.md). +The planet packages default to the semianalytic model, with Legacy or custom engines selectable explicitly. An available range does not imply uniform accuracy across it; Legacy truncation error against complete VSOP87 is not total error against DE ephemerides. See [Legacy truncation error](accuracy.md#legacy-truncation-error). -The `...N` forms relax that baseline further and suit batch scans or live front-end refreshes. +The fast table suits coarse scans. Use the full series and matching time/frame conventions for event refinement or comparisons with external tables. `saturn.Ring` and the physical ephemerides affect only the disk and band rendering: the Saturn ring takes no part in occultation contact geometry and is not drawn as a disk boundary. The planets have no dedicated chart entry point; occultation and Saturn-ring charts are in [Lunar Occultations](occultation.md#lunar-occultation-charts). diff --git a/doc/manual/en/star.md b/doc/manual/en/star.md index 604680e..32302bd 100644 --- a/doc/manual/en/star.md +++ b/doc/manual/en/star.md @@ -8,6 +8,7 @@ The library ships a catalog of 9100 stars (BSC / HR numbers `1-9110`, apparent m ## Contents +- [External catalogs](#external-catalogs) - [Finding Sirius and its rise time](#finding-sirius-and-its-rise-time) - [API Reference](#api-reference) - [Constellation lookup](#constellation-lookup) @@ -26,6 +27,55 @@ The library ships a catalog of 9100 stars (BSC / HR numbers `1-9110`, apparent m - [Parameter and result conventions](#parameter-and-result-conventions) - [Related manuals](#related-manuals) +## External catalogs + +`star/catalog` is independent of the embedded 9100-star database and does not automatically download or load large catalogs. `Entry.Coordinate` is a `basic.StarCoordinate` with epoch, frame and proper motion; pass it directly to `moon.FindStarOccultations` or `phenomena.StarApproaches`. + +| Entry point | Input and purpose | +| --- | --- | +| `ReadCSV` | Header-based CSV / TSV; `ColumnMap` sets columns, epoch and frame; zero `Delimiter` enables detection | +| `ReadHipparcos2007` / `ReadBSC5` | Corresponding fixed-width catalog text | +| `ReadStellariumStars` / `ReadStellariumDir` | One binary shard or an `fs.FS` directory; directory options load names/cross-IDs and filter by `MaxMagnitude` | +| `Lookup` / `Entries` / `Len` | ID or alias lookup, copied records and count | +| `Near` / `Band` | Cone / declination-band searches at catalog epochs, without propagation to the observing date | +| `Merge` / `WithNames` | Return new catalogs; right-hand records win for the same primary ID and component; names use `id\|name` lines | + +```go +import ( + "strings" + "time" + + "b612.me/astro/basic" + "b612.me/astro/star/catalog" +) + +input := strings.NewReader("id,ra,dec,pmra,pmdec,V\nSirius,101.28715533,-16.71611586,-546.01,-1223.07,-1.46\n") +stars, err := catalog.ReadCSV(input, catalog.ColumnMap{ + ID: "id", RA: "ra", Dec: "dec", PMRA: "pmra", PMDec: "pmdec", Magnitude: "V", + PMRAIncludesCosDec: true, Band: catalog.BandV, + Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: basic.CoordinateFrameJ2000, +}, catalog.ReadOptions{Strict: true}) +if err != nil { + panic(err) +} +entry, found := stars.Lookup("Sirius") +_ = entry.Coordinate +_ = found +``` + +Units and required fields: + +- `RA` and `Dec` default to degrees; `AnglesInRadians` selects radians. Proper motions are mas/year, parallax mas and radial velocity km/s. +- Set `PMRAIncludesCosDec: true` if PMRA already represents `dRA*cos(Dec)`; otherwise the reader multiplies by the cosine. +- Supply `Epoch` or `EpochColumn` (Julian year) and the correct `Frame`; a zero epoch is not an unknown-epoch placeholder. + +Reading behavior: + +- With `ReadOptions.Strict`, invalid records return an error; otherwise they are skipped and counted by `Skipped()`. Structural errors such as missing required headers always fail. +- `Filter` controls admission without counting rejected records as bad. +- Missing magnitudes are `NaN`; do not use `BandV`, `BandG` and `BandHp` interchangeably for a V-band visibility threshold. +- `Near` searches at each record's own epoch: normalize mixed epochs/frames first and apply proper-motion propagation for exact observing-date selection. + ## Finding Sirius and its rise time ```go @@ -60,7 +110,7 @@ func main() { Catalog coordinates have epoch J2000. `RaDecByDate` applies proper motion, precession and nutation before date-specific rise/set, altitude or constellation calculations. -Proper motion is propagated in **Julian years** (365.25 days), and `RaDecByDate` converts the civil instant to TT before taking the epoch difference. Whenever a record carries a distance (`Pc > 0`), `RaDecByJde` advances a full **three-dimensional space motion**: proper motion supplies the tangential velocity and `RadVel` the line-of-sight component, the position vector is extrapolated linearly and its direction is taken. Records without a distance fall back to two dimensions, advancing only the two angular components, which is equivalent to treating the star as infinitely distant. The two differ by second-order terms only — the curvature of the great-circle path and the change in angular scale as radial motion alters the distance; even the fastest bright star moves less than about 0.09 arcseconds from it within 26 years. +Proper motion is propagated in **Julian years** (365.25 days), and `RaDecByDate` converts the civil instant to TT before taking the epoch difference. Whenever a record carries a distance (`Pc > 0`), `RaDecByJde` advances a full **three-dimensional space motion**: proper motion supplies the tangential velocity and `RadVel` the line-of-sight component, the position vector is extrapolated linearly and its direction is taken. Records without a distance fall back to two dimensions, advancing only the two angular components, which is equivalent to treating the star as infinitely distant. The two differ only in second-order terms: three-dimensional propagation also changes the star's distance, so the apparent place is slightly smaller than in the two-dimensional case; even the fastest bright star moves less than about 0.09 arcseconds from it within 26 years. ## API Reference @@ -70,11 +120,12 @@ Proper motion is propagated in **Julian years** (365.25 days), and `RaDecByDate` | Star catalog | `InitStarDatabase` / `StarDataByHR` / `StarDataByName` / `TopBrightStars` | Initialise the embedded catalog, look up by HR number or Chinese name, fetch the bright-star sample | HR `1-9110`; `Mag` is apparent magnitude | | Coordinate correction | `StarData.RaDecByDate` (`RaDecByJde` on the `basic` side) | Correct a J2000 position to the given instant | Returns RA/Dec in degrees | | Rise, set, culmination | `RiseTime` / `SetTime` / `CulminationTime` (`DownTime` is a deprecated alias of `SetTime`) | Rise, set and culmination instants | Civil instants; polar cases return sentinel errors | +| Rise/set with weather | `RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` | Rise and set with measured pressure (hPa) and temperature (°C) | Refraction always applied | | Topocentric quantities | `Altitude` / `ApparentAltitude` / `Azimuth` / `Zenith` / `ApparentZenith` | (Apparent) altitude, azimuth, (apparent) zenith distance | Degrees | | Hour angle and parallactic angle | `HourAngle` / `ParallacticAngle` | Stellar hour angle, zenith-direction parallactic angle | Degrees | | Sidereal time | `MeanSiderealTime` / `ApparentSiderealTime` | Mean and apparent sidereal time | Hours | -The `star` package has no `...N` truncated entry points; truncated analytical series live on the matching functions of `sun`, `moon`, the planets and `coord`, where `n < 0` uses every built-in term and `n >= 0` truncates the series (see those manuals). +The `star` package has no `...N` entry points; the `n`-parameterised evaluation functions live on the matching functions of `sun`, `moon`, the planets and `coord`, with the semantics described in those manuals (`n < 0` uses the full series; under the semianalytic model every `n >= 0` uses the fast table, and Legacy truncates by `n`). The snippets below omit shared preamble variables: `date` (observation instant, civil time scale), `lon`/`lat` (observer longitude/latitude, east/north positive, degrees), `height` (observer height, **ellipsoidal**, metres), `aero` (whether refraction and apparent-radius corrections are applied). @@ -100,7 +151,7 @@ bright, _ := star.TopBrightStars() fmt.Println(len(bright), bright[0].HR) // size of the bright-star sample and the leading HR number ``` -The catalog loads lazily behind `sync.Once`; `InitStarDatabase()` only warms it up. It is idempotent and surfaces the load error explicitly, and skipping it still works because the first lookup loads the catalog automatically. +The catalog loads on first lookup; `InitStarDatabase()` only warms it up. It is idempotent and surfaces the load error explicitly, and skipping it still works because the first lookup loads the catalog automatically. `TopBrightStars()` returns 169 built-in entries around magnitude 3 or brighter, roughly ordered from brighter to dimmer. @@ -164,7 +215,7 @@ for _, s := range bright[:3] { } ``` -Run at `date = 2020-01-01 08:08:08 CST` from `115 E, 40 N` with pressure `1010 hPa` and temperature `10 C`, this prints: +Run at `date = 2020-01-01 08:08:08 CST` from `115°E, 40°N` with pressure `1010 hPa` and temperature `10 °C`, this prints: ```text 天狼 Canis Major mag=-1.46 alt=-30.180 @@ -218,11 +269,11 @@ func main() { Output: ```text -2019-12-31 19:22:56.144202053 +0800 CST // Sirius rising -2020-01-01 05:30:39.802506566 +0800 CST // Sirius setting +2019-12-31 19:22:56.32125467 +0800 CST // Sirius rising +2020-01-01 05:30:39.979518949 +0800 CST // Sirius setting 大犬座 // constellation of Sirius -6h3m46.61s // Vega right ascension in 13600 CE -84°18′27.15″ // Vega declination in 13600 CE +6h4m44.89s // Vega right ascension in 13600 CE +84°19′13.71″ // Vega declination in 13600 CE 天狼 Sirius -1.46 // first bright-star entry: Chinese name, common name, magnitude ``` @@ -310,7 +361,6 @@ if errors.Is(err, star.ERR_STAR_NEVER_RISE) || errors.Is(err, star.ERR_STAR_NEVE In the catalog `PmRA`/`PmDec` are arcseconds per year, `RadVel`/`RotVel` are km/s and `Pc` is parsecs. - **Time scale**: every public time argument and return value is a civil instant (a UTC label, equal to UT1 before 1972-01-01); see [Time Scale Declaration](map-geojson.md#time-scale-declaration). - **Coordinate convention**: the catalog is J2000; `RaDecByDate` adds proper motion, precession and nutation. Skipping it puts rise/set and constellation lookups off by a large margin (a century of precession is a degree-level shift). -- **Polar sentinel errors**: `RiseTime` returns `star.ERR_STAR_NEVER_RISE` during a polar night (the star stays below the horizon all day) and `SetTime` returns `star.ERR_STAR_NEVER_SET` during a midnight sun (it stays above it); test with `errors.Is`. `ERR_STAR_NEVER_DOWN` is a deprecated alias of `ERR_STAR_NEVER_SET`. - **Observer height**: `height` is ellipsoidal height in metres; when you only have orthometric height, add the geoid undulation first, as described under "Observer Height Convention" in the README. - **Refraction range**: the Saemundsson approximation behind `ApparentAltitude`/`ApparentZenith` only applies for true altitudes inside `(-5, 90)` degrees and contributes zero outside that range, so well below the horizon the apparent altitude equals the geometric one (the `alt=-30.180` above is the same value `Altitude` returns). diff --git a/doc/manual/en/sun-moon.md b/doc/manual/en/sun-moon.md index 4f2bf66..f97c1b6 100644 --- a/doc/manual/en/sun-moon.md +++ b/doc/manual/en/sun-moon.md @@ -4,13 +4,15 @@ `sun` and `moon` are the main chains; `lite/sun` and `lite/moon` are independent approximation implementations. +The numerical output blocks in this manual illustrate call shapes and come from runs with the default semianalytic model. The differences from Legacy are small: lunar event times by about `1-10 s`, lunar distance by about `5 km`, phase by about `1e-6`, solar and lunar angles by about `1e-5°` and rise/set instants by milliseconds, with `lite/*` unaffected. Use an explicit `Context` / `Instance` to reproduce a fixed model; see [Accuracy and performance](accuracy.md) for the quantified errors of both models. + Unless stated otherwise, angles are in degrees, apparent diameters and semidiameters are in arcseconds, `sun.EarthDistance` is in AU and `moon.EarthDistance` is in kilometers. Observing APIs generally use civil instants. Apparent-solar-time results instead represent local solar readings; see [Time scales](timescale.md). This manual covers the Sun and the Moon themselves: position, rise/set and culmination, topocentric quantities, phases and syzygies, perigee/apogee and nodes, maximum declinations, libration, apparent size, and physical ephemeris. -Eclipse geometry lives in [Solar and Lunar Eclipse Charts](eclipse.md#solar-and-lunar-eclipse-charts), occultations in [Lunar Occultation Charts](occultation.md#lunar-occultation-charts), and general topocentric and refraction conversions in [Coordinate Tools](coord.md). +Eclipse geometry lives in [Eclipses and Transits](eclipse.md#solar-and-lunar-eclipse-charts), occultations in [Lunar Occultation Charts](occultation.md#lunar-occultation-charts), and general topocentric and refraction conversions in [Coordinate Tools](coord.md). ## Contents @@ -20,7 +22,7 @@ Eclipse geometry lives in [Solar and Lunar Eclipse Charts](eclipse.md#solar-and- - [moon](#moon) - [lite/sun](#litesun) - [lite/moon](#litemoon) - - [Truncated ...N family](#truncated-n-family) + - [The `...N` family](#the-n-family) - [Usage examples](#usage-examples) - [Today's sunrise, sunset and twilight](#todays-sunrise-sunset-and-twilight) - [Moonrise, moonset and the Moon's altitude now](#moonrise-moonset-and-the-moons-altitude-now) @@ -94,7 +96,7 @@ func main() { ## API Reference -The tables below group the exported entry points of all four packages. Most evaluation entry points also have a `...N` truncated variant, described together under [Truncated ...N family](#truncated-n-family). +The tables below group the exported entry points of all four packages. Most evaluation entry points also have a `...N` variant, described together under [the `...N` family](#the-n-family). Every `time.Time` parameter is an absolute instant, and `lon`/`lat` are east-positive and north-positive. @@ -113,7 +115,7 @@ Every `time.Time` parameter is an absolute instant, and `lon`/`lat` are east-pos | `DownTime` | Sunset alias | deprecated; calls `SetTime` internally | | `CulminationTime` | Upper culmination | `time.Time` | | `MorningTwilight` / `EveningTwilight` | Morning / evening twilight | `(time.Time, error)`; the angle is usually -6 / -12 / -18 degrees | -| `Altitude` / `Zenith` / `Azimuth` / `HourAngle` | Geometric altitude / zenith distance / azimuth / hour angle | degrees, topocentric | +| `Altitude` / `Zenith` / `Azimuth` / `HourAngle` | Geometric altitude / zenith distance / azimuth / hour angle | degrees; `Altitude` / `Zenith` / `Azimuth` are topocentric, while `HourAngle` is geocentric apparent (without topocentric parallax) | | `ApparentAltitude` / `ApparentZenith` | Apparent altitude / apparent zenith distance | degrees; requires pressure in hPa and temperature in degrees Celsius | | `ParallacticAngle` | Parallactic angle (zenith direction angle) | degrees, signed | | `ApparentSolarTime` | Apparent solar time | `time.Time`, with the zone derived from longitude | @@ -178,7 +180,7 @@ Planet targets use constants `OccultationMercury` through `OccultationNeptune`. | `TrueRa` / `TrueDec` / `TrueRaDec` | Lightweight true equatorial coordinates | degrees, geocentric | | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | Lightweight apparent equatorial coordinates | degrees, geocentric | | `Distance` | Lightweight Earth-Sun distance | AU | -| `HourAngle` / `Azimuth` / `Altitude` / `Zenith` | Lightweight hour angle / azimuth / altitude / zenith distance | degrees, topocentric | +| `HourAngle` / `Azimuth` / `Altitude` / `Zenith` | Lightweight hour angle / azimuth / altitude / zenith distance | degrees; all use the geocentric apparent position, ignoring solar parallax of about 8.8 arcseconds | | `RiseTime` / `SetTime` | Lightweight sunrise / sunset | `(time.Time, error)` | | `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET` | Polar night / polar day | error values | @@ -194,11 +196,11 @@ Planet targets use constants `OccultationMercury` through `OccultationNeptune`. | `RiseTime` / `SetTime` | Lightweight moonrise / moonset | `(time.Time, error)` | | `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET` / `ERR_NOT_TODAY` | Polar night / polar day / event not on the queried date | error values | -### Truncated ...N family +### The `...N` family Most evaluation entry points of `sun` and `moon` have a `...N` variant: the name is the base name plus `N`, one extra `n int` parameter is appended, and the return shape is unchanged. -`n < 0` keeps every analytical term embedded in this repository and matches the non-`N` version; `n >= 0` truncates the series to `n` terms, which is useful for performance comparison, bulk coarse evaluation, and error-sensitivity experiments. +`n < 0` selects the full series and matches the non-`N` version. Default builds use to the semianalytic model, where all `n >= 0` select one fixed fast table: increasing `n` does not add terms. Legacy shortens its series according to `n`. Neither changes units or time zones; measured errors are in [Accuracy and performance](accuracy.md). - sun: `TrueLoN`, `TrueBoN`, `AltitudeN`, `ZenithN`, `AzimuthN`, `HourAngleN`, `ParallacticAngleN`, `ApparentAltitudeN`, `ApparentZenithN`, `DiameterN`, `SemidiameterN`, `PhysicalN`, `RiseTimeN`, `SetTimeN`, `DownTimeN`, `CulminationTimeN`, `MorningTwilightN`, `EveningTwilightN`, `ApparentSolarTimeN` - moon: `TrueLoN`, `TrueBoN`, `AscendingNodeN`, `DescendingNodeN`, `DiameterN`, `SemidiameterN`, `PhysicalN`, `TopocentricPhysicalN`, `BrightLimbPositionAngleN`, `TopocentricBrightLimbPositionAngleN` @@ -294,7 +296,7 @@ delta dRA=0.5021 dDec=-0.3978 280.8950939694744 -23.05840775453492 ``` -The Moon is close, so geocentric and topocentric positions differ by half a degree (here `0.50` degrees in right ascension and `0.40` in declination), together more than one lunar apparent diameter; anything an observer sees must go through `moon.ApparentRaDec`. The Sun, 1 AU away, has negligible parallax and is exposed geocentrically only (of course, you can also call the topocentric coordinate conversion entry points to get a topocentric position =-=). The conventions are in [Observing-angle semantics](#observing-angle-semantics). +The Moon is close, so geocentric and topocentric positions differ by half a degree (here `0.50` degrees in right ascension and `0.40` in declination), together more than one lunar apparent diameter; anything an observer sees must go through `moon.ApparentRaDec`. The Sun, 1 AU away, has negligible parallax and is exposed geocentrically only; convert with `coord.TopocentricEquatorial` when a topocentric position is needed. The conventions are in [Observing-angle semantics](#observing-angle-semantics). ### Main chain vs lite @@ -312,9 +314,9 @@ fmt.Println(litesun.RiseTime(date, lon, lat, height, true)) // l 2020-01-01 07:49:51.69717729 +0800 CST ``` -The lightweight chains mirror the main-chain shapes but not its accuracy: at this instant the altitude differs by about `0.0027` degrees and the phase by `0.0022`. +The lightweight chains mirror the main-chain shapes but not its accuracy: in the output above the main chain and `lite/*` differ by about `0.0027` degrees in altitude and `0.0022` in phase. -Over 2026 at 8 sites the mean absolute error of `lite/sun` sunrise is `0.02 min` (P95 `0.04 min`, max `0.31 min`), of `lite/moon` moonrise `0.28 min` (P95 `0.57 min`, max `1.44 min`), and `lite/moon` `Phase()` peaks at `0.00243`. +Historical comparisons against Legacy over 2026 at 8 sites give a mean absolute difference of `0.02 min` for `lite/sun` sunrise (P95 `0.04 min`, max `0.31 min`) and `0.28 min` for `lite/moon` moonrise (P95 `0.57 min`, max `1.44 min`); the maximum `lite/moon` `Phase()` difference is `0.00243`. These are model-to-model differences, not differences against an independent ephemeris. The full comparison and the truncation errors are under [Differences from the main chain and error levels](#differences-from-the-main-chain-and-error-levels) and in [Lite chains](accuracy.md#lite-lightweight-chains). @@ -384,14 +386,14 @@ func main() { Output: ```text -2020-01-01 07:22:27.960488498 +0800 CST -2020-01-01 07:49:52.413689196 +0800 CST -2020-01-01 12:47:35.933117866 +0800 CST -2020-01-01 17:45:09.188657999 +0800 CST -2020-01-01 18:12:33.624035418 +0800 CST -2020-01-01 11:52:49.860912859 +0800 CST -2020-01-01 17:36:48.811488747 +0800 CST -2020-01-01 23:26:49.313553571 +0800 CST +2020-01-01 07:22:28.91963303 +0800 CST +2020-01-01 07:49:53.401680886 +0800 CST +2020-01-01 12:47:35.933721363 +0800 CST +2020-01-01 17:45:08.557346463 +0800 CST +2020-01-01 18:12:33.02157104 +0800 CST +2020-01-01 11:52:50.013020932 +0800 CST +2020-01-01 17:36:48.954946696 +0800 CST +2020-01-01 23:26:49.448562562 +0800 CST ``` ### Sun and Moon position @@ -444,16 +446,16 @@ func main() { Output: ```text -280.01526210031136 +280.0152652754265 23.4362178391013 -RA: 18h43m34.82s Dec: -23°3′30.27″ +RA: 18h43m34.82s Dec: -23°3′30.20″ Sagittarius -Azimuth: 120.19477090015224 Altitude: 2.4014437419430097 Zenith: 87.59855625805699 -0.983292937163176 -RA: 23h18m56.24s Dec: -10°20′54.42″ +Azimuth: 120.19442333006145 Altitude: 2.398445319378493 Zenith: 87.6015546806215 +0.9832929289388871 +RA: 23h18m56.20s Dec: -10°20′54.64″ Aquarius -Azimuth: 67.63889332004852 Altitude: -45.34916937173283 Zenith: 135.34916937173284 -404238.6096080479 +Azimuth: 67.63846015645981 Altitude: -45.349642390635466 Zenith: 135.34964239063547 +404243.3922335782 ``` ## The Sun @@ -486,10 +488,10 @@ RA: 281.6786097810291 Dec: -23.00369182413533 23.438148552330773 280.83169623098 -0.08703499790144194 ``` -Every entry point in this family also has a `...N` truncated variant. The comparison below truncates at `n = 8`; with `n < 0` the result matches the non-`N` version exactly: +Every entry point in this family also has a `...N` variant. Here `n = 8` selects the semianalytic fast table (series truncation in Legacy); `n < 0` matches the non-`N` version exactly. The output below comes from the default semianalytic model: ```go -// n<0 keeps every embedded VSOP term; n>=0 truncates the series. +// n<0 selects the full series; n>=0 selects fast, or Legacy truncation. fmt.Println(sun.TrueLo(date), sun.TrueLoN(date, 8)) fmt.Println(sun.Altitude(date, lon, lat), sun.AltitudeN(date, lon, lat, 8)) fmt.Println(sun.Diameter(date), sun.DiameterN(date, 8)) @@ -721,12 +723,12 @@ func main() { Output: ```text -0.30004130960877884 // about 30% of the lunar disk is illuminated +0.3000398794307665 // about 30% of the lunar disk is illuminated 上峨眉月 // Chinese phase description -2020-01-25 05:41:58.271192908 +0800 CST // next new moon -2020-01-03 12:45:23.229190707 +0800 CST // next first quarter -2020-01-11 03:21:17.159625291 +0800 CST // next full moon -2020-01-17 20:58:23.396406769 +0800 CST // next last quarter +2020-01-25 05:41:59.860723614 +0800 CST // next new moon +2020-01-03 12:45:24.51480031 +0800 CST // next first quarter +2020-01-11 03:21:18.524373471 +0800 CST // next full moon +2020-01-17 20:58:24.686442017 +0800 CST // next last quarter ``` `Last*`, `Closest*`, and the decimal-year anchors `ShuoYue` / `FullMoon` return UTC. `ClosestConjunctionWithPlanet` finds the closest Moon-planet conjunction, with the target given by a `ConjunctionPlanet` constant: @@ -907,7 +909,7 @@ Output: ## Lite chains -`lite/sun` and `lite/moon` are independent approximation implementations: they do not depend on VSOP87 or the main chain's ELP2000/82 series, they target CPU- and memory-constrained environments, and their calling style matches the main chain. +`lite/sun` and `lite/moon` are independent approximations: they use neither the semianalytic nor Legacy tables and do not follow global model switches. They target CPU- and memory-constrained environments, and their calling style matches the main chain. Rise/set search uses fixed-step scanning plus bisection, without the main chain's high-precision nutation iteration. @@ -1000,11 +1002,11 @@ Output: The lite chains differ from the main chain in implementation and accuracy while keeping nearly the same interface shapes. -Pure evaluation entry points such as position and phase run about `8.3-27.3x` faster than the main chain, and rise/set entry points about `1.0-3.7x`, with zero heap allocation in the computation path; the rise/set scan step is `30` minutes for `lite/sun` and `15` minutes for `lite/moon`. +Performance depends on the main-chain model, epoch cache and whether a search is required; there is no universal speedup factor. Lite's listed benchmarks allocate zero times, and rise/set scan every 30/15 minutes. Current model-specific measurements are in [Accuracy and Performance](accuracy.md#performance). -Errors against `sun` / `moon` (year 2026, 8 sites) are listed below, with data from [Lite lightweight chains](accuracy.md#lite-lightweight-chains): +The following are historical comparisons against the old Legacy `sun` / `moon` chain (2026, 8 sites), not a new semianalytic validation; see [Lite lightweight chains](accuracy.md#lite-lightweight-chains): -| Capability | Mean absolute error | P95 | Max absolute error | +| Quantity | Mean absolute error | P95 | Max absolute error | | --- | --- | --- | --- | | `lite/sun` sunrise | `0.02 min` | `0.04 min` | `0.31 min` | | `lite/sun` sunset | `0.02 min` | `0.06 min` | `0.35 min` | @@ -1015,7 +1017,7 @@ Errors against `sun` / `moon` (year 2026, 8 sites) are listed below, with data f | `lite/moon` geocentric longitude | `2.41'` | `6.82'` | `9.91'` | | `lite/moon` geocentric latitude | `0.87'` | `1.83'` | `2.92'` | -Neither package provides a `...N` truncated family. On the solar side the polar-night / polar-day errors are `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET`; on the lunar side, besides `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET`, there is `ERR_NOT_TODAY`, with the same semantics as the main chain. +Neither package provides a `...N` family. On the solar side the polar-night / polar-day errors are `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET`; on the lunar side, besides `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET`, there is `ERR_NOT_TODAY`, with the same semantics as the main chain. ## Parameter and result conventions @@ -1038,6 +1040,8 @@ The full UT1 and chart-label declaration is in the [Time Scale Declaration](map- - The `height` of the rise/set entry points is the observer elevation interpreted as ellipsoidal (geodetic) height in meters, not orthometric height; see [Observer Height Convention](coord.md#observer-height) - `aero = true`: upper-limb crossing with dynamic standard refraction and the instantaneous semidiameter; `aero = false`: only the geometric center crossing +- Rise/set refraction uses the standard atmosphere (1010 hPa, 10 °C) by default. For measured weather use `sun.RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` and `moon.RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere`, passing pressure (hPa) and temperature (°C) on each call; refraction is always applied. Existing APIs are unchanged +- Body-independent true/apparent altitude conversion lives in `formula`: `formula.ApparentAltitude`, `formula.TrueAltitude`, `formula.RefractionFromTrueAltitude`, `formula.RefractionFromApparentAltitude` - `pressureHPa` and `temperatureC` of `ApparentAltitude` / `ApparentZenith` are the observed pressure (hPa) and temperature (degrees Celsius) ### Zero values and out-of-range @@ -1046,12 +1050,12 @@ The full UT1 and chart-label declaration is in the [Time Scale Declaration](map- - When no twilight exists, they return `ERR_TWILIGHT_NOT_EXISTS` - When a lunar rise/set event falls outside the queried date, they return `ERR_NOT_TODAY`, matching the "moonrise/moonset are computed for the queried civil date" semantics above - `DownTime` / `DownTimeN` are deprecated aliases of `SetTime`, and `ERR_SUN_NEVER_DOWN` / `ERR_MOON_NEVER_DOWN` are deprecated aliases of the polar-day errors; new code should not use them -- In `...N`, `n < 0` uses every embedded term and `n >= 0` truncates; `n` only changes the series length, never the returned unit or time zone +- In `...N`, `n < 0` selects the full series; all `n >= 0` select the same semianalytic fast table, while Legacy truncates according to `n`. Units and time zones never change ### Accuracy and scope -The Sun and planets use embedded VSOP87 analytical terms and the Moon uses an embedded ELP2000/82-style truncated series, covering roughly 4000 years around J2000 with no external ephemeris files. +Default builds use the embedded semianalytic model. The VSOP87 / ELP2000/82-style series remain as Legacy; neither requires external files. An available range is not the same as a measured range, and a measured error only holds for the measured samples. -Truncation errors, the lunar chain's capability boundaries, and the lite chains' quantified errors are documented in [Scope And Accuracy](accuracy.md). +Model-specific DE440 / DE441 Sun and Moon comparisons, fast-table errors and Lite limitations are documented in [Accuracy and performance](accuracy.md). The solar and lunar entry points in this manual suit calendars, observing aids, outreach, and amateur prediction; spacecraft navigation, precise occultation prediction, and rigorous dynamical integration require a professional ephemeris such as JPL DE. diff --git a/doc/manual/en/sundial.md b/doc/manual/en/sundial.md index 1b5957a..9366723 100644 --- a/doc/manual/en/sundial.md +++ b/doc/manual/en/sundial.md @@ -118,7 +118,7 @@ p := dial.ShadowPointByHourAngleDeclination(-45, 23.44) fmt.Println(p.X, p.Y, p.Illuminated) ``` -The four `PlanarDial` fields are latitude, plate-normal azimuth, normal zenith distance and stylus length. `Geometry` returns the dial centre (where the polar stylus is fixed), the polar-stylus length and its angle to the plate; `ShadowPointByHourAngleDeclination` takes a signed hour angle and the solar declination, while `ShadowPointAt` takes the instant and longitude instead. +The four `PlanarDial` fields are latitude, plate-normal azimuth, normal zenith distance and stylus length. `Geometry` returns the dial center (where the polar stylus is fixed), the polar-stylus length and its angle to the plate; `ShadowPointByHourAngleDeclination` takes a signed hour angle and the solar declination, while `ShadowPointAt` takes the instant and longitude instead. ### Plate illumination intervals @@ -145,7 +145,7 @@ segsAt := dial.DeclinationCurveAt(date, 1.0) fmt.Println(len(segs), len(segsAt)) ``` -A time line projects the equal-instant points of a mean-solar-time line straight onto the dial as `TimeLineSample` values; a declination curve samples the plate for a fixed declination or for the declination of the day, and each segment's `Interval` is the usable hour-angle interval described above. +A time line projects the equal-instant points of a mean-solar-time line straight onto the dial as `TimeLineSample` values; a declination curve samples the plate for a fixed declination or for the declination of the day, and each segment's `Interval` is the usable hour-angle interval for that declination. ### Equatorial, horizontal and vertical dials @@ -165,7 +165,7 @@ Each constructor sets the plate normal differently; when drawing, read it from t | `HorizontalDial` | `180°` | `0°` (normal points at the zenith; x east, y north) | | `EquatorialNorthDial` | `0°` | `90° - latitude` | | `EquatorialSouthDial` | `180°` | `90° + latitude` | -| `VerticalDial` | argument normalised to `[0°, 360°)` | `90°` | +| `VerticalDial` | argument normalized to `[0°, 360°)` | `90°` | In the northern hemisphere the north-face equatorial dial serves the spring/summer half-year (positive solar declination) and the south face the autumn/winter half-year. @@ -178,9 +178,9 @@ For `VerticalDial` the normal azimuth runs from north toward east, so a south-fa | `PlanarShadowPoint` | `X` / `Y` | Shadow-point coordinates in the stylus-length unit | | | `DenominatorQ` | Projection denominator; approaching zero means the shadow runs to infinity | | | `SunAboveHorizon` / `PlaneIlluminated` / `Illuminated` | Sun above the horizon, plate lit, and the final combined test | -| `PlanarGeometry` | `CenterX` / `CenterY` | Dial centre (where the polar stylus is fixed) | +| `PlanarGeometry` | `CenterX` / `CenterY` | Dial center (where the polar stylus is fixed) | | | `PolarStylusLength` / `PolarStylusPlaneAngle` | Polar-stylus length and its angle to the plate | -| | `HasFiniteCenter` | False when the centre degenerates to infinity; the related quantities are `NaN` | +| | `HasFiniteCenter` | False when the center degenerates to infinity; the related quantities are `NaN` | | `HourAngleInterval` | `Start` / `End` | Signed hour-angle interval in degrees, `Start <= End` | | `TimeLineSample` | `Date` / `Declination` / `HourAngle` / `Point` | Instant, solar declination, apparent hour angle and the shadow point | | `DeclinationCurveSegment` | `Declination` / `Interval` / `Samples` | Segment declination, usable hour-angle interval and sample chain | @@ -214,8 +214,8 @@ func main() { Output: ```text -2026-06-21 09:34:10.438158222 +0805 LTZ -hour angle=-36.456508 line@9am=-27.405871 line@now=-20.959182 +2026-06-21 09:34:10.404156224 +0805 LTZ +hour angle=-36.456649 line@9am=-27.405871 line@now=-20.959281 ``` The zone in the first line is a synthetic local apparent solar time zone (`+08:05`, `LTZ`, for longitude `121.4737`), so the printed value already shows how far local apparent solar time is from clock time. @@ -239,7 +239,7 @@ decl=23.44 usable=-105.24..105.24 samples=211 2 -37.92998436772365 ``` -The first line says that with the solar declination at `23.44` degrees, a horizontal dial at latitude `31.2304` is usable over hour angles `[-105.24, +105.24]` (about 14 hours) sampled at 211 points; the second gives the two samples of the "local mean solar time 9:30" time line and the first hour angle. Together the two quantities are enough to draw a horizontal dial with its usable range marked. +The first line says that with the solar declination at `23.44` degrees, a horizontal dial at latitude `31.2304` is usable over hour angles `[-105.24, +105.24]` (about 14 hours) sampled at 211 points; the second gives the two samples of the "local mean solar time 9:30" time line and the first hour angle. The usable interval can be used directly as the drawing clip range for the time lines. ### Common pitfalls @@ -323,7 +323,7 @@ fmt.Println(v.PlaneNormalAzimuth, v.PlaneNormalZenithDistance) 180 90 ``` -- **Degenerate case**: when the plate normal is perpendicular to the polar axis (equivalently the polar stylus is parallel to the plate), `Geometry().HasFiniteCenter` is `false`, `CenterX`/`CenterY`/`PolarStylusLength` are `NaN` and `PolarStylusPlaneAngle` is 0 - latitude `45`, normal azimuth `180` and normal zenith distance `45` is exactly such a point, and nothing drawn relative to the centre is usable. +- **Degenerate case**: when the plate normal is perpendicular to the polar axis (equivalently the polar stylus is parallel to the plate), `Geometry().HasFiniteCenter` is `false`, `CenterX`/`CenterY`/`PolarStylusLength` are `NaN` and `PolarStylusPlaneAngle` is 0 - latitude `45`, normal azimuth `180` and normal zenith distance `45` is exactly such a point, and nothing drawn relative to the center is usable. - **Per-face conventions**: a horizontal plate's normal points at the zenith (zenith distance `0`); a vertical plate has zenith distance `90` and its `planeNormalAzimuth` is the **normal** direction (south-facing wall `180`, east-facing wall `90`); the equatorial north and south faces use `90 - latitude` and `90 + latitude`. The full table of the four constructors is under [Equatorial, horizontal and vertical dials](#equatorial-horizontal-and-vertical-dials). @@ -339,9 +339,7 @@ fmt.Println(v.PlaneNormalAzimuth, v.PlaneNormalZenithDistance) `ZoneTimePoint` / `ZoneTimeLine` ignore the hour, minute and second of `date`, keep only its date and zone, and substitute the `zoneTimeHours` argument for the clock reading. Passing the wrong convention shifts the whole time line. - **Three booleans gate a valid shadow**: `SunAboveHorizon` says the Sun is up, `PlaneIlluminated` says the plate faces the Sun, and `Illuminated` is the final test requiring both; draw a shadow only when `Illuminated` is true. -- **Degenerate case**: when the plate normal is perpendicular to the polar axis (equivalently, when the polar stylus is parallel to the plate), `PlanarGeometry.HasFiniteCenter` is `false`, `CenterX`/`CenterY`/`PolarStylusLength` are `NaN` and `PolarStylusPlaneAngle` is `0` - the centre is at infinity and nothing drawn relative to it is usable. - - Latitude `45`, normal azimuth `180` and normal zenith distance `45` is exactly such a point. +- **Degenerate case**: when the plate normal is perpendicular to the polar axis, `HasFiniteCenter` is `false`, `CenterX`/`CenterY`/`PolarStylusLength` are `NaN`, the center is at infinity and nothing drawn relative to the center is usable; see [Degenerate geometry and per-face dial conventions](#degenerate-geometry-and-per-face-dial-conventions). - **`PlaneIlluminated` versus `Illuminated`**: the former is geometric only, the latter also requires the Sun above the horizon; use the latter when answering "how long is this dial usable in a day". ## Related manuals diff --git a/doc/manual/en/timescale.md b/doc/manual/en/timescale.md index a39fc1f..f971a6a 100644 --- a/doc/manual/en/timescale.md +++ b/doc/manual/en/timescale.md @@ -82,6 +82,14 @@ func main() { } ``` +```text +UTC: 2026-04-01T00:00:00Z +TT: 2026-04-01 00:01:09.184014 +UT1: 2026-04-01 00:00:00.051015 +DUT1: 0.051000 s +UTC from TT: 2026-04-01T00:00:00Z +``` + Conversions pass through floating-point Julian days, so round trips can have small rounding differences. The TT/UT1 display formats omit `Z` to avoid presenting them as UTC timestamps. ## Julian-day conversion API @@ -129,7 +137,7 @@ A single `float64` JD has a resolution of about 40 µs near modern dates; the `t ## ΔT models -The default combines SMH2016 and Morrison 2021 splines and long-term extrapolation, using a monthly observed ΔT table wherever available. The table currently extends to September 1, 2026; the last point is a rapid observation, not yet a final solution. Constant endpoint adjustments keep extrapolation continuous. +The default combines SMH2016 and Morrison 2021 splines and long-term extrapolation, using a monthly observed ΔT table wherever available. The table currently extends to October 1, 2026; the September 1 point is final, while the last point is a preliminary extension. Constant endpoint adjustments keep extrapolation continuous. `DeltaT` returns `NaN` outside ±40000 years. @@ -173,6 +181,12 @@ func main() { } ``` +```text +smh2016 86.98252070239269 +espenak_meeus_2006 202.7448354310582 +nasa_canon_2006 202.7448354310582 +``` + Future ΔT is uncertain. The current Espenak–Meeus 2006 and default models differ by about 11, 21, 116 and 275 seconds in 2035, 2050, 2100 and 2200. This affects future eclipse times and ground paths. Match ΔT models before comparing eclipse catalogs. @@ -194,19 +208,27 @@ These settings affect subsequent calculations throughout the process. Configure ## UTC assumptions beyond the observed interval -`SetTimeScaleFuturePolicy` and `GetTimeScaleFuturePolicy` select the civil-time conversion policy, defaulting to `TimeScaleLeapSecond`. All policies apply only after the observed interval except `TimeScaleUT1Civil`, which replaces the scale throughout the timeline. An explicit TT−UTC override takes precedence from 1972 onward. This setting is separate from the output-scale options `TimeScaleUTC` and `TimeScaleUT1`. +> **UTC stops inserting leap seconds on 20 May 2027 (leap hour replaces leap second)** +> +> On 15 October 2026 the 28th General Conference on Weights and Measures (CGPM) adopted [Resolution 5, "On the technical actions needed to ensure the continuity of UTC"](https://www.bipm.org/en/cgpm-2026/resolution-5) (`Resolution-CGPM-28-5`, DOI `10.59161/CGPM2026RES5E`): **continuous UTC becomes effective on 20 May 2027** and the maximum value of `|UT1−UTC|` is widened from 0.9 seconds to **3600 seconds (1 hour)**. +> +> [IERS Bulletin C 72 (2026-07-06)](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) confirms that **no leap second will be introduced at the end of December 2026**; the leap-second mechanism ends on 2027-05-20, so the possible insertion point at the end of June 2027 will not carry one either. +> +> This library therefore changes the default policy beyond the built-in window (which ends 2026-10-01) to `TimeScaleLeapHour`: 1972–2026 still uses the definitive leap-second table and those historical values are unchanged; past the window TT−UTC stays at 69.184 seconds until `|UT1−UTC|` approaches one hour, centuries from now. The 1900–2026 span of the modern lunisolar fast tables is consequently bit-identical as well. + +`SetTimeScaleFuturePolicy` and `GetTimeScaleFuturePolicy` select the civil-time conversion policy, defaulting to `TimeScaleLeapHour`. All policies apply only after the observed interval except `TimeScaleUT1Civil`, which replaces the scale throughout the timeline. An explicit TT−UTC override takes precedence from 1972 onward. This setting is separate from the output-scale options `TimeScaleUTC` and `TimeScaleUT1`. | Policy | Assumption | | --- | --- | -| `TimeScaleLeapSecond` (zero value, default) | Applies the fewest integer-second corrections to the final TT−UTC offset to bring extrapolated DUT1 within ±0.9 seconds | +| `TimeScaleLeapHour` (zero value, default) | Applies the fewest whole-hour corrections to bring DUT1 within ±3600 seconds; matches the continuous UTC effective 2027-05-20 | +| `TimeScaleLeapSecond` | Applies the fewest integer-second corrections to the final TT−UTC offset to bring extrapolated DUT1 within ±0.9 seconds; the definitive 1972–2026 convention, kept for reproduction and comparison | | `TimeScaleAssumeUT1Tracking` | Holds the final observed DUT1 constant, allowing TT−UTC to follow extrapolated ΔT smoothly | | `TimeScaleFreezeUTCOffset` | Holds TT−UTC at its final built-in value, currently 69.184 seconds | -| `TimeScaleLeapHour` | Applies the fewest whole-hour corrections to bring DUT1 within ±3600 seconds; intended for scenario calculations | | `TimeScaleUT1Civil` | Treats civil time as UT1 throughout the timeline, including the historical leap-table interval; DUT1 is zero unless explicitly overridden | -These are calculation assumptions, not predictions of future leap seconds or international decisions. Integer-second or hour corrections depend on ΔT at the queried instant; they do not track prior corrections or restrict steps to announcement calendar boundaries. Pointwise round trips need not be identical near a step. A stepping policy returns `NaN` when its ΔT is invalid or outside the model range, rather than falling back to a normal offset. +The default policy `TimeScaleLeapHour` follows the continuous UTC that takes effect on 2027-05-20, so it is no longer a scenario assumption; `TimeScaleLeapSecond` is kept to reproduce the definitive 1972–2026 convention or for comparison. Whole-hour corrections depend on ΔT at the queried instant; they do not track prior corrections or restrict steps to announcement calendar boundaries. Pointwise round trips need not be identical near a step. A stepping policy returns `NaN` when its ΔT is invalid or outside the model range, rather than falling back to a normal offset. -The default does not guarantee an error below 0.9 seconds against real future UTC. Precise civil timing requires published data for the relevant date. +Under `TimeScaleLeapHour` the `|UT1−UTC|` tolerance is 3600 seconds, so TT−UTC past the window stays at 69.184 seconds for a very long time; `TimeScaleLeapSecond` still extrapolates against ±0.9 seconds and reproduces the old one-to-two-year stepping, which no longer reflects reality. Precise civil timing requires published data for the relevant date. ## SVG, GeoJSON and KML labels diff --git a/doc/manual/ephemeris.md b/doc/manual/ephemeris.md new file mode 100644 index 0000000..d03bdb7 --- /dev/null +++ b/doc/manual/ephemeris.md @@ -0,0 +1,219 @@ +# 显式星历实例 + +[English](en/ephemeris.md) | [返回 README](../../README.md) + +包级函数使用进程当前的星历模型,默认是半解析模型。要在同一个进程里并用多套模型,或接入外部星历(SPK、自研引擎),就用 `astro.Context` 把星历固定下来;需要直接读取几何状态(位置和速度)时,再用底层的 `astro.Instance`。 + +## 目录 + +- [换用另一套内置模型](#换用另一套内置模型) +- [用 Context 求坐标、高度与升落](#用-context-求坐标高度与升落) +- [按日期查询事件](#按日期查询事件) +- [接入外部星历](#接入外部星历) +- [多源叠加](#多源叠加) +- [引擎契约](#引擎契约) +- [限制](#限制) + +## 换用另一套内置模型 + +`astro.NewContext` 创建独立上下文,模型在创建时固定: + +```go +import ( + "fmt" + "time" + + "b612.me/astro" +) + +date := time.Date(2026, 2, 17, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + +semi, err := astro.NewContext(astro.ModelSemianalytic) // 默认构建使用的模型 +if err != nil { + panic(err) +} +legacy, err := astro.NewContext(astro.ModelLegacy) +if err != nil { + panic(err) +} + +ra1, dec1 := semi.Sun().ApparentRaDec(date) +ra2, dec2 := legacy.Sun().ApparentRaDec(date) +fmt.Printf("%.8f %.8f\n", ra1, dec1) +fmt.Printf("%.8f %.8f\n", ra2, dec2) +``` + +```text +330.64554489 -11.99819781 +330.64554785 -11.99820252 +``` + +上下文一旦创建就固定模型,之后的 `astro.SetModel` 不会影响它。`SetModel` 改的是包级函数的默认模型,只接受两个内置模型,其他模型返回 `astro.ErrUnknownModel` 且不改动全局状态;自定义引擎必须显式传给 `Context` 或 `Instance`。`NewContext` 对未知模型返回 `ErrUnknownModel`,引擎个数或取值不符(内置模型带引擎、`ModelCustom` 不是恰好一个非 nil 引擎)返回 `ErrInvalidEngine`;`NewModelInstance` 只接受内置模型。 + +## 用 Context 求坐标、高度与升落 + +访问器返回的方法与同名包级函数参数、返回值一致,把 `sun.Xxx` 换成 `ctx.Sun().Xxx` 即可: + +```go +ctx, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +date := time.Date(2026, 2, 17, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) +lon, lat := 116.4, 39.9 + +// 太阳:地心视赤经/赤纬,站心高度角/方位角 +ra, dec := ctx.Sun().ApparentRaDec(date) +alt := ctx.Sun().Altitude(date, lon, lat) +az := ctx.Sun().Azimuth(date, lon, lat) +fmt.Printf("太阳 ra=%.6f dec=%.6f alt=%.6f az=%.6f\n", ra, dec, alt, az) + +// 月球:地心坐标用 GeocentricApparentRaDec,站心量直接带经纬度 +moonRa, moonDec := ctx.Moon().GeocentricApparentRaDec(date) +moonAlt := ctx.Moon().Altitude(date, lon, lat) +moonRise, err := ctx.Moon().RiseTime(date, lon, lat, 0, true) +fmt.Printf("月球 ra=%.6f dec=%.6f alt=%.6f 升起=%s err=%v\n", + moonRa, moonDec, moonAlt, moonRise.Format("15:04:05"), err) + +// 行星按包名取访问器 +marsRa, marsDec := ctx.Mars().ApparentRaDec(date) +marsAlt := ctx.Mars().Altitude(date, lon, lat) +fmt.Printf("火星 ra=%.6f dec=%.6f alt=%.6f\n", marsRa, marsDec, marsAlt) +``` + +```text +太阳 ra=330.645545 dec=-11.998198 alt=37.682918 az=171.221286 +月球 ra=327.197499 dec=-14.621205 alt=34.596340 升起=06:59:57 err= +火星 ra=322.193276 dec=-16.023967 alt=34.060823 +``` + +时刻为 2026-02-17 12:00 CST,观测点东经 116.4°、北纬 39.9°。`RiseTime` 在当天当地民用日内找不到升落时返回错误,不是零值时刻。 + +| 入口 | 所属包 | 用途 | +| --- | --- | --- | +| `Sun()` | `sun` | 地心坐标、站心高度/方位/天顶距、升落与朦影、视直径、物理星历 | +| `Moon()` | `moon` | 地心与站心坐标、高度/方位、升落、月相、地心距、天平动 | +| `Mercury()` … `Neptune()` | 各行星包 | 视坐标、高度/方位、升落、相位与视星等、冲合和大距等事件 | +| `Eclipse()` | `eclipse` | 日月食查询、地方接触时刻、食带路径 | +| `Phenomena()` | `phenomena` | 黄经穿越、留、最近角距、任意高度升落、偕日可见 | +| `GeoJSON()` | `geojson` | 天象地图数据 | + +`...N` 方法走快速表,例如 `ctx.Sun().AltitudeN(date, lon, lat, n)`、`ctx.Moon().TrueLoN(date, n)`;`n` 的含义见[精度与性能](accuracy.md)。 + +## 按日期查询事件 + +事件查询也挂在模块上: + +```go +date := time.Date(2026, 3, 3, 12, 0, 0, 0, time.FixedZone("CST", 8*3600)) + +info, ok := ctx.Eclipse().LunarEclipseOnDate(date) +next := ctx.Eclipse().NextLunarEclipse(date) +fmt.Println(ok, info.Maximum.Format("2006-01-02 15:04 MST"), info.Type) +fmt.Println(next.Maximum.Format("2006-01-02 15:04 MST"), next.Type) +``` + +```text +true 2026-03-03 19:33 CST total +2026-03-03 19:33 CST total +``` + +日期当天有月食时 `ok` 才为 `true`;示例日期 2026-03-03 当天有月食。筛选与搜索均使用该上下文的实例。内置模型的沙罗表按实例模型选择;自定义引擎可以描述任意轨迹,因此不推断沙罗编号,结果中 `HasSaros` 为 `false`。 + +## 接入外部星历 + +`spk.Open` 读取 JPL SPK 核,作为 `astro.Engine` 传给 `NewContext`,之后的调用方式和内置模型完全相同: + +```go +import "b612.me/astro/planet/spk" + +kernel, err := spk.Open("de440s.bsp") +if err != nil { + panic(err) +} +defer kernel.Close() + +custom, err := astro.NewContext(astro.ModelCustom, kernel) +if err != nil { + panic(err) +} + +// 沿用上面的 date(2026-02-17 12:00 CST)与经纬度 +ra, dec := custom.Sun().ApparentRaDec(date) +alt := custom.Sun().Altitude(date, lon, lat) +marsRa, marsDec := custom.Mars().ApparentRaDec(date) +marsAlt := custom.Mars().Altitude(date, lon, lat) +moonAlt := custom.Moon().Altitude(date, lon, lat) +fmt.Printf("太阳 ra=%.6f dec=%.6f alt=%.6f\n", ra, dec, alt) +fmt.Printf("火星 ra=%.6f dec=%.6f alt=%.6f\n", marsRa, marsDec, marsAlt) +fmt.Printf("月球 alt=%.6f\n", moonAlt) +``` + +```text +太阳 ra=330.645545 dec=-11.998198 alt=37.682918 +火星 ra=322.193276 dec=-16.023967 alt=34.060823 +月球 alt=34.596325 +``` + +同一时刻与内置半解析基本一致,这里月球高度差约 `0.05″`。 + +需要原始几何状态(位置和速度)时改用 `Instance`: + +```go +inst := astro.NewInstance(kernel) +jdTDB := calendar.Date2JD(astro.TDBFromTT(date)) +pos, vel, err := inst.State(astro.BodyMoon, astro.BodyEarth, jdTDB) +start, end, ok := inst.Coverage(astro.BodyMoon) +fmt.Printf("pos=%.1f,%.1f,%.1f km\nvel=%.1f,%.1f,%.1f km/日\nerr=%v coverage=%.1f..%.1f ok=%v\n", + pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], err, start, end, ok) +``` + +```text +pos=312305.1,-204135.4,-98208.0 km +vel=48600.2,64310.3,36543.8 km/日 +err= coverage=2396752.5..2506352.5 ok=true +``` + +`State` 接收 TDB 历元,返回 ICRF 几何位置(km)与速度(km/日)。`Coverage` 给出该天体的可用历元区间,越界返回 `ErrEphemerisOutOfRange`。 + +## 多源叠加 + +`astro.NewStackInstance` 按先后顺序合成多个引擎,先传入的源优先。小天体核常常只给“目标相对太阳”的状态,Stack 会跨源拼出目标相对太阳系质心的状态: + +```go +de440, _ := spk.Open("de440s.bsp") +ceres, _ := spk.Open("ceres.bsp") +defer de440.Close() +defer ceres.Close() + +stack := astro.NewStackInstance(de440, ceres) + +// 小天体核的覆盖往往很窄,查询历元必须落在覆盖内 +smallDate := time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC) +jdTT := calendar.Date2JD(astro.TTFromUTC(smallDate)) +dir, dist, tau, err := stack.Apparent(astro.Body(20000001), jdTT) +fmt.Printf("%.8f,%.8f,%.8f\ndist=%.0f km tau=%.5f 日 err=%v\n", dir[0], dir[1], dir[2], dist, tau, err) +``` + +```text +0.98097637,0.17021858,-0.09333272 +dist=417020638 km tau=0.01610 日 err= +``` + +`astro.Body` 是 `int`,任意 NAIF 编号可以直接写,Ceres 是 `20000001`。示例这份 Ceres 核只覆盖 2026-01-01 至 01-03(`stack.Coverage` 返回 `2461041.5..2461043.5`),超出覆盖返回 `ErrEphemerisOutOfRange`,不会静默改用别的源。 + +## 引擎契约 + +- `Engine.State(target, center, jdTDB)`:ICRF 几何位置(km)与速度(km/日),历元为 TDB。自定义引擎必须实现它。 +- 可选的 `ApparentEngine.Apparent(target, jdTT)`:TT 历元的地心 ICRF 单位视方向、距离(km)与光行时(日)。只实现 `State` 的引擎(如 SPK)走通用矢量视位置链:迭代光行时,加太阳引力偏折与相对论光行差;这需要引擎能给出地球、太阳和目标相对太阳系质心(`BodySSB`)的状态。 +- 引擎会被并发调用,必须只读、可重入;库不会启动 goroutine。 +- 自定义引擎的失败不回退到内置表:标量入口返回 `NaN`,月食核心的 `Maximum` 为 `NaN` 且各阶段标志为 `false`,日期查询返回未找到。 +- 标量坐标和 `basic` 月食入口按 TT 取历元;几何引擎目前以 TT 近似 TDB,需要严格 TDB 时直接调用 `Instance.State`。 + +## 限制 + +- `EphemerisLegacy` 实例只有标量入口,没有几何引擎:`Instance.State` 返回 `astro.ErrEphemerisNoEngine`,`Coverage` 与 `Apparent` 报告不可用。需要位置和速度时用半解析模型或自定义引擎。 +- 显式实例只决定用哪份星历,不保证减小二进制体积:两张内置表都会链接进程序。 +- 半解析模型的可用区间是 TT JD `-1931455.0 … 5373545.0`,越界仍外推;行星现代段为 JD `2086295.0 … 2816795.0`。误差见[精度与性能](accuracy.md)。 +- `Stack.Coverage` 是各源区间的并集,中间可能有断档,也不保证中心链(Stack 为拼接状态在源之间搜索的中心传递路径)所需的每个天体都可用;以 `State` 在具体历元的返回值为准。 +- `basic` 中支持实例的入口多数带 `WithInstance` 后缀,带 `n` 参数的入口带 `NWithInstance`,传 `nil` 表示使用进程当前的全局模型。例外是影半径入口 `LunarEclipseWithLunarLimb` 和 18 个大气升落入口,它们把 `*Instance` 放在参数末尾。一般不需要直接调用这些入口。 diff --git a/doc/manual/formula.md b/doc/manual/formula.md index 84050cd..eec50b6 100644 --- a/doc/manual/formula.md +++ b/doc/manual/formula.md @@ -2,13 +2,14 @@ [English](en/formula.md) | [返回 README](../../README.md) -`formula` 包放的是和具体日期、星历表无关的常用公式,适合科普估算、小说设定和教学演示。 +`formula` 包放的是不读取星历表的纯公式,覆盖辐射、测光、望远镜、大气和地球姿态,适合估算与观测辅助。 -它不做任何时标换算,也不读星历表:输入只有温度、波长、距离、口径、高度角这类瞬时或常量参数。高度角与天顶距的口径沿用[观测角语义](sun-moon.md#观测角语义);需要坐标层的折射修正时改用 `coord` 的[大气质量](coord.md#大气质量);整体精度与适用范围见[适用范围与精度](accuracy.md)。 +它不做时标换算,也不读取全局星历:多数输入是温度、波长、距离、口径和高度角,`TIOLocatorArcsec` 例外接收 TT 儒略日。高度角与天顶距沿用[观测角语义](sun-moon.md#观测角语义),整体精度与适用范围见[精度与性能](accuracy.md)。 ## 目录 - [星等、会合周期与黑体辐射](#星等会合周期与黑体辐射) +- [折射、消光与地球姿态](#折射消光与地球姿态) - [API 参考](#api-参考) - [黑体与辐射](#黑体与辐射) - [会合周期](#会合周期) @@ -29,6 +30,35 @@ - [零值与无效输入](#零值与无效输入) - [精度与适用范围](#精度与适用范围) +## 折射、消光与地球姿态 + +| 入口 | 输入与输出口径 | +| --- | --- | +| `ApparentAltitude` / `TrueAltitude` | 真高度/视高度互换,角度为度;气压 hPa、气温 ℃ | +| `RefractionFromTrueAltitude` / `RefractionFromApparentAltitude` | 折射量为度;分别加到真高度或从视高度扣除 | +| `ExtinctionSchaefer` | 合成 V 波段天顶消光分量及 `Total`,单位星等/大气质量 | +| `TIOLocatorArcsec` | TT 儒略日转 TIO 定位角 s′,返回角秒 | +| `PolarMotionMatrix` | `xp`、`yp`、s′ 均为角秒,矩阵按列向量将 TIRS 转为 ITRS | +| `CelestialPoleOffsetMatrix` | dX、dY 为 mas,返回一阶小角修正矩阵,左乘已有天球旋转矩阵 | + +表中的缩写:`TIO` 是地球中间原点,`TIRS` 与 `ITRS` 分别是地固中间系与地固系,`s′` 是 TIO 定位角;`CPO` 是天球中间极偏移(`dX`、`dY`),单位 `mas`(毫角秒)。 + +```go +apparent := formula.ApparentAltitude(5, 1010, 10) +trueAltitude := formula.TrueAltitude(apparent, 1010, 10) +extinction := formula.ExtinctionSchaefer(100, 20, 50, 39.9, 120) +polarMotion := formula.PolarMotionMatrix(0.1, 0.2, formula.TIOLocatorArcsec(2460000.5)) +_ = trueAltitude +_ = extinction.Total +_ = polarMotion +``` + +折射默认常量是 1010 hPa、10 ℃;气压必须为正、气温高于绝对零度,输入必须有限。真/视高度互换使用同一折射模型,逆解不存在时返回 `NaN`;高度 ≤−5° 或 ≥90° 时折射量为 0。这是经验模型边界,不是地平线附近精密观测的保证。 + +`ExtinctionSchaefer` 的高度是测站海拔(米),不同于多数观测接口的椭球高;湿度是百分比且必须在 `(0,100)`,纬度和太阳赤经为度。把 `Total` 赋给 `phenomena.HeliacalOptions.ExtinctionCoefficient` 才会生效;不赋值时可见性计算使用该选项自身的默认消光。不同波段不能直接共用 V 波段系数。 + +极移和 CPO 参数由调用者提供,本包不下载 IERS(国际地球自转与参考系服务)数据,也不会把修正自动接入默认升落、食或月掩链路。s′、xp/yp 的角秒与 dX/dY 的 mas 不可混用;返回矩阵的元素无量纲。 + ## 星等、会合周期与黑体辐射 ```go @@ -295,18 +325,18 @@ alt=90 KY=0.999712 Pickering=1.000000 plane=1.000000 - 望远镜族:口径毫米 mm;`DawesLimitArcsec`、`RayleighLimitArcsec` 返回**角秒**,不是度;`LightGatheringPowerRatio` 与 `LimitingMagnitudeEmpirical` 分别为无量纲比值与 mag。 - 大气质量族:高度角(或天顶距)为度;返回值是以天顶为 1 的无量纲相对大气质量。 - 会合周期:单位由调用者自定,两个输入必须一致,输出与之一致;本包不假定“天”。 -- 本包不产生视直径/视半径。日食、月掩手册里的视半径字段以角秒计,本包只有 Dawes/Rayleigh 两个极限角用角秒,其余角度一律为度,不要混用。 +- 本包不产生视直径/视半径。常规角度与折射量为度,Dawes/Rayleigh 和 TIO 定位角为角秒,极移输入为角秒、CPO 输入为 mas,不要混用。 ### 时标 -- 全部接口都不接收时刻,也不做任何时标换算:公式只依赖温度、波长、距离、口径、高度角等参数,与 UTC、UT1、TT 无关。 +- 本包不做时标换算;`TIOLocatorArcsec` 接收 TT 儒略日,其余公式使用调用者提供的物理量或周期长度,不读取全局时标状态。 - 会合周期给的是周期长度,不是“下一次会合的时刻”。要落到日期,需要配合行星包的会合接口与民用时刻,时标口径见[时标约定](timescale.md)。 - 本包没有 ΔT、闰秒或 UT1 修正入口;这些只在 `coord`、`eclipse`、`moon` 等依赖时刻的链路里出现。 ### 角度象限与弧度/度的边界 -- 角度参数一律为度,内部转弧度计算,返回值也回到度或角秒,不会把弧度泄漏给调用者。 -- 高度角与天顶距都限制在 `[0,90]`:地平为 `0°`、天顶为 `+90°`,天顶距与高度角互补。本包不做象限折叠,负高度(地平线以下)直接被判为无效,不会自动取绝对值或折到天顶。 +- 常规角度参数为度,极移角为角秒,CPO 为 mas;单位见对应签名说明,内部弧度不泄漏给调用者。 +- 大气质量模型的高度角/天顶距限制在 `[0,90]`,不做象限折叠;折射函数有独立的低高度边界,不能套用大气质量的输入范围。 - 天顶距 `z` 与高度角 `h` 的关系是 `z = 90° − h`;`AirmassPlaneParallel` 收 `h`,`AirmassPlaneParallelByZenithDistance` 收 `z`,两者对同一几何应给出一致结果。 - Dawes/Rayleigh 的返回值是角秒;若要与其他手册的“度”角度换算,需除以 3600。 diff --git a/doc/manual/map-geojson.md b/doc/manual/map-geojson.md index 9661bdb..732cd2e 100644 --- a/doc/manual/map-geojson.md +++ b/doc/manual/map-geojson.md @@ -7,6 +7,7 @@ ## 目录 - [导出日食 GeoJSON](#导出日食-geojson) +- [导出凌日 GeoJSON](#导出凌日-geojson) - [API 参考](#api-参考) - [常用场景](#常用场景) - [给事件选投影](#给事件选投影) @@ -70,6 +71,25 @@ func main() { 偏食可以没有中心路径,因此 `centralPath` 允许为 `nil`。本例把 GeoJSON 写到标准输出,可重定向到文件;SVG 与 KML 用法见后文。 +## 导出凌日 GeoJSON + +凌日入口接收地心 `TransitInfo`,分区口径与凌日全球图一致: + +```go +m := mercury.NextTransit(time.Date(2026, 10, 12, 0, 0, 0, 0, cst)) +data, err := geojson.MarshalMercuryTransit(m, geojson.PlanetTransitOptions{ + BoundaryPoints: 360, + IsochroneStep: 5 * time.Minute, + TimeMarkers: &geojson.TimeMarkerOptions{Step: time.Hour, Location: cst}, +}) +if err != nil { + log.Fatal(err) +} +fmt.Println(string(data)) +``` + +金星把 `mercury.NextTransit` 换成 `venus.NextTransit`、`MarshalMercuryTransit` 换成 `MarshalVenusTransit`,选项相同。`date` 早于该次凌日即可:`NextTransit` 返回其后最近的一次,取前后最近一次用 `ClosestTransit`。`TransitInfo.Valid` 为假时导出返回错误。 + ## API 参考 SVG 片段使用导入别名 `eclipsesvg "b612.me/astro/eclipse/svg"`;月掩图使用 `moonsvg "b612.me/astro/moon/svg"`。日期和时区沿用首例。 @@ -82,6 +102,7 @@ SVG 片段使用导入别名 `eclipsesvg "b612.me/astro/eclipse/svg"`;月掩 | `StarOccultationPathSVG` / `PlanetOccultationPathSVG` | 月掩全球掩带图 | 返回 `(string, error)` | | `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers` | 日食 GeoJSON | 无/带时间标记 | | `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers` / `MarshalLunarEclipseWithOptions` | 月食 GeoJSON | 同上 | +| `MarshalMercuryTransit` / `MarshalVenusTransit` | 凌日 GeoJSON | 选项见 `PlanetTransitOptions` | | `MarshalStarOccultation` / `MarshalPlanetOccultation`(及 `...WithTimeMarkers`) | 月掩 GeoJSON | 同上 | | `NewSolarEclipseShadowSolver` / `MarshalSolarEclipseShadowInstant` | 单时刻足迹及其 GeoJSON | 拖动时间轴用 | | `TimeMarkerOptions` | 时间标记 `Step`、`Location`、`TimeScale` | `Step` 零值 30 分钟,最多 1440 个 | @@ -132,7 +153,7 @@ fmt.Println(err, json.Valid(data), len(data)) - `WithTimeMarkers` 会追加 `role=time-marker` 的 Point 要素:`label` 按 `Location` 本地化显示,`time` 始终是 UTC RFC 3339。 - `Step` 零值为 30 分钟,正值至少 1 分钟,单次导出最多 1440 个标记;不需要标记就用不带后缀的 `MarshalSolarEclipse`。 -- 月食与月掩有对称入口(`MarshalLunarEclipse*`、`MarshalStarOccultation*`、`MarshalPlanetOccultation*`);GeoJSON 不携带底图、边界、样式或投影,Web Mercator 与瓦片选择由应用决定。 +- 月食与月掩有对称入口(`MarshalLunarEclipse*`、`MarshalStarOccultation*`、`MarshalPlanetOccultation*`)。 ### 时标声明与 UT1 @@ -161,7 +182,15 @@ true true 4217 - 单时刻接口只算"这一瞬间的本影/半影足迹"与站心日月几何,不产生可见带、食分线、升落边界或南北界;本影不在地球上时返回空值而不是错误。 - 插值前先比 `interp_signature`:只有相同的相邻时刻才适合按顶点插值,`closed` 翻转、段数/顶点数变化、空↔非空(U1/U4 附近)都必须改取精确几何。 -- 可降级图层用 `data-source` 标注实际几何来源,取值词表见[地图投影](#地图投影)。 +- 可降级图层用 `data-source` 标注实际几何来源,没有降级分支的图层不带该属性;合法取值见下面的图层词表。 + +可降级图层的 `data-source` 取值按出图族分组,完整含义见 `eclipse/svg` 包注释: + +- 日食边界与可见域:`partial-band-union`、`sampled-footprint-sweep`、`partial-band-contours`、`besselian-critical-envelope`、`paired-limit-chords`、`sampled-open-sweep`、`central-path-limits` +- 日食时刻与食分线:`rise-set-phase-lines`、`magnitude-contours`、`greatest-time-isochrones` +- 瞬时轮廓:`penumbral-outlines`、`central-shadow-outlines` +- 月食:`p1-p4-visibility-regions`、`p1-p4-horizon-boundaries` +- 凌日:`c1-c4-visibility-regions`、`c1-c4-horizon-boundaries`、`transit-contact-isochrones` ## 地图投影 @@ -200,9 +229,7 @@ for _, spec := range []struct { } ``` -各族的选项、图层开关与带图示例见[日食与月食手册](eclipse.md#全球见食图与月食出图)与[月掩手册](occultation.md#月掩出图);GeoJSON 侧只携带地理结果,投影由应用自行选择。 - -可降级的图层用 `data-source` 标注实际几何来源,取值词表见 `eclipse/svg` 包注释:`partial-band-union`、`sampled-footprint-sweep`、`partial-band-contours`、`rise-set-phase-lines`、`magnitude-contours`、`greatest-time-isochrones`、`besselian-critical-envelope`、`paired-limit-chords`、`sampled-open-sweep`、`central-path-limits`、`penumbral-outlines`、`central-shadow-outlines`、`p1-p4-visibility-regions`、`p1-p4-horizon-boundaries`。 +各族的选项、图层开关与带图示例见[日月食与凌日手册](eclipse.md#全球见食图与月食出图)与[月掩手册](occultation.md#月掩出图);GeoJSON 侧只携带地理结果,投影由应用自行选择。凌日的可见分区、C1/C4 视地平线与可选站心等时线也可以直接导出为 GeoJSON,见上面的凌日导出示例。 ## 时标声明 @@ -215,7 +242,7 @@ GeoJSON 侧的对应成员是 `time_scale`:UTC 口径省略该成员,UT1 口 ## GeoJSON -`geojson` 接收已经计算好的日食、月食或月掩结果,返回 `[]byte`。这段字节是完整的 UTF-8 RFC 7946 `FeatureCollection` JSON,不是图片,也不是压缩数据,可以直接写入 `.geojson`、交给 `encoding/json`,或发送给前端地图组件。 +`geojson` 接收已经计算好的日食、月食或月掩结果,返回 `[]byte`。这段字节是完整的 UTF-8 RFC 7946 `FeatureCollection` JSON,可直接写入 `.geojson`,或交给 `encoding/json` 与前端地图组件。 ```go package main @@ -264,9 +291,10 @@ func main() { - `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers` - `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers` -- `MarshalLunarEclipseWithOptions`:用 `LunarEclipseOptions` 一次给出时间标记、`SkipRoles` 与时间包络的采样档位。`SkipRoles` 可跳过时间包络和仅见半影带;跳过两个时间包络时不会执行包络采样。`EnvelopeSweepSamples` 默认 48,限制在 `[2, 192]`;`EnvelopeLongitudePoints` 默认 `max(360, boundaryPoints)`,限制在 `[12, 720]`。 +- `MarshalLunarEclipseWithOptions`:用 `LunarEclipseOptions` 一次给出时间标记、`SkipRoles` 与时间包络的采样参数。`SkipRoles` 可跳过时间包络和仅见半影带;跳过两个时间包络时不会执行包络采样。`EnvelopeSweepSamples` 默认 48,限制在 `[2, 192]`;`EnvelopeLongitudePoints` 默认 `max(360, boundaryPoints)`,限制在 `[12, 720]`。 - `MarshalStarOccultation` / `MarshalStarOccultationWithTimeMarkers` - `MarshalPlanetOccultation` / `MarshalPlanetOccultationWithTimeMarkers` +- `MarshalMercuryTransit` / `MarshalVenusTransit`:接收 `mercury.TransitInfo` / `venus.TransitInfo`,分区口径同凌日 SVG 全球图(一触、凌甚、四触太阳中心是否在视地平上,计入标准大气折射)。`BoundaryPoints` 是地平线采样点数,同时决定分区的经度列数 `max(360, BoundaryPoints)`(上限 720)。role 有 `transit-entire`、`transit-at-sunrise`、`transit-at-sunset` 三个分区,`visible-at-c1` / `visible-at-c4` 与 `c1-horizon` / `c4-horizon`,`greatest`(太阳直射点)与时间标记 `time-marker`;`IsochroneStep` 为正(至少 1 秒)时追加 `c1-isochrone` 到 `c4-isochrone` 站心等时线(掠凌无二、三触;时刻是站心接触时刻,受视差影响可落在地心一触到四触之外),某个接触的等时线不落在可见半球上时该 role 不出现;`SkipRoles` 跳过全部三个分区时不做分区计算。 - `MarshalSolarEclipseWithOptions`:用 `SolarEclipseOptions` 一次给出时间标记与 `SkipRoles`,等价于上面的日食入口再加图层裁剪。`SkipRoles` 里最常用的是 `partial-footprint`(瞬时半影轮廓);`partial-band` 是日食带边缘(食分 0 界限),通常要留着。 ### 图层筛选 @@ -320,7 +348,7 @@ GeoJSON 不携带底图、样式或投影,应用可自行选择瓦片、Web Me 只画真实阴影边缘时使用 `central-shadow-boundary`;需要填色时使用 footprint。阴影在 U1/U4 收缩为空时不输出该要素,不会退化成折线。`source_boundary_closed=true` 表示物理边界本身已经闭合。 -采样的半影足迹也带这些属性。足迹是瞬时覆盖区域,静态掩带是整场事件的包络,两者不能互相替代。 +采样的半影足迹也带这些属性。足迹只覆盖一个时刻,静态掩带覆盖整场事件,取几何时不要混用。 相邻帧可先比较 `interp_signature`,例如 `umbra-closed-seg1-pt97`。只有签名一致、分段数与顶点数对应时才适合按顶点插值;这不代表插值具有严格误差上界。闭合状态改变、反经线分段改变或空/非空切换时,应查询精确几何。 @@ -342,7 +370,7 @@ GeoJSON 不携带底图、样式或投影,应用可自行选择瓦片、Web Me 只需要事件列表时,可先用 `eclipse.SolarEclipseCandidates(start, end, options)` 获取食甚时刻、食型、中心食类型、食分、伽马和可选沙罗信息;这个结果不包含地理几何。 -固定地点的中心食搜索使用 `SearchLocalCentralSolarEclipse`,选项包括 `Kind`、`MaxYears`、`Backward`、`Geometric`、`Model`,返回 `(info, status)`。`status.Exhausted` 表示已用尽搜索范围。 +固定地点的中心食搜索使用 `SearchLocalCentralSolarEclipse`,选项包括 `Kind`、`MaxYears`、`Backward`、`Geometric`、`Model`,返回 `(info, SolarEclipseLocalSearchStatus)`,其 `Exhausted` 表示已用尽搜索范围。 `MaxYears<=0` 使用默认搜索预算(6000 次候选步进,约 992 年)。找到事件后再计算路径、足迹或 SVG,可以避免为不需要的事件生成地图数据。 @@ -406,6 +434,7 @@ func main() { | 掩星全掩与偏掩带 | `total-band`、`partial-band`、`total-band-outline`、`occultation-band` | 黄色,带半透明填充 | | 升落可见边界、月食时间包络、日食偏食带边缘 | `visibility-boundary`、`p1-horizon`、`p4-horizon`、`visible-at-p1`、`visible-at-p4`、`visible-during-eclipse`、`visible-throughout-eclipse`、日食的 `partial-band` | 橙色 | | 仅见半影的月出/月落带 | `penumbra-moonrise`、`penumbra-moonset` | 月出蓝紫、月落紫红,带半透明填充 | +| 凌日可见分区与接触线 | `transit-entire`、`transit-at-sunrise`、`transit-at-sunset`、`c1-horizon`、`c4-horizon`、`visible-at-c1`、`visible-at-c4`、`c1-isochrone` … `c4-isochrone` | 三个分区各有填充色;C1/C4 视地平与可见半球橙色;四触等时线各一色 | | 其他限界、轮廓、足迹与时间标记 | 其他 `role` | 灰色,面默认只画轮廓 | `Style` 的零值字段沿用默认样式: diff --git a/doc/manual/occultation.md b/doc/manual/occultation.md index 9b04022..442c07c 100644 --- a/doc/manual/occultation.md +++ b/doc/manual/occultation.md @@ -2,7 +2,7 @@ [English](en/occultation.md) | [返回 README](../../README.md) -月掩接口位于 `moon`,按用户给定的目标搜索,不会遍历恒星表(=-=可自行维护常见被掩星表)。固定地点接口直接接收 `start`、`end`、经度、纬度和椭球高;全球路径接口返回 WGS84 经纬度采样,可继续交给 `moon/svg` 或 `geojson`或`kml`处理。 +月掩接口位于 `moon`,按用户给定的目标搜索,不会遍历恒星表(常用被掩星表由调用方自行维护)。固定地点接口直接接收 `start`、`end`、经度、纬度和椭球高;全球路径接口返回 WGS84 经纬度采样,可继续交给 `moon/svg` 或 `geojson`或`kml`处理。 目标和接触语义分为两类: @@ -26,10 +26,12 @@ - [路径算法与轮廓](#路径算法与轮廓) - [掩甚时刻等时线](#掩甚时刻等时线) - [行星月掩](#行星月掩) +- [月缘地形修正](#月缘地形修正) - [月掩出图](#月掩出图) - [全球掩带图](#全球掩带图) - [详细版式](#详细版式) - [站心事件图](#站心事件图) + - [精密月掩剖面图](#精密月掩剖面图) - [时标口径与 UT1](#时标口径与-ut1) ## 简单示例:搜索指定地点的恒星月掩 @@ -88,12 +90,12 @@ func main() { | `moonsvg.FindLocalStarOccultationSVGs` / `FindLocalPlanetOccultationSVGs` | 搜索并渲染站心图 | 同上 | | `moonsvg.StarOccultationPathSVG` / `PlanetOccultationPathSVG` | 渲染已有全球路径 | 返回 `(string, error)` | | `moonsvg.StarOccultationDetailedSVG` / `PlanetOccultationDetailedSVG` | 一页详细版式 | 固定正射球面 | +| `moon.StarOccultationLimbProfile` / `StarOccultationLimbProfileInUT1` | 沿星迹按月缘剖面精密求解,给出全部遮挡区间与带内采样 | 必须传入剖面;结果见 `StarOccultationLimbProfileResult` | +| `moonsvg.StarOccultationLimbProfileSVG` | 精密月掩剖面图 | 选项 `StarOccultationLimbProfileSVGOptions` | | `moonsvg.StarOccultationSVGOptions` / `moonsvg.OccultationDetailedSVGOptions` | 出图选项(画布、投影、时标、标记步长) | 投影用 `MapProjection*`,时标用 `astro.TimeScale*` | | `moon.OccultationMercury` … `moon.OccultationNeptune` | 行星目标常量 | 传给行星入口 | | `moon.OccultationSearchOptions` / `moon.OccultationPathOptions` | 搜索与路径选项 | 路径选项含 `Step`、`TargetSpacingKM`、`GreatestTimeStep` | - - ## 常用场景 | 场景列表 | 入口 | 返回 | @@ -110,7 +112,6 @@ func main() { | 只要站心轨迹几何数据(不出图) | `moon.StarOccultationDiagram(info, star, diagramOptions)` | `moon.StarOccultationDiagramResult` | | 交给 GIS | `geojson.MarshalStarOccultation(path)` / `MarshalStarOccultationWithTimeMarkers(path, markerOptions)` / `MarshalStarOccultationFootprint(instant)` | `([]byte, error)` | - ### 某地今晚有没有月掩 ```go @@ -184,7 +185,6 @@ fmt.Println(err, len(svg), detailErr, len(detailed)) - 只渲染已有路径用 `StarOccultationPathSVG`;"搜索 + 渲染"一步到位用 `FindStarOccultationSVGs`(见上方两条链)。 - 画布下限:单独掩带图 `640×480`;详细版宽度下限是 `480`,但整幅版式实测要 `670×595` 以上才放得下地图、数据块与页脚,低于下限直接返回错误。 - ### 掠掩、等时线与掩带宽口径 - **掠掩没有中心线**:`HasTotalBand` 为假时全球图只画南北限,此情形不要假设存在中心线。 @@ -198,7 +198,6 @@ fmt.Println(err, len(svg), detailErr, len(detailed)) 恒星由调用者传入 `StarCoordinate`。`RA` / `Dec` 单位为度,`Epoch` 和 `Frame` 必填;自行单位为 `mas/year`,其中 `ProperMotionRACosDecMasPerYear` 使用星表常见的 `dRA*cos(Dec)` 口径。 - | 字段 | 类型 | 零值 | 合法范围与报错 | 作用 | | --- | --- | --- | --- | --- | | `ID` | `string` | `""` | 无限制 | 展示标签,出现在结果和图题里,不参与任何计算 | @@ -220,7 +219,6 @@ fmt.Println(err, len(svg), detailErr, len(detailed)) - `icrs`:坐标是 ICRS 星表位置,先过一次 ~17 mas 的框架偏差矩阵;`Epoch` 填星表历元(Hipparcos `1991.25`、Gaia `2016.0`)。 - `apparent_of_date`:坐标是**该时刻的视位置**(已含岁差、章动、光行差),`Epoch` 必须填那一刻;库会在该时刻反解回平位置再向前传播。直接从星图软件(如stellarium)显示的"当前坐标"时使用这个参数。 - 可以直接构造坐标: ```go @@ -309,10 +307,10 @@ func main() { ```text 进贤增九 total -2025-06-05 19:14:01.076 CST 2025-06-05 20:02:06.311 CST 2025-06-05 20:50:10.721 CST -altitude=75.561 visible=true -2025-06-05 17:45:28.475 CST 2025-06-05 20:02:06.300 CST 2025-06-05 22:18:49.945 CST -greatest=121.566009 6.807079 width=3582.4km center=108 +2025-06-05 19:14:03.127 CST 2025-06-05 20:02:08.397 CST 2025-06-05 20:50:12.789 CST +altitude=75.556 visible=true +2025-06-05 17:45:29.822 CST 2025-06-05 20:02:07.622 CST 2025-06-05 22:18:51.246 CST +greatest=121.561060 6.808173 width=3582.4km center=108 ``` ### 搜索与路径采样选项 @@ -333,7 +331,7 @@ greatest=121.566009 6.807079 width=3582.4km center=108 ### 路径算法与轮廓 -`OccultationPathOptions.Algorithm` 控制恒星和行星全球路径的星历分支:零值或 `moon.OccultationPathAlgorithmOptimized` 默认使用经抽检的 30 分钟节点矢量插值,保留现有站心方程、连续包络和升落曲线;`moon.OccultationPathAlgorithmExact` 在候选筛选时可使用插值,最终求解使用全项星历。优化分支在抽检不合格时回退到全项求解,超出插值时间窗时使用精确星历。 +`OccultationPathOptions.Algorithm` 控制恒星和行星全球路径的星历分支:零值或 `moon.OccultationPathAlgorithmOptimized` 用 30 分钟节点的矢量插值近似星历;插值自检不通过、或时刻超出插值时间窗时,改用完整星历重算。`moon.OccultationPathAlgorithmExact` 只在候选筛选时用插值,最终求解始终用完整星历。两者共用的站心方程、掩带边界与升落曲线不变。 抽检不是全时段严格误差证明;两个分支的几何目标相同,但不保证采样点或 GeoJSON 字节完全相同。此选项不影响仅查询事件、指定站点接触或独立单时刻月影接口,也不影响日月食。 @@ -396,15 +394,52 @@ func main() { ```text Saturn total true -2025-02-01 11:29:09.710 CST -2025-02-01 11:29:40.069 CST -2025-02-01 12:00:48.747 CST -2025-02-01 12:32:46.415 CST -2025-02-01 12:33:18.312 CST +2025-02-01 11:29:10.984 CST +2025-02-01 11:29:41.333 CST +2025-02-01 12:00:50.058 CST +2025-02-01 12:32:47.753 CST +2025-02-01 12:33:19.650 CST ``` `FindPlanetOccultationPaths` 的全球结果同时包含任意圆盘重叠的部分掩区域和整颗行星被遮住的全掩区域。`HasTotalBand` 表示是否存在全掩带,`GreatestTotalWidthKM` 是掩甚处全掩带宽;中心线、边界和启用时的瞬时足迹都带采样时刻。 +## 月缘地形修正 + +默认月面是圆盘。固定地点的恒星、行星月掩搜索可以改用 `moon/limb` 子包的 ISAS/JAXA 月缘剖面(由 SELENE/Kaguya LALT 数据加工,说明见[日月食与凌日手册](eclipse.md#月缘地形修正)):掩始、掩终及行星的内外切按月心指向目标方向上的实际月缘半径精修,掩甚、食型和候选筛选仍按圆月面。 + +```go +import "b612.me/astro/moon/limb" + +events, err := moon.FindStarOccultations(start, end, star, lon, lat, 0, + moon.OccultationSearchOptions{LunarLimb: limb.Default()}) + +// 或在上下文上统一设置 +base, err := astro.NewContext(astro.ModelSemianalytic) +if err != nil { + panic(err) +} +ctx := base.WithLunarLimb(limb.Default()) +events, err = ctx.Moon().FindStarOccultations(start, end, star, lon, lat, 0, moon.OccultationSearchOptions{}) +``` + +修正量通常为秒级,例如 2024-08-21 土星月掩各接触移动 `-1.8 s` 至 `-0.6 s`。 + +精度边界: + +- 内置剖面按天平动 1° 格网插值,单个接触的月缘余量误差约 `±0.05″`(月面上约 100 m),由 16 像素/度的地形分辨率与插值共同决定。 +- 掠掩时星迹贴着月缘走,余量只有百分之几角秒,遮挡段数与短暂的掩入、复现可能与实测不同;只保证主遮挡段的时刻与量级。例:2017-09-18 轩辕十四掠掩(希腊,IOTA 实测 5 段),本库给出 1 段,与实测最后一段的掩入、复现相差 `+0.6 s` / `-0.4 s`。 +- 需要更细的掠掩剖面时,可用更高分辨率的地形自制剖面表,经 `limb.Parse` 读入或实现 `LunarLimb` 接口接入。 + +全球月掩路径(`FindStarOccultationPaths`、`FindPlanetOccultationPaths`,用 `OccultationPathOptions.LunarLimb` 或模块设置)只修正外接触南北界: + +- 边界平移到月缘掠掩线:星迹在切点附近按直线处理,有效月缘取切点两侧 ±8° 位置角内的最高点。 +- 月球在地平线附近的点保留圆月面。 +- 行星全掩界、掩带填充、等时线与升落线仍按圆月面。 +- 掠掩带内多次掩始掩终的逐点剖面不在这里计算。 +- 2025-06-05 HR 4799 掠掩线最大移动约 `4 km`。 + +出图不需要单独开关:结果上的 `LunarLimbApplied` 记录月缘是否真正生效,`moon/svg` 按它在时标声明后写明口径。站心结果只有全部接触都按剖面收敛才置真,任一接触失败则整体保留圆月面;路径结果至少一个界线点被修正才置真。已收敛的接触不再落在圆月面上,站心图对这类结果跳过圆月面接触校验,因此内置或自定义剖面的事件都能直接出图。 + ## 月掩出图 `moon/svg` 分“搜索并渲染”和“渲染已计算结果”两组入口: @@ -416,6 +451,7 @@ Saturn total true | `StarOccultationDetailedSVG` / `PlanetOccultationDetailedSVG` | 一页详细版式 | | `FindLocalStarOccultationSVGs` / `FindLocalPlanetOccultationSVGs` | 指定地点的站心月面轨迹、白道与接触阶段图 | | `LocalStarOccultationSVG` / `LocalPlanetOccultationSVG` | 渲染已有的固定地点事件 | +| `StarOccultationLimbProfileSVG` | 精密月掩剖面图:沿星迹的月缘起伏、全部遮挡区间与掩入/复现编号 | `eclipse/svg` 的入口返回 `(string, bool)`,`moon/svg` 的入口返回 `(string, error)` 或 `([]string, error)`:月掩侧的第二个返回值是真正的错误(UT1 配非 UTC 时区、画布低于下限、路径非法),不是“画不出来”的标志。恒星按点光源处理,行星按有限圆盘处理:行星月掩的接触时刻由圆盘与月缘的几何求解,`OccultationPlanet` 决定圆盘半径;土星环既不参与接触计算、也不作为圆盘边界绘制。 @@ -497,11 +533,11 @@ if err == nil { 详细版按画布推导版式,实测最小画布 `670×595`(宽度下限 `480` 只是参数校验):地图与数据块放不下时返回 `ErrInvalidOccultationDetailedSVGOptions`(`800×600`、`1000×1414`、`1414×1000` 均可出图,`660×600`、`800×590`、`640×420`、`900×400` 会被拒绝)。 -图上标注的“掩带宽”是掩甚处南北限的地面间距 `GreatestLimitSeparationKM`(HR 4799 样例约 `3666.6 km`),与中心线横向宽度 `Greatest.WidthKM`(约 `3582.4 km`)口径不同、不可互换。 +图上标注的“掩带宽”是掩甚处南北限的地面间距 `GreatestLimitSeparationKM`,不是中心线横向宽度 `Greatest.WidthKM`,两者不可互换。 掩甚时刻等时线要在路径层显式请求 `moon.OccultationPathOptions.GreatestTimeStep`:`moon/svg` 自身不提供开关,只绘制路径结果里已有的 `GreatestTimeContours`,因此请求必须在计算路径时提出,线的位置也由核心口径决定(`GreatestTimeStep` 对齐 UTC 整刻度;要按展示时区对齐,就先换算成力学时儒略日再传给 `GreatestTimeValues`)。 -月掩的全球可见窗口通常只有数小时(HR 4799 样例为 4 小时 33 分),常用间隔比日食更密,为 15–30 分钟量级。使用 `DisableFootprints` 的紧凑掩带首次渲染会合并一次,之后同一路径走缓存。 +月掩的全球可见窗口通常只有数小时(HR 4799 样例为 4 小时 33 分),常用间隔比日食更密,为 15–30 分钟量级。使用 `DisableFootprints` 的紧凑掩带只在首次渲染时计算一次,之后同一路径复用缓存。 ### 站心事件图 @@ -515,10 +551,48 @@ localSVGs, err := moonsvg.FindLocalStarOccultationSVGs( fmt.Println(err, len(localSVGs)) ``` -本地图按指定观测者的站心几何绘制,下图沿用前文 `2025-06-05` 月掩进贤增九(HR 4799)的样例。局地图的观测点为 `121.56601°E, 6.80706°N`,靠近全球几何掩甚点;图中的掩始、掩甚和掩终是该地点实际看到的站心接触时刻,并同时给出月面方向、白道、月高、方位和地平可见性。 +本地图按指定观测者的站心几何绘制,下图沿用前文 `2025-06-05` 月掩进贤增九(HR 4799)的样例。该图的观测点为 `121.56601°E, 6.80706°N`,靠近全球几何掩甚点;图中的掩始、掩甚和掩终是该地点实际看到的站心接触时刻,并同时给出月面方向、白道、月高、方位和地平可见性。 ![2025 月掩进贤增九指定地点见掩图](../img/lunar-occultation-hr4799-2025-06-05-local.svg) +### 精密月掩剖面图 + +上面的接触时刻都按圆月面求解。把月缘起伏逐点代入,可以得到星迹上每一刻的月缘几何和**全部**遮挡区间——深掩是掩入、复现两段,掠掩通常只有一段,月缘山谷还会把一次掩星切成多个分离区间。 + +```go +profile, err := moon.StarOccultationLimbProfile(events[0], target, limb.Default(), + moon.StarOccultationLimbProfileOptions{Step: 200 * time.Millisecond}) +fmt.Println(len(profile.Hidden), profile.Hidden[0].Start.UTC(), profile.Hidden[len(profile.Hidden)-1].End.UTC()) +samples := 0 +for _, segment := range profile.Segments { + samples += len(segment) +} +fmt.Println(profile.Step, len(profile.Segments), samples) + +profileSVG, err := moonsvg.StarOccultationLimbProfileSVG(profile, + moonsvg.StarOccultationLimbProfileSVGOptions{Width: 1000, Height: 620, Location: cst}) +fmt.Println(err, len(profileSVG)) +``` + +```text +1 2025-06-05 11:13:59.65061903 +0000 UTC 2025-06-05 12:50:11.632025241 +0000 UTC +200ms 2 191 + 30585 +``` + +![2025 月掩进贤增九精密月缘剖面图](../img/lunar-occultation-hr4799-2025-06-05-limb-profile.svg) + +横轴是时刻,每格同时标出该时刻的月缘位置角;纵轴是相对参考圆月面的高度(km)。棕色实线是恒星方向的月缘半径,红色虚线是恒星中心到月心的角距,两者相交的区间就是遮挡区间,图上用阴影标出并给出编号;`Segments` 有几段就画几个面板,右侧另附天球平面缩略图与掩入/复现清单。同一次事件的圆月面结果(`11:14:03.127` / `12:50:12.789 UTC`)与剖面结果相差数秒,这个差值就是月缘起伏带来的修正。 + +约定: + +- `info` 只取掩甚时刻与观测者,其余接触时刻由剖面重算并写入 `profile.Occultation`;它必须是 UTC 口径的结果,不能传 `...InUT1` 副本。 +- `limb` 传 `nil` 时用模块或上下文的默认剖面(`WithLunarLimb`);两边都没有设置时返回 `ErrInvalidOccultationInput`。 +- `Step` 零值为 200 ms,小于 10 ms 提升到 10 ms;每段至少 64 个采样、总数不超过 20000,越界时自动调整。**短于步长的遮挡或复现可能漏检**,要掠掩细节就把步长调小。 +- `Segments` 是月缘起伏带内的采样段(深掩两段、掠掩通常一段),`Hidden` 是全部遮挡区间,`ReliefMinKM` / `ReliefMaxKM` 是判定所用的月缘半径范围,`ReferenceRadiusKM` 是 `MeanRadiusArcsec` 背后的参考月球半径。 +- 剖面图默认 `920×620`,最小 `640×480`,更小返回 `ErrInvalidStarOccultationLimbProfileSVGOptions`;星迹全程落在起伏带外、没有任何采样时返回 `ErrEmptyStarOccultationLimbProfile`。 +- 要在 UT1 口径下标时刻,用 `moon.StarOccultationLimbProfileInUT1(profile)` 换一份副本,或给绘图选项传 `TimeScale: astro.TimeScaleUT1`(此时 `Location` 必须是 UTC)。 + ### 时标口径与 UT1 月掩图默认按 UTC 口径出图并图内声明;`TimeScale: astro.TimeScaleUT1` 出 UT1 读数与 `DUT1 = UT1−UTC` 差值,此时 `Location` 必须是 UTC,配非 UTC 时区会直接返回错误而不是画错图。 diff --git a/doc/manual/orbit.md b/doc/manual/orbit.md index 5e42456..5327d3e 100644 --- a/doc/manual/orbit.md +++ b/doc/manual/orbit.md @@ -2,7 +2,22 @@ [English](en/orbit.md) | [返回 README](../../README.md) -`orbit` 包用于按日心二体轨道根数传播天体位置,支持小行星、彗星、矮行星和自定义假想轨道。七大行星仍由各行星包使用内置 VSOP87 解析项计算。 +`orbit` 包用于按日心二体轨道根数传播天体位置,支持小行星、彗星、矮行星和自定义假想轨道。七大行星由各行星包使用当前星历模型计算,默认构建使用半解析模型,也可选择 Legacy 或自定义引擎。 + +需要时可以用 `StateWithOptions` 带行星摄动传播到目标历元: + +| `PropagationOptions` 字段 | 含义 | +| --- | --- | +| `Perturbers` | 摄动天体;一旦非空就必须同时给出星历源 | +| `Engine` / `Context` | 摄动星历源,两个字段类型相同(`StateProvider`,即 `astro.Engine`)且 `Engine` 优先;传 `*spk.File` 或自定义引擎 | +| `Step` | 最大步长,单位日 | +| `Tolerance` | 步长加倍的相对误差阈值 | + +- 选项零值严格走现有二体数学链,与 `State` 完全一致。 +- 积分器按太阳中心引力加直接项与间接项求和。`Perturbers` 只接受水星至海王星、地球、月球与地月质心(3);未知天体,或地月质心与地球/月球同时出现,都返回 `ErrUnsupportedPerturber`。 +- 积分从 `EpochJD` 起算;近日点形式只有在 `EpochJD` 为 0 时才从 `TpJD` 起算。 +- `NewEphemerisEngine` 可把这条传播链接入 `astro.NewStack`:根数与选项在创建时固定,引擎缓存的检查点不影响结果,任意查询顺序都与直接调用 `StateWithOptions` 逐位一致。 +- 摄动结果依赖根数质量和积分时长,长期高精度任务仍应优先使用 SPK。 `orbit.Elements` 支持两种常见写法: @@ -142,8 +157,8 @@ func main() { 输出结果: ```text -ceres ra=7.739532 dec=-10.625981 distance=2.164391 -halley ra=312.112360 dec=-11.826451 distance=1.533936 +ceres ra=7.743516 dec=-10.625287 distance=2.164391 +halley ra=312.106763 dec=-11.828059 distance=1.533936 ``` 轨道根数本身有历元,离历元越远,静态根数误差越明显。若数据源提供 `ADot/EDot/IDot/OmegaDot/WDot/MDot` 这类长期线性变化率,也可以填入 `Elements`,用于减轻中长期漂移。 @@ -209,7 +224,7 @@ fmt.Printf("r=%.6f delta=%.6f elong=%.6f phase=%.6f k=%.6f mag=%.3f q=%.6f\n", 日距角接近 180° 时相位角接近 0°,`IlluminatedFraction` 接近 1;这三个量都由同一组地心几何推出。 -`ParallacticAngle` 不另求赤纬,它和 `HourAngle` 复用同一次站心求解,避免时角与赤纬来自两条相差约 1 ULP 的儒略日路径而引入亚纳度漂移。几何量都只依赖 `date` 的绝对时刻,不含观测者参数;含站心参数的只有 `ParallacticAngle` 一个。 +`ParallacticAngle` 不另求赤纬,它与 `HourAngle` 复用同一次站心求解,两者严格自洽。几何量都只依赖 `date` 的绝对时刻,不含观测者参数;含站心参数的只有 `ParallacticAngle` 一个。 ### 升落与中天 @@ -328,7 +343,7 @@ fmt.Println(app.RA, app.Dec, app.Distance) 7.125008 -10.028726 2.262489 ``` -四个层次含义不同:`Heliocentric*` 是相对太阳的位置(第一个数就是日心距),`Geocentric*J2000` 是 J2000 口径的地心位置,`Astrometric*J2000` 去掉光行时、适合与星表对表,`Apparent*` 是当日视位置、观测与出图用它。需要站心视位置用 `ApparentTopocentricEquatorial(when, ceres, lon, lat, height)`。 +四个层次含义不同:`Heliocentric*` 是相对太阳的位置(第一个数就是日心距),`Geocentric*J2000` 是 J2000 口径的地心位置,`Astrometric*J2000` 已加光行时、适合与星表对表,`Apparent*` 是当日视位置、观测与出图用它。需要站心视位置用 `ApparentTopocentricEquatorial(when, ceres, lon, lat, height)`。 ### 今晚会不会升起、现在多高 @@ -413,7 +428,7 @@ NaN NaN ### 零值与越界 - `Elements` 零值不是合法轨道。经典椭圆形式要求 `A` 有限且为正、`E` 落在 `[0,1)`、`EpochJD` 与 `M0` 有限;`E >= 1` 的抛物线与双曲线只能走近日点形式,此时要求 `Q` 有限且为正、`TpJD` 有限、`E >= 0`,三个角度 `I`/`Omega`/`W` 都必须有限。 -- `Q > 0` 且 `TpJD` 有限时优先按近日点形式解释,`A`/`M0`/`EpochJD` 被忽略;此时 `ADot`/`EDot`/`IDot`/`OmegaDot`/`WDot` 都不生效,只有 `MDot` 非零时会被当作平均角速度使用。 +- `Q > 0` 且 `TpJD` 有限时优先按近日点形式解释,`A`/`M0`/`EpochJD` 被忽略;此时 `ADot`/`EDot`/`IDot`/`OmegaDot`/`WDot` 都不生效,只有 `MDot` 非零时会被当作平均角速度使用。摄动传播例外:近日点形式同时给出 `EpochJD` 时,它被当作密切历元,即积分起点。 - 根数非法时 `MeanMotion`、`MeanAnomaly` 返回 `NaN`,位置接口返回三个 `NaN`;`AsteroidMagnitudeHG` 在输入非有限或日心距、地心距、相位角非正时返回 `NaN`,H-G 相位混合项为零时返回 `+Inf`。 - `RiseTime` 与 `SetTime` 在给定当地日内找不到升/落时返回哨兵错误 `ERR_ORBIT_NEVER_RISE` / `ERR_ORBIT_NEVER_SET`,其它失败原样透传;成功时第二个返回值为 `nil`。 - `VisualBinary` 与 `VisualBinaryByYear` 在 `PeriodYears <= 0`、`SemiMajorAxis <= 0`、`Eccentricity` 不落在 `[0,1)` 或任一要素非有限时,把 `VisualBinaryPosition` 的全部数值字段填成 `NaN`。 diff --git a/doc/manual/phenomena.md b/doc/manual/phenomena.md new file mode 100644 index 0000000..b86f15c --- /dev/null +++ b/doc/manual/phenomena.md @@ -0,0 +1,243 @@ +# 通用天象与偕日可见 + +[English](en/phenomena.md) | [返回 README](../../README.md) + +`phenomena` 包按地心视黄经、视黄纬搜索任意两天体的事件,计算行星与恒星的晨昏始见、末见,另外提供任意高度升落和近日点、远日点、过交点。包级入口使用进程当前的星历模型,也可以用 `ctx.Phenomena()` 固定星历实例;只有恒星的任意高度升落按传入的视坐标计算,不读星历。 + +## 目录 + +- [快速示例](#快速示例) +- [天体对事件](#天体对事件) +- [恒星坐标](#恒星坐标) +- [偕日可见](#偕日可见) +- [任意高度升落](#任意高度升落) +- [近日点、远日点与过交点](#近日点远日点与过交点) +- [API 参考](#api-参考) +- [参数与返回值约定](#参数与返回值约定) + +## 快速示例 + +```go +start := time.Date(2020, 10, 1, 0, 0, 0, 0, time.UTC) +end := time.Date(2021, 3, 1, 0, 0, 0, 0, time.UTC) + +events := phenomena.ClosestApproaches(phenomena.Jupiter, phenomena.Saturn, 1, start, end) +fmt.Println(events[0].Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.4f", events[0].Value)) +``` + +```text +2020-12-21 18:21 0.1018 +``` + +`ClosestApproaches` 返回窗口内两天体角距的**局部极小**,`Value` 是当时的角距(度)。窗口内没有小于 `maxSeparation` 的极小值时返回空切片。 + +固定星历实例时换成模块方法: + +```go +ctx, err := astro.NewContext(astro.ModelLegacy) +if err != nil { + panic(err) +} +events = ctx.Phenomena().ClosestApproaches(phenomena.Jupiter, phenomena.Saturn, 1, start, end) +``` + +## 天体对事件 + +| 入口 | 事件 | `Event.Value` | +| --- | --- | --- | +| `LongitudeCrossings(body, lon, start, end)` | 视黄经经过 `lon` | 事件时刻的视黄经(度) | +| `Stations(body, start, end)` | 视黄经的留(行星) | 留时的视黄经(度) | +| `AspectCrossings(a, b, angle, start, end)` | `lon(a) − lon(b) = angle`;`0` 为黄经合,`180` 为冲 | 实际的黄经差(度) | +| `ClosestApproaches(a, b, max, start, end)` | 两天体角距的局部极小 | 角距(度) | +| `StarApproaches(body, star, max, start, end)` | 天体与恒星角距的局部极小 | 角距(度) | + +各入口只接受上表列出的参数,结果按时间升序返回。各行星包里按赤经定义的留与这里按黄经定义的留口径不同,时刻可差数小时。 + +```go +// 2026 年前两次水星留 +stations := phenomena.Stations(phenomena.Mercury, + time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range stations { + fmt.Println(e.Time.UTC().Format("01-02 15:04"), fmt.Sprintf("%.3f", e.Value)) +} +``` + +```text +02-26 06:48 352.565 +03-20 19:32 338.491 +``` + +```go +// 太阳视黄经经过 0°(春分) +crossings := phenomena.LongitudeCrossings(phenomena.Sun, 0, + time.Date(2026, 3, 18, 0, 0, 0, 0, time.UTC), time.Date(2026, 3, 24, 0, 0, 0, 0, time.UTC)) +fmt.Println(crossings[0].Time.UTC().Format("2006-01-02 15:04")) +``` + +```text +2026-03-20 14:45 +``` + +## 恒星坐标 + +行星与恒星的角距、恒星偕日可见都要传入 `StarCoordinate`,内嵌 9100 星表可以直接查: + +| 入口 | 输入 | +| --- | --- | +| `StarCoordinateByName(name)` | 星表中文名,例如 `"天狼星"` | +| `StarCoordinateByHR(hr)` | HR 编号 | +| `StarCoordinateFromStarData(star)` | `star.StarDataByHR` 等取到的星表记录 | + +```go +star, err := phenomena.StarCoordinateByName("天狼星") +fmt.Println(err, star.ID, fmt.Sprintf("%.6f %.6f %.2f %.2f", star.RA, star.Dec, + star.ProperMotionRACosDecMasPerYear, star.ProperMotionDecMasPerYear)) +``` + +```text + 天狼 101.287083 -16.716111 -553.00 -1205.00 +``` + +`RA`、`Dec` 为度,`Epoch` 与 `Frame` 必填;自行字段(`ProperMotionRACosDecMasPerYear` 等)按星表常见的 `dRA·cos(Dec)` 约定,单位毫角秒/年。`ParallaxMas`(或等价的 `DistanceLightYear`)给出距离,`RadialVelocityKmPerSecond` 给出视向速度;距离缺失时按二维自行传播,有距离时才计入视向速度。 + +## 偕日可见 + +`PlanetHeliacalEvents(body, start, end, lon, lat, options)` 与 `StarHeliacalEvents(star, magnitude, start, end, lon, lat, options)` 逐日扫描太阳高度 −18° 到 0° 的晨昏时段,按天光亮度模型求肉眼极限星等,再与目标视星等比较(行星用 Mallama & Hilton 2018 星等),返回四类事件: + +| 常量 | 含义 | +| --- | --- | +| `HeliacalMorningFirst` | 晨始见(偕日升) | +| `HeliacalMorningLast` | 晨末见 | +| `HeliacalEveningFirst` | 昏始见 | +| `HeliacalEveningLast` | 昏末见(偕日落) | + +`HeliacalEvent` 中 `Time` 是当次晨昏的最佳可见时刻,`TargetAltitude`、`SunAltitude` 是当时目标与太阳的几何高度(度),`Margin` 是极限星等减目标星等。 + +| `HeliacalOptions` | 默认 | 说明 | +| --- | --- | --- | +| `ExtinctionCoefficient` | `0.25` | 大气消光,星等/大气质量 | +| `MinTargetAltitude` | `0.5` | 目标最低几何高度(度) | +| `Moonlight` | `false` | 计入月光散射(Krisciunas–Schaefer) | + +```go +events := phenomena.PlanetHeliacalEvents(phenomena.Venus, + time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC), + 31.23, 30.04, phenomena.HeliacalOptions{}) +for _, e := range events { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02"), fmt.Sprintf("margin=%.2f", e.Margin)) +} +``` + +```text +3 2025-03-18 margin=0.10 +0 2025-03-25 margin=0.58 +1 2025-12-01 margin=0.10 +``` + +`Kind` 打印出来是常量序号,判断时用常量本身比较。`LimitingMagnitude(targetAltitude, sunAltitude, separation, extinction)` 单独给出某一时刻无月夜的极限星等,与事件判定同源: + +```go +fmt.Println(fmt.Sprintf("%.3f", phenomena.LimitingMagnitude(20, -12, 60, 0.25))) +``` + +```text +3.408 +``` + +结果对消光系数敏感,可见日期差一两天属正常。 + +## 任意高度升落 + +`AltitudeCrossing(body, date, lon, lat, height, altitude, options)` 求日、月或行星在 `date` 所在**当地民用日**内首次穿过视高度 `altitude` 的升、落时刻。`StarAltitudeCrossing(date, ra, dec, lon, lat, altitude, options)` 对恒星视坐标做同样计算,它不读星历,也没有模块方法。 + +| `AltitudeOptions` | 默认 | 说明 | +| --- | --- | --- | +| `Limb` | `LimbUpper` | 日月取上缘,也可取 `LimbCenter`、`LimbLower`;行星与恒星忽略 | +| `PressureHPa` / `TemperatureC` | 标准大气(`0` 值) | 指定实测气象 | +| `NoRefraction` | `false` | 为 `true` 时按几何高度比较 | + +| `AltitudeCrossingResult` | 说明 | +| --- | --- | +| `Rise`、`Set` | 升、落时刻,没有事件时为零值 | +| `HasRise`、`HasSet` | 当天是否存在升、落 | +| `State` | `AltitudeCrosses`、`AltitudeAlwaysAbove`、`AltitudeAlwaysBelow` 或 `AltitudeTangent`(极值与目标高度相差不足 `1″`) | +| `MinResidual`、`MaxResidual` | 当天高度与目标高度之差的最小、最大值(度) | + +```go +cst := time.FixedZone("CST", 8*3600) +r, err := phenomena.AltitudeCrossing(phenomena.Sun, time.Date(2026, 3, 21, 12, 0, 0, 0, cst), + 116.4, 39.9, 0, -18, phenomena.AltitudeOptions{}) +fmt.Println(err, r.State, r.Rise.In(cst).Format("15:04:05"), r.Set.In(cst).Format("15:04:05"), + fmt.Sprintf("%.3f %.3f", r.MinResidual, r.MaxResidual)) +``` + +```text + 0 04:44:54 19:59:16 -31.806 68.602 +``` + +```go +// 北纬 89° 极昼:太阳整天都在 -18° 之上 +polar, err := phenomena.AltitudeCrossing(phenomena.Sun, time.Date(2026, 6, 21, 12, 0, 0, 0, time.UTC), + 0, 89, 0, -18, phenomena.AltitudeOptions{}) +fmt.Println(err, polar.State, polar.HasRise, polar.HasSet, fmt.Sprintf("%.3f", polar.MinResidual)) +``` + +```text + 1 false false 40.737 +``` + +```go +// 天狼星穿过 -0.833° 的升落 +sirius, err := phenomena.StarAltitudeCrossing(time.Date(2026, 1, 1, 12, 0, 0, 0, cst), + star.RA, star.Dec, 116.4, 39.9, -0.833, phenomena.AltitudeOptions{Limb: phenomena.LimbCenter}) +fmt.Println(err, sirius.State, sirius.Rise.In(cst).Format("15:04:05"), sirius.Set.In(cst).Format("15:04:05")) +``` + +```text + 0 19:04:31 05:27:52 +``` + +`height` 只影响月球视差。需要以下沉地平为基准时,由调用方从 `altitude` 中减去地平俯角。天体不是日、月或行星(例如 `phenomena.Earth`),或恒星视坐标不是有限值时,返回 `basic.ErrInvalidObservationInput`。 + +## 近日点、远日点与过交点 + +| 常量 | 事件 | +| --- | --- | +| `Perihelion`、`Aphelion` | 近日点、远日点 | +| `AscendingNode`、`DescendingNode` | 穿过历元平黄道的升交点、降交点 | + +`Apsides(body, start, end)` 接受行星或地球(`phenomena.Earth`),`Nodes(body, start, end)` 只接受行星,传地球返回空切片。`OrbitalEvent` 的 `Time` 是事件时刻,`Kind` 是上表常量,`DistanceAU` 是该时刻的日心距(AU)。 + +```go +apsides := phenomena.Apsides(phenomena.Earth, + time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range apsides { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.9f AU", e.DistanceAU)) +} +nodes := phenomena.Nodes(phenomena.Jupiter, + time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC)) +for _, e := range nodes { + fmt.Println(e.Kind, e.Time.UTC().Format("2006-01-02 15:04"), fmt.Sprintf("%.6f AU", e.DistanceAU)) +} +``` + +```text +0 2026-01-03 09:15 0.983302049 AU +1 2026-07-06 09:30 1.016643962 AU +3 2020-02-25 18:09 5.207056 AU +2 2025-09-19 11:58 5.174312 AU +``` + +极值取日心距本身:太阳绕质心的反射运动会让天王星、海王星的日心距在近日点附近出现多个小极值,同一段出现多个极小值时只返回前后各 `0.3` 个轨道周期内日心距最小的一个,与吻切轨道近日点可相差数年。 + +## API 参考 + +`ctx.Phenomena()` 的方法与同名包级函数签名一致(`StarAltitudeCrossing` 除外);`NewWithInstance` 用给定星历实例创建模块。各入口的参数与单位见上文各节。 + +## 参数与返回值约定 + +- 角度一律为度,`OrbitalEvent.DistanceAU` 为 AU,`StarHeliacalEvents` 的 `magnitude` 为 V 星等。 +- 时间参数按绝对时刻解释(读取其 UTC 字段),返回时刻换算到 `start`(升落类为 `date`)所在的时区。 +- 事件搜索没有结果时返回空切片,不返回错误;只有任意高度升落在输入非法时返回 `basic.ErrInvalidObservationInput`。 +- 事件搜索会用到星历,可以用模块方法固定模型;`StarAltitudeCrossing`、`StarCoordinate*` 与 `LimitingMagnitude` 与星历无关。 +- 行星视星等、赤经留等口径差异见[行星手册](planets.md);日月升落与观测角语义见[太阳与月亮](sun-moon.md)。 diff --git a/doc/manual/planets.md b/doc/manual/planets.md index 3a9bb14..fcb0d92 100644 --- a/doc/manual/planets.md +++ b/doc/manual/planets.md @@ -2,12 +2,12 @@ [English](en/planets.md) | [返回 README](../../README.md) -七大行星各对应一个同名包:`mercury`、`venus`、`mars`、`jupiter`、`saturn`、`uranus`、`neptune`。它们的公开接口按同一套形状组织:以 `time.Time` 传入民用时刻,以 `float64` 返回角度或距离,事件搜索返回 `time.Time` 或结构体。内行星(水星、金星)额外提供上合/下合、大距与地心凌日;外行星(火星到海王星)额外提供冲日与方照;木星独有伽利略卫星,土星独有土星环参数。 +七大行星各对应一个同名包:`mercury`、`venus`、`mars`、`jupiter`、`saturn`、`uranus`、`neptune`。它们的公开接口按同一套形状组织:以 `time.Time` 传入民用时刻,以 `float64` 返回角度或距离,事件搜索返回 `time.Time` 或结构体。内行星(水星、金星)额外提供上合/下合、大距与地心、站心凌日;外行星(火星到海王星)额外提供冲日与方照;木星独有伽利略卫星,土星独有土星环参数。 -低层的 VSOP87 级数与日月解析级数放在 `planet` 包,被这七个包与 `sun` / `moon` 共用。 +低层内置半解析模型与 Legacy 的 VSOP87/日月级数放在 `planet` 包,被这七个包与 `sun` / `moon` 共用;默认构建使用半解析模型,可用显式 Context 固定模型。 - 七个包公共能力的函数名一致(例如都提供 `ApparentRa`、`ApparentDec` 与 `ApparentRaDec`),差异只体现在各自额外的那几族接口上;调用时必须带对应包名前缀,不能跨包混用。 -- 位置接口给出的是**地心视位置**;站心量与地平坐标单独由 `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` 提供,公式入口见[坐标工具](coord.md)。 +- 位置接口给出的是**地心视位置**;地平量单独提供:`Altitude` / `Azimuth` / `Zenith` 是站心量,`HourAngle` / `ParallacticAngle` 是地心量(不含站心视差),公式入口见[坐标工具](coord.md)。 - 事件搜索族都成对出现(`Last...` / `Next...`,部分另有 `Closest...`),一律取"当前或之前/之后最近一次"并包含端点。 - 行星本身没有独立的 SVG 出图入口;月掩相关出图(含土星环月掩)见[月掩手册](occultation.md#月掩出图)。 @@ -17,7 +17,7 @@ - [API 参考](#api-参考) - [按行星横向对照](#按行星横向对照) - [通用能力与单位](#通用能力与单位) - - [`...N` 截断族](#n-截断族) + - [`...N` 族](#n-族) - [常用场景](#常用场景) - [今晚能看到哪颗行星](#今晚能看到哪颗行星) - [冲日、合日、大距、留与逆行](#冲日合日大距留与逆行) @@ -37,7 +37,7 @@ - [与其他手册的分工](#与其他手册的分工) - [物理星历](#物理星历) - [木星伽利略卫星](#木星伽利略卫星) - - [与 `planet` 包共用的类型与常量](#与-planet-包共用的类型与常量) + - [与 `planet` 包共用的低层入口](#与-planet-包共用的低层入口) - [参数与返回值约定](#参数与返回值约定) - [时标与民用时刻](#时标与民用时刻) - [单位与口径](#单位与口径) @@ -80,7 +80,7 @@ func main() { 下表按能力对照七个行星包。调用时加包名前缀,例如 `mars.NextOpposition`;`/` 分隔并列函数,`—` 表示该包没有对应接口。 -| 能力 | `mercury` | `venus` | `mars` | `jupiter` | `saturn` | `uranus` | `neptune` | +| 计算项 | `mercury` | `venus` | `mars` | `jupiter` | `saturn` | `uranus` | `neptune` | | --- | --- | --- | --- | --- | --- | --- | --- | | 视黄经 / 视黄纬 | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | `ApparentLo` / `ApparentBo` | | 视赤经 / 视赤纬 | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | @@ -89,7 +89,7 @@ func main() { | 轨道升交点 / 降交点 | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | `AscendingNode` / `DescendingNode` | | 视直径 / 视半径 | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | `Diameter` / `Semidiameter` | | 相位角 / 照亮比例 / 亮面位置角 | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | `PhaseAngle` / `Phase` / `IlluminatedFraction` / `BrightLimbPositionAngle` | -| 站心地平量 | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | +| 地平量 | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` | | 升 / 落 / 中天 | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | `RiseTime` / `SetTime` / `DownTime` / `CulminationTime` | | 物理星历 | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` | `Physical` / `PhysicalN` / `CentralMeridians` / `CentralMeridiansN` | `Physical` / `PhysicalN` / `PhysicalSystemIII` / `Ring` | `Physical` / `PhysicalN` / `PhysicalSystemIII` | `Physical` / `PhysicalN` | | 合日 | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | `LastConjunction` / `NextConjunction` | @@ -98,23 +98,25 @@ func main() { | 冲日 | — | — | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | `LastOpposition` / `NextOpposition` | | 方照 | — | — | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | `LastEasternQuadrature` / `NextEasternQuadrature` / `LastWesternQuadrature` / `NextWesternQuadrature` | | 大距 | `LastGreatestElongation` / `NextGreatestElongation` / `LastGreatestElongationEast` / `NextGreatestElongationEast` / `LastGreatestElongationWest` / `NextGreatestElongationWest` | `LastGreatestElongation` / `NextGreatestElongation` / `LastGreatestElongationEast` / `NextGreatestElongationEast` / `LastGreatestElongationWest` / `NextGreatestElongationWest` | — | — | — | — | — | -| 地心凌日 | `LastTransit` / `NextTransit` / `ClosestTransit` | `LastTransit` / `NextTransit` / `ClosestTransit` | — | — | — | — | — | +| 凌日 | `LastTransit` / `NextTransit` / `ClosestTransit` / `LocalTransit` | `LastTransit` / `NextTransit` / `ClosestTransit` / `LocalTransit` | — | — | — | — | — | | 伽利略卫星 | — | — | — | `Satellites` / `SatellitePhenomena` / `LastGalileanPhenomenonEvent` / `NextGalileanPhenomenonEvent` / `ClosestGalileanPhenomenonEvent` / `LastGalileanPhenomenonContactEvent` / `NextGalileanPhenomenonContactEvent` / `ClosestGalileanPhenomenonContactEvent` | — | — | — | -| 结果类型 | `PhysicalInfo` / `TransitInfo` | `PhysicalInfo` / `TransitInfo` | `PhysicalInfo` | `PhysicalInfo` / `CentralMeridianInfo` / `GalileanSatellitesInfo` / `GalileanPhenomenaInfo` / `GalileanSatellitePosition` / `GalileanSatellitePhenomenon` / `GalileanPhenomenonEvent` / `GalileanPhenomenonContactEvent` | `PhysicalInfo` / `RingInfo` | `PhysicalInfo` | `PhysicalInfo` | +| 结果类型 | `PhysicalInfo` / `TransitInfo` / `LocalTransitInfo` | `PhysicalInfo` / `TransitInfo` / `LocalTransitInfo` | `PhysicalInfo` | `PhysicalInfo` / `CentralMeridianInfo` / `GalileanSatellitesInfo` / `GalileanPhenomenaInfo` / `GalileanSatellitePosition` / `GalileanSatellitePhenomenon` / `GalileanPhenomenonEvent` / `GalileanPhenomenonContactEvent` | `PhysicalInfo` / `RingInfo` | `PhysicalInfo` | `PhysicalInfo` | -七个包的**截断族**都叫同样的名字:给任意一个瞬时求值接口加 `N` 后缀即可,`n < 0` 用全部内置项、`n >= 0` 截断,详见 [`...N` 截断族](#n-截断族)。事件搜索族(`Last...` / `Next...` / `Closest...`)没有 `N` 版本。 +七个包的 **`...N` 族**都叫同样的名字:给任意一个瞬时求值接口加 `N` 后缀即可。`n < 0` 用完整级数,半解析的所有 `n >= 0` 用同一张快速表,Legacy 按 `n` 截断,详见 [`...N` 族](#n-族)。事件搜索族(`Last...` / `Next...` / `Closest...`)没有 `N` 版本。 ### 通用能力与单位 -| 能力 | 用途 | 单位与口径 | +| 计算项 | 用途 | 单位与口径 | | --- | --- | --- | | `ApparentLo` / `ApparentBo` | 地心视黄经、视黄纬 | 度;当日真春分点,含光行时、光行差与章动 | | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | 地心视赤经、视赤纬 | 度;当日真赤道;`ApparentRaDec` 一次返回两者 | | `ApparentMagnitude` | 视星等 | 星等(无量纲) | -| `Altitude` / `Azimuth` / `Zenith` / `HourAngle` | 站心地平坐标 | 度;方位角自正北向东增加,`Zenith` 等于 `90 - Altitude` | +| `Altitude` / `Azimuth` / `Zenith` | 站心地平坐标 | 度;方位角自正北向东增加,`Zenith` 等于 `90 - Altitude` | +| `HourAngle` | 地心视时角,不含站心视差 | 度 | | `RiseTime` / `SetTime` / `DownTime` | 当地民用日内的升起、落下 | `time.Time`,保持输入时区;`DownTime` 是 `SetTime` 的兼容别名 | +| `RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` | 按实测气压(hPa)、气温(℃)计算升落 | `time.Time`;恒带大气折射 | | `CulminationTime` | 上中天时刻 | `time.Time`,保持输入时区 | -| `ParallacticAngle` | 视差角(天顶方向角) | 度 | +| `ParallacticAngle` | 地心视差角(天顶方向角),由地心时角与赤纬求得 | 度 | | `Diameter` / `Semidiameter` | 地心视直径、视半径 | 角秒 | | `PhaseAngle` | 太阳–行星–地球夹角 | 度 | | `IlluminatedFraction` / `Phase` | 被照亮比例 | `0–1`;`Phase` 是 `IlluminatedFraction` 的别名 | @@ -122,16 +124,21 @@ func main() { | `EarthDistance` / `SunDistance` | 地心距、日心距 | AU | | `AscendingNode` / `DescendingNode` | 轨道面与黄道面交点的黄经 | 度;同一时刻两者相差约 `180°` | | `Physical` | 盘面朝向、子地/子日经纬度、北极位置角 | 度;经度正方向按各天体 IAU 约定 | -| `planet.WherePlanet` / `planet.WherePlanetN` | VSOP87 黄经、黄纬、日心距 | 度 / AU;越界返回 `NaN` 而不 panic | +| `planet.WherePlanet` / `planet.WherePlanetN` | 当前内置模型的黄经、黄纬、日心距 | 度 / AU;越界返回 `NaN` 而不 panic | -### `...N` 截断族 +### `...N` 族 -每个瞬时求值接口都有 `...N` 后缀版本,用来在精度与开销之间取舍:`n < 0` 使用全部内置项,与非 `N` 接口等价;`n >= 0` 时按约 `n` 个主项截断并等比缩短高阶项。事件搜索族(`Last...` / `Next...` / `Closest...`)没有 `N` 版本。 +每个瞬时求值接口都有 `...N` 后缀版本,用来在精度与开销之间取舍:`n < 0` 使用完整级数,与非 `N` 接口等价;半解析的所有 `n >= 0` 使用同一张快速表,增大 `n` 不提高精度;Legacy 按 `n` 控制主项并等比缩短高阶项。 -带 `N` 的族包括 `ApparentLoN`、`ApparentBoN`、`ApparentRaN`、`ApparentDecN`、`ApparentRaDecN`、`ApparentMagnitudeN`、`EarthDistanceN`、`SunDistanceN`、`AltitudeN`、`AzimuthN`、`ZenithN`、`HourAngleN`、`CulminationTimeN`、`RiseTimeN`、`SetTimeN`、`DownTimeN`、`ParallacticAngleN`、`DiameterN`、`SemidiameterN`、`PhaseAngleN`、`PhaseN`、`IlluminatedFractionN`、`BrightLimbPositionAngleN`、`AscendingNodeN`、`DescendingNodeN`、`PhysicalN`,以及木星的 `CentralMeridiansN`、土星的 `RingN` 与 `PhysicalSystemIIIN`、天王星的 `PhysicalSystemIIIN`。 +带 `N` 的族按用途分组: + +- 位置与距离:`ApparentLoN`、`ApparentBoN`、`ApparentRaN`、`ApparentDecN`、`ApparentRaDecN`、`EarthDistanceN`、`SunDistanceN` +- 地平与时刻:`AltitudeN`、`AzimuthN`、`ZenithN`、`HourAngleN`、`CulminationTimeN`、`RiseTimeN`、`SetTimeN`、`DownTimeN`、`ParallacticAngleN` +- 视面与相位:`ApparentMagnitudeN`、`DiameterN`、`SemidiameterN`、`PhaseAngleN`、`PhaseN`、`IlluminatedFractionN`、`BrightLimbPositionAngleN` +- 轨道交点与自转:`AscendingNodeN`、`DescendingNodeN`、`PhysicalN`,以及木星的 `CentralMeridiansN`、土星的 `RingN` 与 `PhysicalSystemIIIN`、天王星的 `PhysicalSystemIIIN` ```go -fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // 视黄经:全部内置项 / 截断到约 8 项 +fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // 完整级数 / 快速表;Legacy 按 n 截断。 fmt.Println(mars.SunDistance(date), mars.SunDistanceN(date, 8)) // 日心距,AU ``` @@ -151,7 +158,7 @@ fmt.Println(mars.Altitude(date, lon, lat), mars.Azimuth(date, lon, lat)) // 火 31.194578177219057 152.07031660415714 ``` -`Altitude` 大于 `0` 才在地平线上,方位角自正北向东增加;`aero = true` 按标准大气折射把几何地平线降到约 `-0.5667°`,逐参数口径与极区哨兵错误见[升落与中天](#升落与中天)。 +方位角自正北向东增加;`aero = true` 按标准大气折射把几何地平线降到约 `-0.5667°`,逐参数口径与极区哨兵错误见[升落与中天](#升落与中天)。 ### 冲日、合日、大距、留与逆行 @@ -182,7 +189,26 @@ true 2032-11-13 14:41:13.161198198 +0800 CST 2032-11-13 16:54:12.821315824 +0800 4h26m2.695272267s 572.0643215495325 ``` -`TransitInfo.Valid` 为假表示搜索窗口内没有凌日、其余字段是零值;凌日只判断地心几何,不判断观测地当时太阳是否在地平线上。四触、偏凌与内切口径见[大距与地心凌日](#大距与地心凌日)。 +`TransitInfo.Valid` 为假表示搜索窗口内没有凌日、其余字段是零值;`TransitInfo` 是地心几何结果。某个地点看到的接触时刻用 `LocalTransit` 求站心结果: + +```go +transit := mercury.NextTransit(time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)) +local := mercury.LocalTransit(transit, -77.0, 38.9, 0) // 华盛顿,经度、纬度、海拔米 +fmt.Println(local.Valid, local.HasInternal, local.Visible) // 站心有凌日、有内切接触、太阳在视地平上 +fmt.Println(local.Start.Time, local.Start.SunAltitude) // 站心一触与当时太阳高度 +``` + +```text +true true true +2019-11-11 12:36:09.037762284 +0000 UTC 7.933256722170642 +``` + +站心接触对太阳与行星分别做视差修正后重解,所以各地接触时刻与地心值不同:金星可达约 ±7 分钟,水星约 ±2 分钟。 + +- `SunAltitude` 是日心几何高度。 +- `Visible`、`Sunrise`、`Sunset` 按太阳中心是否在视地平上判定,计入标准大气(1010 hPa、10 °C)折射,几何阈值约 −34′。 +- 地心有内切接触、站心可能只是掠凌(例如 1999-11-15 水星凌日在南半球高纬),此时 `HasInternal` 为假,`InternalStart` / `InternalEnd` 是零值。 +- 全球可见图与站心日面图见[日月食与凌日](eclipse.md#凌日出图);四触、偏凌与内切口径见[大距与地心凌日](#大距与地心凌日)。 ### 相位、视直径、视星等与节点 @@ -198,7 +224,7 @@ fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date)) // 升降交点 49.71479005849112 229.71479005849113 ``` -`Phase` 是 `IlluminatedFraction` 的别名,取值 `0–1`;升降交点同一时刻相差约 `180°`。各量的定义、别名与截断版见[节点、相位、视星等、视直径与视差角](#节点相位视星等视直径与视差角)。 +`Phase` 是 `IlluminatedFraction` 的别名,取值 `0–1`;升降交点同一时刻相差约 `180°`。各量的定义、别名与 `n` 变体见[节点、相位、视星等、视直径与视差角](#节点相位视星等视直径与视差角)。 ### 物理星历与木星伽利略卫星 @@ -219,7 +245,7 @@ fmt.Println(sats.Io.OffsetXJupiterR, sats.Io.InFrontOfJupiter) // 木卫一偏 ### 与外部资料的对照口径 ```go -fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // 全部内置项 / 约 8 项截断的视黄经 +fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // 完整级数 / 快速表;Legacy 按 n 截断。 _, err := mars.RiseTime(date, 0, 89, 0, true) // 极区观测点 fmt.Println(errors.Is(err, mars.ERR_MARS_NEVER_RISE), errors.Is(err, mars.ERR_MARS_NEVER_SET)) ``` @@ -229,7 +255,7 @@ fmt.Println(errors.Is(err, mars.ERR_MARS_NEVER_RISE), errors.Is(err, mars.ERR_MA true false ``` -`ApparentLo` 等是**当日真春分点**的地心视位置,与外部 J2000 或平位置对表前先用 `coord.Precess` 归算;截断族 `n < 0` 用全部内置项、`n >= 0` 截断。伽利略卫星接触事件与 JPL Horizons / IMCCE 年表的口径差异见[与外部资料对照](#与外部资料对照),整体边界见[参数与返回值约定](#参数与返回值约定)。 +`ApparentLo` 等是**当日真春分点**的地心视位置。与外部 J2000 或平位置对表时,需同时统一坐标框架、章动与视位置修正,不能仅用岁差变换消除全部差异。`n` 的模型相关语义见 [`...N` 族](#n-族)。伽利略卫星接触事件与外部年表的口径差异见[与外部资料对照](#与外部资料对照),整体边界见[参数与返回值约定](#参数与返回值约定)。 ## 基础示例 @@ -283,21 +309,21 @@ func main() { 输出结果: -``` -2019-11-11 23:21:41.971051096 +0800 CST // 水星上次下合 -2021-03-26 14:57:42.052354216 +0800 CST // 金星下次上合 -2019-11-01 04:31:49.749019145 +0800 CST // 水星上次由顺行转逆行的留 -2020-06-25 02:07:41.599749326 +0800 CST // 金星下次由逆行转顺行的留 -2019-10-20 12:01:37.740152478 +0800 CST // 水星上次东大距 -2020-08-13 08:14:46.304587125 +0800 CST // 金星下次西大距 -2020-01-01 10:02:34.172435402 +0800 CST // 西安当天金星升起时刻;无错误 -2020-01-01 20:25:37.36411482 +0800 CST // 西安当天金星落下时刻;无错误 +```text +2019-11-11 23:21:41.729008555 +0800 CST // 水星上次下合 +2021-03-26 14:57:43.352850079 +0800 CST // 金星下次上合 +2019-11-01 04:31:49.712648391 +0800 CST // 水星上次由顺行转逆行的留 +2020-06-25 02:07:26.280500292 +0800 CST // 金星下次由逆行转顺行的留 +2019-10-20 12:01:37.925546765 +0800 CST // 水星上次东大距 +2020-08-13 08:14:45.946914553 +0800 CST // 金星下次西大距 +2020-01-01 10:02:34.94517982 +0800 CST // 西安当天金星升起时刻;无错误 +2020-01-01 20:25:36.944952607 +0800 CST // 西安当天金星落下时刻;无错误 -4 // 金星视星等 -49.98145049145023 // 金星相位角,单位度 -0.8215177914415865 // 金星被照亮比例 -255.63802053541346 // 金星亮面中心位置角,单位度 -1.2778819631550336 // 金地距离,单位 AU -0.7262651056423838 // 金日距离,单位 AU +49.98142313573699 // 金星相位角,单位度 +0.8215179742647736 // 金星被照亮比例 +255.63803631896667 // 金星亮面中心位置角,单位度 +1.2778823747747166 // 金地距离,单位 AU +0.7262651318993368 // 金日距离,单位 AU ``` 内外行星同样提供 `Diameter` / `Semidiameter`(以及 `N` 版),返回地心视直径/视半径,单位为角秒。 @@ -322,7 +348,7 @@ fmt.Println(venus.AscendingNode(date), venus.DescendingNode(date)) 76.86008484515058 256.8600848451506 // 金星升交点、降交点黄经,单位度 ``` -水星和金星还提供 `NextTransit` / `LastTransit` / `ClosestTransit` 地心凌日查询。这里的“地心”指从地球中心看到的行星圆面经过太阳圆面,不判断某个地点当时太阳是否在地平线上;如果要做观测计划,还需要结合本地太阳高度角和天气条件。 +水星和金星还提供 `NextTransit` / `LastTransit` / `ClosestTransit` 地心凌日查询。这里的“地心”指从地球中心看到的行星圆面经过太阳圆面,不判断某个地点当时太阳是否在地平线上;观测计划用 `LocalTransit` 求站心接触与当时太阳高度(见[水星与金星凌日](#水星与金星凌日))。 ```go package main @@ -365,22 +391,22 @@ func main() { ```text true // 找到一次有效的地心水星凌日 -2019-11-11 12:35:31.567597389 +0000 UTC // 一触:水星外切进入太阳圆面 -2019-11-11 12:37:12.817581295 +0000 UTC // 二触:水星完全进入太阳圆面 -2019-11-11 15:19:48.36056292 +0000 UTC // 凌甚:水星中心最接近太阳中心 -2019-11-11 18:02:29.176982045 +0000 UTC // 三触:水星开始离开太阳圆面 -2019-11-11 18:04:10.637948513 +0000 UTC // 四触:水星外切离开太阳圆面 -5h28m39.070351124s // 一触到四触的地心凌日持续时间 -75.92400059923187 // 凌甚时水星中心与太阳中心的最小角距离,单位角秒 -968.8881519533047 // 凌甚时太阳视半径,单位角秒 -4.978442871670873 // 凌甚时水星视半径,单位角秒 +2019-11-11 12:35:31.142292916 +0000 UTC // 一触:水星外切进入太阳圆面 +2019-11-11 12:37:12.603219151 +0000 UTC // 二触:水星完全进入太阳圆面 +2019-11-11 15:19:48.146160542 +0000 UTC // 凌甚:水星中心最接近太阳中心 +2019-11-11 18:02:28.751637339 +0000 UTC // 三触:水星开始离开太阳圆面 +2019-11-11 18:04:10.212523341 +0000 UTC // 四触:水星外切离开太阳圆面 +5h28m39.070230425s // 一触到四触的地心凌日持续时间 +75.93592092696468 // 凌甚时水星中心与太阳中心的最小角距离,单位角秒 +968.8881399120452 // 凌甚时太阳视半径,单位角秒 +4.978442587642365 // 凌甚时水星视半径,单位角秒 true // 找到一次有效的地心金星凌日 -2012-06-05 22:09:47.466886639 +0000 UTC // 一触:金星外切进入太阳圆面 -2012-06-05 22:27:35.865356326 +0000 UTC // 二触:金星完全进入太阳圆面 -2012-06-06 01:29:35.572371482 +0000 UTC // 凌甚:金星中心最接近太阳中心 -2012-06-06 04:31:35.068444311 +0000 UTC // 三触:金星开始离开太阳圆面 -2012-06-06 04:49:23.25597167 +0000 UTC // 四触:金星外切离开太阳圆面 -6h39m35.789085031s // 一触到四触的地心凌日持续时间 +2012-06-05 22:09:48.690456748 +0000 UTC // 一触:金星外切进入太阳圆面 +2012-06-05 22:27:37.088886201 +0000 UTC // 二触:金星完全进入太阳圆面 +2012-06-06 01:29:36.58495903 +0000 UTC // 凌甚:金星中心最接近太阳中心 +2012-06-06 04:31:35.87008953 +0000 UTC // 三触:金星开始离开太阳圆面 +2012-06-06 04:49:24.268559217 +0000 UTC // 四触:金星外切离开太阳圆面 +6h39m35.578102469s // 一触到四触的地心凌日持续时间 ``` ### 外行星 @@ -440,20 +466,19 @@ func main() { 输出结果: -``` -2020-10-14 07:25:50.441412627 +0800 CST // 火星下次冲日 -2021-01-29 09:39:33.697994649 +0800 CST // 木星下次合日 -2019-04-30 10:28:00.187439918 +0800 CST // 土星上次由顺行转逆行的留 -saturn B=23.577025 Bp=23.266930 P=6.629811 dU=1.171016 major=34.133852 minor=13.652911 // 土星环 B、B'、P、dU、长轴、短轴 -2020-01-11 15:23:23.360308706 +0800 CST // 天王星下次由逆行转顺行的留 -2019-12-08 17:00:15.517960488 +0800 CST // 海王星上次东方照 -2020-06-07 03:11:00.026179254 +0800 CST // 火星下次西方照 -2020-01-01 04:41:29.621566236 +0800 CST // 西安当天火星升起时刻;无错误 -2020-01-01 14:55:32.963508367 +0800 CST // 西安当天火星落下时刻;无错误 +```text +2020-10-14 07:25:53.541214764 +0800 CST // 火星下次冲日 +2021-01-29 09:39:32.556620836 +0800 CST // 木星下次合日 +2019-04-30 10:28:17.254818677 +0800 CST // 土星上次由顺行转逆行的留 +saturn B=23.577010 Bp=23.266930 P=6.629810 dU=1.171003 major=34.133617 minor=13.652809 // 土星环 B、B'、P、dU、长轴、短轴 +2020-01-11 15:22:18.532456755 +0800 CST // 天王星下次由逆行转顺行的留 +2019-12-08 17:00:04.494322836 +0800 CST // 海王星上次东方照 +2020-06-07 03:10:55.059157311 +0800 CST // 火星下次西方照 +2020-01-01 04:41:30.157055854 +0800 CST // 西安当天火星升起时刻;无错误 +2020-01-01 14:55:32.792302966 +0800 CST // 西安当天火星落下时刻;无错误 1.57 // 火星视星等 -2.1844284956325937 // 地火距离,单位 AU -1.5897860004265403 // 日火距离,单位 AU - +2.1844226019239925 // 地火距离,单位 AU +1.5897858533008575 // 日火距离,单位 AU ``` `saturn.Ring` 返回 `RingInfo`:`EarthLatitude` 是土星环张角 B,`SunLatitude` 是 B',`PositionAngle` 是北半短轴位置角,`DeltaU` 是太阳与地球在环面内的土星心黄经差,`MajorAxis` / `MinorAxis` 是土星环外缘长短轴,单位为角秒。 @@ -478,7 +503,7 @@ fmt.Println(venus.EarthDistance(date), venus.SunDistance(date)) ### 升落与中天 -`RiseTime` / `SetTime` / `DownTime` 按**当地民用日**搜索:`date` 用于确定当地日期与输出时区(当地小时数大于 12 时内部先回退 12 小时),`height` 是椭球高(米),`aero` 为 `true` 时加入标准大气折射(几何地平线降到约 `-0.5667°`)。极昼、极夜或当天没有过零时返回[哨兵错误](#参数与返回值约定)而不是时刻。`CulminationTime` 给上中天;`Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` 是站心瞬时量族,几何公式见[坐标工具](coord.md)。 +`RiseTime` / `SetTime` / `DownTime` 按**当地民用日**搜索:`date` 用于确定当地日期与输出时区,返回的事件属于该当地民用日;`height` 是椭球高(米),`aero` 为 `true` 时加入标准大气折射(几何地平线降到约 `-0.5667°`)。极昼、极夜或当天没有过零时返回[哨兵错误](#参数与返回值约定)而不是时刻。`CulminationTime` 给上中天;`Altitude` / `Azimuth` / `Zenith` 是站心瞬时量,`HourAngle` / `ParallacticAngle` 是地心瞬时量,几何公式见[坐标工具](coord.md)。 ```go // 西安市当地民用日内的升起、落下与中天;aero=true 含标准大气折射。 @@ -489,14 +514,14 @@ fmt.Println(mars.CulminationTime(date, lon)) ``` ```go -// 站心地平量:经度东正西负、纬度北正南负,返回值单位度。 +// 地平量:经度东正西负、纬度北正南负,返回值单位度。 fmt.Println(mars.Altitude(date, lon, lat), mars.Azimuth(date, lon, lat)) fmt.Println(mars.Zenith(date, lon, lat), mars.HourAngle(date, lon)) fmt.Println(mars.ParallacticAngle(date, lon, lat)) ``` ```go -// DownTime 是 SetTime 的兼容别名;N 版按截断项求值。 +// DownTime 是 SetTime 的兼容别名;N 版按 `n` 变体求值。 set, err := mars.DownTime(date, lon, lat, height, true) fmt.Println(set, err) fmt.Println(mars.CulminationTimeN(date, lon, 12)) @@ -560,7 +585,7 @@ fmt.Println(transit.SunSemidiameterArcsec, transit.PlanetSemidiameterArcsec) `AscendingNode` / `DescendingNode` 是行星轨道面与黄道面两个交点的黄经,单位度,同一时刻两者相差约 `180°`。`PhaseAngle` 是太阳–行星–地球夹角(度);`IlluminatedFraction`(别名 `Phase`)是 `0–1` 的被照亮比例;`BrightLimbPositionAngle` 是亮面中心位置角(度)。`Diameter` / `Semidiameter` 返回地心视直径/视半径,单位角秒。 -`ParallacticAngle` 返回站心视差角(天顶方向角,度),站心坐标入口见[坐标工具](coord.md)。 +`ParallacticAngle` 返回地心视差角(天顶方向角,度),由地心时角与赤纬求得,不含站心视差;站心坐标入口见[坐标工具](coord.md)。 ```go fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date)) // 升降交点黄经,度 @@ -574,7 +599,7 @@ fmt.Println(mars.ParallacticAngle(date, lon, lat)) // 视差角,度 ``` ```go -// 交点与视直径同样有截断版。 +// 交点与视直径同样有 `n` 变体。 fmt.Println(mars.AscendingNodeN(date, 12), mars.DescendingNodeN(date, 12)) fmt.Println(mars.DiameterN(date, 12), mars.SemidiameterN(date, 12)) ``` @@ -632,8 +657,8 @@ func main() { 输出结果: ```text -jupiter DS=54.342153 DE=1.436485 CMI=292.712909 CMII=276.309048 CMIII=147.241811 // 木星子日/子地赤纬,System I/II/III 中央经线,单位度 -saturn B=-0.608048 Bp=-2.675677 P=4.480276 major=42.709920 minor=0.453248 // 土星环 B、B'、短轴位置角、外缘长短轴,角度单位度,长短轴单位角秒 +jupiter DS=54.342201 DE=1.436449 CMI=292.712946 CMII=276.309085 CMIII=147.241848 // 木星子日/子地赤纬,System I/II/III 中央经线,单位度 +saturn B=-0.608032 Bp=-2.675667 P=4.480277 major=42.709152 minor=0.453228 // 土星环 B、B'、短轴位置角、外缘长短轴,角度单位度,长短轴单位角秒 ``` 只需要中央经线时,可以单独调用 `CentralMeridians`: @@ -660,7 +685,7 @@ fmt.Println(p.SubEarthLongitude, p.SubEarthLatitude, p.SubSolarLongitude, p.SubS ``` ```go -// 土星环与木星中央经线的截断版。 +// 土星环与木星中央经线的 `n` 变体。 fmt.Println(saturn.RingN(date, 12).MinorAxis, jupiter.CentralMeridiansN(date, 12).SystemIII) ``` @@ -753,8 +778,8 @@ func main() { 输出结果: ```text -io x=-0.675026 y=-0.032798 front=true // 木卫一相对木星中心的 X/Y 偏移,单位木星半径;位于木星盘面前方 -europa ra=110.769133 dec=22.335828 // 木卫二视赤经、视赤纬,单位度 +io x=-0.675023 y=-0.032794 front=true // 木卫一相对木星中心的 X/Y 偏移,单位木星半径;位于木星盘面前方 +europa ra=110.769095 dec=22.335877 // 木卫二视赤经、视赤纬,单位度 io transit=true occultation=false eclipse=false shadow=true // 木卫一正在凌日,且影子正在凌日 europa transit=false occultation=false eclipse=false shadow=false // 木卫二此刻无凌日、掩蔽、木星食或影凌 event valid=true sat=1 type=transit // 下一次有效事件为木卫一凌日 @@ -786,13 +811,13 @@ contact valid=true sat=2 type=occultation // 下一次有效接触事件为木 - `GalileanPhenomenonContactEvent` 与 IMCCE 2026 年表的 D/F 接触时刻最大偏差约 `79 s`,接触持续时间最大偏差约 `17 s`。 - `GalileanPhenomenonEvent` 与 IMCCE 的 D/F 接触定义不同,起止时刻的差异可达约 `7` 分钟。 -### 与 `planet` 包共用的类型与常量 +### 与 `planet` 包共用的低层入口 -`planet` 包是低层解析级数入口,被七个行星包与 `sun` / `moon` 共用。它只导出函数,**没有导出类型**,所以行星包之间真正共用的是同一套数值口径与截断语义,而不是共享类型:各包的 `PhysicalInfo` 是 `basic.PlanetPhysicalInfo` 的镜面结构,`TransitInfo` 是 `basic.PlanetTransitResult` 的镜面结构,字段一一对应,类型本身仍属于各自的包。 +`planet` 包提供内置模型与级数的低层入口,被七个行星包与 `sun` / `moon` 共用。它只导出函数,没有导出类型;行星包共用的是数值口径与模型选择,而不是结果类型。各包的 `PhysicalInfo` 与 `basic.PlanetPhysicalInfo`、`TransitInfo` 与 `basic.PlanetTransitResult` 字段一一对应,类型本身仍属于各自的包。外部核引擎类型位于独立子包 `planet/spk`。 | 入口 | 作用 | 单位 | | --- | --- | --- | -| `planet.WherePlanet` / `planet.WherePlanetN` | VSOP87 结果:`xt` 取 `1..7` 依次为水星到海王星、`-1` 或 `0` 为地球,`zn` 取 `0` 黄经、`1` 黄纬、`2` 日心距;`xt` / `zn` 越界返回 `NaN` 而不 panic | 度 / AU | +| `planet.WherePlanet` / `planet.WherePlanetN` | 当前内置模型结果:`xt` 取 `1..7` 依次为水星到海王星、`-1` 或 `0` 为地球,`zn` 取 `0` 黄经、`1` 黄纬、`2` 日心距;`xt` / `zn` 越界返回 `NaN` 而不 panic | 度 / AU | | `planet.Distance` | 日地距离 | AU | | `planet.SunLo` / `planet.SunM` / `planet.SunMidFun` / `planet.SunTrueLo` / `planet.SunApparentLo` | 太阳几何黄经、平近点角、中心差、真黄经、视黄经 | 度 | | `planet.Earthe` / `planet.EarthPI` | 地球轨道偏心率、地球近日点黄经 | 无量纲 / 度 | @@ -803,7 +828,7 @@ contact valid=true sat=2 type=occultation // 下一次有效接触事件为木 ```go // xt=1..7 对应水星..海王星;zn=0 黄经、1 黄纬、2 日心距(AU)。 fmt.Println(planet.WherePlanet(4, 2, 2460000.5)) // 木星日心距,AU -fmt.Println(planet.WherePlanetN(4, 2, 2460000.5, 12)) // 截断版,保留约 12 个主项 +fmt.Println(planet.WherePlanetN(4, 2, 2460000.5, 12)) // 半解析快速表;Legacy 按 n 截断。 fmt.Println(planet.WherePlanet(8, 0, 2460000.5)) // xt 越界:NaN ``` @@ -826,17 +851,17 @@ fmt.Println(planet.WherePlanet(-1, 0, 2460000.5), planet.WherePlanet(4, 0, 24600 角度一律为度;视直径与视半径为角秒;距离按函数名区分——`EarthDistance` / `SunDistance` 与 `planet.WherePlanet` 的 `zn=2` 用 AU,`planet.MoonAway` 用 km。 -`PhaseAngle` 为度,`IlluminatedFraction` 与别名 `Phase` 为 `0–1`;`ApparentMagnitude` 为星等(无量纲);升落、中天与所有事件搜索返回 `time.Time`,时区与输入一致。 +`PhaseAngle` 为度,`IlluminatedFraction` 与别名 `Phase` 为 `0–1`;`ApparentMagnitude` 为星等(无量纲,经典相位多项式);`ApparentMagnitude2018` 用 Mallama & Hilton 2018 模型,水星、金星大相位角与土星环更准,火星不含自转与季节修正;升落、中天与所有事件搜索返回 `time.Time`,时区与输入一致。 事件结构里的历时字段(`TransitInfo.Duration`、`TransitInfo.InternalDuration`、`GalileanPhenomenonEvent.Duration`、`GalileanPhenomenonContact.Duration`)是 Go 的 `time.Duration`,不是儒略日或天数;`TransitInfo` 的 `Start` / `Greatest` / `End` / `InternalStart` / `InternalEnd` 保持调用者输入的时区。 -地心量、站心量与距离是三个不同口径:`ApparentLo` / `ApparentBo` / `ApparentRa` / `ApparentDec` / `ApparentRaDec` 是地心视位置,`Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` 是站心量,`EarthDistance` / `SunDistance` 是地心几何距离。 +地心量、站心量与距离是三个不同口径:`ApparentLo` / `ApparentBo` / `ApparentRa` / `ApparentDec` / `ApparentRaDec` 是地心视位置,`Altitude` / `Azimuth` / `Zenith` 是站心量,`HourAngle` / `ParallacticAngle` 是不含站心视差的地心量,`EarthDistance` / `SunDistance` 是地心几何距离。 ### 零值、越界与哨兵错误 事件搜索在窗口内没有事件时返回零值或 `Valid=false` 的结构(`TransitInfo.Valid`、`GalileanPhenomenonEvent.Valid`、`GalileanPhenomenonContactEvent.Valid`),不返回错误;`TransitInfo` 在 `HasInternal` 为假时 `InternalStart` / `InternalEnd` 为零值,偏凌日没有内切。 -`planet.WherePlanet` / `planet.WherePlanetN` 的 `xt` / `zn` 越界返回 `NaN` 而不 panic;`...N` 截断族的 `n < 0` 用全部内置项、`n >= 0` 截断。`RiseTime` / `SetTime` / `DownTime`(含 `N` 版)是唯一返回 `error` 的一族:当天没有几何升/落时返回下面的哨兵错误,时刻为零值。 +`planet.WherePlanet` / `planet.WherePlanetN` 的 `xt` / `zn` 越界返回 `NaN` 而不 panic;`...N` 的 `n < 0` 用完整级数,半解析的非负 `n` 用同一张快速表,Legacy 按 `n` 截断。`RiseTime` / `SetTime` / `DownTime`(含 `N` 版)当天没有几何升/落时返回下面的哨兵错误,时刻为零值。 每个行星包各有一套极昼/极夜错误,名字里的 `NEVER_RISE` 指"当天永不升起"(极夜),`NEVER_SET` 指"当天永不落下"(极昼),`NEVER_DOWN` 是 `NEVER_SET` 的兼容别名。 @@ -863,10 +888,10 @@ case errors.Is(err, mercury.ERR_MERCURY_NEVER_SET): ### 站心与地心 -`Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` 使用 `date` 的时区做地方时计算,经度东正西负、纬度北正南负,`height` 是椭球高(米,不是海拔正高)。它们与 `ApparentRa` / `ApparentDec` 的地心视位置不是一个口径,跨口径使用前先经[坐标工具](coord.md)换算。 +`Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` 使用 `date` 的时区做地方时计算,经度东正西负、纬度北正南负,`height` 是椭球高(米,不是海拔正高)。其中 `Altitude` / `Azimuth` / `Zenith` 计入站心视差,与 `ApparentRa` / `ApparentDec` 的地心视位置不是一个口径;`HourAngle` / `ParallacticAngle` 直接由地心视位置求得,不含站心视差,跨口径使用前先经[坐标工具](coord.md)换算。 ### 精度与适用范围 -行星包使用内置 VSOP87 截断级数,覆盖 J2000 前后约 4000 年;相对完整 VSOP87 的截断误差量级见[太阳与行星](accuracy.md#太阳与行星),整体适用范围见[适用范围与精度](accuracy.md)。`...N` 截断版会在这条基线之上继续放宽,适合批量扫描或前端实时刷新。 +行星包默认使用半解析模型,也可选择 Legacy 或自定义引擎。可用区间不代表全范围精度相同,Legacy 相对完整 VSOP87 的截断误差也不等于对 DE 星历的综合误差,详见[Legacy 截断误差](accuracy.md#legacy-截断误差)。快速表适合粗扫描;需要精修事件或比较外部年表时使用完整级数并统一时标与坐标口径。 `saturn.Ring` 与物理星历只影响盘面与掩带表达:土星环既不参与月掩接触计算、也不作为圆盘边界绘制。行星没有独立出图入口,月掩与土星环的出图见[月掩手册](occultation.md#月掩出图)。 diff --git a/doc/manual/star.md b/doc/manual/star.md index ecb003c..ab51819 100644 --- a/doc/manual/star.md +++ b/doc/manual/star.md @@ -8,6 +8,7 @@ ## 目录 +- [外挂星表](#外挂星表) - [查询天狼星的位置与升起时刻](#查询天狼星的位置与升起时刻) - [API 参考](#api-参考) - [星座判定](#星座判定) @@ -26,6 +27,55 @@ - [参数与返回值约定](#参数与返回值约定) - [相关手册](#相关手册) +## 外挂星表 + +`star/catalog` 与内置 9100 星数据库独立,不自动下载或加载大型星表。读入的 `Entry.Coordinate` 是带历元、坐标框架和自行的 `basic.StarCoordinate`,可直接用于 `moon.FindStarOccultations` 或 `phenomena.StarApproaches`。 + +| 入口 | 输入与用途 | +| --- | --- | +| `ReadCSV` | 带表头的 CSV/TSV,使用 `ColumnMap` 指定列名、历元与框架;`Delimiter` 为 0 时自动识别 | +| `ReadHipparcos2007` / `ReadBSC5` | 读取对应的固定宽度文本星表 | +| `ReadStellariumStars` / `ReadStellariumDir` | 单个二进制分片或 `fs.FS` 目录;目录选项可加载名称及交叉 ID,按 `MaxMagnitude` 过滤 | +| `Lookup` / `Entries` / `Len` | 按主 ID 或别名查询,取得记录副本或数量 | +| `Near` / `Band` | 星表自身历元下的锥形搜索/赤纬带搜索,不自动传播到观测日期 | +| `Merge` / `WithNames` | 返回新星表;合并时后者覆盖同主 ID、同分量记录,名称文件使用 `id\|name` 行 | + +```go +import ( + "strings" + "time" + + "b612.me/astro/basic" + "b612.me/astro/star/catalog" +) + +input := strings.NewReader("id,ra,dec,pmra,pmdec,V\nSirius,101.28715533,-16.71611586,-546.01,-1223.07,-1.46\n") +stars, err := catalog.ReadCSV(input, catalog.ColumnMap{ + ID: "id", RA: "ra", Dec: "dec", PMRA: "pmra", PMDec: "pmdec", Magnitude: "V", + PMRAIncludesCosDec: true, Band: catalog.BandV, + Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: basic.CoordinateFrameJ2000, +}, catalog.ReadOptions{Strict: true}) +if err != nil { + panic(err) +} +entry, found := stars.Lookup("Sirius") +_ = entry.Coordinate +_ = found +``` + +单位与必填项: + +- `RA`、`Dec` 默认是度,`AnglesInRadians` 可改为弧度;自行是 mas/年,视差是 mas,径向速度是 km/s。 +- 若 `PMRA` 已经是 `dRA·cos(Dec)`,设置 `PMRAIncludesCosDec: true`,否则读入器补乘余弦。 +- 必须给出 `Epoch` 或 `EpochColumn`(儒略年)以及正确的 `Frame`,不得用零历元代替未知历元。 + +读取行为: + +- `ReadOptions.Strict` 为 true 时坏记录返回错误,否则跳过并计入 `Skipped()`;缺少必需表头这类结构错误始终返回错误。 +- `Filter` 只控制入选,不计为坏记录。 +- 未提供星等时 `Magnitude` 为 `NaN`;`BandV`、`BandG`、`BandHp` 不应不经换算就用于同一 V 波段可见性阈值。 +- `Catalog.Near` 在各记录自身历元搜索;混合历元或框架的数据应先统一,观测日期的精确筛选仍需自行传播。 + ## 查询天狼星的位置与升起时刻 ```go @@ -60,7 +110,7 @@ func main() { 星表坐标的历元是 J2000。`RaDecByDate` 加入自行、岁差与章动后,才用于指定日期的升落、高度角和星座判定。 -自行归算按**儒略年**(365.25 日)计,`RaDecByDate` 先把民用时刻换算成 TT 再取历元差。只要记录带距离(`Pc > 0`),`RaDecByJde` 就按**三维空间运动**推进:自行给出切向速度、`RadVel` 给出视向分量,位置矢量线性外推后取方向;没有距离的记录退回二维,即只推进赤经赤纬两个角分量,等价于把恒星当作无穷远。两者的差别是二阶项——大圆路径的曲率和径向运动改变距离后对视角尺度的拉伸,26 年内最亮的高自行星也只有约 0.09 角秒。 +自行归算按**儒略年**(365.25 日)计,`RaDecByDate` 先把民用时刻换算成 TT 再取历元差。只要记录带距离(`Pc > 0`),`RaDecByJde` 就按**三维空间运动**推进:自行给出切向速度、`RadVel` 给出视向分量,位置矢量线性外推后取方向;没有距离的记录退回二维,即只推进赤经赤纬两个角分量,等价于把恒星当作无穷远。两者的差别是二阶项:三维推进同时改变恒星距离,视位置比二维推进略小,26 年内最亮的高自行星也只有约 0.09 角秒。 ## API 参考 @@ -70,11 +120,12 @@ func main() { | 恒星库 | `InitStarDatabase` / `StarDataByHR` / `StarDataByName` / `TopBrightStars` | 初始化内置星表、按 HR 编号或中文名取星、取最亮恒星样本 | HR `1–9110`;`Mag` 为视星等 | | 坐标修正 | `StarData.RaDecByDate`(`basic` 侧对应 `RaDecByJde`) | 把 J2000 位置修正到指定时刻 | 返回赤经赤纬,单位度 | | 升落与中天 | `RiseTime` / `SetTime` / `CulminationTime`(`DownTime` 是 `SetTime` 的废弃别名) | 升起、落下、中天时刻 | 返回民用时刻;极区返回哨兵错误 | +| 带气象升落 | `RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` | 按实测气压(hPa)、气温(℃)计算升落 | 恒带大气折射 | | 站心量 | `Altitude` / `ApparentAltitude` / `Azimuth` / `Zenith` / `ApparentZenith` | (视)高度角、方位角、(视)天顶距 | 度 | | 时角与视差角 | `HourAngle` / `ParallacticAngle` | 恒星时角、天顶方向视差角 | 度 | | 恒星时 | `MeanSiderealTime` / `ApparentSiderealTime` | 平恒星时、真恒星时 | 小时 | -`star` 包没有 `...N` 截断入口;需要截断解析项的场合在 `sun`、`moon`、行星与 `coord` 的对应函数上,语义是 `n < 0` 用本仓库内置的全部解析项、`n >= 0` 截断(见各自手册)。 +`star` 包没有 `...N` 入口;带 `n` 参数的求值接口在 `sun`、`moon`、行星与 `coord` 的对应函数上,语义见各自手册(`n < 0` 用完整级数;半解析模型下 `n >= 0` 用快速表,Legacy 按 `n` 截断)。 以下片段省略公共前置变量:`date`(观测时刻,民用时标)、`lon`/`lat`(观测点经纬度,东经/北纬为正,度)、`height`(观测点高度,**椭球高**,米)、`aero`(是否计入蒙气差与视半径修正)。 @@ -100,7 +151,7 @@ bright, _ := star.TopBrightStars() fmt.Println(len(bright), bright[0].HR) // 最亮恒星样本条数与首项 HR ``` -数据库走 `sync.Once` 懒加载,`InitStarDatabase()` 只是提前预热:它幂等,并且能把首次加载的错误显式暴露出来,不调用也会在第一次查询时自动加载。`TopBrightStars()` 返回 169 颗视星等约不高于 3、按亮到暗大致排列的内置样本。 +星表在首次查询时自动加载,`InitStarDatabase()` 只是提前预热:它幂等,并且能把首次加载的错误显式暴露出来,不调用也会在第一次查询时自动加载。`TopBrightStars()` 返回 169 颗视星等约不高于 3、按亮到暗大致排列的内置样本。 `basic.StarData` 在 `InnerStarData` 之上补了名称字段;字段口径如下: @@ -214,11 +265,11 @@ func main() { 输出结果: ```text -2019-12-31 19:22:56.144202053 +0800 CST // 天狼星升起时刻 -2020-01-01 05:30:39.802506566 +0800 CST // 天狼星落下时刻 +2019-12-31 19:22:56.32125467 +0800 CST // 天狼星升起时刻 +2020-01-01 05:30:39.979518949 +0800 CST // 天狼星落下时刻 大犬座 // 天狼星所在星座 -6h3m46.61s // 织女一在公元 13600 年的赤经 -84°18′27.15″ // 织女一在公元 13600 年的赤纬 +6h4m44.89s // 织女一在公元 13600 年的赤经 +84°19′13.71″ // 织女一在公元 13600 年的赤纬 天狼 Sirius -1.46 // 最亮恒星表第一项:中文名、英文常用名、视星等 ``` @@ -298,7 +349,6 @@ if errors.Is(err, star.ERR_STAR_NEVER_RISE) || errors.Is(err, star.ERR_STAR_NEVE - **单位**:赤经、赤纬、高度角、天顶距、方位角、时角、视差角均为**度**,恒星时为**小时**;`tools.Format` 负责把它们格式化成度分秒或时分秒展示。星表里的 `PmRA`/`PmDec` 是角秒/年,`RadVel`/`RotVel` 是 km/s,`Pc` 是秒差距。 - **时标**:所有公开 API 的时间参数与返回值都是民用时刻(UTC 标签,1972-01-01 之前等于 UT1),约定见[时标声明](map-geojson.md#时标声明)。 - **坐标口径**:星表是 J2000;`RaDecByDate` 叠加自行、岁差与章动。直接用 `InnerStarData.Ra`/`Dec` 会让升落与星座判定出现可观偏差(百年量级的岁差就是度级)。 -- **极区哨兵错误**:`RiseTime` 在极夜返回 `star.ERR_STAR_NEVER_RISE`(该日永远在地平线下),`SetTime` 在极昼返回 `star.ERR_STAR_NEVER_SET`(该日永远在地平线上),用 `errors.Is` 判定;`ERR_STAR_NEVER_DOWN` 是 `ERR_STAR_NEVER_SET` 的废弃别名。 - **观测点高度**:`height` 是椭球高(大地高),单位米;手上只有海拔正高时要先加大地水准面差距,约定见「观测点高度约定」。 - **蒙气差模型的有效区间**:`ApparentAltitude`/`ApparentZenith` 用的 Saemundsson 近似只在真高度角 `(-5°, 90°)` 内生效,区间外修正量为 0——所以深在地平线以下时视高度角与几何高度角完全相等(示例里天狼星 `alt=-30.180` 与 `Altitude` 同值就是这个原因)。气压必须为正、气温必须高于绝对零度,否则返回 `NaN`。 - **`aero` 语义**:真值按标准蒙气差修正后的地平求升落,假值走几何地平;两者的差值在低纬度约 2–3 分钟,高纬度会明显放大。 diff --git a/doc/manual/sun-moon.md b/doc/manual/sun-moon.md index 90896b7..56a2ba1 100644 --- a/doc/manual/sun-moon.md +++ b/doc/manual/sun-moon.md @@ -4,7 +4,9 @@ `sun` 与 `moon` 是主链,`lite/sun` 与 `lite/moon` 是独立近似实现。没有特殊说明时,角度单位为度,视直径与视半径为角秒,`sun.EarthDistance` 为 AU、`moon.EarthDistance` 为千米;观测接口的 `time.Time` 通常表示民用时刻,真太阳时的返回值另按地方太阳时解释,见[时标手册](timescale.md)。 -本手册覆盖日月本体:位置、升落与中天、站心量、相位与朔望、近远地点与交点、最大赤纬、天平动、视直径与物理星历。日月食几何见[日食与月食手册](eclipse.md#全球见食图与月食出图),月掩见[月掩手册](occultation.md#月掩出图),站心几何与折射的通用换算见[坐标工具](coord.md)。 +本手册的数值输出块用于展示调用形状,取自默认半解析模型的运行结果。与 Legacy 的差别很小:月球事件时刻约 `1–10 s`、地月距离约 `5 km`、相位约 `1e-6`、日月角度约 `1e-5°`,升落时刻仅毫秒级,`lite/*` 不受影响。要按固定模型复现,用显式 `Context`/`Instance`;两个模型的量化误差见[精度与性能](accuracy.md)。 + +本手册覆盖日月本体:位置、升落与中天、站心量、相位与朔望、近远地点与交点、最大赤纬、天平动、视直径与物理星历。日月食几何见[日月食与凌日手册](eclipse.md#全球见食图与月食出图),月掩见[月掩手册](occultation.md#月掩出图),站心几何与折射的通用换算见[坐标工具](coord.md)。 ## 目录 @@ -14,7 +16,7 @@ - [moon](#moon) - [lite/sun](#litesun) - [lite/moon](#litemoon) - - [截断项族](#截断项族) + - [`...N` 族](#n-族) - [常用场景](#常用场景) - [今天的日出日落与晨昏朦影](#今天的日出日落与晨昏朦影) - [月出月落与此刻的月亮高度](#月出月落与此刻的月亮高度) @@ -88,7 +90,7 @@ func main() { ## API 参考 -下表按能力分组列出四个包的导出接口。多数求值接口另有 `...N` 截断变体,统一在[截断项族](#截断项族)里说明;表中所有 `time.Time` 参数都是绝对时刻,`lon`/`lat` 均为东正西负、北正南负。 +下表按能力分组列出四个包的导出接口。多数求值接口另有 `...N` 变体,统一在[`...N` 族](#n-族)里说明;表中所有 `time.Time` 参数都是绝对时刻,`lon`/`lat` 均为东正西负、北正南负。 ### sun @@ -105,7 +107,7 @@ func main() { | `DownTime` | 日落别名 | 已废弃,内部转调 `SetTime` | | `CulminationTime` | 上中天 | `time.Time` | | `MorningTwilight` / `EveningTwilight` | 晨光始 / 暮光终 | `(time.Time, error)`;角度常用 -6 / -12 / -18 度 | -| `Altitude` / `Zenith` / `Azimuth` / `HourAngle` | 几何高度角 / 天顶距 / 方位角 / 时角 | 度,站心 | +| `Altitude` / `Zenith` / `Azimuth` / `HourAngle` | 几何高度角 / 天顶距 / 方位角 / 时角 | 度;`Altitude` / `Zenith` / `Azimuth` 为站心,`HourAngle` 为地心视时角(不含站心视差) | | `ApparentAltitude` / `ApparentZenith` | 视高度角 / 视天顶距 | 度;需要气压 hPa 与气温 ℃ | | `ParallacticAngle` | 视差角(天顶方向角) | 度,有符号 | | `ApparentSolarTime` | 真太阳时 | `time.Time`,结果时区按经度换算 | @@ -170,7 +172,7 @@ func main() { | `TrueRa` / `TrueDec` / `TrueRaDec` | 轻量真赤道坐标 | 度,地心 | | `ApparentRa` / `ApparentDec` / `ApparentRaDec` | 轻量视赤道坐标 | 度,地心 | | `Distance` | 轻量日地距离 | AU | -| `HourAngle` / `Azimuth` / `Altitude` / `Zenith` | 轻量时角 / 方位角 / 高度角 / 天顶距 | 度,站心 | +| `HourAngle` / `Azimuth` / `Altitude` / `Zenith` | 轻量时角 / 方位角 / 高度角 / 天顶距 | 度;均按地心视位置计算,太阳视差约 8.8″,忽略 | | `RiseTime` / `SetTime` | 轻量日出 / 日落 | `(time.Time, error)` | | `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET` | 极夜 / 极昼 | 错误值 | @@ -186,14 +188,14 @@ func main() { | `RiseTime` / `SetTime` | 轻量月出 / 月落 | `(time.Time, error)` | | `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET` / `ERR_NOT_TODAY` | 极夜 / 极昼 / 事件不在当天 | 错误值 | -### 截断项族 +### `...N` 族 -`sun` 与 `moon` 的多数求值接口提供 `...N` 变体:名字是原接口名加 `N`,参数在末尾多一个 `n int`,返回形状不变。`n < 0` 使用当前仓库内嵌的全部解析项,结果与不带 `N` 的版本一致;`n >= 0` 把级数截断到 `n` 项,适合性能对比、批量粗算或误差敏感性实验。 +`sun` 与 `moon` 的多数求值接口提供 `...N` 变体:名字是原接口名加 `N`,参数在末尾多一个 `n int`,返回形状不变。`n < 0` 使用完整级数,结果与不带 `N` 的版本一致。默认构建使用半解析模型,所有 `n >= 0` 均使用同一张快速表,增大 `n` 不会增加项数;Legacy 则按 `n` 缩短级数。切换模型不改变单位或时区,误差见[精度与性能](accuracy.md)。 - sun:`TrueLoN`、`TrueBoN`、`AltitudeN`、`ZenithN`、`AzimuthN`、`HourAngleN`、`ParallacticAngleN`、`ApparentAltitudeN`、`ApparentZenithN`、`DiameterN`、`SemidiameterN`、`PhysicalN`、`RiseTimeN`、`SetTimeN`、`DownTimeN`、`CulminationTimeN`、`MorningTwilightN`、`EveningTwilightN`、`ApparentSolarTimeN` - moon:`TrueLoN`、`TrueBoN`、`AscendingNodeN`、`DescendingNodeN`、`DiameterN`、`SemidiameterN`、`PhysicalN`、`TopocentricPhysicalN`、`BrightLimbPositionAngleN`、`TopocentricBrightLimbPositionAngleN` -站心赤道坐标(`ApparentRa` / `ApparentDec` / `ApparentRaDec`)、月亮的升落(`RiseTime` / `SetTime`)与相位族没有 `N` 变体,它们不受截断开关控制。 +站心赤道坐标(`ApparentRa` / `ApparentDec` / `ApparentRaDec`)、月亮的升落(`RiseTime` / `SetTime`)与相位族没有 `N` 变体(即不接收 `n` 参数)。 ## 常用场景 @@ -282,7 +284,7 @@ delta dRA=0.5021 dDec=-0.3978 280.8950939694744 -23.05840775453492 ``` -月亮离得近,站心与地心能差半度左右(这里赤经差 `0.50°`、赤纬差 `0.40°`),合起来比一个月面视直径还大,所以给观测者用的位置必须走 `moon.ApparentRaDec`;太阳在 1 AU 外视差可忽略,只提供地心量(当然也可以调用站心坐标转换接口得到站心位置=-=)。口径定义见[观测角语义](#观测角语义)。 +月亮离得近,站心与地心能差半度左右(这里赤经差 `0.50°`、赤纬差 `0.40°`),合起来比一个月面视直径还大,所以给观测者用的位置必须走 `moon.ApparentRaDec`;太阳在 1 AU 外视差可忽略,只提供地心量;需要站心位置时用 `coord.TopocentricEquatorial` 换算。口径定义见[观测角语义](#观测角语义)。 ### 主链与 lite 的误差对照 @@ -300,7 +302,7 @@ fmt.Println(litesun.RiseTime(date, lon, lat, height, true)) // 2020-01-01 07:49:51.69717729 +0800 CST ``` -轻量链路与主链形状同构、精度不同:同一时刻的高度角只差 `0.0027°` 量级,月相只差 `0.0022`;2026 全年 8 站点统计下 `lite/sun` 日出平均绝对误差 `0.02 min`(P95 `0.04 min`、最大 `0.31 min`),`lite/moon` 月出 `0.28 min`(P95 `0.57 min`、最大 `1.44 min`),`lite/moon` 的 `Phase()` 最大绝对误差 `0.00243`。 +轻量链路与主链形状同构、精度不同:上面这组输出中,同一时刻高度角差约 `0.0027°`、月相差约 `0.0022`。2026 全年 8 站点的历史统计是相对 Legacy 主链测的,未针对半解析默认重测,详见[精度与性能](accuracy.md)。 完整对照与截断误差见[与主链的差异与误差量级](#与主链的差异与误差量级)与 README 的 [Lite 轻量链路](accuracy.md#lite-轻量链路)。 @@ -325,7 +327,7 @@ fmt.Println(litesun.RiseTime(date, lon, lat, height, true)) // > > 例如月亮可能在凌晨1点落下、中午12点再次升起,此时升起时间会晚于降落时间;这一场景晚上的月落时间对应次日日期。 > -> 完整的升降周期由升起时间与降落时间的先后关系决定:判断升起时间是否在降落时间之后,即可确定后续的正确时间点。 +> 完整的升降周期由升起与降落的先后关系决定:若升起的日期晚于(或早于)落下,后一个事件属于次日,跨日查询时应把 `date` 顺延一天。 ```go package main @@ -368,14 +370,14 @@ func main() { 输出结果: ```text -2020-01-01 07:22:27.960488498 +0800 CST -2020-01-01 07:49:52.413689196 +0800 CST -2020-01-01 12:47:35.933117866 +0800 CST -2020-01-01 17:45:09.188657999 +0800 CST -2020-01-01 18:12:33.624035418 +0800 CST -2020-01-01 11:52:49.860912859 +0800 CST -2020-01-01 17:36:48.811488747 +0800 CST -2020-01-01 23:26:49.313553571 +0800 CST +2020-01-01 07:22:28.91963303 +0800 CST +2020-01-01 07:49:53.401680886 +0800 CST +2020-01-01 12:47:35.933721363 +0800 CST +2020-01-01 17:45:08.557346463 +0800 CST +2020-01-01 18:12:33.02157104 +0800 CST +2020-01-01 11:52:50.013020932 +0800 CST +2020-01-01 17:36:48.954946696 +0800 CST +2020-01-01 23:26:49.448562562 +0800 CST ``` ### 日月位置 @@ -428,16 +430,16 @@ func main() { 输出结果: ```text -280.01526210031136 +280.0152652754265 23.4362178391013 -赤经: 18h43m34.82s 赤纬: -23°3′30.27″ +赤经: 18h43m34.82s 赤纬: -23°3′30.20″ 人马座 -方位角: 120.19477090015224 高度角: 2.4014437419430097 天顶距: 87.59855625805699 -0.983292937163176 -赤经: 23h18m56.24s 赤纬: -10°20′54.42″ +方位角: 120.19442333006145 高度角: 2.398445319378493 天顶距: 87.6015546806215 +0.9832929289388871 +赤经: 23h18m56.20s 赤纬: -10°20′54.64″ 宝瓶座 -方位角: 67.63889332004852 高度角: -45.34916937173283 天顶距: 135.34916937173284 -404238.6096080479 +方位角: 67.63846015645981 高度角: -45.349642390635466 天顶距: 135.34964239063547 +404243.3922335782 ``` ## 太阳 @@ -468,10 +470,10 @@ fmt.Println(sun.EclipticObliquity(date, true), sun.GeometricLo(date), sun.MidFun 23.438148552330773 280.83169623098 -0.08703499790144194 ``` -同一族接口都提供 `...N` 截断变体。下面的对照用 `n = 8` 截断,`n < 0` 时结果与不带 `N` 的版本逐位一致: +同一族接口都提供 `...N` 变体。下面用 `n = 8` 选择半解析快速表(Legacy 中为级数截断),`n < 0` 时结果与不带 `N` 的版本逐位一致。示例输出取自默认半解析模型: ```go -// n<0 使用全部内嵌 VSOP 项,n>=0 截断解析项。 +// n<0 使用完整级数,n>=0 使用快速表;Legacy 按 n 截断。 fmt.Println(sun.TrueLo(date), sun.TrueLoN(date, 8)) fmt.Println(sun.Altitude(date, lon, lat), sun.AltitudeN(date, lon, lat, 8)) fmt.Println(sun.Diameter(date), sun.DiameterN(date, 8)) @@ -689,12 +691,12 @@ func main() { 输出结果: ```text -0.30004130960877884 // 月面约有 30% 被太阳照亮 +0.3000398794307665 // 月面约有 30% 被太阳照亮 上峨眉月 // 当前月相描述 -2020-01-25 05:41:58.271192908 +0800 CST // 下一次朔月 -2020-01-03 12:45:23.229190707 +0800 CST // 下一次上弦 -2020-01-11 03:21:17.159625291 +0800 CST // 下一次望月,也就是满月 -2020-01-17 20:58:23.396406769 +0800 CST // 下一次下弦 +2020-01-25 05:41:59.860723614 +0800 CST // 下一次朔月 +2020-01-03 12:45:24.51480031 +0800 CST // 下一次上弦 +2020-01-11 03:21:18.524373471 +0800 CST // 下一次望月,也就是满月 +2020-01-17 20:58:24.686442017 +0800 CST // 下一次下弦 ``` `Last*`、`Closest*` 与按小数年锚点求解的 `ShuoYue` / `FullMoon` 等返回 UTC;`ClosestConjunctionWithPlanet` 求最近一次行星合月,目标用 `ConjunctionPlanet` 常量给出: @@ -869,7 +871,7 @@ fmt.Println(moon.Diameter(date), moon.Semidiameter(date), moon.EarthDistance(dat ## 轻量链路 -`lite/sun` 与 `lite/moon` 是独立于主链的近似实现:不依赖 VSOP87 或主链的 ELP2000/82 级数,面向 CPU / 内存受限环境,调用方式与主链同形。升落搜索用固定步长扫描加二分,不走主链的高精度章动迭代。 +`lite/sun` 与 `lite/moon` 是独立于主链的近似实现,不使用半解析或 Legacy 表,也不随全局模型切换,面向 CPU / 内存受限环境,调用方式与主链同形。升落搜索用固定步长扫描加二分,不走主链的高精度章动迭代。 ```go package main @@ -954,9 +956,9 @@ fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 1 ### 与主链的差异与误差量级 -轻量链路与主链的差别集中在实现与误差上,接口形状基本一致。位置与月相等纯求值接口相对主链约快 `8.3–27.3x`,升落接口约 `1.0–3.7x`,计算链路零堆分配;升落搜索步长为 `lite/sun` `30` 分钟、`lite/moon` `15` 分钟。与 `sun` / `moon` 的误差(2026 全年,8 个站点)如下,数据见[适用范围与精度](accuracy.md#lite-轻量链路): +轻量链路与主链的接口形状基本一致,但性能取决于主链模型、历元缓存和是否进行搜索,不能统一称为快若干倍。Lite 本次表内接口零分配;升落步长为太阳 30 分钟、月球 15 分钟。下面保留相对旧 Legacy 主链的历史误差(2026 全年、8 个站点),新模型性能见[精度与性能](accuracy.md#性能): -| 能力 | 平均绝对误差 | P95 | 最大绝对误差 | +| 计算项 | 平均绝对误差 | P95 | 最大绝对误差 | | --- | --- | --- | --- | | `lite/sun` 日出 | `0.02 min` | `0.04 min` | `0.31 min` | | `lite/sun` 日落 | `0.02 min` | `0.06 min` | `0.35 min` | @@ -967,7 +969,7 @@ fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 1 | `lite/moon` 地心黄经 | `2.41'` | `6.82'` | `9.91'` | | `lite/moon` 地心黄纬 | `0.87'` | `1.83'` | `2.92'` | -两个包都不提供 `...N` 截断族。太阳侧的极夜 / 极昼错误是 `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET`;月球侧除 `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET` 外还有 `ERR_NOT_TODAY`,语义与主链一致。 +两个包都不提供 `...N` 族。太阳侧的极夜 / 极昼错误是 `ERR_SUN_NEVER_RISE` / `ERR_SUN_NEVER_SET`;月球侧除 `ERR_MOON_NEVER_RISE` / `ERR_MOON_NEVER_SET` 外还有 `ERR_NOT_TODAY`,语义与主链一致。 ## 参数与返回值约定 @@ -986,6 +988,8 @@ fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 1 - 升落接口的 `height` 是按椭球高(大地高)解读的观测点高程,单位米,不是正高;换算约定见[观测点高度约定](coord.md#观测点高度) - `aero = true`:按动态标准大气折射与实时视半径计算上缘过地平线;`aero = false`:只做几何中心高度过地平线的判定 +- 升落折射默认用标准大气(1010 hPa、10 ℃);需要实测气象时用 `sun.RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere` 与 `moon.RiseTimeWithAtmosphere` / `SetTimeWithAtmosphere`,每次调用传入气压(hPa)与气温(℃),恒带大气修正;原有 API 不变 +- 与天体无关的真/视高度换算在 `formula` 包:`formula.ApparentAltitude`、`formula.TrueAltitude`、`formula.RefractionFromTrueAltitude`、`formula.RefractionFromApparentAltitude` - `ApparentAltitude` / `ApparentZenith` 的 `pressureHPa`、`temperatureC` 是观测时的气压(hPa)与气温(℃) ### 零值与越界 @@ -994,8 +998,8 @@ fmt.Println(litemoon.Altitude(date, 121.4737, 31.2304), litemoon.Azimuth(date, 1 - 朦影不存在时返回 `ERR_TWILIGHT_NOT_EXISTS` - 月亮升降事件落在查询日期之外时返回 `ERR_NOT_TODAY`,对应上面“月出月落按当天日期计算”的语义 - `DownTime` / `DownTimeN` 是 `SetTime` 的废弃别名,`ERR_SUN_NEVER_DOWN` / `ERR_MOON_NEVER_DOWN` 是极昼错误的废弃别名,新代码不应使用 -- `...N` 中 `n < 0` 用全部内嵌项、`n >= 0` 截断;`n` 只影响级数长度,不改变返回单位与时区 +- `...N` 中 `n < 0` 用完整级数;半解析的 `n >= 0` 用同一张快速表,Legacy 按 `n` 截断;均不改变返回单位与时区 ### 精度与适用范围 -太阳与行星用内置 VSOP87 解析项,月球用内置 ELP2000/82 风格截断级数,覆盖 J2000 前后约 4000 年,无需外部星历文件。截断误差、月球链路的能力边界和 lite 链路的量化误差见[适用范围与精度](accuracy.md)。本手册的太阳 / 月球接口适合历法、观测辅助、科普与业余预报;航天导航、精确掩星预报和严格动力学积分需要 JPL DE 等专业星历。 +默认构建使用内置半解析模型;VSOP87/ELP2000/82 风格级数保留为 Legacy,两者都无需外部星历文件。可用区间不等于实测区间,实测误差也只对测量样本成立。按模型划分的 DE440/DE441 日月对比、快速表误差和 Lite 边界见[精度与性能](accuracy.md)。本手册的太阳/月球接口适合历法、观测辅助、科普与业余预报;航天导航、精确掩星预报和严格动力学积分还需要相应精度的星历、时标与物理模型。 diff --git a/doc/manual/sundial.md b/doc/manual/sundial.md index 2d0a71e..769c3ea 100644 --- a/doc/manual/sundial.md +++ b/doc/manual/sundial.md @@ -141,7 +141,7 @@ segsAt := dial.DeclinationCurveAt(date, 1.0) fmt.Println(len(segs), len(segsAt)) ``` -时间线把"地方平太阳时线上的等时刻点"直接投影成 `TimeLineSample`;赤纬曲线按固定赤纬或当日赤纬分段采样,段内 `Interval` 就是上面那条可用时角区间。 +时间线把"地方平太阳时线上的等时刻点"直接投影成 `TimeLineSample`;赤纬曲线按固定赤纬或当日赤纬分段采样,段内 `Interval` 即该赤纬对应的可用时角区间。 ### 赤道、水平与垂直日晷特例 @@ -208,8 +208,8 @@ func main() { 输出结果: ```text -2026-06-21 09:34:10.438158222 +0805 LTZ -hour angle=-36.456508 line@9am=-27.405871 line@now=-20.959182 +2026-06-21 09:34:10.404156224 +0805 LTZ +hour angle=-36.456649 line@9am=-27.405871 line@now=-20.959281 ``` 第一行的时区是合成的当地真太阳时区(经度 `121.4737°` 对应 `+08:05` 的 `LTZ`),所以打印值本身就体现了地方真太阳时与钟表时间的差。 @@ -233,7 +233,7 @@ decl=23.44 usable=-105.24..105.24 samples=211 2 -37.92998436772365 ``` -第一行说明当天在纬度 `31.2304°` 的水平日晷上,太阳赤纬 `23.44°` 时盘面能用的时角区间是 `[-105.24°, +105.24°]`(约 14 小时),采样 211 点;第二行是"地方平太阳时 9:30 这条时线"的两个采样点与其时角。这两个量配合就能直接画出一张带可用范围的水平日晷。 +第一行说明当天在纬度 `31.2304°` 的水平日晷上,太阳赤纬 `23.44°` 时盘面能用的时角区间是 `[-105.24°, +105.24°]`(约 14 小时),采样 211 点;第二行是"地方平太阳时 9:30 这条时线"的两个采样点与其时角。可用区间可直接作为时线的绘制裁剪范围。 ### 常见坑 @@ -327,7 +327,7 @@ fmt.Println(v.PlaneNormalAzimuth, v.PlaneNormalZenithDistance) - **时角符号**:`HourAngle` 上午为负、下午为正;`HourAngleInterval` 的取值范围是 `[-180, 180]` 且保证 `Start <= End`。跨零点的一天会被拆成多段。 - **`date` 的时区语义(易错)**:`MeanSolarTimePoint` / `MeanSolarTimeLine` 的 `date` 表示**目标地点的地方平太阳时**(通常是 `MeanSolarTime(...)` 的返回值);`ZoneTimePoint` / `ZoneTimeLine` 会忽略传入 `date` 的时分秒,只用它的年月日与时区,再用参数 `zoneTimeHours` 替换钟面时间。传错会把整条时线平移。 - **落影有效性的三个布尔量**:`SunAboveHorizon` 表示太阳在地平线以上,`PlaneIlluminated` 表示盘面朝向太阳,`Illuminated` 是两者同时成立后的最终判据;只画影子时必须看 `Illuminated`。 -- **退化情形**:当盘面法线与极轴垂直(等价于极轴晷针与盘面平行)时,`PlanarGeometry.HasFiniteCenter` 为 `false`,`CenterX`/`CenterY`/`PolarStylusLength` 返回 `NaN`、`PolarStylusPlaneAngle` 为 `0`——此时中心在无穷远,所有以中心为基准的绘制都不可用。例如纬度 `45°`、法线方位 `180°`、法线天顶距 `45°` 就落在这个退化点上。 +- **退化情形**:盘面法线与极轴垂直时 `HasFiniteCenter` 为 `false`、`CenterX`/`CenterY`/`PolarStylusLength` 为 `NaN`,中心在无穷远,以中心为基准的绘制不可用;示例见[退化情形与各面日晷口径](#退化情形与各面日晷口径)。 - **`PlaneIlluminated` 与 `Illuminated` 的区别**:前者只做几何受光判断,后者还要求太阳在地平线以上;做"这张日晷一天里能用多久"的结论时用后者。 ## 相关手册 diff --git a/doc/manual/timescale.md b/doc/manual/timescale.md index 38df956..752d48b 100644 --- a/doc/manual/timescale.md +++ b/doc/manual/timescale.md @@ -40,7 +40,7 @@ 这些接口位于根包 `b612.me/astro`。 -| 接口 | 返回值 | +| 函数 | 返回值 | | --- | --- | | `TTFromUTC(date)` | 同一时刻的 TT 读数 | | `UTCFromTT(tt)` | TT 读数对应的UTC时刻 | @@ -78,13 +78,21 @@ func main() { } ``` +```text +UTC: 2026-04-01T00:00:00Z +TT: 2026-04-01 00:01:09.184014 +UT1: 2026-04-01 00:00:00.051015 +DUT1: 0.051000 s +UTC from TT: 2026-04-01T00:00:00Z +``` + 数值换算经过浮点儒略日,往返结果可能有微小舍入差。TT/UT1 的显示格式不加 `Z`,以免被当成 UTC 时间戳。 ## 儒略日换算 API 下列函数位于 `basic`,参数与返回值是 `float64`。换算函数返回儒略日,差值函数返回秒。 -| 接口 | 输入 → 输出 | 说明 | +| 函数 | 输入 → 输出 | 说明 | | --- | --- | --- | | `UTC2TT` | UTC JD → TT JD | 1972 年前按 UT1,之后使用 TT−UTC 模型 | | `TT2UTC` | TT JD → UTC JD | 上述逆变换 | @@ -125,7 +133,7 @@ TDB−TT 使用 40 项截断级数和质量调整项。在 −3000 至 +6000 年 ## ΔT 模型 -默认模型为 SMH2016 与 Morrison 2021 的样条和长期外推,并在实测覆盖期内优先使用逐月 ΔT 表。当前表延伸到 2026 年 9 月 1 日,末点为快速观测值,尚非最终解;表外在端点作常值锚定以保持连续。`DeltaT` 对超出 ±40000 年的输入返回 `NaN`。 +默认模型为 SMH2016 与 Morrison 2021 的样条和长期外推,并在实测覆盖期内优先使用逐月 ΔT 表。当前表延伸到 2026 年 10 月 1 日,2026 年 9 月 1 日为最终值,末点为预备扩展值;表外在端点作常值锚定以保持连续。`DeltaT` 对超出 ±40000 年的输入返回 `NaN`。 | 常量 | 模型 | | --- | --- | @@ -165,6 +173,12 @@ func main() { } ``` +```text +smh2016 86.98252070239269 +espenak_meeus_2006 202.7448354310582 +nasa_canon_2006 202.7448354310582 +``` + ΔT 的未来值不能精确预知。按现有模型计算,Espenak–Meeus 2006 与默认模型在 2035、2050、2100、2200 年的差异约为 11、21、116、275 秒。远期日月食的时刻和地面路径会受此影响;与日月食目录对照时,应先统一 ΔT 模型。 ### 接入外部模型 @@ -184,19 +198,27 @@ func main() { ## 未来的 UTC 对齐口径假设 -`SetTimeScaleFuturePolicy` 与 `GetTimeScaleFuturePolicy` 选择民用时标换算政策,默认是 `TimeScaleLeapSecond`。除 `TimeScaleUT1Civil` 替换全时轴外,其余政策只控制实测窗口之后的 TT−UTC;1972 年后显式注入的 TT−UTC 覆盖始终优先。它与出图的 `TimeScaleUTC` / `TimeScaleUT1` 选项用途不同。 +> **2027-05-20 起 UTC 不再插入闰秒(闰时取代闰秒)** +> +> 2026 年 10 月 15 日,第 28 届国际计量大会(CGPM)通过[第 5 号决议《关于确保 UTC 连续性所需的技术行动》](https://www.bipm.org/en/cgpm-2026/resolution-5)(`Resolution-CGPM-28-5`,DOI `10.59161/CGPM2026RES5E`),决定**连续 UTC 自 2027 年 5 月 20 日起生效**,并把 `|UT1−UTC|` 的最大值由 0.9 秒放宽到 **3600 秒(1 小时)**。 +> +> [IERS Bulletin C 72(2026-07-06)](https://datacenter.iers.org/data/latestVersion/bulletinC.txt) 已确认 **2026 年 12 月底不插入闰秒**;闰秒机制在 2027-05-20 终止,2027 年 6 月底这一可能的插入点同样不会有闰秒。 +> +> 本库据此把内置窗口(截至 2026-10-01)之后的默认政策改为 `TimeScaleLeapHour`:1972–2026 仍按确定的闰秒表计算,历史值不变;窗口之后 TT−UTC 将长期保持 69.184 秒,直到数百年后 `|UT1−UTC|` 逼近 1 小时才需要闰时。农历现代快速表的 1900–2026 段也因此保持逐位不变。 + +`SetTimeScaleFuturePolicy` 与 `GetTimeScaleFuturePolicy` 选择民用时标换算政策,默认是 `TimeScaleLeapHour`。除 `TimeScaleUT1Civil` 替换全时轴外,其余政策只控制实测窗口之后的 TT−UTC;1972 年后显式注入的 TT−UTC 覆盖始终优先。`TimeScaleUTC` / `TimeScaleUT1` 只决定输出标签的口径,本政策决定民用时标与 TT 的换算,两者互不影响。 | 政策 | 计算假设 | | --- | --- | -| `TimeScaleLeapSecond`(零值、默认) | 从末端 TT−UTC 出发,以最少整数秒校正使外推 DUT1 回到 ±0.9 秒内 | +| `TimeScaleLeapHour`(零值、默认) | 按 ΔT 外推,以最少整小时校正使 DUT1 回到 ±3600 秒内,对应 2027-05-20 起生效的连续 UTC | +| `TimeScaleLeapSecond` | 从末端 TT−UTC 出发,以最少整数秒校正使外推 DUT1 回到 ±0.9 秒内;对应 1972–2026 的确定闰秒口径,仅供复现与对照 | | `TimeScaleAssumeUT1Tracking` | 保留末端 DUT1,TT−UTC 随外推 ΔT 平滑变化 | | `TimeScaleFreezeUTCOffset` | TT−UTC 固定在内置窗口末端值,当前为 69.184 秒 | -| `TimeScaleLeapHour` | 按 ΔT 外推,以最少整小时校正使 DUT1 回到 ±3600 秒内,用于情景计算 | | `TimeScaleUT1Civil` | 全时轴将民用时间视为 UT1,含历史闰秒表覆盖期;无显式覆盖时 DUT1 恒为 0 | -这些选项是计算假设,不是对未来闰秒或国际决议的预报。整数秒或小时校正由当前查询时刻的 ΔT 决定,不记录历次校正,也不强制落在公告的日历边界。阶跃附近不保证逐值往返恒等;步进政策依赖的 ΔT 无效或超出模型范围时返回 `NaN`,不会退回正常偏移。 +默认政策 `TimeScaleLeapHour` 直接对应 2027-05-20 起生效的连续 UTC,不再是情景假设;`TimeScaleLeapSecond` 保留用于复现 1972–2026 的确定口径或作对照。整小时校正由查询时刻的 ΔT 决定,可能不落在公告的日历月边界;步进政策依赖的 ΔT 无效或超出模型范围时返回 `NaN`。 -默认模型也不能保证任意未来日期相对真实 UTC 的误差小于 0.9 秒。需要精确民用时刻时,应使用对应日期的已发布时标数据。 +`TimeScaleLeapHour` 下 `|UT1−UTC|` 的容限是 3600 秒,因此窗口之后的 TT−UTC 在很长时间内恒为 69.184 秒;`TimeScaleLeapSecond` 仍按 ±0.9 秒外推,会给出每 1–2 年一次阶跃的旧行为,不代表现实。需要精确民用时刻时,应使用对应日期的已发布时标数据。 ## SVG、GeoJSON 与 KML 的时间标签 diff --git a/eclipse/api_default.go b/eclipse/api_default.go new file mode 100644 index 0000000..cdcbc3a --- /dev/null +++ b/eclipse/api_default.go @@ -0,0 +1,492 @@ +package eclipse + +import ( + "time" +) + +// ClosestGeometricLocalLunarEclipse 最近一次几何月食,默认 Danjon 模型 / closest geometric local lunar eclipse, Danjon by default. +func ClosestGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestGeometricLocalLunarEclipse(date, lon, lat, height) +} + +// ClosestGeometricLocalLunarEclipseChauvenet 最近一次几何月食(Chauvenet) / closest geometric local lunar eclipse with Chauvenet model. +func ClosestGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestGeometricLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// ClosestGeometricLocalLunarEclipseDanjon 最近一次几何月食(Danjon) / closest geometric local lunar eclipse with Danjon model. +func ClosestGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// ClosestGeometricLocalSolarEclipse 最近一次站心几何日食,默认 NASA bulletin Split-K 模型 / closest geometric local solar eclipse, NASA bulletin Split-K by default. +func ClosestGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestGeometricLocalSolarEclipse(date, lon, lat, height) +} + +// ClosestGeometricLocalSolarEclipseIAUSingleK 最近一次站心几何日食(IAU Single-K) / closest geometric local solar eclipse with IAU Single-K. +func ClosestGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestGeometricLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// ClosestGeometricLocalSolarEclipseNASABulletinSplitK 最近一次站心几何日食(NASA bulletin Split-K) / closest geometric local solar eclipse with NASA bulletin Split-K. +func ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// ClosestLocalAnnularSolarEclipse 最近一次站心日环食,默认 NASA bulletin Split-K 模型 / closest local annular solar eclipse, NASA bulletin Split-K by default. +func ClosestLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.ClosestLocalAnnularSolarEclipse(date, lon, lat, height) +} + +// ClosestLocalLunarEclipse 最近一次可见月食,默认 Danjon 模型 / closest visible local lunar eclipse, Danjon by default. +func ClosestLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestLocalLunarEclipse(date, lon, lat, height) +} + +// ClosestLocalLunarEclipseChauvenet 最近一次可见月食(Chauvenet) / closest visible local lunar eclipse with Chauvenet model. +func ClosestLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// ClosestLocalLunarEclipseDanjon 最近一次可见月食(Danjon) / closest visible local lunar eclipse with Danjon model. +func ClosestLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.ClosestLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// ClosestLocalSolarEclipse 最近一次站心日食,默认 NASA bulletin Split-K 模型 / closest local solar eclipse, NASA bulletin Split-K by default. +func ClosestLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestLocalSolarEclipse(date, lon, lat, height) +} + +// ClosestLocalSolarEclipseIAUSingleK 最近一次站心日食(IAU Single-K) / closest local solar eclipse with IAU Single-K. +func ClosestLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// ClosestLocalSolarEclipseNASABulletinSplitK 最近一次站心日食(NASA bulletin Split-K) / closest local solar eclipse with NASA bulletin Split-K. +func ClosestLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.ClosestLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// ClosestLocalTotalLunarEclipse 最近一次可见月全食,默认 Danjon 模型 / closest visible local total lunar eclipse, Danjon by default. +func ClosestLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.ClosestLocalTotalLunarEclipse(date, lon, lat, height) +} + +// ClosestLocalTotalSolarEclipse 最近一次站心日全食,默认 NASA bulletin Split-K 模型 / closest local total solar eclipse, NASA bulletin Split-K by default. +func ClosestLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.ClosestLocalTotalSolarEclipse(date, lon, lat, height) +} + +// ClosestSolarEclipse 最近一次日食,默认 NASA bulletin Split-K 模型 / closest solar eclipse, NASA bulletin Split-K by default. +func ClosestSolarEclipse(date time.Time) SolarEclipseInfo { + return defaultModule.ClosestSolarEclipse(date) +} + +// ClosestSolarEclipseIAUSingleK 最近一次日食(IAU Single-K) / closest solar eclipse with IAU Single-K. +func ClosestSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + return defaultModule.ClosestSolarEclipseIAUSingleK(date) +} + +// ClosestSolarEclipseNASABulletinSplitK 最近一次日食(NASA bulletin Split-K) / closest solar eclipse with NASA bulletin Split-K. +func ClosestSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + return defaultModule.ClosestSolarEclipseNASABulletinSplitK(date) +} + +// ClosestSolarEclipseWithOptions 最近一次日食(自定义半径口径) / closest solar eclipse with custom radius conventions. +func ClosestSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + return defaultModule.ClosestSolarEclipseWithOptions(date, options) +} + +// GeometricLocalLunarEclipseOnDate 当地几何月食查询,默认 Danjon 模型 / local geometric lunar eclipse query, Danjon by default. +func GeometricLocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.GeometricLocalLunarEclipseOnDate(date, lon, lat, height) +} + +// GeometricLocalLunarEclipseOnDateChauvenet 当地几何月食查询(Chauvenet) / local geometric lunar eclipse query with Chauvenet model. +func GeometricLocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.GeometricLocalLunarEclipseOnDateChauvenet(date, lon, lat, height) +} + +// GeometricLocalLunarEclipseOnDateDanjon 当地几何月食查询(Danjon) / local geometric lunar eclipse query with Danjon model. +func GeometricLocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.GeometricLocalLunarEclipseOnDateDanjon(date, lon, lat, height) +} + +// GeometricLocalSolarEclipseOnDate 当地站心几何日食查询,默认 NASA bulletin Split-K 模型 / local geometric solar eclipse query, NASA bulletin Split-K by default. +func GeometricLocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.GeometricLocalSolarEclipseOnDate(date, lon, lat, height) +} + +// GeometricLocalSolarEclipseOnDateIAUSingleK 当地站心几何日食查询(IAU Single-K) / local geometric solar eclipse query with IAU Single-K. +func GeometricLocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.GeometricLocalSolarEclipseOnDateIAUSingleK(date, lon, lat, height) +} + +// GeometricLocalSolarEclipseOnDateNASABulletinSplitK 当地站心几何日食查询(NASA bulletin Split-K) / local geometric solar eclipse query with NASA bulletin Split-K. +func GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) +} + +// LastGeometricLocalLunarEclipse 上次几何月食,默认 Danjon 模型 / previous geometric local lunar eclipse, Danjon by default. +func LastGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastGeometricLocalLunarEclipse(date, lon, lat, height) +} + +// LastGeometricLocalLunarEclipseChauvenet 上次几何月食(Chauvenet) / previous geometric local lunar eclipse with Chauvenet model. +func LastGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastGeometricLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// LastGeometricLocalLunarEclipseDanjon 上次几何月食(Danjon) / previous geometric local lunar eclipse with Danjon model. +func LastGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// LastGeometricLocalSolarEclipse 上次站心几何日食,默认 NASA bulletin Split-K 模型 / previous geometric local solar eclipse, NASA bulletin Split-K by default. +func LastGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastGeometricLocalSolarEclipse(date, lon, lat, height) +} + +// LastGeometricLocalSolarEclipseIAUSingleK 上次站心几何日食(IAU Single-K) / previous geometric local solar eclipse with IAU Single-K. +func LastGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastGeometricLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// LastGeometricLocalSolarEclipseNASABulletinSplitK 上次站心几何日食(NASA bulletin Split-K) / previous geometric local solar eclipse with NASA bulletin Split-K. +func LastGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// LastLocalAnnularSolarEclipse 上次站心日环食,默认 NASA bulletin Split-K 模型 / previous local annular solar eclipse, NASA bulletin Split-K by default. +func LastLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.LastLocalAnnularSolarEclipse(date, lon, lat, height) +} + +// LastLocalLunarEclipse 上次可见月食,默认 Danjon 模型 / previous visible local lunar eclipse, Danjon by default. +func LastLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastLocalLunarEclipse(date, lon, lat, height) +} + +// LastLocalLunarEclipseChauvenet 上次可见月食(Chauvenet) / previous visible local lunar eclipse with Chauvenet model. +func LastLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// LastLocalLunarEclipseDanjon 上次可见月食(Danjon) / previous visible local lunar eclipse with Danjon model. +func LastLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.LastLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// LastLocalSolarEclipse 上次站心日食,默认 NASA bulletin Split-K 模型 / previous local solar eclipse, NASA bulletin Split-K by default. +func LastLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastLocalSolarEclipse(date, lon, lat, height) +} + +// LastLocalSolarEclipseIAUSingleK 上次站心日食(IAU Single-K) / previous local solar eclipse with IAU Single-K. +func LastLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// LastLocalSolarEclipseNASABulletinSplitK 上次站心日食(NASA bulletin Split-K) / previous local solar eclipse with NASA bulletin Split-K. +func LastLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.LastLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// LastLocalTotalLunarEclipse 上次可见月全食,默认 Danjon 模型 / previous visible local total lunar eclipse, Danjon by default. +func LastLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.LastLocalTotalLunarEclipse(date, lon, lat, height) +} + +// LastLocalTotalSolarEclipse 上次站心日全食,默认 NASA bulletin Split-K 模型 / previous local total solar eclipse, NASA bulletin Split-K by default. +func LastLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.LastLocalTotalSolarEclipse(date, lon, lat, height) +} + +// LastSolarEclipse 上次日食,默认 NASA bulletin Split-K 模型 / previous solar eclipse, NASA bulletin Split-K by default. +func LastSolarEclipse(date time.Time) SolarEclipseInfo { return defaultModule.LastSolarEclipse(date) } + +// LastSolarEclipseIAUSingleK 上次日食(IAU Single-K) / previous solar eclipse with IAU Single-K. +func LastSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + return defaultModule.LastSolarEclipseIAUSingleK(date) +} + +// LastSolarEclipseNASABulletinSplitK 上次日食(NASA bulletin Split-K) / previous solar eclipse with NASA bulletin Split-K. +func LastSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + return defaultModule.LastSolarEclipseNASABulletinSplitK(date) +} + +// LastSolarEclipseWithOptions 上次日食(自定义半径口径) / previous solar eclipse with custom radius conventions. +func LastSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + return defaultModule.LastSolarEclipseWithOptions(date, options) +} + +// LocalLunarEclipseOnDate 当地可见月食查询,默认 Danjon 模型 / local visible lunar eclipse query, Danjon by default. +func LocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.LocalLunarEclipseOnDate(date, lon, lat, height) +} + +// LocalLunarEclipseOnDateChauvenet 当地可见月食查询(Chauvenet) / local visible lunar eclipse query with Chauvenet model. +func LocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.LocalLunarEclipseOnDateChauvenet(date, lon, lat, height) +} + +// LocalLunarEclipseOnDateDanjon 当地可见月食查询(Danjon) / local visible lunar eclipse query with Danjon model. +func LocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.LocalLunarEclipseOnDateDanjon(date, lon, lat, height) +} + +// LocalSolarEclipseOnDate 当地站心日食查询,默认 NASA bulletin Split-K 模型 / local topocentric solar eclipse query, NASA bulletin Split-K by default. +func LocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.LocalSolarEclipseOnDate(date, lon, lat, height) +} + +// LocalSolarEclipseOnDateIAUSingleK 当地站心日食查询(IAU Single-K) / local topocentric solar eclipse query with IAU Single-K. +func LocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.LocalSolarEclipseOnDateIAUSingleK(date, lon, lat, height) +} + +// LocalSolarEclipseOnDateNASABulletinSplitK 当地站心日食查询(NASA bulletin Split-K) / local topocentric solar eclipse query with NASA bulletin Split-K. +func LocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.LocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) +} + +// LocalSolarEclipseOnDateWithOptions 当地站心日食查询(自定义半径口径) / local topocentric solar eclipse query with custom radius conventions. +func LocalSolarEclipseOnDateWithOptions(date time.Time, lon, lat, height float64, options SolarEclipseOptions) (LocalSolarEclipseInfo, bool) { + return defaultModule.LocalSolarEclipseOnDateWithOptions(date, lon, lat, height, options) +} + +// LunarEclipseGeocentricPanelAt 计算给定日期月食的地心量面板,使用 Danjon 影半径模型 / computes the geocentric panel of one lunar eclipse with the Danjon shadow model. +func LunarEclipseGeocentricPanelAt(date time.Time) (LunarEclipseGeocentricPanel, bool) { + return defaultModule.LunarEclipseGeocentricPanelAt(date) +} + +// LunarEclipseGeocentricPanelChauvenet 计算给定日期月食的地心量面板,使用 Chauvenet 影半径模型 / computes the geocentric panel with the Chauvenet shadow model. +func LunarEclipseGeocentricPanelChauvenet(date time.Time) (LunarEclipseGeocentricPanel, bool) { + return defaultModule.LunarEclipseGeocentricPanelChauvenet(date) +} + +// NewSolarEclipseShadowSolver 构造单时刻求解器 / builds a single-instant solver. +func NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver { + return defaultModule.NewSolarEclipseShadowSolver(options) +} + +// NextGeometricLocalLunarEclipse 下次几何月食,默认 Danjon 模型 / next geometric local lunar eclipse, Danjon by default. +func NextGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextGeometricLocalLunarEclipse(date, lon, lat, height) +} + +// NextGeometricLocalLunarEclipseChauvenet 下次几何月食(Chauvenet) / next geometric local lunar eclipse with Chauvenet model. +func NextGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextGeometricLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// NextGeometricLocalLunarEclipseDanjon 下次几何月食(Danjon) / next geometric local lunar eclipse with Danjon model. +func NextGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// NextGeometricLocalSolarEclipse 下次站心几何日食,默认 NASA bulletin Split-K 模型 / next geometric local solar eclipse, NASA bulletin Split-K by default. +func NextGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextGeometricLocalSolarEclipse(date, lon, lat, height) +} + +// NextGeometricLocalSolarEclipseIAUSingleK 下次站心几何日食(IAU Single-K) / next geometric local solar eclipse with IAU Single-K. +func NextGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextGeometricLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// NextGeometricLocalSolarEclipseNASABulletinSplitK 下次站心几何日食(NASA bulletin Split-K) / next geometric local solar eclipse with NASA bulletin Split-K. +func NextGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// NextLocalAnnularSolarEclipse 下次站心日环食,默认 NASA bulletin Split-K 模型 / next local annular solar eclipse, NASA bulletin Split-K by default. +func NextLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.NextLocalAnnularSolarEclipse(date, lon, lat, height) +} + +// NextLocalLunarEclipse 下次可见月食,默认 Danjon 模型 / next visible local lunar eclipse, Danjon by default. +func NextLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextLocalLunarEclipse(date, lon, lat, height) +} + +// NextLocalLunarEclipseChauvenet 下次可见月食(Chauvenet) / next visible local lunar eclipse with Chauvenet model. +func NextLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextLocalLunarEclipseChauvenet(date, lon, lat, height) +} + +// NextLocalLunarEclipseDanjon 下次可见月食(Danjon) / next visible local lunar eclipse with Danjon model. +func NextLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return defaultModule.NextLocalLunarEclipseDanjon(date, lon, lat, height) +} + +// NextLocalSolarEclipse 下次站心日食,默认 NASA bulletin Split-K 模型 / next local solar eclipse, NASA bulletin Split-K by default. +func NextLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextLocalSolarEclipse(date, lon, lat, height) +} + +// NextLocalSolarEclipseIAUSingleK 下次站心日食(IAU Single-K) / next local solar eclipse with IAU Single-K. +func NextLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextLocalSolarEclipseIAUSingleK(date, lon, lat, height) +} + +// NextLocalSolarEclipseNASABulletinSplitK 下次站心日食(NASA bulletin Split-K) / next local solar eclipse with NASA bulletin Split-K. +func NextLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return defaultModule.NextLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +} + +// NextLocalTotalLunarEclipse 下次可见月全食,默认 Danjon 模型 / next visible local total lunar eclipse, Danjon by default. +func NextLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return defaultModule.NextLocalTotalLunarEclipse(date, lon, lat, height) +} + +// NextLocalTotalSolarEclipse 下次站心日全食,默认 NASA bulletin Split-K 模型 / next local total solar eclipse, NASA bulletin Split-K by default. +func NextLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return defaultModule.NextLocalTotalSolarEclipse(date, lon, lat, height) +} + +// NextSolarEclipse 下次日食,默认 NASA bulletin Split-K 模型 / next solar eclipse, NASA bulletin Split-K by default. +func NextSolarEclipse(date time.Time) SolarEclipseInfo { return defaultModule.NextSolarEclipse(date) } + +// NextSolarEclipseIAUSingleK 下次日食(IAU Single-K) / next solar eclipse with IAU Single-K. +func NextSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + return defaultModule.NextSolarEclipseIAUSingleK(date) +} + +// NextSolarEclipseNASABulletinSplitK 下次日食(NASA bulletin Split-K) / next solar eclipse with NASA bulletin Split-K. +func NextSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + return defaultModule.NextSolarEclipseNASABulletinSplitK(date) +} + +// NextSolarEclipseWithOptions 下次日食(自定义半径口径) / next solar eclipse with custom radius conventions. +func NextSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + return defaultModule.NextSolarEclipseWithOptions(date, options) +} + +// SearchLocalCentralSolarEclipse 在给定跨度内搜索站心食并回报是否用尽跨度 / station search within a horizon. +func SearchLocalCentralSolarEclipse( + date time.Time, lon, lat, height float64, + options SolarEclipseLocalSearchOptions, +) (LocalSolarEclipseInfo, SolarEclipseLocalSearchStatus) { + return defaultModule.SearchLocalCentralSolarEclipse(date, lon, lat, height, options) +} + +// SolarEclipseBesselianElements 计算给定近朔时刻附近一次日食的多项式贝塞尔根数,窗口内无日食时返回 false / Polynomial Besselian elements for the solar eclipse near the given new-moon instant. +func SolarEclipseBesselianElements( + seedJDE float64, options SolarEclipseBesselianElementsOptions, +) (SolarEclipseBesselianElementsResult, bool) { + return defaultModule.SolarEclipseBesselianElements(seedJDE, options) +} + +// SolarEclipseCandidates 枚举区间内的日食时刻表,按时间升序 / eclipses within a range, in time order. +func SolarEclipseCandidates( + start, end time.Time, options SolarEclipseCandidateOptions, +) []SolarEclipseCandidate { + return defaultModule.SolarEclipseCandidates(start, end, options) +} + +// SolarEclipseCentralPath 计算指定日期附近的日食中心路径,默认使用 NASA bulletin Split-K 模型 / computes the central path near the given date, using NASA bulletin Split-K by default. +func SolarEclipseCentralPath(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return defaultModule.SolarEclipseCentralPath(date, options) +} + +// SolarEclipseCentralPathIAUSingleK 使用 IAU Single-K 模型计算日食中心路径 / computes the central path with the IAU Single-K model. +func SolarEclipseCentralPathIAUSingleK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return defaultModule.SolarEclipseCentralPathIAUSingleK(date, options) +} + +// SolarEclipseCentralPathNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食中心路径 / computes the central path with the NASA bulletin Split-K model. +func SolarEclipseCentralPathNASABulletinSplitK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return defaultModule.SolarEclipseCentralPathNASABulletinSplitK(date, options) +} + +// SolarEclipseGeocentricPanelAt 计算给定日食在食甚时刻的地心量面板 / computes the geocentric panel of one eclipse at greatest eclipse. +func SolarEclipseGeocentricPanelAt(date time.Time) (SolarEclipseGeocentricPanel, bool) { + return defaultModule.SolarEclipseGeocentricPanelAt(date) +} + +// SolarEclipseGeocentricPanelIAUSingleK 使用 IAU Single-K 计算地心量面板 / computes the geocentric panel with the IAU Single-K model. +func SolarEclipseGeocentricPanelIAUSingleK(date time.Time) (SolarEclipseGeocentricPanel, bool) { + return defaultModule.SolarEclipseGeocentricPanelIAUSingleK(date) +} + +// SolarEclipseGeocentricPanelWithOptions 使用自定义半径口径计算食甚时刻的地心量面板 / computes the geocentric panel at greatest eclipse with custom radius conventions. +func SolarEclipseGeocentricPanelWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseGeocentricPanel, bool) { + return defaultModule.SolarEclipseGeocentricPanelWithOptions(date, options) +} + +// SolarEclipseOnDate 当地自然日全局日食查询,默认 NASA bulletin Split-K 模型 / local-date global solar eclipse query, NASA bulletin Split-K by default. +func SolarEclipseOnDate(date time.Time) (SolarEclipseInfo, bool) { + return defaultModule.SolarEclipseOnDate(date) +} + +// SolarEclipseOnDateIAUSingleK 当地自然日全局日食查询(IAU Single-K) / local-date global solar eclipse query with IAU Single-K. +func SolarEclipseOnDateIAUSingleK(date time.Time) (SolarEclipseInfo, bool) { + return defaultModule.SolarEclipseOnDateIAUSingleK(date) +} + +// SolarEclipseOnDateNASABulletinSplitK 当地自然日全局日食查询(NASA bulletin Split-K) / local-date global solar eclipse query with NASA bulletin Split-K. +func SolarEclipseOnDateNASABulletinSplitK(date time.Time) (SolarEclipseInfo, bool) { + return defaultModule.SolarEclipseOnDateNASABulletinSplitK(date) +} + +// SolarEclipseOnDateWithOptions 当地自然日全局日食查询(自定义半径口径) / local-date global solar eclipse query with custom radius conventions. +func SolarEclipseOnDateWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseInfo, bool) { + return defaultModule.SolarEclipseOnDateWithOptions(date, options) +} + +// SolarEclipsePartialArea 计算半影足迹,是 SolarEclipsePartialFootprints 的兼容包装 / computes penumbral footprint samples and is a compatibility wrapper for SolarEclipsePartialFootprints. +func SolarEclipsePartialArea(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return defaultModule.SolarEclipsePartialArea(date, options) +} + +// SolarEclipsePartialAreaIAUSingleK 是 SolarEclipsePartialFootprintsIAUSingleK 的兼容包装 / is a compatibility wrapper for SolarEclipsePartialFootprintsIAUSingleK. +func SolarEclipsePartialAreaIAUSingleK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return defaultModule.SolarEclipsePartialAreaIAUSingleK(date, options) +} + +// SolarEclipsePartialAreaNASABulletinSplitK 是 SolarEclipsePartialFootprintsNASABulletinSplitK 的兼容包装 / is a compatibility wrapper for SolarEclipsePartialFootprintsNASABulletinSplitK. +func SolarEclipsePartialAreaNASABulletinSplitK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return defaultModule.SolarEclipsePartialAreaNASABulletinSplitK(date, options) +} + +// SolarEclipsePartialFootprints 计算指定日期附近的日食半影足迹,默认使用 NASA bulletin Split-K 模型 / computes penumbral footprint samples near the given date, using NASA bulletin Split-K by default. +func SolarEclipsePartialFootprints(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return defaultModule.SolarEclipsePartialFootprints(date, options) +} + +// SolarEclipsePartialFootprintsIAUSingleK 使用 IAU Single-K 模型计算日食半影足迹 / computes penumbral footprint samples with the IAU Single-K model. +func SolarEclipsePartialFootprintsIAUSingleK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return defaultModule.SolarEclipsePartialFootprintsIAUSingleK(date, options) +} + +// SolarEclipsePartialFootprintsNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食半影足迹 / computes penumbral footprint samples with the NASA bulletin Split-K model. +func SolarEclipsePartialFootprintsNASABulletinSplitK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return defaultModule.SolarEclipsePartialFootprintsNASABulletinSplitK(date, options) +} + +// SolarEclipseShadowAt 无状态便捷入口,可并发调用 / stateless convenience entry point. +func SolarEclipseShadowAt(value time.Time) (SolarEclipseShadowInstant, bool) { + return defaultModule.SolarEclipseShadowAt(value) +} + +// SolarEclipseShadowBetween 无状态批量入口,使用进程级 ΔT / stateless batch entry point. +func SolarEclipseShadowBetween( + start, end time.Time, step time.Duration, +) []SolarEclipseShadowInstant { + return defaultModule.SolarEclipseShadowBetween(start, end, step) +} + +// SolarEclipseShadowBetweenWithOptions 可显式指定 ΔT 与模型 / with explicit ΔT and model. +func SolarEclipseShadowBetweenWithOptions( + start, end time.Time, step time.Duration, options SolarEclipseShadowSolverOptions, +) []SolarEclipseShadowInstant { + return defaultModule.SolarEclipseShadowBetweenWithOptions(start, end, step, options) +} + +// SolarEclipseStationStateAt 无状态便捷入口,可并发调用 / stateless convenience entry point. +func SolarEclipseStationStateAt(value time.Time, lon, lat, height float64) SolarEclipseStationState { + return defaultModule.SolarEclipseStationStateAt(value, lon, lat, height) +} diff --git a/eclipse/context.go b/eclipse/context.go new file mode 100644 index 0000000..9279f98 --- /dev/null +++ b/eclipse/context.go @@ -0,0 +1,159 @@ +package eclipse + +import ( + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +// Eclipse 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Eclipse struct { + inst *ephem.Instance + // lunarLimb 是站心日食与月食的默认月缘剖面,nil 为圆月面。 + lunarLimb LunarLimb +} + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Eclipse { return &Eclipse{inst: inst} } + +// WithLunarLimb 返回使用指定月缘剖面的新模块,原模块不变;nil 恢复圆月面 / +// a copy of the module using the given lunar limb; nil restores the circular limb. +func (module *Eclipse) WithLunarLimb(limb LunarLimb) *Eclipse { + copied := *module + copied.lunarLimb = limb + return &copied +} + +// lunarResult 按模块星历与月缘计算一次月食;未设置月缘时与包级计算相同。 +func (module *Eclipse) lunarResult(seedJDE float64, chauvenet bool) basic.LunarEclipseResult { + result := basic.LunarEclipseDanjonWithInstance(seedJDE, module.inst) + if chauvenet { + result = basic.LunarEclipseChauvenetWithInstance(seedJDE, module.inst) + } + return basic.LunarEclipseWithLunarLimb(result, module.lunarLimb, module.inst) +} + +// withLunarLimb 对已求出的全局月食按模块月缘重算接触时刻与接触点。 +func (module *Eclipse) withLunarLimb(info LunarEclipseInfo) LunarEclipseInfo { + if module.lunarLimb == nil || info.Type == LunarEclipseNone || info.Maximum.IsZero() { + return info + } + result := module.lunarResult(timeToTTJDE(info.Maximum), basic.LunarEclipseShadowModel(info.ShadowModel) == basic.LunarEclipseShadowModelChauvenet) + if result.Type == basic.LunarEclipseNone { + return info + } + return lunarEclipseInfoFromBasicWithInstance(result, info.Maximum.Location(), module.inst) +} + +var defaultModule Eclipse + +func (module *Eclipse) model() ephem.Model { + if module.inst == nil { + return ephem.Active() + } + return module.inst.Model() +} + +// ClosestLunarEclipse 最近一次月食 / closest lunar eclipse. +func (module *Eclipse) ClosestLunarEclipse(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(ClosestLunarEclipseWithInstance(date, module.inst)) +} + +// ClosestLunarEclipseChauvenet 最近一次月食(Chauvenet) / closest lunar eclipse with Chauvenet model. +func (module *Eclipse) ClosestLunarEclipseChauvenet(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(ClosestLunarEclipseChauvenetWithInstance(date, module.inst)) +} + +// ClosestLunarEclipseDanjon 最近一次月食(Danjon) / closest lunar eclipse with Danjon model. +func (module *Eclipse) ClosestLunarEclipseDanjon(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(ClosestLunarEclipseDanjonWithInstance(date, module.inst)) +} + +// LastLunarEclipse 上次月食 / previous lunar eclipse. +func (module *Eclipse) LastLunarEclipse(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(LastLunarEclipseWithInstance(date, module.inst)) +} + +// LastLunarEclipseChauvenet 上次月食(Chauvenet) / previous lunar eclipse with Chauvenet model. +func (module *Eclipse) LastLunarEclipseChauvenet(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(LastLunarEclipseChauvenetWithInstance(date, module.inst)) +} + +// LastLunarEclipseDanjon 上次月食(Danjon) / previous lunar eclipse with Danjon model. +func (module *Eclipse) LastLunarEclipseDanjon(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(LastLunarEclipseDanjonWithInstance(date, module.inst)) +} + +// LocalSolarEclipseInfoInUT1 返回各阶段与接触点换成 UT1 时刻的副本 / copy in UT1. +func (module *Eclipse) LocalSolarEclipseInfoInUT1(info LocalSolarEclipseInfo) LocalSolarEclipseInfo { + return LocalSolarEclipseInfoInUT1(info) +} + +// LunarEclipseInfoInUT1 返回各阶段与接触点换成 UT1 时刻的副本 / copy in UT1. +func (module *Eclipse) LunarEclipseInfoInUT1(info LunarEclipseInfo) LunarEclipseInfo { + return LunarEclipseInfoInUT1(info) +} + +// LunarEclipseOnDate 当地自然日月食查询 / local-date lunar eclipse query. +func (module *Eclipse) LunarEclipseOnDate(date time.Time) (LunarEclipseInfo, bool) { + info, ok := LunarEclipseOnDateWithInstance(date, module.inst) + return module.withLunarLimb(info), ok +} + +// LunarEclipseOnDateChauvenet 当地自然日月食查询(Chauvenet) / local-date lunar eclipse query with Chauvenet model. +func (module *Eclipse) LunarEclipseOnDateChauvenet(date time.Time) (LunarEclipseInfo, bool) { + info, ok := LunarEclipseOnDateChauvenetWithInstance(date, module.inst) + return module.withLunarLimb(info), ok +} + +// LunarEclipseOnDateDanjon 当地自然日月食查询(Danjon) / local-date lunar eclipse query with Danjon model. +func (module *Eclipse) LunarEclipseOnDateDanjon(date time.Time) (LunarEclipseInfo, bool) { + info, ok := LunarEclipseOnDateDanjonWithInstance(date, module.inst) + return module.withLunarLimb(info), ok +} + +// NextLunarEclipse 下次月食 / next lunar eclipse. +func (module *Eclipse) NextLunarEclipse(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(NextLunarEclipseWithInstance(date, module.inst)) +} + +// NextLunarEclipseChauvenet 下次月食(Chauvenet) / next lunar eclipse with Chauvenet model. +func (module *Eclipse) NextLunarEclipseChauvenet(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(NextLunarEclipseChauvenetWithInstance(date, module.inst)) +} + +// NextLunarEclipseDanjon 下次月食(Danjon) / next lunar eclipse with Danjon model. +func (module *Eclipse) NextLunarEclipseDanjon(date time.Time) LunarEclipseInfo { + return module.withLunarLimb(NextLunarEclipseDanjonWithInstance(date, module.inst)) +} + +// SolarEclipseBesselianMuForPublishedTable 把本库的 Mu 换算成与已发布根数表直接可比的取值 / convert Mu to the convention of published element tables. +func (module *Eclipse) SolarEclipseBesselianMuForPublishedTable( + mu SolarEclipseBesselianPolynomial, deltaTSeconds float64, +) SolarEclipseBesselianPolynomial { + return SolarEclipseBesselianMuForPublishedTable(mu, deltaTSeconds) +} + +// SolarEclipseGeocentricPanelInUT1 返回两个朔时刻换成 UT1 时刻的副本 / copy in UT1. +func (module *Eclipse) SolarEclipseGeocentricPanelInUT1(panel SolarEclipseGeocentricPanel) SolarEclipseGeocentricPanel { + return SolarEclipseGeocentricPanelInUT1(panel) +} + +// SolarEclipseInfoInUT1 返回各阶段时刻换成 UT1 时刻的副本 / returns a copy in UT1. +func (module *Eclipse) SolarEclipseInfoInUT1(info SolarEclipseInfo) SolarEclipseInfo { + return SolarEclipseInfoInUT1(info) +} + +// SolarEclipsePartialFootprintsInUT1 返回足迹、轮廓与接触点都换成 UT1 时刻的副本 / copy in UT1. +func (module *Eclipse) SolarEclipsePartialFootprintsInUT1(info SolarEclipsePartialFootprintsInfo) SolarEclipsePartialFootprintsInfo { + return SolarEclipsePartialFootprintsInUT1(info) +} + +// SolarEclipsePathInUT1 返回中心线与限界都换成 UT1 时刻的副本 / copy in UT1. +func (module *Eclipse) SolarEclipsePathInUT1(path SolarEclipsePath) SolarEclipsePath { + return SolarEclipsePathInUT1(path) +} + +// TimeLabelsInUT1 返回换成 UT1 时刻的新切片 / returns a copy in UT1. +func (module *Eclipse) TimeLabelsInUT1(times []time.Time) []time.Time { return TimeLabelsInUT1(times) } diff --git a/eclipse/context_test.go b/eclipse/context_test.go new file mode 100644 index 0000000..b17601a --- /dev/null +++ b/eclipse/context_test.go @@ -0,0 +1,186 @@ +package eclipse + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "reflect" + "testing" + "time" +) + +type contextAPI interface { + ClosestGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + ClosestLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + ClosestLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + ClosestLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + ClosestLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + ClosestLunarEclipse(date time.Time) LunarEclipseInfo + ClosestLunarEclipseChauvenet(date time.Time) LunarEclipseInfo + ClosestLunarEclipseDanjon(date time.Time) LunarEclipseInfo + ClosestSolarEclipse(date time.Time) SolarEclipseInfo + ClosestSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo + ClosestSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo + ClosestSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo + GeometricLocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + GeometricLocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + GeometricLocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + GeometricLocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + GeometricLocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LastGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LastLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + LastLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + LastLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + LastLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LastLunarEclipse(date time.Time) LunarEclipseInfo + LastLunarEclipseChauvenet(date time.Time) LunarEclipseInfo + LastLunarEclipseDanjon(date time.Time) LunarEclipseInfo + LastSolarEclipse(date time.Time) SolarEclipseInfo + LastSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo + LastSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo + LastSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo + LocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + LocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + LocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + LocalSolarEclipseInfoInUT1(info LocalSolarEclipseInfo) LocalSolarEclipseInfo + LocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + LocalSolarEclipseOnDateWithOptions(date time.Time, lon, lat, height float64, options SolarEclipseOptions) (LocalSolarEclipseInfo, bool) + LunarEclipseGeocentricPanelAt(date time.Time) (LunarEclipseGeocentricPanel, bool) + LunarEclipseGeocentricPanelChauvenet(date time.Time) (LunarEclipseGeocentricPanel, bool) + LunarEclipseInfoInUT1(info LunarEclipseInfo) LunarEclipseInfo + LunarEclipseOnDate(date time.Time) (LunarEclipseInfo, bool) + LunarEclipseOnDateChauvenet(date time.Time) (LunarEclipseInfo, bool) + LunarEclipseOnDateDanjon(date time.Time) (LunarEclipseInfo, bool) + NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver + NextGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + NextLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo + NextLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo + NextLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) + NextLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) + NextLunarEclipse(date time.Time) LunarEclipseInfo + NextLunarEclipseChauvenet(date time.Time) LunarEclipseInfo + NextLunarEclipseDanjon(date time.Time) LunarEclipseInfo + NextSolarEclipse(date time.Time) SolarEclipseInfo + NextSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo + NextSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo + NextSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo + SearchLocalCentralSolarEclipse( + date time.Time, lon, lat, height float64, + options SolarEclipseLocalSearchOptions, + ) (LocalSolarEclipseInfo, SolarEclipseLocalSearchStatus) + SolarEclipseBesselianElements( + seedJDE float64, options SolarEclipseBesselianElementsOptions, + ) (SolarEclipseBesselianElementsResult, bool) + SolarEclipseBesselianMuForPublishedTable( + mu SolarEclipseBesselianPolynomial, deltaTSeconds float64, + ) SolarEclipseBesselianPolynomial + SolarEclipseCandidates( + start, end time.Time, options SolarEclipseCandidateOptions, + ) []SolarEclipseCandidate + SolarEclipseCentralPath(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) + SolarEclipseCentralPathIAUSingleK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) + SolarEclipseCentralPathNASABulletinSplitK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) + SolarEclipseGeocentricPanelAt(date time.Time) (SolarEclipseGeocentricPanel, bool) + SolarEclipseGeocentricPanelIAUSingleK(date time.Time) (SolarEclipseGeocentricPanel, bool) + SolarEclipseGeocentricPanelInUT1(panel SolarEclipseGeocentricPanel) SolarEclipseGeocentricPanel + SolarEclipseGeocentricPanelWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseGeocentricPanel, bool) + SolarEclipseInfoInUT1(info SolarEclipseInfo) SolarEclipseInfo + SolarEclipseOnDate(date time.Time) (SolarEclipseInfo, bool) + SolarEclipseOnDateIAUSingleK(date time.Time) (SolarEclipseInfo, bool) + SolarEclipseOnDateNASABulletinSplitK(date time.Time) (SolarEclipseInfo, bool) + SolarEclipseOnDateWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseInfo, bool) + SolarEclipsePartialArea(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) + SolarEclipsePartialAreaIAUSingleK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) + SolarEclipsePartialAreaNASABulletinSplitK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) + SolarEclipsePartialFootprints(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) + SolarEclipsePartialFootprintsIAUSingleK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) + SolarEclipsePartialFootprintsInUT1(info SolarEclipsePartialFootprintsInfo) SolarEclipsePartialFootprintsInfo + SolarEclipsePartialFootprintsNASABulletinSplitK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) + SolarEclipsePathInUT1(path SolarEclipsePath) SolarEclipsePath + SolarEclipseShadowAt(value time.Time) (SolarEclipseShadowInstant, bool) + SolarEclipseShadowBetween( + start, end time.Time, step time.Duration, + ) []SolarEclipseShadowInstant + SolarEclipseShadowBetweenWithOptions( + start, end time.Time, step time.Duration, options SolarEclipseShadowSolverOptions, + ) []SolarEclipseShadowInstant + SolarEclipseStationStateAt(value time.Time, lon, lat, height float64) SolarEclipseStationState + TimeLabelsInUT1(times []time.Time) []time.Time +} + +var _ contextAPI = (*Eclipse)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want, wantOK := SolarEclipseOnDate(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + got, gotOK := module.SolarEclipseOnDate(date) + if !wantOK || gotOK != wantOK || got != want { + t.Fatalf("model %d: solar eclipse escaped instance", model) + } + } +} + +// 站心日食、候选枚举与中心食搜索必须使用模块实例,切换全局模型不得改变结果。 +func TestContextLocalSolarEclipseIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 3, 1, 0, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + module := NewWithInstance(ephem.NewModelInstance(model)) + run := func() []interface{} { + next := module.NextLocalSolarEclipse(date, -97, 32, 0) + iau := module.NextLocalSolarEclipseIAUSingleK(date, -97, 32, 0) + onDate, ok := module.LocalSolarEclipseOnDate(time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC), -97, 32, 0) + candidates := module.SolarEclipseCandidates(date, date.AddDate(1, 0, 0), SolarEclipseCandidateOptions{}) + central, status := module.SearchLocalCentralSolarEclipse(date, -97, 32, 0, SolarEclipseLocalSearchOptions{MaxYears: 2}) + return []interface{}{next, iau, onDate, ok, candidates, central, status} + } + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := run() + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + got := run() + for i := range want { + if !reflect.DeepEqual(got[i], want[i]) { + t.Fatalf("model %d: result %d escaped instance:\n got %+v\nwant %+v", model, i, got[i], want[i]) + } + } + } +} diff --git a/eclipse/label_ut1.go b/eclipse/label_ut1.go index 76039e7..05c8ae7 100644 --- a/eclipse/label_ut1.go +++ b/eclipse/label_ut1.go @@ -3,7 +3,7 @@ package eclipse import ( "time" - "b612.me/astro" + "b612.me/astro/basic" ) // 以下函数把结构里的民用时刻换成同一物理时刻的 UT1 时刻,供出图与导出共用。 @@ -13,7 +13,7 @@ func ut1Label(t time.Time) time.Time { if t.IsZero() { return t } - return astro.LabelIn(astro.TimeScaleUT1, t) + return basic.LabelIn(basic.TimeScaleUT1, t) } // TimeLabelsInUT1 返回换成 UT1 时刻的新切片 / returns a copy in UT1. diff --git a/eclipse/lunar.go b/eclipse/lunar.go index 44d9698..219c4f7 100644 --- a/eclipse/lunar.go +++ b/eclipse/lunar.go @@ -5,6 +5,7 @@ import ( "time" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" ) const ( @@ -83,6 +84,8 @@ type LunarEclipseContactPoint struct { type LunarEclipseInfo struct { // Type 月食类型, eclipse type. Type LunarEclipseType + // LunarLimbApplied 接触时刻已按月缘剖面修正;剖面失败时为 false 且结果保持圆月面 / contacts refined with a lunar limb profile. + LunarLimbApplied bool // HasSaros 存在沙罗序列信息(可能是锚点外推结果), has Saros series metadata (possibly extrapolated). HasSaros bool // Saros 是沙罗序列信息,包括系列号、系列内序号和总成员数。 @@ -166,22 +169,26 @@ func LunarEclipseOnDateChauvenet(date time.Time) (LunarEclipseInfo, bool) { } func lunarEclipseOnDateWithFallback(date time.Time, calculator lunarEclipseCalculator, allowDefaultFallback bool) (LunarEclipseInfo, bool) { + return lunarEclipseOnDateWithInstance(date, calculator, allowDefaultFallback, nil) +} + +func lunarEclipseOnDateWithInstance(date time.Time, calculator lunarEclipseCalculator, allowDefaultFallback bool, inst *ephem.Instance) (LunarEclipseInfo, bool) { location := date.Location() dayStart, dayMid, dayEnd := lunarEclipseLocalDayBounds(date) - phaseDiff := moonSunLoDiff(dayMid) + phaseDiff := moonSunLoDiffWithInstance(dayMid, inst) if phaseDiff < lunarEclipseDayPhaseMin || phaseDiff > lunarEclipseDayPhaseMax { return LunarEclipseInfo{}, false } - candidateTT := basic.CalcMoonSHByJDE(timeToTTJDE(dayMid), 1) - if !isPotentialLunarEclipse(candidateTT) { + candidateTT := basic.CalcMoonSHByJDEWithInstance(timeToTTJDE(dayMid), 1, inst) + if !isPotentialLunarEclipseWithInstance(candidateTT, inst) { return LunarEclipseInfo{}, false } result := calculator(candidateTT) if result.Type == basic.LunarEclipseNone && allowDefaultFallback { - if fallback, ok := lunarEclipseDefaultFallback(candidateTT); ok { + if fallback, ok := lunarEclipseDefaultFallbackWithInstance(candidateTT, inst); ok { result = fallback } } @@ -189,7 +196,7 @@ func lunarEclipseOnDateWithFallback(date time.Time, calculator lunarEclipseCalcu return LunarEclipseInfo{}, false } - info := lunarEclipseInfoFromBasic(result, location) + info := lunarEclipseInfoFromBasicWithInstance(result, location, inst) if !lunarEclipseOverlapsDate(info, dayStart, dayEnd) { return LunarEclipseInfo{}, false } @@ -298,29 +305,40 @@ func searchLunarEclipse( calculator lunarEclipseCalculator, allowDefaultFallback bool, ) (LunarEclipseInfo, bool) { + return searchLunarEclipseWithInstance(date, direction, includeCurrent, calculator, allowDefaultFallback, nil) +} + +func searchLunarEclipseWithInstance(date time.Time, direction int, includeCurrent bool, calculator lunarEclipseCalculator, allowDefaultFallback bool, inst *ephem.Instance) (LunarEclipseInfo, bool) { targetTT := timeToTTJDE(date) - candidateTT := basic.CalcMoonSHByJDE(targetTT, 1) + candidateTT := basic.CalcMoonSHByJDEWithInstance(targetTT, 1, inst) for i := 0; i < lunarEclipseSearchLimit; i++ { - if isPotentialLunarEclipse(candidateTT) { + if math.IsNaN(candidateTT) || math.IsInf(candidateTT, 0) { + break + } + if isPotentialLunarEclipseWithInstance(candidateTT, inst) { result := calculator(candidateTT) if result.Type == basic.LunarEclipseNone && allowDefaultFallback { - if fallback, ok := lunarEclipseDefaultFallback(candidateTT); ok { + if fallback, ok := lunarEclipseDefaultFallbackWithInstance(candidateTT, inst); ok { result = fallback } } if result.Type != basic.LunarEclipseNone && lunarEclipseMatchesDirection(result.Maximum, targetTT, direction, includeCurrent) { - return lunarEclipseInfoFromBasic(result, date.Location()), true + return lunarEclipseInfoFromBasicWithInstance(result, date.Location(), inst), true } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays) + candidateTT = nextLunarEclipseSearchCandidateTTWithInstance(candidateTT, direction, inst) } return LunarEclipseInfo{}, false } func lunarEclipseDefaultFallback(candidateTT float64) (basic.LunarEclipseResult, bool) { - result := basic.LunarEclipseChauvenet(candidateTT) + return lunarEclipseDefaultFallbackWithInstance(candidateTT, nil) +} + +func lunarEclipseDefaultFallbackWithInstance(candidateTT float64, inst *ephem.Instance) (basic.LunarEclipseResult, bool) { + result := basic.LunarEclipseChauvenetWithInstance(candidateTT, inst) if result.Type != basic.LunarEclipsePenumbral { return basic.LunarEclipseResult{}, false } @@ -345,18 +363,26 @@ func lunarEclipseMatchesDirection(maximumTT, targetTT float64, direction int, in } func isPotentialLunarEclipse(fullMoonTT float64) bool { - moonLatitude := math.Abs(basic.HMoonTrueBo(fullMoonTT)) + return isPotentialLunarEclipseWithInstance(fullMoonTT, nil) +} + +func isPotentialLunarEclipseWithInstance(fullMoonTT float64, inst *ephem.Instance) bool { + moonLatitude := math.Abs(basic.HMoonTrueBoWithInstance(fullMoonTT, inst)) if moonLatitude > lunarEclipseLatitudeLimitDeg { return false } - phaseDiff := math.Abs(normalizeDegree180(basic.HMoonApparentLo(fullMoonTT) - basic.HSunApparentLo(fullMoonTT) - 180)) + phaseDiff := math.Abs(normalizeDegree180(basic.HMoonApparentLoWithInstance(fullMoonTT, inst) - basic.HSunApparentLoWithInstance(fullMoonTT, inst) - 180)) return phaseDiff <= lunarEclipseLongitudeLimitDeg } func lunarEclipseInfoFromBasic(result basic.LunarEclipseResult, location *time.Location) LunarEclipseInfo { - saros, hasSaros := lunarSarosInfo(result.Maximum) - geometry := basic.LunarEclipseShadowGeometryAtModel(result.Maximum, result.ShadowModel) + return lunarEclipseInfoFromBasicWithInstance(result, location, nil) +} + +func lunarEclipseInfoFromBasicWithInstance(result basic.LunarEclipseResult, location *time.Location, inst *ephem.Instance) LunarEclipseInfo { + saros, hasSaros := lunarSarosInfoWithInstance(result.Maximum, inst) + geometry := basic.LunarEclipseShadowGeometryAtModelWithInstance(result.Maximum, result.ShadowModel, inst) return LunarEclipseInfo{ ShadowModel: LunarEclipseShadowModel(result.ShadowModel), Gamma: geometry.Gamma, @@ -367,6 +393,7 @@ func lunarEclipseInfoFromBasic(result basic.LunarEclipseResult, location *time.L HasSaros: hasSaros, Saros: saros, Type: mapBasicLunarEclipseType(result.Type), + LunarLimbApplied: result.LunarLimbApplied, PenumbralMagnitude: result.PenumbralMagnitude, UmbralMagnitude: result.Magnitude, PenumbralStart: ttJDEToTime(result.PenumbralStart, location), @@ -378,7 +405,7 @@ func lunarEclipseInfoFromBasic(result basic.LunarEclipseResult, location *time.L PenumbralEnd: ttJDEToTime(result.PenumbralEnd, location), GreatestJDE: result.Maximum, DeltaTSeconds: basic.DeltaT(result.Maximum, true), - ContactPoints: lunarEclipseContactPointsFromBasic(result, location), + ContactPoints: lunarEclipseContactPointsFromBasicWithInstance(result, location, inst), HasPenumbral: result.HasPenumbral, HasPartial: result.HasPartial, HasTotal: result.HasTotal, @@ -389,26 +416,30 @@ func lunarEclipseContactPointsFromBasic( result basic.LunarEclipseResult, location *time.Location, ) []LunarEclipseContactPoint { + return lunarEclipseContactPointsFromBasicWithInstance(result, location, nil) +} + +func lunarEclipseContactPointsFromBasicWithInstance(result basic.LunarEclipseResult, location *time.Location, inst *ephem.Instance) []LunarEclipseContactPoint { if !result.HasPenumbral { return nil } contacts := []LunarEclipseContactPoint{ - lunarEclipseContactPoint("P1", result.PenumbralStart, location, false), + lunarEclipseContactPointWithInstance("P1", result.PenumbralStart, location, false, inst), } if result.HasPartial { - contacts = append(contacts, lunarEclipseContactPoint("U1", result.PartialStart, location, false)) + contacts = append(contacts, lunarEclipseContactPointWithInstance("U1", result.PartialStart, location, false, inst)) } if result.HasTotal { - contacts = append(contacts, lunarEclipseContactPoint("U2", result.TotalStart, location, true)) + contacts = append(contacts, lunarEclipseContactPointWithInstance("U2", result.TotalStart, location, true, inst)) } if result.HasTotal { - contacts = append(contacts, lunarEclipseContactPoint("U3", result.TotalEnd, location, true)) + contacts = append(contacts, lunarEclipseContactPointWithInstance("U3", result.TotalEnd, location, true, inst)) } if result.HasPartial { - contacts = append(contacts, lunarEclipseContactPoint("U4", result.PartialEnd, location, false)) + contacts = append(contacts, lunarEclipseContactPointWithInstance("U4", result.PartialEnd, location, false, inst)) } - contacts = append(contacts, lunarEclipseContactPoint("P4", result.PenumbralEnd, location, false)) + contacts = append(contacts, lunarEclipseContactPointWithInstance("P4", result.PenumbralEnd, location, false, inst)) return contacts } @@ -418,7 +449,11 @@ func lunarEclipseContactPoint( location *time.Location, internalContact bool, ) LunarEclipseContactPoint { - moonCenterPA := lunarEclipseMoonCenterPositionAngle(ttJDE) + return lunarEclipseContactPointWithInstance(label, ttJDE, location, internalContact, nil) +} + +func lunarEclipseContactPointWithInstance(label string, ttJDE float64, location *time.Location, internalContact bool, inst *ephem.Instance) LunarEclipseContactPoint { + moonCenterPA := lunarEclipseMoonCenterPositionAngleWithInstance(ttJDE, inst) shadowCenterPA := normalizeDegree360(moonCenterPA + 180) contactPA := shadowCenterPA if internalContact { diff --git a/eclipse/lunar_ephemeris_instance.go b/eclipse/lunar_ephemeris_instance.go new file mode 100644 index 0000000..eb10b23 --- /dev/null +++ b/eclipse/lunar_ephemeris_instance.go @@ -0,0 +1,142 @@ +package eclipse + +import ( + "math" + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +// LunarEclipseOnDateWithInstance 指定星历实例的当地日月食查询,自定义引擎不推断沙罗 / local-date lunar eclipse with an explicit instance; custom engines omit Saros metadata. +func LunarEclipseOnDateWithInstance(date time.Time, inst *ephem.Instance) (LunarEclipseInfo, bool) { + return lunarEclipseOnDateWithInstance(date, lunarCalculatorWithInstance(false, inst), true, inst) +} + +// LunarEclipseOnDateDanjonWithInstance 指定星历实例的 Danjon 当地日月食查询 / local-date Danjon lunar eclipse with an explicit instance. +func LunarEclipseOnDateDanjonWithInstance(date time.Time, inst *ephem.Instance) (LunarEclipseInfo, bool) { + return lunarEclipseOnDateWithInstance(date, lunarCalculatorWithInstance(false, inst), false, inst) +} + +// LunarEclipseOnDateChauvenetWithInstance 指定星历实例的 Chauvenet 当地日月食查询 / local-date Chauvenet lunar eclipse with an explicit instance. +func LunarEclipseOnDateChauvenetWithInstance(date time.Time, inst *ephem.Instance) (LunarEclipseInfo, bool) { + return lunarEclipseOnDateWithInstance(date, lunarCalculatorWithInstance(true, inst), false, inst) +} + +// LastLunarEclipseWithInstance 指定星历实例的上次月食 / previous lunar eclipse with an explicit instance. +func LastLunarEclipseWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, -1, true, lunarCalculatorWithInstance(false, inst), true, inst) + return info +} + +// LastLunarEclipseDanjonWithInstance 指定星历实例的 Danjon 上次月食 / previous Danjon lunar eclipse with an explicit instance. +func LastLunarEclipseDanjonWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, -1, true, lunarCalculatorWithInstance(false, inst), false, inst) + return info +} + +// LastLunarEclipseChauvenetWithInstance 指定星历实例的 Chauvenet 上次月食 / previous Chauvenet lunar eclipse with an explicit instance. +func LastLunarEclipseChauvenetWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, -1, true, lunarCalculatorWithInstance(true, inst), false, inst) + return info +} + +// NextLunarEclipseWithInstance 指定星历实例的下次月食 / next lunar eclipse with an explicit instance. +func NextLunarEclipseWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, 1, false, lunarCalculatorWithInstance(false, inst), true, inst) + return info +} + +// NextLunarEclipseDanjonWithInstance 指定星历实例的 Danjon 下次月食 / next Danjon lunar eclipse with an explicit instance. +func NextLunarEclipseDanjonWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, 1, false, lunarCalculatorWithInstance(false, inst), false, inst) + return info +} + +// NextLunarEclipseChauvenetWithInstance 指定星历实例的 Chauvenet 下次月食 / next Chauvenet lunar eclipse with an explicit instance. +func NextLunarEclipseChauvenetWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + info, _ := searchLunarEclipseWithInstance(date, 1, false, lunarCalculatorWithInstance(true, inst), false, inst) + return info +} + +// ClosestLunarEclipseWithInstance 指定星历实例的最近月食 / closest lunar eclipse with an explicit instance. +func ClosestLunarEclipseWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + return closestLunarEclipseWithInstance(date, false, true, inst) +} + +// ClosestLunarEclipseDanjonWithInstance 指定星历实例的 Danjon 最近月食 / closest Danjon lunar eclipse with an explicit instance. +func ClosestLunarEclipseDanjonWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + return closestLunarEclipseWithInstance(date, false, false, inst) +} + +// ClosestLunarEclipseChauvenetWithInstance 指定星历实例的 Chauvenet 最近月食 / closest Chauvenet lunar eclipse with an explicit instance. +func ClosestLunarEclipseChauvenetWithInstance(date time.Time, inst *ephem.Instance) LunarEclipseInfo { + return closestLunarEclipseWithInstance(date, true, false, inst) +} + +func lunarCalculatorWithInstance(chauvenet bool, inst *ephem.Instance) lunarEclipseCalculator { + if chauvenet { + return func(jde float64) basic.LunarEclipseResult { return basic.LunarEclipseChauvenetWithInstance(jde, inst) } + } + return func(jde float64) basic.LunarEclipseResult { return basic.LunarEclipseDanjonWithInstance(jde, inst) } +} + +func closestLunarEclipseWithInstance(date time.Time, chauvenet, allowDefaultFallback bool, inst *ephem.Instance) LunarEclipseInfo { + calculator := lunarCalculatorWithInstance(chauvenet, inst) + last, hasLast := searchLunarEclipseWithInstance(date, -1, true, calculator, allowDefaultFallback, inst) + next, hasNext := searchLunarEclipseWithInstance(date, 1, false, calculator, allowDefaultFallback, inst) + return closestLunarEclipse(date, last, hasLast, next, hasNext) +} + +func moonSunLoDiffWithInstance(date time.Time, inst *ephem.Instance) float64 { + if inst == nil { + return moonSunLoDiff(date) + } + jde := timeToTTJDE(date) + return normalizeDegree360(basic.HMoonApparentLoWithInstance(jde, inst) - basic.HSunApparentLoWithInstance(jde, inst)) +} + +func nextLunarEclipseSearchCandidateTTWithInstance(candidateTT float64, direction int, inst *ephem.Instance) float64 { + if inst == nil { + return nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays) + } + step := 1 + if inst.Model() != ephem.Custom { + step = eclipseSearchStep(candidateTT, direction, lunarEclipseSynodicMonthDays) + } + return basic.CalcMoonSHByJDEWithInstance(candidateTT+float64(direction*step)*lunarEclipseSynodicMonthDays, 1, inst) +} + +func lunarEclipseMoonCenterPositionAngleWithInstance(ttJDE float64, inst *ephem.Instance) float64 { + if inst == nil { + return lunarEclipseMoonCenterPositionAngle(ttJDE) + } + sunRA, sunDec := basic.HSunApparentRaDecWithInstance(ttJDE, inst) + moonRA, moonDec := basic.HMoonTrueRaDecWithInstance(ttJDE, inst) + return positionAngle(normalizeDegree360(sunRA+180), -sunDec, moonRA, moonDec) +} + +func lunarSarosInfoWithInstance(ttJDE float64, inst *ephem.Instance) (SarosInfo, bool) { + if inst == nil { + return lunarSarosInfo(ttJDE) + } + if inst.Model() == ephem.Custom { + return SarosInfo{}, false + } + if info, ok := matchSarosMagic(lunarSarosAnchors[:], 1, lunarSarosHeadOverrides[:], ttJDE); ok { + return info, true + } + info, ok := extendedSarosInfoForModel(ttJDE, 1, inst.Model(), func(lunation, phase int) (float64, bool, bool) { + seed := sarosLunationEpoch + (float64(lunation)+float64(phase)/2)*solarEclipseSynodicMonthDays + result := basic.LunarEclipseDanjonWithInstance(seed, inst) + if result.Type == basic.LunarEclipseNone { + result = basic.LunarEclipseChauvenetWithInstance(seed, inst) + } + valid := !math.IsNaN(result.Maximum) && !math.IsInf(result.Maximum, 0) && math.Abs(result.Maximum-seed) < sarosEclipseSeedToleranceDays + return result.Maximum, result.Type != basic.LunarEclipseNone, valid + }) + if !ok { + return SarosInfo{}, false + } + return reconcileSarosSpan(info, ttJDE, 1) +} diff --git a/eclipse/lunar_ephemeris_instance_test.go b/eclipse/lunar_ephemeris_instance_test.go new file mode 100644 index 0000000..ad8ad2e --- /dev/null +++ b/eclipse/lunar_ephemeris_instance_test.go @@ -0,0 +1,153 @@ +package eclipse + +import ( + "math" + "reflect" + "testing" + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +type p3LunarShiftEngine struct { + base *ephem.Instance + offset float64 +} + +func (engine p3LunarShiftEngine) State(target, center ephem.Body, jde float64) ([3]float64, [3]float64, error) { + return engine.base.State(target, center, jde+engine.offset) +} + +func (engine p3LunarShiftEngine) Apparent(target ephem.Body, jde float64) ([3]float64, float64, float64, error) { + return engine.base.Apparent(target, jde+engine.offset) +} + +func (engine p3LunarShiftEngine) Coverage(target ephem.Body) (float64, float64, bool) { + start, end, ok := engine.base.Coverage(target) + return start - engine.offset, end - engine.offset, ok +} + +func TestP3LunarQueriesPreserveNilAndLegacy(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + date := time.Date(2022, 11, 8, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + ephem.SetActive(model) + inst := ephem.NewModelInstance(model) + for _, query := range []struct { + old func(time.Time) (LunarEclipseInfo, bool) + new func(time.Time, *ephem.Instance) (LunarEclipseInfo, bool) + }{ + {LunarEclipseOnDate, LunarEclipseOnDateWithInstance}, + {LunarEclipseOnDateDanjon, LunarEclipseOnDateDanjonWithInstance}, + {LunarEclipseOnDateChauvenet, LunarEclipseOnDateChauvenetWithInstance}, + } { + want, wantOK := query.old(date) + got, gotOK := query.new(date, nil) + if gotOK != wantOK || !reflect.DeepEqual(got, want) { + t.Error("nil changed local-date query") + } + if model == ephem.Legacy { + got, gotOK = query.new(date, inst) + if gotOK != wantOK || !reflect.DeepEqual(got, want) { + t.Error("explicit Legacy changed local-date query") + } + } + } + for _, query := range []struct { + old func(time.Time) LunarEclipseInfo + new func(time.Time, *ephem.Instance) LunarEclipseInfo + }{ + {LastLunarEclipse, LastLunarEclipseWithInstance}, + {LastLunarEclipseDanjon, LastLunarEclipseDanjonWithInstance}, + {LastLunarEclipseChauvenet, LastLunarEclipseChauvenetWithInstance}, + {NextLunarEclipse, NextLunarEclipseWithInstance}, + {NextLunarEclipseDanjon, NextLunarEclipseDanjonWithInstance}, + {NextLunarEclipseChauvenet, NextLunarEclipseChauvenetWithInstance}, + {ClosestLunarEclipse, ClosestLunarEclipseWithInstance}, + {ClosestLunarEclipseDanjon, ClosestLunarEclipseDanjonWithInstance}, + {ClosestLunarEclipseChauvenet, ClosestLunarEclipseChauvenetWithInstance}, + } { + want := query.old(date) + if got := query.new(date, nil); !reflect.DeepEqual(got, want) { + t.Error("nil changed directional search") + } + if model == ephem.Legacy { + if got := query.new(date, inst); !reflect.DeepEqual(got, want) { + t.Error("explicit Legacy changed directional search") + } + } + } + } +} + +func TestP3LunarQueriesUseInstanceThroughout(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + base := ephem.NewModelInstance(ephem.Semianalytic) + first := ephem.NewInstance(p3LunarShiftEngine{base: base, offset: 0.1}) + second := ephem.NewInstance(p3LunarShiftEngine{base: base, offset: 0.2}) + date := time.Date(2022, 11, 8, 12, 0, 0, 0, time.FixedZone("UTC+8", 8*3600)) + global, model, generation := ephem.Global(), ephem.Active(), ephem.Generation() + firstInfo, firstOK := LunarEclipseOnDateWithInstance(date, first) + secondInfo, secondOK := LunarEclipseOnDateWithInstance(date, second) + if !firstOK || !secondOK || !firstInfo.HasTotal || !secondInfo.HasTotal { + t.Fatalf("explicit query lost total eclipse: %+v / %+v", firstInfo, secondInfo) + } + if firstInfo.HasSaros || secondInfo.HasSaros { + t.Error("custom engines must not inherit built-in Saros metadata") + } + if math.Abs(firstInfo.GreatestJDE-secondInfo.GreatestJDE-0.1) > 0.001 { + t.Errorf("event time did not follow instances: %v / %v", firstInfo.GreatestJDE, secondInfo.GreatestJDE) + } + if firstInfo.Maximum.Location() != date.Location() || len(firstInfo.ContactPoints) != 6 { + t.Error("local timezone or contacts were lost") + } + if ephem.Global() != global || ephem.Active() != model || ephem.Generation() != generation { + t.Error("query changed global ephemeris state") + } + for _, active := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + ephem.SetActive(active) + got, ok := LunarEclipseOnDateWithInstance(date, first) + if !ok || !reflect.DeepEqual(got, firstInfo) { + t.Error("result, contact orientation or shadow geometry read the global model") + } + for _, query := range []func(time.Time, *ephem.Instance) LunarEclipseInfo{LastLunarEclipseWithInstance, NextLunarEclipseWithInstance, ClosestLunarEclipseWithInstance} { + got := query(firstInfo.Maximum.Add(-time.Minute), first) + if got.Type == LunarEclipseNone || got.Maximum.IsZero() { + t.Error("directional search lost instance") + } + } + } +} + +func TestP3LunarSarosSelectsInstanceModel(t *testing.T) { + previous := ephem.Active() + t.Cleanup(func() { ephem.SetActive(previous) }) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + inst := ephem.NewModelInstance(model) + ephem.SetActive(model) + for _, seed := range []float64{basic.JDCalc(2022, 11, 8), basic.JDCalc(7000, 1, 1), basic.JDCalc(-5000, 1, 1)} { + want, wantOK := lunarSarosInfoWithInstance(seed, inst) + ephem.SetActive(ephem.Model(1 - int(model))) + got, gotOK := lunarSarosInfoWithInstance(seed, inst) + if gotOK != wantOK || got != want { + t.Errorf("Saros read active model instead of instance: %+v / %+v", got, want) + } + } + } +} + +func TestP3LunarQueriesStopOnEngineFailure(t *testing.T) { + inst := ephem.NewInstance(nil) + date := time.Date(2022, 11, 8, 12, 0, 0, 0, time.UTC) + if info, ok := LunarEclipseOnDateWithInstance(date, inst); ok || info.Type != "" { + t.Error("failed engine produced a local-date eclipse") + } + for _, query := range []func(time.Time, *ephem.Instance) LunarEclipseInfo{LastLunarEclipseWithInstance, NextLunarEclipseWithInstance, ClosestLunarEclipseWithInstance} { + if info := query(date, inst); !info.Maximum.IsZero() { + t.Error("failed engine fell back during directional search") + } + } +} diff --git a/eclipse/lunar_limb_test.go b/eclipse/lunar_limb_test.go new file mode 100644 index 0000000..a9fe845 --- /dev/null +++ b/eclipse/lunar_limb_test.go @@ -0,0 +1,272 @@ +package eclipse + +import ( + "math" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +type lunarLimbCase struct { + name string + date time.Time + lon, lat float64 + total bool +} + +var lunarLimbCases = []lunarLimbCase{ + {"2024-04-08 Dallas total", time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC), -96.8, 32.8, true}, + {"2024-04-08 Torreon total", time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC), -103.43, 25.54, true}, + {"2023-10-14 Albuquerque annular", time.Date(2023, 10, 14, 16, 0, 0, 0, time.UTC), -106.65, 35.08, false}, +} + +func lunarLimbContacts(info LocalSolarEclipseInfo) [4]time.Time { + return [4]time.Time{info.PartialStart, info.CentralStart, info.CentralEnd, info.PartialEnd} +} + +// 月缘只应让接触时刻移动几秒:既要确实移动,又不能超过 15 s;食型与默认结果都不变。 +func TestLocalSolarEclipseLunarLimbShiftsContacts(t *testing.T) { + limbed := NewWithInstance(nil).WithLunarLimb(limb.Default()) + for _, c := range lunarLimbCases { + circular, ok := defaultModule.LocalSolarEclipseOnDate(c.date, c.lon, c.lat, 0) + if !ok || !circular.HasCentral || circular.HasTotal != c.total { + t.Fatalf("%s: circular type %v", c.name, circular.Type) + } + corrected, ok := limbed.LocalSolarEclipseOnDate(c.date, c.lon, c.lat, 0) + if !ok || corrected.Type != circular.Type { + t.Fatalf("%s: limb changed type %v -> %v", c.name, circular.Type, corrected.Type) + } + moved := false + a, b := lunarLimbContacts(circular), lunarLimbContacts(corrected) + for _, point := range corrected.ContactPoints { + want := b[map[string]int{"C1": 0, "C2": 1, "C3": 2, "C4": 3}[point.Label]] + if gap := point.Time.Sub(want).Seconds(); math.Abs(gap) > 0.05 { + t.Errorf("%s %s contact point off phase time by %.3f s", c.name, point.Label, gap) + } + } + if len(corrected.ContactPoints) != 4 { + t.Errorf("%s: %d contact points", c.name, len(corrected.ContactPoints)) + } + for index := range a { + shift := b[index].Sub(a[index]).Seconds() + t.Logf("%s contact %d shift %+.3f s", c.name, index, shift) + if math.Abs(shift) > 15 { + t.Errorf("%s contact %d moved %.3f s", c.name, index, shift) + } + if math.Abs(shift) > 0.01 { + moved = true + } + } + if !moved { + t.Errorf("%s: lunar limb did not move any contact", c.name) + } + t.Logf("%s central duration circular %.3f s, limb %.3f s", c.name, + circular.CentralEnd.Sub(circular.CentralStart).Seconds(), + corrected.CentralEnd.Sub(corrected.CentralStart).Seconds()) + + // 选项里的月缘与模块设置等价;模块设置不影响原模块。 + viaOptions, _ := defaultModule.LocalSolarEclipseOnDateWithOptions(c.date, c.lon, c.lat, 0, + SolarEclipseOptions{LunarLimb: limb.Default()}) + if lunarLimbContacts(viaOptions) != b { + t.Errorf("%s: options limb differs from module limb", c.name) + } + again, _ := defaultModule.LocalSolarEclipseOnDate(c.date, c.lon, c.lat, 0) + if lunarLimbContacts(again) != a { + t.Errorf("%s: WithLunarLimb changed the original module", c.name) + } + } +} + +// 2025-09-07 月全食紫金山天文台发布时刻(北京时间列换算 UT,0.1 分钟精度),只用于观察月缘修正的方向与量级。 +func TestLunarEclipseLunarLimbAgainstPMO(t *testing.T) { + pmo := []struct { + label string + ut time.Time + }{ + {"P1", time.Date(2025, 9, 7, 15, 26, 54, 0, time.UTC)}, + {"U1", time.Date(2025, 9, 7, 16, 26, 48, 0, time.UTC)}, + {"U2", time.Date(2025, 9, 7, 17, 30, 24, 0, time.UTC)}, + {"U3", time.Date(2025, 9, 7, 18, 53, 12, 0, time.UTC)}, + {"U4", time.Date(2025, 9, 7, 19, 56, 54, 0, time.UTC)}, + {"P4", time.Date(2025, 9, 7, 20, 56, 36, 0, time.UTC)}, + } + date := time.Date(2025, 9, 7, 18, 0, 0, 0, time.UTC) + plain := NewWithInstance(nil) + limbed := plain.WithLunarLimb(limb.Default()) + for _, chauvenet := range []bool{false, true} { + var circular, corrected LunarEclipseInfo + if chauvenet { + circular, corrected = plain.ClosestLunarEclipseChauvenet(date), limbed.ClosestLunarEclipseChauvenet(date) + } else { + circular, corrected = plain.ClosestLunarEclipseDanjon(date), limbed.ClosestLunarEclipseDanjon(date) + } + if !corrected.Maximum.Equal(circular.Maximum) || corrected.Type != circular.Type { + t.Fatalf("chauvenet=%v: maximum or type changed", chauvenet) + } + c := []time.Time{circular.PenumbralStart, circular.PartialStart, circular.TotalStart, circular.TotalEnd, circular.PartialEnd, circular.PenumbralEnd} + l := []time.Time{corrected.PenumbralStart, corrected.PartialStart, corrected.TotalStart, corrected.TotalEnd, corrected.PartialEnd, corrected.PenumbralEnd} + var circErr, limbErr float64 + for i, ref := range pmo { + ce := LunarEclipseInfoInUT1(circular) + le := LunarEclipseInfoInUT1(corrected) + cu := []time.Time{ce.PenumbralStart, ce.PartialStart, ce.TotalStart, ce.TotalEnd, ce.PartialEnd, ce.PenumbralEnd}[i] + lu := []time.Time{le.PenumbralStart, le.PartialStart, le.TotalStart, le.TotalEnd, le.PartialEnd, le.PenumbralEnd}[i] + shift := l[i].Sub(c[i]).Seconds() + de, dl := cu.Sub(ref.ut).Seconds(), lu.Sub(ref.ut).Seconds() + circErr += math.Abs(de) + limbErr += math.Abs(dl) + t.Logf("chauvenet=%v %s shift %+6.2f s PMO err circular %+6.1f s limb %+6.1f s", chauvenet, ref.label, shift, de, dl) + if math.Abs(shift) > 20 { + t.Errorf("chauvenet=%v %s: limb shift %.2f s too large", chauvenet, ref.label, shift) + } + } + t.Logf("chauvenet=%v PMO MAE circular %.2f s, limb %.2f s", chauvenet, circErr/6, limbErr/6) + // 紫金山按 Chauvenet 口径发布,只有该口径可比精度;Danjon 与之差约 90 s 属口径差。 + if chauvenet && (limbErr > circErr || limbErr/6 > 2) { + t.Errorf("PMO MAE: limb %.2f s, circular %.2f s; want the limb no worse and within 2 s", limbErr/6, circErr/6) + } + } +} + +// 2024-04-08 全球日食紫金山天文台发布 P1/P4(UT,整秒),只用于观察月缘修正的方向与量级。 +func TestGlobalSolarEclipseLunarLimbAgainstPMO(t *testing.T) { + date := time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC) + plain := NewWithInstance(nil) + limbed := plain.WithLunarLimb(limb.Default()) + circular, corrected := plain.ClosestSolarEclipse(date), limbed.ClosestSolarEclipse(date) + if !corrected.GreatestEclipse.Equal(circular.GreatestEclipse) || corrected.Type != circular.Type { + t.Fatal("greatest or type changed") + } + cu, lu := SolarEclipseInfoInUT1(circular), SolarEclipseInfoInUT1(corrected) + for _, c := range []struct { + label string + ref time.Time + circ, lb time.Time + }{ + {"P1", time.Date(2024, 4, 8, 15, 42, 13, 0, time.UTC), cu.PartialBeginOnEarth, lu.PartialBeginOnEarth}, + {"P4", time.Date(2024, 4, 8, 20, 52, 21, 0, time.UTC), cu.PartialEndOnEarth, lu.PartialEndOnEarth}, + } { + shift := c.lb.Sub(c.circ).Seconds() + t.Logf("%s shift %+6.2f s PMO err circular %+6.2f s limb %+6.2f s", c.label, shift, + c.circ.Sub(c.ref).Seconds(), c.lb.Sub(c.ref).Seconds()) + if math.Abs(shift) > 20 { + t.Errorf("%s: limb shift %.2f s too large", c.label, shift) + } + // 发布值取整秒,±0.5 s 的舍入淹没圆月面与月缘之差,只约束月缘结果贴近发布值。 + if d := c.lb.Sub(c.ref).Seconds(); math.Abs(d) > 1.5 { + t.Errorf("%s: limb result %.2f s from PMO", c.label, d) + } + } + t.Logf("central duration circular %.2f s limb %.2f s", circular.CentralDuration.Seconds(), corrected.CentralDuration.Seconds()) +} + +// 偏食足迹里的全球结果与 P1/P4 必须与全球日食接口同源,不能只有可见域边界按月缘修正。 +func TestPartialFootprintsLunarLimbMatchesGlobal(t *testing.T) { + date := time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC) + limbed := NewWithInstance(nil).WithLunarLimb(limb.Default()) + global, ok := limbed.SolarEclipseOnDate(date) + if !ok || !global.LunarLimbApplied { + t.Fatal("global limb correction not applied") + } + footprints, ok := limbed.SolarEclipsePartialFootprints(date, SolarEclipsePartialFootprintOptions{}) + if !ok || !footprints.PartialBoundaryLimbApplied { + t.Fatal("partial boundary limb correction not applied") + } + embedded := footprints.Eclipse + if !embedded.LunarLimbApplied || + !embedded.PartialBeginOnEarth.Equal(global.PartialBeginOnEarth) || + !embedded.PartialEndOnEarth.Equal(global.PartialEndOnEarth) || + embedded.CentralDuration != global.CentralDuration { + t.Fatalf("embedded eclipse differs from global: applied=%v P1 %v/%v P4 %v/%v", + embedded.LunarLimbApplied, embedded.PartialBeginOnEarth, global.PartialBeginOnEarth, + embedded.PartialEndOnEarth, global.PartialEndOnEarth) + } + for _, c := range []struct { + label string + point SolarEclipsePathPoint + when time.Time + contact SolarEclipseLimbContact + }{ + {"P1", footprints.P1, global.PartialBeginOnEarth, global.PartialBeginContact}, + {"P4", footprints.P4, global.PartialEndOnEarth, global.PartialEndContact}, + } { + if !c.contact.Defined { + continue + } + if !c.point.Time.Equal(c.when) || c.point.Longitude != c.contact.Longitude || c.point.Latitude != c.contact.Latitude { + t.Fatalf("%s: footprint contact %v (%.4f, %.4f) differs from global %v (%.4f, %.4f)", + c.label, c.point.Time, c.point.Longitude, c.point.Latitude, c.when, c.contact.Longitude, c.contact.Latitude) + } + } +} + +// 月缘只平移全食带南北界,量级应为数百米到数公里。 +func TestCentralPathLunarLimbShiftsLimits(t *testing.T) { + date := time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC) + plain := NewWithInstance(nil) + circular, ok := plain.SolarEclipseCentralPath(date, SolarEclipsePathOptions{}) + if !ok { + t.Fatal("no central path") + } + corrected, ok := plain.WithLunarLimb(limb.Default()).SolarEclipseCentralPath(date, SolarEclipsePathOptions{}) + if !ok || len(corrected.NorthernLimit) != len(circular.NorthernLimit) || len(corrected.SouthernLimit) != len(circular.SouthernLimit) { + t.Fatal("limit lengths changed") + } + for side, pair := range [][2][]SolarEclipsePathPoint{{circular.NorthernLimit, corrected.NorthernLimit}, {circular.SouthernLimit, corrected.SouthernLimit}} { + worst, sum, moved := 0.0, 0.0, 0 + for i := range pair[0] { + a, b := pair[0][i], pair[1][i] + d := math.Hypot((a.Longitude-b.Longitude)*math.Cos(a.Latitude*math.Pi/180), a.Latitude-b.Latitude) * 111.2 + if d > 0 { + moved++ + } + sum += d + worst = math.Max(worst, d) + } + t.Logf("side %d: %d/%d points moved, mean %.3f km, max %.3f km", side, moved, len(pair[0]), sum/float64(len(pair[0])), worst) + if worst > 10 || moved == 0 { + t.Errorf("side %d: implausible limb shift (max %.3f km, moved %d)", side, worst, moved) + } + } +} + +// 月缘让全食带变窄、时长变化在数秒以内;带宽与足迹必须与修正后的南北界同向。 +func TestCentralPathLunarLimbWidthsAndDuration(t *testing.T) { + date := time.Date(2024, 4, 8, 18, 0, 0, 0, time.UTC) + plain := NewWithInstance(nil) + circular, ok := plain.SolarEclipseCentralPath(date, SolarEclipsePathOptions{}) + if !ok { + t.Fatal("no central path") + } + corrected, ok := plain.WithLunarLimb(limb.Default()).SolarEclipseCentralPath(date, SolarEclipsePathOptions{}) + if !ok || len(corrected.CenterLine) != len(circular.CenterLine) { + t.Fatal("center line changed") + } + narrower, total := 0, 0 + worst := 0.0 + for i := range circular.CenterLine { + a, b := circular.CenterLine[i].WidthKM, corrected.CenterLine[i].WidthKM + if a <= 0 || b <= 0 { + continue + } + total++ + if b < a { + narrower++ + } + worst = math.Max(worst, math.Abs(b-a)) + } + t.Logf("width: %d/%d samples narrower, max change %.3f km; greatest %.3f -> %.3f km", + narrower, total, worst, circular.Greatest.WidthKM, corrected.Greatest.WidthKM) + t.Logf("max central duration %.2f s -> %.2f s", + circular.MaxCentralDuration.Seconds(), corrected.MaxCentralDuration.Seconds()) + if worst > 10 || total == 0 { + t.Errorf("implausible width change %.3f km over %d samples", worst, total) + } + if d := math.Abs((corrected.MaxCentralDuration - circular.MaxCentralDuration).Seconds()); d > 10 { + t.Errorf("max duration moved by %.2f s", d) + } + if len(corrected.CentralBandSegments) != len(circular.CentralBandSegments) { + t.Fatal("band segment count changed") + } +} diff --git a/eclipse/lunar_local.go b/eclipse/lunar_local.go index 9b046ee..60dd8df 100644 --- a/eclipse/lunar_local.go +++ b/eclipse/lunar_local.go @@ -99,57 +99,63 @@ type LocalLunarEclipseInfo struct { } // LocalLunarEclipseOnDate 当地可见月食查询 / local visible lunar eclipse query. -// Determine whether a visible local lunar eclipse occurs on the local date, using Danjon by default. -func LocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return LocalLunarEclipseOnDateDanjon(date, lon, lat, height) +func (module *Eclipse) LocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return module.LocalLunarEclipseOnDateDanjon(date, lon, lat, height) } // LocalLunarEclipseOnDateDanjon 当地可见月食查询(Danjon) / local visible lunar eclipse query with Danjon model. -// Determine whether a visible local lunar eclipse occurs on the local date with the Danjon model. -func LocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return localLunarEclipseOnDate(date, lon, lat, height, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) LocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return localLunarEclipseOnDate(date, lon, lat, height, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) } // LocalLunarEclipseOnDateChauvenet 当地可见月食查询(Chauvenet) / local visible lunar eclipse query with Chauvenet model. -// Determine whether a visible local lunar eclipse occurs on the local date with the Chauvenet model. -func LocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return localLunarEclipseOnDate(date, lon, lat, height, basic.LunarEclipseChauvenet, localLunarEclipseQueryVisible) +func (module *Eclipse) LocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return localLunarEclipseOnDate(date, lon, lat, height, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryVisible, module) } // GeometricLocalLunarEclipseOnDate 当地几何月食查询 / local geometric lunar eclipse query. -// Determine whether a geometric local lunar eclipse occurs on the local date, using Danjon by default. -func GeometricLocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return GeometricLocalLunarEclipseOnDateDanjon(date, lon, lat, height) +func (module *Eclipse) GeometricLocalLunarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return module.GeometricLocalLunarEclipseOnDateDanjon(date, lon, lat, height) } // GeometricLocalLunarEclipseOnDateDanjon 当地几何月食查询(Danjon) / local geometric lunar eclipse query with Danjon model. -// Determine whether a geometric local lunar eclipse occurs on the local date with the Danjon model. -func GeometricLocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return localLunarEclipseOnDate(date, lon, lat, height, basic.LunarEclipseDanjon, localLunarEclipseQueryGeometric) +func (module *Eclipse) GeometricLocalLunarEclipseOnDateDanjon(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return localLunarEclipseOnDate(date, lon, lat, height, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryGeometric, module) } // GeometricLocalLunarEclipseOnDateChauvenet 当地几何月食查询(Chauvenet) / local geometric lunar eclipse query with Chauvenet model. -// Determine whether a geometric local lunar eclipse occurs on the local date with the Chauvenet model. -func GeometricLocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return localLunarEclipseOnDate(date, lon, lat, height, basic.LunarEclipseChauvenet, localLunarEclipseQueryGeometric) +func (module *Eclipse) GeometricLocalLunarEclipseOnDateChauvenet(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return localLunarEclipseOnDate(date, lon, lat, height, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryGeometric, module) } func localLunarEclipseOnDate( date time.Time, lon, lat, height float64, calculator lunarEclipseCalculator, - mode localLunarEclipseQueryMode, -) (LocalLunarEclipseInfo, bool) { + mode localLunarEclipseQueryMode, modules ...*Eclipse) (LocalLunarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + location := date.Location() dayStart, dayMid, dayEnd := lunarEclipseLocalDayBounds(date) - phaseDiff := moonSunLoDiff(dayMid) + phaseDiff := moonSunLoDiffWithInstance(dayMid, module.inst) if phaseDiff < lunarEclipseDayPhaseMin || phaseDiff > lunarEclipseDayPhaseMax { return LocalLunarEclipseInfo{}, false } - candidateTT := basic.CalcMoonSHByJDE(timeToTTJDE(dayMid), 1) - if !isPotentialLunarEclipse(candidateTT) { + candidateTT := basic.CalcMoonSHByJDEWithInstance(timeToTTJDE(dayMid), 1, module.inst) + if !isPotentialLunarEclipseWithInstance(candidateTT, module.inst) { return LocalLunarEclipseInfo{}, false } @@ -158,157 +164,176 @@ func localLunarEclipseOnDate( return LocalLunarEclipseInfo{}, false } - info := localLunarEclipseInfoFromBasic(result, lon, lat, height, location) + info := localLunarEclipseInfoFromBasic(result, lon, lat, height, location, module) if !localLunarEclipseOverlapsDate(info, dayStart, dayEnd) { return LocalLunarEclipseInfo{}, false } - if mode == localLunarEclipseQueryVisible && !localLunarEclipseVisibleOnDate(info, dayStart, dayEnd) { + if mode == localLunarEclipseQueryVisible && !localLunarEclipseVisibleOnDate(info, dayStart, dayEnd, module) { return LocalLunarEclipseInfo{}, false } return info, true } // LastLocalLunarEclipse 上次可见月食 / previous visible local lunar eclipse. -// Previous visible local lunar eclipse, using Danjon by default. -func LastLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return LastLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) LastLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.LastLocalLunarEclipseDanjon(date, lon, lat, height) } // LastLocalTotalLunarEclipse 上次可见月全食 / previous visible local total lunar eclipse. -// Previous visible local total lunar eclipse, using Danjon by default. -func LastLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return searchLocalTotalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) LastLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return searchLocalTotalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) } // LastLocalLunarEclipseDanjon 上次可见月食(Danjon) / previous visible local lunar eclipse with Danjon model. -// Previous visible local lunar eclipse with the Danjon model. -func LastLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) LastLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) return info } // LastLocalLunarEclipseChauvenet 上次可见月食(Chauvenet) / previous visible local lunar eclipse with Chauvenet model. -// Previous visible local lunar eclipse with the Chauvenet model. -func LastLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseChauvenet, localLunarEclipseQueryVisible) +func (module *Eclipse) LastLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryVisible, module) return info } // LastGeometricLocalLunarEclipse 上次几何月食 / previous geometric local lunar eclipse. -// Previous geometric local lunar eclipse, using Danjon by default. -func LastGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return LastGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) LastGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.LastGeometricLocalLunarEclipseDanjon(date, lon, lat, height) } // LastGeometricLocalLunarEclipseDanjon 上次几何月食(Danjon) / previous geometric local lunar eclipse with Danjon model. -// Previous geometric local lunar eclipse with the Danjon model. -func LastGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryGeometric) +func (module *Eclipse) LastGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryGeometric, module) return info } // LastGeometricLocalLunarEclipseChauvenet 上次几何月食(Chauvenet) / previous geometric local lunar eclipse with Chauvenet model. -// Previous geometric local lunar eclipse with the Chauvenet model. -func LastGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseChauvenet, localLunarEclipseQueryGeometric) +func (module *Eclipse) LastGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryGeometric, module) return info } // NextLocalLunarEclipse 下次可见月食 / next visible local lunar eclipse. -// Next visible local lunar eclipse, using Danjon by default. -func NextLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return NextLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) NextLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.NextLocalLunarEclipseDanjon(date, lon, lat, height) } // NextLocalTotalLunarEclipse 下次可见月全食 / next visible local total lunar eclipse. -// Next visible local total lunar eclipse, using Danjon by default. -func NextLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - return searchLocalTotalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) NextLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + return searchLocalTotalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) } // NextLocalLunarEclipseDanjon 下次可见月食(Danjon) / next visible local lunar eclipse with Danjon model. -// Next visible local lunar eclipse with the Danjon model. -func NextLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) NextLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) return info } // NextLocalLunarEclipseChauvenet 下次可见月食(Chauvenet) / next visible local lunar eclipse with Chauvenet model. -// Next visible local lunar eclipse with the Chauvenet model. -func NextLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseChauvenet, localLunarEclipseQueryVisible) +func (module *Eclipse) NextLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryVisible, module) return info } // NextGeometricLocalLunarEclipse 下次几何月食 / next geometric local lunar eclipse. -// Next geometric local lunar eclipse, using Danjon by default. -func NextGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return NextGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) NextGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.NextGeometricLocalLunarEclipseDanjon(date, lon, lat, height) } // NextGeometricLocalLunarEclipseDanjon 下次几何月食(Danjon) / next geometric local lunar eclipse with Danjon model. -// Next geometric local lunar eclipse with the Danjon model. -func NextGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryGeometric) +func (module *Eclipse) NextGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryGeometric, module) return info } // NextGeometricLocalLunarEclipseChauvenet 下次几何月食(Chauvenet) / next geometric local lunar eclipse with Chauvenet model. -// Next geometric local lunar eclipse with the Chauvenet model. -func NextGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseChauvenet, localLunarEclipseQueryGeometric) +func (module *Eclipse) NextGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + info, _ := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryGeometric, module) return info } // ClosestLocalLunarEclipse 最近一次可见月食 / closest visible local lunar eclipse. -// Closest visible local lunar eclipse, using Danjon by default. -func ClosestLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return ClosestLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) ClosestLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.ClosestLocalLunarEclipseDanjon(date, lon, lat, height) } // ClosestLocalTotalLunarEclipse 最近一次可见月全食 / closest visible local total lunar eclipse. -// Closest visible local total lunar eclipse, using Danjon by default. -func ClosestLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { - last, hasLast := searchLocalTotalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) - next, hasNext := searchLocalTotalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalTotalLunarEclipse(date time.Time, lon, lat, height float64) (LocalLunarEclipseInfo, bool) { + last, hasLast := searchLocalTotalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) + next, hasNext := searchLocalTotalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) return closestLocalLunarEclipseResult(date, last, hasLast, next, hasNext) } // ClosestLocalLunarEclipseDanjon 最近一次可见月食(Danjon) / closest visible local lunar eclipse with Danjon model. -// Closest visible local lunar eclipse with the Danjon model. -func ClosestLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) - next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) + next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryVisible, module) return closestLocalLunarEclipse(date, last, hasLast, next, hasNext) } // ClosestLocalLunarEclipseChauvenet 最近一次可见月食(Chauvenet) / closest visible local lunar eclipse with Chauvenet model. -// Closest visible local lunar eclipse with the Chauvenet model. -func ClosestLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseChauvenet, localLunarEclipseQueryVisible) - next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseChauvenet, localLunarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryVisible, module) + next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryVisible, module) return closestLocalLunarEclipse(date, last, hasLast, next, hasNext) } // ClosestGeometricLocalLunarEclipse 最近一次几何月食 / closest geometric local lunar eclipse. -// Closest geometric local lunar eclipse, using Danjon by default. -func ClosestGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - return ClosestGeometricLocalLunarEclipseDanjon(date, lon, lat, height) +func (module *Eclipse) ClosestGeometricLocalLunarEclipse(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + return module.ClosestGeometricLocalLunarEclipseDanjon(date, lon, lat, height) } // ClosestGeometricLocalLunarEclipseDanjon 最近一次几何月食(Danjon) / closest geometric local lunar eclipse with Danjon model. -// Closest geometric local lunar eclipse with the Danjon model. -func ClosestGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseDanjon, localLunarEclipseQueryGeometric) - next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseDanjon, localLunarEclipseQueryGeometric) +func (module *Eclipse) ClosestGeometricLocalLunarEclipseDanjon(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryGeometric, module) + next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, localLunarEclipseQueryGeometric, module) return closestLocalLunarEclipse(date, last, hasLast, next, hasNext) } // ClosestGeometricLocalLunarEclipseChauvenet 最近一次几何月食(Chauvenet) / closest geometric local lunar eclipse with Chauvenet model. -// Closest geometric local lunar eclipse with the Chauvenet model. -func ClosestGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { - last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, basic.LunarEclipseChauvenet, localLunarEclipseQueryGeometric) - next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, basic.LunarEclipseChauvenet, localLunarEclipseQueryGeometric) +func (module *Eclipse) ClosestGeometricLocalLunarEclipseChauvenet(date time.Time, lon, lat, height float64) LocalLunarEclipseInfo { + last, hasLast := searchLocalLunarEclipse(date, lon, lat, height, -1, true, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryGeometric, module) + next, hasNext := searchLocalLunarEclipse(date, lon, lat, height, 1, false, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, localLunarEclipseQueryGeometric, module) return closestLocalLunarEclipse(date, last, hasLast, next, hasNext) } @@ -353,23 +378,27 @@ func searchLocalLunarEclipse( direction int, includeCurrent bool, calculator lunarEclipseCalculator, - mode localLunarEclipseQueryMode, -) (LocalLunarEclipseInfo, bool) { + mode localLunarEclipseQueryMode, modules ...*Eclipse) (LocalLunarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + targetTT := timeToTTJDE(date) - candidateTT := basic.CalcMoonSHByJDE(targetTT, 1) + candidateTT := basic.CalcMoonSHByJDEWithInstance(targetTT, 1, module.inst) for i := 0; i < localLunarEclipseSearchLimit; i++ { - if isPotentialLunarEclipse(candidateTT) { + if isPotentialLunarEclipseWithInstance(candidateTT, module.inst) { result := calculator(candidateTT) if result.Type != basic.LunarEclipseNone { - info := localLunarEclipseInfoFromBasic(result, lon, lat, height, date.Location()) - if (mode != localLunarEclipseQueryVisible || localLunarEclipseVisible(info)) && + info := localLunarEclipseInfoFromBasic(result, lon, lat, height, date.Location(), module) + if (mode != localLunarEclipseQueryVisible || localLunarEclipseVisible(info, module)) && lunarEclipseMatchesDirection(result.Maximum, targetTT, direction, includeCurrent) { return info, true } } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays) + candidateTT = nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays, module) } return LocalLunarEclipseInfo{}, false @@ -381,23 +410,27 @@ func searchLocalTotalLunarEclipse( direction int, includeCurrent bool, calculator lunarEclipseCalculator, - mode localLunarEclipseQueryMode, -) (LocalLunarEclipseInfo, bool) { + mode localLunarEclipseQueryMode, modules ...*Eclipse) (LocalLunarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + targetTT := timeToTTJDE(date) - candidateTT := basic.CalcMoonSHByJDE(targetTT, 1) + candidateTT := basic.CalcMoonSHByJDEWithInstance(targetTT, 1, module.inst) for i := 0; i < localLunarEclipseSearchLimit; i++ { - if isPotentialLunarEclipse(candidateTT) { + if isPotentialLunarEclipseWithInstance(candidateTT, module.inst) { result := calculator(candidateTT) if result.HasTotal { - info := localLunarEclipseInfoFromBasic(result, lon, lat, height, date.Location()) - if (mode != localLunarEclipseQueryVisible || localTotalLunarEclipseVisible(info)) && + info := localLunarEclipseInfoFromBasic(result, lon, lat, height, date.Location(), module) + if (mode != localLunarEclipseQueryVisible || localTotalLunarEclipseVisible(info, module)) && lunarEclipseMatchesDirection(result.Maximum, targetTT, direction, includeCurrent) { return info, true } } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays) + candidateTT = nextEclipseSearchCandidateTT(candidateTT, 1, direction, lunarEclipseSynodicMonthDays, module) } return LocalLunarEclipseInfo{}, false @@ -406,12 +439,16 @@ func searchLocalTotalLunarEclipse( func localLunarEclipseInfoFromBasic( result basic.LunarEclipseResult, lon, lat, height float64, - location *time.Location, -) LocalLunarEclipseInfo { + location *time.Location, modules ...*Eclipse) LocalLunarEclipseInfo { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + maximum := ttJDEToTime(result.Maximum, location) visibleThreshold := localLunarEclipseVisibilityThreshold(height, lat) - moonAltitude := lunarAltitude(maximum, lon, lat) - saros, hasSaros := lunarSarosInfo(result.Maximum) + moonAltitude := lunarAltitude(maximum, lon, lat, module) + saros, hasSaros := lunarSarosInfoWithInstance(result.Maximum, module.inst) info := LocalLunarEclipseInfo{ HasSaros: hasSaros, @@ -430,13 +467,13 @@ func localLunarEclipseInfoFromBasic( PartialEnd: ttJDEToTime(result.PartialEnd, location), PenumbralEnd: ttJDEToTime(result.PenumbralEnd, location), MoonAltitude: moonAltitude, - MoonAzimuth: lunarAzimuth(maximum, lon, lat), + MoonAzimuth: lunarAzimuth(maximum, lon, lat, module), VisibleAtMaximum: moonAltitude > visibleThreshold, HasPenumbral: result.HasPenumbral, HasPartial: result.HasPartial, HasTotal: result.HasTotal, } - info.Visibility = localLunarEclipseVisibility(info) + info.Visibility = localLunarEclipseVisibility(info, module) return info } @@ -455,22 +492,37 @@ func localLunarEclipseRange(info LocalLunarEclipseInfo) (time.Time, time.Time, b return info.PenumbralStart, info.PenumbralEnd, true } -func localLunarEclipseVisible(info LocalLunarEclipseInfo) bool { +func localLunarEclipseVisible(info LocalLunarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + eventStart, eventEnd, ok := localLunarEclipseRange(info) if !ok { return false } - return localLunarEclipseVisibleDuring(info, eventStart, eventEnd) + return localLunarEclipseVisibleDuring(info, eventStart, eventEnd, module) } -func localTotalLunarEclipseVisible(info LocalLunarEclipseInfo) bool { +func localTotalLunarEclipseVisible(info LocalLunarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !info.HasTotal || info.TotalStart.IsZero() || info.TotalEnd.IsZero() { return false } - return localLunarEclipseVisibleDuring(info, info.TotalStart, info.TotalEnd) + return localLunarEclipseVisibleDuring(info, info.TotalStart, info.TotalEnd, module) } -func localLunarEclipseVisibleOnDate(info LocalLunarEclipseInfo, dayStart, dayEnd time.Time) bool { +func localLunarEclipseVisibleOnDate(info LocalLunarEclipseInfo, dayStart, dayEnd time.Time, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + eventStart, eventEnd, ok := localLunarEclipseRange(info) if !ok { return false @@ -480,21 +532,26 @@ func localLunarEclipseVisibleOnDate(info LocalLunarEclipseInfo, dayStart, dayEnd if !segmentStart.Before(segmentEnd) { return false } - return localLunarEclipseVisibleDuring(info, segmentStart, segmentEnd) + return localLunarEclipseVisibleDuring(info, segmentStart, segmentEnd, module) } // localLunarEclipseVisibility 按站点自己的中天对食内高度分类。 -func localLunarEclipseVisibility(info LocalLunarEclipseInfo) LocalLunarEclipseVisibility { +func localLunarEclipseVisibility(info LocalLunarEclipseInfo, modules ...*Eclipse) LocalLunarEclipseVisibility { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + start, end, ok := localLunarEclipseRange(info) if !ok { return LocalLunarEclipseInvisible } - startVisible := localLunarEclipseAltitudeVisible(start, info) - endVisible := localLunarEclipseAltitudeVisible(end, info) - umbralVisible := localLunarEclipseUmbralVisible(info) + startVisible := localLunarEclipseAltitudeVisible(start, info, module) + endVisible := localLunarEclipseAltitudeVisible(end, info, module) + umbralVisible := localLunarEclipseUmbralVisible(info, module) switch { case startVisible && endVisible: - if localLunarEclipseExtremumAltitude(info, start, end, false) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) { + if localLunarEclipseExtremumAltitude(info, start, end, false, module) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) { return LocalLunarEclipseFull } return LocalLunarEclipseInterrupted @@ -508,7 +565,7 @@ func localLunarEclipseVisibility(info LocalLunarEclipseInfo) LocalLunarEclipseVi return LocalLunarEclipsePenumbraMoonset } return LocalLunarEclipseMoonset - case localLunarEclipseVisibleDuring(info, start, end): + case localLunarEclipseVisibleDuring(info, start, end, module): return LocalLunarEclipseRiseAndSet default: return LocalLunarEclipseInvisible @@ -518,7 +575,12 @@ func localLunarEclipseVisibility(info LocalLunarEclipseInfo) LocalLunarEclipseVi // localLunarEclipseUmbralVisible 本影阶段是否有任何时刻月亮在地平线上;纯半影月食没有本影可看, // 恒为 true,不把这类站点降级成"仅见半影"。极区掠射时 U1、食甚、U4 三点都可能在地平下而中途 // 短暂露出,所以这里取整个本影区间的高度极值,不能只比三个采样点。 -func localLunarEclipseUmbralVisible(info LocalLunarEclipseInfo) bool { +func localLunarEclipseUmbralVisible(info LocalLunarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !info.HasPartial { return true } @@ -526,25 +588,30 @@ func localLunarEclipseUmbralVisible(info LocalLunarEclipseInfo) bool { if start.IsZero() || end.IsZero() || !start.Before(end) { return false } - if localLunarEclipseAltitudeVisible(start, info) || localLunarEclipseAltitudeVisible(end, info) { + if localLunarEclipseAltitudeVisible(start, info, module) || localLunarEclipseAltitudeVisible(end, info, module) { return true } - return localLunarEclipseExtremumAltitude(info, start, end, true) > + return localLunarEclipseExtremumAltitude(info, start, end, true, module) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) } const lunarEclipseVisibilityScanSamples = 48 // localLunarEclipseExtremumAltitude 先粗扫定位、再在相邻两格内三分细化地取有限窗口内的月心高度极值。 -func localLunarEclipseExtremumAltitude(info LocalLunarEclipseInfo, start, end time.Time, maximum bool) float64 { +func localLunarEclipseExtremumAltitude(info LocalLunarEclipseInfo, start, end time.Time, maximum bool, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !start.Before(end) { - return lunarAltitude(start, info.Longitude, info.Latitude) + return lunarAltitude(start, info.Longitude, info.Latitude, module) } step := end.Sub(start) / lunarEclipseVisibilityScanSamples - best := lunarAltitude(start, info.Longitude, info.Latitude) + best := lunarAltitude(start, info.Longitude, info.Latitude, module) bestIndex := 0 for index := 1; index <= lunarEclipseVisibilityScanSamples; index++ { - value := lunarAltitude(start.Add(step*time.Duration(index)), info.Longitude, info.Latitude) + value := lunarAltitude(start.Add(step*time.Duration(index)), info.Longitude, info.Latitude, module) if (value > best) == maximum { best, bestIndex = value, index } @@ -561,29 +628,39 @@ func localLunarEclipseExtremumAltitude(info LocalLunarEclipseInfo, start, end ti for iteration := 0; iteration < 30; iteration++ { third := high.Sub(low) / 3 first, second := low.Add(third), high.Add(-third) - if (lunarAltitude(first, info.Longitude, info.Latitude) < lunarAltitude(second, info.Longitude, info.Latitude)) == maximum { + if (lunarAltitude(first, info.Longitude, info.Latitude, module) < lunarAltitude(second, info.Longitude, info.Latitude, module)) == maximum { low = first } else { high = second } } - refined := lunarAltitude(low.Add(high.Sub(low)/2), info.Longitude, info.Latitude) + refined := lunarAltitude(low.Add(high.Sub(low)/2), info.Longitude, info.Latitude, module) if maximum { return math.Max(best, refined) } return math.Min(best, refined) } -func localLunarEclipseVisibleDuring(info LocalLunarEclipseInfo, start, end time.Time) bool { - if localLunarEclipseAltitudeVisible(start, info) || localLunarEclipseAltitudeVisible(end, info) { +func localLunarEclipseVisibleDuring(info LocalLunarEclipseInfo, start, end time.Time, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + if localLunarEclipseAltitudeVisible(start, info, module) || localLunarEclipseAltitudeVisible(end, info, module) { return true } - return localLunarEclipseExtremumAltitude(info, start, end, true) > + return localLunarEclipseExtremumAltitude(info, start, end, true, module) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) } -func localLunarEclipseAltitudeVisible(date time.Time, info LocalLunarEclipseInfo) bool { - return lunarAltitude(date, info.Longitude, info.Latitude) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) +func localLunarEclipseAltitudeVisible(date time.Time, info LocalLunarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + return lunarAltitude(date, info.Longitude, info.Latitude, module) > localLunarEclipseVisibilityThreshold(info.Height, info.Latitude) } func localLunarEclipseVisibilityThreshold(height, latitude float64) float64 { diff --git a/eclipse/lunar_local_test.go b/eclipse/lunar_local_test.go index 646ddb3..bb1482a 100644 --- a/eclipse/lunar_local_test.go +++ b/eclipse/lunar_local_test.go @@ -3,6 +3,8 @@ package eclipse import ( "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestLocalLunarEclipseOnDateByLocalDay(t *testing.T) { @@ -229,13 +231,14 @@ func TestLocalLunarEclipseInfoKeepsLocation(t *testing.T) { } func TestLocalLunarEclipseChauvenetRemainsAvailable(t *testing.T) { + values := ephemtest.NewValues(t, "local_lunar_chauvenet_available") date := time.Date(2025, 3, 14, 12, 0, 0, 0, time.FixedZone("CDT", -5*3600)) lon, lat, height := -87.65, 41.85, 0.0 defaultInfo := ClosestLocalLunarEclipse(date, lon, lat, height) chauvenetInfo := ClosestLocalLunarEclipseChauvenet(date, lon, lat, height) - assertFloatClose(t, "Chauvenet.PenumbralMagnitude", chauvenetInfo.PenumbralMagnitude, 2.285436461, 1e-6) - assertFloatClose(t, "Chauvenet.UmbralMagnitude", chauvenetInfo.UmbralMagnitude, 1.182806541, 1e-6) + assertFloatClose(t, "Chauvenet.PenumbralMagnitude", chauvenetInfo.PenumbralMagnitude, values.Want("Chauvenet.PenumbralMagnitude", 2.285436461, chauvenetInfo.PenumbralMagnitude), 1e-6) + assertFloatClose(t, "Chauvenet.UmbralMagnitude", chauvenetInfo.UmbralMagnitude, values.Want("Chauvenet.UmbralMagnitude", 1.182806541, chauvenetInfo.UmbralMagnitude), 1e-6) if !(chauvenetInfo.PenumbralMagnitude > defaultInfo.PenumbralMagnitude) { t.Fatalf("expected Chauvenet penumbral magnitude > Danjon: chauvenet=%.6f danjon=%.6f", chauvenetInfo.PenumbralMagnitude, defaultInfo.PenumbralMagnitude) diff --git a/eclipse/lunar_panel.go b/eclipse/lunar_panel.go index d5eafb7..c028536 100644 --- a/eclipse/lunar_panel.go +++ b/eclipse/lunar_panel.go @@ -51,26 +51,33 @@ type LunarEclipseGeocentricPanel struct { Ephemeris string } -// LunarEclipseGeocentricPanelAt 计算给定日期月食的地心量面板,使用 Danjon 影半径模型。 -// LunarEclipseGeocentricPanelAt computes the geocentric panel of one lunar eclipse with the Danjon shadow model. -func LunarEclipseGeocentricPanelAt(date time.Time) (LunarEclipseGeocentricPanel, bool) { - return lunarEclipseGeocentricPanelAt(date, basic.LunarEclipseDanjon) +// LunarEclipseGeocentricPanelAt 计算给定日期月食的地心量面板,使用 Danjon 影半径模型 / geocentric lunar eclipse panel with Danjon shadow radii. +func (module *Eclipse) LunarEclipseGeocentricPanelAt(date time.Time) (LunarEclipseGeocentricPanel, bool) { + return lunarEclipseGeocentricPanelAt(date, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, false) + }, module) } -// LunarEclipseGeocentricPanelChauvenet 计算给定日期月食的地心量面板,使用 Chauvenet 影半径模型。 -// LunarEclipseGeocentricPanelChauvenet computes the geocentric panel with the Chauvenet shadow model. -func LunarEclipseGeocentricPanelChauvenet(date time.Time) (LunarEclipseGeocentricPanel, bool) { - return lunarEclipseGeocentricPanelAt(date, basic.LunarEclipseChauvenet) +// LunarEclipseGeocentricPanelChauvenet 计算给定日期月食的地心量面板,使用 Chauvenet 影半径模型 / geocentric lunar eclipse panel with Chauvenet shadow radii. +func (module *Eclipse) LunarEclipseGeocentricPanelChauvenet(date time.Time) (LunarEclipseGeocentricPanel, bool) { + return lunarEclipseGeocentricPanelAt(date, func(seedJDE float64) basic.LunarEclipseResult { + return module.lunarResult(seedJDE, true) + }, module) } -func lunarEclipseGeocentricPanelAt(date time.Time, calculator func(float64) basic.LunarEclipseResult) (LunarEclipseGeocentricPanel, bool) { +func lunarEclipseGeocentricPanelAt(date time.Time, calculator func(float64) basic.LunarEclipseResult, modules ...*Eclipse) (LunarEclipseGeocentricPanel, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + seed := timeToTTJDE(date) - fullMoonJDE := basic.CalcMoonSHByJDE(seed, 1) + fullMoonJDE := basic.CalcMoonSHByJDEWithInstance(seed, 1, module.inst) result := calculator(fullMoonJDE) if result.Type == basic.LunarEclipseNone { return LunarEclipseGeocentricPanel{}, false } - geometry := basic.LunarEclipseShadowGeometryAtModel(result.Maximum, result.ShadowModel) + geometry := basic.LunarEclipseShadowGeometryAtModelWithInstance(result.Maximum, result.ShadowModel, module.inst) ephemeris := "Danjon" if result.ShadowModel == basic.LunarEclipseShadowModelChauvenet { ephemeris = "Chauvenet" @@ -89,10 +96,10 @@ func lunarEclipseGeocentricPanelAt(date time.Time, calculator func(float64) basi UmbralMagnitude: result.Magnitude, Ephemeris: ephemeris, } - panel.SunRightAscensionDeg, panel.SunDeclinationDeg = basic.SunApparentRaDec(result.Maximum) - panel.MoonRightAscensionDeg, panel.MoonDeclinationDeg = basic.HMoonTrueRaDec(result.Maximum) - panel.SunSemidiameterArcsec = basic.SolarEclipseSunSemidiameter(result.Maximum, basic.SolarEclipseSunRadiusStandard) - panel.MoonSemidiameterArcsec = basic.MoonSemidiameter(result.Maximum) + panel.SunRightAscensionDeg, panel.SunDeclinationDeg = basic.SunApparentRaDecWithInstance(result.Maximum, module.inst) + panel.MoonRightAscensionDeg, panel.MoonDeclinationDeg = basic.HMoonTrueRaDecWithInstance(result.Maximum, module.inst) + panel.SunSemidiameterArcsec = basic.SolarEclipseSunSemidiameterWithInstance(result.Maximum, basic.SolarEclipseSunRadiusStandard, module.inst) + panel.MoonSemidiameterArcsec = basic.MoonSemidiameterWithInstance(result.Maximum, module.inst) panel.SunParallaxArcsec = horizontalParallaxArcsec(panel.SunSemidiameterArcsec, horizontalParallaxSunRatio) panel.MoonParallaxArcsec = horizontalParallaxArcsec(panel.MoonSemidiameterArcsec, horizontalParallaxMoonRatio) return panel, true diff --git a/eclipse/lunar_test.go b/eclipse/lunar_test.go index 329e222..0de268b 100644 --- a/eclipse/lunar_test.go +++ b/eclipse/lunar_test.go @@ -3,6 +3,8 @@ package eclipse import ( "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestLunarEclipseLocalDayBoundsRespectDST(t *testing.T) { @@ -179,6 +181,7 @@ func TestLunarEclipseInfoKeepsLocation(t *testing.T) { } func TestLunarEclipseContactPoints(t *testing.T) { + values := ephemtest.NewValues(t, "lunar_contact_points") loc := time.FixedZone("UTC+08", 8*3600) info := ClosestLunarEclipse(time.Date(2026, 3, 3, 12, 0, 0, 0, loc)) if info.Type != LunarEclipseTotal { @@ -193,20 +196,21 @@ func TestLunarEclipseContactPoints(t *testing.T) { points[point.Label] = point } u1 := points["U1"] - assertFloatClose(t, "U1.ContactPositionAngle", u1.ContactPositionAngle, 96.181711, 1e-3) - assertFloatClose(t, "U1.ContactClockwiseAngle", u1.ContactClockwiseAngle, 263.818289, 1e-3) + assertFloatClose(t, "U1.ContactPositionAngle", u1.ContactPositionAngle, values.Want("U1.ContactPositionAngle", 96.181711, u1.ContactPositionAngle), 1e-3) + assertFloatClose(t, "U1.ContactClockwiseAngle", u1.ContactClockwiseAngle, values.Want("U1.ContactClockwiseAngle", 263.818289, u1.ContactClockwiseAngle), 1e-3) u2 := points["U2"] - assertFloatClose(t, "U2.ContactPositionAngle", u2.ContactPositionAngle, 243.025171, 1e-3) - assertFloatClose(t, "U2.MoonCenterPositionAngle", u2.MoonCenterPositionAngle, 243.025171, 1e-3) + assertFloatClose(t, "U2.ContactPositionAngle", u2.ContactPositionAngle, values.Want("U2.ContactPositionAngle", 243.025171, u2.ContactPositionAngle), 1e-3) + assertFloatClose(t, "U2.MoonCenterPositionAngle", u2.MoonCenterPositionAngle, values.Want("U2.MoonCenterPositionAngle", 243.025171, u2.MoonCenterPositionAngle), 1e-3) } func TestLunarEclipseChauvenetRemainsAvailable(t *testing.T) { + values := ephemtest.NewValues(t, "lunar_chauvenet_available") date := time.Date(2025, 3, 14, 12, 0, 0, 0, time.UTC) defaultInfo := ClosestLunarEclipse(date) chauvenetInfo := ClosestLunarEclipseChauvenet(date) - assertFloatClose(t, "Chauvenet.PenumbralMagnitude", chauvenetInfo.PenumbralMagnitude, 2.285436461, 1e-6) - assertFloatClose(t, "Chauvenet.UmbralMagnitude", chauvenetInfo.UmbralMagnitude, 1.182806541, 1e-6) + assertFloatClose(t, "Chauvenet.PenumbralMagnitude", chauvenetInfo.PenumbralMagnitude, values.Want("Chauvenet.PenumbralMagnitude", 2.285436461, chauvenetInfo.PenumbralMagnitude), 1e-6) + assertFloatClose(t, "Chauvenet.UmbralMagnitude", chauvenetInfo.UmbralMagnitude, values.Want("Chauvenet.UmbralMagnitude", 1.182806541, chauvenetInfo.UmbralMagnitude), 1e-6) if !(chauvenetInfo.PenumbralMagnitude > defaultInfo.PenumbralMagnitude) { t.Fatalf("expected Chauvenet penumbral magnitude > Danjon: chauvenet=%.6f danjon=%.6f", chauvenetInfo.PenumbralMagnitude, defaultInfo.PenumbralMagnitude) @@ -217,6 +221,8 @@ func TestLunarEclipseChauvenetRemainsAvailable(t *testing.T) { } func TestLunarEclipseDefaultFallsBackForUltraShallowPenumbralEdge(t *testing.T) { + // 夹具按旧模型几何选取,用来覆盖该边界分支。 + ephemtest.PinLegacy(t) date := time.Date(-780, 12, 13, 12, 0, 0, 0, time.UTC) defaultInfo := ClosestLunarEclipse(date) diff --git a/eclipse/observation_helpers.go b/eclipse/observation_helpers.go index 7a3c0bc..f533b5b 100644 --- a/eclipse/observation_helpers.go +++ b/eclipse/observation_helpers.go @@ -14,28 +14,48 @@ func moonSunLoDiff(date time.Time) float64 { return tools.Limit360(moonLo - sunLo) } -func solarAltitude(date time.Time, lon, lat float64) float64 { +func solarAltitude(date time.Time, lon, lat float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.SunHeight(localJD, lon, lat, float64(loc)/3600.0) + return basic.SunHeightWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } -func solarCulminationTime(date time.Time, lon float64) time.Time { +func solarCulminationTime(date time.Time, lon float64, modules ...*Eclipse) time.Time { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + localJD := basic.Date2JD(date.Add(time.Duration(-1*date.Hour())*time.Hour)) + 0.5 _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.CulminationTime(localJD, lon, timezone) - timezone/24.0 + calcJD := basic.CulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -func lunarAzimuth(date time.Time, lon, lat float64) float64 { +func lunarAzimuth(date time.Time, lon, lat float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonAzimuth(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonAzimuthWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } -func lunarAltitude(date time.Time, lon, lat float64) float64 { +func lunarAltitude(date time.Time, lon, lat float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + localJD := basic.Date2JD(date) _, loc := date.Zone() - return basic.HMoonHeight(localJD, lon, lat, float64(loc)/3600.0) + return basic.HMoonHeightWithInstance(localJD, lon, lat, float64(loc)/3600.0, module.inst) } diff --git a/eclipse/saros.go b/eclipse/saros.go index 3f55770..229468d 100644 --- a/eclipse/saros.go +++ b/eclipse/saros.go @@ -5,6 +5,7 @@ import ( "sync" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" ) const ( @@ -29,6 +30,11 @@ const ( ) // SarosInfo 沙罗序列信息, Saros series metadata. +// +// Series 与星历模型无关;Member 与 Count 在序列首尾可能随模型变化:食分接近 0 的 +// 擦边食是否成立取决于模型,差异量级小于模型自身误差,不同模型因而可能多算或少算 +// 一个首尾成员。Series is model-independent; Member and Count can differ by one at +// a series head or tail, where a grazing eclipse falls within model error. type SarosInfo struct { // Series 是权威目录系列编号;Verified=false 时为根据周期外推得到的暂定编号。 // Series is the authoritative catalog series number, or a provisional number when Verified is false. @@ -154,11 +160,19 @@ var lunarSarosHeadOverrides = [...]sarosHeadOverride{ {Series: 61, Count: 78, HeadYear: -762, HeadMonth: 12, HeadDay: 24, MemberOffset: 1}, } -func solarSarosInfo(ttJDE float64) (SarosInfo, bool) { +func solarSarosInfo(ttJDE float64, modules ...*Eclipse) (SarosInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + if module.model() == ephem.Custom { + return SarosInfo{}, false + } if info, ok := matchSarosMagic(solarSarosAnchors[:], 0, solarSarosHeadOverrides[:], ttJDE); ok { return info, true } - return reconciledSarosInfo(extendedSarosInfo, ttJDE, 0) + return reconciledSarosInfo(module.extendedSarosInfo, ttJDE, 0) } func lunarSarosInfo(ttJDE float64) (SarosInfo, bool) { diff --git a/eclipse/saros_anchor_consistency_test.go b/eclipse/saros_anchor_consistency_test.go index e365ff4..8af81f3 100644 --- a/eclipse/saros_anchor_consistency_test.go +++ b/eclipse/saros_anchor_consistency_test.go @@ -80,9 +80,9 @@ func sarosTestMember(window sarosTestWindow, ttJDE float64) int { func sarosTestSpans(phase int) []sarosSpan { if phase == 1 { - return lunarSarosExtended[:] + return sarosExtensionSpans(1) } - return solarSarosExtended[:] + return sarosExtensionSpans(0) } func sarosTestInfo(ttJDE float64, phase int) (SarosInfo, bool) { diff --git a/eclipse/saros_benchmark_test.go b/eclipse/saros_benchmark_test.go index 0d12eb3..a1b4ba5 100644 --- a/eclipse/saros_benchmark_test.go +++ b/eclipse/saros_benchmark_test.go @@ -42,10 +42,10 @@ func BenchmarkSarosLookup(b *testing.B) { } func BenchmarkSolarEclipsePrecomputedSaros(b *testing.B) { - date := time.Date(3288, 11, 15, 12, 0, 0, 0, time.UTC) + date := time.Date(3306, 11, 28, 12, 0, 0, 0, time.UTC) for i := 0; i < b.N; i++ { info, ok := SolarEclipseOnDate(date) - if !ok || !info.HasSaros || info.Saros != (SarosInfo{Series: 202, Member: 1, Count: 71}) { + if !ok || !info.HasSaros || info.Saros.Series != 202 { b.Fatal("missing precomputed Saros metadata") } } diff --git a/eclipse/saros_extended.go b/eclipse/saros_extended.go index 9318f8d..f68cbcc 100644 --- a/eclipse/saros_extended.go +++ b/eclipse/saros_extended.go @@ -5,11 +5,14 @@ import ( "sync" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" ) const ( - sarosInexLunations = 358 - sarosLunationEpoch = 2451550.09765 + sarosExtendedStartTT = 625308.343824885320 + sarosExtendedEndTT = 3912880.500800740905 + sarosInexLunations = 358 + sarosLunationEpoch = 2451550.09765 // Keep the acceptance interval below half a synodic month while allowing // the mean-phase seed to drift in remote eras. sarosEclipseSeedToleranceDays = 7.0 @@ -17,9 +20,9 @@ const ( sarosEclipseMemoSlots = 4096 ) -// 键是(朔望月序号,相位);判定与模型无关,只有月序号和相位决定结果。 type sarosEclipseMemoEntry struct { key int64 + gen uint64 maximum float64 exists bool valid bool @@ -42,7 +45,7 @@ func sarosEclipseLookup(k, phase int) (float64, bool, bool, bool) { sarosEclipseMemoMu.RLock() entry := sarosEclipseMemoEntries[slot] sarosEclipseMemoMu.RUnlock() - if !entry.set || entry.key != key { + if !entry.set || entry.key != key || entry.gen != ephem.Generation() { return 0, false, false, false } return entry.maximum, entry.exists, entry.valid, true @@ -53,7 +56,7 @@ func sarosEclipseStore(k, phase int, maximum float64, exists, valid bool) { slot := sarosEclipseMemoSlot(key) sarosEclipseMemoMu.Lock() sarosEclipseMemoEntries[slot] = sarosEclipseMemoEntry{ - key: key, maximum: maximum, exists: exists, valid: valid, set: true, + key: key, gen: ephem.Generation(), maximum: maximum, exists: exists, valid: valid, set: true, } sarosEclipseMemoMu.Unlock() } @@ -108,15 +111,20 @@ func matchSarosSpans(spans []sarosSpan, k int) (SarosInfo, bool) { } func extendedSarosInfo(ttJDE float64, phase int) (SarosInfo, bool) { + return defaultModule.extendedSarosInfo(ttJDE, phase) +} + +func (module *Eclipse) extendedSarosInfo(ttJDE float64, phase int) (SarosInfo, bool) { + return extendedSarosInfoForModel(ttJDE, phase, module.model(), module.sarosEclipse) +} + +func extendedSarosInfoForModel(ttJDE float64, phase int, model ephem.Model, evaluate func(int, int) (float64, bool, bool)) (SarosInfo, bool) { k, ok := sarosLunation(ttJDE, phase) if !ok { return SarosInfo{}, false } if ttJDE >= sarosExtendedStartTT && ttJDE < sarosExtendedEndTT { - spans := solarSarosExtended[:] - if phase == 1 { - spans = lunarSarosExtended[:] - } + spans := sarosExtensionSpansForModel(phase, model) info, ok := matchSarosSpans(spans, k) if !ok { return SarosInfo{}, false @@ -126,31 +134,58 @@ func extendedSarosInfo(ttJDE float64, phase int) (SarosInfo, bool) { } return info, true } - return extrapolateSaros(k, phase) + return extrapolateSarosWithEvaluator(k, phase, evaluate) } -func sarosEclipse(k, phase int) (float64, bool, bool) { +func sarosExtensionSpans(phase int) []sarosSpan { + return sarosExtensionSpansForModel(phase, ephem.Active()) +} + +func sarosExtensionSpansForModel(phase int, model ephem.Model) []sarosSpan { + if model == ephem.Semianalytic { + if phase == 1 { + return lunarSarosExtendedSemianalytic[:] + } + return solarSarosExtendedSemianalytic[:] + } + if phase == 1 { + return lunarSarosExtendedLegacy[:] + } + return solarSarosExtendedLegacy[:] +} + +func sarosEclipse(k, phase int) (float64, bool, bool) { return defaultModule.sarosEclipse(k, phase) } + +func (module *Eclipse) sarosEclipse(k, phase int) (float64, bool, bool) { + if module.inst != nil { + return sarosEclipseUncached(k, phase, module) + } if maximum, exists, valid, ok := sarosEclipseLookup(k, phase); ok { return maximum, exists, valid } - maximum, exists, valid := sarosEclipseUncached(k, phase) + maximum, exists, valid := sarosEclipseUncached(k, phase, module) sarosEclipseStore(k, phase, maximum, exists, valid) return maximum, exists, valid } // Saros metadata uses the Split-K solar model and the union of Danjon and // Chauvenet lunar detections, independent of the observer or display model. -func sarosEclipseUncached(k, phase int) (float64, bool, bool) { +func sarosEclipseUncached(k, phase int, modules ...*Eclipse) (float64, bool, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + seed := sarosLunationEpoch + (float64(k)+float64(phase)/2)*solarEclipseSynodicMonthDays var maximum float64 var exists bool if phase == 0 { - result := basic.SolarEclipseNASABulletinSplitK(seed) + result := basic.SolarEclipseNASABulletinSplitKWithInstance(seed, module.inst) maximum, exists = result.GreatestEclipse, result.Type != basic.SolarEclipseNone } else { - result := basic.LunarEclipseDanjon(seed) + result := basic.LunarEclipseDanjonWithInstance(seed, module.inst) if result.Type == basic.LunarEclipseNone { - result = basic.LunarEclipseChauvenet(seed) + result = basic.LunarEclipseChauvenetWithInstance(seed, module.inst) } maximum, exists = result.Maximum, result.Type != basic.LunarEclipseNone } @@ -159,9 +194,13 @@ func sarosEclipseUncached(k, phase int) (float64, bool, bool) { } func extrapolateSaros(k, phase int) (SarosInfo, bool) { + return extrapolateSarosWithEvaluator(k, phase, sarosEclipse) +} + +func extrapolateSarosWithEvaluator(k, phase int, evaluate func(int, int) (float64, bool, bool)) (SarosInfo, bool) { info := SarosInfo{Series: sarosNumber(k, phase)} for offset := -sarosExtrapolationWindow; offset <= sarosExtrapolationWindow; offset++ { - _, exists, valid := sarosEclipse(k+offset*sarosCycleLunations, phase) + _, exists, valid := evaluate(k+offset*sarosCycleLunations, phase) if !valid || (offset == 0 && !exists) { return SarosInfo{}, false } diff --git a/eclipse/saros_extended_test.go b/eclipse/saros_extended_test.go index df16508..05a3a1d 100644 --- a/eclipse/saros_extended_test.go +++ b/eclipse/saros_extended_test.go @@ -7,6 +7,8 @@ import ( "time" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "b612.me/astro/internal/ephemtest" ) func TestSarosNumberAgainstAllCatalogAnchors(t *testing.T) { @@ -28,20 +30,31 @@ func TestSarosNumberAgainstAllCatalogAnchors(t *testing.T) { } func TestExtendedSarosUsesPrecomputedSeries(t *testing.T) { - got := ClosestSolarEclipse(time.Date(3288, 11, 15, 12, 0, 0, 0, time.UTC)) - if !got.HasSaros || got.Saros != (SarosInfo{Series: 202, Member: 1, Count: 71}) { - t.Fatalf("unexpected precomputed solar Saros: has=%v info=%+v", got.HasSaros, got.Saros) + var span sarosSpan + for _, candidate := range sarosExtensionSpans(0) { + if candidate.Series == 202 { + span = candidate + break + } } - next := ClosestSolarEclipse(time.Date(3306, 11, 28, 12, 0, 0, 0, time.UTC)) - if !next.HasSaros || next.Saros != (SarosInfo{Series: 202, Member: 2, Count: 71}) { - t.Fatalf("unexpected second precomputed member: %+v", next.Saros) + if span.Count == 0 { + t.Fatal("missing precomputed solar Saros series 202") + } + for _, k := range []int{span.First, span.Last} { + ttJDE, exists, valid := sarosEclipse(k, 0) + if !exists || !valid { + t.Fatalf("precomputed solar Saros k=%d: exists=%v valid=%v", k, exists, valid) + } + want := SarosInfo{Series: span.Series, Member: span.Member + (k-span.First)/sarosCycleLunations, Count: span.Count} + got, ok := extendedSarosInfo(ttJDE, 0) + if !ok || got != want { + t.Fatalf("precomputed solar Saros k=%d: got=%+v/%v want=%+v", k, got, ok, want) + } } - previous := ClosestSolarEclipse(time.Date(3288, 10, 17, 12, 0, 0, 0, time.UTC)) - assertSarosInfo(t, previous.HasSaros, previous.Saros, 164, 67, 80) } func TestExtendedSarosSpansAreComplete(t *testing.T) { - for phase, spans := range [][]sarosSpan{solarSarosExtended[:], lunarSarosExtended[:]} { + for phase, spans := range [][]sarosSpan{sarosExtensionSpans(0), sarosExtensionSpans(1)} { seen := make(map[int]bool) for i, span := range spans { if span.First > span.Last || (span.Last-span.First)%sarosCycleLunations != 0 || span.Member < 1 || span.Member > span.Count { @@ -96,10 +109,7 @@ func TestExtendedSarosMatchesLiveSeries(t *testing.T) { {phase: 1, series: 202}, } { t.Run(fmt.Sprintf("phase%d/series%d", tc.phase, tc.series), func(t *testing.T) { - spans := solarSarosExtended[:] - if tc.phase == 1 { - spans = lunarSarosExtended[:] - } + spans := sarosExtensionSpans(tc.phase) found := false for _, span := range spans { if span.Series != tc.series { @@ -137,7 +147,7 @@ func TestExtendedSarosRangeAndInvalidInput(t *testing.T) { if sarosExtendedStartTT != timeToTTJDE(time.Date(-3000, 1, 1, 0, 0, 0, 0, time.UTC)) || sarosExtendedEndTT != timeToTTJDE(time.Date(6001, 1, 1, 0, 0, 0, 0, time.UTC)) { t.Fatal("precomputed range must include both end years") } - for phase, spans := range [][]sarosSpan{solarSarosExtended[:], lunarSarosExtended[:]} { + for phase, spans := range [][]sarosSpan{sarosExtensionSpans(0), sarosExtensionSpans(1)} { for _, tt := range []float64{sarosExtendedStartTT, sarosExtendedEndTT - 1.0/86400} { k, _ := sarosLunation(tt, phase) want, wantOK := matchSarosSpans(spans, k) @@ -152,9 +162,35 @@ func TestExtendedSarosRangeAndInvalidInput(t *testing.T) { } } } - if got, ok := extrapolateSaros(-56340, 1); ok || got != (SarosInfo{}) { - t.Fatalf("missing lunar return produced metadata: %+v/%v", got, ok) + missing, found := firstMissingSarosReturn(sarosExtensionSpans(1)) + if !found { + t.Fatal("test table has no missing lunar return") } + if got, ok := extrapolateSaros(missing, 1); ok || got != (SarosInfo{}) { + t.Fatalf("missing lunar return k=%d produced metadata: %+v/%v", missing, got, ok) + } +} + +func TestSarosTablesFollowRuntimeModel(t *testing.T) { + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic, ephem.Legacy} { + t.Run(fmt.Sprintf("model%d", model), func(t *testing.T) { + ephemtest.Pin(t, model) + TestSarosExtensionTableIsFresh(t) + TestExtendedSarosUsesPrecomputedSeries(t) + TestExtendedSarosSpansAreComplete(t) + TestExtendedSarosMatchesLiveSeries(t) + }) + } +} + +func firstMissingSarosReturn(spans []sarosSpan) (int, bool) { + for index := 1; index < len(spans); index++ { + previous, current := spans[index-1], spans[index] + if previous.Series == current.Series && current.First > previous.Last+sarosCycleLunations { + return previous.Last + sarosCycleLunations, true + } + } + return 0, false } // 直映缓存只复用结果:命中与未命中都必须与未缓存求值逐字段相同,外推结果也必须与 @@ -165,7 +201,8 @@ func TestSarosEclipseMemoMatchesUncachedEvaluation(t *testing.T) { for attempt := 0; attempt < 2; attempt++ { maximum, exists, valid := sarosEclipse(k, phase) wantMaximum, wantExists, wantValid := sarosEclipseUncached(k, phase) - if maximum != wantMaximum || exists != wantExists || valid != wantValid { + sameMaximum := maximum == wantMaximum || (math.IsNaN(maximum) && math.IsNaN(wantMaximum)) + if !sameMaximum || exists != wantExists || valid != wantValid { t.Fatalf("phase=%d k=%d attempt=%d cached=%v/%v/%v uncached=%v/%v/%v", phase, k, attempt, maximum, exists, valid, wantMaximum, wantExists, wantValid) } @@ -204,3 +241,30 @@ func sarosExtrapolationUncachedForTest(k, phase int) (SarosInfo, bool) { } return info, true } + +// 序列首尾的擦边食是否成立随模型而定:Series 不变,Member/Count 可差一位。 +func TestSarosGrazingHeadDependsOnModel(t *testing.T) { + head := time.Date(3288, 11, 15, 12, 0, 0, 0, time.UTC) + next := time.Date(3306, 11, 28, 12, 0, 0, 0, time.UTC) + for _, tc := range []struct { + model ephem.Model + hasHead bool + head SarosInfo + next SarosInfo + }{ + {ephem.Legacy, true, SarosInfo{Series: 202, Member: 1, Count: 71}, SarosInfo{Series: 202, Member: 2, Count: 71}}, + {ephem.Semianalytic, false, SarosInfo{}, SarosInfo{Series: 202, Member: 1, Count: 70}}, + } { + t.Run(fmt.Sprintf("model%d", tc.model), func(t *testing.T) { + ephemtest.Pin(t, tc.model) + info, ok := SolarEclipseOnDate(head) + if ok != tc.hasHead || (ok && (!info.HasSaros || info.Saros != tc.head)) { + t.Fatalf("head: ok=%v hasSaros=%v %+v, want ok=%v %+v", ok, info.HasSaros, info.Saros, tc.hasHead, tc.head) + } + info, ok = SolarEclipseOnDate(next) + if !ok || !info.HasSaros || info.Saros != tc.next { + t.Fatalf("next: ok=%v hasSaros=%v %+v, want %+v", ok, info.HasSaros, info.Saros, tc.next) + } + }) + } +} diff --git a/eclipse/saros_generate_test.go b/eclipse/saros_generate_test.go index 18451a9..a548b37 100644 --- a/eclipse/saros_generate_test.go +++ b/eclipse/saros_generate_test.go @@ -11,6 +11,7 @@ import ( "time" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" ) // 默认只校验发布表的新鲜度与金标准;重新生成必须同时给出 -saros-generate 与环境变量。 @@ -41,7 +42,7 @@ type sarosTableGolden struct { span sarosSpan } -var sarosTableGoldens = []sarosTableGolden{ +var sarosTableLegacyGoldens = []sarosTableGolden{ {name: "solar/head", span: sarosSpan{Series: -47, First: -79891, Last: -61605, Member: 1, Count: 83}}, {name: "solar/202", span: sarosSpan{Series: 202, First: 15941, Last: 31551, Member: 1, Count: 71}}, {name: "solar/tail", span: sarosSpan{Series: 293, First: 48742, Last: 63906, Member: 1, Count: 69}}, @@ -51,6 +52,21 @@ var sarosTableGoldens = []sarosTableGolden{ {name: "lunar/tail", span: sarosSpan{Series: 287, First: 49211, Last: 64821, Member: 1, Count: 71}}, } +func sarosTableGoldens() []sarosTableGolden { + if ephem.Active() != ephem.Semianalytic { + return sarosTableLegacyGoldens + } + return []sarosTableGolden{ + {name: "solar/head", span: sarosSpan{Series: -47, First: -80114, Last: -61605, Member: 1, Count: 84}}, + {name: "solar/202", span: sarosSpan{Series: 202, First: 16164, Last: 31551, Member: 1, Count: 70}}, + {name: "solar/tail", span: sarosSpan{Series: 293, First: 48742, Last: 63906, Member: 1, Count: 69}}, + {name: "lunar/head", span: sarosSpan{Series: -54, First: -81564, Last: -61717, Member: 1, Count: 90}}, + {name: "lunar/4-head", span: sarosSpan{Series: 4, First: -57901, Last: -56340, Member: 1, Count: 86}}, + {name: "lunar/4-body", span: sarosSpan{Series: 4, First: -55894, Last: -38723, Member: 9, Count: 86}}, + {name: "lunar/tail", span: sarosSpan{Series: 287, First: 49211, Last: 64821, Member: 1, Count: 71}}, + } +} + // 发布表必须与文档化的窗口、生成时的序列总数和若干金标准行一致。 func TestSarosExtensionTableIsFresh(t *testing.T) { pinSarosWindowPolicy(t) @@ -64,17 +80,25 @@ func TestSarosExtensionTableIsFresh(t *testing.T) { rows int span []sarosSpan }{ - {name: "solar", phase: 0, rows: 159, span: solarSarosExtended[:]}, - {name: "lunar", phase: 1, rows: 249, span: lunarSarosExtended[:]}, + {name: "solar", phase: 0, rows: 159, span: sarosExtensionSpans(0)}, + {name: "lunar", phase: 1, rows: 249, span: sarosExtensionSpans(1)}, } { + if ephem.Active() == ephem.Semianalytic { + switch table.phase { + case 0: + table.rows = 163 + case 1: + table.rows = 252 + } + } if len(table.span) != table.rows { t.Fatalf("%s table has %d rows, golden table has %d", table.name, len(table.span), table.rows) } validateSarosExtensionSpans(t, table.name, table.phase, table.span) } - for _, golden := range sarosTableGoldens { + for _, golden := range sarosTableGoldens() { found := false - for _, span := range append(solarSarosExtended[:], lunarSarosExtended[:]...) { + for _, span := range append(append([]sarosSpan{}, sarosExtensionSpans(0)...), sarosExtensionSpans(1)...) { if span == golden.span { found = true break @@ -121,7 +145,7 @@ type sarosGenerationEvent struct { func TestGenerateSarosExtensionTables(t *testing.T) { if !sarosTableGenerationEnabled() { - t.Skipf("set %s=1 and pass -saros-generate to rewrite saros_table_extended.go", sarosGenerateEnv) + t.Skipf("set %s=1 and pass -saros-generate to rewrite the active model's Saros table", sarosGenerateEnv) } pinSarosWindowPolicy(t) startTT := timeToTTJDE(time.Date(-3000, 1, 1, 0, 0, 0, 0, time.UTC)) @@ -129,17 +153,17 @@ func TestGenerateSarosExtensionTables(t *testing.T) { var buf bytes.Buffer fmt.Fprintln(&buf, "// Code generated by go generate ./eclipse; DO NOT EDIT.") fmt.Fprintln(&buf, "\npackage eclipse") - fmt.Fprintln(&buf, "\n// UTC astronomical years -3000 through +6000, including both end years.") - fmt.Fprintln(&buf, "// Series numbers follow NASA's Saros-Inex rules. Members are computed with") - fmt.Fprintln(&buf, "// Split-K solar geometry and the union of Danjon/Chauvenet lunar detections.") - fmt.Fprintf(&buf, "const sarosExtendedStartTT = %.12f\nconst sarosExtendedEndTT = %.12f\n", startTT, endTT) + suffix := "Legacy" + if ephem.Active() == ephem.Semianalytic { + suffix = "Semianalytic" + } for phase, name := range []string{"solar", "lunar"} { events := scanSarosGenerationEvents(t, phase) assignSarosGenerationNumbers(t, events, phase) spans := buildSarosGenerationSpans(t, events, phase, startTT, endTT) // 写盘前先按发布口径校验,坏表不得进入仓库。 validateSarosExtensionSpans(t, name, phase, spans) - fmt.Fprintf(&buf, "\nvar %sSarosExtended = [...]sarosSpan{\n", name) + fmt.Fprintf(&buf, "\nvar %sSarosExtended%s = [...]sarosSpan{\n", name, suffix) for _, span := range spans { fmt.Fprintf(&buf, "{Series: %d, First: %d, Last: %d, Member: %d, Count: %d},\n", span.Series, span.First, span.Last, span.Member, span.Count) } @@ -150,10 +174,14 @@ func TestGenerateSarosExtensionTables(t *testing.T) { if err != nil { t.Fatal(err) } - if err := os.WriteFile("saros_table_extended.go", source, 0644); err != nil { + filename := "saros_table_extended_legacy.go" + if ephem.Active() == ephem.Semianalytic { + filename = "saros_table_extended_semianalytic.go" + } + if err := os.WriteFile(filename, source, 0644); err != nil { t.Fatal(err) } - t.Logf("rewrote saros_table_extended.go; rerun TestSarosExtensionTableIsFresh and update the goldens") + t.Logf("rewrote %s; rerun TestSarosExtensionTableIsFresh and update the goldens", filename) } func scanSarosGenerationEvents(t *testing.T, phase int) []sarosGenerationEvent { diff --git a/eclipse/saros_table_extended.go b/eclipse/saros_table_extended_legacy.go similarity index 98% rename from eclipse/saros_table_extended.go rename to eclipse/saros_table_extended_legacy.go index f835107..b5cea3f 100644 --- a/eclipse/saros_table_extended.go +++ b/eclipse/saros_table_extended_legacy.go @@ -2,13 +2,7 @@ package eclipse -// UTC astronomical years -3000 through +6000, including both end years. -// Series numbers follow NASA's Saros-Inex rules. Members are computed with -// Split-K solar geometry and the union of Danjon/Chauvenet lunar detections. -const sarosExtendedStartTT = 625308.343824885320 -const sarosExtendedEndTT = 3912880.500800740905 - -var solarSarosExtended = [...]sarosSpan{ +var solarSarosExtendedLegacy = [...]sarosSpan{ {Series: -47, First: -79891, Last: -61605, Member: 1, Count: 83}, {Series: -46, First: -80871, Last: -61693, Member: 1, Count: 87}, {Series: -45, First: -79844, Last: -60443, Member: 1, Count: 88}, @@ -170,7 +164,7 @@ var solarSarosExtended = [...]sarosSpan{ {Series: 293, First: 48742, Last: 63906, Member: 1, Count: 69}, } -var lunarSarosExtended = [...]sarosSpan{ +var lunarSarosExtendedLegacy = [...]sarosSpan{ {Series: -54, First: -81564, Last: -61717, Member: 1, Count: 90}, {Series: -53, First: -80091, Last: -60913, Member: 1, Count: 87}, {Series: -52, First: -80179, Last: -60778, Member: 1, Count: 88}, diff --git a/eclipse/saros_table_extended_semianalytic.go b/eclipse/saros_table_extended_semianalytic.go new file mode 100644 index 0000000..82a22ff --- /dev/null +++ b/eclipse/saros_table_extended_semianalytic.go @@ -0,0 +1,424 @@ +// Code generated by go generate ./eclipse; DO NOT EDIT. + +package eclipse + +var solarSarosExtendedSemianalytic = [...]sarosSpan{ + {Series: -47, First: -80114, Last: -61605, Member: 1, Count: 84}, + {Series: -46, First: -80871, Last: -61693, Member: 1, Count: 87}, + {Series: -45, First: -79844, Last: -60443, Member: 1, Count: 88}, + {Series: -44, First: -79263, Last: -60308, Member: 1, Count: 86}, + {Series: -42, First: -78324, Last: -59146, Member: 1, Count: 87}, + {Series: -41, First: -77743, Last: -61241, Member: 1, Count: 75}, + {Series: -40, First: -77608, Last: -61329, Member: 1, Count: 74}, + {Series: -39, First: -76581, Last: -60525, Member: 1, Count: 73}, + {Series: -38, First: -76000, Last: -59944, Member: 1, Count: 73}, + {Series: -37, First: -75642, Last: -59809, Member: 1, Count: 72}, + {Series: -36, First: -74838, Last: -58782, Member: 1, Count: 73}, + {Series: -35, First: -74257, Last: -58201, Member: 1, Count: 73}, + {Series: -34, First: -73899, Last: -58066, Member: 1, Count: 72}, + {Series: -33, First: -73095, Last: -57039, Member: 1, Count: 73}, + {Series: -32, First: -72514, Last: -56458, Member: 1, Count: 73}, + {Series: -31, First: -72379, Last: -56323, Member: 1, Count: 73}, + {Series: -30, First: -71352, Last: -55296, Member: 1, Count: 73}, + {Series: -29, First: -70994, Last: -54715, Member: 1, Count: 74}, + {Series: -28, First: -73535, Last: -54580, Member: 1, Count: 86}, + {Series: -27, First: -72954, Last: -53553, Member: 1, Count: 88}, + {Series: -26, First: -71927, Last: -52972, Member: 1, Count: 86}, + {Series: -25, First: -72238, Last: -53283, Member: 1, Count: 86}, + {Series: -24, First: -71657, Last: -52256, Member: 1, Count: 88}, + {Series: -23, First: -70630, Last: -51675, Member: 1, Count: 86}, + {Series: -22, First: -70495, Last: -54216, Member: 1, Count: 74}, + {Series: -21, First: -69914, Last: -53858, Member: 1, Count: 73}, + {Series: -20, First: -68887, Last: -52831, Member: 1, Count: 73}, + {Series: -19, First: -68752, Last: -52696, Member: 1, Count: 73}, + {Series: -18, First: -68171, Last: -52115, Member: 1, Count: 73}, + {Series: -17, First: -67144, Last: -51311, Member: 1, Count: 72}, + {Series: -16, First: -67009, Last: -50953, Member: 1, Count: 73}, + {Series: -15, First: -66428, Last: -50372, Member: 1, Count: 73}, + {Series: -14, First: -65401, Last: -49568, Member: 1, Count: 72}, + {Series: -13, First: -65266, Last: -49210, Member: 1, Count: 73}, + {Series: -12, First: -64685, Last: -48629, Member: 1, Count: 73}, + {Series: -11, First: -63658, Last: -47825, Member: 1, Count: 72}, + {Series: -10, First: -63969, Last: -47690, Member: 1, Count: 74}, + {Series: -9, First: -65618, Last: -65395, Member: 1, Count: 77}, + {Series: -9, First: -63388, Last: -46886, Member: 3, Count: 77}, + {Series: -8, First: -62361, Last: -46082, Member: 1, Count: 74}, + {Series: -7, First: -64902, Last: -45947, Member: 1, Count: 86}, + {Series: -6, First: -64767, Last: -45589, Member: 1, Count: 87}, + {Series: -5, First: -63517, Last: -44562, Member: 1, Count: 86}, + {Series: -4, First: -63605, Last: -45988, Member: 1, Count: 80}, + {Series: -3, First: -63247, Last: -46968, Member: 1, Count: 74}, + {Series: -2, First: -61997, Last: -45495, Member: 1, Count: 75}, + {Series: -1, First: -61862, Last: -45583, Member: 1, Count: 74}, + {Series: 11, First: -55782, Last: -38834, Member: 1, Count: 77}, + {Series: 22, First: -51844, Last: -36011, Member: 1, Count: 72}, + {Series: 30, First: -50318, Last: -31809, Member: 1, Count: 84}, + {Series: 69, First: -33903, Last: -16509, Member: 1, Count: 79}, + {Series: 181, First: 10430, Last: 26040, Member: 1, Count: 71}, + {Series: 182, First: 8558, Last: 25952, Member: 1, Count: 79}, + {Series: 183, First: 8247, Last: 24080, Member: 1, Count: 72}, + {Series: 184, First: 9720, Last: 26668, Member: 1, Count: 77}, + {Series: 185, First: 9409, Last: 25465, Member: 1, Count: 73}, + {Series: 186, First: 9767, Last: 25154, Member: 1, Count: 70}, + {Series: 187, First: 10794, Last: 26181, Member: 1, Count: 70}, + {Series: 188, First: 10929, Last: 26539, Member: 1, Count: 71}, + {Series: 189, First: 11287, Last: 26674, Member: 1, Count: 70}, + {Series: 190, First: 12314, Last: 27701, Member: 1, Count: 70}, + {Series: 191, First: 12672, Last: 28282, Member: 1, Count: 71}, + {Series: 192, First: 13030, Last: 28417, Member: 1, Count: 70}, + {Series: 193, First: 14057, Last: 29221, Member: 1, Count: 69}, + {Series: 194, First: 14415, Last: 29802, Member: 1, Count: 70}, + {Series: 195, First: 14550, Last: 29937, Member: 1, Count: 70}, + {Series: 196, First: 15800, Last: 30964, Member: 1, Count: 69}, + {Series: 197, First: 15935, Last: 31322, Member: 1, Count: 70}, + {Series: 198, First: 15847, Last: 31457, Member: 1, Count: 71}, + {Series: 199, First: 17097, Last: 32261, Member: 1, Count: 69}, + {Series: 200, First: 16786, Last: 32842, Member: 1, Count: 73}, + {Series: 201, First: 15137, Last: 31193, Member: 1, Count: 73}, + {Series: 202, First: 16164, Last: 31551, Member: 1, Count: 70}, + {Series: 203, First: 16299, Last: 32355, Member: 1, Count: 73}, + {Series: 204, First: 16434, Last: 31821, Member: 1, Count: 70}, + {Series: 205, First: 17238, Last: 32402, Member: 1, Count: 69}, + {Series: 206, First: 17819, Last: 33206, Member: 1, Count: 70}, + {Series: 207, First: 17954, Last: 33341, Member: 1, Count: 70}, + {Series: 208, First: 18758, Last: 33922, Member: 1, Count: 69}, + {Series: 209, First: 19562, Last: 34949, Member: 1, Count: 70}, + {Series: 210, First: 19697, Last: 35084, Member: 1, Count: 70}, + {Series: 211, First: 20501, Last: 35665, Member: 1, Count: 69}, + {Series: 212, First: 21082, Last: 36469, Member: 1, Count: 70}, + {Series: 213, First: 21217, Last: 36604, Member: 1, Count: 70}, + {Series: 214, First: 22021, Last: 37185, Member: 1, Count: 69}, + {Series: 215, First: 22825, Last: 37989, Member: 1, Count: 69}, + {Series: 216, First: 22514, Last: 38124, Member: 1, Count: 71}, + {Series: 217, First: 23318, Last: 38482, Member: 1, Count: 69}, + {Series: 218, First: 23899, Last: 39509, Member: 1, Count: 71}, + {Series: 219, First: 22027, Last: 38752, Member: 1, Count: 76}, + {Series: 220, First: 22162, Last: 37549, Member: 1, Count: 70}, + {Series: 221, First: 23412, Last: 39022, Member: 1, Count: 71}, + {Series: 222, First: 23101, Last: 38711, Member: 1, Count: 71}, + {Series: 223, First: 23682, Last: 38846, Member: 1, Count: 69}, + {Series: 224, First: 24709, Last: 39873, Member: 1, Count: 69}, + {Series: 225, First: 24621, Last: 40231, Member: 1, Count: 71}, + {Series: 226, First: 25202, Last: 40366, Member: 1, Count: 69}, + {Series: 227, First: 26229, Last: 41393, Member: 1, Count: 69}, + {Series: 228, First: 26364, Last: 41751, Member: 1, Count: 70}, + {Series: 229, First: 26945, Last: 41886, Member: 1, Count: 68}, + {Series: 230, First: 27749, Last: 42913, Member: 1, Count: 69}, + {Series: 231, First: 27884, Last: 43271, Member: 1, Count: 70}, + {Series: 232, First: 28465, Last: 43406, Member: 1, Count: 68}, + {Series: 233, First: 29492, Last: 44656, Member: 1, Count: 69}, + {Series: 234, First: 29404, Last: 44791, Member: 1, Count: 70}, + {Series: 235, First: 29316, Last: 44703, Member: 1, Count: 70}, + {Series: 236, First: 30789, Last: 45953, Member: 1, Count: 69}, + {Series: 237, First: 29140, Last: 45865, Member: 1, Count: 76}, + {Series: 238, First: 28606, Last: 43993, Member: 1, Count: 70}, + {Series: 239, First: 29856, Last: 45020, Member: 1, Count: 69}, + {Series: 240, First: 29991, Last: 45378, Member: 1, Count: 70}, + {Series: 241, First: 30126, Last: 45290, Member: 1, Count: 69}, + {Series: 242, First: 31153, Last: 46094, Member: 1, Count: 68}, + {Series: 243, First: 31511, Last: 46898, Member: 1, Count: 70}, + {Series: 244, First: 31646, Last: 46810, Member: 1, Count: 69}, + {Series: 245, First: 32673, Last: 47614, Member: 1, Count: 68}, + {Series: 246, First: 33031, Last: 48418, Member: 1, Count: 70}, + {Series: 247, First: 33389, Last: 48553, Member: 1, Count: 69}, + {Series: 248, First: 34416, Last: 49357, Member: 1, Count: 68}, + {Series: 249, First: 34774, Last: 49938, Member: 1, Count: 69}, + {Series: 250, First: 34909, Last: 50073, Member: 1, Count: 69}, + {Series: 251, First: 35936, Last: 50877, Member: 1, Count: 68}, + {Series: 252, First: 36294, Last: 51458, Member: 1, Count: 69}, + {Series: 253, First: 35760, Last: 51370, Member: 1, Count: 71}, + {Series: 254, First: 37010, Last: 51728, Member: 1, Count: 67}, + {Series: 255, First: 36699, Last: 52532, Member: 1, Count: 72}, + {Series: 256, First: 35273, Last: 50883, Member: 1, Count: 71}, + {Series: 257, First: 36077, Last: 51018, Member: 1, Count: 68}, + {Series: 258, First: 36658, Last: 52045, Member: 1, Count: 70}, + {Series: 259, First: 36793, Last: 51957, Member: 1, Count: 69}, + {Series: 260, First: 37374, Last: 52315, Member: 1, Count: 68}, + {Series: 261, First: 38178, Last: 53342, Member: 1, Count: 69}, + {Series: 262, First: 38313, Last: 53477, Member: 1, Count: 69}, + {Series: 263, First: 39117, Last: 53835, Member: 1, Count: 67}, + {Series: 264, First: 39698, Last: 54862, Member: 1, Count: 69}, + {Series: 265, First: 39833, Last: 55220, Member: 1, Count: 70}, + {Series: 266, First: 40637, Last: 55578, Member: 1, Count: 68}, + {Series: 267, First: 41441, Last: 56605, Member: 1, Count: 69}, + {Series: 268, First: 41576, Last: 56740, Member: 1, Count: 69}, + {Series: 269, First: 42157, Last: 56875, Member: 1, Count: 67}, + {Series: 270, First: 42961, Last: 58125, Member: 1, Count: 69}, + {Series: 271, First: 42427, Last: 58037, Member: 1, Count: 71}, + {Series: 272, First: 41893, Last: 56611, Member: 1, Count: 67}, + {Series: 273, First: 43812, Last: 58753, Member: 1, Count: 68}, + {Series: 274, First: 42163, Last: 57773, Member: 1, Count: 71}, + {Series: 275, First: 42298, Last: 57239, Member: 1, Count: 68}, + {Series: 276, First: 43325, Last: 58266, Member: 1, Count: 68}, + {Series: 277, First: 43460, Last: 58624, Member: 1, Count: 69}, + {Series: 278, First: 43818, Last: 58759, Member: 1, Count: 68}, + {Series: 279, First: 44845, Last: 59786, Member: 1, Count: 68}, + {Series: 280, First: 44980, Last: 60144, Member: 1, Count: 69}, + {Series: 281, First: 45338, Last: 60279, Member: 1, Count: 68}, + {Series: 282, First: 46365, Last: 61306, Member: 1, Count: 68}, + {Series: 283, First: 46500, Last: 61887, Member: 1, Count: 70}, + {Series: 284, First: 46858, Last: 62022, Member: 1, Count: 69}, + {Series: 285, First: 47885, Last: 62826, Member: 1, Count: 68}, + {Series: 286, First: 48243, Last: 63407, Member: 1, Count: 69}, + {Series: 287, First: 48155, Last: 63319, Member: 1, Count: 69}, + {Series: 288, First: 49405, Last: 64123, Member: 1, Count: 67}, + {Series: 289, First: 49317, Last: 64704, Member: 1, Count: 70}, + {Series: 290, First: 47891, Last: 63278, Member: 1, Count: 70}, + {Series: 292, First: 48830, Last: 64440, Member: 1, Count: 71}, + {Series: 293, First: 48742, Last: 63906, Member: 1, Count: 69}, +} + +var lunarSarosExtendedSemianalytic = [...]sarosSpan{ + {Series: -54, First: -81564, Last: -61717, Member: 1, Count: 90}, + {Series: -53, First: -80091, Last: -60913, Member: 1, Count: 87}, + {Series: -52, First: -80179, Last: -60778, Member: 1, Count: 88}, + {Series: -51, First: -80044, Last: -60197, Member: 1, Count: 90}, + {Series: -50, First: -78794, Last: -59393, Member: 1, Count: 88}, + {Series: -49, First: -78659, Last: -61711, Member: 1, Count: 77}, + {Series: -48, First: -78301, Last: -61799, Member: 1, Count: 75}, + {Series: -47, First: -77051, Last: -60326, Member: 1, Count: 76}, + {Series: -46, First: -76916, Last: -60637, Member: 1, Count: 74}, + {Series: -45, First: -76335, Last: -60279, Member: 1, Count: 73}, + {Series: -44, First: -75308, Last: -59029, Member: 1, Count: 74}, + {Series: -43, First: -75173, Last: -58894, Member: 1, Count: 74}, + {Series: -42, First: -74592, Last: -58536, Member: 1, Count: 73}, + {Series: -41, First: -73565, Last: -57509, Member: 1, Count: 73}, + {Series: -40, First: -73430, Last: -57151, Member: 1, Count: 74}, + {Series: -39, First: -73072, Last: -56793, Member: 1, Count: 74}, + {Series: -38, First: -71822, Last: -55766, Member: 1, Count: 73}, + {Series: -37, First: -71910, Last: -55408, Member: 1, Count: 75}, + {Series: -36, First: -74228, Last: -55050, Member: 1, Count: 87}, + {Series: -35, First: -70302, Last: -54023, Member: 1, Count: 74}, + {Series: -34, First: -72843, Last: -53665, Member: 1, Count: 87}, + {Series: -33, First: -72931, Last: -53307, Member: 1, Count: 89}, + {Series: -32, First: -71681, Last: -52280, Member: 1, Count: 88}, + {Series: -31, First: -71546, Last: -52368, Member: 1, Count: 87}, + {Series: -30, First: -71411, Last: -54686, Member: 1, Count: 76}, + {Series: -29, First: -70161, Last: -50983, Member: 1, Count: 87}, + {Series: -28, First: -69803, Last: -53301, Member: 1, Count: 75}, + {Series: -27, First: -69668, Last: -53389, Member: 1, Count: 74}, + {Series: -26, First: -68418, Last: -52139, Member: 1, Count: 74}, + {Series: -25, First: -68060, Last: -51781, Member: 1, Count: 74}, + {Series: -24, First: -67702, Last: -51646, Member: 1, Count: 73}, + {Series: -23, First: -66675, Last: -50619, Member: 1, Count: 73}, + {Series: -22, First: -66317, Last: -50261, Member: 1, Count: 73}, + {Series: -21, First: -65959, Last: -49903, Member: 1, Count: 73}, + {Series: -20, First: -64932, Last: -48876, Member: 1, Count: 73}, + {Series: -19, First: -64574, Last: -48518, Member: 1, Count: 73}, + {Series: -18, First: -64662, Last: -48160, Member: 1, Count: 75}, + {Series: -17, First: -63412, Last: -47133, Member: 1, Count: 74}, + {Series: -16, First: -63500, Last: -46775, Member: 1, Count: 76}, + {Series: -15, First: -65818, Last: -46640, Member: 1, Count: 87}, + {Series: -14, First: -64791, Last: -45390, Member: 1, Count: 88}, + {Series: -13, First: -64433, Last: -45032, Member: 1, Count: 88}, + {Series: -12, First: -64298, Last: -45566, Member: 1, Count: 85}, + {Series: -11, First: -63494, Last: -43870, Member: 1, Count: 89}, + {Series: -10, First: -62913, Last: -44181, Member: 1, Count: 85}, + {Series: -9, First: -62555, Last: -46276, Member: 1, Count: 74}, + {Series: -8, First: -61751, Last: -45472, Member: 1, Count: 74}, + {Series: -7, First: -61170, Last: -44891, Member: 1, Count: 74}, + {Series: -6, First: -60812, Last: -44756, Member: 1, Count: 73}, + {Series: -5, First: -60008, Last: -43952, Member: 1, Count: 73}, + {Series: -4, First: -59427, Last: -43148, Member: 1, Count: 74}, + {Series: -3, First: -59069, Last: -43013, Member: 1, Count: 73}, + {Series: -2, First: -58265, Last: -42209, Member: 1, Count: 73}, + {Series: -1, First: -57684, Last: -41628, Member: 1, Count: 73}, + {Series: 0, First: -57549, Last: -41270, Member: 1, Count: 74}, + {Series: 2, First: -55941, Last: -39662, Member: 1, Count: 74}, + {Series: 3, First: -56921, Last: -39527, Member: 1, Count: 79}, + {Series: 4, First: -57901, Last: -56340, Member: 1, Count: 86}, + {Series: 4, First: -55894, Last: -38723, Member: 9, Count: 86}, + {Series: 5, First: -55759, Last: -38142, Member: 1, Count: 80}, + {Series: 8, First: -55800, Last: -36622, Member: 1, Count: 87}, + {Series: 9, First: -55665, Last: -38940, Member: 1, Count: 76}, + {Series: 11, First: -54057, Last: -37555, Member: 1, Count: 75}, + {Series: 20, First: -49051, Last: -32995, Member: 1, Count: 73}, + {Series: 22, First: -48781, Last: -32056, Member: 1, Count: 76}, + {Series: 23, First: -47531, Last: -31252, Member: 1, Count: 74}, + {Series: 24, First: -50072, Last: -31117, Member: 1, Count: 86}, + {Series: 25, First: -49937, Last: -30536, Member: 1, Count: 88}, + {Series: 26, First: -48687, Last: -29509, Member: 1, Count: 87}, + {Series: 27, First: -48775, Last: -29820, Member: 1, Count: 86}, + {Series: 28, First: -48417, Last: -31915, Member: 1, Count: 75}, + {Series: 29, First: -47167, Last: -28658, Member: 1, Count: 84}, + {Series: 30, First: -47032, Last: -30530, Member: 1, Count: 75}, + {Series: 34, First: -44931, Last: -28875, Member: 1, Count: 73}, + {Series: 37, First: -43188, Last: -27132, Member: 1, Count: 73}, + {Series: 40, First: -41668, Last: -25389, Member: 1, Count: 74}, + {Series: 42, First: -40729, Last: -24004, Member: 1, Count: 76}, + {Series: 43, First: -43047, Last: -23869, Member: 1, Count: 87}, + {Series: 44, First: -41351, Last: -22842, Member: 1, Count: 84}, + {Series: 45, First: -41439, Last: -22484, Member: 1, Count: 86}, + {Series: 46, First: -41527, Last: -24579, Member: 1, Count: 77}, + {Series: 47, First: -40500, Last: -21322, Member: 1, Count: 87}, + {Series: 48, First: -39919, Last: -23194, Member: 1, Count: 76}, + {Series: 51, First: -38399, Last: -22120, Member: 1, Count: 74}, + {Series: 54, First: -36656, Last: -20600, Member: 1, Count: 73}, + {Series: 59, First: -33751, Last: -17695, Member: 1, Count: 73}, + {Series: 60, First: -33393, Last: -17114, Member: 1, Count: 74}, + {Series: 61, First: -35265, Last: -17202, Member: 1, Count: 82}, + {Series: 62, First: -32677, Last: -16175, Member: 1, Count: 75}, + {Series: 63, First: -33880, Last: -15594, Member: 1, Count: 83}, + {Series: 64, First: -34637, Last: -15905, Member: 1, Count: 85}, + {Series: 67, First: -32894, Last: -16615, Member: 1, Count: 74}, + {Series: 68, First: -32313, Last: -16257, Member: 1, Count: 73}, + {Series: 69, First: -31509, Last: -15230, Member: 1, Count: 74}, + {Series: 72, First: -29766, Last: -13710, Member: 1, Count: 73}, + {Series: 79, First: -26591, Last: -10312, Member: 1, Count: 74}, + {Series: 83, First: -27166, Last: -8434, Member: 1, Count: 85}, + {Series: 85, First: -26004, Last: -9056, Member: 1, Count: 77}, + {Series: 87, First: -24619, Last: -8340, Member: 1, Count: 74}, + {Series: 88, First: -24261, Last: -8205, Member: 1, Count: 73}, + {Series: 92, First: -22160, Last: -6327, Member: 1, Count: 72}, + {Series: 93, First: -21133, Last: -5300, Member: 1, Count: 72}, + {Series: 94, First: -20998, Last: -4942, Member: 1, Count: 73}, + {Series: 95, First: -20640, Last: -4807, Member: 1, Count: 72}, + {Series: 96, First: -19390, Last: -3557, Member: 1, Count: 72}, + {Series: 97, First: -19478, Last: -3422, Member: 1, Count: 73}, + {Series: 100, First: -19742, Last: -1679, Member: 1, Count: 82}, + {Series: 101, First: -20499, Last: -1990, Member: 1, Count: 84}, + {Series: 103, First: -19114, Last: -605, Member: 1, Count: 84}, + {Series: 104, First: -18979, Last: -2700, Member: 1, Count: 74}, + {Series: 105, First: -17729, Last: -1450, Member: 1, Count: 74}, + {Series: 109, First: -15851, Last: 205, Member: 1, Count: 73}, + {Series: 117, First: -11203, Last: 4630, Member: 1, Count: 72}, + {Series: 118, First: -11068, Last: 5211, Member: 1, Count: 74}, + {Series: 119, First: -13386, Last: 4900, Member: 1, Count: 83}, + {Series: 120, First: -12582, Last: 5927, Member: 1, Count: 84}, + {Series: 121, First: -12001, Last: 6508, Member: 1, Count: 84}, + {Series: 122, First: -12089, Last: 4413, Member: 1, Count: 75}, + {Series: 123, First: -11285, Last: 4771, Member: 1, Count: 73}, + {Series: 124, First: -10481, Last: 5798, Member: 1, Count: 74}, + {Series: 126, First: -9765, Last: 6068, Member: 1, Count: 72}, + {Series: 130, First: -7218, Last: 8615, Member: 1, Count: 72}, + {Series: 134, First: -5563, Last: 10493, Member: 1, Count: 73}, + {Series: 137, First: -5827, Last: 12013, Member: 1, Count: 81}, + {Series: 138, First: -6138, Last: 12148, Member: 1, Count: 83}, + {Series: 139, First: -4442, Last: 13398, Member: 1, Count: 81}, + {Series: 140, First: -5199, Last: 12418, Member: 1, Count: 80}, + {Series: 141, First: -4841, Last: 11215, Member: 1, Count: 73}, + {Series: 142, First: -3591, Last: 12688, Member: 1, Count: 74}, + {Series: 143, First: -3679, Last: 12377, Member: 1, Count: 73}, + {Series: 147, First: -1578, Last: 14032, Member: 1, Count: 71}, + {Series: 148, First: -328, Last: 15282, Member: 1, Count: 71}, + {Series: 154, First: 2712, Last: 18545, Member: 1, Count: 72}, + {Series: 155, First: 2401, Last: 18680, Member: 1, Count: 74}, + {Series: 156, First: 529, Last: 18592, Member: 1, Count: 82}, + {Series: 157, First: 3563, Last: 19842, Member: 1, Count: 74}, + {Series: 158, First: 1914, Last: 19977, Member: 1, Count: 82}, + {Series: 166, First: 6116, Last: 21726, Member: 1, Count: 71}, + {Series: 172, First: 9379, Last: 24989, Member: 1, Count: 71}, + {Series: 173, First: 9514, Last: 25570, Member: 1, Count: 73}, + {Series: 174, First: 7642, Last: 25482, Member: 1, Count: 81}, + {Series: 175, First: 8669, Last: 26063, Member: 1, Count: 79}, + {Series: 176, First: 9027, Last: 26867, Member: 1, Count: 81}, + {Series: 177, First: 8716, Last: 24995, Member: 1, Count: 74}, + {Series: 178, First: 9297, Last: 25130, Member: 1, Count: 72}, + {Series: 180, First: 10236, Last: 26069, Member: 1, Count: 72}, + {Series: 181, First: 10817, Last: 26427, Member: 1, Count: 71}, + {Series: 182, First: 11621, Last: 27454, Member: 1, Count: 72}, + {Series: 183, First: 11979, Last: 27589, Member: 1, Count: 71}, + {Series: 184, First: 12560, Last: 27947, Member: 1, Count: 70}, + {Series: 185, First: 13364, Last: 28974, Member: 1, Count: 71}, + {Series: 186, First: 13499, Last: 29109, Member: 1, Count: 71}, + {Series: 187, First: 14303, Last: 29690, Member: 1, Count: 70}, + {Series: 188, First: 15107, Last: 30494, Member: 1, Count: 70}, + {Series: 189, First: 15019, Last: 30852, Member: 1, Count: 72}, + {Series: 190, First: 15600, Last: 31210, Member: 1, Count: 71}, + {Series: 191, First: 16627, Last: 32237, Member: 1, Count: 71}, + {Series: 192, First: 15647, Last: 32372, Member: 1, Count: 76}, + {Series: 193, First: 14667, Last: 32061, Member: 1, Count: 79}, + {Series: 194, First: 16586, Last: 33534, Member: 1, Count: 77}, + {Series: 195, First: 15606, Last: 32554, Member: 1, Count: 77}, + {Series: 196, First: 15741, Last: 31351, Member: 1, Count: 71}, + {Series: 197, First: 16991, Last: 32824, Member: 1, Count: 72}, + {Series: 198, First: 16903, Last: 32736, Member: 1, Count: 72}, + {Series: 199, First: 17261, Last: 32871, Member: 1, Count: 71}, + {Series: 200, First: 18511, Last: 33898, Member: 1, Count: 70}, + {Series: 201, First: 18646, Last: 34256, Member: 1, Count: 71}, + {Series: 202, First: 19004, Last: 34391, Member: 1, Count: 70}, + {Series: 203, First: 20031, Last: 35418, Member: 1, Count: 70}, + {Series: 204, First: 20389, Last: 35776, Member: 1, Count: 70}, + {Series: 205, First: 20524, Last: 36134, Member: 1, Count: 71}, + {Series: 206, First: 21774, Last: 37161, Member: 1, Count: 70}, + {Series: 207, First: 21909, Last: 37519, Member: 1, Count: 71}, + {Series: 208, First: 22044, Last: 37654, Member: 1, Count: 71}, + {Series: 209, First: 23294, Last: 38681, Member: 1, Count: 70}, + {Series: 210, First: 22983, Last: 39039, Member: 1, Count: 73}, + {Series: 211, First: 21111, Last: 38505, Member: 1, Count: 79}, + {Series: 212, First: 23476, Last: 39755, Member: 1, Count: 74}, + {Series: 213, First: 22496, Last: 39667, Member: 1, Count: 78}, + {Series: 214, First: 22408, Last: 38018, Member: 1, Count: 71}, + {Series: 215, First: 23435, Last: 38822, Member: 1, Count: 70}, + {Series: 216, First: 23793, Last: 39626, Member: 1, Count: 72}, + {Series: 217, First: 23928, Last: 39538, Member: 1, Count: 71}, + {Series: 218, First: 24955, Last: 40119, Member: 1, Count: 69}, + {Series: 219, First: 25313, Last: 40923, Member: 1, Count: 71}, + {Series: 220, First: 25671, Last: 41058, Member: 1, Count: 70}, + {Series: 221, First: 26475, Last: 41639, Member: 1, Count: 69}, + {Series: 222, First: 27056, Last: 42443, Member: 1, Count: 70}, + {Series: 223, First: 27191, Last: 42801, Member: 1, Count: 71}, + {Series: 224, First: 28218, Last: 43382, Member: 1, Count: 69}, + {Series: 225, First: 28576, Last: 44186, Member: 1, Count: 71}, + {Series: 226, First: 28711, Last: 44321, Member: 1, Count: 71}, + {Series: 227, First: 29515, Last: 44902, Member: 1, Count: 70}, + {Series: 228, First: 29873, Last: 45706, Member: 1, Count: 72}, + {Series: 229, First: 28224, Last: 45395, Member: 1, Count: 78}, + {Series: 230, First: 28582, Last: 44861, Member: 1, Count: 74}, + {Series: 231, First: 29386, Last: 46557, Member: 1, Count: 78}, + {Series: 232, First: 29075, Last: 44908, Member: 1, Count: 72}, + {Series: 233, First: 29656, Last: 45043, Member: 1, Count: 70}, + {Series: 234, First: 30460, Last: 46070, Member: 1, Count: 71}, + {Series: 235, First: 30595, Last: 46205, Member: 1, Count: 71}, + {Series: 236, First: 31176, Last: 46340, Member: 1, Count: 69}, + {Series: 237, First: 32203, Last: 47590, Member: 1, Count: 70}, + {Series: 238, First: 32338, Last: 47725, Member: 1, Count: 70}, + {Series: 239, First: 32919, Last: 48083, Member: 1, Count: 69}, + {Series: 240, First: 33723, Last: 49110, Member: 1, Count: 70}, + {Series: 241, First: 33858, Last: 49468, Member: 1, Count: 71}, + {Series: 242, First: 34439, Last: 49603, Member: 1, Count: 69}, + {Series: 243, First: 35243, Last: 50630, Member: 1, Count: 70}, + {Series: 244, First: 35378, Last: 50988, Member: 1, Count: 71}, + {Series: 245, First: 35513, Last: 50900, Member: 1, Count: 70}, + {Series: 246, First: 36763, Last: 52150, Member: 1, Count: 70}, + {Series: 247, First: 35337, Last: 52285, Member: 1, Count: 77}, + {Series: 248, First: 34803, Last: 50636, Member: 1, Count: 72}, + {Series: 249, First: 36276, Last: 52778, Member: 1, Count: 75}, + {Series: 250, First: 35742, Last: 51798, Member: 1, Count: 73}, + {Series: 251, First: 36100, Last: 51487, Member: 1, Count: 70}, + {Series: 252, First: 37127, Last: 52514, Member: 1, Count: 70}, + {Series: 253, First: 37262, Last: 52872, Member: 1, Count: 71}, + {Series: 254, First: 37620, Last: 53007, Member: 1, Count: 70}, + {Series: 255, First: 38647, Last: 53811, Member: 1, Count: 69}, + {Series: 256, First: 39005, Last: 54392, Member: 1, Count: 70}, + {Series: 257, First: 39140, Last: 54527, Member: 1, Count: 70}, + {Series: 258, First: 40390, Last: 55331, Member: 1, Count: 68}, + {Series: 259, First: 40525, Last: 55912, Member: 1, Count: 70}, + {Series: 260, First: 40883, Last: 56047, Member: 1, Count: 69}, + {Series: 261, First: 41910, Last: 56851, Member: 1, Count: 68}, + {Series: 262, First: 42045, Last: 57655, Member: 1, Count: 71}, + {Series: 263, First: 41734, Last: 57344, Member: 1, Count: 71}, + {Series: 264, First: 43207, Last: 58371, Member: 1, Count: 69}, + {Series: 265, First: 42673, Last: 58729, Member: 1, Count: 73}, + {Series: 266, First: 41247, Last: 57303, Member: 1, Count: 73}, + {Series: 267, First: 42274, Last: 57438, Member: 1, Count: 69}, + {Series: 268, First: 42632, Last: 58465, Member: 1, Count: 72}, + {Series: 269, First: 42544, Last: 58154, Member: 1, Count: 71}, + {Series: 270, First: 43348, Last: 58512, Member: 1, Count: 69}, + {Series: 271, First: 44152, Last: 59539, Member: 1, Count: 70}, + {Series: 272, First: 44064, Last: 59674, Member: 1, Count: 71}, + {Series: 273, First: 44868, Last: 60032, Member: 1, Count: 69}, + {Series: 274, First: 45672, Last: 61059, Member: 1, Count: 70}, + {Series: 275, First: 45807, Last: 61194, Member: 1, Count: 70}, + {Series: 276, First: 46611, Last: 61552, Member: 1, Count: 68}, + {Series: 277, First: 47192, Last: 62579, Member: 1, Count: 70}, + {Series: 278, First: 47327, Last: 62714, Member: 1, Count: 70}, + {Series: 279, First: 48131, Last: 63072, Member: 1, Count: 68}, + {Series: 280, First: 48712, Last: 64099, Member: 1, Count: 70}, + {Series: 281, First: 48401, Last: 64011, Member: 1, Count: 71}, + {Series: 282, First: 48982, Last: 63923, Member: 1, Count: 68}, + {Series: 284, First: 47914, Last: 64193, Member: 1, Count: 74}, + {Series: 285, First: 48272, Last: 63436, Member: 1, Count: 69}, + {Series: 286, First: 49299, Last: 64686, Member: 1, Count: 70}, + {Series: 287, First: 49211, Last: 64821, Member: 1, Count: 71}, +} diff --git a/eclipse/saros_table_lunar.go b/eclipse/saros_table_lunar.go index 11b7bd9..0808307 100644 --- a/eclipse/saros_table_lunar.go +++ b/eclipse/saros_table_lunar.go @@ -1,6 +1,6 @@ package eclipse -// Code generated by /tmp/generate_saros_tables.go; DO NOT EDIT. +// Code generated by a table generator; DO NOT EDIT. var lunarSarosAnchors = [...]sarosMagic{ 0x21ae3749, diff --git a/eclipse/saros_table_solar.go b/eclipse/saros_table_solar.go index 0efeace..a3cbed8 100644 --- a/eclipse/saros_table_solar.go +++ b/eclipse/saros_table_solar.go @@ -1,6 +1,6 @@ package eclipse -// Code generated by /tmp/generate_saros_tables.go; DO NOT EDIT. +// Code generated by a table generator; DO NOT EDIT. var solarSarosAnchors = [...]sarosMagic{ 0x202d5bc8, diff --git a/eclipse/saros_test.go b/eclipse/saros_test.go index c71b5b0..1682258 100644 --- a/eclipse/saros_test.go +++ b/eclipse/saros_test.go @@ -33,7 +33,7 @@ func TestSolarSarosInfoDerivedOutsideCatalogRange(t *testing.T) { date time.Time }{ {name: "before catalog", date: time.Date(-3200, time.January, 1, 12, 0, 0, 0, time.UTC)}, - {name: "after catalog", date: time.Date(3288, time.November, 15, 12, 0, 0, 0, time.UTC)}, + {name: "after extension", date: time.Date(6500, time.November, 15, 12, 0, 0, 0, time.UTC)}, } { t.Run(tc.name, func(t *testing.T) { info := ClosestSolarEclipse(tc.date) diff --git a/eclipse/search_skip.go b/eclipse/search_skip.go index a89e127..94beb33 100644 --- a/eclipse/search_skip.go +++ b/eclipse/search_skip.go @@ -11,9 +11,14 @@ const ( eclipseSeasonMaxSearchStep = 4 ) -func nextEclipseSearchCandidateTT(candidateTT float64, phaseType, direction int, synodicMonthDays float64) float64 { +func nextEclipseSearchCandidateTT(candidateTT float64, phaseType, direction int, synodicMonthDays float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + step := eclipseSearchStep(candidateTT, direction, synodicMonthDays) - return basic.CalcMoonSHByJDE(candidateTT+float64(direction*step)*synodicMonthDays, phaseType) + return basic.CalcMoonSHByJDEWithInstance(candidateTT+float64(direction*step)*synodicMonthDays, phaseType, module.inst) } func eclipseSearchStep(candidateTT float64, direction int, synodicMonthDays float64) int { diff --git a/eclipse/solar.go b/eclipse/solar.go index 82e2da2..1a9ed7a 100644 --- a/eclipse/solar.go +++ b/eclipse/solar.go @@ -42,8 +42,16 @@ type SolarEclipseOptions struct { RadiusModel SolarEclipseRadiusModel // SunRadiusModel 太阳半径口径,零值为标准档, solar radius convention. SunRadiusModel SolarEclipseSunRadiusModel + // LunarLimb 月缘剖面,nil 沿用模块设置;修正站心接触、食分与遮蔽率,以及全球始末与食甚点中心食时长 / lunar limb profile for local contacts and values, global P1/P4 and central duration. + LunarLimb LunarLimb } +// LunarLimb 月缘剖面接口,内置实现见 b612.me/astro/moon/limb, lunar limb profile interface. +type LunarLimb = basic.LunarLimb + +// SolarEclipseLimbContact 月缘修正后的全球偏食始末接触点位置 / limb-corrected global contact location. +type SolarEclipseLimbContact = basic.SolarEclipseLimbContact + // SolarEclipseType 全局日食食型, global solar eclipse type. type SolarEclipseType string @@ -85,6 +93,11 @@ type SolarEclipseInfo struct { Type SolarEclipseType // Centrality 中心性, eclipse centrality. Centrality SolarEclipseCentrality + // LunarLimbApplied 全球偏食始末与食甚点中心食时长已按月缘剖面修正;剖面失败时为 false 且结果保持圆月面 / contacts refined with a lunar limb profile. + LunarLimbApplied bool + // PartialBeginContact / PartialEndContact 月缘修正后的偏食始末接触点位置,未修正时 Defined 为 false / limb-corrected contact locations. + PartialBeginContact SolarEclipseLimbContact + PartialEndContact SolarEclipseLimbContact // HasSaros 存在沙罗序列信息(可能是锚点外推结果), has Saros series metadata (possibly extrapolated). HasSaros bool // Saros 是沙罗序列信息,包括系列号、系列内序号和总成员数。 @@ -148,48 +161,54 @@ type SolarEclipseInfo struct { } // SolarEclipseOnDate 当地自然日全局日食查询 / local-date global solar eclipse query. -// Determine whether a global solar eclipse overlaps the local date, using NASA bulletin Split-K by default. -func SolarEclipseOnDate(date time.Time) (SolarEclipseInfo, bool) { - return SolarEclipseOnDateNASABulletinSplitK(date) +func (module *Eclipse) SolarEclipseOnDate(date time.Time) (SolarEclipseInfo, bool) { + return module.SolarEclipseOnDateNASABulletinSplitK(date) } // SolarEclipseOnDateNASABulletinSplitK 当地自然日全局日食查询(NASA bulletin Split-K) / local-date global solar eclipse query with NASA bulletin Split-K. -// Determine whether a global solar eclipse overlaps the local date with the NASA bulletin Split-K model. -func SolarEclipseOnDateNASABulletinSplitK(date time.Time) (SolarEclipseInfo, bool) { - return solarEclipseOnDate(date, basic.SolarEclipseNASABulletinSplitK) +func (module *Eclipse) SolarEclipseOnDateNASABulletinSplitK(date time.Time) (SolarEclipseInfo, bool) { + return solarEclipseOnDate(date, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelNASABulletinSplitK) + }, module) } // SolarEclipseOnDateIAUSingleK 当地自然日全局日食查询(IAU Single-K) / local-date global solar eclipse query with IAU Single-K. -// Determine whether a global solar eclipse overlaps the local date with the IAU Single-K model. -func SolarEclipseOnDateIAUSingleK(date time.Time) (SolarEclipseInfo, bool) { - return solarEclipseOnDate(date, basic.SolarEclipseIAUSingleK) +func (module *Eclipse) SolarEclipseOnDateIAUSingleK(date time.Time) (SolarEclipseInfo, bool) { + return solarEclipseOnDate(date, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelIAUSingleK) + }, module) } // SolarEclipseOnDateWithOptions 当地自然日全局日食查询(自定义半径口径) / local-date global solar eclipse query with custom radius conventions. -func SolarEclipseOnDateWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseInfo, bool) { - return solarEclipseOnDate(date, solarEclipseCalculatorFor(options)) +func (module *Eclipse) SolarEclipseOnDateWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseInfo, bool) { + return solarEclipseOnDate(date, solarEclipseCalculatorFor(options, module), module) } // LastSolarEclipseWithOptions 上次日食(自定义半径口径) / previous solar eclipse with custom radius conventions. -func LastSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, -1, true, solarEclipseCalculatorFor(options)) +func (module *Eclipse) LastSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, -1, true, solarEclipseCalculatorFor(options, module), module) return info } // NextSolarEclipseWithOptions 下次日食(自定义半径口径) / next solar eclipse with custom radius conventions. -func NextSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, 1, false, solarEclipseCalculatorFor(options)) +func (module *Eclipse) NextSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, 1, false, solarEclipseCalculatorFor(options, module), module) return info } // ClosestSolarEclipseWithOptions 最近一次日食(自定义半径口径) / closest solar eclipse with custom radius conventions. -func ClosestSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { - last, hasLast := searchSolarEclipse(date, -1, true, solarEclipseCalculatorFor(options)) - next, hasNext := searchSolarEclipse(date, 1, false, solarEclipseCalculatorFor(options)) +func (module *Eclipse) ClosestSolarEclipseWithOptions(date time.Time, options SolarEclipseOptions) SolarEclipseInfo { + last, hasLast := searchSolarEclipse(date, -1, true, solarEclipseCalculatorFor(options, module), module) + next, hasNext := searchSolarEclipse(date, 1, false, solarEclipseCalculatorFor(options, module), module) return closestSolarEclipse(date, last, hasLast, next, hasNext) } -func solarEclipseCalculatorFor(options SolarEclipseOptions) solarEclipseCalculator { +func solarEclipseCalculatorFor(options SolarEclipseOptions, modules ...*Eclipse) solarEclipseCalculator { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + basicOptions := basic.SolarEclipseOptions{ RadiusModel: basic.SolarEclipseRadiusModel(options.RadiusModel), SunRadiusModel: basic.SolarEclipseSunRadiusModel(options.SunRadiusModel), @@ -197,22 +216,43 @@ func solarEclipseCalculatorFor(options SolarEclipseOptions) solarEclipseCalculat if basicOptions.RadiusModel != basic.SolarEclipseModelIAUSingleK { basicOptions.RadiusModel = basic.SolarEclipseModelNASABulletinSplitK } + basicOptions.LunarLimb = options.LunarLimb + if basicOptions.LunarLimb == nil { + basicOptions.LunarLimb = module.lunarLimb + } return func(seed float64) basic.SolarEclipseResult { - return basic.SolarEclipseWithOptions(seed, basicOptions) + return basic.SolarEclipseWithOptionsWithInstance(seed, basicOptions, module.inst) } } -func solarEclipseOnDate(date time.Time, calculator solarEclipseCalculator) (SolarEclipseInfo, bool) { +// solarResult 按模块星历与月缘计算一次全球日食;未设置月缘时与包级计算相同。 +func (module *Eclipse) solarResult(seedJDE float64, model basic.SolarEclipseRadiusModel) basic.SolarEclipseResult { + if module.lunarLimb == nil { + if model == basic.SolarEclipseModelIAUSingleK { + return basic.SolarEclipseIAUSingleKWithInstance(seedJDE, module.inst) + } + return basic.SolarEclipseNASABulletinSplitKWithInstance(seedJDE, module.inst) + } + return basic.SolarEclipseWithOptionsWithInstance(seedJDE, + basic.SolarEclipseOptions{RadiusModel: model, LunarLimb: module.lunarLimb}, module.inst) +} + +func solarEclipseOnDate(date time.Time, calculator solarEclipseCalculator, modules ...*Eclipse) (SolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + location := date.Location() dayStart, dayMid, dayEnd := solarEclipseLocalDayBounds(date) - candidateTT := basic.CalcMoonSHByJDE(solarEclipseTimeToTTJDE(dayMid), 0) + candidateTT := basic.CalcMoonSHByJDEWithInstance(solarEclipseTimeToTTJDE(dayMid), 0, module.inst) result := calculator(candidateTT) if result.Type == basic.SolarEclipseNone { return SolarEclipseInfo{}, false } - info := solarEclipseInfoFromBasic(result, location) + info := solarEclipseInfoFromBasic(result, location, module) if !solarEclipseOverlapsDate(info, dayStart, dayEnd) { return SolarEclipseInfo{}, false } @@ -220,64 +260,71 @@ func solarEclipseOnDate(date time.Time, calculator solarEclipseCalculator) (Sola } // LastSolarEclipse 上次日食 / previous solar eclipse. -// Previous solar eclipse, using NASA bulletin Split-K by default. -func LastSolarEclipse(date time.Time) SolarEclipseInfo { - return LastSolarEclipseNASABulletinSplitK(date) +func (module *Eclipse) LastSolarEclipse(date time.Time) SolarEclipseInfo { + return module.LastSolarEclipseNASABulletinSplitK(date) } // LastSolarEclipseNASABulletinSplitK 上次日食(NASA bulletin Split-K) / previous solar eclipse with NASA bulletin Split-K. -// Previous solar eclipse with the NASA bulletin Split-K model. -func LastSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, -1, true, basic.SolarEclipseNASABulletinSplitK) +func (module *Eclipse) LastSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, -1, true, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelNASABulletinSplitK) + }, module) return info } // LastSolarEclipseIAUSingleK 上次日食(IAU Single-K) / previous solar eclipse with IAU Single-K. -// Previous solar eclipse with the IAU Single-K model. -func LastSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, -1, true, basic.SolarEclipseIAUSingleK) +func (module *Eclipse) LastSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, -1, true, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelIAUSingleK) + }, module) return info } // NextSolarEclipse 下次日食 / next solar eclipse. -// Next solar eclipse, using NASA bulletin Split-K by default. -func NextSolarEclipse(date time.Time) SolarEclipseInfo { - return NextSolarEclipseNASABulletinSplitK(date) +func (module *Eclipse) NextSolarEclipse(date time.Time) SolarEclipseInfo { + return module.NextSolarEclipseNASABulletinSplitK(date) } // NextSolarEclipseNASABulletinSplitK 下次日食(NASA bulletin Split-K) / next solar eclipse with NASA bulletin Split-K. -// Next solar eclipse with the NASA bulletin Split-K model. -func NextSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, 1, false, basic.SolarEclipseNASABulletinSplitK) +func (module *Eclipse) NextSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, 1, false, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelNASABulletinSplitK) + }, module) return info } // NextSolarEclipseIAUSingleK 下次日食(IAU Single-K) / next solar eclipse with IAU Single-K. -// Next solar eclipse with the IAU Single-K model. -func NextSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { - info, _ := searchSolarEclipse(date, 1, false, basic.SolarEclipseIAUSingleK) +func (module *Eclipse) NextSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + info, _ := searchSolarEclipse(date, 1, false, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelIAUSingleK) + }, module) return info } // ClosestSolarEclipse 最近一次日食 / closest solar eclipse. -// Closest solar eclipse, using NASA bulletin Split-K by default. -func ClosestSolarEclipse(date time.Time) SolarEclipseInfo { - return ClosestSolarEclipseNASABulletinSplitK(date) +func (module *Eclipse) ClosestSolarEclipse(date time.Time) SolarEclipseInfo { + return module.ClosestSolarEclipseNASABulletinSplitK(date) } // ClosestSolarEclipseNASABulletinSplitK 最近一次日食(NASA bulletin Split-K) / closest solar eclipse with NASA bulletin Split-K. -// Closest solar eclipse with the NASA bulletin Split-K model. -func ClosestSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { - last, hasLast := searchSolarEclipse(date, -1, true, basic.SolarEclipseNASABulletinSplitK) - next, hasNext := searchSolarEclipse(date, 1, false, basic.SolarEclipseNASABulletinSplitK) +func (module *Eclipse) ClosestSolarEclipseNASABulletinSplitK(date time.Time) SolarEclipseInfo { + last, hasLast := searchSolarEclipse(date, -1, true, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelNASABulletinSplitK) + }, module) + next, hasNext := searchSolarEclipse(date, 1, false, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelNASABulletinSplitK) + }, module) return closestSolarEclipse(date, last, hasLast, next, hasNext) } // ClosestSolarEclipseIAUSingleK 最近一次日食(IAU Single-K) / closest solar eclipse with IAU Single-K. -// Closest solar eclipse with the IAU Single-K model. -func ClosestSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { - last, hasLast := searchSolarEclipse(date, -1, true, basic.SolarEclipseIAUSingleK) - next, hasNext := searchSolarEclipse(date, 1, false, basic.SolarEclipseIAUSingleK) +func (module *Eclipse) ClosestSolarEclipseIAUSingleK(date time.Time) SolarEclipseInfo { + last, hasLast := searchSolarEclipse(date, -1, true, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelIAUSingleK) + }, module) + next, hasNext := searchSolarEclipse(date, 1, false, func(seedJDE float64) basic.SolarEclipseResult { + return module.solarResult(seedJDE, basic.SolarEclipseModelIAUSingleK) + }, module) return closestSolarEclipse(date, last, hasLast, next, hasNext) } @@ -309,26 +356,34 @@ func searchSolarEclipse( date time.Time, direction int, includeCurrent bool, - calculator solarEclipseCalculator, -) (SolarEclipseInfo, bool) { + calculator solarEclipseCalculator, modules ...*Eclipse) (SolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + targetTT := solarEclipseTimeToTTJDE(date) - candidateTT := basic.CalcMoonSHByJDE(targetTT, 0) + candidateTT := basic.CalcMoonSHByJDEWithInstance(targetTT, 0, module.inst) for i := 0; i < solarEclipseSearchLimit; i++ { - if isPotentialSolarEclipse(candidateTT) { + if module.isPotentialSolarEclipse(candidateTT) { result := calculator(candidateTT) if result.Type != basic.SolarEclipseNone && solarEclipseMatchesDirection(result.GreatestEclipse, targetTT, direction, includeCurrent) { - return solarEclipseInfoFromBasic(result, date.Location()), true + return solarEclipseInfoFromBasic(result, date.Location(), module), true } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, direction, solarEclipseSynodicMonthDays) + candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, direction, solarEclipseSynodicMonthDays, module) } return SolarEclipseInfo{}, false } func isPotentialSolarEclipse(newMoonTT float64) bool { - return math.Abs(basic.HMoonTrueBo(newMoonTT)) <= solarEclipseLatitudeLimitDeg + return defaultModule.isPotentialSolarEclipse(newMoonTT) +} + +func (module *Eclipse) isPotentialSolarEclipse(newMoonTT float64) bool { + return math.Abs(basic.HMoonTrueBoWithInstance(newMoonTT, module.inst)) <= solarEclipseLatitudeLimitDeg } func solarEclipseMatchesDirection(greatestTT, targetTT float64, direction int, includeCurrent bool) bool { @@ -355,13 +410,21 @@ func nextSolarEclipseLocalDayStart(dayStart time.Time) time.Time { return time.Date(dayStart.Year(), dayStart.Month(), dayStart.Day()+1, 0, 0, 0, 0, location) } -func solarEclipseInfoFromBasic(result basic.SolarEclipseResult, location *time.Location) SolarEclipseInfo { - saros, hasSaros := solarSarosInfo(result.GreatestEclipse) +func solarEclipseInfoFromBasic(result basic.SolarEclipseResult, location *time.Location, modules ...*Eclipse) SolarEclipseInfo { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + saros, hasSaros := solarSarosInfo(result.GreatestEclipse, module) return SolarEclipseInfo{ Model: mapBasicSolarEclipseModel(result.Model), SunRadiusModel: result.SunRadiusModel, Type: mapBasicSolarEclipseType(result.Type), Centrality: mapBasicSolarEclipseCentrality(result.Centrality), + LunarLimbApplied: result.LunarLimbApplied, + PartialBeginContact: result.PartialBeginContact, + PartialEndContact: result.PartialEndContact, HasSaros: hasSaros, Saros: saros, GreatestEclipse: solarEclipseTTJDEToTime(result.GreatestEclipse, location), diff --git a/eclipse/solar_bessel.go b/eclipse/solar_bessel.go index ccf20ee..729adb3 100644 --- a/eclipse/solar_bessel.go +++ b/eclipse/solar_bessel.go @@ -1,6 +1,8 @@ package eclipse -import "b612.me/astro/basic" +import ( + "b612.me/astro/basic" +) // SolarEclipseBesselianPolynomial 是三次多项式系数,索引 n 对应 t 的 n 次幂,t 为自 T0 起算的 TT 小时数。 // SolarEclipseBesselianPolynomial holds the cubic coefficients; index n multiplies t^n with t in TT hours from T0. @@ -53,18 +55,17 @@ type SolarEclipseBesselianElementsResult struct { DeltaTSeconds float64 } -// SolarEclipseBesselianElements 计算给定近朔时刻附近一次日食的多项式贝塞尔根数,窗口内无日食时返回 false。 -// Polynomial Besselian elements for the solar eclipse near the given new-moon instant. -func SolarEclipseBesselianElements( +// SolarEclipseBesselianElements 计算给定近朔时刻附近一次日食的多项式贝塞尔根数,窗口内无日食时返回 false / polynomial Besselian elements near a new moon; false when no eclipse. +func (module *Eclipse) SolarEclipseBesselianElements( seedJDE float64, options SolarEclipseBesselianElementsOptions, ) (SolarEclipseBesselianElementsResult, bool) { - result, ok := basic.SolarEclipseBesselianElements(seedJDE, basic.SolarEclipseBesselianElementsOptions{ + result, ok := basic.SolarEclipseBesselianElementsWithInstance(seedJDE, basic.SolarEclipseBesselianElementsOptions{ Model: basic.SolarEclipseRadiusModel(options.Model), SunRadiusModel: basic.SolarEclipseSunRadiusModel(options.SunRadiusModel), DeltaTSeconds: options.DeltaTSeconds, ReferenceJDE: options.ReferenceJDE, ValidHours: options.ValidHours, - }) + }, module.inst) if !ok { return SolarEclipseBesselianElementsResult{}, false } diff --git a/eclipse/solar_candidates.go b/eclipse/solar_candidates.go index 927ce5f..30951d5 100644 --- a/eclipse/solar_candidates.go +++ b/eclipse/solar_candidates.go @@ -46,23 +46,23 @@ func solarEclipseCandidateIsCentral(value basic.SolarEclipseType) bool { } // SolarEclipseCandidates 枚举区间内的日食时刻表,按时间升序 / eclipses within a range, in time order. -func SolarEclipseCandidates( +func (module *Eclipse) SolarEclipseCandidates( start, end time.Time, options SolarEclipseCandidateOptions, ) []SolarEclipseCandidate { if !end.After(start) { return nil } - calculator := localSolarEclipseCalculatorForModel(options.Model) + calculator := localSolarEclipseCalculatorForModel(options.Model, module) startTT := solarEclipseTimeToTTJDE(start) endTT := solarEclipseTimeToTTJDE(end) candidates := make([]SolarEclipseCandidate, 0, 8) - candidateTT := basic.CalcMoonSHByJDE(startTT, 0) + candidateTT := basic.CalcMoonSHByJDEWithInstance(startTT, 0, module.inst) for iteration := 0; iteration < localSolarEclipseSearchIterationCap; iteration++ { // 食甚可能略早于朔,上界要多留一点余量,否则窗口末尾那场会被漏掉。 if candidateTT > endTT+solarEclipseCandidateGreatestLeadDays { break } - if isPotentialLocalSolarEclipse(candidateTT) { + if isPotentialLocalSolarEclipse(candidateTT, module) { globalResult := calculator.global(candidateTT) if globalResult.Type != basic.SolarEclipseNone && (!options.CentralOnly || solarEclipseCandidateIsCentral(globalResult.Type)) { @@ -78,13 +78,13 @@ func SolarEclipseCandidates( Gamma: globalResult.Gamma, } if options.IncludeSaros { - candidate.Saros, candidate.HasSaros = solarSarosInfo(globalResult.GreatestEclipse) + candidate.Saros, candidate.HasSaros = solarSarosInfo(globalResult.GreatestEclipse, module) } candidates = append(candidates, candidate) } } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, 1, localSolarEclipseSynodicMonthDays) + candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, 1, localSolarEclipseSynodicMonthDays, module) } return candidates } diff --git a/eclipse/solar_local.go b/eclipse/solar_local.go index 6102b47..31214df 100644 --- a/eclipse/solar_local.go +++ b/eclipse/solar_local.go @@ -16,6 +16,8 @@ const ( type localSolarEclipseCalculator struct { global func(float64) basic.SolarEclipseResult local func(float64, float64, float64, float64) basic.LocalSolarEclipseResult + // limb 是 local 实际使用的月缘剖面,接触点图须与之一致。 + limb basic.LunarLimb } type localSolarEclipseQueryMode int @@ -56,6 +58,8 @@ type LocalSolarEclipseInfo struct { SunRadiusModel SolarEclipseSunRadiusModel // Type 站心食型, local eclipse type. Type SolarEclipseType + // LunarLimbApplied 接触时刻已按月缘剖面修正;剖面失败时为 false 且结果保持圆月面 / contacts refined with a lunar limb profile. + LunarLimbApplied bool // HasSaros 存在沙罗序列信息(可能是锚点外推结果), has Saros series metadata (possibly extrapolated). HasSaros bool // Saros 是沙罗序列信息,包括系列号、系列内序号和总成员数。 @@ -115,47 +119,46 @@ type LocalSolarEclipseInfo struct { } // LocalSolarEclipseOnDate 当地站心日食查询 / local topocentric solar eclipse query. -// Determine whether a visible local solar eclipse overlaps the local date, using NASA bulletin Split-K by default. -func LocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return LocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) LocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return module.LocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) } // LocalSolarEclipseOnDateNASABulletinSplitK 当地站心日食查询(NASA bulletin Split-K) / local topocentric solar eclipse query with NASA bulletin Split-K. -// Determine whether a visible local solar eclipse overlaps the local date with the NASA bulletin Split-K model. -func LocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) LocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) } // LocalSolarEclipseOnDateIAUSingleK 当地站心日食查询(IAU Single-K) / local topocentric solar eclipse query with IAU Single-K. -// Determine whether a visible local solar eclipse overlaps the local date with the IAU Single-K model. -func LocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseIAUSingleK, localSolarEclipseQueryVisible) +func (module *Eclipse) LocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryVisible, module) } // GeometricLocalSolarEclipseOnDate 当地站心几何日食查询 / local geometric solar eclipse query. -// Determine whether a geometric local solar eclipse overlaps the local date, using NASA bulletin Split-K by default. -func GeometricLocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) GeometricLocalSolarEclipseOnDate(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return module.GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date, lon, lat, height) } // GeometricLocalSolarEclipseOnDateNASABulletinSplitK 当地站心几何日食查询(NASA bulletin Split-K) / local geometric solar eclipse query with NASA bulletin Split-K. -// Determine whether a geometric local solar eclipse overlaps the local date with the NASA bulletin Split-K model. -func GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryGeometric) +func (module *Eclipse) GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryGeometric, module) } // GeometricLocalSolarEclipseOnDateIAUSingleK 当地站心几何日食查询(IAU Single-K) / local geometric solar eclipse query with IAU Single-K. -// Determine whether a geometric local solar eclipse overlaps the local date with the IAU Single-K model. -func GeometricLocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseIAUSingleK, localSolarEclipseQueryGeometric) +func (module *Eclipse) GeometricLocalSolarEclipseOnDateIAUSingleK(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryGeometric, module) } // LocalSolarEclipseOnDateWithOptions 当地站心日食查询(自定义半径口径) / local topocentric solar eclipse query with custom radius conventions. -func LocalSolarEclipseOnDateWithOptions(date time.Time, lon, lat, height float64, options SolarEclipseOptions) (LocalSolarEclipseInfo, bool) { - return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorFor(options), localSolarEclipseQueryVisible) +func (module *Eclipse) LocalSolarEclipseOnDateWithOptions(date time.Time, lon, lat, height float64, options SolarEclipseOptions) (LocalSolarEclipseInfo, bool) { + return localSolarEclipseOnDate(date, lon, lat, height, localSolarEclipseCalculatorFor(options, module), localSolarEclipseQueryVisible, module) } -func localSolarEclipseCalculatorFor(options SolarEclipseOptions) localSolarEclipseCalculator { +func localSolarEclipseCalculatorFor(options SolarEclipseOptions, modules ...*Eclipse) localSolarEclipseCalculator { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + basicOptions := basic.SolarEclipseOptions{ RadiusModel: basic.SolarEclipseRadiusModel(options.RadiusModel), SunRadiusModel: basic.SolarEclipseSunRadiusModel(options.SunRadiusModel), @@ -163,12 +166,19 @@ func localSolarEclipseCalculatorFor(options SolarEclipseOptions) localSolarEclip if basicOptions.RadiusModel != basic.SolarEclipseModelIAUSingleK { basicOptions.RadiusModel = basic.SolarEclipseModelNASABulletinSplitK } + // 全局食况(是否有食、食型)仍按圆月面判定,月缘只修正站心接触。 + localOptions := basicOptions + localOptions.LunarLimb = options.LunarLimb + if localOptions.LunarLimb == nil { + localOptions.LunarLimb = module.lunarLimb + } return localSolarEclipseCalculator{ + limb: localOptions.LunarLimb, global: func(seed float64) basic.SolarEclipseResult { - return basic.SolarEclipseWithOptions(seed, basicOptions) + return basic.SolarEclipseWithOptionsWithInstance(seed, basicOptions, module.inst) }, local: func(seed, lon, lat, height float64) basic.LocalSolarEclipseResult { - return basic.LocalSolarEclipseWithOptions(seed, lon, lat, height, basicOptions) + return basic.LocalSolarEclipseWithOptionsWithInstance(seed, lon, lat, height, localOptions, module.inst) }, } } @@ -177,13 +187,17 @@ func localSolarEclipseOnDate( date time.Time, lon, lat, height float64, calculator localSolarEclipseCalculator, - mode localSolarEclipseQueryMode, -) (LocalSolarEclipseInfo, bool) { + mode localSolarEclipseQueryMode, modules ...*Eclipse) (LocalSolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + location := date.Location() dayStart, dayMid, dayEnd := solarEclipseLocalDayBounds(date) - candidateTT := basic.CalcMoonSHByJDE(solarEclipseTimeToTTJDE(dayMid), 0) - if !isPotentialLocalSolarEclipse(candidateTT) { + candidateTT := basic.CalcMoonSHByJDEWithInstance(solarEclipseTimeToTTJDE(dayMid), 0, module.inst) + if !isPotentialLocalSolarEclipse(candidateTT, module) { return LocalSolarEclipseInfo{}, false } @@ -197,177 +211,153 @@ func localSolarEclipseOnDate( return LocalSolarEclipseInfo{}, false } - info := localSolarEclipseInfoFromBasic(result, lon, lat, height, location) + info := localSolarEclipseInfoFromBasic(result, lon, lat, height, location, calculator.limb, module) if !localSolarEclipseOverlapsDate(info, dayStart, dayEnd) { return LocalSolarEclipseInfo{}, false } - if mode == localSolarEclipseQueryVisible && !localSolarEclipseVisibleOnDate(info, dayStart, dayEnd) { + if mode == localSolarEclipseQueryVisible && !localSolarEclipseVisibleOnDate(info, dayStart, dayEnd, module) { return LocalSolarEclipseInfo{}, false } return info, true } // LastLocalSolarEclipse 上次站心日食 / previous local solar eclipse. -// Previous visible local solar eclipse, using NASA bulletin Split-K by default. -func LastLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return LastLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) LastLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.LastLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // LastLocalSolarEclipseNASABulletinSplitK 上次站心日食(NASA bulletin Split-K) / previous local solar eclipse with NASA bulletin Split-K. -// Previous visible local solar eclipse with the NASA bulletin Split-K model. -func LastLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) LastLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) return info } // LastLocalSolarEclipseIAUSingleK 上次站心日食(IAU Single-K) / previous local solar eclipse with IAU Single-K. -// Previous visible local solar eclipse with the IAU Single-K model. -func LastLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseIAUSingleK, localSolarEclipseQueryVisible) +func (module *Eclipse) LastLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryVisible, module) return info } // LastGeometricLocalSolarEclipse 上次站心几何日食 / previous geometric local solar eclipse. -// Previous geometric local solar eclipse, using NASA bulletin Split-K by default. -func LastGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return LastGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) LastGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.LastGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // LastGeometricLocalSolarEclipseNASABulletinSplitK 上次站心几何日食(NASA bulletin Split-K) / previous geometric local solar eclipse with NASA bulletin Split-K. -// Previous geometric local solar eclipse with the NASA bulletin Split-K model. -func LastGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryGeometric) +func (module *Eclipse) LastGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryGeometric, module) return info } // LastGeometricLocalSolarEclipseIAUSingleK 上次站心几何日食(IAU Single-K) / previous geometric local solar eclipse with IAU Single-K. -// Previous geometric local solar eclipse with the IAU Single-K model. -func LastGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseIAUSingleK, localSolarEclipseQueryGeometric) +func (module *Eclipse) LastGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryGeometric, module) return info } // LastLocalTotalSolarEclipse 上次站心日全食 / previous local total solar eclipse. -// Previous visible local total solar eclipse, using NASA bulletin Split-K by default. -func LastLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return searchLocalTotalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) LastLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return searchLocalTotalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) } // LastLocalAnnularSolarEclipse 上次站心日环食 / previous local annular solar eclipse. -// Previous visible local annular solar eclipse, using NASA bulletin Split-K by default. -func LastLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return searchLocalAnnularSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) LastLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return searchLocalAnnularSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) } // NextLocalSolarEclipse 下次站心日食 / next local solar eclipse. -// Next visible local solar eclipse, using NASA bulletin Split-K by default. -func NextLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return NextLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) NextLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.NextLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // NextLocalSolarEclipseNASABulletinSplitK 下次站心日食(NASA bulletin Split-K) / next local solar eclipse with NASA bulletin Split-K. -// Next visible local solar eclipse with the NASA bulletin Split-K model. -func NextLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) NextLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) return info } // NextLocalSolarEclipseIAUSingleK 下次站心日食(IAU Single-K) / next local solar eclipse with IAU Single-K. -// Next visible local solar eclipse with the IAU Single-K model. -func NextLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseIAUSingleK, localSolarEclipseQueryVisible) +func (module *Eclipse) NextLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryVisible, module) return info } // NextGeometricLocalSolarEclipse 下次站心几何日食 / next geometric local solar eclipse. -// Next geometric local solar eclipse, using NASA bulletin Split-K by default. -func NextGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return NextGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) NextGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.NextGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // NextGeometricLocalSolarEclipseNASABulletinSplitK 下次站心几何日食(NASA bulletin Split-K) / next geometric local solar eclipse with NASA bulletin Split-K. -// Next geometric local solar eclipse with the NASA bulletin Split-K model. -func NextGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryGeometric) +func (module *Eclipse) NextGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryGeometric, module) return info } // NextGeometricLocalSolarEclipseIAUSingleK 下次站心几何日食(IAU Single-K) / next geometric local solar eclipse with IAU Single-K. -// Next geometric local solar eclipse with the IAU Single-K model. -func NextGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseIAUSingleK, localSolarEclipseQueryGeometric) +func (module *Eclipse) NextGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + info, _ := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryGeometric, module) return info } // NextLocalTotalSolarEclipse 下次站心日全食 / next local total solar eclipse. -// Next visible local total solar eclipse, using NASA bulletin Split-K by default. -func NextLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return searchLocalTotalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) NextLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return searchLocalTotalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) } // NextLocalAnnularSolarEclipse 下次站心日环食 / next local annular solar eclipse. -// Next visible local annular solar eclipse, using NASA bulletin Split-K by default. -func NextLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - return searchLocalAnnularSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) NextLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + return searchLocalAnnularSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) } // ClosestLocalSolarEclipse 最近一次站心日食 / closest local solar eclipse. -// Closest visible local solar eclipse, using NASA bulletin Split-K by default. -func ClosestLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return ClosestLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) ClosestLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.ClosestLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // ClosestLocalSolarEclipseNASABulletinSplitK 最近一次站心日食(NASA bulletin Split-K) / closest local solar eclipse with NASA bulletin Split-K. -// Closest visible local solar eclipse with the NASA bulletin Split-K model. -func ClosestLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) - next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) + next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) return closestLocalSolarEclipse(date, last, hasLast, next, hasNext) } // ClosestLocalSolarEclipseIAUSingleK 最近一次站心日食(IAU Single-K) / closest local solar eclipse with IAU Single-K. -// Closest visible local solar eclipse with the IAU Single-K model. -func ClosestLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseIAUSingleK, localSolarEclipseQueryVisible) - next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseIAUSingleK, localSolarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryVisible, module) + next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryVisible, module) return closestLocalSolarEclipse(date, last, hasLast, next, hasNext) } // ClosestGeometricLocalSolarEclipse 最近一次站心几何日食 / closest geometric local solar eclipse. -// Closest geometric local solar eclipse, using NASA bulletin Split-K by default. -func ClosestGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - return ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) +func (module *Eclipse) ClosestGeometricLocalSolarEclipse(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + return module.ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date, lon, lat, height) } // ClosestGeometricLocalSolarEclipseNASABulletinSplitK 最近一次站心几何日食(NASA bulletin Split-K) / closest geometric local solar eclipse with NASA bulletin Split-K. -// Closest geometric local solar eclipse with the NASA bulletin Split-K model. -func ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryGeometric) - next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryGeometric) +func (module *Eclipse) ClosestGeometricLocalSolarEclipseNASABulletinSplitK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryGeometric, module) + next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryGeometric, module) return closestLocalSolarEclipse(date, last, hasLast, next, hasNext) } // ClosestGeometricLocalSolarEclipseIAUSingleK 最近一次站心几何日食(IAU Single-K) / closest geometric local solar eclipse with IAU Single-K. -// Closest geometric local solar eclipse with the IAU Single-K model. -func ClosestGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { - last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseIAUSingleK, localSolarEclipseQueryGeometric) - next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseIAUSingleK, localSolarEclipseQueryGeometric) +func (module *Eclipse) ClosestGeometricLocalSolarEclipseIAUSingleK(date time.Time, lon, lat, height float64) LocalSolarEclipseInfo { + last, hasLast := searchLocalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryGeometric, module) + next, hasNext := searchLocalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelIAUSingleK, module), localSolarEclipseQueryGeometric, module) return closestLocalSolarEclipse(date, last, hasLast, next, hasNext) } // ClosestLocalTotalSolarEclipse 最近一次站心日全食 / closest local total solar eclipse. -// Closest visible local total solar eclipse, using NASA bulletin Split-K by default. -func ClosestLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - last, hasLast := searchLocalTotalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) - next, hasNext := searchLocalTotalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalTotalSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + last, hasLast := searchLocalTotalSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) + next, hasNext := searchLocalTotalSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) return closestLocalSolarEclipseResult(date, last, hasLast, next, hasNext) } // ClosestLocalAnnularSolarEclipse 最近一次站心日环食 / closest local annular solar eclipse. -// Closest visible local annular solar eclipse, using NASA bulletin Split-K by default. -func ClosestLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { - last, hasLast := searchLocalAnnularSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) - next, hasNext := searchLocalAnnularSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseNASABulletinSplitK, localSolarEclipseQueryVisible) +func (module *Eclipse) ClosestLocalAnnularSolarEclipse(date time.Time, lon, lat, height float64) (LocalSolarEclipseInfo, bool) { + last, hasLast := searchLocalAnnularSolarEclipse(date, lon, lat, height, -1, true, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) + next, hasNext := searchLocalAnnularSolarEclipse(date, lon, lat, height, 1, false, localSolarEclipseCalculatorForModel(SolarEclipseModelNASABulletinSplitK, module), localSolarEclipseQueryVisible, module) return closestLocalSolarEclipseResult(date, last, hasLast, next, hasNext) } @@ -412,13 +402,17 @@ func searchLocalSolarEclipse( direction int, includeCurrent bool, calculator localSolarEclipseCalculator, - mode localSolarEclipseQueryMode, -) (LocalSolarEclipseInfo, bool) { + mode localSolarEclipseQueryMode, modules ...*Eclipse) (LocalSolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + info, _, ok := searchLocalCentralSolarEclipse( solarEclipseTimeToTTJDE(date), solarEclipseTimeToTTJDE(date)+float64(direction)*localSolarEclipseDefaultSearchYears*365.25, direction, includeCurrent, SolarEclipseLocalSearchAny, lon, lat, height, date.Location(), - calculator, mode, + calculator, mode, module, ) return info, ok } @@ -429,13 +423,17 @@ func searchLocalTotalSolarEclipse( direction int, includeCurrent bool, calculator localSolarEclipseCalculator, - mode localSolarEclipseQueryMode, -) (LocalSolarEclipseInfo, bool) { + mode localSolarEclipseQueryMode, modules ...*Eclipse) (LocalSolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + info, _, ok := searchLocalCentralSolarEclipse( solarEclipseTimeToTTJDE(date), solarEclipseTimeToTTJDE(date)+float64(direction)*localSolarEclipseDefaultSearchYears*365.25, direction, includeCurrent, SolarEclipseLocalSearchTotal, lon, lat, height, date.Location(), - calculator, mode, + calculator, mode, module, ) return info, ok } @@ -446,19 +444,28 @@ func searchLocalAnnularSolarEclipse( direction int, includeCurrent bool, calculator localSolarEclipseCalculator, - mode localSolarEclipseQueryMode, -) (LocalSolarEclipseInfo, bool) { + mode localSolarEclipseQueryMode, modules ...*Eclipse) (LocalSolarEclipseInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + info, _, ok := searchLocalCentralSolarEclipse( solarEclipseTimeToTTJDE(date), solarEclipseTimeToTTJDE(date)+float64(direction)*localSolarEclipseDefaultSearchYears*365.25, direction, includeCurrent, SolarEclipseLocalSearchAnnular, lon, lat, height, date.Location(), - calculator, mode, + calculator, mode, module, ) return info, ok } -func isPotentialLocalSolarEclipse(newMoonTT float64) bool { - return math.Abs(basic.HMoonTrueBo(newMoonTT)) <= localSolarEclipseLatitudeLimitDeg +func isPotentialLocalSolarEclipse(newMoonTT float64, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + return math.Abs(basic.HMoonTrueBoWithInstance(newMoonTT, module.inst)) <= localSolarEclipseLatitudeLimitDeg } func localSolarEclipseMatchesDirection(greatestTT, targetTT float64, direction int, includeCurrent bool) bool { @@ -475,10 +482,14 @@ func localSolarEclipseMatchesDirection(greatestTT, targetTT float64, direction i func localSolarEclipseInfoFromBasic( result basic.LocalSolarEclipseResult, lon, lat, height float64, - location *time.Location, -) LocalSolarEclipseInfo { - info := localSolarEclipseInfoFieldsFromBasic(result, lon, lat, height, location) - info.ContactPoints = localSolarEclipseContactPointsFromBasic(result, lon, lat, height, location) + location *time.Location, limb basic.LunarLimb, modules ...*Eclipse) LocalSolarEclipseInfo { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + info := localSolarEclipseInfoFieldsFromBasic(result, lon, lat, height, location, module) + info.ContactPoints = localSolarEclipseContactPointsFromBasic(result, lon, lat, height, location, limb, module) return info } @@ -495,12 +506,17 @@ func localSolarEclipseInfoFromDiagram( func localSolarEclipseInfoFieldsFromBasic( result basic.LocalSolarEclipseResult, lon, lat, height float64, - location *time.Location, -) LocalSolarEclipseInfo { + location *time.Location, modules ...*Eclipse) LocalSolarEclipseInfo { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + visibleThreshold := localSolarEclipseVisibilityThreshold(height, lat) - saros, hasSaros := solarSarosInfo(result.GreatestEclipse) + saros, hasSaros := solarSarosInfo(result.GreatestEclipse, module) return LocalSolarEclipseInfo{ Model: mapBasicSolarEclipseModel(result.Model), + LunarLimbApplied: result.LunarLimbApplied, SunRadiusModel: result.SunRadiusModel, Type: mapBasicSolarEclipseType(result.Type), HasSaros: hasSaros, @@ -531,17 +547,21 @@ func localSolarEclipseInfoFieldsFromBasic( func localSolarEclipseContactPointsFromBasic( result basic.LocalSolarEclipseResult, lon, lat, height float64, - location *time.Location, -) []LocalSolarEclipseContactPoint { + location *time.Location, limb basic.LunarLimb, modules ...*Eclipse) []LocalSolarEclipseContactPoint { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !result.HasPartial { return nil } - options := basic.LocalSolarEclipseDiagramOptions{StepDays: 1, SunRadiusModel: result.SunRadiusModel} + options := basic.LocalSolarEclipseDiagramOptions{StepDays: 1, SunRadiusModel: result.SunRadiusModel, LunarLimb: limb} var diagram basic.LocalSolarEclipseDiagramResult if result.Model == basic.SolarEclipseModelIAUSingleK { - diagram = basic.LocalSolarEclipseDiagramIAUSingleK(result.GreatestEclipse, lon, lat, height, options) + diagram = basic.LocalSolarEclipseDiagramIAUSingleKWithInstance(result.GreatestEclipse, lon, lat, height, options, module.inst) } else { - diagram = basic.LocalSolarEclipseDiagramNASABulletinSplitK(result.GreatestEclipse, lon, lat, height, options) + diagram = basic.LocalSolarEclipseDiagramNASABulletinSplitKWithInstance(result.GreatestEclipse, lon, lat, height, options, module.inst) } return localSolarEclipseContactPointsFromFrames(diagram.Frames, location) } @@ -597,22 +617,37 @@ func localSolarEclipseRange(info LocalSolarEclipseInfo) (time.Time, time.Time, b return info.PartialStart, info.PartialEnd, true } -func localSolarEclipseVisible(info LocalSolarEclipseInfo) bool { +func localSolarEclipseVisible(info LocalSolarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + eventStart, eventEnd, ok := localSolarEclipseRange(info) if !ok { return false } - return localSolarEclipseVisibleDuring(info, eventStart, eventEnd) + return localSolarEclipseVisibleDuring(info, eventStart, eventEnd, module) } -func localCentralSolarEclipseVisible(info LocalSolarEclipseInfo) bool { +func localCentralSolarEclipseVisible(info LocalSolarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !info.HasCentral || info.CentralStart.IsZero() || info.CentralEnd.IsZero() { return false } - return localSolarEclipseVisibleDuring(info, info.CentralStart, info.CentralEnd) + return localSolarEclipseVisibleDuring(info, info.CentralStart, info.CentralEnd, module) } -func localSolarEclipseVisibleOnDate(info LocalSolarEclipseInfo, dayStart, dayEnd time.Time) bool { +func localSolarEclipseVisibleOnDate(info LocalSolarEclipseInfo, dayStart, dayEnd time.Time, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + eventStart, eventEnd, ok := localSolarEclipseRange(info) if !ok { return false @@ -622,25 +657,30 @@ func localSolarEclipseVisibleOnDate(info LocalSolarEclipseInfo, dayStart, dayEnd if !segmentStart.Before(segmentEnd) { return false } - return localSolarEclipseVisibleDuring(info, segmentStart, segmentEnd) + return localSolarEclipseVisibleDuring(info, segmentStart, segmentEnd, module) } -func localSolarEclipseVisibleDuring(info LocalSolarEclipseInfo, start, end time.Time) bool { +func localSolarEclipseVisibleDuring(info LocalSolarEclipseInfo, start, end time.Time, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if !start.Before(end) && !start.Equal(end) { return false } - if localSolarEclipseAltitudeVisible(start, info) || localSolarEclipseAltitudeVisible(end, info) { + if localSolarEclipseAltitudeVisible(start, info, module) || localSolarEclipseAltitudeVisible(end, info, module) { return true } for dayStart, _, _ := solarEclipseLocalDayBounds(start); !dayStart.After(end); dayStart = nextSolarEclipseLocalDayStart(dayStart) { _, culminationSeed, _ := solarEclipseLocalDayBounds(dayStart) - culmination := solarCulminationTime(culminationSeed, info.Longitude) + culmination := solarCulminationTime(culminationSeed, info.Longitude, module) if culmination.Before(start) || culmination.After(end) { continue } - if localSolarEclipseAltitudeVisible(culmination, info) { + if localSolarEclipseAltitudeVisible(culmination, info, module) { return true } } @@ -648,8 +688,13 @@ func localSolarEclipseVisibleDuring(info LocalSolarEclipseInfo, start, end time. return false } -func localSolarEclipseAltitudeVisible(date time.Time, info LocalSolarEclipseInfo) bool { - return solarAltitude(date, info.Longitude, info.Latitude) > localSolarEclipseVisibilityThreshold(info.Height, info.Latitude) +func localSolarEclipseAltitudeVisible(date time.Time, info LocalSolarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + return solarAltitude(date, info.Longitude, info.Latitude, module) > localSolarEclipseVisibilityThreshold(info.Height, info.Latitude) } func localSolarEclipseVisibilityThreshold(height, latitude float64) float64 { @@ -680,14 +725,3 @@ func minLocalSolarTime(a, b time.Time) time.Time { } return b } - -var ( - localSolarEclipseNASABulletinSplitK = localSolarEclipseCalculator{ - global: basic.SolarEclipseNASABulletinSplitK, - local: basic.LocalSolarEclipseNASABulletinSplitK, - } - localSolarEclipseIAUSingleK = localSolarEclipseCalculator{ - global: basic.SolarEclipseIAUSingleK, - local: basic.LocalSolarEclipseIAUSingleK, - } -) diff --git a/eclipse/solar_local_search.go b/eclipse/solar_local_search.go index 32ff786..12e74e3 100644 --- a/eclipse/solar_local_search.go +++ b/eclipse/solar_local_search.go @@ -52,18 +52,18 @@ type SolarEclipseLocalSearchStatus struct { } const ( - // 旧口径为 6000 次候选步进,实测覆盖 992.33~992.47 年;取 992.5 年完整覆盖旧跨度。 + // 默认搜索跨度 992.5 年,完整覆盖旧候选步进口径。 localSolarEclipseDefaultSearchYears = 992.5 // 时间跨度搜索的迭代硬上限,防止病态输入死循环。 localSolarEclipseSearchIterationCap = 200000 ) // SearchLocalCentralSolarEclipse 在给定跨度内搜索站心食并回报是否用尽跨度 / station search within a horizon. -func SearchLocalCentralSolarEclipse( +func (module *Eclipse) SearchLocalCentralSolarEclipse( date time.Time, lon, lat, height float64, options SolarEclipseLocalSearchOptions, ) (LocalSolarEclipseInfo, SolarEclipseLocalSearchStatus) { - calculator := localSolarEclipseCalculatorForModel(options.Model) + calculator := localSolarEclipseCalculatorForModel(options.Model, module) mode := localSolarEclipseQueryVisible if options.Geometric { mode = localSolarEclipseQueryGeometric @@ -80,18 +80,16 @@ func SearchLocalCentralSolarEclipse( info, status, ok := searchLocalCentralSolarEclipse( targetTT, targetTT+float64(direction)*maxYears*365.25, direction, options.IncludeCurrent, options.Kind, lon, lat, height, date.Location(), - calculator, mode, + calculator, mode, module, ) status.Found = ok status.Exhausted = !ok && !status.Capped return info, status } -func localSolarEclipseCalculatorForModel(model SolarEclipseRadiusModel) localSolarEclipseCalculator { - if model == SolarEclipseModelIAUSingleK { - return localSolarEclipseIAUSingleK - } - return localSolarEclipseNASABulletinSplitK +// localSolarEclipseCalculatorForModel 构造绑定模块星历实例的计算器,非 IAU Single-K 一律按 Split-K。 +func localSolarEclipseCalculatorForModel(model SolarEclipseRadiusModel, module *Eclipse) localSolarEclipseCalculator { + return localSolarEclipseCalculatorFor(SolarEclipseOptions{RadiusModel: model}, module) } func searchLocalCentralSolarEclipse( @@ -102,10 +100,14 @@ func searchLocalCentralSolarEclipse( lon, lat, height float64, location *time.Location, calculator localSolarEclipseCalculator, - mode localSolarEclipseQueryMode, -) (LocalSolarEclipseInfo, SolarEclipseLocalSearchStatus, bool) { + mode localSolarEclipseQueryMode, modules ...*Eclipse) (LocalSolarEclipseInfo, SolarEclipseLocalSearchStatus, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + status := SolarEclipseLocalSearchStatus{} - candidateTT := basic.CalcMoonSHByJDE(targetTT, 0) + candidateTT := basic.CalcMoonSHByJDEWithInstance(targetTT, 0, module.inst) capped := true for iteration := 0; iteration < localSolarEclipseSearchIterationCap; iteration++ { if direction > 0 && candidateTT > horizonTT { @@ -118,7 +120,7 @@ func searchLocalCentralSolarEclipse( } status.MonthsScanned++ status.YearsScanned = math.Abs(candidateTT-targetTT) / 365.25 - if isPotentialLocalSolarEclipse(candidateTT) { + if isPotentialLocalSolarEclipse(candidateTT, module) { globalResult := calculator.global(candidateTT) status.GlobalChecks++ if globalResult.Type != basic.SolarEclipseNone { @@ -126,8 +128,8 @@ func searchLocalCentralSolarEclipse( result := calculator.local(globalResult.GreatestEclipse, lon, lat, height) status.LocalChecks++ if localSolarEclipseKindMatchesLocal(kind, result) { - info := localSolarEclipseInfoFromBasic(result, lon, lat, height, location) - if (mode != localSolarEclipseQueryVisible || localSolarEclipseKindVisible(kind, info)) && + info := localSolarEclipseInfoFromBasic(result, lon, lat, height, location, calculator.limb, module) + if (mode != localSolarEclipseQueryVisible || localSolarEclipseKindVisible(kind, info, module)) && localSolarEclipseMatchesDirection(result.GreatestEclipse, targetTT, direction, includeCurrent) { return info, status, true } @@ -135,7 +137,7 @@ func searchLocalCentralSolarEclipse( } } } - candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, direction, localSolarEclipseSynodicMonthDays) + candidateTT = nextEclipseSearchCandidateTT(candidateTT, 0, direction, localSolarEclipseSynodicMonthDays, module) } // 循环自然结束说明撞上了迭代硬上限。 status.Capped = capped @@ -162,9 +164,14 @@ func localSolarEclipseKindMatchesLocal(kind SolarEclipseLocalSearchKind, result return result.Type != basic.SolarEclipseNone } -func localSolarEclipseKindVisible(kind SolarEclipseLocalSearchKind, info LocalSolarEclipseInfo) bool { - if kind == SolarEclipseLocalSearchAny { - return localSolarEclipseVisible(info) +func localSolarEclipseKindVisible(kind SolarEclipseLocalSearchKind, info LocalSolarEclipseInfo, modules ...*Eclipse) bool { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] } - return localCentralSolarEclipseVisible(info) + + if kind == SolarEclipseLocalSearchAny { + return localSolarEclipseVisible(info, module) + } + return localCentralSolarEclipseVisible(info, module) } diff --git a/eclipse/solar_local_search_test.go b/eclipse/solar_local_search_test.go index 6355539..a456f86 100644 --- a/eclipse/solar_local_search_test.go +++ b/eclipse/solar_local_search_test.go @@ -3,6 +3,8 @@ package eclipse import ( "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestSearchLocalCentralSolarEclipseReportsExhaustion(t *testing.T) { @@ -155,6 +157,8 @@ func TestSolarEclipseCandidatesRespectRequestedRange(t *testing.T) { } func TestSolarEclipseCandidatesCentralOnlyUsesGlobalType(t *testing.T) { + // 夹具按旧模型几何选取,用来覆盖该边界分支。 + ephemtest.PinLegacy(t) start := time.Date(-911, time.January, 1, 0, 0, 0, 0, time.UTC) end := time.Date(-908, time.January, 1, 0, 0, 0, 0, time.UTC) all := SolarEclipseCandidates(start, end, SolarEclipseCandidateOptions{}) diff --git a/eclipse/solar_local_test.go b/eclipse/solar_local_test.go index 4150426..5f06a24 100644 --- a/eclipse/solar_local_test.go +++ b/eclipse/solar_local_test.go @@ -5,6 +5,8 @@ import ( "time" "b612.me/astro/basic" + + "b612.me/astro/internal/ephemtest" ) func TestLocalSolarEclipseOnDateByLocalDay(t *testing.T) { @@ -284,6 +286,7 @@ func TestLocalSolarEclipseInfoKeepsLocation(t *testing.T) { } func TestLocalSolarEclipseContactPoints(t *testing.T) { + values := ephemtest.NewValues(t, "local_solar_contact_points") loc := time.FixedZone("UTC+08", 8*3600) info := ClosestLocalSolarEclipse( time.Date(2024, 4, 8, 12, 0, 0, 0, loc), @@ -302,10 +305,10 @@ func TestLocalSolarEclipseContactPoints(t *testing.T) { for _, point := range info.ContactPoints { points[point.Label] = point } - assertSolarFloatClose(t, "C1.ContactPositionAngle", points["C1"].ContactPositionAngle, 226.219228, 1e-3) - assertSolarFloatClose(t, "C2.ContactPositionAngle", points["C2"].ContactPositionAngle, 19.133602, 1e-3) - assertSolarFloatClose(t, "C2.MoonCenterPositionAngle", points["C2"].MoonCenterPositionAngle, 199.133602, 1e-3) - assertSolarFloatClose(t, "C4.ContactClockwiseAngle", points["C4"].ContactClockwiseAngle, 310.781438, 1e-3) + assertSolarFloatClose(t, "C1.ContactPositionAngle", points["C1"].ContactPositionAngle, values.Want("C1.ContactPositionAngle", 226.219228, points["C1"].ContactPositionAngle), 1e-3) + assertSolarFloatClose(t, "C2.ContactPositionAngle", points["C2"].ContactPositionAngle, values.Want("C2.ContactPositionAngle", 19.133602, points["C2"].ContactPositionAngle), 1e-3) + assertSolarFloatClose(t, "C2.MoonCenterPositionAngle", points["C2"].MoonCenterPositionAngle, values.Want("C2.MoonCenterPositionAngle", 199.133602, points["C2"].MoonCenterPositionAngle), 1e-3) + assertSolarFloatClose(t, "C4.ContactClockwiseAngle", points["C4"].ContactClockwiseAngle, values.Want("C4.ContactClockwiseAngle", 310.781438, points["C4"].ContactClockwiseAngle), 1e-3) } func TestLocalSolarEclipseContactPointsFromMergedFrameLabels(t *testing.T) { diff --git a/eclipse/solar_panel.go b/eclipse/solar_panel.go index d409ba1..b498fa3 100644 --- a/eclipse/solar_panel.go +++ b/eclipse/solar_panel.go @@ -5,6 +5,7 @@ import ( "time" "b612.me/astro/basic" + "b612.me/astro/internal/geodata" ) const ( @@ -13,8 +14,8 @@ const ( // brownLunationSynodicMonth 是平均朔望月长度,用于把朔的儒略日换算成月序数。 brownLunationSynodicMonth = 29.530588853 // 视半径换算地平视差用的半径比:地球赤道半径除以天体半径。 - horizontalParallaxMoonRatio = 6378.137 / 1737.4 - horizontalParallaxSunRatio = 6378.137 / 696000.0 + horizontalParallaxMoonRatio = geodata.EarthWGS84RadiusKM / 1737.4 + horizontalParallaxSunRatio = geodata.EarthWGS84RadiusKM / 696000.0 ) // SolarEclipseGeocentricPanel 详细面板所需的食甚时刻地心量 / geocentric quantities at greatest eclipse for detailed panels. @@ -70,26 +71,23 @@ func horizontalParallaxArcsec(semidiameterArcsec, radiusRatio float64) float64 { return math.Asin(math.Max(-1, math.Min(1, sine*radiusRatio))) * 180 / math.Pi * 3600 } -// SolarEclipseGeocentricPanelAt 计算给定日食在食甚时刻的地心量面板。 -// SolarEclipseGeocentricPanelAt computes the geocentric panel of one eclipse at greatest eclipse. -func SolarEclipseGeocentricPanelAt(date time.Time) (SolarEclipseGeocentricPanel, bool) { - return SolarEclipseGeocentricPanelWithOptions(date, SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK}) +// SolarEclipseGeocentricPanelAt 计算给定日食在食甚时刻的地心量面板 / geocentric solar eclipse panel at greatest eclipse. +func (module *Eclipse) SolarEclipseGeocentricPanelAt(date time.Time) (SolarEclipseGeocentricPanel, bool) { + return module.SolarEclipseGeocentricPanelWithOptions(date, SolarEclipseOptions{RadiusModel: SolarEclipseModelNASABulletinSplitK}) } -// SolarEclipseGeocentricPanelIAUSingleK 使用 IAU Single-K 计算地心量面板。 -// SolarEclipseGeocentricPanelIAUSingleK computes the geocentric panel with the IAU Single-K model. -func SolarEclipseGeocentricPanelIAUSingleK(date time.Time) (SolarEclipseGeocentricPanel, bool) { - return SolarEclipseGeocentricPanelWithOptions(date, SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK}) +// SolarEclipseGeocentricPanelIAUSingleK 使用 IAU Single-K 计算地心量面板 / geocentric solar eclipse panel with IAU Single-K. +func (module *Eclipse) SolarEclipseGeocentricPanelIAUSingleK(date time.Time) (SolarEclipseGeocentricPanel, bool) { + return module.SolarEclipseGeocentricPanelWithOptions(date, SolarEclipseOptions{RadiusModel: SolarEclipseModelIAUSingleK}) } -// SolarEclipseGeocentricPanelWithOptions 使用自定义半径口径计算食甚时刻的地心量面板。 -// SolarEclipseGeocentricPanelWithOptions computes the geocentric panel at greatest eclipse with custom radius conventions. -func SolarEclipseGeocentricPanelWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseGeocentricPanel, bool) { - info, ok := SolarEclipseOnDateWithOptions(date, options) +// SolarEclipseGeocentricPanelWithOptions 使用自定义半径口径计算食甚时刻的地心量面板 / geocentric solar eclipse panel with custom radius options. +func (module *Eclipse) SolarEclipseGeocentricPanelWithOptions(date time.Time, options SolarEclipseOptions) (SolarEclipseGeocentricPanel, bool) { + info, ok := module.SolarEclipseOnDateWithOptions(date, options) if !ok { return SolarEclipseGeocentricPanel{}, false } - panel := solarEclipseGeocentricPanelAt(info) + panel := solarEclipseGeocentricPanelAt(info, module) if info.Model == SolarEclipseModelIAUSingleK { panel.Ephemeris = "IAU Single-K" panel.PenumbralK = basic.SolarEclipseIAUSingleRadiusK @@ -103,10 +101,15 @@ func SolarEclipseGeocentricPanelWithOptions(date time.Time, options SolarEclipse return panel, true } -func solarEclipseGeocentricPanelAt(info SolarEclipseInfo) SolarEclipseGeocentricPanel { +func solarEclipseGeocentricPanelAt(info SolarEclipseInfo, modules ...*Eclipse) SolarEclipseGeocentricPanel { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + tt := solarEclipseTimeToTTJDE(info.GreatestEclipse) - conjunctionJDE := basic.CalcMoonSHByJDE(tt, 0) - rightAscensionJDE := solarEclipseRightAscensionConjunction(tt) + conjunctionJDE := basic.CalcMoonSHByJDEWithInstance(tt, 0, module.inst) + rightAscensionJDE := solarEclipseRightAscensionConjunction(tt, module) panel := SolarEclipseGeocentricPanel{ Conjunction: solarEclipseTTJDEToTime(conjunctionJDE, info.GreatestEclipse.Location()), ConjunctionJDE: conjunctionJDE, @@ -115,14 +118,14 @@ func solarEclipseGeocentricPanelAt(info SolarEclipseInfo) SolarEclipseGeocentric RightAscensionConjunctionJD: rightAscensionJDE - basic.DeltaT(tt, true)/86400, DeltaTSeconds: basic.DeltaT(tt, true), } - panel.SunRightAscensionDeg, panel.SunDeclinationDeg = basic.SunApparentRaDec(tt) - panel.MoonRightAscensionDeg, panel.MoonDeclinationDeg = basic.HMoonTrueRaDec(tt) + panel.SunRightAscensionDeg, panel.SunDeclinationDeg = basic.SunApparentRaDecWithInstance(tt, module.inst) + panel.MoonRightAscensionDeg, panel.MoonDeclinationDeg = basic.HMoonTrueRaDecWithInstance(tt, module.inst) panel.SunRadiusModel = info.SunRadiusModel - panel.SunSemidiameterArcsec = basic.SolarEclipseSunSemidiameter(tt, info.SunRadiusModel) - panel.MoonSemidiameterArcsec = basic.MoonSemidiameter(tt) + panel.SunSemidiameterArcsec = basic.SolarEclipseSunSemidiameterWithInstance(tt, info.SunRadiusModel, module.inst) + panel.MoonSemidiameterArcsec = basic.MoonSemidiameterWithInstance(tt, module.inst) panel.SunParallaxArcsec = horizontalParallaxArcsec(panel.SunSemidiameterArcsec, horizontalParallaxSunRatio) panel.MoonParallaxArcsec = horizontalParallaxArcsec(panel.MoonSemidiameterArcsec, horizontalParallaxMoonRatio) - physical := basic.MoonPhysical(tt) + physical := basic.MoonPhysicalWithInstance(tt, module.inst) panel.LibrationLongitudeDeg = physical.LibrationLongitude panel.LibrationLatitudeDeg = physical.LibrationLatitude panel.LibrationPositionAngleDeg = physical.PositionAngle @@ -131,22 +134,32 @@ func solarEclipseGeocentricPanelAt(info SolarEclipseInfo) SolarEclipseGeocentric } // solarEclipseRightAscensionGap 返回月亮与太阳的地心视赤经差,单位弧度。 -func solarEclipseRightAscensionGap(tt float64) float64 { - sunRightAscension, _ := basic.SunApparentRaDec(tt) - moonRightAscension, _ := basic.HMoonTrueRaDec(tt) +func solarEclipseRightAscensionGap(tt float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + sunRightAscension, _ := basic.SunApparentRaDecWithInstance(tt, module.inst) + moonRightAscension, _ := basic.HMoonTrueRaDecWithInstance(tt, module.inst) return math.Remainder(moonRightAscension-sunRightAscension, 360) * math.Pi / 180 } // solarEclipseRightAscensionConjunction 求地心视赤经相等的时刻。 // 赤经差在合附近单调,用牛顿法从食甚时刻收敛即可,不需要预先求朔。 -func solarEclipseRightAscensionConjunction(tt float64) float64 { +func solarEclipseRightAscensionConjunction(tt float64, modules ...*Eclipse) float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + estimate := tt for iteration := 0; iteration < 40; iteration++ { - value := solarEclipseRightAscensionGap(estimate) + value := solarEclipseRightAscensionGap(estimate, module) if math.Abs(value) < 1e-10 { break } - derivative := (solarEclipseRightAscensionGap(estimate+1e-5) - solarEclipseRightAscensionGap(estimate-1e-5)) / 2e-5 + derivative := (solarEclipseRightAscensionGap(estimate+1e-5, module) - solarEclipseRightAscensionGap(estimate-1e-5, module)) / 2e-5 if derivative == 0 || math.IsNaN(derivative) || math.IsInf(derivative, 0) { break } diff --git a/eclipse/solar_path.go b/eclipse/solar_path.go index 56dad4d..b192a5b 100644 --- a/eclipse/solar_path.go +++ b/eclipse/solar_path.go @@ -21,6 +21,8 @@ type SolarEclipsePathOptions struct { // SkipCentralBand reports that the caller already holds the full-footprint // result for the same eclipse, so the central-band envelope is not rebuilt. SkipCentralBand bool + // LunarLimb 月缘剖面,nil 沿用模块设置;修正南北界、带宽、中心带足迹、最长中心食时长与全球始末,中心线不变 / lunar limb profile for limits, widths, band, longest duration and P1/P4. + LunarLimb LunarLimb } // SolarEclipsePathPoint 表示日食路径上的一个地理点。 @@ -73,6 +75,8 @@ type SolarEclipsePath struct { // CentralBandSampled reports that the envelope was reconstructed from sampled // instantaneous footprints rather than an analytic envelope. CentralBandSampled bool + // CentralLimitsLimbApplied 南北界至少一点按月缘剖面平移,带宽随之修正 / the central limits were moved by the lunar limb. + CentralLimitsLimbApplied bool // Step 是实际采用的基础时间采样步长, effective base time step. Step time.Duration // TargetSpacingKM 是实际采用的目标空间采样距离,单位千米。 @@ -113,6 +117,10 @@ type SolarEclipsePartialFootprintOptions struct { // and non-positive values disable the isolines. Levels align to UTC ticks; display layers that // need the viewing timezone grid should generate the instants themselves. GreatestTimeStep time.Duration + // LunarLimb 月缘剖面,nil 沿用模块设置;修正偏食可见域外边界(零食分包络与初亏/复圆升落曲线), + // 内嵌 Eclipse 与 P1/P4 也与 SolarEclipseOnDate 同源地修正;食分线与等时线仍按圆月面 / + // lunar limb profile for the outer partial boundary, the embedded Eclipse and P1/P4. + LunarLimb LunarLimb } // SolarEclipsePartialAreaOptions 是 SolarEclipsePartialFootprintOptions 的兼容别名。 @@ -176,6 +184,8 @@ type SolarEclipsePartialFootprintsInfo struct { // RiseSetCurves 是初亏、食甚和复圆分别发生在日出或日落时的六类边界。 // RiseSetCurves are the six boundaries where local start, greatest, or end occurs at sunrise or sunset. RiseSetCurves []SolarEclipseRiseSetCurve + // PartialBoundaryLimbApplied 偏食可见域外边界已按月缘剖面平移 / the outer partial boundary was moved by the lunar limb. + PartialBoundaryLimbApplied bool // P1-P4 是半影与地球的外切/内切接触点;不存在的内切点保持零值。 // P1-P4 are external/internal penumbral contacts; absent internal contacts remain zero. P1 SolarEclipsePathPoint @@ -272,73 +282,80 @@ type SolarEclipsePartialAreaInfo = SolarEclipsePartialFootprintsInfo type solarEclipsePathCalculator func(float64, basic.SolarEclipsePathOptions) basic.SolarEclipsePathResult type solarEclipsePartialFootprintsCalculator func(float64, basic.SolarEclipsePartialFootprintOptions) basic.SolarEclipsePartialFootprintsResult -// SolarEclipseCentralPath 计算指定日期附近的日食中心路径,默认使用 NASA bulletin Split-K 模型。 -// SolarEclipseCentralPath computes the central path near the given date, using NASA bulletin Split-K by default. -func SolarEclipseCentralPath(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { - return SolarEclipseCentralPathNASABulletinSplitK(date, options) +// SolarEclipseCentralPath 计算指定日期附近的日食中心路径,默认使用 NASA bulletin Split-K 模型 / solar eclipse central path, NASA bulletin Split-K by default. +func (module *Eclipse) SolarEclipseCentralPath(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return module.SolarEclipseCentralPathNASABulletinSplitK(date, options) } -// SolarEclipseCentralPathNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食中心路径。 -// SolarEclipseCentralPathNASABulletinSplitK computes the central path with the NASA bulletin Split-K model. -func SolarEclipseCentralPathNASABulletinSplitK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { - return solarEclipseCentralPath(date, options, basic.SolarEclipseCentralPathNASABulletinSplitK) +// SolarEclipseCentralPathNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食中心路径 / solar eclipse central path with NASA bulletin Split-K. +func (module *Eclipse) SolarEclipseCentralPathNASABulletinSplitK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return solarEclipseCentralPath(date, options, func(seedJDE float64, options basic.SolarEclipsePathOptions) basic.SolarEclipsePathResult { + return basic.SolarEclipseCentralPathNASABulletinSplitKWithInstance(seedJDE, options, module.inst) + }, module) } -// SolarEclipseCentralPathIAUSingleK 使用 IAU Single-K 模型计算日食中心路径。 -// SolarEclipseCentralPathIAUSingleK computes the central path with the IAU Single-K model. -func SolarEclipseCentralPathIAUSingleK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { - return solarEclipseCentralPath(date, options, basic.SolarEclipseCentralPathIAUSingleK) +// SolarEclipseCentralPathIAUSingleK 使用 IAU Single-K 模型计算日食中心路径 / solar eclipse central path with IAU Single-K. +func (module *Eclipse) SolarEclipseCentralPathIAUSingleK(date time.Time, options SolarEclipsePathOptions) (SolarEclipsePath, bool) { + return solarEclipseCentralPath(date, options, func(seedJDE float64, options basic.SolarEclipsePathOptions) basic.SolarEclipsePathResult { + return basic.SolarEclipseCentralPathIAUSingleKWithInstance(seedJDE, options, module.inst) + }, module) } -// SolarEclipsePartialFootprints 计算指定日期附近的日食半影足迹,默认使用 NASA bulletin Split-K 模型。 -// SolarEclipsePartialFootprints computes penumbral footprint samples near the given date, using NASA bulletin Split-K by default. -func SolarEclipsePartialFootprints(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { - return SolarEclipsePartialFootprintsNASABulletinSplitK(date, options) +// SolarEclipsePartialFootprints 计算指定日期附近的日食半影足迹,默认使用 NASA bulletin Split-K 模型 / solar eclipse penumbral footprints, NASA bulletin Split-K by default. +func (module *Eclipse) SolarEclipsePartialFootprints(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return module.SolarEclipsePartialFootprintsNASABulletinSplitK(date, options) } -// SolarEclipsePartialFootprintsNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食半影足迹。 -// SolarEclipsePartialFootprintsNASABulletinSplitK computes penumbral footprint samples with the NASA bulletin Split-K model. -func SolarEclipsePartialFootprintsNASABulletinSplitK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { - return solarEclipsePartialFootprints(date, options, basic.SolarEclipsePartialFootprintsNASABulletinSplitK) +// SolarEclipsePartialFootprintsNASABulletinSplitK 使用 NASA bulletin Split-K 模型计算日食半影足迹 / solar eclipse penumbral footprints with NASA bulletin Split-K. +func (module *Eclipse) SolarEclipsePartialFootprintsNASABulletinSplitK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return solarEclipsePartialFootprints(date, options, func(seedJDE float64, options basic.SolarEclipsePartialFootprintOptions) basic.SolarEclipsePartialFootprintsResult { + return basic.SolarEclipsePartialFootprintsNASABulletinSplitKWithInstance(seedJDE, options, module.inst) + }, module) } -// SolarEclipsePartialFootprintsIAUSingleK 使用 IAU Single-K 模型计算日食半影足迹。 -// SolarEclipsePartialFootprintsIAUSingleK computes penumbral footprint samples with the IAU Single-K model. -func SolarEclipsePartialFootprintsIAUSingleK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { - return solarEclipsePartialFootprints(date, options, basic.SolarEclipsePartialFootprintsIAUSingleK) +// SolarEclipsePartialFootprintsIAUSingleK 使用 IAU Single-K 模型计算日食半影足迹 / solar eclipse penumbral footprints with IAU Single-K. +func (module *Eclipse) SolarEclipsePartialFootprintsIAUSingleK(date time.Time, options SolarEclipsePartialFootprintOptions) (SolarEclipsePartialFootprintsInfo, bool) { + return solarEclipsePartialFootprints(date, options, func(seedJDE float64, options basic.SolarEclipsePartialFootprintOptions) basic.SolarEclipsePartialFootprintsResult { + return basic.SolarEclipsePartialFootprintsIAUSingleKWithInstance(seedJDE, options, module.inst) + }, module) } -// SolarEclipsePartialArea 计算半影足迹,是 SolarEclipsePartialFootprints 的兼容包装。 -// SolarEclipsePartialArea computes penumbral footprint samples and is a compatibility wrapper for SolarEclipsePartialFootprints. -func SolarEclipsePartialArea(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { - return SolarEclipsePartialFootprints(date, options) +// SolarEclipsePartialArea 计算半影足迹,是 SolarEclipsePartialFootprints 的兼容包装 / compatibility wrapper of SolarEclipsePartialFootprints. +func (module *Eclipse) SolarEclipsePartialArea(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return module.SolarEclipsePartialFootprints(date, options) } -// SolarEclipsePartialAreaNASABulletinSplitK 是 SolarEclipsePartialFootprintsNASABulletinSplitK 的兼容包装。 -// SolarEclipsePartialAreaNASABulletinSplitK is a compatibility wrapper for SolarEclipsePartialFootprintsNASABulletinSplitK. -func SolarEclipsePartialAreaNASABulletinSplitK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { - return SolarEclipsePartialFootprintsNASABulletinSplitK(date, options) +// SolarEclipsePartialAreaNASABulletinSplitK 是 SolarEclipsePartialFootprintsNASABulletinSplitK 的兼容包装 / compatibility wrapper of SolarEclipsePartialFootprintsNASABulletinSplitK. +func (module *Eclipse) SolarEclipsePartialAreaNASABulletinSplitK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return module.SolarEclipsePartialFootprintsNASABulletinSplitK(date, options) } -// SolarEclipsePartialAreaIAUSingleK 是 SolarEclipsePartialFootprintsIAUSingleK 的兼容包装。 -// SolarEclipsePartialAreaIAUSingleK is a compatibility wrapper for SolarEclipsePartialFootprintsIAUSingleK. -func SolarEclipsePartialAreaIAUSingleK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { - return SolarEclipsePartialFootprintsIAUSingleK(date, options) +// SolarEclipsePartialAreaIAUSingleK 是 SolarEclipsePartialFootprintsIAUSingleK 的兼容包装 / compatibility wrapper of SolarEclipsePartialFootprintsIAUSingleK. +func (module *Eclipse) SolarEclipsePartialAreaIAUSingleK(date time.Time, options SolarEclipsePartialAreaOptions) (SolarEclipsePartialAreaInfo, bool) { + return module.SolarEclipsePartialFootprintsIAUSingleK(date, options) } func solarEclipseCentralPath( date time.Time, options SolarEclipsePathOptions, - calculator solarEclipsePathCalculator, -) (SolarEclipsePath, bool) { + calculator solarEclipsePathCalculator, modules ...*Eclipse) (SolarEclipsePath, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + location := date.Location() - result := calculator(solarEclipseTimeToTTJDE(date), basicSolarEclipsePathOptions(options)) + basicOptions := basicSolarEclipsePathOptions(options) + if basicOptions.LunarLimb == nil { + basicOptions.LunarLimb = module.lunarLimb + } + result := calculator(solarEclipseTimeToTTJDE(date), basicOptions) if !result.Eclipse.HasCentral || len(result.CenterLine) == 0 { return SolarEclipsePath{}, false } path := SolarEclipsePath{ - Eclipse: solarEclipseInfoFromBasic(result.Eclipse, location), + Eclipse: solarEclipseInfoFromBasic(result.Eclipse, location, module), PathWidthDefined: result.PathWidthDefined, Greatest: solarEclipsePathPointFromBasic(result.Greatest, location), CenterLine: solarEclipsePathPointsFromBasic(result.CenterLine, location), @@ -349,6 +366,7 @@ func solarEclipseCentralPath( MaxCentralDurationLatitude: result.MaxCentralDurationLatitude, CentralBandSegments: solarEclipsePathSegmentsFromBasic(result.CentralBandSegments, location), CentralBandSampled: result.CentralBandSampled, + CentralLimitsLimbApplied: result.CentralLimitsLimbApplied, Step: solarEclipsePathStepDuration(result.StepDays), TargetSpacingKM: result.TargetSpacingKM, } @@ -419,20 +437,27 @@ func solarEclipseGreatestTimeContoursFromBasic( func solarEclipsePartialFootprints( date time.Time, options SolarEclipsePartialFootprintOptions, - calculator solarEclipsePartialFootprintsCalculator, -) (SolarEclipsePartialFootprintsInfo, bool) { + calculator solarEclipsePartialFootprintsCalculator, modules ...*Eclipse) (SolarEclipsePartialFootprintsInfo, bool) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + location := date.Location() basicOptions := basicSolarEclipsePartialFootprintOptions(options) greatestTimeLevels := solarEclipseGreatestTimeLevels(options.GreatestTimeValues) basicOptions.GreatestTimeValues = solarEclipseGreatestTimeLevelJDEs(greatestTimeLevels) basicOptions.GreatestTimeStep = options.GreatestTimeStep + if basicOptions.LunarLimb == nil { + basicOptions.LunarLimb = module.lunarLimb + } result := calculator(solarEclipseTimeToTTJDE(date), basicOptions) if !result.Eclipse.HasPartial || len(result.Footprints) == 0 { return SolarEclipsePartialFootprintsInfo{}, false } footprints := SolarEclipsePartialFootprintsInfo{ - Eclipse: solarEclipseInfoFromBasic(result.Eclipse, location), + Eclipse: solarEclipseInfoFromBasic(result.Eclipse, location, module), Footprints: solarEclipsePartialFootprintsFromBasic(result.Footprints, location), CentralShadowFootprints: solarEclipsePartialFootprintsFromBasic(result.CentralShadowFootprints, location), CentralBandFootprints: solarEclipsePartialFootprintsFromBasic(result.CentralBandFootprints, location), @@ -443,6 +468,7 @@ func solarEclipsePartialFootprints( MagnitudeContours: solarEclipseMagnitudeContoursFromBasic(result.MagnitudeContours, location), GreatestTimeContours: solarEclipseGreatestTimeContoursFromBasic(result.GreatestTimeContours, greatestTimeLevels, location), RiseSetCurves: solarEclipseRiseSetCurvesFromBasic(result.RiseSetCurves, location), + PartialBoundaryLimbApplied: result.PartialBoundaryLimbApplied, P1: solarEclipseOptionalPathPointFromBasic(result.P1, location), P2: solarEclipseOptionalPathPointFromBasic(result.P2, location), P3: solarEclipseOptionalPathPointFromBasic(result.P3, location), @@ -483,6 +509,7 @@ func basicSolarEclipsePathOptions(options SolarEclipsePathOptions) basic.SolarEc basicOptions := basic.SolarEclipsePathOptions{ TargetSpacingKM: options.TargetSpacingKM, SkipCentralBand: options.SkipCentralBand, + LunarLimb: options.LunarLimb, } if options.Step > 0 { basicOptions.StepDays = options.Step.Hours() / 24 @@ -495,6 +522,7 @@ func basicSolarEclipsePartialFootprintOptions(options SolarEclipsePartialFootpri BoundaryPoints: options.BoundaryPoints, MagnitudeValues: append([]float64(nil), options.MagnitudeValues...), DisableRiseSetCurves: options.DisableRiseSet, + LunarLimb: options.LunarLimb, } if options.Step > 0 { basicOptions.StepDays = options.Step.Hours() / 24 diff --git a/eclipse/solar_path_test.go b/eclipse/solar_path_test.go index 28eea57..07cb7b2 100644 --- a/eclipse/solar_path_test.go +++ b/eclipse/solar_path_test.go @@ -4,6 +4,8 @@ import ( "math" "testing" "time" + + "b612.me/astro/internal/ephemtest" ) func TestSolarEclipseCentralPathKeepsLocation(t *testing.T) { @@ -168,6 +170,8 @@ func TestSolarEclipsePartialAreaCompatibilityWrapper(t *testing.T) { } func TestSolarEclipsePartialFootprints20140429NonCentralContactsMatchNASA(t *testing.T) { + // NASA 交食根数由 VSOP87/ELP 算出,接触时刻只与旧模型逐秒可比。 + ephemtest.PinLegacy(t) partial, ok := SolarEclipsePartialFootprints( time.Date(2014, time.April, 29, 0, 0, 0, 0, time.UTC), SolarEclipsePartialFootprintOptions{Step: 10 * time.Minute, BoundaryPoints: 96}, @@ -213,6 +217,8 @@ func TestSolarEclipsePartialFootprintsRetryTinyGrazingFootprints(t *testing.T) { } func TestSolarEclipseCentralPathRecoversFromCoarseSampling(t *testing.T) { + // 夹具按旧模型几何选取,用来覆盖该边界分支。 + ephemtest.PinLegacy(t) path, ok := SolarEclipseCentralPath( time.Date(2119, 3, 11, 0, 0, 0, 0, time.UTC), SolarEclipsePathOptions{Step: time.Hour}, diff --git a/eclipse/solar_shadow.go b/eclipse/solar_shadow.go index 96a08fd..e235365 100644 --- a/eclipse/solar_shadow.go +++ b/eclipse/solar_shadow.go @@ -121,16 +121,16 @@ type SolarEclipseShadowSolver struct { } // NewSolarEclipseShadowSolver 构造单时刻求解器 / builds a single-instant solver. -func NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver { +func (module *Eclipse) NewSolarEclipseShadowSolver(options SolarEclipseShadowSolverOptions) *SolarEclipseShadowSolver { return &SolarEclipseShadowSolver{ options: options, - inner: basic.NewSolarEclipseShadowSolver(basic.SolarEclipseShadowSolverOptions{ + inner: basic.NewSolarEclipseShadowSolverWithInstance(basic.SolarEclipseShadowSolverOptions{ Model: basic.SolarEclipseRadiusModel(options.Model), DeltaTSeconds: options.DeltaTSeconds, BoundaryPoints: options.BoundaryPoints, Kind: basic.SolarEclipseShadowKind(options.Kind), TargetSpacingKM: options.TargetSpacingKM, - }), + }, module.inst), } } @@ -239,13 +239,13 @@ func solarEclipseStationStateFromBasic( } // SolarEclipseShadowAt 无状态便捷入口,可并发调用 / stateless convenience entry point. -func SolarEclipseShadowAt(value time.Time) (SolarEclipseShadowInstant, bool) { - return NewSolarEclipseShadowSolver(SolarEclipseShadowSolverOptions{}).ShadowAt(value) +func (module *Eclipse) SolarEclipseShadowAt(value time.Time) (SolarEclipseShadowInstant, bool) { + return module.NewSolarEclipseShadowSolver(SolarEclipseShadowSolverOptions{}).ShadowAt(value) } // SolarEclipseStationStateAt 无状态便捷入口,可并发调用 / stateless convenience entry point. -func SolarEclipseStationStateAt(value time.Time, lon, lat, height float64) SolarEclipseStationState { - return NewSolarEclipseShadowSolver(SolarEclipseShadowSolverOptions{}).StationStateAt(value, lon, lat, height) +func (module *Eclipse) SolarEclipseStationStateAt(value time.Time, lon, lat, height float64) SolarEclipseStationState { + return module.NewSolarEclipseShadowSolver(SolarEclipseShadowSolverOptions{}).StationStateAt(value, lon, lat, height) } func finiteEclipseValue(value float64) bool { @@ -299,15 +299,15 @@ func (solver *SolarEclipseShadowSolver) StationStatesBetween( } // SolarEclipseShadowBetween 无状态批量入口,使用进程级 ΔT / stateless batch entry point. -func SolarEclipseShadowBetween( +func (module *Eclipse) SolarEclipseShadowBetween( start, end time.Time, step time.Duration, ) []SolarEclipseShadowInstant { - return SolarEclipseShadowBetweenWithOptions(start, end, step, SolarEclipseShadowSolverOptions{}) + return module.SolarEclipseShadowBetweenWithOptions(start, end, step, SolarEclipseShadowSolverOptions{}) } // SolarEclipseShadowBetweenWithOptions 可显式指定 ΔT 与模型 / with explicit ΔT and model. -func SolarEclipseShadowBetweenWithOptions( +func (module *Eclipse) SolarEclipseShadowBetweenWithOptions( start, end time.Time, step time.Duration, options SolarEclipseShadowSolverOptions, ) []SolarEclipseShadowInstant { - return NewSolarEclipseShadowSolver(options).ShadowBetween(start, end, step) + return module.NewSolarEclipseShadowSolver(options).ShadowBetween(start, end, step) } diff --git a/eclipse/svg/data_source_test.go b/eclipse/svg/data_source_test.go index deeeb3c..c8f550f 100644 --- a/eclipse/svg/data_source_test.go +++ b/eclipse/svg/data_source_test.go @@ -17,6 +17,7 @@ func TestEclipseMapDataSourcesUseDocumentedVocabulary(t *testing.T) { "besselian-critical-envelope", "paired-limit-chords", "sampled-open-sweep", "central-path-limits", "penumbral-outlines", "central-shadow-outlines", "p1-p4-visibility-regions", "p1-p4-horizon-boundaries", + "c1-c4-visibility-regions", "c1-c4-horizon-boundaries", "transit-contact-isochrones", } if len(documented) != len(eclipseMapDataSources) { t.Fatalf("vocabulary has %d entries, want %d", len(eclipseMapDataSources), len(documented)) @@ -51,6 +52,13 @@ func TestEclipseMapDataSourcesUseDocumentedVocabulary(t *testing.T) { t.Fatal("missing 2026-03-03 lunar map") } + if rendered, ok := TransitMapSVG(TransitVenus, time.Date(2012, time.June, 6, 0, 0, 0, 0, time.UTC), + TransitMapSVGOptions{Location: time.UTC, Isochrones: TransitIsochroneExternal, IsochroneStep: 10 * time.Minute}); ok { + charts["2012-06-06 venus transit"] = rendered + } else { + t.Fatal("missing 2012-06-06 transit map") + } + seen := map[string]bool{} for name, chart := range charts { for _, match := range eclipseMapDataSourcePattern.FindAllStringSubmatch(chart, -1) { @@ -73,6 +81,9 @@ func TestEclipseMapDataSourcesUseDocumentedVocabulary(t *testing.T) { eclipseMapSourceCentralShadowOutlines, eclipseMapSourceLunarVisibilityRegions, eclipseMapSourceLunarHorizonBoundaries, + eclipseMapSourceTransitVisibilityRegions, + eclipseMapSourceTransitHorizonBoundaries, + eclipseMapSourceTransitContactIsochrones, } { if !seen[source] { t.Fatalf("main layer source %q carries no data-source marker", source) diff --git a/eclipse/svg/doc.go b/eclipse/svg/doc.go index 6be32e4..81ee315 100644 --- a/eclipse/svg/doc.go +++ b/eclipse/svg/doc.go @@ -1,4 +1,4 @@ -// Package svg 生成日食与月食的 SVG 图件。 +// Package svg 生成日食、月食与水星、金星凌日的 SVG 图件。 // // 可降级的图层用 data-source 标注实际几何来源,取值词表如下;没有降级分支的图层不带该属性。 // @@ -16,27 +16,33 @@ // central-shadow-outlines 瞬时本影、反本影轮廓 // p1-p4-visibility-regions 月食可见性分区 // p1-p4-horizon-boundaries 月食 P1、P4 地平边界 +// c1-c4-visibility-regions 凌日可见性分区 +// c1-c4-horizon-boundaries 凌日 C1、C4 视地平边界 +// transit-contact-isochrones 凌日站心接触等时线 // -// Package svg renders solar- and lunar-eclipse SVG charts. Degradable layers tag the geometry +// Package svg renders solar-eclipse, lunar-eclipse and Mercury/Venus transit SVG charts. Degradable layers tag the geometry // source they actually used in data-source, using exactly the values above. package svg // data-source 词表,见包注释。 const ( - eclipseMapSourcePartialBandUnion = "partial-band-union" - eclipseMapSourceSampledFootprintSweep = "sampled-footprint-sweep" - eclipseMapSourcePartialBandContours = "partial-band-contours" - eclipseMapSourceRiseSetPhaseLines = "rise-set-phase-lines" - eclipseMapSourceMagnitudeContours = "magnitude-contours" - eclipseMapSourceGreatestTimeIsochrones = "greatest-time-isochrones" - eclipseMapSourceBesselianEnvelope = "besselian-critical-envelope" - eclipseMapSourcePairedLimitChords = "paired-limit-chords" - eclipseMapSourceSampledOpenSweep = "sampled-open-sweep" - eclipseMapSourceCentralPathLimits = "central-path-limits" - eclipseMapSourcePenumbralOutlines = "penumbral-outlines" - eclipseMapSourceCentralShadowOutlines = "central-shadow-outlines" - eclipseMapSourceLunarVisibilityRegions = "p1-p4-visibility-regions" - eclipseMapSourceLunarHorizonBoundaries = "p1-p4-horizon-boundaries" + eclipseMapSourcePartialBandUnion = "partial-band-union" + eclipseMapSourceSampledFootprintSweep = "sampled-footprint-sweep" + eclipseMapSourcePartialBandContours = "partial-band-contours" + eclipseMapSourceRiseSetPhaseLines = "rise-set-phase-lines" + eclipseMapSourceMagnitudeContours = "magnitude-contours" + eclipseMapSourceGreatestTimeIsochrones = "greatest-time-isochrones" + eclipseMapSourceBesselianEnvelope = "besselian-critical-envelope" + eclipseMapSourcePairedLimitChords = "paired-limit-chords" + eclipseMapSourceSampledOpenSweep = "sampled-open-sweep" + eclipseMapSourceCentralPathLimits = "central-path-limits" + eclipseMapSourcePenumbralOutlines = "penumbral-outlines" + eclipseMapSourceCentralShadowOutlines = "central-shadow-outlines" + eclipseMapSourceLunarVisibilityRegions = "p1-p4-visibility-regions" + eclipseMapSourceLunarHorizonBoundaries = "p1-p4-horizon-boundaries" + eclipseMapSourceTransitVisibilityRegions = "c1-c4-visibility-regions" + eclipseMapSourceTransitHorizonBoundaries = "c1-c4-horizon-boundaries" + eclipseMapSourceTransitContactIsochrones = "transit-contact-isochrones" ) // eclipseMapDataSources 是全部合法取值,测试用它封闭词表。 @@ -55,4 +61,7 @@ var eclipseMapDataSources = []string{ eclipseMapSourceCentralShadowOutlines, eclipseMapSourceLunarVisibilityRegions, eclipseMapSourceLunarHorizonBoundaries, + eclipseMapSourceTransitVisibilityRegions, + eclipseMapSourceTransitHorizonBoundaries, + eclipseMapSourceTransitContactIsochrones, } diff --git a/eclipse/svg/eclipse_limb_svg_test.go b/eclipse/svg/eclipse_limb_svg_test.go new file mode 100644 index 0000000..2df27bc --- /dev/null +++ b/eclipse/svg/eclipse_limb_svg_test.go @@ -0,0 +1,70 @@ +package svg + +// 各日月食出图入口都接受月缘剖面,并在图注写明所用口径。 + +import ( + "strings" + "testing" + "time" + + "b612.me/astro/moon/limb" +) + +func TestEclipseSVGsDeclareLunarLimbConvention(t *testing.T) { + solarDate := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) + lunarDate := time.Date(2025, time.September, 7, 0, 0, 0, 0, time.UTC) + profile := limb.Default() + render := map[string]func(applied bool) (string, bool){ + "local solar": func(applied bool) (string, bool) { + options := LocalSolarEclipseSVGOptions{Location: time.UTC} + if applied { + options.LunarLimb = profile + } + return LocalSolarEclipseSVG(solarDate, -96.8, 32.78, 0, options) + }, + "solar map": func(applied bool) (string, bool) { + options := SolarEclipseMapSVGOptions{Location: time.UTC} + if applied { + options.LunarLimb = profile + } + return SolarEclipseMapSVG(solarDate, options) + }, + "lunar": func(applied bool) (string, bool) { + options := LunarEclipseSVGOptions{Location: time.UTC} + if applied { + options.LunarLimb = profile + } + return LunarEclipseSVG(lunarDate, options) + }, + "lunar map": func(applied bool) (string, bool) { + options := LunarEclipseMapSVGOptions{Location: time.UTC} + if applied { + options.LunarLimb = profile + } + return LunarEclipseMapSVG(lunarDate, options) + }, + "lunar detailed": func(applied bool) (string, bool) { + options := LunarEclipseDetailedSVGOptions{Location: time.UTC} + if applied { + options.LunarLimb = profile + } + return LunarEclipseDetailedSVG(lunarDate, options) + }, + } + for name, draw := range render { + circular, ok := draw(false) + if !ok { + t.Fatalf("%s: circular chart missing", name) + } + corrected, ok := draw(true) + if !ok { + t.Fatalf("%s: limb chart missing", name) + } + if !strings.Contains(circular, "圆月面") { + t.Fatalf("%s: circular chart does not declare the circular limb", name) + } + if !strings.Contains(corrected, "月缘剖面") { + t.Fatalf("%s: limb chart does not declare the limb profile", name) + } + } +} diff --git a/eclipse/svg/lunar.go b/eclipse/svg/lunar.go index 555d27a..13fbdbb 100644 --- a/eclipse/svg/lunar.go +++ b/eclipse/svg/lunar.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "fmt" "html" @@ -73,6 +74,8 @@ type LunarEclipseSVGOptions struct { // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 uses UT1 labels and requires // Location to be nil or UTC (otherwise the renderer returns false). TimeScale astro.TimeScale + // LunarLimb 月缘剖面,nil 为圆月面;只精修接触时刻,月盘仍按圆面绘制 / lunar limb profile, nil for circular; refines contacts only. + LunarLimb basic.LunarLimb } type lunarEclipseSVGCalculator func(float64, basic.LunarEclipseDiagramOptions) basic.LunarEclipseDiagramResult @@ -164,7 +167,8 @@ func lunarEclipseSVG( } options = normalizeLunarEclipseSVGOptions(options) diagram := calculator(timeToTTJDE(date), basic.LunarEclipseDiagramOptions{ - StepDays: durationToDays(options.Step), + StepDays: durationToDays(options.Step), + LunarLimb: options.LunarLimb, }) if diagram.Eclipse.Type == basic.LunarEclipseNone || len(diagram.Points) == 0 { return "", false @@ -1045,7 +1049,8 @@ func lunarEclipseSVGFooterLines(direction string, info LunarEclipseInfo, options if maxLines < 1 { maxLines = 1 } - note := timenote.Scale(options.TimeScale, info.Maximum, options.Location, options.Language) + note := limbnote.Join(timenote.Scale(options.TimeScale, info.Maximum, options.Location, options.Language), + limbnote.Contacts(info.LunarLimbApplied, options.Language), options.Language) lines := []string{lunarEclipseSVGFooterNoteText(options)} if options.FooterNote != "" { lines = svgchart.WrapText(options.FooterNote, float64(options.Width)-80, fontSize) diff --git a/eclipse/svg/lunar_detailed.go b/eclipse/svg/lunar_detailed.go index 55d3836..c5b80fa 100644 --- a/eclipse/svg/lunar_detailed.go +++ b/eclipse/svg/lunar_detailed.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "fmt" "html" @@ -11,6 +12,7 @@ import ( "b612.me/astro" "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/internal/geodata" "b612.me/astro/internal/svgasset" "b612.me/astro/internal/svgchart" "b612.me/astro/internal/svgmap" @@ -40,6 +42,8 @@ type LunarEclipseDetailedSVGOptions struct { // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 uses UT1 labels and requires // Location to be nil or UTC (otherwise the renderer returns false). TimeScale astro.TimeScale + // LunarLimb 月缘剖面,nil 为圆月面;只精修接触时刻,月盘仍按圆面绘制 / lunar limb profile, nil for circular; refines contacts only. + LunarLimb basic.LunarLimb // Step 是月心路径采样步长;<=0 时使用 5 分钟。 // Step is the Moon-center path sampling step; values <= 0 use five minutes. Step time.Duration @@ -118,7 +122,8 @@ func lunarEclipseDetailedSVG( } options = normalizeLunarEclipseDetailedSVGOptions(options) diagram := calculator(timeToTTJDE(date), basic.LunarEclipseDiagramOptions{ - StepDays: durationToDays(options.Step), + StepDays: durationToDays(options.Step), + LunarLimb: options.LunarLimb, }) if diagram.Eclipse.Type == basic.LunarEclipseNone || len(diagram.Points) == 0 { return "", false @@ -457,7 +462,8 @@ func writeLunarEclipseDetailedSummary( fmt.Fprintf(b, `%s`, view.width/2, view.metaY+float64(index)*22, html.EscapeString(line)) } - zoneNote := timenote.Scale(options.TimeScale, info.Maximum, options.Location, options.Language) + zoneNote := limbnote.Join(timenote.Scale(options.TimeScale, info.Maximum, options.Location, options.Language), + limbnote.Contacts(info.LunarLimbApplied, options.Language), options.Language) labels.ReserveText(view.width/2, view.metaY+4*22, 11, zoneNote, "middle") fmt.Fprintf(b, `%s`, view.width/2, view.metaY+4*22, html.EscapeString(zoneNote)) @@ -499,13 +505,13 @@ func writeLunarEclipseDetailedGeocentricBlocks( {Label: "赤经 R.A.", Value: formatSolarEclipseRA(sunRa)}, {Label: "赤纬 Dec.", Value: formatSolarEclipseDec(sunDec)}, {Label: "视半径 S.D.", Value: formatSolarEclipseArcsec(sunSd)}, - {Label: "地平视差 H.P.", Value: formatSolarEclipseArcsec(horizontalParallaxArcsecRatio(sunSd, 6378.137/696000.0))}, + {Label: "地平视差 H.P.", Value: formatSolarEclipseArcsec(horizontalParallaxArcsecRatio(sunSd, geodata.EarthWGS84RadiusKM/696000.0))}, }, { {Label: "赤经 R.A.", Value: formatSolarEclipseRA(moonRa)}, {Label: "赤纬 Dec.", Value: formatSolarEclipseDec(moonDec)}, {Label: "视半径 S.D.", Value: formatSolarEclipseArcsec(moonSd)}, - {Label: "地平视差 H.P.", Value: formatSolarEclipseArcsec(horizontalParallaxArcsecRatio(moonSd, 6378.137/1737.4))}, + {Label: "地平视差 H.P.", Value: formatSolarEclipseArcsec(horizontalParallaxArcsecRatio(moonSd, geodata.EarthWGS84RadiusKM/1737.4))}, }, } if english { @@ -578,7 +584,7 @@ func writeLunarEclipseArcMinuteScaleBar( return } // 示意图以"月球半径"为单位,一个月球半径对应的真实角径是 asin(R月 / 月距),不是地球视差。 - moonSemidiameterArcmin := math.Asin((lunarMoonRadiusKM/lunarEarthRadiusKM)/shadow.MoonDistanceEarthRadii) * 180 / math.Pi * 60 + moonSemidiameterArcmin := math.Asin((lunarMoonRadiusKM/geodata.EarthWGS84RadiusKM)/shadow.MoonDistanceEarthRadii) * 180 / math.Pi * 60 if moonSemidiameterArcmin <= 0 { return } @@ -650,7 +656,6 @@ func writeLunarEclipseDetailedFooter( } const ( - // lunarMoonRadiusKM 与 lunarEarthRadiusKM 只用于把示意图的"月球半径"刻度换算成角分比例尺。 - lunarMoonRadiusKM = 1737.4 - lunarEarthRadiusKM = 6378.137 + // lunarMoonRadiusKM 与 geodata.EarthWGS84RadiusKM 只用于把示意图的"月球半径"刻度换算成角分比例尺。 + lunarMoonRadiusKM = 1737.4 ) diff --git a/eclipse/svg/lunar_geocentric_maximum_test.go b/eclipse/svg/lunar_geocentric_maximum_test.go index d4fdc03..cb096a9 100644 --- a/eclipse/svg/lunar_geocentric_maximum_test.go +++ b/eclipse/svg/lunar_geocentric_maximum_test.go @@ -9,6 +9,8 @@ import ( "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" + + "b612.me/astro/internal/ephemtest" ) // 详细版"食甚时的月亮(地心坐标)"块必须用食甚时刻的力学时求值,与 NASA 月食图同一时刻; @@ -26,6 +28,7 @@ func TestLunarEclipseDetailedGeocentricBlockUsesGreatestEclipse(t *testing.T) { {"2026-03-03", time.Date(2026, 3, 3, 0, 0, 0, 0, cst), "10h56m15.1s", "+06°24'05.2""}, {"2025-03-14", time.Date(2025, 3, 14, 0, 0, 0, 0, cst), "11h38m23.0s", "+02°40'54.3""}, } + values := ephemtest.NewValues(t, "lunar_geocentric_block") for _, want := range cases { doc, ok := LunarEclipseDetailedSVG(want.date, LunarEclipseDetailedSVGOptions{ Language: "zh", Location: cst}) @@ -33,6 +36,8 @@ func TestLunarEclipseDetailedGeocentricBlockUsesGreatestEclipse(t *testing.T) { t.Fatalf("%s: missing chart", want.name) } ra, dec := lunarGeocentricMoonBlock(t, doc) + want.ra = values.WantString(want.name+"/ra", want.ra, ra) + want.dec = values.WantString(want.name+"/dec", want.dec, dec) if ra != want.ra || dec != want.dec { t.Errorf("%s: 食甚坐标 RA=%s Dec=%s, want RA=%s Dec=%s", want.name, ra, dec, want.ra, want.dec) } diff --git a/eclipse/svg/lunar_map.go b/eclipse/svg/lunar_map.go index 71fc09d..a30bcaa 100644 --- a/eclipse/svg/lunar_map.go +++ b/eclipse/svg/lunar_map.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "fmt" "html" @@ -11,6 +12,7 @@ import ( "b612.me/astro" "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/internal/geodata" "b612.me/astro/internal/lunarhorizon" "b612.me/astro/internal/svgchart" "b612.me/astro/internal/svgmap" @@ -39,6 +41,8 @@ type LunarEclipseMapSVGOptions struct { // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 uses UT1 labels, requires // Location to be nil or UTC (otherwise the renderer returns false) and declares the scale in the footer. TimeScale astro.TimeScale + // LunarLimb 月缘剖面,nil 为圆月面;精修接触时刻,阶段地平圈随之移动 / lunar limb profile; refines contacts and moves phase horizons. + LunarLimb basic.LunarLimb // Projection 选择地图投影;零值使用等经纬投影,不支持的值使渲染器返回 false。 // Projection selects the map projection. The zero value selects the equirectangular projection; unsupported values make the renderer return false. Projection EclipseMapProjection @@ -57,19 +61,24 @@ type LunarEclipseMapSVGOptions struct { // LunarEclipseMapSVG 使用默认月食模型绘制全球 P1-P4 可见区域。 // LunarEclipseMapSVG renders the global P1-P4 visibility regions using the default lunar-eclipse model. func LunarEclipseMapSVG(date time.Time, options LunarEclipseMapSVGOptions) (string, bool) { - return lunarEclipseMapSVG(date, options, eclipsecore.LunarEclipseOnDate) + return lunarEclipseMapSVG(date, options, lunarEclipseMapModule(options).LunarEclipseOnDate) } // LunarEclipseMapSVGDanjon 使用 Danjon 模型绘制全球可见区域。 // LunarEclipseMapSVGDanjon renders the global visibility regions with Danjon's model. func LunarEclipseMapSVGDanjon(date time.Time, options LunarEclipseMapSVGOptions) (string, bool) { - return lunarEclipseMapSVG(date, options, eclipsecore.LunarEclipseOnDateDanjon) + return lunarEclipseMapSVG(date, options, lunarEclipseMapModule(options).LunarEclipseOnDateDanjon) } // LunarEclipseMapSVGChauvenet 使用 Chauvenet 模型绘制全球可见区域。 // LunarEclipseMapSVGChauvenet renders the global visibility regions with Chauvenet's model. func LunarEclipseMapSVGChauvenet(date time.Time, options LunarEclipseMapSVGOptions) (string, bool) { - return lunarEclipseMapSVG(date, options, eclipsecore.LunarEclipseOnDateChauvenet) + return lunarEclipseMapSVG(date, options, lunarEclipseMapModule(options).LunarEclipseOnDateChauvenet) +} + +// lunarEclipseMapModule 返回按选项月缘配置的核心模块;未设置月缘时等同包级计算。 +func lunarEclipseMapModule(options LunarEclipseMapSVGOptions) *eclipsecore.Eclipse { + return eclipsecore.NewWithInstance(nil).WithLunarLimb(options.LunarLimb) } func lunarEclipseMapSVG( @@ -159,7 +168,7 @@ func renderLunarEclipseMapSVG( frame.WriteFrame(&builder) legendRows := lunarEclipseMapLegendRows(frame, float64(options.Width), options.Language, penumbralBands) writeLunarEclipseMapLegend(&builder, frame, float64(options.Width), options.Language, penumbralBands) - writeLunarEclipseMapFooter(&builder, frame, options, projection, info.Maximum, len(legendRows)) + writeLunarEclipseMapFooter(&builder, frame, options, projection, info.Maximum, info.LunarLimbApplied, len(legendRows)) builder.WriteString(``) return builder.String() } @@ -223,11 +232,11 @@ func lunarEclipseVisibilityPath(value time.Time, frame svgmap.Frame) (string, [] } if frame.Projection == svgmap.ProjectionEquirectangular { // 等距圆柱投影在高纬把经度拉伸:1° 的球面地平弧在极点附近可以横跨近 180° 经度, - // 只连端点会在极区切出地平线以外的假可见帽(1904-09-24 实测 −0.054°,约 6 km)。 + // 只连端点会在极区切出地平线以外的假可见帽。 // 极射投影没有这个畸变,保持原输出。 // Equirectangular projection stretches longitude near a pole: a 1-degree spherical // horizon arc can span almost 180 degrees there, so joining endpoints alone cuts a fake - // visible cap outside the horizon (measured -0.054 deg, about 6 km, on 1904-09-24). + // visible cap outside the horizon. // Polar projections have no such distortion and keep their existing output. // 门限取画布上约 0.03 像素:极区细分再多也画不出来,而 GeoJSON 用的 0.002° // 在 812 像素宽的世界图上相当于 0.005 像素,会让 SVG 无谓地大三分之一。 @@ -476,6 +485,7 @@ func writeLunarEclipseMapFooter( options LunarEclipseMapSVGOptions, projection svgmap.Projection, instant time.Time, + limbApplied bool, legendRows int, ) { text := options.FooterNote @@ -497,7 +507,8 @@ func writeLunarEclipseMapFooter( } } // 默认说明是单行;调用方文本按图框宽度折行,行数按画布底边截断,首行位置不变。 - note := timenote.Scale(options.TimeScale, instant, options.Location, options.Language) + note := limbnote.Join(timenote.Scale(options.TimeScale, instant, options.Location, options.Language), + limbnote.Contacts(limbApplied, options.Language), options.Language) lines := []string{text} if options.FooterNote != "" { lines = svgchart.WrapText(options.FooterNote, float64(options.Width)-frame.X-24, 11) @@ -511,20 +522,12 @@ func writeLunarEclipseMapFooter( } } -func normalizeDegree180(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} - // lunarEclipseOrthographicCenter 给出月食正射图的视点:食甚时刻的月下点(月球位于天顶的地点)。 func lunarEclipseOrthographicCenter(at time.Time) (float64, float64) { jd := basic.Date2JD(at.UTC()) tt := basic.UTC2TT(jd) ra, dec := basic.HMoonTrueRaDec(tt) - return normalizeDegree180(ra - basic.ApparentSiderealTime(basic.TT2UT1(tt))*15), dec + return geodata.NormalizeLongitude(ra - basic.ApparentSiderealTime(basic.TT2UT1(tt))*15), dec } // writeLunarEclipseMapDefinitions 输出四类可见性区域所需的形状与掩膜定义。 @@ -558,8 +561,7 @@ func writeLunarEclipseUmbralVisibilityShapes(b *strings.Builder, frame svgmap.Fr } // writeLunarEclipseUmbralVisibleShapes 输出"本影阶段任意时刻在地平上"的取样半球:本影区间按固定档位 -// 取样,每一刻的可见半球都是一个精确的圆盘(按经纬度插值会把近乎子午线方向的边界切进真实区域—— -// 实测 1932-03-22 西经 75.5° 一带上边界 0.5° 经度内从 +19.9° 掉到 −27.2°,1° 列的弦会切掉十几度)。 +// 取样,每一刻的可见半球都是一个精确的圆盘(按经纬度插值会把近乎子午线方向的边界切进真实区域)。 // 这些形状同时用于底带的裁剪(取并集)与半影带的掩膜(取补集),两侧严丝合缝。 func writeLunarEclipseUmbralVisibleShapes(b *strings.Builder, frame svgmap.Frame, info eclipsecore.LunarEclipseInfo) { instants := lunarEclipseUmbralVisibilityInstants(info) diff --git a/eclipse/svg/lunar_map_polar_test.go b/eclipse/svg/lunar_map_polar_test.go index fec209c..e77cffa 100644 --- a/eclipse/svg/lunar_map_polar_test.go +++ b/eclipse/svg/lunar_map_polar_test.go @@ -12,13 +12,7 @@ import ( // TestLunarEclipseMapSVGPolarWitnessStaysOutsideEquirectangularVisibility 固定全球月食地图在 // 极区的可见性契约。等距圆柱投影在高纬把经度拉伸:1° 的球面地平弧在极点附近可以横跨近 // 180° 经度,只连端点就会把地平线以外画进可见区。1904-09-24 的 P1 在南极点附近有一个 -// 月心高度约 −0.05° 的见证点,曾经被画进 visible-at-p1 多边形。 -// TestLunarEclipseMapSVGPolarWitnessStaysOutsideEquirectangularVisibility pins the polar -// visibility contract of the global lunar-eclipse map. Equirectangular projection stretches -// longitude near a pole: a 1-degree spherical horizon arc can span almost 180 degrees there, so -// joining endpoints alone paints sky below the horizon as visible. At P1 of 1904-09-24 a witness -// near the south pole sits about 0.05 degrees below the Moon's horizon and used to be painted -// inside the visible-at-p1 polygon. +// 月心高度约 −0.05° 的见证点,不得落入 visible-at-p1 多边形。 func TestLunarEclipseMapSVGPolarWitnessStaysOutsideEquirectangularVisibility(t *testing.T) { date := time.Date(1904, 9, 24, 0, 0, 0, 0, time.UTC) info, ok := eclipsecore.LunarEclipseOnDate(date) diff --git a/eclipse/svg/lunar_model.go b/eclipse/svg/lunar_model.go index 0db3433..f22ca9e 100644 --- a/eclipse/svg/lunar_model.go +++ b/eclipse/svg/lunar_model.go @@ -28,6 +28,7 @@ func lunarEclipseInfoFromBasic( ) LunarEclipseInfo { info := LunarEclipseInfo{ Type: mapBasicLunarEclipseType(result.Type), + LunarLimbApplied: result.LunarLimbApplied, PenumbralMagnitude: result.PenumbralMagnitude, UmbralMagnitude: result.Magnitude, PenumbralStart: ttJDEToTime(result.PenumbralStart, location), diff --git a/eclipse/svg/lunar_penumbral_phase_test.go b/eclipse/svg/lunar_penumbral_phase_test.go index f9d8a53..cc606c4 100644 --- a/eclipse/svg/lunar_penumbral_phase_test.go +++ b/eclipse/svg/lunar_penumbral_phase_test.go @@ -13,6 +13,8 @@ import ( "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" + + "b612.me/astro/internal/ephemtest" ) // 半影阶段开关的契约:零值输出与不区分半影阶段的基线逐字节一致,打开后补画 U1–U4 地平边界、 @@ -47,6 +49,7 @@ func TestLunarEclipseMapSVGDisablePenumbralPhaseMatchesBaseline(t *testing.T) { if len(render) != len(lunarPenumbralPhaseBaseline) { t.Fatalf("baseline table has %d cases, renderers %d", len(lunarPenumbralPhaseBaseline), len(render)) } + values := ephemtest.NewValues(t, "lunar_penumbral_phase_digest") for _, want := range lunarPenumbralPhaseBaseline { draw, ok := render[want.name] if !ok { @@ -57,7 +60,10 @@ func TestLunarEclipseMapSVGDisablePenumbralPhaseMatchesBaseline(t *testing.T) { t.Fatalf("%s: chart missing", want.name) } sum := md5.Sum([]byte(doc)) - if got := hex.EncodeToString(sum[:]); got != want.digest || len(doc) != want.length { + got := hex.EncodeToString(sum[:]) + want.digest = values.WantString(want.name+"/md5", want.digest, got) + want.length = int(values.Want(want.name+"/bytes", float64(want.length), float64(len(doc)))) + if got != want.digest || len(doc) != want.length { t.Fatalf("%s: md5=%s bytes=%d, want md5=%s bytes=%d", want.name, got, len(doc), want.digest, want.length) } diff --git a/eclipse/svg/solar.go b/eclipse/svg/solar.go index 4ac780a..29966d4 100644 --- a/eclipse/svg/solar.go +++ b/eclipse/svg/solar.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "fmt" "html" @@ -71,6 +72,8 @@ type LocalSolarEclipseSVGOptions struct { // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 uses UT1 labels, requires // Location to be nil or UTC (otherwise the renderer returns false) and declares the scale in the footer. TimeScale astro.TimeScale + // LunarLimb 月缘剖面,nil 为圆月面;精修接触时刻与食甚时的食分、遮蔽率,食甚时刻不变、月盘仍按圆面绘制 / lunar limb profile for contacts, magnitude and obscuration. + LunarLimb basic.LunarLimb } type localSolarEclipseSVGCalculator func(float64, float64, float64, float64, basic.LocalSolarEclipseDiagramOptions) basic.LocalSolarEclipseDiagramResult @@ -125,7 +128,7 @@ func localSolarEclipseSVG( lon, lat, height, - basic.LocalSolarEclipseDiagramOptions{StepDays: solarEclipseDurationToDays(options.Step)}, + basic.LocalSolarEclipseDiagramOptions{StepDays: solarEclipseDurationToDays(options.Step), LunarLimb: options.LunarLimb}, ) if diagram.Eclipse.Type == basic.SolarEclipseNone || len(diagram.Frames) == 0 { return "", false @@ -170,7 +173,8 @@ func renderLocalSolarEclipseSVG( if options.DirectionText != "" { direction = svgchart.EllipsizeText(direction, width-80, 12) } - scaleNote := timenote.Scale(options.TimeScale, info.GreatestEclipse, options.Location, options.Language) + scaleNote := limbnote.Join(timenote.Scale(options.TimeScale, info.GreatestEclipse, options.Location, options.Language), + limbnote.Contacts(info.LunarLimbApplied, options.Language), options.Language) footerLines := localSolarEclipseSVGFooterLines(direction, localSolarEclipseSVGFooterNoteText(options), scaleNote, width, height, headerBottom) footerBaselines, footerOccupied := svgchart.FooterBlock(len(footerLines), height, 12, svgchart.FooterLineHeight(12), localDiagramFooterBottomPadding) diff --git a/eclipse/svg/solar_map.go b/eclipse/svg/solar_map.go index f40ea10..34d11bc 100644 --- a/eclipse/svg/solar_map.go +++ b/eclipse/svg/solar_map.go @@ -1,6 +1,7 @@ package svg import ( + "b612.me/astro/internal/limbnote" "b612.me/astro/internal/timenote" "fmt" "html" @@ -57,6 +58,8 @@ type SolarEclipseMapSVGOptions struct { // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 uses UT1 labels, requires // Location to be nil or UTC (otherwise the renderer returns false) and declares the scale in the footer. TimeScale astro.TimeScale + // LunarLimb 月缘剖面,nil 为圆月面;修正全球始末、食甚点站心面板、中心带界线与带宽、偏食可见域外边界,页脚只声明实际生效的层 / lunar limb profile for contacts, local panel, central limits and partial boundary. + LunarLimb basic.LunarLimb // Projection 选择地图投影;零值从事件几何中自动选择,不支持的值使渲染器返回 false。 // Projection selects the map projection. The zero value selects one from the event geometry; unsupported values make the renderer return false. Projection EclipseMapProjection @@ -137,27 +140,34 @@ func SolarEclipseMapSVG(date time.Time, options SolarEclipseMapSVGOptions) (stri // SolarEclipseMapSVGNASABulletinSplitK 使用 NASA bulletin Split-K 绘制地图。 // SolarEclipseMapSVGNASABulletinSplitK renders a NASA bulletin Split-K map. func SolarEclipseMapSVGNASABulletinSplitK(date time.Time, options SolarEclipseMapSVGOptions) (string, bool) { + module := solarEclipseMapModule(options) return solarEclipseMapSVG(date, options, solarEclipseMapCalculators{ - global: eclipsecore.SolarEclipseOnDateNASABulletinSplitK, + global: module.SolarEclipseOnDateNASABulletinSplitK, panel: eclipsecore.SolarEclipseGeocentricPanelAt, - partial: eclipsecore.SolarEclipsePartialFootprintsNASABulletinSplitK, - central: eclipsecore.SolarEclipseCentralPathNASABulletinSplitK, - local: eclipsecore.GeometricLocalSolarEclipseOnDateNASABulletinSplitK, + partial: module.SolarEclipsePartialFootprintsNASABulletinSplitK, + central: module.SolarEclipseCentralPathNASABulletinSplitK, + local: module.GeometricLocalSolarEclipseOnDateNASABulletinSplitK, }) } // SolarEclipseMapSVGIAUSingleK 使用 IAU Single-K 模型绘制地图。 // SolarEclipseMapSVGIAUSingleK renders an IAU Single-K map. func SolarEclipseMapSVGIAUSingleK(date time.Time, options SolarEclipseMapSVGOptions) (string, bool) { + module := solarEclipseMapModule(options) return solarEclipseMapSVG(date, options, solarEclipseMapCalculators{ - global: eclipsecore.SolarEclipseOnDateIAUSingleK, + global: module.SolarEclipseOnDateIAUSingleK, panel: eclipsecore.SolarEclipseGeocentricPanelIAUSingleK, - partial: eclipsecore.SolarEclipsePartialFootprintsIAUSingleK, - central: eclipsecore.SolarEclipseCentralPathIAUSingleK, - local: eclipsecore.GeometricLocalSolarEclipseOnDateIAUSingleK, + partial: module.SolarEclipsePartialFootprintsIAUSingleK, + central: module.SolarEclipseCentralPathIAUSingleK, + local: module.GeometricLocalSolarEclipseOnDateIAUSingleK, }) } +// solarEclipseMapModule 返回按选项月缘配置的核心模块,用于全局结果、中心带、偏食可见域外边界与食甚点站心面板;食分线、等时线与地心面板保持圆月面。 +func solarEclipseMapModule(options SolarEclipseMapSVGOptions) *eclipsecore.Eclipse { + return eclipsecore.NewWithInstance(nil).WithLunarLimb(options.LunarLimb) +} + func solarEclipseMapSVG( date time.Time, options SolarEclipseMapSVGOptions, @@ -211,6 +221,11 @@ func solarEclipseMapSVG( if calculators.panel != nil { geocentric, hasGeocentric = calculators.panel(date) } + // 带宽与画出的南北界同源:南北界真正按月缘修正时取中心路径食甚点带宽。 + if solarEclipseMapLimbLayers(partial, central, hasCentral, local, hasLocal).CentralLimits { + partial.Eclipse.PathWidthDefined = central.PathWidthDefined + partial.Eclipse.PathWidthKM = central.Greatest.WidthKM + } // 几何(直射点、闭合弧、等时线取值)必须用民用时刻算;只有写出的文字换时标。 // 时刻标记按输出时标的整点取点(那里本就是另一个物理时刻),位置随整点标注移动。 civilPartial := partial @@ -232,6 +247,22 @@ func solarEclipseMapSVG( local, hasLocal, geocentric, hasGeocentric, options, projection), true } +// solarEclipseMapLimbLayers 汇总各图层是否真正按月缘修正;各层彼此独立,页脚与带宽同步都读这里。 +func solarEclipseMapLimbLayers( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central eclipsecore.SolarEclipsePath, + hasCentral bool, + local eclipsecore.LocalSolarEclipseInfo, + hasLocal bool, +) limbnote.SolarMapLayers { + return limbnote.SolarMapLayers{ + GlobalContacts: partial.Eclipse.LunarLimbApplied, + LocalPanel: hasLocal && local.LunarLimbApplied, + CentralLimits: hasCentral && central.CentralLimitsLimbApplied, + PartialBoundary: partial.PartialBoundaryLimbApplied, + } +} + // solarEclipseMapHasCentralBand 报告该事件是否有中心食带(包络、限界或瞬时足迹任一存在)。 func solarEclipseMapHasCentralBand(partial eclipsecore.SolarEclipsePartialFootprintsInfo) bool { return len(partial.CentralBandFootprints) > 0 || len(partial.CentralShadowFootprints) > 0 || @@ -479,7 +510,8 @@ func (plan solarEclipseMapPlan) render( options.Width-44, options.Height-36) fmt.Fprintf(&builder, `%s`, float64(options.Width)/2, html.EscapeString(titleText)) - writeSolarEclipseMapSummary(&builder, labels, partial.Eclipse, local, hasLocal, geocentric, hasGeocentric, options) + limbLayers := solarEclipseMapLimbLayers(partial, central, hasCentral, local, hasLocal) + writeSolarEclipseMapSummary(&builder, labels, partial.Eclipse, local, hasLocal, geocentric, hasGeocentric, limbLayers, options) writeSolarEclipseMapSectionTitle(&builder, labels, layout, options, hasCentral) frame.WriteOcean(&builder) @@ -708,7 +740,7 @@ func solarEclipseAppendEquirectangularPoleRim( for step := 1; step <= steps; step++ { longitude := lastLongitude + delta*float64(step)/float64(steps) result = append(result, svgmap.GeoPoint{ - Longitude: normalizeDegree180(longitude), + Longitude: geodata.NormalizeLongitude(longitude), Latitude: poleLatitude, }) } @@ -795,7 +827,7 @@ func solarEclipseSubsolarPoint(value time.Time) svgmap.GeoPoint { ttJDE := solarEclipseTimeToTTJDE(value) ra, dec := basic.HSunApparentRaDec(ttJDE) ut1JDE := basic.TT2UT1(ttJDE) - longitude := normalizeDegree180(ra - basic.ApparentSiderealTime(ut1JDE)*15) + longitude := geodata.NormalizeLongitude(ra - basic.ApparentSiderealTime(ut1JDE)*15) return svgmap.GeoPoint{Longitude: longitude, Latitude: dec} } @@ -1111,6 +1143,7 @@ func writeSolarEclipseMapSummary( hasLocal bool, geocentric eclipsecore.SolarEclipseGeocentricPanel, hasGeocentric bool, + limbLayers limbnote.SolarMapLayers, options SolarEclipseMapSVGOptions, ) { start := info.PartialBeginOnEarth.In(options.Location) @@ -1192,7 +1225,8 @@ func writeSolarEclipseMapSummary( summaryX, conjunctionY, html.EscapeString(conjunctionText)) } // 图上所有时刻都按展示时区,这里明确写出它与 UT 的偏差,避免被当成 UT 读。 - zoneNote := timenote.Scale(options.TimeScale, maximum, options.Location, options.Language) + zoneNote := limbnote.Join(timenote.Scale(options.TimeScale, maximum, options.Location, options.Language), + limbnote.SolarMap(limbLayers, options.Language), options.Language) labels.ReserveText(summaryX, conjunctionY+20, 11, zoneNote, "middle") fmt.Fprintf(builder, `%s`, summaryX, conjunctionY+20, html.EscapeString(zoneNote)) diff --git a/eclipse/svg/solar_map_benchmark_test.go b/eclipse/svg/solar_map_benchmark_test.go index 15e2553..543feb1 100644 --- a/eclipse/svg/solar_map_benchmark_test.go +++ b/eclipse/svg/solar_map_benchmark_test.go @@ -5,7 +5,7 @@ import ( "time" ) -// 一秒步长请求曾经要跑满整条 18605 个瞬时足迹的扫描;夹到两分钟后应与默认请求同量级。 +// 一秒步长请求应被夹到两分钟,耗时与默认请求同量级。 func BenchmarkSolarEclipseMapSVGPartialStepOneSecond(b *testing.B) { date := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) options := SolarEclipseMapSVGOptions{ diff --git a/eclipse/svg/solar_map_detailed.go b/eclipse/svg/solar_map_detailed.go index 5eb8316..507e36e 100644 --- a/eclipse/svg/solar_map_detailed.go +++ b/eclipse/svg/solar_map_detailed.go @@ -8,6 +8,7 @@ import ( "time" eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/internal/geodata" "b612.me/astro/internal/svgchart" "b612.me/astro/internal/svgmap" ) @@ -90,7 +91,7 @@ func writeSolarEclipseScaleBar( if radius <= 0 || slot.Width <= 0 || slot.Height <= 0 { return } - kilometresPerPixel := (math.Pi / 2 * 6371.0088) / radius + kilometresPerPixel := (math.Pi / 2 * geodata.EarthMeanRadiusKM) / radius target := 130.0 * kilometresPerPixel step := solarEclipseScaleStep(target, slot.Width, func(candidate float64) float64 { barWidth := candidate / kilometresPerPixel diff --git a/eclipse/svg/solar_map_legend_test.go b/eclipse/svg/solar_map_legend_test.go index ecbd861..2781e43 100644 --- a/eclipse/svg/solar_map_legend_test.go +++ b/eclipse/svg/solar_map_legend_test.go @@ -6,7 +6,7 @@ import ( "time" ) -// 图例只能画一次:横版曾经在渲染器与横版面板里各画一次,而且两处传入的中心带参数还不一致。 +// 图例只能画一次,横版渲染器与面板不得重复绘制。 func TestSolarEclipseMapLegendRendersOnce(t *testing.T) { for _, test := range []struct { name string diff --git a/eclipse/svg/solar_map_limb_panel_test.go b/eclipse/svg/solar_map_limb_panel_test.go new file mode 100644 index 0000000..9eb59b8 --- /dev/null +++ b/eclipse/svg/solar_map_limb_panel_test.go @@ -0,0 +1,203 @@ +package svg + +// 全球日食图开月缘时,面板数值与画出的界线同源;剖面失败时整张图保持圆月面。 + +import ( + "fmt" + "math" + "strings" + "testing" + "time" + + eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/moon/limb" +) + +type failingEclipseLimb struct{} + +func (failingEclipseLimb) LimbRadiusKM(_, _, _ float64) (float64, bool) { return 0, false } + +func TestSolarEclipseMapLunarLimbPanelMatchesLimbResults(t *testing.T) { + date := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) + profile := limb.Default() + circular, ok := SolarEclipseMapSVG(date, SolarEclipseMapSVGOptions{Location: time.UTC}) + if !ok { + t.Fatal("circular map missing") + } + corrected, ok := SolarEclipseMapSVG(date, SolarEclipseMapSVGOptions{Location: time.UTC, LunarLimb: profile}) + if !ok { + t.Fatal("limb map missing") + } + + module := eclipsecore.NewWithInstance(nil).WithLunarLimb(profile) + global, _ := module.SolarEclipseOnDateNASABulletinSplitK(date) + if !global.LunarLimbApplied { + t.Fatal("global limb result not flagged") + } + // 与地图同一组路径选项,带宽才和画出的南北界同源。 + path, ok := module.SolarEclipseCentralPathNASABulletinSplitK(date, eclipsecore.SolarEclipsePathOptions{ + Step: 2 * time.Minute, TargetSpacingKM: 150, SkipCentralBand: true, + }) + if !ok || !path.PathWidthDefined { + t.Fatal("limb central path missing") + } + limbWidth := fmt.Sprintf("%.1f km", path.Greatest.WidthKM) + circularGlobal, _ := eclipsecore.SolarEclipseOnDateNASABulletinSplitK(date) + circularWidth := fmt.Sprintf("%.1f km", circularGlobal.PathWidthKM) + if limbWidth == circularWidth { + t.Fatalf("limb width %s equals circular width; fixture no longer distinguishes them", limbWidth) + } + if !strings.Contains(corrected, limbWidth) || strings.Contains(corrected, circularWidth) { + t.Fatalf("limb map must show width %s and not %s", limbWidth, circularWidth) + } + if !strings.Contains(circular, circularWidth) { + t.Fatalf("circular map must show width %s", circularWidth) + } + + local, ok := module.GeometricLocalSolarEclipseOnDateNASABulletinSplitK(date, global.GreatestLongitude, global.GreatestLatitude, 0) + if !ok || !local.LunarLimbApplied { + t.Fatal("limb local panel result missing or not flagged") + } + if magnitude := fmt.Sprintf("%.4f", local.Magnitude); !strings.Contains(corrected, magnitude) { + t.Fatalf("limb map must show the limb local magnitude %s", magnitude) + } + begin := global.PartialBeginOnEarth.UTC().Format("15:04:05") + if begin == circularGlobal.PartialBeginOnEarth.UTC().Format("15:04:05") { + t.Fatal("limb P1 rounds to the circular value; pick a fixture where it moves") + } + if !strings.Contains(corrected, begin) || strings.Contains(circular, begin) { + t.Fatalf("limb P1 %s must appear only in the limb map", begin) + } + if !strings.Contains(corrected, "按月缘剖面修正全球始末、食甚点站心值、中心带界线、可见域边界") { + t.Fatal("limb map does not declare every corrected layer") + } +} + +// selectiveEclipseLimb 只在位置角 [low, high) 内给出剖面值并报告半径范围;外切扫描靠范围剪枝停在日心方向附近,需要全方向的判据退回圆月面。 +type selectiveEclipseLimb struct{ low, high float64 } + +func (s selectiveEclipseLimb) LimbRadiusKM(longitude, latitude, angle float64) (float64, bool) { + if angle < s.low || angle >= s.high { + return 0, false + } + return limb.Default().LimbRadiusKM(longitude, latitude, angle) +} + +func (selectiveEclipseLimb) RadiusRangeKM() (float64, float64) { return limb.Default().RadiusRangeKM() } + +func (selectiveEclipseLimb) AngleStepDeg() float64 { return limb.Default().AngleStepDeg() } + +func TestSolarEclipseMapFooterDeclaresOnlyCorrectedLayers(t *testing.T) { + date := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) + circular, ok := SolarEclipseMapSVG(date, SolarEclipseMapSVGOptions{Location: time.UTC}) + if !ok { + t.Fatal("circular map missing") + } + // 位置角 45°~90° 只覆盖偏食外边界的接触点:全球始末、站心面板与中心界线都退回圆月面。 + selective, ok := SolarEclipseMapSVG(date, SolarEclipseMapSVGOptions{Location: time.UTC, LunarLimb: selectiveEclipseLimb{45, 90}}) + if !ok { + t.Fatal("selective-limb map missing") + } + if !strings.Contains(selective, "按月缘剖面修正可见域边界(食分线与等时线仍按圆月面)") { + t.Fatal("selective map must declare only the visibility boundary") + } + for _, layer := range []string{"全球始末", "食甚点站心值", "中心带界线"} { + if strings.Contains(selective, layer) { + t.Errorf("selective map declares uncorrected layer %s", layer) + } + } + circularGlobal, _ := eclipsecore.SolarEclipseOnDateNASABulletinSplitK(date) + if width := fmt.Sprintf("%.1f km", circularGlobal.PathWidthKM); !strings.Contains(selective, width) { + t.Errorf("selective map must keep the circular width %s while the limits stay circular", width) + } + if strings.Contains(circular, "按月缘剖面修正") { + t.Error("circular map declares a limb correction") + } +} + +func TestEclipseSVGsWithFailingLimbStayCircular(t *testing.T) { + solarDate := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) + lunarDate := time.Date(2025, time.September, 7, 0, 0, 0, 0, time.UTC) + failing := failingEclipseLimb{} + pairs := map[string][2]func() (string, bool){ + "local solar": { + func() (string, bool) { + return LocalSolarEclipseSVG(solarDate, -96.8, 32.78, 0, LocalSolarEclipseSVGOptions{Location: time.UTC}) + }, + func() (string, bool) { + return LocalSolarEclipseSVG(solarDate, -96.8, 32.78, 0, LocalSolarEclipseSVGOptions{Location: time.UTC, LunarLimb: failing}) + }, + }, + "solar map": { + func() (string, bool) { + return SolarEclipseMapSVG(solarDate, SolarEclipseMapSVGOptions{Location: time.UTC}) + }, + func() (string, bool) { + return SolarEclipseMapSVG(solarDate, SolarEclipseMapSVGOptions{Location: time.UTC, LunarLimb: failing}) + }, + }, + "lunar": { + func() (string, bool) { return LunarEclipseSVG(lunarDate, LunarEclipseSVGOptions{Location: time.UTC}) }, + func() (string, bool) { + return LunarEclipseSVG(lunarDate, LunarEclipseSVGOptions{Location: time.UTC, LunarLimb: failing}) + }, + }, + "lunar map": { + func() (string, bool) { + return LunarEclipseMapSVG(lunarDate, LunarEclipseMapSVGOptions{Location: time.UTC}) + }, + func() (string, bool) { + return LunarEclipseMapSVG(lunarDate, LunarEclipseMapSVGOptions{Location: time.UTC, LunarLimb: failing}) + }, + }, + "lunar detailed": { + func() (string, bool) { + return LunarEclipseDetailedSVG(lunarDate, LunarEclipseDetailedSVGOptions{Location: time.UTC}) + }, + func() (string, bool) { + return LunarEclipseDetailedSVG(lunarDate, LunarEclipseDetailedSVGOptions{Location: time.UTC, LunarLimb: failing}) + }, + }, + } + for name, pair := range pairs { + circular, ok := pair[0]() + if !ok { + t.Fatalf("%s: circular chart missing", name) + } + failed, ok := pair[1]() + if !ok { + t.Fatalf("%s: failing-limb chart missing", name) + } + if failed != circular { + t.Errorf("%s: failing limb profile changed the chart", name) + } + } +} + +// TestSolarEclipseMapContactMarkerMatchesCorrectedTime 检查全球图的 P1/P4 标记与页脚时刻同源: +// 月缘修正过时刻时,标记位置必须取修正后的接触点,而不是圆月面接触点。 +func TestSolarEclipseMapContactMarkerMatchesCorrectedTime(t *testing.T) { + date := time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC) + module := eclipsecore.NewWithInstance(nil).WithLunarLimb(limb.Default()) + global, ok := module.SolarEclipseOnDateNASABulletinSplitK(date) + if !ok { + t.Fatal("global result missing") + } + if !global.LunarLimbApplied || !global.PartialBeginContact.Defined { + t.Fatal("the default limb profile must correct the global contacts") + } + circular, ok := eclipsecore.SolarEclipsePartialFootprintsNASABulletinSplitK(date, eclipsecore.SolarEclipsePartialFootprintOptions{}) + if !ok { + t.Fatal("circular footprints missing") + } + // 修正后的接触点必须真的离开圆月面接触点,否则这条回归就测不到东西。 + moved := math.Hypot( + (global.PartialBeginContact.Longitude-circular.P1.Longitude)*math.Cos(circular.P1.Latitude*math.Pi/180), + global.PartialBeginContact.Latitude-circular.P1.Latitude) + if moved < 1e-4 { + t.Fatalf("corrected P1 did not move (%.6f deg)", moved) + } + if delta := global.PartialBeginOnEarth.Sub(circular.P1.Time).Seconds(); math.Abs(delta) < 0.5 { + t.Fatalf("corrected P1 time moved by only %.3f s", delta) + } +} diff --git a/eclipse/svg/solar_model.go b/eclipse/svg/solar_model.go index 4537fb1..b88ac50 100644 --- a/eclipse/svg/solar_model.go +++ b/eclipse/svg/solar_model.go @@ -50,6 +50,7 @@ func localSolarEclipseInfoFieldsFromBasic( visibleThreshold := localSolarEclipseVisibilityThreshold(height, lat) return LocalSolarEclipseInfo{ Model: mapBasicSolarEclipseModel(result.Model), + LunarLimbApplied: result.LunarLimbApplied, Type: mapBasicSolarEclipseType(result.Type), Longitude: lon, Latitude: lat, diff --git a/eclipse/svg/testdata/semianalytic/lunar_geocentric_block.json b/eclipse/svg/testdata/semianalytic/lunar_geocentric_block.json new file mode 100644 index 0000000..ca8f9bc --- /dev/null +++ b/eclipse/svg/testdata/semianalytic/lunar_geocentric_block.json @@ -0,0 +1,6 @@ +{ + "2025-03-14/dec": "+02°40\u0026#39;54.6\u0026#34;", + "2025-03-14/ra": "11h38m23.0s", + "2026-03-03/dec": "+06°24\u0026#39;05.4\u0026#34;", + "2026-03-03/ra": "10h56m15.0s" +} diff --git a/eclipse/svg/testdata/semianalytic/lunar_penumbral_phase_digest.json b/eclipse/svg/testdata/semianalytic/lunar_penumbral_phase_digest.json new file mode 100644 index 0000000..b2b8e4a --- /dev/null +++ b/eclipse/svg/testdata/semianalytic/lunar_penumbral_phase_digest.json @@ -0,0 +1,30 @@ +{ + "2016-08-18 半影 1200x800 zh UTC/bytes": 241472, + "2016-08-18 半影 1200x800 zh UTC/md5": "b74c49dceb93ecb0d14054d46d29c2ac", + "2016-08-18 详细版 en/bytes": 261576, + "2016-08-18 详细版 en/md5": "d4799bb8e1ca37b62a720bba373b94eb", + "2025-03-14 全食 1200x800 zh UTC/bytes": 255228, + "2025-03-14 全食 1200x800 zh UTC/md5": "35803c58d004859285d2e6f5d2ae7f44", + "2025-03-14 全食 960x640 zh CST/bytes": 250322, + "2025-03-14 全食 960x640 zh CST/md5": "314dcafb7d30d76ae3b19fd6a048c9a8", + "2026-03-03 全食 1200x800 en UTC/bytes": 247498, + "2026-03-03 全食 1200x800 en UTC/md5": "3c47a2d2610542b12179543598f36531", + "2026-03-03 全食 1200x800 zh UTC/bytes": 247428, + "2026-03-03 全食 1200x800 zh UTC/md5": "e64e51c143f9190ac013f3b6bf329f70", + "2026-03-03 全食 960x640 en CST/bytes": 243630, + "2026-03-03 全食 960x640 en CST/md5": "5eb2a1bc355201f6e1cb6027c496a6c8", + "2026-03-03 全食 960x640 zh CST/bytes": 243568, + "2026-03-03 全食 960x640 zh CST/md5": "162ee33df427370b2d3055e66b8c2a00", + "2026-03-03 北极投影 zh/bytes": 159015, + "2026-03-03 北极投影 zh/md5": "d725d389b047a189a6b3b51dcb6d1291", + "2026-03-03 南极投影 zh/bytes": 84949, + "2026-03-03 南极投影 zh/md5": "d6ae40520fab72b9885751fb7907c3de", + "2026-03-03 详细版 en/bytes": 271080, + "2026-03-03 详细版 en/md5": "9f20d5c3a7408d0f8a0071ad08357e75", + "2026-03-03 详细版 zh/bytes": 270931, + "2026-03-03 详细版 zh/md5": "e938aa1d6eb4221eefdd1d0675ffb70f", + "2029-01-01 全食 960x640 en CST/bytes": 231513, + "2029-01-01 全食 960x640 en CST/md5": "2118fff17ef4dabfd09ac59bef4794e9", + "2029-01-01 详细版 zh/bytes": 257099, + "2029-01-01 详细版 zh/md5": "d9a1acb5188eb92706a00012dfc78448" +} diff --git a/eclipse/svg/transit_local.go b/eclipse/svg/transit_local.go new file mode 100644 index 0000000..4b9b766 --- /dev/null +++ b/eclipse/svg/transit_local.go @@ -0,0 +1,438 @@ +package svg + +import ( + "fmt" + "html" + "math" + "strings" + "time" + + "b612.me/astro" + "b612.me/astro/basic" + "b612.me/astro/internal/svgchart" + "b612.me/astro/internal/timenote" +) + +const ( + transitLocalDefaultWidth = 960 + transitLocalDefaultHeight = 680 +) + +// TransitLocalSVGOptions 控制站心凌日日面图。 +// TransitLocalSVGOptions controls a topocentric planetary-transit solar-disk chart. +type TransitLocalSVGOptions struct { + // Width 和 Height 是 SVG 画布尺寸;宽度小于 640 或高度小于 480 时使用 960x680 默认值。 + // Width and Height are SVG canvas dimensions. Width values below 640 and height values below 480 use the 960x680 defaults. + Width int + Height int + // Step 是行星轨迹采样步长;非正值使用两分钟。 + // Step is the planet-track sampling step. Non-positive values use two minutes. + Step time.Duration + // Language 为 "en"(不区分大小写)时使用英文,否则使用中文。 + // Language uses English for "en" (case-insensitive) and Chinese otherwise. + Language string + // Location 控制显示的事件时刻;nil 使用 date.Location()。 + // Location controls displayed event times. Nil uses date.Location(). + Location *time.Location + // TimeScale 选择图中时刻的时标:零值 UTC;TimeScaleUT1 要求 Location 为 nil 或 UTC,否则返回 false。 + // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 requires Location to be nil or UTC, otherwise the renderer returns false. + TimeScale astro.TimeScale + // 空文本字段使用本地化的自动标签。 + // Empty text fields use localized automatic labels. + Title string + FooterNote string +} + +// TransitLocalSVG 绘制距 date 最近一次水星或金星凌日在观测地(经度 lon、纬度 lat,东经北纬为正,海拔 height 米) +// 的站心日面图:上北左东的天空视向,画出行星中心轨迹、各接触与凌甚时的行星圆面,以及接触时刻表。 +// 轨迹上太阳中心在视地平下(计入标准大气折射)的部分画成灰色虚线。该地几何上看不到凌日、找不到凌日或选项无效时返回 false。 +// TransitLocalSVG renders the topocentric solar-disk view of the Mercury or Venus transit closest to date, seen from +// longitude lon and latitude lat (east and north positive) at height metres: north up and east left as on the sky, with +// the planet-centre track, the planet disk at each contact and at greatest, and a contact table. Track parts where the +// Sun's centre is below the apparent horizon (standard refraction) are drawn grey and dashed. It returns false when the +// site sees no geometric transit, no transit is found or the options are invalid. +func TransitLocalSVG(planet TransitPlanet, date time.Time, lon, lat, height float64, options TransitLocalSVGOptions) (string, bool) { + transit, ok := closestPlanetTransit(planet, date) + if !ok { + return "", false + } + if options.TimeScale == astro.TimeScaleUT1 { + if options.Location != nil && options.Location != time.UTC { + return "", false + } + options.Location = time.UTC + } + local := basic.PlanetTransitLocal(transit, basic.Observer{Longitude: lon, Latitude: lat, Height: height}) + if !local.Valid { + return "", false + } + if options.Step <= 0 { + options.Step = 2 * time.Minute + } + frames := basic.PlanetTransitLocalFrames(transit, local, options.Step.Hours()/24) + if len(frames) < 2 { + return "", false + } + options = normalizeTransitLocalSVGOptions(date, options) + return renderTransitLocalSVG(planet, local, frames, options), true +} + +func normalizeTransitLocalSVGOptions(date time.Time, options TransitLocalSVGOptions) TransitLocalSVGOptions { + if options.Width < 640 { + options.Width = transitLocalDefaultWidth + } + if options.Height < 480 { + options.Height = transitLocalDefaultHeight + } + if strings.EqualFold(options.Language, "en") { + options.Language = "en" + } else { + options.Language = "zh" + } + if options.Location == nil { + options.Location = date.Location() + } + return options +} + +// transitLocalEvent 是日面上标注的一个接触或凌甚。 +type transitLocalEvent struct { + label, name string + contact basic.PlanetTransitLocalContact + separation float64 +} + +func transitLocalEvents(local basic.PlanetTransitLocalResult, language string) []transitLocalEvent { + names := []string{"一触", "二触", "凌甚", "三触", "四触"} + if language == "en" { + names = []string{"C1", "C2", "Greatest", "C3", "C4"} + } + external := local.SunSemidiameterArcsec + local.PlanetSemidiameterArcsec + internal := local.SunSemidiameterArcsec - local.PlanetSemidiameterArcsec + candidates := []transitLocalEvent{ + {"C1", names[0], local.ExternalIngress, external}, + {"C2", names[1], local.InternalIngress, internal}, + {"G", names[2], local.Greatest, local.MinimumSeparationArcsec}, + {"C3", names[3], local.InternalEgress, internal}, + {"C4", names[4], local.ExternalEgress, external}, + } + events := candidates[:0] + for _, event := range candidates { + if event.contact.Valid { + events = append(events, event) + } + } + return events +} + +// transitLocalSunUp 判断太阳中心几何高度 altitude 是否在视地平上(标准大气折射)。 +func transitLocalSunUp(altitude float64) bool { + return altitude > -basic.RefractionFromApparentAltitude(0, transitMapRefractionPressureHPa, transitMapRefractionTemperatureC) +} + +func transitDisplayTime(value time.Time, scale astro.TimeScale, location *time.Location) time.Time { + if value.IsZero() { + return value + } + if scale == astro.TimeScaleUT1 { + return basic.UT1FromUTC(value) + } + return value.In(location) +} + +func renderTransitLocalSVG( + planet TransitPlanet, + local basic.PlanetTransitLocalResult, + frames []basic.PlanetTransitLocalFrame, + options TransitLocalSVGOptions, +) string { + width, height := float64(options.Width), float64(options.Height) + at := func(jd float64) time.Time { + return transitDisplayTime(basic.JD2DateByZone(jd, time.UTC, false), options.TimeScale, options.Location) + } + title := options.Title + if title == "" { + date := at(local.Greatest.JD).Format("2006-01-02") + if options.Language == "en" { + title = fmt.Sprintf("%s Transit of %s, Local Circumstances", date, transitMapPlanetName(planet, "en")) + } else { + title = fmt.Sprintf("%s %s凌日站心图", date, transitMapPlanetName(planet, "zh")) + } + } + site := transitLocalSiteText(local.Observer, options.Language) + summary := transitLocalSummaryText(local, options.Language) + + var b strings.Builder + fmt.Fprintf(&b, ``, + options.Width, options.Height, options.Width, options.Height, html.EscapeString(title)) + b.WriteString(``) + b.WriteString(``) + fmt.Fprintf(&b, ``, width-44, height-36) + titleText := title + if options.Title != "" { + titleText = svgchart.EllipsizeText(title, width-52, 24) + } + fmt.Fprintf(&b, `%s`, + width/2, html.EscapeString(titleText)) + for index, line := range []string{site, summary} { + fmt.Fprintf(&b, `%s`, + width/2, 72+float64(index)*19, html.EscapeString(line)) + } + + // 日面区占左侧,接触表占右侧。 + panelWidth := 300.0 + top, bottom := 112.0, height-86 + diskLeft, diskRight := 46.0, width-panelWidth-60 + cx, cy := (diskLeft+diskRight)/2, (top+bottom)/2 + extent := local.SunSemidiameterArcsec + 2.2*local.PlanetSemidiameterArcsec + 60 + scale := math.Min((diskRight-diskLeft)/2, (bottom-top)/2) / extent + mapX := func(x float64) float64 { return cx - x*scale } // 东在左 + mapY := func(y float64) float64 { return cy - y*scale } + sunRadius := local.SunSemidiameterArcsec * scale + fmt.Fprintf(&b, ``, cx, cy, sunRadius) + writeTransitLocalAxes(&b, cx, cy, sunRadius, options.Language) + writeTransitLocalTrack(&b, frames, mapX, mapY) + + events := transitLocalEvents(local, options.Language) + planetRadius := math.Max(local.PlanetSemidiameterArcsec*scale, 2) + table := &svgchart.LabelTable{} + for _, event := range events { + x, y := transitLocalEventPosition(frames, event.contact.JD, mapX, mapY) + fill, stroke := "#1d1d1d", "#000000" + if !transitLocalSunUp(event.contact.SunAltitudeDeg) { + fill, stroke = "#9a9a9a", "#6d6d6d" + } + fmt.Fprintf(&b, ``, + strings.ToLower(event.label), x, y, planetRadius, fill, stroke) + table.Reserve(x-planetRadius, y-planetRadius, 2*planetRadius, 2*planetRadius) + } + for _, event := range events { + x, y := transitLocalEventPosition(frames, event.contact.JD, mapX, mapY) + // 标签放在远离日心的一侧,避开日面内的轨迹与圆面。 + dx, dy := x-cx, y-cy + norm := math.Hypot(dx, dy) + if norm < 1e-6 { + dx, dy, norm = 0, -1, 1 + } + text := event.name + " " + at(event.contact.JD).Format("15:04:05") + var placement svgchart.LabelPlacement + placed := false + // 相邻接触相隔仅数分钟时近处位次全被占,逐圈外推并画引线,不得丢标签。 + for ring := 0; ring < 6 && !placed; ring++ { + offset := planetRadius + 12 + float64(ring)*16 + candidates := []svgchart.LabelPlacement{} + for _, turn := range []float64{0, 0.5, -0.5, 1, -1} { + ux := dx/norm*math.Cos(turn) - dy/norm*math.Sin(turn) + uy := dx/norm*math.Sin(turn) + dy/norm*math.Cos(turn) + anchor := "start" + if ux < -0.3 { + anchor = "end" + } else if ux < 0.3 { + anchor = "middle" + } + candidate := svgchart.LabelPlacement{X: x + ux*offset, Y: y + uy*offset + 4, Anchor: anchor} + // 标签平移进白色图框内、接触表左侧,不能伸出画面。 + box := svgchart.LabelTextBox(candidate.X, candidate.Y, 11, text, anchor) + candidate.X += math.Max(0, 30-box.X) - math.Max(0, box.X+box.Width-(width-panelWidth-44)) + candidate.Y += math.Max(0, top-8-box.Y) - math.Max(0, box.Y+box.Height-(height-30)) + candidates = append(candidates, candidate) + } + placement, placed = table.Place(text, 11, candidates) + if placed && transitLocalLabelGap(placement, text, x, y) > planetRadius+16 { + // 引线连到标签框上离接触点最近的位置。 + box := svgchart.LabelTextBox(placement.X, placement.Y, 11, text, placement.Anchor) + endX := math.Max(box.X, math.Min(x, box.X+box.Width)) + endY := math.Max(box.Y, math.Min(y, box.Y+box.Height)) + fmt.Fprintf(&b, ``, + x, y, endX, endY) + } + } + if !placed { + continue + } + fmt.Fprintf(&b, `%s`, + placement.X, placement.Y, placement.Anchor, html.EscapeString(text)) + } + + writeTransitLocalContactTable(&b, local, events, at, width-panelWidth-36, top+10, panelWidth, options.Language) + writeTransitLocalFooter(&b, local, options, width, height) + b.WriteString(``) + return b.String() +} + +// transitLocalLabelGap 是接触点到标签框的最近距离。 +func transitLocalLabelGap(placement svgchart.LabelPlacement, text string, x, y float64) float64 { + box := svgchart.LabelTextBox(placement.X, placement.Y, 11, text, placement.Anchor) + return math.Hypot(x-math.Max(box.X, math.Min(x, box.X+box.Width)), y-math.Max(box.Y, math.Min(y, box.Y+box.Height))) +} + +func transitLocalSiteText(observer basic.Observer, language string) string { + ew, ns := "E", "N" + if language != "en" { + ew, ns = "东经", "北纬" + } + lon, lat := observer.Longitude, observer.Latitude + if lon < 0 { + ew = map[bool]string{true: "W", false: "西经"}[language == "en"] + lon = -lon + } + if lat < 0 { + ns = map[bool]string{true: "S", false: "南纬"}[language == "en"] + lat = -lat + } + if language == "en" { + return fmt.Sprintf("Site %.4f°%s %.4f°%s, height %.0f m", lon, ew, lat, ns, observer.Height) + } + return fmt.Sprintf("观测地 %s %.4f° %s %.4f°,海拔 %.0f 米", ew, lon, ns, lat, observer.Height) +} + +func transitLocalSummaryText(local basic.PlanetTransitLocalResult, language string) string { + kind := map[bool]string{true: "完整凌日", false: "掠凌(无内切接触)"}[local.HasInternal] + visible := map[bool]string{true: "可见", false: "全程太阳在地平下"}[local.Visible] + if language == "en" { + kind = map[bool]string{true: "full transit", false: "grazing transit (no internal contacts)"}[local.HasInternal] + visible = map[bool]string{true: "visible", false: "Sun below the horizon throughout"}[local.Visible] + return fmt.Sprintf("Topocentric %s, %s | minimum separation %.1f″ | Sun %.1f″ | planet %.2f″", + kind, visible, local.MinimumSeparationArcsec, local.SunSemidiameterArcsec, local.PlanetSemidiameterArcsec) + } + return fmt.Sprintf("站心%s,%s | 最小角距 %.1f″ | 太阳视半径 %.1f″ | 行星视半径 %.2f″", + kind, visible, local.MinimumSeparationArcsec, local.SunSemidiameterArcsec, local.PlanetSemidiameterArcsec) +} + +// writeTransitLocalAxes 画过日心的南北、东西虚线与方向字。 +func writeTransitLocalAxes(b *strings.Builder, cx, cy, radius float64, language string) { + reach := radius + 18 + fmt.Fprintf(b, ``, cx, cy-reach, cx, cy+reach) + fmt.Fprintf(b, ``, cx-reach, cy, cx+reach, cy) + labels := [4]string{"北", "南", "东", "西"} + if language == "en" { + labels = [4]string{"N", "S", "E", "W"} + } + for index, item := range []struct{ x, y float64 }{ + {cx, cy - reach - 6}, {cx, cy + reach + 16}, {cx - reach - 12, cy + 5}, {cx + reach + 12, cy + 5}, + } { + fmt.Fprintf(b, `%s`, + item.x, item.y, labels[index]) + } +} + +// writeTransitLocalTrack 画行星中心轨迹:太阳在视地平上的段为实线,地平下为灰色虚线。 +func writeTransitLocalTrack(b *strings.Builder, frames []basic.PlanetTransitLocalFrame, mapX, mapY func(float64) float64) { + start := 0 + for index := 1; index <= len(frames); index++ { + if index < len(frames) && transitLocalSunUp(frames[index].SunAltitudeDeg) == transitLocalSunUp(frames[start].SunAltitudeDeg) { + continue + } + // 段尾取下一段首点,相邻两段首尾相接。 + end := index + if end == len(frames) { + end-- + } + class, color, dash := "transit-track", "#24518a", "none" + if !transitLocalSunUp(frames[start].SunAltitudeDeg) { + class, color, dash = "transit-track-below-horizon", "#8a8a8a", "5 4" + } + fmt.Fprintf(b, ``, color, dash) + start = index + } +} + +// transitLocalEventPosition 取时刻最接近 jd 的采样帧位置;接触与凌甚时刻本身在采样里。 +func transitLocalEventPosition(frames []basic.PlanetTransitLocalFrame, jd float64, mapX, mapY func(float64) float64) (float64, float64) { + best := 0 + for index := range frames { + if math.Abs(frames[index].JD-jd) < math.Abs(frames[best].JD-jd) { + best = index + } + } + return mapX(frames[best].PlanetXArcsec), mapY(frames[best].PlanetYArcsec) +} + +func writeTransitLocalContactTable( + b *strings.Builder, + local basic.PlanetTransitLocalResult, + events []transitLocalEvent, + at func(float64) time.Time, + x, y, width float64, + language string, +) { + heading := [4]string{"接触时刻", "时刻", "太阳高度", "方位"} + if language == "en" { + heading = [4]string{"Contacts", "Time", "Sun alt.", "Az."} + } + fmt.Fprintf(b, `%s`, x, y, heading[0]) + columns := []float64{x, x + 78, x + 176, x + 252} + rowY := y + 26 + for index, text := range []string{"", heading[1], heading[2], heading[3]} { + fmt.Fprintf(b, `%s`, columns[index], rowY, text) + } + fmt.Fprintf(b, ``, x, rowY+6, x+width-8, rowY+6) + for _, event := range events { + rowY += 22 + fill := "#222" + if !transitLocalSunUp(event.contact.SunAltitudeDeg) { + fill = "#8a8a8a" + } + cells := []string{ + event.name, + at(event.contact.JD).Format("15:04:05"), + fmt.Sprintf("%+.1f°", event.contact.SunAltitudeDeg), + fmt.Sprintf("%.1f°", event.contact.SunAzimuthDeg), + } + for index, text := range cells { + fmt.Fprintf(b, `%s`, + columns[index], rowY, fill, html.EscapeString(text)) + } + } + rowY += 30 + lines := []string{} + duration := time.Duration((local.ExternalEgress.JD - local.ExternalIngress.JD) * 86400 * float64(time.Second)).Round(time.Second) + if language == "en" { + lines = append(lines, "Duration C1–C4 "+duration.String()) + } else { + lines = append(lines, "一触至四触历时 "+duration.String()) + } + for _, item := range []struct { + jd float64 + zh, en string + }{{local.Sunrise, "凌日中日出 ", "Sunrise during transit "}, {local.Sunset, "凌日中日落 ", "Sunset during transit "}} { + if item.jd == 0 { + continue + } + label := item.zh + if language == "en" { + label = item.en + } + lines = append(lines, label+at(item.jd).Format("15:04:05")) + } + for _, line := range lines { + fmt.Fprintf(b, `%s`, x, rowY, html.EscapeString(line)) + rowY += 19 + } +} + +func writeTransitLocalFooter(b *strings.Builder, local basic.PlanetTransitLocalResult, options TransitLocalSVGOptions, width, height float64) { + note := options.FooterNote + if note == "" { + if options.Language == "en" { + note = "Sky view, north up and east left. Topocentric contacts; Sun altitudes are geometric centre altitudes, grey parts have the Sun's centre below the apparent horizon (standard refraction)." + } else { + note = "天空视向,上北左东;站心接触时刻,太阳高度为日心几何高度,灰色部分太阳中心在视地平下(计入标准大气折射)。" + } + } + scaleNote := timenote.Scale(options.TimeScale, basic.JD2DateByZone(local.Greatest.JD, time.UTC, false), options.Location, options.Language) + lines := svgchart.FooterLinesWithScale(svgchart.WrapText(note, width-80, 12), scaleNote, width-80, 12, 3) + baselines, _ := svgchart.FooterBlock(len(lines), height, 12, svgchart.FooterLineHeight(12), svgchart.FooterBottomPadding(12)+18) + for index, line := range lines { + fmt.Fprintf(b, `%s`, + baselines[index], html.EscapeString(line)) + } +} diff --git a/eclipse/svg/transit_local_test.go b/eclipse/svg/transit_local_test.go new file mode 100644 index 0000000..edc5402 --- /dev/null +++ b/eclipse/svg/transit_local_test.go @@ -0,0 +1,82 @@ +package svg + +import ( + "regexp" + "strconv" + "strings" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/internal/svgchart" +) + +func TestTransitLocalSVG(t *testing.T) { + venus := time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC) + chart, ok := TransitLocalSVG(TransitVenus, venus, 116.4, 39.9, 50, TransitLocalSVGOptions{Location: time.FixedZone("UTC+8", 8*3600)}) + if !ok { + t.Fatal("Beijing 2012 render failed") + } + for _, want := range []string{"金星凌日站心图", `class="transit-track"`, "transit-planet-c1", "transit-planet-c2", + "transit-planet-g", "transit-planet-c3", "transit-planet-c4", "一触 06:10:13", "四触 12:49:17"} { + if !strings.Contains(chart, want) { + t.Fatalf("Beijing: missing %s", want) + } + } + if strings.Contains(chart, "transit-track-below-horizon") || strings.Contains(chart, "NaN") { + t.Fatal("Beijing sees the whole transit in daylight") + } + + // 伦敦:日出时凌日已在进行,日出前的轨迹画成地平下虚线。 + chart, ok = TransitLocalSVG(TransitVenus, venus, -0.1, 51.5, 20, TransitLocalSVGOptions{Language: "en"}) + if !ok || !strings.Contains(chart, "transit-track-below-horizon") || !strings.Contains(chart, "Sunrise during transit") || + !strings.Contains(chart, "Local Circumstances") { + t.Fatal("London must show the below-horizon track and sunrise row in English") + } + + // 1999 水星掠凌,南纬 60° 只有一触、凌甚、四触。 + chart, ok = TransitLocalSVG(TransitMercury, time.Date(1999, 11, 15, 0, 0, 0, 0, time.UTC), 150, -60, 0, TransitLocalSVGOptions{}) + if !ok || !strings.Contains(chart, "掠凌") || strings.Contains(chart, "transit-planet-c2") || strings.Contains(chart, "transit-planet-c3") { + t.Fatal("grazing site must omit internal contacts") + } + if !strings.Contains(chart, "1999-11-15") { + t.Fatal("same-day query must find the 1999-11-15 transit") + } + + // 一、二触相隔约 2 分钟,二触标签不得被避让丢弃。 + chart, ok = TransitLocalSVG(TransitMercury, time.Date(2032, 11, 13, 0, 0, 0, 0, time.UTC), 116.4074, 39.9042, 0, + TransitLocalSVGOptions{Location: time.FixedZone("UTC+8", 8*3600)}) + if !ok { + t.Fatal("Beijing 2032 render failed") + } + for _, want := range []string{">一触 ", ">二触 ", ">凌甚 ", ">三触 ", ">四触 "} { + if strings.Count(chart, want) != 1 { + t.Fatalf("Beijing 2032: label %s must appear exactly once on the disk", want) + } + } + for _, match := range regexp.MustCompile(`class="transit-event-label" x="([0-9.-]+)" y="([0-9.-]+)"[^>]*text-anchor="(\w+)">([^<]*)<`).FindAllStringSubmatch(chart, -1) { + x, _ := strconv.ParseFloat(match[1], 64) + y, _ := strconv.ParseFloat(match[2], 64) + if box := svgchart.LabelTextBox(x, y, 11, match[4], match[3]); box.X < 30 || box.X+box.Width > 960-344 { + t.Fatalf("Beijing 2032: label %q leaves the disk frame (box x %.1f..%.1f)", match[4], box.X, box.X+box.Width) + } + } +} + +func TestTransitLocalSVGOptions(t *testing.T) { + venus := time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC) + if _, ok := TransitLocalSVG(3, venus, 0, 0, 0, TransitLocalSVGOptions{}); ok { + t.Fatal("unsupported planet must fail") + } + if _, ok := TransitLocalSVG(TransitVenus, venus, 0, 0, 0, TransitLocalSVGOptions{ + TimeScale: astro.TimeScaleUT1, Location: time.FixedZone("CST", 8*3600)}); ok { + t.Fatal("UT1 with non-UTC location must fail") + } + if _, ok := TransitLocalSVG(TransitVenus, venus, 0, 95, 0, TransitLocalSVGOptions{}); ok { + t.Fatal("invalid latitude must fail") + } + chart, ok := TransitLocalSVG(TransitVenus, venus, 116.4, 39.9, 0, TransitLocalSVGOptions{TimeScale: astro.TimeScaleUT1, Title: "X"}) + if !ok || !strings.Contains(chart, ">X<") || !strings.Contains(chart, "UT1") { + t.Fatal("UT1 labels and custom title") + } +} diff --git a/eclipse/svg/transit_map.go b/eclipse/svg/transit_map.go new file mode 100644 index 0000000..8306a3e --- /dev/null +++ b/eclipse/svg/transit_map.go @@ -0,0 +1,536 @@ +package svg + +import ( + "fmt" + "html" + "math" + "strings" + "time" + + "b612.me/astro" + "b612.me/astro/basic" + "b612.me/astro/internal/geodata" + "b612.me/astro/internal/svgchart" + "b612.me/astro/internal/svgmap" + "b612.me/astro/internal/timenote" +) + +const ( + transitMapDefaultWidth = 960 + transitMapDefaultHeight = 640 +) + +// TransitPlanet 选择凌日行星 / selects the transiting planet. +type TransitPlanet int + +const ( + // TransitMercury 是水星凌日 / Mercury transit. + TransitMercury TransitPlanet = 1 + // TransitVenus 是金星凌日 / Venus transit. + TransitVenus TransitPlanet = 2 +) + +// TransitIsochrones 是等时线开关的位组合 / bit set of contact isochrones. +type TransitIsochrones uint8 + +const ( + // TransitIsochroneC1 画一触等时线 / draw C1 isochrones. + TransitIsochroneC1 TransitIsochrones = 1 << iota + // TransitIsochroneC2 画二触等时线;掠凌没有内切接触时忽略 / draw C2 isochrones, ignored for a grazing transit. + TransitIsochroneC2 + // TransitIsochroneC3 画三触等时线;掠凌没有内切接触时忽略 / draw C3 isochrones, ignored for a grazing transit. + TransitIsochroneC3 + // TransitIsochroneC4 画四触等时线 / draw C4 isochrones. + TransitIsochroneC4 + // TransitIsochroneExternal 是一触与四触 / C1 and C4. + TransitIsochroneExternal = TransitIsochroneC1 | TransitIsochroneC4 + // TransitIsochroneAll 是全部四个接触 / all four contacts. + TransitIsochroneAll = TransitIsochroneC1 | TransitIsochroneC2 | TransitIsochroneC3 | TransitIsochroneC4 +) + +// TransitMapSVGOptions 控制全球凌日可见图。 +// TransitMapSVGOptions controls a global planetary-transit visibility map. +type TransitMapSVGOptions struct { + // Width 和 Height 是 SVG 画布尺寸;宽度小于 640 或高度小于 420 时使用 960x640 默认值。 + // Width and Height are SVG canvas dimensions. Width values below 640 and height values below 420 use the 960x640 defaults. + Width int + Height int + // Language 为 "en"(不区分大小写)时使用英文,否则使用中文。 + // Language uses English for "en" (case-insensitive) and Chinese otherwise. + Language string + // Location 控制显示的事件时刻;nil 使用 date.Location()。 + // Location controls displayed event times. Nil uses date.Location(). + Location *time.Location + // TimeScale 选择图中时刻的时标:零值 UTC;TimeScaleUT1 要求 Location 为 nil 或 UTC,否则返回 false。 + // TimeScale selects the label scale: the zero value is UTC; TimeScaleUT1 requires Location to be nil or UTC, otherwise the renderer returns false. + TimeScale astro.TimeScale + // Projection 选择地图投影;零值使用等经纬投影,正射投影视点取凌甚时的太阳直射点。 + // Projection selects the map projection. The zero value uses equirectangular; orthographic centres on the subsolar point at greatest. + Projection EclipseMapProjection + // Isochrones 选择要画的站心接触等时线,可按位组合;零值不画(默认关闭,需显式开启)。 + // Isochrones selects the topocentric contact isochrones to draw and may combine flags. The zero value draws none (off by default). + Isochrones TransitIsochrones + // IsochroneStep 是等时线的时间间隔;非正值按行星自动取(水星 20 秒、金星 1 分钟)。 + // IsochroneStep is the isochrone spacing. Non-positive values select 20 s for Mercury and 1 min for Venus. + IsochroneStep time.Duration + // 空文本字段使用本地化的自动标签。 + // Empty text fields use localized automatic labels. + Title string + FooterNote string +} + +// TransitMapSVG 绘制距 date 最近一次水星或金星凌日的全球可见图:按一触、凌甚、四触太阳中心在视地平上 +// (计入标准大气折射)的区域分区;Options.Isochrones 显式开启时叠加所选接触的站心等时线。找不到凌日或选项无效时返回 false。 +// TransitMapSVG renders the global visibility of the Mercury or Venus transit closest to date: regions follow where the +// Sun's centre is above the apparent horizon (standard refraction) at C1, greatest and C4. Topocentric isochrones of the +// selected contacts are overlaid only when Options.Isochrones enables them. It returns false when no transit is found +// or the options are invalid. +func TransitMapSVG(planet TransitPlanet, date time.Time, options TransitMapSVGOptions) (string, bool) { + transit, ok := closestPlanetTransit(planet, date) + if !ok || !validEclipseMapProjection(options.Projection) { + return "", false + } + if options.TimeScale == astro.TimeScaleUT1 { + if options.Location != nil && options.Location != time.UTC { + return "", false + } + options.Location = time.UTC + } + options = normalizeTransitMapSVGOptions(date, options) + if !transit.HasInternal { + options.Isochrones &^= TransitIsochroneC2 | TransitIsochroneC3 + } + options.Isochrones &= TransitIsochroneAll + if options.IsochroneStep <= 0 { + options.IsochroneStep = time.Minute + if planet == TransitMercury { + options.IsochroneStep = 20 * time.Second + } + } + projection := internalEclipseMapProjection(options.Projection) + if projection == "" { + projection = svgmap.ProjectionEquirectangular + } + return renderTransitMapSVG(planet, transit, options, projection), true +} + +// closestPlanetTransit 返回距 date 最近的一次地心凌日;行星无效或找不到时第二个返回值为 false。 +func closestPlanetTransit(planet TransitPlanet, date time.Time) (basic.PlanetTransitResult, bool) { + jd := basic.Date2JD(date.UTC()) + var transit basic.PlanetTransitResult + switch planet { + case TransitMercury: + transit = basic.ClosestMercuryTransit(jd) + case TransitVenus: + transit = basic.ClosestVenusTransit(jd) + default: + return transit, false + } + return transit, transit.Valid +} + +func normalizeTransitMapSVGOptions(date time.Time, options TransitMapSVGOptions) TransitMapSVGOptions { + if options.Width < 640 { + options.Width = transitMapDefaultWidth + } + if options.Height < 420 { + options.Height = transitMapDefaultHeight + } + if strings.EqualFold(options.Language, "en") { + options.Language = "en" + } else { + options.Language = "zh" + } + if options.Location == nil { + options.Location = date.Location() + } + return options +} + +// transitMapContacts 是地心一触、凌甚、四触的 UTC 时刻。 +type transitMapContacts struct { + start, greatest, end time.Time + internalStart, internalEnd time.Time + hasInternal bool +} + +func transitMapContactsOf(transit basic.PlanetTransitResult) transitMapContacts { + at := func(jd float64) time.Time { return basic.JD2DateByZone(jd, time.UTC, false) } + contacts := transitMapContacts{ + start: at(transit.ExternalIngress), + greatest: at(transit.Greatest), + end: at(transit.ExternalEgress), + hasInternal: transit.HasInternal, + } + if transit.HasInternal { + contacts.internalStart, contacts.internalEnd = at(transit.InternalIngress), at(transit.InternalEgress) + } + return contacts +} + +func renderTransitMapSVG( + planet TransitPlanet, + transit basic.PlanetTransitResult, + options TransitMapSVGOptions, + projection svgmap.Projection, +) string { + contacts := transitMapContactsOf(transit) + // 底部比月食图多留两行:图例下要放可折行的说明与时标声明。 + frame := eclipseMapFrame(options.Width, options.Height, projection, 142, 122) + if projection == svgmap.ProjectionOrthographic { + center := solarEclipseSubsolarPoint(contacts.greatest) + frame.CenterLongitude, frame.CenterLatitude = center.Longitude, center.Latitude + } + title := options.Title + if title == "" { + date := transitMapDisplayTime(contacts.greatest, options).Format("2006-01-02") + if options.Language == "en" { + title = fmt.Sprintf("%s Transit of %s Global Visibility", date, transitMapPlanetName(planet, "en")) + } else { + title = fmt.Sprintf("%s %s凌日全球可见图", date, transitMapPlanetName(planet, "zh")) + } + } + + var b strings.Builder + fmt.Fprintf(&b, ``, + options.Width, options.Height, options.Width, options.Height, html.EscapeString(title)) + b.WriteString(``) + writeTransitMapDefinitions(&b, frame, contacts) + b.WriteString(``) + b.WriteString(``) + fmt.Fprintf(&b, ``, + options.Width-44, options.Height-36) + titleText := title + if options.Title != "" { + titleText = svgchart.EllipsizeText(title, float64(options.Width)-52, 24) + } + fmt.Fprintf(&b, `%s`, + float64(options.Width)/2, html.EscapeString(titleText)) + writeTransitMapSummary(&b, transit, contacts, options) + + frame.WriteOcean(&b) + frame.WriteGraticule(&b, "transit-map-clip") + frame.WriteLand(&b, "transit-map-clip") + writeTransitMapRegions(&b, frame, contacts) + if options.Isochrones != 0 { + writeTransitMapIsochrones(&b, frame, transit, options) + } + frame.WriteFrame(&b) + legendRows := writeTransitMapLegend(&b, frame, float64(options.Width), options) + writeTransitMapFooter(&b, frame, options, projection, contacts.greatest, legendRows) + b.WriteString(``) + return b.String() +} + +func transitMapPlanetName(planet TransitPlanet, language string) string { + if language == "en" { + if planet == TransitMercury { + return "Mercury" + } + return "Venus" + } + if planet == TransitMercury { + return "水星" + } + return "金星" +} + +// transitMapDisplayTime 把 UTC 时刻换成图上显示的时刻:UT1 时标给 UT1 读数,否则换到 Location。 +func transitMapDisplayTime(value time.Time, options TransitMapSVGOptions) time.Time { + return transitDisplayTime(value, options.TimeScale, options.Location) +} + +// transitMapSunUpPath 返回 value 时刻太阳中心在视地平上的区域面路径(evenodd)与地平边界。 +// 视地平计入标准大气折射:太阳中心距直射点小于 90°+34′ 即可见,太阳视差 8.8″ 忽略。 +// 半径超过 90° 的球冠环在拆分日界线时分不清内外,所以改画对日点为心、半径不足 90° 的不可见冠, +// 再用图框减去它得到可见区。 +func transitMapSunUpPath(value time.Time, frame svgmap.Frame) (string, []svgmap.GeoPoint) { + subsolar := solarEclipseSubsolarPoint(value) + antisolar := svgmap.GeoPoint{Longitude: geodata.NormalizeLongitude(subsolar.Longitude + 180), Latitude: -subsolar.Latitude} + radius := 90 - basic.RefractionFromApparentAltitude(0, transitMapRefractionPressureHPa, transitMapRefractionTemperatureC) + boundary := geodata.SphericalCircle(antisolar, radius, 360) + closed := append(append([]svgmap.GeoPoint(nil), boundary...), boundary[0]) + path := fmt.Sprintf("M %.3f %.3f H %.3f V %.3f H %.3f Z %s", + frame.X, frame.Y, frame.X+frame.Width, frame.Y+frame.Height, frame.X, lunarEclipseGeoPolygonPath(boundary, frame)) + return path, closed +} + +// 标准大气,与 basic 站心凌日的视地平判定一致。 +const ( + transitMapRefractionPressureHPa = 1010.0 + transitMapRefractionTemperatureC = 10.0 +) + +// writeTransitMapDefinitions 输出一触、凌甚、四触三个太阳在地平上半球的形状与排除掩膜,分区口径同月食可见图。 +func writeTransitMapDefinitions(b *strings.Builder, frame svgmap.Frame, contacts transitMapContacts) { + b.WriteString(frame.ClipDefinition("transit-map-clip")) + startPath, _ := transitMapSunUpPath(contacts.start, frame) + endPath, _ := transitMapSunUpPath(contacts.end, frame) + greatestPath, _ := transitMapSunUpPath(contacts.greatest, frame) + fmt.Fprintf(b, ``, startPath) + fmt.Fprintf(b, ``, endPath) + fmt.Fprintf(b, ``, greatestPath) + b.WriteString(``) + b.WriteString(``) + writeLunarEclipseVisibilityMask(b, "transit-not-visible-mask", frame, + "transit-sun-c1-shape", "transit-sun-c4-shape", "transit-sun-greatest-shape") + writeLunarEclipseVisibilityMask(b, "transit-not-c1-c4-mask", frame, "transit-sun-c1-shape", "transit-sun-c4-shape") + writeLunarEclipseVisibilityMask(b, "transit-not-greatest-mask", frame, "transit-sun-greatest-shape") + writeLunarEclipseVisibilityMask(b, "transit-not-c4-mask", frame, "transit-sun-c4-shape") + writeLunarEclipseVisibilityMask(b, "transit-not-c1-mask", frame, "transit-sun-c1-shape") +} + +const ( + transitMapColorEntire = "#5e846d" + transitMapColorSunrise = "#4e9da0" + transitMapColorSunset = "#e2aa4b" + transitMapColorNone = "#747b7d" + transitMapColorC1 = "#a3392f" + transitMapColorC2 = "#c0702a" + transitMapColorC3 = "#7a4ca0" + transitMapColorC4 = "#2f5ea3" +) + +// writeTransitMapRegions 着色四类可见区:全程可见、日出时凌日已开始、日落时凌日未结束、不可见。 +// 只有凌甚前后太阳在地平上的极区透镜与两端可见、凌甚不可见的高纬带都归入日落带,同月食可见图。 +func writeTransitMapRegions(b *strings.Builder, frame svgmap.Frame, contacts transitMapContacts) { + _, startBoundary := transitMapSunUpPath(contacts.start, frame) + _, endBoundary := transitMapSunUpPath(contacts.end, frame) + fmt.Fprintf(b, ``, eclipseMapSourceTransitVisibilityRegions) + if frame.IsPolar() { + fmt.Fprintf(b, ``, + frame.X+frame.Width/2, frame.Y+frame.Height/2, frame.Width/2, transitMapColorNone) + } else { + fmt.Fprintf(b, ``, + frame.X, frame.Y, frame.Width, frame.Height, transitMapColorNone) + } + fmt.Fprintf(b, ``, transitMapColorSunset) + fmt.Fprintf(b, ``, transitMapColorSunrise) + fmt.Fprintf(b, ``, transitMapColorEntire) + fmt.Fprintf(b, ``, transitMapColorSunset) + fmt.Fprintf(b, ``, transitMapColorSunset) + b.WriteString(``) + writeEclipseMapGeoLine(b, frame, startBoundary, "c1-horizon", "#a56c16", 1.2, "4 3", "transit-map-clip", eclipseMapSourceTransitHorizonBoundaries) + writeEclipseMapGeoLine(b, frame, endBoundary, "c4-horizon", "#197a82", 1.2, "4 3", "transit-map-clip", eclipseMapSourceTransitHorizonBoundaries) +} + +// writeTransitMapIsochrones 画选中接触的站心等时线:每条线是同一时刻看到该接触的地点,只保留该时刻太阳在视地平上的部分。 +// 每隔若干条标一次时刻,标签按 LabelTable 避让。 +func writeTransitMapIsochrones(b *strings.Builder, frame svgmap.Frame, transit basic.PlanetTransitResult, options TransitMapSVGOptions) { + table := &svgchart.LabelTable{} + format := "15:04" + if options.IsochroneStep%time.Minute != 0 { + format = "15:04:05" + } + for _, contact := range []struct { + kind basic.PlanetTransitContactKind + class string + color string + flag TransitIsochrones + }{ + {basic.PlanetTransitC1, "c1-isochrone", transitMapColorC1, TransitIsochroneC1}, + {basic.PlanetTransitC2, "c2-isochrone", transitMapColorC2, TransitIsochroneC2}, + {basic.PlanetTransitC3, "c3-isochrone", transitMapColorC3, TransitIsochroneC3}, + {basic.PlanetTransitC4, "c4-isochrone", transitMapColorC4, TransitIsochroneC4}, + } { + if options.Isochrones&contact.flag == 0 { + continue + } + lines := basic.PlanetTransitContactIsochrones(transit, contact.kind, options.IsochroneStep) + labelEvery := transitMapIsochroneLabelEvery(len(lines)) + for index, line := range lines { + segments := make([][]svgmap.GeoPoint, 0, len(line.Segments)) + for _, segment := range line.Segments { + points := make([]svgmap.GeoPoint, len(segment)) + for position, point := range segment { + points[position] = svgmap.GeoPoint{Longitude: point.Longitude, Latitude: point.Latitude} + } + segments = append(segments, points) + writeEclipseMapGeoLine(b, frame, points, contact.class, contact.color, 0.9, "none", "transit-map-clip", eclipseMapSourceTransitContactIsochrones) + } + if index%labelEvery != 0 { + continue + } + text := transitMapDisplayTime(basic.JD2DateByZone(line.JD, time.UTC, false), options).Format(format) + placement, ok := table.Place(text, 10, transitMapIsochroneLabelCandidates(segments, frame)) + if !ok { + continue + } + fmt.Fprintf(b, `%s`, + contact.class, placement.X, placement.Y, contact.color, placement.Anchor, html.EscapeString(text)) + } + } +} + +// transitMapIsochroneLabelEvery 让每个接触最多标八条线。 +func transitMapIsochroneLabelEvery(count int) int { + every := int(math.Ceil(float64(count) / 8)) + if every < 1 { + every = 1 + } + return every +} + +// transitMapIsochroneLabelCandidates 从最长支路的中点向两端取画布内的候选位置。 +func transitMapIsochroneLabelCandidates(segments [][]svgmap.GeoPoint, frame svgmap.Frame) []svgchart.LabelPlacement { + longest := -1 + for index, segment := range segments { + if longest < 0 || len(segment) > len(segments[longest]) { + longest = index + } + } + if longest < 0 { + return nil + } + segment := segments[longest] + middle := len(segment) / 2 + candidates := make([]svgchart.LabelPlacement, 0, 32) + for offset := 0; offset <= middle && len(candidates) < 32; offset++ { + for _, index := range []int{middle + offset, middle - offset} { + if index < 0 || index >= len(segment) || (offset == 0 && index != middle) { + continue + } + x, y, visible := frame.Project(segment[index].Longitude, segment[index].Latitude) + if !visible || x < frame.X+24 || x > frame.X+frame.Width-24 || y < frame.Y+14 || y > frame.Y+frame.Height-6 { + continue + } + candidates = append(candidates, svgchart.LabelPlacement{X: x, Y: y - 4, Anchor: "middle"}) + } + } + return candidates +} + +func writeTransitMapSummary(b *strings.Builder, transit basic.PlanetTransitResult, contacts transitMapContacts, options TransitMapSVGOptions) { + clock := func(value time.Time) string { return transitMapDisplayTime(value, options).Format("15:04:05") } + zone, _ := transitMapDisplayTime(contacts.greatest, options).Zone() + if zone == "" { + zone = "UTC" + } + if options.TimeScale == astro.TimeScaleUT1 { + zone = "UT1" + } + greatest, separation := "凌甚", "最小角距" + if options.Language == "en" { + greatest, separation = "Greatest", "least separation" + } + parts := []string{"C1 " + clock(contacts.start)} + if contacts.hasInternal { + parts = append(parts, "C2 "+clock(contacts.internalStart)) + } + parts = append(parts, greatest+" "+clock(contacts.greatest)) + if contacts.hasInternal { + parts = append(parts, "C3 "+clock(contacts.internalEnd)) + } + parts = append(parts, "C4 "+clock(contacts.end)) + line := fmt.Sprintf("%s (%s)", strings.Join(parts, " | "), zone) + fmt.Fprintf(b, `%s`, + float64(options.Width)/2, html.EscapeString(svgchart.EllipsizeText(line, float64(options.Width)-60, 13))) + note := fmt.Sprintf("%s %.1f″ | 太阳视半径 %.1f″ | 行星视半径 %.2f″(地心)", separation, + transit.MinimumSeparationArcsec, transit.SunSemidiameterArcsec, transit.PlanetSemidiameterArcsec) + if options.Language == "en" { + note = fmt.Sprintf("%s %.1f″ | solar semidiameter %.1f″ | planet semidiameter %.2f″ (geocentric)", separation, + transit.MinimumSeparationArcsec, transit.SunSemidiameterArcsec, transit.PlanetSemidiameterArcsec) + } + fmt.Fprintf(b, `%s`, + float64(options.Width)/2, html.EscapeString(note)) +} + +// writeTransitMapLegend 画区域色块与等时线图例,按画布宽度折行,返回行数。 +func writeTransitMapLegend(b *strings.Builder, frame svgmap.Frame, canvasWidth float64, options TransitMapSVGOptions) int { + type entry struct { + text, color string + line bool + } + labels := []string{"全程可见", "日出时已在凌日", "日落时仍在凌日", "不可见", "一触等时线", "二触等时线", "三触等时线", "四触等时线"} + if options.Language == "en" { + labels = []string{"Entire transit", "Transit at sunrise", "Transit at sunset", "Not visible", + "C1 isochrones", "C2 isochrones", "C3 isochrones", "C4 isochrones"} + } + entries := []entry{ + {labels[0], transitMapColorEntire, false}, {labels[1], transitMapColorSunrise, false}, + {labels[2], transitMapColorSunset, false}, {labels[3], transitMapColorNone, false}, + } + for index, item := range []struct { + flag TransitIsochrones + color string + }{ + {TransitIsochroneC1, transitMapColorC1}, {TransitIsochroneC2, transitMapColorC2}, + {TransitIsochroneC3, transitMapColorC3}, {TransitIsochroneC4, transitMapColorC4}, + } { + if options.Isochrones&item.flag != 0 { + entries = append(entries, entry{labels[4+index], item.color, true}) + } + } + width := func(item entry) float64 { + return lunarEclipseDetailedMapLegendIconWidth + svgchart.EstimatedTextWidth(item.text, lunarEclipseDetailedMapLegendFontSize) + } + available := canvasWidth - 2*lunarEclipseDetailedPageInset + var rows [][]entry + used := 0.0 + for _, item := range entries { + if len(rows) == 0 || used+lunarEclipseDetailedMapLegendGap+width(item) > available { + rows = append(rows, nil) + used = -lunarEclipseDetailedMapLegendGap + } + rows[len(rows)-1] = append(rows[len(rows)-1], item) + used += lunarEclipseDetailedMapLegendGap + width(item) + } + baseY := frame.Y + frame.Height + 31 + for rowIndex, row := range rows { + total := -lunarEclipseDetailedMapLegendGap + for _, item := range row { + total += lunarEclipseDetailedMapLegendGap + width(item) + } + x := math.Max(lunarEclipseDetailedPageInset, frame.X+frame.Width/2-total/2) + y := baseY + float64(rowIndex)*lunarEclipseDetailedMapLegendLineStep + for _, item := range row { + if item.line { + fmt.Fprintf(b, ``, x, y-4, x+18, y-4, item.color) + } else { + fmt.Fprintf(b, ``, x, y-8, item.color) + } + fmt.Fprintf(b, `%s`, + x+lunarEclipseDetailedMapLegendIconWidth, y, lunarEclipseDetailedMapLegendFontSize, html.EscapeString(item.text)) + x += width(item) + lunarEclipseDetailedMapLegendGap + } + } + return len(rows) +} + +func writeTransitMapFooter( + b *strings.Builder, + frame svgmap.Frame, + options TransitMapSVGOptions, + projection svgmap.Projection, + instant time.Time, + legendRows int, +) { + text := options.FooterNote + if text == "" { + if options.Language == "en" { + text = eclipseMapProjectionLabel(projection, "en") + "; regions follow whether the Sun's centre is above the apparent horizon (standard refraction) at C1, greatest and C4" + if options.Isochrones != 0 { + text += "; isochrones are topocentric contact times" + } + text += "." + } else { + text = eclipseMapProjectionLabel(projection, "zh") + ";按一触、凌甚、四触太阳中心是否在视地平上分区(计入标准大气折射)" + if options.Isochrones != 0 { + text += ";等时线为站心接触时刻" + } + text += "。" + } + } + const footerBottomPadding = 26.0 + // 页脚紧跟图例末行,不与图例重叠;行数按画布底边截断,时标声明独占最后一行。 + + baseline := frame.Y + frame.Height + 31 + float64(legendRows-1)*lunarEclipseDetailedMapLegendLineStep + 18 + maxWidth := float64(options.Width) - frame.X - 24 + lines := svgchart.WrapText(text, maxWidth, 11) + lines = svgchart.FooterLinesWithScale(lines, timenote.Scale(options.TimeScale, instant, options.Location, options.Language), maxWidth, 11, + svgchart.BaselineLineLimit(11, svgchart.FooterLineHeight(11), baseline, float64(options.Height)-footerBottomPadding)) + for index, line := range lines { + fmt.Fprintf(b, `%s`, + frame.X, baseline+float64(index)*svgchart.FooterLineHeight(11), html.EscapeString(line)) + } +} diff --git a/eclipse/svg/transit_map_test.go b/eclipse/svg/transit_map_test.go new file mode 100644 index 0000000..bf5b773 --- /dev/null +++ b/eclipse/svg/transit_map_test.go @@ -0,0 +1,91 @@ +package svg + +import ( + "fmt" + "math" + "strings" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/basic" + "b612.me/astro/internal/svgmap" +) + +func TestTransitMapSVGRendersRegionsAndIsochrones(t *testing.T) { + cases := []struct { + name string + planet TransitPlanet + date time.Time + }{ + {"venus-2012", TransitVenus, time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC)}, + {"mercury-2019", TransitMercury, time.Date(2019, 11, 11, 0, 0, 0, 0, time.UTC)}, + } + for _, tc := range cases { + for _, projection := range []EclipseMapProjection{"", EclipseMapProjectionOrthographic, EclipseMapProjectionNorthPolar} { + chart, ok := TransitMapSVG(tc.planet, tc.date, TransitMapSVGOptions{Location: time.UTC, Projection: projection, Isochrones: TransitIsochroneAll}) + if !ok { + t.Fatalf("%s %q: render failed", tc.name, projection) + } + for _, want := range []string{"transit-entire-region", "transit-sunrise-region", "transit-sunset-region", + "c1-horizon", "c4-horizon", `class="c1-isochrone"`, `class="c2-isochrone"`, `class="c3-isochrone"`, + `class="c4-isochrone"`, "c1-isochrone-label"} { + if !strings.Contains(chart, want) { + t.Fatalf("%s %q: missing %s", tc.name, projection, want) + } + } + if strings.Contains(chart, "NaN") || strings.Contains(chart, "Inf") { + t.Fatalf("%s %q: non-finite coordinate", tc.name, projection) + } + } + } +} + +func TestTransitMapSVGOptions(t *testing.T) { + date := time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC) + if _, ok := TransitMapSVG(3, date, TransitMapSVGOptions{}); ok { + t.Fatal("unsupported planet must fail") + } + if _, ok := TransitMapSVG(TransitVenus, date, TransitMapSVGOptions{ + TimeScale: astro.TimeScaleUT1, Location: time.FixedZone("CST", 8*3600)}); ok { + t.Fatal("UT1 with non-UTC location must fail") + } + chart, ok := TransitMapSVG(TransitVenus, date, TransitMapSVGOptions{Location: time.UTC, Language: "en"}) + if !ok || strings.Contains(chart, "isochrone") || !strings.Contains(chart, "Transit of Venus") { + t.Fatal("isochrones must be off by default and English labels kept") + } + chart, ok = TransitMapSVG(TransitVenus, date, TransitMapSVGOptions{Location: time.UTC, Isochrones: TransitIsochroneC2, IsochroneStep: 5 * time.Minute}) + if !ok || !strings.Contains(chart, `class="c2-isochrone"`) || strings.Contains(chart, `class="c1-isochrone"`) || + !strings.Contains(chart, "二触等时线") || strings.Contains(chart, "一触等时线") { + t.Fatal("only the selected contact must be drawn and listed in the legend") + } +} + +// 太阳在视地平上的区域由图框减去对日点夜冠得到,边界距直射点 90°+地平折射。 +// 曾直接画 90° 半球环,拆分日界线时分不清内外而整片填反。 +func TestTransitMapSunUpBoundaryIncludesRefraction(t *testing.T) { + transit := basic.ClosestVenusTransit(basic.Date2JD(time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC))) + contacts := transitMapContactsOf(transit) + radius := 90 + basic.RefractionFromApparentAltitude(0, transitMapRefractionPressureHPa, transitMapRefractionTemperatureC) + frame := eclipseMapFrame(960, 640, svgmap.ProjectionEquirectangular, 142, 92) + for _, at := range []time.Time{contacts.start, contacts.greatest, contacts.end} { + path, _ := transitMapSunUpPath(at, frame) + if !strings.HasPrefix(path, fmt.Sprintf("M %.3f %.3f H", frame.X, frame.Y)) { + t.Fatalf("%v: sun-up path must start with the frame rectangle", at) + } + subsolar := solarEclipseSubsolarPoint(at) + _, boundary := transitMapSunUpPath(at, frame) + for _, point := range boundary { + distance := transitMapTestAngularDistance(point, subsolar) + if math.Abs(distance-radius) > 1e-6 { + t.Fatalf("%v: boundary point %.4f deg from subsolar point, want %.4f", at, distance, radius) + } + } + } +} + +func transitMapTestAngularDistance(a, b svgmap.GeoPoint) float64 { + r := math.Pi / 180 + cosine := math.Sin(a.Latitude*r)*math.Sin(b.Latitude*r) + math.Cos(a.Latitude*r)*math.Cos(b.Latitude*r)*math.Cos((a.Longitude-b.Longitude)*r) + return math.Acos(math.Max(-1, math.Min(1, cosine))) / r +} diff --git a/eclipse/testdata/semianalytic/local_lunar_chauvenet_available.json b/eclipse/testdata/semianalytic/local_lunar_chauvenet_available.json new file mode 100644 index 0000000..7d52dab --- /dev/null +++ b/eclipse/testdata/semianalytic/local_lunar_chauvenet_available.json @@ -0,0 +1,4 @@ +{ + "Chauvenet.PenumbralMagnitude": 2.2854632847154006, + "Chauvenet.UmbralMagnitude": 1.1828338861983332 +} diff --git a/eclipse/testdata/semianalytic/local_solar_contact_points.json b/eclipse/testdata/semianalytic/local_solar_contact_points.json new file mode 100644 index 0000000..f555620 --- /dev/null +++ b/eclipse/testdata/semianalytic/local_solar_contact_points.json @@ -0,0 +1,6 @@ +{ + "C1.ContactPositionAngle": 226.22722697257004, + "C2.ContactPositionAngle": 19.47323958289263, + "C2.MoonCenterPositionAngle": 199.47323958289263, + "C4.ContactClockwiseAngle": 310.7896361107794 +} diff --git a/eclipse/testdata/semianalytic/lunar_chauvenet_available.json b/eclipse/testdata/semianalytic/lunar_chauvenet_available.json new file mode 100644 index 0000000..7d52dab --- /dev/null +++ b/eclipse/testdata/semianalytic/lunar_chauvenet_available.json @@ -0,0 +1,4 @@ +{ + "Chauvenet.PenumbralMagnitude": 2.2854632847154006, + "Chauvenet.UmbralMagnitude": 1.1828338861983332 +} diff --git a/eclipse/testdata/semianalytic/lunar_contact_points.json b/eclipse/testdata/semianalytic/lunar_contact_points.json new file mode 100644 index 0000000..e5f915d --- /dev/null +++ b/eclipse/testdata/semianalytic/lunar_contact_points.json @@ -0,0 +1,6 @@ +{ + "U1.ContactClockwiseAngle": 263.81639399705364, + "U1.ContactPositionAngle": 96.18360600294636, + "U2.ContactPositionAngle": 243.037377535088, + "U2.MoonCenterPositionAngle": 243.037377535088 +} diff --git a/ephemeris.go b/ephemeris.go new file mode 100644 index 0000000..3df1634 --- /dev/null +++ b/ephemeris.go @@ -0,0 +1,100 @@ +package astro + +import ( + "errors" + + "b612.me/astro/internal/ephem" +) + +// Instance 显式星历实例,不改变进程默认模型 / explicit ephemeris instance independent of the process default. +type Instance = ephem.Instance + +// Engine 只读 ICRF 几何星历,位置 km、速度 km/日,历元 TDB / read-only ICRF ephemeris in km and km/day at TDB epochs. +type Engine = ephem.Engine + +// Stack 按顺序叠加多个只读星历引擎 / ordered stack of read-only ephemeris engines. +type Stack = ephem.Stack + +// ApparentEngine 可选的地心 ICRF 单位视方向能力,历元 TT / optional apparent geocentric ICRF unit directions at TT epochs. +type ApparentEngine = ephem.ApparentEngine + +// Body NAIF 天体编号 / NAIF body identifier. +type Body = ephem.Body + +const ( + // BodySSB 太阳系质心 / solar system barycenter. + BodySSB = ephem.SSB + // BodyMercury 水星 / Mercury. + BodyMercury = ephem.Mercury + // BodyVenus 金星 / Venus. + BodyVenus = ephem.Venus + // BodyEMB 地月质心 / Earth-Moon barycenter. + BodyEMB = ephem.EMB + // BodyMars 火星 / Mars. + BodyMars = ephem.Mars + // BodyJupiter 木星 / Jupiter. + BodyJupiter = ephem.Jupiter + // BodySaturn 土星 / Saturn. + BodySaturn = ephem.Saturn + // BodyUranus 天王星 / Uranus. + BodyUranus = ephem.Uranus + // BodyNeptune 海王星 / Neptune. + BodyNeptune = ephem.Neptune + // BodySun 太阳 / Sun. + BodySun = ephem.Sun + // BodyMoon 月球 / Moon. + BodyMoon = ephem.Moon + // BodyEarth 地球 / Earth. + BodyEarth = ephem.Earth +) + +// EphemerisModel 内置或自定义星历模型标识 / built-in or custom ephemeris model identifier. +type EphemerisModel = ephem.Model + +const ( + // EphemerisLegacy 旧级数模型 / legacy series model. + EphemerisLegacy = ephem.Legacy + // EphemerisSemianalytic 半解析模型 / semi-analytic model. + EphemerisSemianalytic = ephem.Semianalytic + // EphemerisCustom 自定义几何引擎 / custom geometric engine. + EphemerisCustom = ephem.Custom +) + +var ( + // ErrEphemerisOutOfRange 超出星历覆盖 / epoch outside ephemeris coverage. + ErrEphemerisOutOfRange = ephem.ErrOutOfRange + // ErrEphemerisUnsupportedBody 引擎不含该天体 / body not provided by the engine. + ErrEphemerisUnsupportedBody = ephem.ErrUnsupportedBody + // ErrEphemerisNoEngine 实例没有几何引擎 / instance has no geometric engine. + ErrEphemerisNoEngine = ephem.ErrNoEngine + // ErrEphemerisUnsupportedApparent 引擎不提供视位置 / apparent positions are unsupported. + ErrEphemerisUnsupportedApparent = ephem.ErrUnsupportedApparent + // ErrUnknownModel 模型不是本函数接受的模型 / model is not accepted by this function. + ErrUnknownModel = errors.New("astro: unknown or unsupported ephemeris model") + // ErrInvalidEngine 引擎个数或取值不符合模型要求 / engine count or value does not fit the model. + ErrInvalidEngine = errors.New("astro: invalid ephemeris engine") +) + +// NewInstance 创建显式自定义星历实例 / create an explicit custom ephemeris instance. +func NewInstance(engine Engine) *Instance { + return ephem.NewInstance(engine) +} + +// NewStack 创建多源星历,先传入的引擎优先 / creates a multi-source ephemeris; earlier engines have priority. +func NewStack(engines ...Engine) *Stack { + return ephem.NewStack(engines...) +} + +// NewStackInstance 创建使用多源星历的显式实例 / creates an explicit instance backed by multiple ephemeris sources. +func NewStackInstance(engines ...Engine) *Instance { + return ephem.NewInstance(ephem.NewStack(engines...)) +} + +// NewModelInstance 创建内置模型实例,非内置模型(含 EphemerisCustom)返回 ErrUnknownModel;旧模型目前仅支持标量入口 / +// create a built-in model instance; non-built-in models including EphemerisCustom return ErrUnknownModel. +func NewModelInstance(model EphemerisModel) (*Instance, error) { + if model != EphemerisLegacy && model != EphemerisSemianalytic { + return nil, ErrUnknownModel + } + return ephem.NewModelInstance(model), nil +} diff --git a/ephemeris_public_test.go b/ephemeris_public_test.go new file mode 100644 index 0000000..fa8181f --- /dev/null +++ b/ephemeris_public_test.go @@ -0,0 +1,75 @@ +package astro_test + +import ( + "errors" + "math" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/basic" + "b612.me/astro/eclipse" + "b612.me/astro/planet/spk" +) + +type publicEphemerisEngine struct{} + +func (publicEphemerisEngine) State(target, center astro.Body, jde float64) ([3]float64, [3]float64, error) { + return [3]float64{149597870.7, 0, 0}, [3]float64{}, nil +} + +func (publicEphemerisEngine) Coverage(target astro.Body) (float64, float64, bool) { + return 2400000, 2500000, true +} + +var _ astro.Engine = publicEphemerisEngine{} +var _ astro.Engine = (*spk.File)(nil) +var _ func(float64, *astro.Instance) basic.LunarEclipseResult = basic.LunarEclipseWithInstance +var _ func(float64, *astro.Instance) float64 = basic.MoonPhaseWithInstance +var _ func(float64, *astro.Instance) float64 = basic.MercuryApparentLoWithInstance +var _ func(float64, *astro.Instance) float64 = basic.HSunTrueLoWithInstance +var _ func(int, int, *astro.Instance) float64 = basic.GetJQTimeWithInstance + +func TestP3PublicInstanceConstruction(t *testing.T) { + for _, model := range []astro.EphemerisModel{astro.EphemerisLegacy, astro.EphemerisSemianalytic} { + inst := mustInstance(t, model) + if inst.Model() != model { + t.Fatalf("model constructor lost identity: %v", inst.Model()) + } + if result := basic.LunarEclipseWithInstance(basic.JDCalc(2022, 11, 8), inst); !result.HasTotal { + t.Errorf("public model instance lost eclipse: %+v", result) + } + } + inst := astro.NewInstance(publicEphemerisEngine{}) + position, _, err := inst.State(astro.BodySun, astro.BodyEarth, 2451545) + if err != nil || position[0] != 149597870.7 || inst.Model() != astro.EphemerisCustom { + t.Fatalf("custom instance: %v, %v", position, err) + } + if distance := basic.EarthAwayNWithInstance(2451545, -1, inst); math.Abs(distance-1) > 1e-9 { + t.Errorf("public instance could not reach basic: %v", distance) + } + _, _, _, err = inst.Apparent(astro.BodySun, 2451545) + if !errors.Is(err, astro.ErrEphemerisUnsupportedApparent) { + t.Errorf("public error identity was lost: %v", err) + } + if longitude := basic.HSunTrueLoWithInstance(2451545, astro.NewInstance(nil)); !math.IsNaN(longitude) { + t.Errorf("empty custom engine fell back: %v", longitude) + } +} + +func TestNewModelInstanceRejectsNonBuiltin(t *testing.T) { + for _, model := range []astro.EphemerisModel{astro.EphemerisCustom, astro.EphemerisModel(42)} { + if inst, err := astro.NewModelInstance(model); inst != nil || !errors.Is(err, astro.ErrUnknownModel) { + t.Errorf("model %v: inst=%v err=%v", model, inst, err) + } + } +} + +func TestP3PublicLunarDateQuery(t *testing.T) { + inst := mustInstance(t, astro.EphemerisSemianalytic) + date := time.Date(2022, 11, 8, 12, 0, 0, 0, time.UTC) + info, ok := eclipse.LunarEclipseOnDateWithInstance(date, inst) + if !ok || !info.HasTotal { + t.Errorf("public instance did not reach eclipse query: %+v", info) + } +} diff --git a/formula/earth_orientation.go b/formula/earth_orientation.go new file mode 100644 index 0000000..c368237 --- /dev/null +++ b/formula/earth_orientation.go @@ -0,0 +1,82 @@ +package formula + +import "math" + +const arcsecToRadian = math.Pi / 180 / 3600 + +// TIOLocatorArcsec 地球自转原点定位角 s′,单位角秒 / TIO locator s′ in arcseconds. +// +// jdeTT 为 TT 儒略日。采用 IERS 2010 的线性近似 s′ = −47 µas·t,t 为 J2000 起的儒略世纪数。 +// jdeTT is a TT Julian date. Uses the IERS 2010 linear model s′ = −47 µas·t, +// with t in Julian centuries since J2000. +func TIOLocatorArcsec(jdeTT float64) float64 { + if !isFinitePhotometry(jdeTT) { + return math.NaN() + } + return -47e-6 * (jdeTT - 2451545.0) / 36525 +} + +// PolarMotionMatrix 极移矩阵 W,把地球中间参考系 (TIRS) 矢量转到 ITRS / polar-motion matrix from TIRS to ITRS. +// +// xp、yp 为 IERS 公布的极坐标,sPrime 为 TIOLocatorArcsec 的结果,单位都是角秒。 +// 返回 W = R1(−yp)·R2(−xp)·R3(s′),按 v_ITRS = W·v_TIRS 使用。 +// xp and yp are IERS pole coordinates and sPrime is the TIO locator, all in +// arcseconds. Returns W = R1(−yp)·R2(−xp)·R3(s′) for v_ITRS = W·v_TIRS. +func PolarMotionMatrix(xp, yp, sPrime float64) [3][3]float64 { + if !isFinitePhotometry(xp) || !isFinitePhotometry(yp) || !isFinitePhotometry(sPrime) { + return nanMatrix() + } + return multiplyMatrix(rotationX(-yp*arcsecToRadian), + multiplyMatrix(rotationY(-xp*arcsecToRadian), rotationZ(sPrime*arcsecToRadian))) +} + +// CelestialPoleOffsetMatrix 天球极偏差修正矩阵 / celestial pole offset correction matrix. +// +// dX、dY 为 IERS 公布的 CIP 坐标改正,单位毫角秒。返回一阶小角矩阵 Δ, +// 左乘模型给出的 GCRS→CIRS(或 GCRS→真赤道)矩阵即得改正后的矩阵:C′ = Δ·C。 +// dX and dY are IERS CIP corrections in milliarcseconds. Returns the +// first-order matrix Δ; left-multiply a model GCRS→CIRS (or GCRS→true-equator) +// matrix to apply the offsets: C′ = Δ·C. +func CelestialPoleOffsetMatrix(dX, dY float64) [3][3]float64 { + if !isFinitePhotometry(dX) || !isFinitePhotometry(dY) { + return nanMatrix() + } + x := dX * arcsecToRadian / 1000 + y := dY * arcsecToRadian / 1000 + return [3][3]float64{ + {1, 0, -x}, + {0, 1, -y}, + {x, y, 1}, + } +} + +// rotationX 等为被动旋转:把矢量所在坐标轴绕 x/y/z 轴转 angle 弧度。 +func rotationX(angle float64) [3][3]float64 { + s, c := math.Sincos(angle) + return [3][3]float64{{1, 0, 0}, {0, c, s}, {0, -s, c}} +} + +func rotationY(angle float64) [3][3]float64 { + s, c := math.Sincos(angle) + return [3][3]float64{{c, 0, -s}, {0, 1, 0}, {s, 0, c}} +} + +func rotationZ(angle float64) [3][3]float64 { + s, c := math.Sincos(angle) + return [3][3]float64{{c, s, 0}, {-s, c, 0}, {0, 0, 1}} +} + +func multiplyMatrix(a, b [3][3]float64) [3][3]float64 { + var out [3][3]float64 + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + out[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + } + } + return out +} + +func nanMatrix() [3][3]float64 { + n := math.NaN() + return [3][3]float64{{n, n, n}, {n, n, n}, {n, n, n}} +} diff --git a/formula/earth_orientation_test.go b/formula/earth_orientation_test.go new file mode 100644 index 0000000..78633c3 --- /dev/null +++ b/formula/earth_orientation_test.go @@ -0,0 +1,44 @@ +package formula + +import ( + "math" + "testing" +) + +func TestTIOLocator(t *testing.T) { + // 2002-09-29 0h TT(MJD 52541):s′ = −6.216698469981019e-12 rad。 + got := TIOLocatorArcsec(2452541.5) * arcsecToRadian + assertFormulaClose(t, "TIOLocator", got, -0.6216698469981019309e-11, 1e-24) + if !math.IsNaN(TIOLocatorArcsec(math.NaN())) { + t.Fatal("expected NaN") + } +} + +func TestPolarMotionMatrix(t *testing.T) { + const r2as = 1 / arcsecToRadian + w := PolarMotionMatrix(2.55060238e-7*r2as, 1.860359247e-6*r2as, -0.1367174580728891460e-10*r2as) + want := [3][3]float64{ + {0.9999999999999674721, -0.1367174580728846989e-10, 0.2550602379999972345e-6}, + {0.1414624947957029801e-10, 0.9999999999982695317, -0.1860359246998866389e-5}, + {-0.2550602379741215021e-6, 0.1860359247002414021e-5, 0.9999999999982370039}, + } + for i := range want { + for j := range want[i] { + assertFormulaClose(t, "PolarMotionMatrix", w[i][j], want[i][j], 1e-16) + } + } +} + +func TestCelestialPoleOffsetMatrix(t *testing.T) { + // 改正后的极方向必须被映射到 z 轴:Δ·(dX, dY, 1) ≈ (0, 0, 1)。 + d := CelestialPoleOffsetMatrix(0.3, -0.2) + x, y := 0.3*arcsecToRadian/1000, -0.2*arcsecToRadian/1000 + p := [3]float64{x, y, 1} + for i, want := range [3]float64{0, 0, 1} { + got := d[i][0]*p[0] + d[i][1]*p[1] + d[i][2]*p[2] + assertFormulaClose(t, "CelestialPoleOffsetMatrix", got, want, 1e-20) + } + if !math.IsNaN(CelestialPoleOffsetMatrix(math.Inf(1), 0)[0][0]) { + t.Fatal("expected NaN matrix") + } +} diff --git a/formula/extinction.go b/formula/extinction.go new file mode 100644 index 0000000..9d3af30 --- /dev/null +++ b/formula/extinction.go @@ -0,0 +1,47 @@ +package formula + +import "math" + +// ExtinctionComponents V 波段天顶消光系数分量,单位星等/大气质量 / V-band zenith extinction components in magnitudes per airmass. +type ExtinctionComponents struct { + Rayleigh float64 // 分子散射 / molecular scattering + Aerosol float64 // 气溶胶 / aerosol + Ozone float64 // 臭氧 / ozone + Water float64 // 水汽 / water vapour + Total float64 // 四项之和 / sum of the four terms +} + +// ExtinctionSchaefer 按 Schaefer 经验模型由气象条件合成 V 波段天顶消光系数 / V-band zenith extinction from weather by Schaefer's empirical model. +// +// heightM 为测站海拔(米),temperatureC 为气温(°C),relativeHumidity 为相对湿度(百分比, +// 须在 (0, 100) 内),latitude 为测站纬度(度),sunRA 为太阳赤经(度,用于季节项)。 +// Total 可直接作为 basic.HeliacalOptions.ExtinctionCoefficient;输入越界时各分量为 NaN。 +// heightM is site elevation in metres, temperatureC is air temperature, +// relativeHumidity is a percentage in (0, 100), latitude is the site latitude, +// and sunRA is the Sun's right ascension in degrees for the seasonal terms. +// Total can be used as basic.HeliacalOptions.ExtinctionCoefficient. Out-of-range +// inputs yield NaN components. +func ExtinctionSchaefer(heightM, temperatureC, relativeHumidity, latitude, sunRA float64) ExtinctionComponents { + if !isFinitePhotometry(heightM) || !isFinitePhotometry(temperatureC) || !isFinitePhotometry(relativeHumidity) || + !isFinitePhotometry(latitude) || !isFinitePhotometry(sunRA) || + relativeHumidity <= 0 || relativeHumidity >= 100 || latitude < -90 || latitude > 90 { + n := math.NaN() + return ExtinctionComponents{n, n, n, n, n} + } + lat := latitude * math.Pi / 180 + ra := sunRA * math.Pi / 180 + hemisphere := 1.0 + if latitude < 0 { + hemisphere = -1 + } + // 标高:分子与水汽 8200 m,气溶胶 1500 m;臭氧层在高空,不随测站海拔变化。 + components := ExtinctionComponents{ + Rayleigh: 0.1066 * math.Exp(-heightM/8200), + Aerosol: 0.1 * math.Exp(-heightM/1500) * math.Pow(1-0.32/math.Log(relativeHumidity/100), 1.33) * + (1 + 0.33*hemisphere*math.Sin(ra)), + Ozone: 0.031 * (3 + 0.4*(lat*math.Cos(ra)-math.Cos(3*lat))) / 3, + Water: 0.031 * 0.94 * (relativeHumidity / 100) * math.Exp(temperatureC/15) * math.Exp(-heightM/8200), + } + components.Total = components.Rayleigh + components.Aerosol + components.Ozone + components.Water + return components +} diff --git a/formula/extinction_test.go b/formula/extinction_test.go new file mode 100644 index 0000000..3126ecc --- /dev/null +++ b/formula/extinction_test.go @@ -0,0 +1,38 @@ +package formula + +import ( + "math" + "testing" +) + +func TestExtinctionSchaefer(t *testing.T) { + // 海平面、20 °C、湿度 50%、北纬 30°、春分点:逐项手算。 + got := ExtinctionSchaefer(0, 20, 50, 30, 0) + aerosol := 0.1 * math.Pow(1-0.32/math.Log(0.5), 1.33) + lat := math.Pi / 6 + ozone := 0.031 * (3 + 0.4*(lat-math.Cos(3*lat))) / 3 + water := 0.031 * 0.94 * 0.5 * math.Exp(20.0/15) + assertFormulaClose(t, "Rayleigh", got.Rayleigh, 0.1066, 1e-15) + assertFormulaClose(t, "Aerosol", got.Aerosol, aerosol, 1e-15) + assertFormulaClose(t, "Ozone", got.Ozone, ozone, 1e-15) + assertFormulaClose(t, "Water", got.Water, water, 1e-15) + assertFormulaClose(t, "Total", got.Total, 0.1066+aerosol+ozone+water, 1e-15) + if got.Total < 0.2 || got.Total > 0.5 { + t.Fatalf("sea-level humid extinction out of typical range: %.3f", got.Total) + } + + // 高山干燥站点消光明显更小。 + high := ExtinctionSchaefer(2500, 0, 20, 30, 0) + if high.Total >= got.Total*0.6 { + t.Fatalf("high dry site %.3f should be well below sea level %.3f", high.Total, got.Total) + } + + for _, rh := range []float64{0, 100, -1, math.NaN()} { + if !math.IsNaN(ExtinctionSchaefer(0, 20, rh, 30, 0).Total) { + t.Fatalf("humidity %v should be rejected", rh) + } + } + if !math.IsNaN(ExtinctionSchaefer(0, 20, 50, 91, 0).Total) { + t.Fatal("latitude out of range should be rejected") + } +} diff --git a/formula/refraction.go b/formula/refraction.go new file mode 100644 index 0000000..3269a53 --- /dev/null +++ b/formula/refraction.go @@ -0,0 +1,29 @@ +package formula + +import "b612.me/astro/internal/refraction" + +// RefractionStandardPressureHPa 与 RefractionStandardTemperatureC 是折射公式的标准气压与气温 / standard pressure and temperature of the refraction formula. +const ( + RefractionStandardPressureHPa = refraction.StandardPressureHPa + RefractionStandardTemperatureC = refraction.StandardTemperatureC +) + +// RefractionFromApparentAltitude 由视高度角求折射量(度,视高度减去它得真高度),无逆解时为 NaN / refraction from apparent altitude. +func RefractionFromApparentAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { + return refraction.RefractionFromApparentAltitude(apparentAltitude, pressureHPa, temperatureC) +} + +// TrueAltitude 视高度角转真高度角(度),无逆解时为 NaN / true altitude from apparent altitude. +func TrueAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { + return refraction.TrueAltitude(apparentAltitude, pressureHPa, temperatureC) +} + +// ApparentAltitude 真高度角转视高度角(度) / apparent altitude from true altitude. +func ApparentAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + return refraction.ApparentAltitude(trueAltitude, pressureHPa, temperatureC) +} + +// RefractionFromTrueAltitude 由真高度角求折射量(度,加到真高度上得视高度) / refraction from true altitude. +func RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + return refraction.RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) +} diff --git a/formula/refraction_test.go b/formula/refraction_test.go new file mode 100644 index 0000000..5049e53 --- /dev/null +++ b/formula/refraction_test.go @@ -0,0 +1,21 @@ +package formula + +import ( + "math" + "testing" +) + +func TestRefractionRoundTrip(t *testing.T) { + for _, alt := range []float64{-1, 0, 0.5, 5, 30, 80} { + apparent := ApparentAltitude(alt, RefractionStandardPressureHPa, RefractionStandardTemperatureC) + if back := TrueAltitude(apparent, RefractionStandardPressureHPa, RefractionStandardTemperatureC); math.Abs(back-alt) > 1e-9 { + t.Errorf("alt %v: round trip %v", alt, back) + } + } + if r := RefractionFromTrueAltitude(0, 1010, 10) * 60; r < 28 || r > 36 { + t.Errorf("horizon refraction %.2f arcmin", r) + } + if !math.IsNaN(RefractionFromTrueAltitude(0, 0, 10)) { + t.Error("zero pressure must be NaN") + } +} diff --git a/geojson/api_default.go b/geojson/api_default.go new file mode 100644 index 0000000..622f470 --- /dev/null +++ b/geojson/api_default.go @@ -0,0 +1,84 @@ +package geojson + +import ( + eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/mercury" + "b612.me/astro/venus" +) + +// MarshalLunarEclipse 将月食 P1/P4 站心月心可见区、几何地平线以及 P1-P4 的时间包络编码为 GeoJSON,不含折射。 +// MarshalLunarEclipse encodes the P1/P4 topocentric Moon-center visibility regions, their geometric horizons and the P1-P4 time envelopes, without refraction. +// boundaryPoints 小于等于零时使用 360;其他值限制在 [12, 1440]。 +// boundaryPoints values <= 0 use 360; other values are clamped to [12, 1440]. +func MarshalLunarEclipse(info eclipsecore.LunarEclipseInfo, boundaryPoints int) ([]byte, error) { + return defaultModule.MarshalLunarEclipse(info, boundaryPoints) +} + +// MarshalLunarEclipseWithOptions 编码月食,输出内容与采样精度由 options 选择。 +// MarshalLunarEclipseWithOptions encodes a lunar eclipse with the content and sampling selected by options. +func MarshalLunarEclipseWithOptions( + info eclipsecore.LunarEclipseInfo, + boundaryPoints int, + options LunarEclipseOptions, +) ([]byte, error) { + return defaultModule.MarshalLunarEclipseWithOptions(info, boundaryPoints, options) +} + +// MarshalLunarEclipseWithTimeMarkers 编码月食,并沿半影开始到结束的月下点轨迹追加 Point 要素。 +// MarshalLunarEclipseWithTimeMarkers encodes a lunar eclipse and adds Point Features along the sublunar track from penumbral start through end. +// 已有要素保持不变;标记标签使用 options.Location,时间值保持 UTC。 +// Existing features are unchanged; marker labels use options.Location while time values stay UTC. +func MarshalLunarEclipseWithTimeMarkers( + info eclipsecore.LunarEclipseInfo, + boundaryPoints int, + options TimeMarkerOptions, +) ([]byte, error) { + return defaultModule.MarshalLunarEclipseWithTimeMarkers(info, boundaryPoints, options) +} + +// MarshalSolarEclipse 将日食半影足迹和可选中心食带编码为 GeoJSON。 +// MarshalSolarEclipse encodes penumbral footprints and an optional central path as GeoJSON. +func MarshalSolarEclipse( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, +) ([]byte, error) { + return defaultModule.MarshalSolarEclipse(partial, central) +} + +// MarshalSolarEclipseShadowInstant 单时刻阴影足迹的最小 GeoJSON:只含该时刻的区域与物理边界 / one instant as the smallest useful GeoJSON. +func MarshalSolarEclipseShadowInstant( + instant eclipsecore.SolarEclipseShadowInstant, +) ([]byte, error) { + return defaultModule.MarshalSolarEclipseShadowInstant(instant) +} + +// MarshalSolarEclipseWithOptions 编码日食,输出内容由 options 选择 / encodes a solar eclipse with the content selected by options. +func MarshalSolarEclipseWithOptions( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, + options SolarEclipseOptions, +) ([]byte, error) { + return defaultModule.MarshalSolarEclipseWithOptions(partial, central, options) +} + +// MarshalSolarEclipseWithTimeMarkers 编码日食,并沿中心线按固定间隔追加 Point 要素;已有要素不变,标记标签使用 options.Location,时间值保持 UTC。 +// MarshalSolarEclipseWithTimeMarkers encodes a solar eclipse and adds Point Features at regular intervals along the central line. Existing features are unchanged; marker labels use options.Location while time values stay UTC. +func MarshalSolarEclipseWithTimeMarkers( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, + options TimeMarkerOptions, +) ([]byte, error) { + return defaultModule.MarshalSolarEclipseWithTimeMarkers(partial, central, options) +} + +// MarshalMercuryTransit 将水星凌日的全球可见分区、一触/四触视地平线与可选站心接触等时线编码为 GeoJSON。 +// MarshalMercuryTransit encodes the global visibility regions, C1/C4 apparent horizons and optional topocentric contact isochrones of a Mercury transit. +func MarshalMercuryTransit(info mercury.TransitInfo, options PlanetTransitOptions) ([]byte, error) { + return defaultModule.MarshalMercuryTransit(info, options) +} + +// MarshalVenusTransit 将金星凌日的全球可见分区、一触/四触视地平线与可选站心接触等时线编码为 GeoJSON。 +// MarshalVenusTransit encodes the global visibility regions, C1/C4 apparent horizons and optional topocentric contact isochrones of a Venus transit. +func MarshalVenusTransit(info venus.TransitInfo, options PlanetTransitOptions) ([]byte, error) { + return defaultModule.MarshalVenusTransit(info, options) +} diff --git a/geojson/context.go b/geojson/context.go new file mode 100644 index 0000000..be521e0 --- /dev/null +++ b/geojson/context.go @@ -0,0 +1,50 @@ +package geojson + +import ( + "b612.me/astro/internal/ephem" + "b612.me/astro/moon" +) + +// Generator 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Generator struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Generator { return &Generator{inst: inst} } + +var defaultModule Generator + +// MarshalPlanetOccultation 将月掩行星的部分掩、全掩和中心线编码为 GeoJSON / encode a planetary occultation path as GeoJSON. +func (module *Generator) MarshalPlanetOccultation(path moon.PlanetOccultationPath) ([]byte, error) { + return MarshalPlanetOccultation(path) +} + +// MarshalPlanetOccultationFootprints 将指定时刻可用的精确行星外切和内切足迹编码为 GeoJSON FeatureCollection;事件外返回空集合 / encode planetary occultation footprints at an instant; empty outside the event. +func (module *Generator) MarshalPlanetOccultationFootprints(instant moon.PlanetOccultationInstant) ([]byte, error) { + return MarshalPlanetOccultationFootprints(instant) +} + +// MarshalPlanetOccultationWithTimeMarkers 编码行星月掩,并沿中心线按固定间隔追加 Point 要素 / encode a planetary occultation with time markers along the centre line. +func (module *Generator) MarshalPlanetOccultationWithTimeMarkers( + path moon.PlanetOccultationPath, + options TimeMarkerOptions, +) ([]byte, error) { + return MarshalPlanetOccultationWithTimeMarkers(path, options) +} + +// MarshalStarOccultation 将月掩恒星的全球掩带和中心线编码为 GeoJSON / encode a stellar occultation path as GeoJSON. +func (module *Generator) MarshalStarOccultation(path moon.StarOccultationPath) ([]byte, error) { + return MarshalStarOccultation(path) +} + +// MarshalStarOccultationFootprint 将指定时刻的精确恒星月掩足迹编码为 GeoJSON FeatureCollection;事件外返回空集合 / encode a stellar occultation footprint at an instant; empty outside the event. +func (module *Generator) MarshalStarOccultationFootprint(instant moon.StarOccultationInstant) ([]byte, error) { + return MarshalStarOccultationFootprint(instant) +} + +// MarshalStarOccultationWithTimeMarkers 编码恒星月掩,并沿中心线按固定间隔追加 Point 要素 / encode a stellar occultation with time markers along the centre line. +func (module *Generator) MarshalStarOccultationWithTimeMarkers( + path moon.StarOccultationPath, + options TimeMarkerOptions, +) ([]byte, error) { + return MarshalStarOccultationWithTimeMarkers(path, options) +} diff --git a/geojson/context_test.go b/geojson/context_test.go new file mode 100644 index 0000000..9354c39 --- /dev/null +++ b/geojson/context_test.go @@ -0,0 +1,74 @@ +package geojson + +import ( + "b612.me/astro/basic" + eclipsecore "b612.me/astro/eclipse" + "b612.me/astro/internal/ephem" + "b612.me/astro/moon" + "testing" + "time" +) + +type contextAPI interface { + MarshalLunarEclipse(info eclipsecore.LunarEclipseInfo, boundaryPoints int) ([]byte, error) + MarshalLunarEclipseWithOptions( + info eclipsecore.LunarEclipseInfo, + boundaryPoints int, + options LunarEclipseOptions, + ) ([]byte, error) + MarshalLunarEclipseWithTimeMarkers( + info eclipsecore.LunarEclipseInfo, + boundaryPoints int, + options TimeMarkerOptions, + ) ([]byte, error) + MarshalPlanetOccultation(path moon.PlanetOccultationPath) ([]byte, error) + MarshalPlanetOccultationFootprints(instant moon.PlanetOccultationInstant) ([]byte, error) + MarshalPlanetOccultationWithTimeMarkers( + path moon.PlanetOccultationPath, + options TimeMarkerOptions, + ) ([]byte, error) + MarshalSolarEclipse( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, + ) ([]byte, error) + MarshalSolarEclipseShadowInstant( + instant eclipsecore.SolarEclipseShadowInstant, + ) ([]byte, error) + MarshalSolarEclipseWithOptions( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, + options SolarEclipseOptions, + ) ([]byte, error) + MarshalSolarEclipseWithTimeMarkers( + partial eclipsecore.SolarEclipsePartialFootprintsInfo, + central *eclipsecore.SolarEclipsePath, + options TimeMarkerOptions, + ) ([]byte, error) + MarshalStarOccultation(path moon.StarOccultationPath) ([]byte, error) + MarshalStarOccultationFootprint(instant moon.StarOccultationInstant) ([]byte, error) + MarshalStarOccultationWithTimeMarkers( + path moon.StarOccultationPath, + options TimeMarkerOptions, + ) ([]byte, error) +} + +var _ contextAPI = (*Generator)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + info, ok := eclipsecore.LunarEclipseOnDateWithInstance(time.Date(2024, 3, 25, 0, 0, 0, 0, time.UTC), ephem.NewModelInstance(model)) + if !ok { + t.Fatal("known lunar eclipse not found") + } + want, wantErr := MarshalLunarEclipse(info, 12) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + got, gotErr := module.MarshalLunarEclipse(info, 12) + if gotErr != nil || wantErr != nil || string(got) != string(want) { + t.Fatalf("model %d: GeoJSON escaped instance: %v/%v", model, gotErr, wantErr) + } + } +} diff --git a/geojson/eclipse.go b/geojson/eclipse.go index b675294..d32db23 100644 --- a/geojson/eclipse.go +++ b/geojson/eclipse.go @@ -6,7 +6,6 @@ import ( "sort" "time" - "b612.me/astro" "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" "b612.me/astro/internal/geodata" @@ -447,13 +446,12 @@ const ( maximumLunarBoundaryPoints = 1440 ) -// MarshalSolarEclipse 将日食半影足迹和可选中心食带编码为 GeoJSON。 -// MarshalSolarEclipse encodes penumbral footprints and an optional central path as GeoJSON. -func MarshalSolarEclipse( +// MarshalSolarEclipse 将日食半影足迹和可选中心食带编码为 GeoJSON / encode solar eclipse footprints and the central band as GeoJSON. +func (module *Generator) MarshalSolarEclipse( partial eclipsecore.SolarEclipsePartialFootprintsInfo, central *eclipsecore.SolarEclipsePath, ) ([]byte, error) { - return marshalSolarEclipse(partial, central, SolarEclipseOptions{}) + return marshalSolarEclipse(partial, central, SolarEclipseOptions{}, module) } // SolarEclipseOptions 控制日食 GeoJSON 的输出内容。 @@ -473,29 +471,32 @@ type SolarEclipseOptions struct { } // MarshalSolarEclipseWithOptions 编码日食,输出内容由 options 选择 / encodes a solar eclipse with the content selected by options. -func MarshalSolarEclipseWithOptions( +func (module *Generator) MarshalSolarEclipseWithOptions( partial eclipsecore.SolarEclipsePartialFootprintsInfo, central *eclipsecore.SolarEclipsePath, options SolarEclipseOptions, ) ([]byte, error) { - return marshalSolarEclipse(partial, central, options) + return marshalSolarEclipse(partial, central, options, module) } -// MarshalSolarEclipseWithTimeMarkers 编码日食,并沿中心线按固定间隔追加 Point 要素;已有要素不变,标记标签使用 options.Location,时间值保持 UTC。 -// MarshalSolarEclipseWithTimeMarkers encodes a solar eclipse and adds Point Features at regular intervals along the central line. Existing features are unchanged; marker labels use options.Location while time values stay UTC. -func MarshalSolarEclipseWithTimeMarkers( +// MarshalSolarEclipseWithTimeMarkers 编码日食,并沿中心线按固定间隔追加 Point 要素;已有要素不变,标记标签使用 options.Location,时间值保持 UTC / encode a solar eclipse with time markers along the centre line. +func (module *Generator) MarshalSolarEclipseWithTimeMarkers( partial eclipsecore.SolarEclipsePartialFootprintsInfo, central *eclipsecore.SolarEclipsePath, options TimeMarkerOptions, ) ([]byte, error) { - return marshalSolarEclipse(partial, central, SolarEclipseOptions{TimeMarkers: &options}) + return marshalSolarEclipse(partial, central, SolarEclipseOptions{TimeMarkers: &options}, module) } func marshalSolarEclipse( partial eclipsecore.SolarEclipsePartialFootprintsInfo, central *eclipsecore.SolarEclipsePath, - options SolarEclipseOptions, -) ([]byte, error) { + options SolarEclipseOptions, modules ...*Generator) ([]byte, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + markerOptions := options.TimeMarkers if markerOptions != nil { if err := validateTimeMarkerOptions(*markerOptions); err != nil { @@ -515,7 +516,7 @@ func marshalSolarEclipse( // 几何(月下点、地平闭合弧、带宽限界)必须用民用时刻算:只有写进属性的时刻换时标。 geometryPartial := partial civilCentral := central - if timeScale == astro.TimeScaleUT1 { + if timeScale == basic.TimeScaleUT1 { partial = eclipsecore.SolarEclipsePartialFootprintsInUT1(partial) if central != nil { converted := eclipsecore.SolarEclipsePathInUT1(*central) @@ -530,7 +531,7 @@ func marshalSolarEclipse( features := make([]feature, 0, len(partial.Footprints)+9) for footprintIndex, footprint := range partial.Footprints { // 闭合弧由月下点决定,必须用未换时标的同一足迹算几何。 - polygon, err := solarPartialFootprintPolygon(geometryPartial.Footprints[footprintIndex], true) + polygon, err := solarPartialFootprintPolygon(geometryPartial.Footprints[footprintIndex], true, module) if err != nil { return nil, err } @@ -552,7 +553,7 @@ func marshalSolarEclipse( footprintProperties["geometry_role"] = "horizon-closed-region" footprintProperties["closure"] = solarHorizonClosureProperties( geometryPartial.Footprints[footprintIndex].Time, footprint.Time, - solarHorizonClosureExact(footprint.Boundaries, footprint.HorizonEnds), + solarHorizonClosureExact(footprint.Boundaries, footprint.HorizonEnds), module, ) } if len(polygon) == 1 { @@ -574,7 +575,7 @@ func marshalSolarEclipse( )) } - if value, source, ok, err := solarPartialBandGeometry(geometryPartial); err != nil { + if value, source, ok, err := solarPartialBandGeometry(geometryPartial, module); err != nil { return nil, fmt.Errorf("geojson: solar partial band: %w", err) } else if ok { bandProperties := cloneProperties(properties) @@ -590,7 +591,7 @@ func marshalSolarEclipse( return nil, err } features, err = appendSolarFootprintFeatures( - features, "central-shadow-footprint", partial.CentralShadowFootprints, properties, + features, "central-shadow-footprint", partial.CentralShadowFootprints, properties, module, ) if err != nil { return nil, err @@ -675,7 +676,7 @@ func marshalSolarEclipse( // ends of the real annular/total region. // 带宽与限界几何按民用时刻构造;写出的时刻仍取换过时标的 central。 geometryCentral := central - if timeScale == astro.TimeScaleUT1 && civilCentral != nil { + if timeScale == basic.TimeScaleUT1 && civilCentral != nil { geometryCentral = civilCentral } bandFootprints = solarCentralBandFootprintsForPath(geometryPartial, geometryCentral) @@ -1061,7 +1062,7 @@ type solarBandSideRun struct { north bool } -// solarCentralBandLimitSidesFromRings 由食带边界派生南北限:逐点投影定侧,同侧最长连通段按路径序拼接。 +// solarCentralBandLimitSidesFromRings 由食带边界派生南北限:环顶点按自身投影侧别切段,同侧最长连通组按路径序拼接。 func solarCentralBandLimitSidesFromRings( rings [][]eclipsecore.SolarEclipsePathPoint, centerLine []eclipsecore.SolarEclipsePathPoint, @@ -1081,9 +1082,7 @@ func solarCentralBandLimitSidesFromRings( return northern, southern, true } -// solarBandSideRuns cuts every ring into runs that keep one side of the center -// line. A run ends where the boundary crosses the center line, jumps across the -// seam, or stalls against the projection. +// solarBandSideRuns 把每个环按逐点投影侧别切成同侧段;投影沿环连续追踪,中心线回折处不跳支。 func solarBandSideRuns( rings [][]eclipsecore.SolarEclipsePathPoint, centerLine []eclipsecore.SolarEclipsePathPoint, @@ -1091,20 +1090,23 @@ func solarBandSideRuns( var runs []solarBandSideRun for _, ring := range rings { points := openSolarPathRing(ring) - if len(points) < 3 { + count := len(points) + if count < 3 { continue } - progress := make([]float64, len(points)) - times := make([]time.Time, len(points)) - north := make([]bool, len(points)) - for index, point := range points { - value, stamp, isNorth, ok := solarBandProjectOnCenterLine(point, centerLine) - if !ok { - return nil, false + projections, start, ok := solarBandTrackRingProjections(points, centerLine) + if !ok { + return nil, false + } + // 从起点之后第一个侧别翻转处开环,使首段不在环的接缝处被截断。 + + origin := start + for offset := 1; offset < count; offset++ { + index := (start + offset) % count + if projections[index].side != projections[(index+count-1)%count].side { + origin = index + break } - progress[index] = value - times[index] = stamp - north[index] = isNorth } current := solarBandSideRun{} flush := func() { @@ -1113,15 +1115,16 @@ func solarBandSideRuns( } current = solarBandSideRun{} } - for index := range points { - next := (index + 1) % len(points) + for offset := 0; offset < count; offset++ { + index := (origin + offset) % count + next := (index + 1) % count + projection := projections[index] current.points = append(current.points, points[index]) - current.times = append(current.times, times[index]) - current.progress = append(current.progress, progress[index]) - current.north = north[index] + current.progress = append(current.progress, projection.progress) + current.times = append(current.times, solarBandCenterLineTime(centerLine, projection.progress)) + current.north = projection.side > 0 seamJump := math.Abs(math.Remainder(points[next].Longitude-points[index].Longitude, 360)) > 180 - stalled := math.Abs(progress[next]-progress[index]) > 3 - if north[index] != north[next] || seamJump || stalled { + if projection.side != projections[next].side || seamJump { flush() } } @@ -1130,6 +1133,143 @@ func solarBandSideRuns( return runs, true } +// solarBandTrackRingProjections 为环上每个顶点在中心线的局部最近点中选一个,使"距离 + 沿线跳变超额"之和最小(Viterbi),回折与端点处不跳支。 +func solarBandTrackRingProjections( + points []eclipsecore.SolarEclipsePathPoint, + centerLine []eclipsecore.SolarEclipsePathPoint, +) ([]solarBandSideProjection, int, bool) { + count := len(points) + if len(centerLine) < 2 { + return nil, 0, false + } + middle := float64(len(centerLine)-1) / 2 + along := solarBandCenterLineAlongKM(centerLine) + ringCandidates := make([][]solarBandTrackCandidate, count) + start, startGap := 0, math.Inf(1) + for index, point := range points { + ringCandidates[index] = solarBandTrackCandidates(point, centerLine, along) + // 全局最近点就是距离最小的局部极小。 + nearest := ringCandidates[index][0] + for _, candidate := range ringCandidates[index][1:] { + if candidate.distanceKM < nearest.distanceKM { + nearest = candidate + } + } + if gap := math.Abs(nearest.projection.progress - middle); gap < startGap { + start, startGap = index, gap + } + } + candidates := make([][]solarBandTrackCandidate, count) + for offset := range candidates { + candidates[offset] = ringCandidates[(start+offset)%count] + } + cost := make([][]float64, count) + from := make([][]int, count) + cost[0] = make([]float64, len(candidates[0])) + for state, candidate := range candidates[0] { + cost[0][state] = candidate.distanceKM + } + for offset := 1; offset < count; offset++ { + step := solarCentralBandPointDistanceKM(points[(start+offset-1)%count], points[(start+offset)%count]) + cost[offset] = make([]float64, len(candidates[offset])) + from[offset] = make([]int, len(candidates[offset])) + for state, candidate := range candidates[offset] { + best, bestFrom := math.Inf(1), 0 + for previousState, previous := range candidates[offset-1] { + // 沿中心线的移动超过环上步长两倍的部分才计罚。 + jump := math.Max(0, math.Abs(candidate.alongKM-previous.alongKM)-2*step) + if value := cost[offset-1][previousState] + jump; value < best { + best, bestFrom = value, previousState + } + } + cost[offset][state] = best + candidate.distanceKM + from[offset][state] = bestFrom + } + } + state := 0 + for candidate := range cost[count-1] { + if cost[count-1][candidate] < cost[count-1][state] { + state = candidate + } + } + projections := make([]solarBandSideProjection, count) + for offset := count - 1; offset >= 0; offset-- { + projections[(start+offset)%count] = candidates[offset][state].projection + if offset > 0 { + state = from[offset][state] + } + } + return projections, start, true +} + +type solarBandTrackCandidate struct { + projection solarBandSideProjection + distanceKM float64 + alongKM float64 +} + +// solarBandTrackCandidates 返回顶点到中心线距离沿线的全部局部极小(含平台与两端)。 +func solarBandTrackCandidates( + point eclipsecore.SolarEclipsePathPoint, + centerLine []eclipsecore.SolarEclipsePathPoint, + along []float64, +) []solarBandTrackCandidate { + segments := len(centerLine) - 1 + projections := make([]solarBandSideProjection, segments) + distances := make([]float64, segments) + for position := range projections { + projections[position], distances[position] = solarBandProjectOnSegment(point, centerLine, position) + } + var candidates []solarBandTrackCandidate + for position := range projections { + if position > 0 && distances[position-1] < distances[position] || + position+1 < segments && distances[position+1] < distances[position] { + continue + } + // 平台只取首段,避免同一极小重复入选。 + if position > 0 && distances[position-1] == distances[position] { + continue + } + progress := projections[position].progress + segment := solarBandProgressSegment(progress, len(centerLine)) + fraction := progress - float64(segment) + candidates = append(candidates, solarBandTrackCandidate{ + projection: projections[position], + distanceKM: 111.32 * distances[position], + alongKM: along[segment] + fraction*(along[segment+1]-along[segment]), + }) + } + return candidates +} + +// solarBandCenterLineAlongKM 是中心线各顶点的累计沿线距离。 +func solarBandCenterLineAlongKM(centerLine []eclipsecore.SolarEclipsePathPoint) []float64 { + along := make([]float64, len(centerLine)) + for index := 1; index < len(centerLine); index++ { + along[index] = along[index-1] + solarCentralBandPointDistanceKM(centerLine[index-1], centerLine[index]) + } + return along +} + +func solarBandProgressSegment(progress float64, size int) int { + segment := int(math.Floor(progress)) + if segment > size-2 { + segment = size - 2 + } + if segment < 0 { + segment = 0 + } + return segment +} + +// solarBandCenterLineTime is the center-line time at one fractional position. +func solarBandCenterLineTime(centerLine []eclipsecore.SolarEclipsePathPoint, progress float64) time.Time { + position := solarBandProgressSegment(progress, len(centerLine)) + fraction := math.Max(0, math.Min(1, progress-float64(position))) + first, second := centerLine[position], centerLine[position+1] + return first.Time.Add(time.Duration(float64(second.Time.Sub(first.Time)) * fraction)) +} + // solarBandSidePoints concatenates the runs of one side in path order and // spreads their times evenly, because the export requires strictly increasing // times. Runs that do not touch each other belong to different boundary @@ -1230,43 +1370,41 @@ func meanProgress(values []float64) float64 { return total / float64(len(values)) } -// solarBandProjectOnCenterLine projects one band point onto the center line and -// reports its position along the path, the matching time, and whether it falls -// north of the center line at that position. -func solarBandProjectOnCenterLine( +// solarBandSideProjection is the nearest center-line position of one band vertex. +type solarBandSideProjection struct { + // progress is the segment index plus the fraction along that segment. + progress float64 + // side is +1 when the vertex lies at or north of the center line there, -1 south. + side int +} + +// solarBandProjectOnSegment 返回顶点在中心线第 position 段上的投影与局部平面距离(度)。 +func solarBandProjectOnSegment( point eclipsecore.SolarEclipsePathPoint, centerLine []eclipsecore.SolarEclipsePathPoint, -) (float64, time.Time, bool, bool) { - bestDistance := math.Inf(1) - bestProgress := 0.0 - bestTime := centerLine[0].Time - bestLatitude := centerLine[0].Latitude + position int, +) (solarBandSideProjection, float64) { scale := math.Cos(point.Latitude * math.Pi / 180) - for position := 0; position+1 < len(centerLine); position++ { - first, second := centerLine[position], centerLine[position+1] - ax := math.Remainder(first.Longitude-point.Longitude, 360) * scale - ay := first.Latitude - point.Latitude - bx := math.Remainder(second.Longitude-point.Longitude, 360) * scale - by := second.Latitude - point.Latitude - dx, dy := bx-ax, by-ay - length := dx*dx + dy*dy - fraction := 0.0 - if length > 0 { - fraction = math.Max(0, math.Min(1, -(ax*dx+ay*dy)/length)) - } - distance := math.Hypot(ax+fraction*dx, ay+fraction*dy) - if distance >= bestDistance { - continue - } - bestDistance = distance - bestProgress = float64(position) + fraction - bestTime = first.Time.Add(time.Duration(float64(second.Time.Sub(first.Time)) * fraction)) - bestLatitude = first.Latitude + fraction*(second.Latitude-first.Latitude) + first, second := centerLine[position], centerLine[position+1] + ax := math.Remainder(first.Longitude-point.Longitude, 360) * scale + ay := first.Latitude - point.Latitude + bx := math.Remainder(second.Longitude-point.Longitude, 360) * scale + by := second.Latitude - point.Latitude + dx, dy := bx-ax, by-ay + length := dx*dx + dy*dy + fraction := 0.0 + if length > 0 { + fraction = math.Max(0, math.Min(1, -(ax*dx+ay*dy)/length)) } - if math.IsInf(bestDistance, 1) { - return 0, time.Time{}, false, false + offset := point.Latitude - (first.Latitude + fraction*(second.Latitude-first.Latitude)) + side := -1 + if offset >= 0 { + side = 1 } - return bestProgress, bestTime, point.Latitude >= bestLatitude, true + return solarBandSideProjection{ + progress: float64(position) + fraction, + side: side, + }, math.Hypot(ax+fraction*dx, ay+fraction*dy) } // solarCentralLimitSeparationKM returns the greatest distance from one exported @@ -1802,34 +1940,27 @@ type LunarEclipseOptions struct { EnvelopeLongitudePoints int } -// MarshalLunarEclipseWithOptions 编码月食,输出内容与采样精度由 options 选择。 -// MarshalLunarEclipseWithOptions encodes a lunar eclipse with the content and sampling selected by options. -func MarshalLunarEclipseWithOptions( +// MarshalLunarEclipseWithOptions 编码月食,输出内容与采样精度由 options 选择 / encode a lunar eclipse with selectable content and sampling. +func (module *Generator) MarshalLunarEclipseWithOptions( info eclipsecore.LunarEclipseInfo, boundaryPoints int, options LunarEclipseOptions, ) ([]byte, error) { - return marshalLunarEclipse(info, boundaryPoints, options) + return marshalLunarEclipse(info, boundaryPoints, options, module) } -// MarshalLunarEclipse 将月食 P1/P4 站心月心可见区、几何地平线以及 P1-P4 的时间包络编码为 GeoJSON,不含折射。 -// MarshalLunarEclipse encodes the P1/P4 topocentric Moon-center visibility regions, their geometric horizons and the P1-P4 time envelopes, without refraction. -// boundaryPoints 小于等于零时使用 360;其他值限制在 [12, 1440]。 -// boundaryPoints values <= 0 use 360; other values are clamped to [12, 1440]. -func MarshalLunarEclipse(info eclipsecore.LunarEclipseInfo, boundaryPoints int) ([]byte, error) { - return marshalLunarEclipse(info, boundaryPoints, LunarEclipseOptions{}) +// MarshalLunarEclipse 将月食 P1/P4 站心月心可见区、几何地平线以及 P1-P4 的时间包络编码为 GeoJSON,不含折射 / encode lunar eclipse visibility regions and contact envelopes as GeoJSON. +func (module *Generator) MarshalLunarEclipse(info eclipsecore.LunarEclipseInfo, boundaryPoints int) ([]byte, error) { + return marshalLunarEclipse(info, boundaryPoints, LunarEclipseOptions{}, module) } -// MarshalLunarEclipseWithTimeMarkers 编码月食,并沿半影开始到结束的月下点轨迹追加 Point 要素。 -// MarshalLunarEclipseWithTimeMarkers encodes a lunar eclipse and adds Point Features along the sublunar track from penumbral start through end. -// 已有要素保持不变;标记标签使用 options.Location,时间值保持 UTC。 -// Existing features are unchanged; marker labels use options.Location while time values stay UTC. -func MarshalLunarEclipseWithTimeMarkers( +// MarshalLunarEclipseWithTimeMarkers 编码月食,并沿半影开始到结束的月下点轨迹追加 Point 要素 / encode a lunar eclipse with time markers along the sublunar track. +func (module *Generator) MarshalLunarEclipseWithTimeMarkers( info eclipsecore.LunarEclipseInfo, boundaryPoints int, options TimeMarkerOptions, ) ([]byte, error) { - return marshalLunarEclipse(info, boundaryPoints, LunarEclipseOptions{TimeMarkers: &options}) + return marshalLunarEclipse(info, boundaryPoints, LunarEclipseOptions{TimeMarkers: &options}, module) } type lunarLatitudeInterval struct { @@ -1946,15 +2077,20 @@ type lunarHorizonColumns struct { roots [][]float64 } -func lunarHorizonColumnsAt(at time.Time, longitudePoints int, longitudes []float64) (lunarHorizonColumns, error) { - state := basic.MoonStateAt(basic.Date2JD(at.UTC())) +func lunarHorizonColumnsAt(at time.Time, longitudePoints int, longitudes []float64, modules ...*Generator) (lunarHorizonColumns, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + state := basic.MoonStateAtWithInstance(basic.Date2JD(at.UTC()), module.inst) points := state.MoonHorizon(longitudePoints) if len(points) < 3 { return lunarHorizonColumns{}, fmt.Errorf("geojson: lunar visibility horizon is unavailable") } return lunarHorizonColumns{ state: state, - roots: lunarHorizonSeriesFor(lunarHorizonRefinedPoints(points, at)).rootsByLongitude(longitudes), + roots: lunarHorizonSeriesFor(lunarHorizonRefinedPoints(points, at, module)).rootsByLongitude(longitudes), }, nil } @@ -1979,15 +2115,19 @@ func lunarBandColumn(primary lunarHorizonColumns, longitude float64, index int, // lunarVisibilityUnionColumns 逐经度取 [start, end] 上"月亮在地平上"可见区的并集: // 只比区间端点会漏掉极区掠射时两刻之间短暂露出的窗口,必须按与包络同一档位采样后求并。 func lunarVisibilityUnionColumns( - start, end time.Time, samples, longitudePoints int, longitudes []float64, -) ([][]lunarLatitudeInterval, error) { + start, end time.Time, samples, longitudePoints int, longitudes []float64, modules ...*Generator) ([][]lunarLatitudeInterval, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + columns := make([][]lunarLatitudeInterval, len(longitudes)) if samples < 1 { samples = 1 } for index := 0; index <= samples; index++ { at := start.Add(end.Sub(start) * time.Duration(index) / time.Duration(samples)) - instant, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes) + instant, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes, module) if err != nil { return nil, err } @@ -2022,21 +2162,25 @@ func lunarPenumbraBandGeometries( info eclipsecore.LunarEclipseInfo, boundaryPoints int, options LunarEclipseOptions, - wantMoonset, wantMoonrise bool, -) ([2]geometry, error) { + wantMoonset, wantMoonrise bool, modules ...*Generator) ([2]geometry, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + var result [2]geometry longitudes := lunarVisibilityLongitudes(boundaryPoints, options) longitudePoints := len(longitudes) - 1 columns := make([]lunarHorizonColumns, 2) for index, at := range []time.Time{info.PenumbralStart, info.PenumbralEnd} { - value, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes) + value, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes, module) if err != nil { return result, err } columns[index] = value } umbralColumns, err := lunarVisibilityUnionColumns( - info.PartialStart, info.PartialEnd, lunarUmbralBandSamples(info, options), longitudePoints, longitudes, + info.PartialStart, info.PartialEnd, lunarUmbralBandSamples(info, options), longitudePoints, longitudes, module, ) if err != nil { return result, err @@ -2083,8 +2227,12 @@ func lunarVisibilityEnvelopeGeometries( info eclipsecore.LunarEclipseInfo, boundaryPoints int, options LunarEclipseOptions, - wantUnion, wantIntersection bool, -) (geometry, geometry, error) { + wantUnion, wantIntersection bool, modules ...*Generator) (geometry, geometry, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + start, end := info.PenumbralStart, info.PenumbralEnd if !start.Before(end) { return geometry{}, geometry{}, fmt.Errorf("geojson: lunar eclipse penumbral interval is invalid") @@ -2099,7 +2247,7 @@ func lunarVisibilityEnvelopeGeometries( states := make([]basic.MoonState, len(times)) roots := make([][][]float64, len(times)) for index, at := range times { - columns, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes) + columns, err := lunarHorizonColumnsAt(at, longitudePoints, longitudes, module) if err != nil { return geometry{}, geometry{}, err } @@ -2149,12 +2297,17 @@ func lunarVisibilityEnvelopeGeometries( return unionGeometry, intersectionGeometry, nil } -func lunarHorizonRefinedPoints(points [][2]float64, at time.Time) [][2]float64 { +func lunarHorizonRefinedPoints(points [][2]float64, at time.Time, modules ...*Generator) [][2]float64 { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + horizon := make([]geodata.GeoPoint, len(points)) for index, point := range points { horizon[index] = geodata.GeoPoint{Longitude: point[0], Latitude: point[1]} } - horizon = lunarhorizon.RefineWithin(horizon, at, lunarVisibilityHorizonToleranceDegrees) + horizon = lunarhorizon.RefineWithinWithInstance(horizon, at, lunarVisibilityHorizonToleranceDegrees, module.inst) refined := make([][2]float64, len(horizon)) for index, point := range horizon { refined[index] = [2]float64{point.Longitude, point.Latitude} @@ -2397,8 +2550,12 @@ func lunarVisibilityEnvelopeGeometry(longitudes []float64, columns [][]lunarLati func marshalLunarEclipse( info eclipsecore.LunarEclipseInfo, boundaryPoints int, - options LunarEclipseOptions, -) ([]byte, error) { + options LunarEclipseOptions, modules ...*Generator) ([]byte, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + markerOptions := options.TimeMarkers if markerOptions != nil { if err := validateTimeMarkerOptions(*markerOptions); err != nil { @@ -2414,7 +2571,7 @@ func marshalLunarEclipse( } // 几何一律用民用时刻,只有写进属性的时刻换时标,否则把 UT1 读数当民用时刻会平移月下点。 geometry := info - if timeScale == astro.TimeScaleUT1 { + if timeScale == basic.TimeScaleUT1 { info = eclipsecore.LunarEclipseInfoInUT1(info) } boundaryPoints = normalizeLunarBoundaryPoints(boundaryPoints) @@ -2434,22 +2591,22 @@ func marshalLunarEclipse( {role: "visible-at-p4", horizonRole: "p4-horizon", geometryTime: geometry.PenumbralEnd, labelTime: info.PenumbralEnd}, } for _, contact := range contacts { - points := basic.MoonHorizon(basic.Date2JD(contact.geometryTime.UTC()), boundaryPoints) + points := basic.MoonHorizonWithInstance(basic.Date2JD(contact.geometryTime.UTC()), boundaryPoints, module.inst) horizon := make([]geodata.GeoPoint, len(points)) for index, point := range points { horizon[index] = geodata.GeoPoint{Longitude: point[0], Latitude: point[1]} } - horizon = lunarhorizon.Refine(horizon, contact.geometryTime) + horizon = lunarhorizon.RefineWithInstance(horizon, contact.geometryTime, module.inst) value, err := multiPolygonGeometry([][]geodata.GeoPoint{horizon}) if err != nil { return nil, fmt.Errorf("geojson: %s: %w", contact.role, err) } // 日界线剪裁会在相邻世界各输出一次零宽薄片:顶点全落在同一条子午线上、平面面积只剩 - // 浮点噪声(实测 1.8e-12 deg²,刚好越过共享剪裁器 1e-12 的零面积阈值)。只在本月食 + // 浮点噪声(可能越过共享剪裁器 1e-12 的零面积阈值)。只在本月食 // 可见区导出里丢弃它——共享剪裁器的输出被掩星拓扑依赖,不能在那里过滤。 // The antimeridian split can emit one zero-width sliver per adjacent world: every vertex - // on one meridian and a planar area of pure floating-point noise (measured 1.8e-12 deg^2, - // just past the shared splitter's 1e-12 zero-area floor). Drop it here, in the lunar + // on one meridian and a planar area of pure floating-point noise (possibly past the + // shared splitter's 1e-12 zero-area floor). Drop it here, in the lunar // visibility export only; the shared splitter's output feeds occultation topology and must // not be filtered. value = dropDegenerateMultiPolygonRings(value) @@ -2478,7 +2635,7 @@ func marshalLunarEclipse( wantPenumbraMoonrise := !skippedLunarRole(options.SkipRoles, "penumbra-moonrise") if info.HasPartial && (wantPenumbraMoonset || wantPenumbraMoonrise) { bandGeometries, bandErr := lunarPenumbraBandGeometries( - geometry, boundaryPoints, options, wantPenumbraMoonset, wantPenumbraMoonrise, + geometry, boundaryPoints, options, wantPenumbraMoonset, wantPenumbraMoonrise, module, ) if bandErr != nil { return nil, bandErr @@ -2497,7 +2654,7 @@ func marshalLunarEclipse( wantThroughout := !skippedLunarRole(options.SkipRoles, "visible-throughout-eclipse") if wantDuring || wantThroughout { unionGeometry, intersectionGeometry, envelopeErr := lunarVisibilityEnvelopeGeometries( - geometry, boundaryPoints, options, wantDuring, wantThroughout, + geometry, boundaryPoints, options, wantDuring, wantThroughout, module, ) if envelopeErr != nil { return nil, envelopeErr @@ -2513,7 +2670,7 @@ func marshalLunarEclipse( } } - maximum := lunarSubpoint(geometry.Maximum) + maximum := lunarSubpoint(geometry.Maximum, module) features, err := appendPointFeature( features, lunarEclipseEvent, @@ -2525,7 +2682,7 @@ func marshalLunarEclipse( return nil, err } if markerOptions != nil { - markers, markerErr := lunarEclipseTimeMarkerSamples(geometry, timeScale, *markerOptions) + markers, markerErr := lunarEclipseTimeMarkerSamples(geometry, timeScale, *markerOptions, module) if markerErr != nil { return nil, markerErr } @@ -2547,12 +2704,16 @@ func marshalLunarEclipse( // 因为掩带的面选择依赖这些填充提示,端点外扩会改变极区边缘的面归属。 func solarPartialFootprintPolygon( footprint eclipsecore.SolarEclipsePartialFootprint, - horizonExact bool, -) ([]geodata.GeoPoint, error) { + horizonExact bool, modules ...*Generator) ([]geodata.GeoPoint, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if footprint.Time.IsZero() { return nil, fmt.Errorf("geojson: solar partial footprint time is required") } - input, err := solarClosureFootprint(footprint) + input, err := solarClosureFootprint(footprint, module) if err != nil { return nil, err } @@ -2568,8 +2729,12 @@ func solarPartialFootprintPolygon( // Instantaneous footprints remain available for selecting the current shadow, // but are not part of this time-independent outline. func solarPartialBandGeometry( - partial eclipsecore.SolarEclipsePartialFootprintsInfo, -) (geometry, string, bool, error) { + partial eclipsecore.SolarEclipsePartialFootprintsInfo, modules ...*Generator) (geometry, string, bool, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + if len(partial.PartialBandContours) == 0 || len(partial.RiseSetCurves) == 0 { return solarPartialBandOverlayGeometry(partial.Footprints, partial.RiseSetCurves) } @@ -2585,7 +2750,7 @@ func solarPartialBandGeometry( } footprints := make([]solarclosure.Footprint, 0, len(partial.Footprints)) for _, footprint := range partial.Footprints { - input, err := solarClosureFootprint(footprint) + input, err := solarClosureFootprint(footprint, module) if err != nil { return geometry{}, "", false, err } @@ -2613,7 +2778,7 @@ func solarPartialBandGeometry( } for polygonIndex, polygon := range polygons { for pointIndex, point := range polygon { - polygons[polygonIndex][pointIndex].Longitude = normalizeLongitude(point.Longitude) + polygons[polygonIndex][pointIndex].Longitude = geodata.NormalizeLongitude(point.Longitude) } } value, err := multiPolygonGeometry(polygons) @@ -2655,7 +2820,7 @@ func solarPartialBandOverlayGeometry( } for polygonIndex, polygon := range polygons { for pointIndex, point := range polygon { - polygons[polygonIndex][pointIndex].Longitude = normalizeLongitude(point.Longitude) + polygons[polygonIndex][pointIndex].Longitude = geodata.NormalizeLongitude(point.Longitude) } } value, err := multiPolygonGeometry(polygons) @@ -2743,7 +2908,7 @@ func solarPartialBandGeoPointDistanceToRing( ) float64 { minimum := math.Inf(1) for index := range ring { - minimum = math.Min(minimum, solarCentralBandGeoPointDistanceKM(point, ring[index])) + minimum = math.Min(minimum, geodata.MeanDistanceKM(point, ring[index])) } return minimum } @@ -2760,9 +2925,9 @@ func solarPartialBandBridgeLineToRing( } nearest := func(point geodata.GeoPoint) (int, float64) { bestIndex := 0 - bestDistance := solarCentralBandGeoPointDistanceKM(point, open[0]) + bestDistance := geodata.MeanDistanceKM(point, open[0]) for index := 1; index < len(open); index++ { - if distance := solarCentralBandGeoPointDistanceKM(point, open[index]); distance < bestDistance { + if distance := geodata.MeanDistanceKM(point, open[index]); distance < bestDistance { bestIndex, bestDistance = index, distance } } @@ -2783,7 +2948,7 @@ func solarPartialBandBridgeLineToRing( pathLength := func(points []geodata.GeoPoint) float64 { length := 0.0 for index := 1; index < len(points); index++ { - length += solarCentralBandGeoPointDistanceKM(points[index-1], points[index]) + length += geodata.MeanDistanceKM(points[index-1], points[index]) } return length } @@ -2800,18 +2965,22 @@ func appendSolarFootprintFeatures( features []feature, role string, footprints []eclipsecore.SolarEclipsePartialFootprint, - properties map[string]interface{}, -) ([]feature, error) { + properties map[string]interface{}, modules ...*Generator) ([]feature, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + for _, footprint := range footprints { if role == solarCentralShadowFootprintRole && !footprint.Closed { - appended, err := appendSolarHorizonClosedShadowFootprint(features, footprint, properties) + appended, err := appendSolarHorizonClosedShadowFootprint(features, footprint, properties, module) if err != nil { return nil, err } features = appended continue } - polygon, err := solarPartialFootprintPolygon(footprint, false) + polygon, err := solarPartialFootprintPolygon(footprint, false, module) if err != nil { return nil, fmt.Errorf("geojson: solar %s at %s: %w", role, formatTime(footprint.Time), err) } @@ -2964,7 +3133,7 @@ type solarCentralBandVertexGrid map[int][]geodata.GeoPoint func solarCentralBandVertexGridKey(latitude, longitude float64) int { latitudeCell := int(math.Floor(latitude/solarCentralBandVertexGridDegrees)) + 90 - longitudeCell := int(math.Floor(normalizeLongitude(longitude)/solarCentralBandVertexGridDegrees)) + 180 + longitudeCell := int(math.Floor(geodata.NormalizeLongitude(longitude)/solarCentralBandVertexGridDegrees)) + 180 return latitudeCell*360 + longitudeCell } @@ -2997,7 +3166,7 @@ func (grid solarCentralBandVertexGrid) vertexWithinKM( for latitudeCell := minimumLatitudeCell; latitudeCell <= maximumLatitudeCell; latitudeCell++ { for longitudeCell := minimumLongitudeCell; longitudeCell <= maximumLongitudeCell; longitudeCell++ { for _, vertex := range grid[latitudeCell*360+longitudeCell] { - if solarCentralBandGeoPointDistanceKM(point, vertex) <= toleranceKM { + if geodata.MeanDistanceKM(point, vertex) <= toleranceKM { return true } } @@ -3108,7 +3277,7 @@ func solarCentralBandCenterlineProbes( return nil } probes = append(probes, geodata.GeoPoint{ - Longitude: normalizeLongitude( + Longitude: geodata.NormalizeLongitude( point.Longitude + math.Remainder(next.Longitude-point.Longitude, 360)/2, ), Latitude: (point.Latitude + next.Latitude) / 2, @@ -3363,7 +3532,7 @@ func solarCentralMagnitudeOneOrientedClosure( func solarCentralBandPathDistanceKM( first, second eclipsecore.SolarEclipsePathPoint, ) float64 { - return solarCentralBandGeoPointDistanceKM( + return geodata.MeanDistanceKM( geodata.GeoPoint{Longitude: first.Longitude, Latitude: first.Latitude}, geodata.GeoPoint{Longitude: second.Longitude, Latitude: second.Latitude}, ) @@ -3622,7 +3791,7 @@ func snapSolarCentralSweepContacts(polygons [][]geodata.GeoPoint, contacts []geo if used[[2]int{polygonIndex, pointIndex}] { continue } - distance := solarCentralBandGeoPointDistanceKM(point, contact) + distance := geodata.MeanDistanceKM(point, contact) if distance < bestDistance { bestDistance = distance bestPolygon, bestPoint = polygonIndex, pointIndex @@ -3670,8 +3839,8 @@ func solarCentralTwoLimitRibbonPieces( {centerPrevious, centerCurrent, southCurrent}, {centerPrevious, southCurrent, southPrevious}, } { - if solarCentralBandGeoPointDistanceKM(piece[0], piece[1]) == 0 && - solarCentralBandGeoPointDistanceKM(piece[1], piece[2]) == 0 { + if geodata.MeanDistanceKM(piece[0], piece[1]) == 0 && + geodata.MeanDistanceKM(piece[1], piece[2]) == 0 { continue } pieces = append(pieces, piece) @@ -3934,7 +4103,7 @@ func solarCentralLocalChartUnion(inputs [][]geodata.GeoPoint) ([][]geodata.GeoPo return geodata.GeoPoint{} } return geodata.GeoPoint{ - Longitude: normalizeLongitude(math.Atan2(vector[1]/length, vector[0]/length) * 180 / math.Pi), + Longitude: geodata.NormalizeLongitude(math.Atan2(vector[1]/length, vector[0]/length) * 180 / math.Pi), Latitude: math.Asin(math.Max(-1, math.Min(1, vector[2]/length))) * 180 / math.Pi, } } @@ -3980,7 +4149,7 @@ func solarCentralBandRefineRingSpacing(points []geodata.GeoPoint, targetSpacingK for index, start := range points { end := points[(index+1)%len(points)] result = append(result, start) - steps := int(math.Ceil(solarCentralBandGeoPointDistanceKM(start, end) / targetSpacingKM)) + steps := int(math.Ceil(geodata.MeanDistanceKM(start, end) / targetSpacingKM)) for step := 1; step < steps; step++ { result = append(result, solarCentralBandSphericalInterpolate( start, end, float64(step)/float64(steps), @@ -4018,7 +4187,7 @@ func solarCentralBandSphericalInterpolate( y := firstWeight*firstVector[1] + secondWeight*secondVector[1] z := firstWeight*firstVector[2] + secondWeight*secondVector[2] return geodata.GeoPoint{ - Longitude: normalizeLongitude(math.Atan2(y, x) * 180 / math.Pi), + Longitude: geodata.NormalizeLongitude(math.Atan2(y, x) * 180 / math.Pi), Latitude: math.Atan2(z, math.Hypot(x, y)) * 180 / math.Pi, } } @@ -4056,8 +4225,8 @@ func solarCentralBandContactCaps( return } contactPoint := geodata.GeoPoint{Longitude: contact.Longitude, Latitude: contact.Latitude} - if solarCentralBandGeoPointDistanceKM(contactPoint, boundary[0]) > 2000 || - solarCentralBandGeoPointDistanceKM(contactPoint, boundary[len(boundary)-1]) > 2000 { + if geodata.MeanDistanceKM(contactPoint, boundary[0]) > 2000 || + geodata.MeanDistanceKM(contactPoint, boundary[len(boundary)-1]) > 2000 { return } caps = append(caps, []geodata.GeoPoint{contactPoint, boundary[0], boundary[len(boundary)-1]}) @@ -4067,14 +4236,6 @@ func solarCentralBandContactCaps( return caps } -func solarCentralBandGeoPointDistanceKM(first, second geodata.GeoPoint) float64 { - lat1, lat2 := first.Latitude*math.Pi/180, second.Latitude*math.Pi/180 - dlat := lat2 - lat1 - dlon := math.Remainder((second.Longitude-first.Longitude)*math.Pi/180, 2*math.Pi) - h := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlon/2)*math.Sin(dlon/2) - return 6371.0088 * 2 * math.Asin(math.Sqrt(math.Max(0, math.Min(1, h)))) -} - func solarCentralTwoLimitAxisCappedPolygon( north, south, centerLine []eclipsecore.SolarEclipsePathPoint, ) ([]geodata.GeoPoint, bool) { @@ -4217,11 +4378,10 @@ func solarCentralBandEndpointCaps( } func solarCentralBandPointDistanceKM(first, second eclipsecore.SolarEclipsePathPoint) float64 { - lat1, lat2 := first.Latitude*math.Pi/180, second.Latitude*math.Pi/180 - dlat := lat2 - lat1 - dlon := math.Mod((second.Longitude-first.Longitude)*math.Pi/180+math.Pi, 2*math.Pi) - math.Pi - h := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlon/2)*math.Sin(dlon/2) - return 6371.0088 * 2 * math.Asin(math.Sqrt(math.Max(0, math.Min(1, h)))) + return geodata.MeanDistanceKM( + geodata.GeoPoint{Longitude: first.Longitude, Latitude: first.Latitude}, + geodata.GeoPoint{Longitude: second.Longitude, Latitude: second.Latitude}, + ) } // Near a high-latitude apex a traced limit curve runs through a cusp: its time @@ -4464,7 +4624,7 @@ func degenerateGeoJSONRing(ring [][]float64) bool { minimumLongitude = math.Min(minimumLongitude, ring[index][0]) maximumLongitude = math.Max(maximumLongitude, ring[index][0]) } - // 顶点全落在同一条子午线上时面积只剩求和噪声,量级随顶点数增长(实测 9e-12,越过 1e-12 阈值), + // 顶点全落在同一条子午线上时面积只剩求和噪声,量级随顶点数增长,可能越过 1e-12 阈值; // 必须先按经度跨度判死,不能只靠面积。 if maximumLongitude-minimumLongitude < 1e-9 { return true @@ -4496,27 +4656,41 @@ func lunarEclipseMetadata(info eclipsecore.LunarEclipseInfo) map[string]interfac } } -func solarSubsolarPoint(value time.Time) geodata.GeoPoint { +func solarSubsolarPoint(value time.Time, modules ...*Generator) geodata.GeoPoint { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + ttJDE := basic.UTC2TT(basic.Date2JD(value.UTC())) - ra, dec := basic.HSunApparentRaDec(ttJDE) + ra, dec := basic.HSunApparentRaDecWithInstance(ttJDE, module.inst) ut1JDE := basic.TT2UT1(ttJDE) - longitude := normalizeLongitude(ra - basic.ApparentSiderealTime(ut1JDE)*15) + longitude := geodata.NormalizeLongitude(ra - basic.ApparentSiderealTime(ut1JDE)*15) return geodata.GeoPoint{Longitude: longitude, Latitude: dec} } -func lunarSubpoint(value time.Time) geodata.GeoPoint { +func lunarSubpoint(value time.Time, modules ...*Generator) geodata.GeoPoint { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + ttJDE := basic.UTC2TT(basic.Date2JD(value.UTC())) - ra, dec := basic.HMoonTrueRaDec(ttJDE) + ra, dec := basic.HMoonTrueRaDecWithInstance(ttJDE, module.inst) ut1JDE := basic.TT2UT1(ttJDE) - longitude := normalizeLongitude(ra - basic.ApparentSiderealTime(ut1JDE)*15) + longitude := geodata.NormalizeLongitude(ra - basic.ApparentSiderealTime(ut1JDE)*15) return geodata.GeoPoint{Longitude: longitude, Latitude: dec} } func lunarEclipseTimeMarkerSamples( info eclipsecore.LunarEclipseInfo, - scale astro.TimeScale, - options TimeMarkerOptions, -) ([]pathSample, error) { + scale basic.TimeScale, + options TimeMarkerOptions, modules ...*Generator) ([]pathSample, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + step, err := normalizeTimeMarkerStep(options.Step) if err != nil { return nil, fmt.Errorf("geojson: lunar eclipse time markers: %w", err) @@ -4530,10 +4704,10 @@ func lunarEclipseTimeMarkerSamples( current := firstTimeMarkerAfter(start, step, location) markers := make([]pathSample, 0, capacity) for current.Before(end) { - point := lunarSubpoint(current) + point := lunarSubpoint(current, module) label := current - if scale == astro.TimeScaleUT1 { - label = astro.LabelIn(astro.TimeScaleUT1, current) + if scale == basic.TimeScaleUT1 { + label = basic.LabelIn(basic.TimeScaleUT1, current) } markers = append(markers, pathSample{ Time: label, @@ -4559,12 +4733,12 @@ func normalizeLunarBoundaryPoints(value int) int { } // timeScaleForMarkers 校验导出时标选项:UT1 时刻没有时区语义,配非 UTC 时区时明确失败。 -func timeScaleForMarkers(options *TimeMarkerOptions) (astro.TimeScale, error) { - if options == nil || options.TimeScale != astro.TimeScaleUT1 { - return astro.TimeScaleUTC, nil +func timeScaleForMarkers(options *TimeMarkerOptions) (basic.TimeScale, error) { + if options == nil || options.TimeScale != basic.TimeScaleUT1 { + return basic.TimeScaleUTC, nil } if options.Location != nil && options.Location != time.UTC { - return astro.TimeScaleUTC, fmt.Errorf("geojson: UT1 time properties do not take a non-UTC location") + return basic.TimeScaleUTC, fmt.Errorf("geojson: UT1 time properties do not take a non-UTC location") } - return astro.TimeScaleUT1, nil + return basic.TimeScaleUT1, nil } diff --git a/geojson/eclipse_limb_test.go b/geojson/eclipse_limb_test.go new file mode 100644 index 0000000..5df333c --- /dev/null +++ b/geojson/eclipse_limb_test.go @@ -0,0 +1,49 @@ +package geojson_test + +// 开启月缘时偏食足迹与食甚等时线必须落在月缘修正后的 P1–P4 内,整条管线(足迹 → 中心带 → 编码)不得被校验拒收。 + +import ( + "testing" + "time" + + "b612.me/astro/eclipse" + "b612.me/astro/geojson" + "b612.me/astro/moon/limb" +) + +func TestMarshalSolarEclipseWithLunarLimb(t *testing.T) { + module := eclipse.NewWithInstance(nil).WithLunarLimb(limb.Default()) + for _, date := range []time.Time{ + time.Date(2028, 7, 22, 0, 0, 0, 0, time.UTC), + time.Date(2029, 6, 12, 0, 0, 0, 0, time.UTC), + time.Date(2032, 5, 9, 0, 0, 0, 0, time.UTC), + time.Date(2040, 11, 4, 0, 0, 0, 0, time.UTC), + } { + label := date.Format("2006-01-02") + partial, ok := module.SolarEclipsePartialFootprints(date, eclipse.SolarEclipsePartialFootprintOptions{}) + if !ok || len(partial.Footprints) == 0 { + t.Fatalf("%s: footprints unavailable", label) + } + if !partial.Eclipse.LunarLimbApplied { + t.Fatalf("%s: limb not applied to the global contacts", label) + } + begin, end := partial.Eclipse.PartialBeginOnEarth, partial.Eclipse.PartialEndOnEarth + for index, footprint := range partial.Footprints { + if footprint.Time.Before(begin) || footprint.Time.After(end) { + t.Fatalf("%s: footprint %d at %v outside limb P1–P4 %v–%v", label, index, footprint.Time, begin, end) + } + } + for index, contour := range partial.GreatestTimeContours { + if contour.Time.Before(begin) || contour.Time.After(end) { + t.Errorf("%s: greatest-time contour %d at %v outside limb P1–P4", label, index, contour.Time) + } + } + var central *eclipse.SolarEclipsePath + if path, ok := module.SolarEclipseCentralPath(date, eclipse.SolarEclipsePathOptions{}); ok { + central = &path + } + if _, err := geojson.MarshalSolarEclipse(partial, central); err != nil { + t.Errorf("%s: %v", label, err) + } + } +} diff --git a/geojson/geojson.go b/geojson/geojson.go index b152515..fd05217 100644 --- a/geojson/geojson.go +++ b/geojson/geojson.go @@ -21,7 +21,7 @@ import ( "math" "time" - "b612.me/astro" + "b612.me/astro/basic" "b612.me/astro/internal/geodata" ) @@ -75,7 +75,7 @@ type TimeMarkerOptions struct { // 并在集合上输出 time_scale 成员消歧(RFC 3339 的 Z 后缀严格说不等于 UT1)。此时 Location 必须是 nil 或 UTC。 // TimeScale selects the scale of time properties: the zero value is UTC; TimeScaleUT1 writes UT1 labels // and adds a time_scale member (a RFC 3339 Z suffix is not exactly UT1). Location must then be nil or UTC. - TimeScale astro.TimeScale + TimeScale basic.TimeScale } // dropFeaturesByRole 按 role 属性丢弃要素;空列表原样返回。 @@ -108,7 +108,7 @@ func marshalEmptyFeatureCollection() ([]byte, error) { return encodeFeatureCollection(make([]feature, 0)) } -func marshalFeatureCollectionWithTimeScale(features []feature, scale astro.TimeScale) ([]byte, error) { +func marshalFeatureCollectionWithTimeScale(features []feature, scale basic.TimeScale) ([]byte, error) { if len(features) == 0 { return nil, fmt.Errorf("geojson: no geographic features") } @@ -116,12 +116,12 @@ func marshalFeatureCollectionWithTimeScale(features []feature, scale astro.TimeS } func encodeFeatureCollection(features []feature) ([]byte, error) { - return encodeFeatureCollectionWithTimeScale(features, astro.TimeScaleUTC) + return encodeFeatureCollectionWithTimeScale(features, basic.TimeScaleUTC) } -func encodeFeatureCollectionWithTimeScale(features []feature, scale astro.TimeScale) ([]byte, error) { +func encodeFeatureCollectionWithTimeScale(features []feature, scale basic.TimeScale) ([]byte, error) { member := "" - if scale == astro.TimeScaleUT1 { + if scale == basic.TimeScaleUT1 { member = "UT1" } value, err := json.Marshal(featureCollection{Type: featureCollectionType, Features: features, TimeScale: member}) @@ -304,7 +304,7 @@ func interpolateTimeMarker(a, b pathSample, fraction float64, value time.Time) p } return pathSample{ Time: value, - Longitude: normalizeLongitude(a.Longitude + fraction*deltaLongitude), + Longitude: geodata.NormalizeLongitude(a.Longitude + fraction*deltaLongitude), Latitude: a.Latitude + fraction*(b.Latitude-a.Latitude), } } @@ -613,11 +613,3 @@ func formatTime(value time.Time) string { } return value.UTC().Format(time.RFC3339Nano) } - -func normalizeLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} diff --git a/geojson/geojson_test.go b/geojson/geojson_test.go index 5b070a9..6bdbdde 100644 --- a/geojson/geojson_test.go +++ b/geojson/geojson_test.go @@ -2005,6 +2005,9 @@ func TestMarshalPlanetOccultationWithoutFootprintsUsesBands(t *testing.T) { } func TestMarshalPlanetOccultationCompactBandHandlesTangentBranchConvergence(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } for _, date := range []time.Time{ time.Date(1954, time.June, 30, 0, 0, 0, 0, time.UTC), time.Date(1962, time.April, 1, 0, 0, 0, 0, time.UTC), diff --git a/geojson/lunar_penumbra_band_test.go b/geojson/lunar_penumbra_band_test.go index 4924977..83cb232 100644 --- a/geojson/lunar_penumbra_band_test.go +++ b/geojson/lunar_penumbra_band_test.go @@ -13,6 +13,9 @@ import ( // 月出侧 = 食终在地平上、本影阶段整段在地平下、食始也在地平下。本影可见性取整个区间的最大高度, // 只比 U1/U4 会漏掉极区掠射时两刻之间的短暂窗口。用高度独立复算,并允许边界附近半格误差。 func TestLunarGeoJSONPenumbraBandsFollowUmbralContacts(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } date := time.Date(2029, 1, 1, 0, 0, 0, 0, time.FixedZone("CST", 8*3600)) info, ok := eclipse.LunarEclipseOnDate(date) if !ok || !info.HasPartial { diff --git a/geojson/review15_contract_test.go b/geojson/marshal_contract_test.go similarity index 89% rename from geojson/review15_contract_test.go rename to geojson/marshal_contract_test.go index 06df1cd..f094708 100644 --- a/geojson/review15_contract_test.go +++ b/geojson/marshal_contract_test.go @@ -10,10 +10,10 @@ import ( "b612.me/astro/moon" ) -// 本文件钉住 §1.5 的三条契约:全掩可见性轮廓必须校验、退化影区整条缺省、 +// 本文件钉住三条契约:全掩可见性轮廓必须校验、退化影区整条缺省、 // 地平闭合必须能区分精确擦地点闭合与采样端点近似闭合。 -func review15CopyContours(contours [][]moon.OccultationPathPoint) [][]moon.OccultationPathPoint { +func copyOccultationContours(contours [][]moon.OccultationPathPoint) [][]moon.OccultationPathPoint { copied := make([][]moon.OccultationPathPoint, len(contours)) for index, contour := range contours { copied[index] = append([]moon.OccultationPathPoint(nil), contour...) @@ -66,7 +66,7 @@ func TestMarshalPlanetOccultationValidatesTotalVisibilityContours(t *testing.T) } } -func review15DegenerateBoundary(stamp time.Time) [][]eclipse.SolarEclipsePathPoint { +func degenerateShadowBoundary(stamp time.Time) [][]eclipse.SolarEclipsePathPoint { return [][]eclipse.SolarEclipsePathPoint{{ {Time: stamp, Longitude: 0, Latitude: 0}, {Time: stamp, Longitude: 0.01, Latitude: 0}, @@ -78,7 +78,7 @@ func review15DegenerateBoundary(stamp time.Time) [][]eclipse.SolarEclipsePathPoi func TestMarshalSolarEclipseShadowInstantSkipsDegenerateRegion(t *testing.T) { stamp := time.Date(2024, time.April, 8, 18, 0, 0, 0, time.UTC) instant := eclipse.SolarEclipseShadowInstant{ - Time: stamp, Closed: true, Boundaries: review15DegenerateBoundary(stamp), + Time: stamp, Closed: true, Boundaries: degenerateShadowBoundary(stamp), } raw, err := geojson.MarshalSolarEclipseShadowInstant(instant) if err != nil { @@ -95,7 +95,7 @@ func TestMarshalSolarEclipseShadowInstantSkipsDegenerateRegion(t *testing.T) { } } -func review15HorizonCutInstant(t *testing.T) eclipse.SolarEclipseShadowInstant { +func horizonCutShadowInstant(t *testing.T) eclipse.SolarEclipseShadowInstant { t.Helper() date := time.Date(2009, time.July, 22, 0, 0, 0, 0, time.UTC) info, ok := eclipse.SolarEclipsePartialFootprints(date, eclipse.SolarEclipsePartialFootprintOptions{ @@ -118,7 +118,7 @@ func review15HorizonCutInstant(t *testing.T) eclipse.SolarEclipseShadowInstant { return eclipse.SolarEclipseShadowInstant{} } -func review15ClosureExact(t *testing.T, instant eclipse.SolarEclipseShadowInstant) interface{} { +func shadowClosureExact(t *testing.T, instant eclipse.SolarEclipseShadowInstant) interface{} { t.Helper() raw, err := geojson.MarshalSolarEclipseShadowInstant(instant) if err != nil { @@ -136,13 +136,13 @@ func review15ClosureExact(t *testing.T, instant eclipse.SolarEclipseShadowInstan } func TestMarshalSolarEclipseShadowInstantMarksSampledClosure(t *testing.T) { - instant := review15HorizonCutInstant(t) - if exact := review15ClosureExact(t, instant); exact != true { + instant := horizonCutShadowInstant(t) + if exact := shadowClosureExact(t, instant); exact != true { t.Fatalf("closure exact=%v, want true with two grazing points", exact) } sampled := instant sampled.HorizonEnds = nil - if exact := review15ClosureExact(t, sampled); exact != false { + if exact := shadowClosureExact(t, sampled); exact != false { t.Fatalf("closure exact=%v, want false without grazing points", exact) } } diff --git a/geojson/occultation.go b/geojson/occultation.go index fd17679..6ffe497 100644 --- a/geojson/occultation.go +++ b/geojson/occultation.go @@ -5,7 +5,7 @@ import ( "math" "time" - "b612.me/astro" + "b612.me/astro/basic" "b612.me/astro/internal/geodata" "b612.me/astro/internal/occultationgeo" "b612.me/astro/moon" @@ -124,7 +124,7 @@ func marshalStarOccultation(path moon.StarOccultationPath, markerOptions *TimeMa if scaleErr != nil { return nil, scaleErr } - if timeScale == astro.TimeScaleUT1 { + if timeScale == basic.TimeScaleUT1 { path = moon.StarOccultationPathInUT1(path) } if err := validateStarOccultationPathData(path); err != nil { @@ -269,7 +269,7 @@ func marshalPlanetOccultation(path moon.PlanetOccultationPath, markerOptions *Ti if scaleErr != nil { return nil, scaleErr } - if timeScale == astro.TimeScaleUT1 { + if timeScale == basic.TimeScaleUT1 { path = moon.PlanetOccultationPathInUT1(path) } if err := path.Planet.Validate(); err != nil { @@ -846,7 +846,7 @@ func occultationNearestRingVertex( index := -1 distance := math.Inf(1) for candidate, value := range ring { - current := occultationGeoDistanceKM(point, value) + current := geodata.EquatorialDistanceKM(point, value) if current < distance { index, distance = candidate, current } @@ -862,7 +862,7 @@ func occultationRingPathLengthKM(ring []geodata.GeoPoint, start, end, direction index := start for index != end { next := (index + direction + len(ring)) % len(ring) - length += occultationGeoDistanceKM(ring[index], ring[next]) + length += geodata.EquatorialDistanceKM(ring[index], ring[next]) index = next if length > 1e8 { return math.Inf(1) @@ -873,7 +873,7 @@ func occultationRingPathLengthKM(ring []geodata.GeoPoint, start, end, direction func occultationRingEdgesWithinKM(ring []geodata.GeoPoint, maximum float64) bool { for index := 1; index < len(ring); index++ { - if occultationGeoDistanceKM(ring[index-1], ring[index]) > maximum { + if geodata.EquatorialDistanceKM(ring[index-1], ring[index]) > maximum { return false } } @@ -883,23 +883,11 @@ func occultationRingEdgesWithinKM(ring []geodata.GeoPoint, maximum float64) bool func occultationGeoLineLengthKM(points []geodata.GeoPoint) float64 { length := 0.0 for index := 1; index < len(points); index++ { - length += occultationGeoDistanceKM(points[index-1], points[index]) + length += geodata.EquatorialDistanceKM(points[index-1], points[index]) } return length } -func occultationGeoDistanceKM(first, second geodata.GeoPoint) float64 { - const radiusKM = 6378.1366 - firstLatitude := first.Latitude * math.Pi / 180 - secondLatitude := second.Latitude * math.Pi / 180 - deltaLatitude := secondLatitude - firstLatitude - deltaLongitude := math.Remainder(second.Longitude-first.Longitude, 360) * math.Pi / 180 - sineLatitude := math.Sin(deltaLatitude / 2) - sineLongitude := math.Sin(deltaLongitude / 2) - a := sineLatitude*sineLatitude + math.Cos(firstLatitude)*math.Cos(secondLatitude)*sineLongitude*sineLongitude - return 2 * radiusKM * math.Atan2(math.Sqrt(math.Max(0, a)), math.Sqrt(math.Max(0, 1-a))) -} - func reverseOccultationGeoPoints(points []geodata.GeoPoint) { for left, right := 0, len(points)-1; left < right; left, right = left+1, right-1 { points[left], points[right] = points[right], points[left] diff --git a/geojson/occultation_envelope_regression_test.go b/geojson/occultation_envelope_regression_test.go index 635c2ef..fd07537 100644 --- a/geojson/occultation_envelope_regression_test.go +++ b/geojson/occultation_envelope_regression_test.go @@ -11,6 +11,9 @@ import ( ) func TestOccultationDistantEnvelopesRemainComplete(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } if err := basic.LoadStarData(); err != nil { t.Fatal(err) } diff --git a/geojson/occultation_noncentral_band_regression_test.go b/geojson/occultation_noncentral_band_regression_test.go index 0809498..5febd8b 100644 --- a/geojson/occultation_noncentral_band_regression_test.go +++ b/geojson/occultation_noncentral_band_regression_test.go @@ -176,14 +176,7 @@ func TestMarshalPlanetOccultationVisibilityBoundaryStaysInsidePartialBand(t *tes // TestMarshalPlanetOccultationHorizonConnectorsFollowBandFootprintSource 固定连接线与掩带同源的 // 契约:紧凑模式下两者都由紧凑足迹给出,连接线存在;默认(密集瞬时足迹)模式下瞬时足迹没有 -// 可与相位端点配对的开放地平边界,因此不产生连接线。此前掩带会走解析回退、连接线却按空的 -// 原始紧凑足迹生成,两种模式的连接线来源不一致。 -// TestMarshalPlanetOccultationHorizonConnectorsFollowBandFootprintSource pins the contract that -// connectors and band share one footprint source: in compact mode both come from the compact -// footprints and connectors exist, while dense instantaneous footprints carry no open horizon -// boundary to pair with phase endpoints and therefore yield none. Previously a band built from -// the analytic fallback still asked for connectors from the empty compact footprints, so the two -// modes disagreed about the connector source. +// 可与相位端点配对的开放地平边界,因此不产生连接线。 func TestMarshalPlanetOccultationHorizonConnectorsFollowBandFootprintSource(t *testing.T) { start := time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC) for _, test := range []struct { diff --git a/geojson/regulus_20261007_regression_test.go b/geojson/regulus_20261007_regression_test.go new file mode 100644 index 0000000..632f408 --- /dev/null +++ b/geojson/regulus_20261007_regression_test.go @@ -0,0 +1,70 @@ +package geojson_test + +import ( + "encoding/json" + "testing" + "time" + + "b612.me/astro/basic" + "b612.me/astro/geojson" + "b612.me/astro/moon" +) + +func TestMarshalStarOccultationRegulus20261007UsesAnalyticVisibleEnvelope(t *testing.T) { + if err := basic.LoadStarData(); err != nil { + t.Fatal(err) + } + starData, err := basic.StarDataByHR(3982) + if err != nil { + t.Fatal(err) + } + star, err := moon.StarCoordinateFromStarData(starData) + if err != nil { + t.Fatal(err) + } + start := time.Date(2026, time.October, 7, 0, 0, 0, 0, time.UTC) + paths, err := moon.FindStarOccultationPaths(start, start.Add(24*time.Hour), star, moon.OccultationPathOptions{}) + if err != nil || len(paths) != 1 { + t.Fatalf("FindStarOccultationPaths() paths=%d err=%v, want one", len(paths), err) + } + path := paths[0] + if len(path.Footprints) == 0 || len(path.BandFootprints) != 0 { + t.Fatalf("dense path footprints=%d bandFootprints=%d, want dense mode", len(path.Footprints), len(path.BandFootprints)) + } + data, err := geojson.MarshalStarOccultation(path) + if err != nil { + t.Fatalf("MarshalStarOccultation: %v", err) + } + collection := decodeCollection(t, data) + band := featureWithRole(t, collection, "occultation-band") + if authoritative, ok := band.Properties["static_band_authoritative"].(bool); !ok || !authoritative { + t.Fatalf("occultation-band source=%v authoritative=%v, want analytic authoritative boundary", + band.Properties["source"], band.Properties["static_band_authoritative"]) + } + if source := band.Properties["source"]; source != "visible-footprint-sweep" { + t.Fatalf("occultation-band source=%v, want visible-footprint-sweep", source) + } + assertClosedMultiPolygon(t, band) + if rings := geoJSONMultiPolygonOuterRings(t, band); len(rings) != 1 { + t.Fatalf("occultation-band polygons=%d, want one continuous visible band", len(rings)) + } + assertOccultationBandMaximumEdge(t, band, 45) + for _, feature := range featuresWithRole(collection, "visibility-boundary") { + var lines [][][]float64 + if err := json.Unmarshal(feature.Geometry.Coordinates, &lines); err != nil { + t.Fatalf("decode visibility-boundary: %v", err) + } + for lineIndex, line := range lines { + for pointIndex, point := range line { + if len(point) < 2 || !geometryContainsPointWithinKM(t, band.Geometry, point[0], point[1], 2) { + t.Fatalf("visibility-boundary line %d point %d lies outside visible band at %.6f, %.6f", lineIndex, pointIndex, point[0], point[1]) + } + } + } + } + for index, point := range path.CenterLine { + if !geometryContainsPoint(t, band.Geometry, point.Longitude, point.Latitude) { + t.Fatalf("center-line sample %d lies outside visible band at %.6f, %.6f", index, point.Longitude, point.Latitude) + } + } +} diff --git a/geojson/review15_coverage_internal_test.go b/geojson/solar_central_band_coverage_internal_test.go similarity index 70% rename from geojson/review15_coverage_internal_test.go rename to geojson/solar_central_band_coverage_internal_test.go index 0330164..66b6692 100644 --- a/geojson/review15_coverage_internal_test.go +++ b/geojson/solar_central_band_coverage_internal_test.go @@ -9,11 +9,11 @@ import ( "b612.me/astro/internal/geodata" ) -// 本文件钉住 §1.5 的两条导出契约:覆盖判据必须逐点成立(不能靠抽样), +// 本文件钉住两条导出契约:覆盖判据必须逐点成立(不能靠抽样), // 中心线走廊必须让每个探针都落在容差内,且只修补越界的那一段。 -const review15ToleranceKM = solarCentralBandCoverageToleranceKM +const centralBandToleranceKM = solarCentralBandCoverageToleranceKM -func review15SquareRing(minLongitude, minLatitude, maxLongitude, maxLatitude float64) []geodata.GeoPoint { +func centralBandSquareRing(minLongitude, minLatitude, maxLongitude, maxLatitude float64) []geodata.GeoPoint { return []geodata.GeoPoint{ {Longitude: minLongitude, Latitude: minLatitude}, {Longitude: maxLongitude, Latitude: minLatitude}, @@ -22,7 +22,7 @@ func review15SquareRing(minLongitude, minLatitude, maxLongitude, maxLatitude flo } } -func review15CenterLine(points ...[2]float64) []eclipsecore.SolarEclipsePathPoint { +func centralBandCenterLine(points ...[2]float64) []eclipsecore.SolarEclipsePathPoint { base := time.Date(2024, time.April, 8, 18, 0, 0, 0, time.UTC) line := make([]eclipsecore.SolarEclipsePathPoint, len(points)) for index, point := range points { @@ -36,7 +36,7 @@ func review15CenterLine(points ...[2]float64) []eclipsecore.SolarEclipsePathPoin } func TestSolarCentralBandPointsCoverProbesEveryVertex(t *testing.T) { - polygons := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} + polygons := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} points := make([]geodata.GeoPoint, 0, 257) for index := 0; index < 257; index++ { points = append(points, geodata.GeoPoint{ @@ -44,32 +44,32 @@ func TestSolarCentralBandPointsCoverProbesEveryVertex(t *testing.T) { Latitude: 0.1 + 0.8*float64(index%97)/96, }) } - if !solarCentralBandPointsCover(polygons, points, review15ToleranceKM) { + if !solarCentralBandPointsCover(polygons, points, centralBandToleranceKM) { t.Fatal("points inside the ring must be covered") } for index := range points { moved := append([]geodata.GeoPoint(nil), points...) moved[index] = geodata.GeoPoint{Longitude: 40, Latitude: 40} - if solarCentralBandPointsCover(polygons, moved, review15ToleranceKM) { + if solarCentralBandPointsCover(polygons, moved, centralBandToleranceKM) { t.Fatalf("vertex %d outside the tolerance was accepted; every vertex must be probed", index) } } } func TestSolarCentralBandPointsCoverKeepsMacroTolerance(t *testing.T) { - polygons := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} + polygons := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} near := []geodata.GeoPoint{{Longitude: 0.5, Latitude: 1.4}} far := []geodata.GeoPoint{{Longitude: 0.5, Latitude: 2.0}} - if !solarCentralBandPointsCover(polygons, near, review15ToleranceKM) { + if !solarCentralBandPointsCover(polygons, near, centralBandToleranceKM) { t.Fatal("a point 45 km outside must stay inside the 100 km macro tolerance") } - if solarCentralBandPointsCover(polygons, far, review15ToleranceKM) { + if solarCentralBandPointsCover(polygons, far, centralBandToleranceKM) { t.Fatal("a point 111 km outside must exceed the 100 km macro tolerance") } } func TestSolarCentralBandRingsCoverProbesEveryFootprintVertex(t *testing.T) { - rings := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} + rings := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} stamp := time.Date(2024, time.April, 8, 18, 0, 0, 0, time.UTC) boundary := make([]eclipsecore.SolarEclipsePathPoint, 0, 64) for index := 0; index < 64; index++ { @@ -83,7 +83,7 @@ func TestSolarCentralBandRingsCoverProbesEveryFootprintVertex(t *testing.T) { Time: stamp, Boundaries: [][]eclipsecore.SolarEclipsePathPoint{boundary}, }} - if !solarCentralBandRingsCover(rings, review15CenterLine([2]float64{0.5, 0.5}), footprints) { + if !solarCentralBandRingsCover(rings, centralBandCenterLine([2]float64{0.5, 0.5}), footprints) { t.Fatal("a covered footprint must pass the coverage check") } for index := range boundary { @@ -93,15 +93,15 @@ func TestSolarCentralBandRingsCoverProbesEveryFootprintVertex(t *testing.T) { Time: stamp, Boundaries: [][]eclipsecore.SolarEclipsePathPoint{moved}, }} - if solarCentralBandRingsCover(rings, review15CenterLine([2]float64{0.5, 0.5}), probe) { + if solarCentralBandRingsCover(rings, centralBandCenterLine([2]float64{0.5, 0.5}), probe) { t.Fatalf("footprint vertex %d outside the tolerance was accepted", index) } } } func TestSolarCentralBandRingsCoverProbesEveryCenterLineVertex(t *testing.T) { - rings := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} - line := review15CenterLine([2]float64{0.5, 0.5}, [2]float64{0.5, 0.6}, [2]float64{0.5, 0.7}) + rings := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} + line := centralBandCenterLine([2]float64{0.5, 0.5}, [2]float64{0.5, 0.6}, [2]float64{0.5, 0.7}) if !solarCentralBandRingsCover(rings, line, nil) { t.Fatal("a covered center line must pass the coverage check") } @@ -116,8 +116,8 @@ func TestSolarCentralBandRingsCoverProbesEveryCenterLineVertex(t *testing.T) { } func TestSolarCentralBandCorridorCoversEveryCenterlineProbe(t *testing.T) { - polygons := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} - line := review15CenterLine([2]float64{0.5, 1.2}, [2]float64{0.5, 1.8}) + polygons := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} + line := centralBandCenterLine([2]float64{0.5, 1.2}, [2]float64{0.5, 1.8}) repaired := solarCentralBandWithCenterlineCorridor(polygons, line) probes := solarCentralBandCenterlineProbes(line) miss := geodata.SphericalPolygonsPathMissDistanceKM( @@ -130,8 +130,8 @@ func TestSolarCentralBandCorridorCoversEveryCenterlineProbe(t *testing.T) { } func TestSolarCentralBandCorridorPatchesOnlyTheClippedSegment(t *testing.T) { - polygons := [][]geodata.GeoPoint{review15SquareRing(-1, -0.05, 1, 0.05)} - line := review15CenterLine([2]float64{-0.5, 0.08}, [2]float64{0.5, 0.6}) + polygons := [][]geodata.GeoPoint{centralBandSquareRing(-1, -0.05, 1, 0.05)} + line := centralBandCenterLine([2]float64{-0.5, 0.08}, [2]float64{0.5, 0.6}) repaired := solarCentralBandWithCenterlineCorridor(polygons, line) witness := []geodata.GeoPoint{{Longitude: -0.5, Latitude: 0.35}} if covered := geodata.SphericalPolygonsContainPoints(repaired, witness); covered[0] { @@ -140,8 +140,8 @@ func TestSolarCentralBandCorridorPatchesOnlyTheClippedSegment(t *testing.T) { } func TestSolarCentralBandCorridorLeavesUnboundedMissAlone(t *testing.T) { - polygons := [][]geodata.GeoPoint{review15SquareRing(0, 0, 1, 1)} - line := review15CenterLine([2]float64{0.5, 0.5}, [2]float64{0.5, 20}) + polygons := [][]geodata.GeoPoint{centralBandSquareRing(0, 0, 1, 1)} + line := centralBandCenterLine([2]float64{0.5, 0.5}, [2]float64{0.5, 20}) repaired := solarCentralBandWithCenterlineCorridor(polygons, line) if len(repaired) != len(polygons) { t.Fatalf("polygons=%d, want the input unchanged when the miss exceeds the corridor cap", len(repaired)) @@ -154,7 +154,7 @@ func TestSolarCentralBandCorridorLeavesUnboundedMissAlone(t *testing.T) { } func TestSolarShadowRegionDegenerateSkipsOnlyDegenerateRings(t *testing.T) { - normal := review15SquareRing(0, 0, 0.1, 0.1) + normal := centralBandSquareRing(0, 0, 0.1, 0.1) if solarShadowRegionDegenerate(normal, normal) { t.Fatal("an 11 km square must not be degenerate") } @@ -255,8 +255,8 @@ func TestAppendSolarFootprintFeaturesSkipsDegenerateFootprint(t *testing.T) { } } -// review15ProjectTime 独立复算顶点在中心线上的投影时刻。 -func review15ProjectTime( +// centralBandProjectTime 独立复算顶点在中心线上的投影时刻。 +func centralBandProjectTime( point eclipsecore.SolarEclipsePathPoint, centerLine []eclipsecore.SolarEclipsePathPoint, ) time.Time { @@ -324,10 +324,76 @@ func TestSolarCentralBandLimitSidesKeepProjectionTimes(t *testing.T) { if !side.north && point.Latitude >= 0 { t.Fatalf("%s vertex %d latitude=%v, want south of the center line", side.name, index, point.Latitude) } - projected := review15ProjectTime(point, centerLine) + projected := centralBandProjectTime(point, centerLine) if delta := point.Time.Sub(projected); delta > time.Millisecond || delta < -time.Millisecond { t.Fatalf("%s vertex %d time=%v, projected=%v", side.name, index, point.Time, projected) } } } } + +// 掠食端中心线回折时,界线顶点的最近点投影会落到回折支上(1552-07-21)。 +// 投影沿环连续追踪后,顶点不得被丢弃,界线也不得跳点抄近路。 +func TestSolarCentralBandLimitSidesSurviveFoldedCenterLine(t *testing.T) { + base := time.Date(1552, time.July, 21, 6, 0, 0, 0, time.UTC) + centerLine := []eclipsecore.SolarEclipsePathPoint{ + {Time: base, Longitude: 0, Latitude: 0}, + {Time: base.Add(5 * time.Minute), Longitude: 0.5, Latitude: 0}, + {Time: base.Add(10 * time.Minute), Longitude: 1, Latitude: 0}, + {Time: base.Add(12 * time.Minute), Longitude: 1, Latitude: 0.25}, + {Time: base.Add(15 * time.Minute), Longitude: 0.6, Latitude: 0.25}, + } + northVertices := []eclipsecore.SolarEclipsePathPoint{ + {Longitude: 0, Latitude: 0.15}, + {Longitude: 0.3, Latitude: 0.15}, + // 这两点离回折支(0.10°)比离去程支(0.15°)更近,仍须判到北侧。 + {Longitude: 0.65, Latitude: 0.15}, + {Longitude: 0.8, Latitude: 0.15}, + {Longitude: 0.95, Latitude: 0.15}, + } + southVertices := []eclipsecore.SolarEclipsePathPoint{ + {Longitude: 0.95, Latitude: -0.15}, + {Longitude: 0.5, Latitude: -0.15}, + {Longitude: 0, Latitude: -0.15}, + } + ring := append([]eclipsecore.SolarEclipsePathPoint(nil), northVertices...) + ring = append(ring, eclipsecore.SolarEclipsePathPoint{Longitude: 1.05, Latitude: 0}) + ring = append(ring, southVertices...) + ring = append(ring, eclipsecore.SolarEclipsePathPoint{Longitude: -0.05, Latitude: 0}) + north, south, ok := solarCentralBandLimitSidesFromRings( + [][]eclipsecore.SolarEclipsePathPoint{ring}, centerLine, + ) + if !ok { + t.Fatal("folded center line must still produce two limit sides") + } + for _, side := range []struct { + name string + want []eclipsecore.SolarEclipsePathPoint + points []eclipsecore.SolarEclipsePathPoint + }{{"north-limit", northVertices, north}, {"south-limit", southVertices, south}} { + for _, vertex := range side.want { + found := false + for _, point := range side.points { + found = found || point.Longitude == vertex.Longitude && point.Latitude == vertex.Latitude + } + if !found { + t.Fatalf("%s lost boundary vertex (%v,%v): %+v", side.name, vertex.Longitude, vertex.Latitude, side.points) + } + } + for index := 1; index < len(side.points); index++ { + if !side.points[index].Time.After(side.points[index-1].Time) { + t.Fatalf("%s times not increasing at %d: %+v", side.name, index, side.points) + } + } + } + for _, point := range north { + if point.Latitude < 0 { + t.Fatalf("north-limit took southern vertex (%v,%v)", point.Longitude, point.Latitude) + } + } + for _, point := range south { + if point.Latitude > 0 { + t.Fatalf("south-limit took northern vertex (%v,%v)", point.Longitude, point.Latitude) + } + } +} diff --git a/geojson/solar_eclipse_20120521_regression_test.go b/geojson/solar_eclipse_20120521_regression_test.go index 98929ca..965c032 100644 --- a/geojson/solar_eclipse_20120521_regression_test.go +++ b/geojson/solar_eclipse_20120521_regression_test.go @@ -2,11 +2,14 @@ package geojson_test import ( "encoding/json" + "fmt" "testing" "time" "b612.me/astro/eclipse" "b612.me/astro/geojson" + + "b612.me/astro/internal/ephemtest" ) func TestSolarEclipse20120521HasCentralBandHorizonClosure(t *testing.T) { @@ -20,10 +23,7 @@ func TestSolarEclipse20120521HasCentralBandHorizonClosure(t *testing.T) { if len(partial.CentralBandHorizonClosures) != 2 { t.Fatalf("central-limit horizon closures=%d, want start and end", len(partial.CentralBandHorizonClosures)) } - expectedRoots := [2][2][]float64{ - {{109.6259942, 19.9359024}, {107.7443439, 22.3910851}}, - {{-100.0855931, 34.1224725}, {-102.2045906, 31.7284072}}, - } + expectedRoots := solarEclipse20120521ExpectedRoots(t, partial) for closureIndex, closure := range partial.CentralBandHorizonClosures { if len(closure) < 2 { t.Fatalf("horizon closure %d has %d points", closureIndex, len(closure)) @@ -152,10 +152,13 @@ func assertSolarPathMaximumEdgeKM( func TestSolarEclipse20120521HorizonClosuresAreStableAcrossSampling(t *testing.T) { date := time.Date(2012, time.May, 21, 0, 0, 0, 0, time.UTC) - expectedRoots := [2][2][]float64{ - {{109.6259942, 19.9359024}, {107.7443439, 22.3910851}}, - {{-100.0855931, 34.1224725}, {-102.2045906, 31.7284072}}, + reference, ok := eclipse.SolarEclipsePartialFootprints(date, eclipse.SolarEclipsePartialFootprintOptions{ + Step: 2 * time.Minute, BoundaryPoints: 96, CentralShadowStep: 2 * time.Minute, + }) + if !ok || len(reference.CentralBandHorizonClosures) != 2 { + t.Fatalf("reference closures=%d ok=%v, want two", len(reference.CentralBandHorizonClosures), ok) } + expectedRoots := solarEclipse20120521ExpectedRoots(t, reference) for _, step := range []time.Duration{time.Minute, 5 * time.Minute, 10 * time.Minute} { partial, ok := eclipse.SolarEclipsePartialFootprints(date, eclipse.SolarEclipsePartialFootprintOptions{ Step: step, BoundaryPoints: 96, CentralShadowStep: step, @@ -198,3 +201,24 @@ func solarGreatestCurveContainsPoint( } return false } + +// solarEclipse20120521ExpectedRoots 两条地平闭合线的起止根;新模型的值取自 2 分钟步长的参考结果。 +func solarEclipse20120521ExpectedRoots(t *testing.T, reference eclipse.SolarEclipsePartialFootprintsInfo) [2][2][]float64 { + t.Helper() + roots := [2][2][]float64{ + {{109.6259942, 19.9359024}, {107.7443439, 22.3910851}}, + {{-100.0855931, 34.1224725}, {-102.2045906, 31.7284072}}, + } + values := ephemtest.NewValues(t, "solar_eclipse_20120521_roots") + for c, closure := range reference.CentralBandHorizonClosures { + ends := [2]eclipse.SolarEclipsePathPoint{closure[0], closure[len(closure)-1]} + for r, end := range ends { + key := fmt.Sprintf("closure%d/root%d", c, r) + roots[c][r] = []float64{ + values.Want(key+"/lon", roots[c][r][0], end.Longitude), + values.Want(key+"/lat", roots[c][r][1], end.Latitude), + } + } + } + return roots +} diff --git a/geojson/solar_eclipse_32881115_regression_test.go b/geojson/solar_eclipse_32881115_regression_test.go index 7910999..b3fd7cc 100644 --- a/geojson/solar_eclipse_32881115_regression_test.go +++ b/geojson/solar_eclipse_32881115_regression_test.go @@ -7,12 +7,13 @@ import ( "b612.me/astro/eclipse" "b612.me/astro/geojson" "b612.me/astro/internal/geodata" + + "b612.me/astro/internal/ephemtest" ) -// 3288-11-16 in the local display falls on 3288-11-15 UTC. It is an -// extremely shallow non-central eclipse: the sampled footprints are open, -// while the rise/set phase tracks still form part of the visible envelope. +// 极浅非中心食:采样足迹不闭合,日出日没相位线仍须并入可见包络。 func TestMarshalSolarEclipse32881115FallbackCoversPhaseTracks(t *testing.T) { + ephemtest.PinLegacy(t) path, ok := eclipse.SolarEclipsePartialFootprints( time.Date(3288, time.November, 15, 0, 0, 0, 0, time.UTC), eclipse.SolarEclipsePartialFootprintOptions{ diff --git a/geojson/solar_eclipse_grazing_band_regression_test.go b/geojson/solar_eclipse_grazing_band_regression_test.go index 7732f5b..2f2e9d7 100644 --- a/geojson/solar_eclipse_grazing_band_regression_test.go +++ b/geojson/solar_eclipse_grazing_band_regression_test.go @@ -51,6 +51,9 @@ var grazingBandCases = []grazingBandCase{ } func TestSolarEclipseGrazingCentralBandCoversUmbralSweep(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } for _, testCase := range grazingBandCases { t.Run(testCase.date+"-"+testCase.options, func(t *testing.T) { date := grazingBandDate(t, testCase.date) diff --git a/geojson/review15_limits_test.go b/geojson/solar_eclipse_limit_line_test.go similarity index 70% rename from geojson/review15_limits_test.go rename to geojson/solar_eclipse_limit_line_test.go index 27b07c6..b89196c 100644 --- a/geojson/review15_limits_test.go +++ b/geojson/solar_eclipse_limit_line_test.go @@ -10,16 +10,16 @@ import ( "b612.me/astro/geojson" ) -// 本文件钉住 §1.5 的限线口径:north-limit / south-limit 的标签由每个顶点自身的投影侧决定, +// 本文件钉住限线口径:north-limit / south-limit 的标签由每个顶点自身的投影侧决定, // times 是该顶点在中心线上的投影时刻,不按顶点顺序均匀铺开。 -const review15LimitSideSlackKM = 25.0 +const limitSideSlackKM = 25.0 -type review15LimitLine struct { +type derivedLimitLine struct { coordinates [][2]float64 times []time.Time } -func review15SolarEclipseFixture( +func limitSolarEclipseFixture( t *testing.T, text string, ) ([]byte, eclipse.SolarEclipsePath) { @@ -45,7 +45,7 @@ func review15SolarEclipseFixture( return raw, central } -func review15LimitLineFrom(t *testing.T, raw []byte, role string) review15LimitLine { +func derivedLimitLineFrom(t *testing.T, raw []byte, role string) derivedLimitLine { t.Helper() feature := featureWithRole(t, decodeCollection(t, raw), role) var line [][2]float64 @@ -54,18 +54,26 @@ func review15LimitLineFrom(t *testing.T, raw []byte, role string) review15LimitL if multiErr := json.Unmarshal(feature.Geometry.Coordinates, &lines); multiErr != nil { t.Fatalf("%s coordinates: %v", role, err) } - if len(lines) != 1 { - t.Fatalf("%s is split into %d segments", role, len(lines)) + line = nil + for index, segment := range lines { + if index > 0 && !segmentsMeetAtSeam(lines[index-1], segment) { + t.Fatalf("%s is split into %d segments", role, len(lines)) + } + line = append(line, segment...) } - line = lines[0] } encoded, ok := feature.Properties["times"].([]interface{}) if !ok { t.Fatalf("%s has no times array", role) } - if len(encoded) == 1 { - if nested, nestedOK := encoded[0].([]interface{}); nestedOK { - encoded = nested + if len(encoded) > 0 { + if _, nestedOK := encoded[0].([]interface{}); nestedOK { + var flat []interface{} + for _, segment := range encoded { + nested, _ := segment.([]interface{}) + flat = append(flat, nested...) + } + encoded = flat } } if len(encoded) != len(line) { @@ -80,11 +88,20 @@ func review15LimitLineFrom(t *testing.T, raw []byte, role string) review15LimitL } times[index] = stamp } - return review15LimitLine{coordinates: line, times: times} + return derivedLimitLine{coordinates: line, times: times} } -// review15ProjectOnCenterLine 独立复算顶点在中心线上的投影纬度与插值时刻。 -func review15ProjectOnCenterLine( +// segmentsMeetAtSeam 报告前段终点与后段起点是否为 ±180° 上的同一点。 +func segmentsMeetAtSeam(first, second [][2]float64) bool { + if len(first) == 0 || len(second) == 0 { + return false + } + end, start := first[len(first)-1], second[0] + return math.Abs(end[0]) == 180 && end[0] == -start[0] && math.Abs(end[1]-start[1]) < 1e-9 +} + +// projectOnCenterLine 独立复算顶点在中心线上的投影纬度与插值时刻。 +func projectOnCenterLine( point [2]float64, centerLine []eclipse.SolarEclipsePathPoint, ) (float64, time.Time) { @@ -118,19 +135,19 @@ func review15ProjectOnCenterLine( func TestSolarEclipseLimitSidesCarryTheirOwnProjectionSide(t *testing.T) { for _, text := range []string{"2003-05-31", "1874-10-10", "2185-07-26", "4862-09-28"} { t.Run(text, func(t *testing.T) { - raw, central := review15SolarEclipseFixture(t, text) + raw, central := limitSolarEclipseFixture(t, text) for _, side := range []struct { role string north bool }{{"north-limit", true}, {"south-limit", false}} { - line := review15LimitLineFrom(t, raw, side.role) + line := derivedLimitLineFrom(t, raw, side.role) for index, point := range line.coordinates { - latitude, _ := review15ProjectOnCenterLine(point, central.CenterLine) + latitude, _ := projectOnCenterLine(point, central.CenterLine) offset := (point[1] - latitude) * 111.3 - if side.north && offset < -review15LimitSideSlackKM { + if side.north && offset < -limitSideSlackKM { t.Fatalf("%s vertex %d sits %.1f km south of the center line", side.role, index, -offset) } - if !side.north && offset > review15LimitSideSlackKM { + if !side.north && offset > limitSideSlackKM { t.Fatalf("%s vertex %d sits %.1f km north of the center line", side.role, index, offset) } } @@ -139,8 +156,8 @@ func TestSolarEclipseLimitSidesCarryTheirOwnProjectionSide(t *testing.T) { } } -// review15BandRingVertices 收集 central-band 环上的顶点,用于判定限线是否由带边界派生。 -func review15BandRingVertices(t *testing.T, raw []byte) map[[2]float64]bool { +// bandRingVertices 收集 central-band 环上的顶点,用于判定限线是否由带边界派生。 +func bandRingVertices(t *testing.T, raw []byte) map[[2]float64]bool { t.Helper() vertices := map[[2]float64]bool{} for _, ring := range grazingBandRings(t, raw) { @@ -151,7 +168,7 @@ func review15BandRingVertices(t *testing.T, raw []byte) map[[2]float64]bool { return vertices } -func review15LimitFollowsBandRing(line review15LimitLine, vertices map[[2]float64]bool) bool { +func limitFollowsBandRing(line derivedLimitLine, vertices map[[2]float64]bool) bool { if len(line.coordinates) < 3 || len(vertices) == 0 { return false } @@ -167,17 +184,17 @@ func review15LimitFollowsBandRing(line review15LimitLine, vertices map[[2]float6 func TestSolarEclipseDerivedLimitTimesAreProjectionTimes(t *testing.T) { for _, text := range []string{"2003-05-31"} { t.Run(text, func(t *testing.T) { - raw, central := review15SolarEclipseFixture(t, text) - ringVertices := review15BandRingVertices(t, raw) + raw, central := limitSolarEclipseFixture(t, text) + ringVertices := bandRingVertices(t, raw) derived := 0 for _, role := range []string{"north-limit", "south-limit"} { - line := review15LimitLineFrom(t, raw, role) - if !review15LimitFollowsBandRing(line, ringVertices) { + line := derivedLimitLineFrom(t, raw, role) + if !limitFollowsBandRing(line, ringVertices) { continue } derived++ for index, point := range line.coordinates { - _, projected := review15ProjectOnCenterLine(point, central.CenterLine) + _, projected := projectOnCenterLine(point, central.CenterLine) if delta := line.times[index].Sub(projected); delta > 2*time.Second || delta < -2*time.Second { t.Fatalf("%s vertex %d time=%v, projected=%v", role, index, line.times[index].UTC(), projected.UTC()) diff --git a/geojson/review15_benchmark_test.go b/geojson/solar_eclipse_marshal_benchmark_test.go similarity index 76% rename from geojson/review15_benchmark_test.go rename to geojson/solar_eclipse_marshal_benchmark_test.go index a5db37b..b68642d 100644 --- a/geojson/review15_benchmark_test.go +++ b/geojson/solar_eclipse_marshal_benchmark_test.go @@ -10,8 +10,8 @@ import ( "b612.me/astro/geojson" ) -// 本文件钉住 §1.5 涉及的导出路径成本:掠食带的限线派生与打包足迹都在这条路径上。 -func review15BenchmarkDate(b *testing.B, text string) time.Time { +// 本文件钉住导出路径成本:掠食带的限线派生与打包足迹都在这条路径上。 +func marshalBenchmarkDate(b *testing.B, text string) time.Time { b.Helper() if strings.HasPrefix(text, "-") { parts := strings.Split(strings.TrimPrefix(text, "-"), "-") @@ -39,8 +39,8 @@ func review15BenchmarkDate(b *testing.B, text string) time.Time { return date } -func benchmarkReview15SolarEclipseMarshal(b *testing.B, text string) { - date := review15BenchmarkDate(b, text) +func benchmarkSolarEclipseMarshal(b *testing.B, text string) { + date := marshalBenchmarkDate(b, text) info, ok := eclipse.SolarEclipseOnDate(date) if !ok || !info.HasCentral { b.Fatalf("expected a central solar eclipse on %s", text) @@ -69,14 +69,14 @@ func benchmarkReview15SolarEclipseMarshal(b *testing.B, text string) { } } -func BenchmarkReview15SolarEclipseMarshalGrazing(b *testing.B) { +func BenchmarkSolarEclipseMarshalGrazing(b *testing.B) { for _, text := range []string{"2003-05-31", "1874-10-10", "2061-10-13"} { b.Run(text, func(b *testing.B) { - benchmarkReview15SolarEclipseMarshal(b, text) + benchmarkSolarEclipseMarshal(b, text) }) } } -func BenchmarkReview15SolarEclipseMarshalOrdinary(b *testing.B) { - benchmarkReview15SolarEclipseMarshal(b, "2024-04-08") +func BenchmarkSolarEclipseMarshalOrdinary(b *testing.B) { + benchmarkSolarEclipseMarshal(b, "2024-04-08") } diff --git a/geojson/solar_eclipse_polar_two_limit_regression_test.go b/geojson/solar_eclipse_polar_two_limit_regression_test.go index ce76131..3905e48 100644 --- a/geojson/solar_eclipse_polar_two_limit_regression_test.go +++ b/geojson/solar_eclipse_polar_two_limit_regression_test.go @@ -12,6 +12,9 @@ import ( ) func TestMarshalSolarEclipsePolarTwoLimitBandUsesSimpleFaces(t *testing.T) { + if testing.Short() { + t.Skip("slow regression") + } for _, sample := range []struct { date time.Time centralStep time.Duration diff --git a/geojson/solar_shadow_instant.go b/geojson/solar_shadow_instant.go index 79f5fd8..bab3ca5 100644 --- a/geojson/solar_shadow_instant.go +++ b/geojson/solar_shadow_instant.go @@ -8,7 +8,7 @@ import ( ) // MarshalSolarEclipseShadowInstant 单时刻阴影足迹的最小 GeoJSON:只含该时刻的区域与物理边界 / one instant as the smallest useful GeoJSON. -func MarshalSolarEclipseShadowInstant( +func (module *Generator) MarshalSolarEclipseShadowInstant( instant eclipsecore.SolarEclipseShadowInstant, ) ([]byte, error) { if instant.Empty() { @@ -52,10 +52,10 @@ func MarshalSolarEclipseShadowInstant( regionProperties["source_boundary_closed"] = false regionProperties["geometry_role"] = "horizon-closed-region" regionProperties["closure"] = solarHorizonClosureProperties( - instant.Time, instant.Time, solarHorizonClosureExact(instant.Boundaries, instant.HorizonEnds), + instant.Time, instant.Time, solarHorizonClosureExact(instant.Boundaries, instant.HorizonEnds), module, ) ring, boundary := solarShadowFootprintHorizonRing( - instant.Time, instant.Boundaries, instant.HorizonEnds, curve, + instant.Time, instant.Boundaries, instant.HorizonEnds, curve, module, ) if solarShadowRegionDegenerate(curve, ring) { return marshalEmptyFeatureCollection() diff --git a/geojson/solar_shadow_region.go b/geojson/solar_shadow_region.go index efd497e..1ae38e2 100644 --- a/geojson/solar_shadow_region.go +++ b/geojson/solar_shadow_region.go @@ -6,7 +6,6 @@ import ( "time" "b612.me/astro/basic" - eclipsecore "b612.me/astro/eclipse" "b612.me/astro/internal/geodata" "b612.me/astro/internal/solarclosure" @@ -30,8 +29,12 @@ const ( func appendSolarHorizonClosedShadowFootprint( features []feature, footprint eclipsecore.SolarEclipsePartialFootprint, - properties map[string]interface{}, -) ([]feature, error) { + properties map[string]interface{}, modules ...*Generator) ([]feature, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + curve, err := solarShadowFootprintCurve(footprint) if err != nil { return nil, fmt.Errorf("geojson: solar %s at %s: %w", @@ -41,7 +44,7 @@ func appendSolarHorizonClosedShadowFootprint( return features, nil } ring, boundary := solarShadowFootprintHorizonRing( - footprint.Time, footprint.Boundaries, footprint.HorizonEnds, curve, + footprint.Time, footprint.Boundaries, footprint.HorizonEnds, curve, module, ) if solarShadowRegionDegenerate(curve, ring) { return features, nil @@ -56,7 +59,7 @@ func appendSolarHorizonClosedShadowFootprint( regionProperties["source_boundary_closed"] = false regionProperties["geometry_role"] = "horizon-closed-region" regionProperties["closure"] = solarHorizonClosureProperties( - footprint.Time, footprint.Time, solarHorizonClosureExact(footprint.Boundaries, footprint.HorizonEnds), + footprint.Time, footprint.Time, solarHorizonClosureExact(footprint.Boundaries, footprint.HorizonEnds), module, ) regionProperties["interp_signature"] = solarShadowFootprintSignature( footprint.Boundaries, false, eclipsecore.SolarEclipseShadowUmbra, @@ -145,7 +148,7 @@ func solarShadowRegionAreaDegrees2(ring []geodata.GeoPoint) float64 { func solarShadowFootprintCurveLengthKM(curve []geodata.GeoPoint) float64 { length := 0.0 for index := 1; index < len(curve); index++ { - length += solarCentralBandGeoPointDistanceKM(curve[index-1], curve[index]) + length += geodata.MeanDistanceKM(curve[index-1], curve[index]) } return length } @@ -154,12 +157,16 @@ func solarShadowFootprintHorizonRing( value time.Time, boundaries [][]eclipsecore.SolarEclipsePathPoint, horizonEnds []eclipsecore.SolarEclipsePathPoint, - curve []geodata.GeoPoint, -) ([]geodata.GeoPoint, []geodata.GeoPoint) { + curve []geodata.GeoPoint, modules ...*Generator) ([]geodata.GeoPoint, []geodata.GeoPoint) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + return solarclosure.HorizonRing(solarclosure.Footprint{ Boundaries: solarClosureSegments(boundaries), HorizonEnds: solarClosureEnds(horizonEnds), - Subsolar: solarSubsolarPoint(value), + Subsolar: solarSubsolarPoint(value, module), }, curve) } @@ -199,8 +206,12 @@ func solarClosureEnds(horizonEnds []eclipsecore.SolarEclipsePathPoint) []geodata // solarClosureFootprint 转换瞬时足迹;边界坐标非法时报错。 func solarClosureFootprint( - footprint eclipsecore.SolarEclipsePartialFootprint, -) (solarclosure.Footprint, error) { + footprint eclipsecore.SolarEclipsePartialFootprint, modules ...*Generator) (solarclosure.Footprint, error) { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + segments := make([][]geodata.GeoPoint, len(footprint.Boundaries)) for index, source := range footprint.Boundaries { segment := make([]geodata.GeoPoint, len(source)) @@ -215,7 +226,7 @@ func solarClosureFootprint( return solarclosure.Footprint{ Boundaries: segments, HorizonEnds: solarClosureEnds(footprint.HorizonEnds), - Subsolar: solarSubsolarPoint(footprint.Time), + Subsolar: solarSubsolarPoint(footprint.Time, module), Closed: footprint.Closed, }, nil } @@ -230,8 +241,13 @@ func solarHorizonClosureExact( // solarHorizonClosureProperties 声明式闭合弧;exact 为假表示缺精确擦地点、按采样端点近似闭合。 // 月下点按几何时刻算,time 属性写输出时标的时刻。 -func solarHorizonClosureProperties(geometryTime, labelTime time.Time, exact bool) map[string]interface{} { - subsolar := solarSubsolarPoint(geometryTime) +func solarHorizonClosureProperties(geometryTime, labelTime time.Time, exact bool, modules ...*Generator) map[string]interface{} { + module := &defaultModule + if len(modules) > 0 { + module = modules[0] + } + + subsolar := solarSubsolarPoint(geometryTime, module) return map[string]interface{}{ "kind": "horizon", "exact": exact, diff --git a/geojson/spherical_sampling.go b/geojson/spherical_sampling.go index 0d9402a..5cebe14 100644 --- a/geojson/spherical_sampling.go +++ b/geojson/spherical_sampling.go @@ -69,7 +69,7 @@ func appendSphericalMapArcWithin( // linear in lon/lat (notably the long horizon closure edges of shallow // polar eclipses). The adaptive angular-error test alone cannot see that // case and leaves visually abrupt 250+ km segments. - arcDistanceKM := solarCentralBandGeoPointDistanceKM(first, last) + arcDistanceKM := geodata.MeanDistanceKM(first, last) if (sphericalMapChordError(first, middle, last) <= chordErrorDegrees && arcDistanceKM <= chordLimitKM) || depth >= 20 { return append(points, first) } @@ -107,7 +107,7 @@ func sampleSphericalMapPath(source []pathSample) []pathSample { func sampleSphericalMapTriangle(points []geodata.GeoPoint) []geodata.GeoPoint { apex, shortest := 0, math.Inf(1) for i := range points { - if length := solarCentralBandGeoPointDistanceKM(points[(i+1)%3], points[(i+2)%3]); length < shortest { + if length := geodata.MeanDistanceKM(points[(i+1)%3], points[(i+2)%3]); length < shortest { apex, shortest = i, length } } diff --git a/geojson/testdata/semianalytic/solar_eclipse_20120521_roots.json b/geojson/testdata/semianalytic/solar_eclipse_20120521_roots.json new file mode 100644 index 0000000..8e3359b --- /dev/null +++ b/geojson/testdata/semianalytic/solar_eclipse_20120521_roots.json @@ -0,0 +1,10 @@ +{ + "closure0/root0/lat": 19.9313852763035, + "closure0/root0/lon": 109.62972147083349, + "closure0/root1/lat": 22.386460994738165, + "closure0/root1/lon": 107.74831305482383, + "closure1/root0/lat": 34.11797301252737, + "closure1/root0/lon": -100.08865816367663, + "closure1/root1/lat": 31.724001846285333, + "closure1/root1/lon": -102.2073355598305 +} diff --git a/geojson/transit.go b/geojson/transit.go new file mode 100644 index 0000000..b90bff5 --- /dev/null +++ b/geojson/transit.go @@ -0,0 +1,451 @@ +package geojson + +import ( + "fmt" + "math" + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/geodata" + "b612.me/astro/mercury" + "b612.me/astro/venus" +) + +const planetTransitEvent = "planet-transit" + +// 标准大气,与 basic 站心凌日的视地平判定及凌日 SVG 一致。 +const ( + planetTransitRefractionPressureHPa = 1010.0 + planetTransitRefractionTemperatureC = 10.0 + minimumPlanetTransitIsochroneStep = time.Second +) + +// PlanetTransitOptions 是凌日 GeoJSON 导出选项;所有要素都被丢弃时导出失败。 +// PlanetTransitOptions are the planetary-transit GeoJSON export options; the export fails when every +// Feature is dropped. +type PlanetTransitOptions struct { + // BoundaryPoints 是地平线采样点数,同时决定分区的经度列数 max(360, BoundaryPoints)(上限 720); + // 小于等于零时使用 360,其他值限制在 [12, 1440]。 + // BoundaryPoints is the horizon sample count and also sets the region longitude columns to + // max(360, BoundaryPoints), capped at 720. Values <= 0 use 360; others are clamped to [12, 1440]. + BoundaryPoints int + // IsochroneStep 为正时输出一触到四触的站心接触等时线(掠凌没有二触、三触),至少一秒;零值不输出。 + // A positive IsochroneStep adds topocentric C1-C4 isochrones (no C2/C3 for a grazing transit) and must be + // at least one second. The zero value adds none. + IsochroneStep time.Duration + // TimeMarkers 非空时沿一触到四触的太阳直射点轨迹追加时间标记 Point 要素。 + // TimeMarkers adds time-marker Point Features along the subsolar track from C1 through C4 when non-nil. + TimeMarkers *TimeMarkerOptions + // SkipRoles 列出不写进输出的 role;分区三个 role 都跳过时不做分区计算。 + // SkipRoles lists roles to leave out; skipping all three region roles skips the region computation. + SkipRoles []string +} + +// planetTransitInput 是水星、金星两种 TransitInfo 的公共视图。 +type planetTransitInput struct { + planet string + planetIndex int + start time.Time + c2 time.Time + greatest time.Time + c3 time.Time + end time.Time + hasInternal bool + separation float64 + sunRadius float64 + planetRad float64 +} + +// MarshalMercuryTransit 将水星凌日的全球可见分区、一触/四触地平线与可选站心等时线编码为 GeoJSON / encode a Mercury transit as GeoJSON. +func (module *Generator) MarshalMercuryTransit(info mercury.TransitInfo, options PlanetTransitOptions) ([]byte, error) { + if !info.Valid { + return nil, fmt.Errorf("geojson: mercury transit is invalid") + } + return marshalPlanetTransit(planetTransitInput{ + planet: "mercury", planetIndex: 1, + start: info.Start, c2: info.InternalStart, greatest: info.Greatest, c3: info.InternalEnd, end: info.End, + hasInternal: info.HasInternal, separation: info.MinimumSeparationArcsec, + sunRadius: info.SunSemidiameterArcsec, planetRad: info.PlanetSemidiameterArcsec, + }, options, module) +} + +// MarshalVenusTransit 将金星凌日的全球可见分区、一触/四触地平线与可选站心等时线编码为 GeoJSON / encode a Venus transit as GeoJSON. +func (module *Generator) MarshalVenusTransit(info venus.TransitInfo, options PlanetTransitOptions) ([]byte, error) { + if !info.Valid { + return nil, fmt.Errorf("geojson: venus transit is invalid") + } + return marshalPlanetTransit(planetTransitInput{ + planet: "venus", planetIndex: 2, + start: info.Start, c2: info.InternalStart, greatest: info.Greatest, c3: info.InternalEnd, end: info.End, + hasInternal: info.HasInternal, separation: info.MinimumSeparationArcsec, + sunRadius: info.SunSemidiameterArcsec, planetRad: info.PlanetSemidiameterArcsec, + }, options, module) +} + +func validatePlanetTransitInput(input planetTransitInput) error { + if input.start.IsZero() || input.greatest.IsZero() || input.end.IsZero() { + return fmt.Errorf("geojson: %s transit contact times are required", input.planet) + } + if !input.hasInternal && (!input.c2.IsZero() || !input.c3.IsZero()) { + return fmt.Errorf("geojson: %s transit cannot have internal contact times without internal contacts", input.planet) + } + ordered := []time.Time{input.start} + if input.hasInternal { + if input.c2.IsZero() || input.c3.IsZero() { + return fmt.Errorf("geojson: %s transit internal contact times are required", input.planet) + } + ordered = append(ordered, input.c2) + } + ordered = append(ordered, input.greatest) + if input.hasInternal { + ordered = append(ordered, input.c3) + } + ordered = append(ordered, input.end) + for index := 1; index < len(ordered); index++ { + if !ordered[index-1].Before(ordered[index]) { + return fmt.Errorf("geojson: %s transit contact times are not strictly ordered", input.planet) + } + } + return nil +} + +func marshalPlanetTransit(input planetTransitInput, options PlanetTransitOptions, module *Generator) ([]byte, error) { + markerOptions := options.TimeMarkers + if markerOptions != nil { + if err := validateTimeMarkerOptions(*markerOptions); err != nil { + return nil, err + } + } + if options.IsochroneStep < 0 || (options.IsochroneStep > 0 && options.IsochroneStep < minimumPlanetTransitIsochroneStep) { + return nil, fmt.Errorf("geojson: transit isochrone step must be zero or at least %s", minimumPlanetTransitIsochroneStep) + } + if err := validatePlanetTransitInput(input); err != nil { + return nil, err + } + timeScale, scaleErr := timeScaleForMarkers(markerOptions) + if scaleErr != nil { + return nil, scaleErr + } + // 几何一律用民用时刻,只有写进属性的时刻换时标。 + label := func(value time.Time) string { + if value.IsZero() { + return "" + } + return formatTime(basic.LabelIn(timeScale, value.UTC())) + } + boundaryPoints := normalizeLunarBoundaryPoints(options.BoundaryPoints) + longitudePoints := lunarVisibilityLongitudePoints(boundaryPoints, LunarEclipseOptions{}) + longitudes := make([]float64, longitudePoints+1) + for index := range longitudes { + longitudes[index] = -180 + 360*float64(index)/float64(longitudePoints) + } + refraction := basic.RefractionFromApparentAltitude(0, planetTransitRefractionPressureHPa, planetTransitRefractionTemperatureC) + base := map[string]interface{}{"planet": input.planet, "has_internal": input.hasInternal} + withBase := func(extra map[string]interface{}) map[string]interface{} { + properties := cloneProperties(base) + for key, value := range extra { + properties[key] = value + } + return properties + } + features := make([]feature, 0, 12) + + columnsAt := func(at time.Time) [][]lunarLatitudeInterval { + subsolar := solarSubsolarPoint(at, module) + columns := make([][]lunarLatitudeInterval, len(longitudes)) + for index, longitude := range longitudes { + columns[index] = planetTransitSunUpIntervals(subsolar, longitude, refraction) + } + return columns + } + wantEntire := !skippedLunarRole(options.SkipRoles, "transit-entire") + wantSunrise := !skippedLunarRole(options.SkipRoles, "transit-at-sunrise") + wantSunset := !skippedLunarRole(options.SkipRoles, "transit-at-sunset") + wantRegions := wantEntire || wantSunrise || wantSunset + needC1 := wantRegions || !skippedLunarRole(options.SkipRoles, "visible-at-c1") + needGreatest := wantRegions + needC4 := wantRegions || !skippedLunarRole(options.SkipRoles, "visible-at-c4") + var c1Columns, greatestColumns, c4Columns [][]lunarLatitudeInterval + if needC1 { + c1Columns = columnsAt(input.start) + } + if needGreatest { + greatestColumns = columnsAt(input.greatest) + } + if needC4 { + c4Columns = columnsAt(input.end) + } + + for _, contact := range []struct { + role, horizonRole string + at time.Time + columns [][]lunarLatitudeInterval + }{ + {"visible-at-c1", "c1-horizon", input.start, c1Columns}, + {"visible-at-c4", "c4-horizon", input.end, c4Columns}, + } { + if !skippedLunarRole(options.SkipRoles, contact.role) { + value, err := planetTransitRegionGeometry(longitudes, contact.columns) + if err != nil { + return nil, fmt.Errorf("geojson: %s: %w", contact.role, err) + } + features = append(features, newFeature(planetTransitEvent, contact.role, value, + withBase(map[string]interface{}{"time": label(contact.at)}))) + } + if !skippedLunarRole(options.SkipRoles, contact.horizonRole) { + subsolar := solarSubsolarPoint(contact.at, module) + antisolar := geodata.GeoPoint{Longitude: geodata.NormalizeLongitude(subsolar.Longitude + 180), Latitude: -subsolar.Latitude} + value, err := geoMultiLineGeometry(geodata.SphericalCircle(antisolar, 90-refraction, boundaryPoints), true) + if err != nil { + return nil, fmt.Errorf("geojson: %s: %w", contact.horizonRole, err) + } + features = append(features, newFeature(planetTransitEvent, contact.horizonRole, value, + withBase(map[string]interface{}{"time": label(contact.at)}))) + } + } + + regionFeatures, err := planetTransitRegionFeatures(longitudes, c1Columns, greatestColumns, c4Columns, options.SkipRoles, + withBase(map[string]interface{}{ + "time_start": label(input.start), + "time_end": label(input.end), + "longitude_points": longitudePoints, + })) + if err != nil { + return nil, err + } + features = append(features, regionFeatures...) + + if options.IsochroneStep > 0 { + isochrones, isoErr := planetTransitIsochroneFeatures(input, options.IsochroneStep, timeScale, options.SkipRoles, withBase, module) + if isoErr != nil { + return nil, isoErr + } + features = append(features, isochrones...) + } + + greatest := solarSubsolarPoint(input.greatest, module) + metadata := withBase(map[string]interface{}{ + "start": label(input.start), + "greatest": label(input.greatest), + "end": label(input.end), + "minimum_separation_arcsec": input.separation, + "sun_semidiameter_arcsec": input.sunRadius, + "planet_semidiameter_arcsec": input.planetRad, + }) + if input.hasInternal { + metadata["internal_start"], metadata["internal_end"] = label(input.c2), label(input.c3) + } + features, err = appendPointFeature(features, planetTransitEvent, "greatest", + pathSample{Time: basic.LabelIn(timeScale, input.greatest.UTC()), Longitude: greatest.Longitude, Latitude: greatest.Latitude}, + metadata) + if err != nil { + return nil, err + } + if markerOptions != nil { + markers, markerErr := planetTransitTimeMarkerSamples(input.start, input.end, timeScale, *markerOptions, module) + if markerErr != nil { + return nil, markerErr + } + features, err = appendTimeMarkerPointFeatures(features, planetTransitEvent, "subsolar-track", markers, markerOptions.Location) + if err != nil { + return nil, err + } + } + return marshalFeatureCollectionWithTimeScale(dropFeaturesByRole(features, options.SkipRoles), timeScale) +} + +// planetTransitSunUpIntervals 返回 longitude 子午线上太阳中心在视地平上的纬度区间: +// 地点到直射点的角距小于 90°+折射即可见,即 sinδ·sinφ + cosδ·cosΔλ·cosφ > -sin(折射)。 +func planetTransitSunUpIntervals(subsolar geodata.GeoPoint, longitude, refraction float64) []lunarLatitudeInterval { + rad := math.Pi / 180 + a := math.Sin(subsolar.Latitude * rad) + b := math.Cos(subsolar.Latitude*rad) * math.Cos((longitude-subsolar.Longitude)*rad) + threshold := -math.Sin(refraction * rad) + visible := func(latitude float64) bool { + return a*math.Sin(latitude*rad)+b*math.Cos(latitude*rad) > threshold + } + boundaries := []float64{-90} + if amplitude := math.Hypot(a, b); amplitude > 0 && math.Abs(threshold/amplitude) < 1 { + // a·sinφ + b·cosφ = R·sin(φ+α),α = atan2(b, a)。 + alpha := math.Atan2(b, a) / rad + base := math.Asin(threshold/amplitude) / rad + roots := make([]float64, 0, 2) + for _, value := range []float64{base - alpha, 180 - base - alpha} { + for _, shift := range []float64{-360, 0, 360} { + if root := value + shift; root > -90 && root < 90 { + roots = append(roots, root) + } + } + } + if len(roots) == 2 && roots[0] > roots[1] { + roots[0], roots[1] = roots[1], roots[0] + } + boundaries = append(boundaries, roots...) + } + boundaries = append(boundaries, 90) + intervals := make([]lunarLatitudeInterval, 0, 2) + for index := 0; index+1 < len(boundaries); index++ { + low, high := boundaries[index], boundaries[index+1] + if high-low > 1e-9 && visible((low+high)/2) { + intervals = append(intervals, lunarLatitudeInterval{low: low, high: high}) + } + } + return lunarUnionLatitudeIntervals(intervals...) +} + +func planetTransitRegionGeometry(longitudes []float64, columns [][]lunarLatitudeInterval) (geometry, error) { + value, err := lunarVisibilityEnvelopeGeometry(longitudes, columns) + if err != nil { + return geometry{}, err + } + return dropDegenerateMultiPolygonRings(value), nil +} + +// planetTransitRegionFeatures 按与凌日 SVG 相同的口径分区:全程可见是三刻都在地平上,日出带是四触可见而一触不可见, +// 其余凡有一刻可见的都归入日落带(含凌甚前后可见的极区透镜与两端可见、凌甚不可见的高纬带)。 +func planetTransitRegionFeatures( + longitudes []float64, + c1, greatest, c4 [][]lunarLatitudeInterval, + skip []string, + properties map[string]interface{}, +) ([]feature, error) { + wantEntire := !skippedLunarRole(skip, "transit-entire") + wantSunrise := !skippedLunarRole(skip, "transit-at-sunrise") + wantSunset := !skippedLunarRole(skip, "transit-at-sunset") + if !wantEntire && !wantSunrise && !wantSunset { + return nil, nil + } + union := func(groups ...[]lunarLatitudeInterval) []lunarLatitudeInterval { + var merged []lunarLatitudeInterval + for _, group := range groups { + merged = append(merged, group...) + } + return lunarUnionLatitudeIntervals(merged...) + } + entire := make([][]lunarLatitudeInterval, len(longitudes)) + sunrise := make([][]lunarLatitudeInterval, len(longitudes)) + sunset := make([][]lunarLatitudeInterval, len(longitudes)) + for index := range longitudes { + bothEnds := lunarIntersectLatitudeIntervals(c1[index], c4[index]) + entire[index] = lunarIntersectLatitudeIntervals(bothEnds, greatest[index]) + sunrise[index] = lunarSubtractLatitudeIntervals(c4[index], c1[index]) + sunset[index] = lunarSubtractLatitudeIntervals(union(c1[index], c4[index], greatest[index]), + union(entire[index], sunrise[index])) + } + features := make([]feature, 0, 3) + for _, region := range []struct { + role string + want bool + columns [][]lunarLatitudeInterval + }{ + {"transit-entire", wantEntire, entire}, + {"transit-at-sunrise", wantSunrise, sunrise}, + {"transit-at-sunset", wantSunset, sunset}, + } { + if !region.want { + continue + } + value, err := planetTransitRegionGeometry(longitudes, region.columns) + if err != nil { + return nil, fmt.Errorf("geojson: %s: %w", region.role, err) + } + features = append(features, newFeature(planetTransitEvent, region.role, value, cloneProperties(properties))) + } + return features, nil +} + +// planetTransitIsochroneFeatures 把每个接触的站心等时线编码为一条带 times 的 MultiLineString: +// 每段是一条支路,times 与坐标对齐,同一支路上所有点同刻。 +func planetTransitIsochroneFeatures( + input planetTransitInput, + step time.Duration, + timeScale basic.TimeScale, + skip []string, + withBase func(map[string]interface{}) map[string]interface{}, + module *Generator, +) ([]feature, error) { + result := basic.PlanetTransitResultFromTimes(input.planetIndex, input.start, input.c2, input.greatest, input.c3, input.end) + features := make([]feature, 0, 4) + for _, contact := range []struct { + kind basic.PlanetTransitContactKind + role string + }{ + {basic.PlanetTransitC1, "c1-isochrone"}, + {basic.PlanetTransitC2, "c2-isochrone"}, + {basic.PlanetTransitC3, "c3-isochrone"}, + {basic.PlanetTransitC4, "c4-isochrone"}, + } { + if skippedLunarRole(skip, contact.role) { + continue + } + lines := basic.PlanetTransitContactIsochronesWithInstance(result, contact.kind, step, module.inst) + coordinates := make([][][]float64, 0, len(lines)) + times := make([][]string, 0, len(lines)) + for _, line := range lines { + at := basic.LabelIn(timeScale, basic.JD2DateByZone(line.JD, time.UTC, false)) + for _, segment := range line.Segments { + if len(segment) < 2 { + continue + } + points := make([]pathSample, len(segment)) + for index, point := range segment { + points[index] = pathSample{Time: at, Longitude: point.Longitude, Latitude: point.Latitude} + } + pieces, err := splitTimedLine(points, false) + if err != nil { + return nil, fmt.Errorf("geojson: %s: %w", contact.role, err) + } + for _, piece := range pieces { + if len(piece) < 2 { + continue + } + coordinate := make([][]float64, len(piece)) + stamp := make([]string, len(piece)) + for index, point := range piece { + if err := validateCoordinate(point.Longitude, point.Latitude); err != nil { + return nil, fmt.Errorf("geojson: %s: %w", contact.role, err) + } + coordinate[index] = []float64{point.Longitude, point.Latitude} + stamp[index] = formatTime(point.Time) + } + coordinates = append(coordinates, coordinate) + times = append(times, stamp) + } + } + } + if len(coordinates) == 0 { + continue + } + properties := withBase(map[string]interface{}{"step_seconds": step.Seconds(), "times": times}) + features = append(features, newFeature(planetTransitEvent, contact.role, + geometry{Type: "MultiLineString", Coordinates: coordinates}, properties)) + } + return features, nil +} + +func planetTransitTimeMarkerSamples( + start, end time.Time, + scale basic.TimeScale, + options TimeMarkerOptions, + module *Generator, +) ([]pathSample, error) { + step, err := normalizeTimeMarkerStep(options.Step) + if err != nil { + return nil, fmt.Errorf("geojson: transit time markers: %w", err) + } + location := normalizeTimeMarkerLocation(options.Location) + capacity, err := timeMarkerCapacity(start, end, step, location) + if err != nil { + return nil, fmt.Errorf("geojson: transit time markers: %w", err) + } + markers := make([]pathSample, 0, capacity) + for current := firstTimeMarkerAfter(start, step, location); current.Before(end); current = current.Add(step) { + point := solarSubsolarPoint(current, module) + markers = append(markers, pathSample{ + Time: basic.LabelIn(scale, current), + Longitude: point.Longitude, + Latitude: point.Latitude, + }) + } + return markers, nil +} diff --git a/geojson/transit_test.go b/geojson/transit_test.go new file mode 100644 index 0000000..1bd7973 --- /dev/null +++ b/geojson/transit_test.go @@ -0,0 +1,147 @@ +package geojson_test + +import ( + "encoding/json" + "testing" + "time" + + "b612.me/astro" + "b612.me/astro/geojson" + "b612.me/astro/mercury" + "b612.me/astro/venus" +) + +type transitCollection struct { + TimeScale string `json:"time_scale"` + Features []struct { + Properties map[string]interface{} `json:"properties"` + Geometry struct { + Type string `json:"type"` + Coordinates json.RawMessage `json:"coordinates"` + } `json:"geometry"` + } `json:"features"` +} + +func decodeTransit(t *testing.T, data []byte) (transitCollection, map[string]int) { + t.Helper() + var collection transitCollection + if err := json.Unmarshal(data, &collection); err != nil { + t.Fatal(err) + } + roles := map[string]int{} + for _, item := range collection.Features { + if item.Properties["event"] != "planet-transit" { + t.Fatalf("unexpected event %v", item.Properties["event"]) + } + roles[item.Properties["role"].(string)]++ + } + return collection, roles +} + +// 2032-11-13 水星凌日:等时线四个接触俱全,区域与地平线成对输出,凌甚点落在太阳直射点。 +func TestMarshalMercuryTransit(t *testing.T) { + info := mercury.ClosestTransit(time.Date(2032, 11, 13, 0, 0, 0, 0, time.UTC)) + data, err := geojson.MarshalMercuryTransit(info, geojson.PlanetTransitOptions{ + IsochroneStep: 20 * time.Second, + TimeMarkers: &geojson.TimeMarkerOptions{Step: 30 * time.Minute}, + }) + if err != nil { + t.Fatal(err) + } + collection, roles := decodeTransit(t, data) + for _, role := range []string{ + "visible-at-c1", "c1-horizon", "visible-at-c4", "c4-horizon", + "transit-entire", "transit-at-sunrise", "transit-at-sunset", + "c1-isochrone", "c2-isochrone", "c3-isochrone", "c4-isochrone", "greatest", + } { + if roles[role] != 1 { + t.Fatalf("role %s count = %d, want 1", role, roles[role]) + } + } + if roles["time-marker"] < 8 { + t.Fatalf("time markers = %d, want one every 30 min over ~4.4 h", roles["time-marker"]) + } + if collection.TimeScale != "" { + t.Fatal("UTC export must not carry time_scale") + } + for _, item := range collection.Features { + role := item.Properties["role"] + switch role { + case "c1-isochrone", "c2-isochrone", "c3-isochrone", "c4-isochrone": + var lines [][][]float64 + if err := json.Unmarshal(item.Geometry.Coordinates, &lines); err != nil { + t.Fatal(err) + } + times := item.Properties["times"].([]interface{}) + if len(times) != len(lines) || len(lines) == 0 { + t.Fatalf("%s: times %d do not align with %d lines", role, len(times), len(lines)) + } + for index, line := range lines { + if len(times[index].([]interface{})) != len(line) { + t.Fatalf("%s line %d: times length mismatch", role, index) + } + } + case "greatest": + if item.Properties["internal_start"] == nil || item.Properties["minimum_separation_arcsec"] == nil { + t.Fatal("greatest point must carry contact metadata") + } + } + } +} + +// 默认不出等时线;SkipRoles 跳过全部分区后只剩地平线、可见区与凌甚点。 +func TestMarshalVenusTransitDefaultsAndSkip(t *testing.T) { + info := venus.ClosestTransit(time.Date(2012, 6, 6, 0, 0, 0, 0, time.UTC)) + data, err := geojson.MarshalVenusTransit(info, geojson.PlanetTransitOptions{}) + if err != nil { + t.Fatal(err) + } + _, roles := decodeTransit(t, data) + if roles["c1-isochrone"] != 0 || roles["transit-entire"] != 1 { + t.Fatalf("default roles = %v", roles) + } + data, err = geojson.MarshalVenusTransit(info, geojson.PlanetTransitOptions{ + SkipRoles: []string{"transit-entire", "transit-at-sunrise", "transit-at-sunset"}, + }) + if err != nil { + t.Fatal(err) + } + _, roles = decodeTransit(t, data) + if len(roles) != 5 { + t.Fatalf("skipped region roles = %v", roles) + } +} + +func TestMarshalPlanetTransitValidation(t *testing.T) { + info := mercury.ClosestTransit(time.Date(2019, 11, 11, 0, 0, 0, 0, time.UTC)) + if _, err := geojson.MarshalMercuryTransit(mercury.TransitInfo{}, geojson.PlanetTransitOptions{}); err == nil { + t.Fatal("invalid transit must be rejected") + } + if _, err := geojson.MarshalMercuryTransit(info, geojson.PlanetTransitOptions{IsochroneStep: time.Millisecond}); err == nil { + t.Fatal("sub-second isochrone step must be rejected") + } + swapped := info + swapped.Start, swapped.End = info.End, info.Start + if _, err := geojson.MarshalMercuryTransit(swapped, geojson.PlanetTransitOptions{}); err == nil { + t.Fatal("unordered contacts must be rejected") + } + inconsistent := info + inconsistent.HasInternal = false + if _, err := geojson.MarshalMercuryTransit(inconsistent, geojson.PlanetTransitOptions{IsochroneStep: time.Minute}); err == nil { + t.Fatal("internal contact times without HasInternal must be rejected") + } + if _, err := geojson.MarshalMercuryTransit(info, geojson.PlanetTransitOptions{ + TimeMarkers: &geojson.TimeMarkerOptions{TimeScale: astro.TimeScaleUT1, Location: time.FixedZone("CST", 8*3600)}, + }); err == nil { + t.Fatal("UT1 with a non-UTC location must be rejected") + } + data, err := geojson.MarshalMercuryTransit(info, geojson.PlanetTransitOptions{ + TimeMarkers: &geojson.TimeMarkerOptions{TimeScale: astro.TimeScaleUT1}, + }) + if err != nil { + t.Fatal(err) + } + if collection, _ := decodeTransit(t, data); collection.TimeScale != "UT1" { + t.Fatal("UT1 export must carry time_scale") + } +} diff --git a/internal/ephem/apparent.go b/internal/ephem/apparent.go new file mode 100644 index 0000000..d1d9517 --- /dev/null +++ b/internal/ephem/apparent.go @@ -0,0 +1,8 @@ +package ephem + +// ApparentEngine 支持高精度视位置的星历引擎;Apparent 返回 ICRF 视方向单位矢量(含光行时、太阳引力偏折、相对论光行差)、 +// 几何地心距 km 与光行时(日) / ephemeris engine with apparent positions. +type ApparentEngine interface { + Engine + Apparent(target Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) +} diff --git a/internal/ephem/apparent_generic.go b/internal/ephem/apparent_generic.go new file mode 100644 index 0000000..54f113e --- /dev/null +++ b/internal/ephem/apparent_generic.go @@ -0,0 +1,88 @@ +package ephem + +import "math" + +const ( + genericCKMPerDay = 299792.458 * 86400 + genericAUKM = 149597870.7 + // 太阳史瓦西半径 2GM☉/c²,AU。 + genericSunSchwarzschildAU = 1.97412574336e-8 +) + +// genericApparent 用 Engine.State 求地心视方向:迭代光行时、太阳引力偏折、相对论光行差,均在 ICRF 中进行。 +// 引擎不能给出相对太阳系质心的地球、太阳或目标状态时返回 ErrUnsupportedApparent。 +func genericApparent(engine Engine, target Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) { + earth, earthVel, err := engine.State(Earth, SSB, jdTT) + if err != nil { + return dir, 0, 0, ErrUnsupportedApparent + } + now, _, err := engine.State(target, SSB, jdTT) + if err != nil { + return dir, 0, 0, ErrUnsupportedApparent + } + g := genericSub(now, earth) + distKM = genericLen(g) + if !(distKM > 0) || math.IsInf(distKM, 0) { + return dir, 0, 0, ErrUnsupportedApparent + } + tauDays = distKM / genericCKMPerDay + var retarded [3]float64 + for k := 0; k < 3; k++ { + if retarded, _, err = engine.State(target, SSB, jdTT-tauDays); err != nil { + return dir, 0, 0, ErrUnsupportedApparent + } + g = genericSub(retarded, earth) + tauDays = genericLen(g) / genericCKMPerDay + } + q := genericScale(g, 1/genericLen(g)) + if target != Sun && target != Moon && target != Earth { + sunThen, _, sunErr := engine.State(Sun, SSB, jdTT-tauDays) + sunNow, _, sunNowErr := engine.State(Sun, SSB, jdTT) + if sunErr == nil && sunNowErr == nil { + q = genericDeflect(q, genericSub(retarded, sunThen), genericSub(earth, sunNow)) + } + } + return genericAberrate(q, earthVel), distKM, tauDays, nil +} + +// genericDeflect 太阳引力偏折;p 目标方向,t 目标日心位置,e 地球日心位置。 +func genericDeflect(p, t, e [3]float64) [3]float64 { + el, tl := genericLen(e), genericLen(t) + if el <= 0 || tl <= 0 { + return p + } + eu, qu := genericScale(e, 1/el), genericScale(t, 1/tl) + qpe := genericDot(qu, eu) + 1 + if qpe < 1e-9 { + return p + } + w := genericSunSchwarzschildAU / (el / genericAUKM) / qpe + pe, pq := genericDot(p, eu), genericDot(p, qu) + for i := range p { + p[i] += w * (eu[i]*pq - qu[i]*pe) + } + return genericScale(p, 1/genericLen(p)) +} + +// genericAberrate 相对论光行差;v 观测者相对质心速度 km/日。 +func genericAberrate(p, v [3]float64) [3]float64 { + b := genericScale(v, 1/genericCKMPerDay) + bm := 1 / math.Sqrt(1-genericDot(b, b)) + pdv := genericDot(p, b) + w1 := 1 + pdv/(1+1/bm) + w2 := 1 / (1 + pdv) + var r [3]float64 + for i := range p { + r[i] = (p[i]/bm + w1*b[i]) * w2 + } + return genericScale(r, 1/genericLen(r)) +} + +func genericDot(a, b [3]float64) float64 { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] } +func genericLen(a [3]float64) float64 { return math.Sqrt(genericDot(a, a)) } +func genericSub(a, b [3]float64) [3]float64 { + return [3]float64{a[0] - b[0], a[1] - b[1], a[2] - b[2]} +} +func genericScale(a [3]float64, k float64) [3]float64 { + return [3]float64{a[0] * k, a[1] * k, a[2] * k} +} diff --git a/internal/ephem/body.go b/internal/ephem/body.go new file mode 100644 index 0000000..61e012f --- /dev/null +++ b/internal/ephem/body.go @@ -0,0 +1,54 @@ +package ephem + +import ( + "errors" + "reflect" +) + +// Body NAIF 天体编号 / NAIF body code. +type Body int + +const ( + SSB Body = 0 + Mercury Body = 1 + Venus Body = 2 + EMB Body = 3 + Mars Body = 4 + Jupiter Body = 5 + Saturn Body = 6 + Uranus Body = 7 + Neptune Body = 8 + Sun Body = 10 + Moon Body = 301 + Earth Body = 399 +) + +var ( + // ErrOutOfRange 时刻超出星历覆盖 / epoch outside the ephemeris coverage. + ErrOutOfRange = errors.New("ephem: epoch outside coverage") + // ErrUnsupportedBody 星历不含该天体 / body not provided by the ephemeris. + ErrUnsupportedBody = errors.New("ephem: unsupported body") + // ErrNoEngine 实例未配置引擎 / instance has no engine. + ErrNoEngine = errors.New("ephem: instance has no engine") + // ErrUnsupportedApparent 引擎不支持视位置 / engine does not support apparent positions. + ErrUnsupportedApparent = errors.New("ephem: apparent positions are unsupported") +) + +// Engine 几何星历:ICRF 赤道,km 与 km/日,时间 TDB;只读、可并发 / geometric ephemeris in ICRF, km and km/day, TDB. +type Engine interface { + State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) + Coverage(target Body) (start, end float64, ok bool) +} + +// IsNilEngine 引擎为 nil 或持有 nil 指针等空值 / engine is nil or wraps a nil value. +func IsNilEngine(engine Engine) bool { + if engine == nil { + return true + } + value := reflect.ValueOf(engine) + switch value.Kind() { + case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Func, reflect.Interface: + return value.IsNil() + } + return false +} diff --git a/internal/ephem/default_legacy.go b/internal/ephem/default_legacy.go new file mode 100644 index 0000000..aeb2388 --- /dev/null +++ b/internal/ephem/default_legacy.go @@ -0,0 +1,9 @@ +//go:build astro_legacy +// +build astro_legacy + +package ephem + +// BuildDefault 本次构建的初始模型 / the model active at program start. +// +// 构建标签 astro_legacy 让全库以旧级数模型为默认运行。 +const BuildDefault = Legacy diff --git a/internal/ephem/default_semianalytic.go b/internal/ephem/default_semianalytic.go new file mode 100644 index 0000000..ddac0f4 --- /dev/null +++ b/internal/ephem/default_semianalytic.go @@ -0,0 +1,7 @@ +//go:build !astro_legacy +// +build !astro_legacy + +package ephem + +// BuildDefault 本次构建的初始模型 / the model active at program start. +const BuildDefault = Semianalytic diff --git a/internal/ephem/ephem.go b/internal/ephem/ephem.go new file mode 100644 index 0000000..58556c5 --- /dev/null +++ b/internal/ephem/ephem.go @@ -0,0 +1,160 @@ +// Package ephem 全局星历模型状态与实例接口 / global ephemeris model state and instance interfaces. +package ephem + +import ( + "sync" + "sync/atomic" +) + +// Model 星历模型 / ephemeris model. +type Model int32 + +const ( + // Legacy VSOP87 + ELP2000 截断级数 / truncated VSOP87 and ELP2000 series. + Legacy Model = iota + // Semianalytic 按 DE441 重拟的半解析表 / semi-analytic tables refitted to DE441. + Semianalytic + // Custom 外部 Engine 实例,不对应内置模型 / external Engine instance without a built-in model. + Custom Model = -1 +) + +var ( + active = int32(BuildDefault) + generation uint64 = 1 + modelEngines = make(map[Model]func() Engine) + modelEnginesMu sync.RWMutex +) + +// Active 当前模型 / the active model. +func Active() Model { return Model(atomic.LoadInt32(&active)) } + +// Generation 模型切换世代,起始为 1 / model switch generation, starting at 1. +func Generation() uint64 { return atomic.LoadUint64(&generation) } + +// SetActive 切换模型,返回是否发生变化 / switches the model and reports whether it changed. +func SetActive(m Model) bool { + if m != Legacy && m != Semianalytic { + return false + } + globalInstanceMu.Lock() + defer globalInstanceMu.Unlock() + if atomic.SwapInt32(&active, int32(m)) == int32(m) { + return false + } + atomic.AddUint64(&generation, 1) + globalInstance.Store(NewModelInstance(m)) + return true +} + +// RegisterModelEngine 注册内置模型引擎工厂 / register a built-in model engine factory. +func RegisterModelEngine(m Model, factory func() Engine) { + if m != Legacy && m != Semianalytic || factory == nil { + return + } + modelEnginesMu.Lock() + modelEngines[m] = factory + modelEnginesMu.Unlock() +} + +// Instance 星历实例,可包含配置和缓存 / ephemeris instance with configuration and cache. +type Instance struct { + model Model + engine Engine +} + +// NewInstance 创建自定义星历实例 / create an instance from a custom engine. +func NewInstance(engine Engine) *Instance { + if IsNilEngine(engine) { + engine = nil + } + return &Instance{model: Custom, engine: engine} +} + +// NewModelInstance 创建内置模型实例 / create an instance for a built-in model. +func NewModelInstance(m Model) *Instance { + if m != Legacy && m != Semianalytic { + m = BuildDefault + } + modelEnginesMu.RLock() + factory := modelEngines[m] + modelEnginesMu.RUnlock() + var engine Engine + if factory != nil { + engine = factory() + } + return &Instance{model: m, engine: engine} +} + +// Model 获取实例使用的模型 / get model used by this instance. +func (inst *Instance) Model() Model { + if inst == nil { + return Custom + } + return inst.model +} + +// Engine 获取底层星历引擎 / get the underlying ephemeris engine. +func (inst *Instance) Engine() Engine { + if inst == nil { + return nil + } + return inst.engine +} + +// State 计算目标相对中心的状态 / calculate target state relative to center. +func (inst *Instance) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if inst == nil || inst.engine == nil { + return pos, vel, ErrNoEngine + } + return inst.engine.State(target, center, jdTDB) +} + +// Coverage 返回目标覆盖范围 / return target coverage. +func (inst *Instance) Coverage(target Body) (start, end float64, ok bool) { + if inst == nil || inst.engine == nil { + return 0, 0, false + } + return inst.engine.Coverage(target) +} + +// Apparent 计算地心视方向 / calculate apparent geocentric direction. +func (inst *Instance) Apparent(target Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) { + if inst == nil || inst.engine == nil { + return dir, 0, 0, ErrNoEngine + } + if engine, ok := inst.engine.(ApparentEngine); ok { + return engine.Apparent(target, jdTT) + } + // 只给几何状态的引擎(如 SPK)走通用矢量链。 + return genericApparent(inst.engine, target, jdTT) +} + +var ( + globalInstance atomic.Value + globalInstanceMu sync.Mutex +) + +// Global 全局默认星历实例 / global default ephemeris instance. +func Global() *Instance { + if inst, ok := globalInstance.Load().(*Instance); ok && inst != nil { + return inst + } + globalInstanceMu.Lock() + defer globalInstanceMu.Unlock() + if inst, ok := globalInstance.Load().(*Instance); ok && inst != nil { + return inst + } + inst := NewModelInstance(Active()) + globalInstance.Store(inst) + return inst +} + +// SetGlobal 设置全局默认星历实例 / set global default ephemeris instance. +func SetGlobal(inst *Instance) { + if inst == nil { + return + } + globalInstanceMu.Lock() + defer globalInstanceMu.Unlock() + globalInstance.Store(inst) +} diff --git a/internal/ephem/ephem_test.go b/internal/ephem/ephem_test.go new file mode 100644 index 0000000..81fd8d2 --- /dev/null +++ b/internal/ephem/ephem_test.go @@ -0,0 +1,63 @@ +package ephem + +import "testing" + +type testEngine struct { + marker float64 +} + +func (engine testEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + pos[0] = engine.marker + float64(target-center) + vel[0] = jdTDB + return pos, vel, nil +} + +func (engine testEngine) Coverage(target Body) (start, end float64, ok bool) { + return engine.marker, engine.marker + float64(target), true +} + +func (engine testEngine) Apparent(target Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) { + dir[0] = engine.marker + float64(target) + return dir, jdTT, engine.marker, nil +} + +func TestInstanceUsesItsOwnEngine(t *testing.T) { + first := NewInstance(testEngine{marker: 1}) + second := NewInstance(testEngine{marker: 2}) + firstPos, _, err := first.State(Mars, Earth, 2451545) + if err != nil { + t.Fatal(err) + } + secondPos, _, err := second.State(Mars, Earth, 2451545) + if err != nil { + t.Fatal(err) + } + if firstPos[0] == secondPos[0] || first.Engine() == second.Engine() { + t.Fatal("instances are not isolated") + } + if got, want := first.Model(), Custom; got != want { + t.Fatalf("custom model = %v, want %v", got, want) + } +} + +func TestInstanceApparentAndCoverage(t *testing.T) { + instance := NewInstance(testEngine{marker: 3}) + start, end, ok := instance.Coverage(Mars) + if !ok || start != 3 || end != 7 { + t.Fatalf("coverage = %v, %v, %v", start, end, ok) + } + dir, distance, tau, err := instance.Apparent(Mars, 2451545) + if err != nil || dir[0] != 7 || distance != 2451545 || tau != 3 { + t.Fatalf("apparent = %v, %v, %v, %v", dir, distance, tau, err) + } +} + +func TestInstanceWithoutEngineReportsExplicitErrors(t *testing.T) { + instance := &Instance{model: BuildDefault} + if _, _, err := instance.State(Mars, Earth, 2451545); err != ErrNoEngine { + t.Fatalf("State error = %v, want %v", err, ErrNoEngine) + } + if _, _, _, err := instance.Apparent(Mars, 2451545); err != ErrNoEngine { + t.Fatalf("Apparent error = %v, want %v", err, ErrNoEngine) + } +} diff --git a/internal/ephem/global_test.go b/internal/ephem/global_test.go new file mode 100644 index 0000000..a0e83fc --- /dev/null +++ b/internal/ephem/global_test.go @@ -0,0 +1,31 @@ +package ephem + +import "testing" + +func TestGlobalDefaultInstanceIsStable(t *testing.T) { + model, generation := Active(), Generation() + first := Global() + if first == nil || first.Model() != model { + t.Fatalf("default instance = %v, active model = %v", first, model) + } + if second := Global(); second != first { + t.Error("lazy default must retain its instance identity") + } + if Active() != model || Generation() != generation { + t.Error("reading the default changed the active model or generation") + } +} + +func TestGlobalExplicitInstanceIsStable(t *testing.T) { + previous := Global() + t.Cleanup(func() { SetGlobal(previous) }) + custom := NewInstance(testEngine{marker: 7}) + SetGlobal(custom) + if Global() != custom || Global() != custom { + t.Error("explicit default lost its instance identity") + } + SetGlobal(nil) + if Global() != custom { + t.Error("nil must not replace an explicit default") + } +} diff --git a/internal/ephem/nil_engine_test.go b/internal/ephem/nil_engine_test.go new file mode 100644 index 0000000..4e92179 --- /dev/null +++ b/internal/ephem/nil_engine_test.go @@ -0,0 +1,44 @@ +// 被测契约:持有 nil 指针的 Engine 与裸 nil 等价,构造器必须拦下而不是留到调用时 panic。 +package ephem + +import "testing" + +type nilCheckEngine struct{ offset float64 } + +func (engine *nilCheckEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + pos[0] = engine.offset + return pos, vel, nil +} + +func (engine *nilCheckEngine) Coverage(target Body) (start, end float64, ok bool) { + return 0, engine.offset, true +} + +func TestConstructorsRejectTypedNilEngine(t *testing.T) { + var typedNil *nilCheckEngine + cases := map[string]func() error{ + "NewInstance": func() error { + _, _, err := NewInstance(typedNil).State(Mars, Sun, 2451545) + return err + }, + "NewStack": func() error { + _, _, err := NewInstance(NewStack(typedNil)).State(Mars, Sun, 2451545) + return err + }, + } + for name, call := range cases { + if err := call(); err == nil { + t.Errorf("%s: err = nil, want an error", name) + } + } +} + +func TestInstanceKeepsUsableEngine(t *testing.T) { + pos, _, err := NewInstance(&nilCheckEngine{offset: 7}).State(Mars, Sun, 2451545) + if err != nil { + t.Fatalf("State err = %v", err) + } + if pos[0] != 7 { + t.Errorf("pos[0] = %v, want 7", pos[0]) + } +} diff --git a/internal/ephem/stack.go b/internal/ephem/stack.go new file mode 100644 index 0000000..7e0e5ed --- /dev/null +++ b/internal/ephem/stack.go @@ -0,0 +1,243 @@ +package ephem + +import ( + "errors" + "sort" +) + +// BodyProvider 可选地报告引擎能够触及的天体编号 / optionally reports bodies reachable by an engine. +type BodyProvider interface { + Bodies() []Body +} + +// Stack 按顺序叠加多个只读星历引擎 / ordered stack of read-only ephemeris engines. +type Stack struct { + engines []Engine + // extra 为各源声明、且不在 chainBodies 中的天体,创建时算好。 + extra []Body +} + +// stackInlineBodies 中心链搜索在栈上预留的候选数,超出时 append 退回堆分配。 +const stackInlineBodies = 24 + +type stackNode struct { + body Body + pos, vel [3]float64 +} + +// chainBodies 中心链搜索的固定候选,顺序决定同等路径的取舍。 +var chainBodies = [...]Body{SSB, Sun, Mercury, Venus, EMB, Earth, Moon, Mars, Jupiter, Saturn, Uranus, Neptune} + +// NewStack 创建多源星历;先传入的引擎优先 / creates a multi-source ephemeris; earlier engines have priority. +func NewStack(engines ...Engine) *Stack { + usable := make([]Engine, 0, len(engines)) + for _, engine := range engines { + if !IsNilEngine(engine) { + usable = append(usable, engine) + } + } + stack := &Stack{engines: usable} + for _, body := range stack.Bodies() { + if !containsBody(chainBodies[:], body) { + stack.extra = append(stack.extra, body) + } + } + return stack +} + +// Engines 返回底层引擎快照 / returns a snapshot of the underlying engines. +func (stack *Stack) Engines() []Engine { + if stack == nil { + return nil + } + return append([]Engine(nil), stack.engines...) +} + +// Bodies 返回各源声明的天体编号 / returns body identifiers declared by the sources. +func (stack *Stack) Bodies() []Body { + if stack == nil { + return nil + } + seen := make(map[Body]bool) + result := make([]Body, 0) + for _, engine := range stack.engines { + provider, ok := engine.(BodyProvider) + if !ok { + continue + } + for _, body := range provider.Bodies() { + if !seen[body] { + seen[body] = true + result = append(result, body) + } + } + } + sort.Slice(result, func(left, right int) bool { return result[left] < result[right] }) + return result +} + +// Coverage 返回所有源覆盖区间的包络 / returns the envelope of all source coverage intervals. +func (stack *Stack) Coverage(target Body) (start, end float64, ok bool) { + if stack == nil { + return 0, 0, false + } + for _, engine := range stack.engines { + engineStart, engineEnd, engineOK := engine.Coverage(target) + if !engineOK { + continue + } + if !ok || engineStart < start { + start = engineStart + } + if !ok || engineEnd > end { + end = engineEnd + } + ok = true + } + return start, end, ok +} + +// State 返回目标相对中心的状态,并在源之间拼接中心链 / returns target-relative state, composing center chains across sources. +func (stack *Stack) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if target == center { + return pos, vel, nil + } + if stack == nil || len(stack.engines) == 0 { + return pos, vel, ErrNoEngine + } + if !finiteEpoch(jdTDB) { + return pos, vel, ErrOutOfRange + } + failures := &stackFailures{} + if pos, vel, ok := stack.direct(target, center, jdTDB, failures); ok { + return pos, vel, nil + } + + var candidateBuf [stackInlineBodies]Body + candidates := stack.candidates(candidateBuf[:0], target, center) + var queueBuf [stackInlineBodies + 1]stackNode + queue := append(queueBuf[:0], stackNode{body: target}) + var visitedBuf [stackInlineBodies]bool + visited := visitedBuf[:0] + for _, candidate := range candidates { + visited = append(visited, candidate == target) + } + for head := 0; head < len(queue); head++ { + current := queue[head] + if current.body == center { + return current.pos, current.vel, nil + } + for index, candidate := range candidates { + if visited[index] || candidate == current.body { + continue + } + edgePos, edgeVel, ok := stack.direct(current.body, candidate, jdTDB, failures) + if !ok { + continue + } + addVector(&edgePos, current.pos) + addVector(&edgeVel, current.vel) + visited[index] = true + queue = append(queue, stackNode{body: candidate, pos: edgePos, vel: edgeVel}) + } + } + return pos, vel, failures.err() +} + +func (stack *Stack) direct(target, center Body, jdTDB float64, failures *stackFailures) (pos, vel [3]float64, ok bool) { + for _, engine := range stack.engines { + start, end, covered := engine.Coverage(target) + if !covered || jdTDB < start || jdTDB > end { + if covered && (jdTDB < start || jdTDB > end) { + failures.add(ErrOutOfRange) + } + start, end, covered = engine.Coverage(center) + if !covered || jdTDB < start || jdTDB > end { + if covered && (jdTDB < start || jdTDB > end) { + failures.add(ErrOutOfRange) + } + continue + } + } + pos, vel, err := engine.State(target, center, jdTDB) + if err == nil { + return pos, vel, true + } + failures.add(err) + } + return pos, vel, false +} + +// candidates 按 center、固定候选、target、各源额外天体的顺序去重。 +func (stack *Stack) candidates(result []Body, target, center Body) []Body { + result = appendUniqueBody(result, center) + for _, body := range chainBodies { + result = appendUniqueBody(result, body) + } + result = appendUniqueBody(result, target) + for _, body := range stack.extra { + result = appendUniqueBody(result, body) + } + return result +} + +func appendUniqueBody(bodies []Body, body Body) []Body { + if containsBody(bodies, body) { + return bodies + } + return append(bodies, body) +} + +func containsBody(bodies []Body, body Body) bool { + for _, existing := range bodies { + if existing == body { + return true + } + } + return false +} + +type stackFailures struct { + last error + outOfRange bool + unsupported bool +} + +func (failures *stackFailures) add(err error) { + if err == nil { + return + } + failures.last = err + if errors.Is(err, ErrOutOfRange) { + failures.outOfRange = true + } + if errors.Is(err, ErrUnsupportedBody) { + failures.unsupported = true + } +} + +func (failures *stackFailures) err() error { + if failures.outOfRange { + return ErrOutOfRange + } + if failures.unsupported { + return ErrUnsupportedBody + } + if failures.last != nil { + return failures.last + } + return ErrUnsupportedBody +} + +func finiteEpoch(value float64) bool { + return value == value && value < 1.7976931348623157e308 && value > -1.7976931348623157e308 +} + +func addVector(dst *[3]float64, src [3]float64) { + for index := range dst { + dst[index] += src[index] + } +} + +var _ Engine = (*Stack)(nil) +var _ BodyProvider = (*Stack)(nil) diff --git a/internal/ephem/stack_test.go b/internal/ephem/stack_test.go new file mode 100644 index 0000000..62618e0 --- /dev/null +++ b/internal/ephem/stack_test.go @@ -0,0 +1,127 @@ +package ephem + +import ( + "errors" + "testing" +) + +type stackTestEngine struct { + bodies []Body + pairs map[[2]Body][3]float64 + start float64 + end float64 +} + +func (engine stackTestEngine) State(target, center Body, jdTDB float64) (pos, vel [3]float64, err error) { + if value, ok := engine.pairs[[2]Body{target, center}]; ok && jdTDB >= engine.start && jdTDB <= engine.end { + pos = value + return pos, vel, nil + } + for _, body := range engine.bodies { + if body == target || body == center { + return pos, vel, ErrOutOfRange + } + } + return pos, vel, ErrUnsupportedBody +} + +func (engine stackTestEngine) Coverage(target Body) (start, end float64, ok bool) { + for _, body := range engine.bodies { + if body == target { + return engine.start, engine.end, true + } + } + return 0, 0, false +} + +func (engine stackTestEngine) Bodies() []Body { return append([]Body(nil), engine.bodies...) } + +func TestStackComposesAcrossSources(t *testing.T) { + first := stackTestEngine{ + bodies: []Body{Sun, SSB}, + pairs: map[[2]Body][3]float64{ + {Sun, SSB}: {10, 20, 30}, + }, + start: 0, + end: 3000000, + } + second := stackTestEngine{ + bodies: []Body{Body(2000001), Sun}, + pairs: map[[2]Body][3]float64{ + {Body(2000001), Sun}: {1, 2, 3}, + }, + start: 0, + end: 3000000, + } + stack := NewStack(first, second) + pos, _, err := stack.State(Body(2000001), SSB, 2451545) + if err != nil { + t.Fatal(err) + } + if pos != [3]float64{11, 22, 33} { + t.Fatalf("position = %v", pos) + } +} + +func TestStackFirstSourceWinsDirectState(t *testing.T) { + first := stackTestEngine{bodies: []Body{Mars, Earth}, pairs: map[[2]Body][3]float64{{Mars, Earth}: {1, 2, 3}}, start: 0, end: 10} + second := stackTestEngine{bodies: []Body{Mars, Earth}, pairs: map[[2]Body][3]float64{{Mars, Earth}: {4, 5, 6}}, start: 0, end: 10} + pos, _, err := NewStack(first, second).State(Mars, Earth, 5) + if err != nil || pos != [3]float64{1, 2, 3} { + t.Fatalf("state = %v, %v", pos, err) + } +} + +func TestStackErrorPriority(t *testing.T) { + engine := stackTestEngine{bodies: []Body{Mars}, start: 0, end: 1} + _, _, err := NewStack(engine).State(Mars, Earth, 2) + if !errors.Is(err, ErrOutOfRange) { + t.Fatalf("error = %v", err) + } +} + +func TestStackGenericApparentAcceptsArbitraryBody(t *testing.T) { + engine := stackTestEngine{ + bodies: []Body{Earth, Sun, Body(2000001), SSB}, + pairs: map[[2]Body][3]float64{ + {Earth, SSB}: {0, 0, 0}, + {Sun, SSB}: {1000000, 0, 0}, + {Body(2000001), SSB}: {1001000, 0, 0}, + }, + start: 0, + end: 3000000, + } + instance := NewInstance(NewStack(engine)) + if _, distance, _, err := instance.Apparent(Body(2000001), 2451545); err != nil || distance != 1001000 { + t.Fatalf("apparent distance = %.3f, err = %v", distance, err) + } +} + +func BenchmarkStackStateChain(b *testing.B) { + first := stackTestEngine{bodies: []Body{Sun, SSB}, pairs: map[[2]Body][3]float64{{Sun, SSB}: {10, 20, 30}}, start: 0, end: 3000000} + second := stackTestEngine{bodies: []Body{Body(2000001), Sun}, pairs: map[[2]Body][3]float64{{Body(2000001), Sun}: {1, 2, 3}}, start: 0, end: 3000000} + stack := NewStack(first, second) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, _, err := stack.State(Body(2000001), SSB, 2451545); err != nil { + b.Fatal(err) + } + } +} + +func TestStackChainBeyondInlineCapacity(t *testing.T) { + bodies := []Body{SSB} + pairs := map[[2]Body][3]float64{} + previous := SSB + for index := 0; index < 2*stackInlineBodies; index++ { + body := Body(3000000 + index) + bodies = append(bodies, body) + pairs[[2]Body{body, previous}] = [3]float64{1, 0, 0} + previous = body + } + engine := stackTestEngine{bodies: bodies, pairs: pairs, start: 0, end: 10} + pos, _, err := NewStack(engine).State(previous, SSB, 5) + if err != nil || pos != [3]float64{2 * stackInlineBodies, 0, 0} { + t.Fatalf("state = %v, %v", pos, err) + } +} diff --git a/internal/ephemtest/ephemtest.go b/internal/ephemtest/ephemtest.go new file mode 100644 index 0000000..52beb33 --- /dev/null +++ b/internal/ephemtest/ephemtest.go @@ -0,0 +1,31 @@ +// Package ephemtest 测试辅助:固定星历模型、按模型选择基线文件。 +package ephemtest + +import ( + "os" + "path/filepath" + "testing" + + "b612.me/astro/internal/ephem" +) + +// Pin 在本测试内固定模型,结束后恢复 / pins the model for the duration of a test. +func Pin(tb testing.TB, m ephem.Model) { + prev := ephem.Active() + ephem.SetActive(m) + tb.Cleanup(func() { ephem.SetActive(prev) }) +} + +// PinLegacy 只针对旧级数实现本身的测试使用 / for tests of the legacy series implementation itself. +func PinLegacy(tb testing.TB) { Pin(tb, ephem.Legacy) } + +// Golden 当前模型对应的基线文件:旧模型用原路径,新模型用同目录 semianalytic/ 下的同名文件。 +func Golden(path string) string { + if ephem.Active() != ephem.Semianalytic { + return path + } + return filepath.Join(filepath.Dir(path), "semianalytic", filepath.Base(path)) +} + +// Update 环境变量 ASTRO_UPDATE_GOLDEN=1 时重写当前模型的基线 / whether to rewrite the active model's baselines. +func Update() bool { return os.Getenv("ASTRO_UPDATE_GOLDEN") == "1" } diff --git a/internal/ephemtest/values.go b/internal/ephemtest/values.go new file mode 100644 index 0000000..085f142 --- /dev/null +++ b/internal/ephemtest/values.go @@ -0,0 +1,98 @@ +package ephemtest + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "b612.me/astro/internal/ephem" +) + +// Values 按模型取期望值:旧模型用内联常量,新模型用 testdata/semianalytic/.json。 +// 新模型且 Update() 时记录实际值,测试结束写回文件 / per-model expected values for inline-constant tests. +type Values struct { + tb testing.TB + path string + vals map[string]interface{} + semi bool + update bool + dirty bool +} + +// NewValues 打开名为 name 的期望值表 / opens the expected-value table called name. +func NewValues(tb testing.TB, name string) *Values { + tb.Helper() + v := &Values{ + tb: tb, + path: filepath.Join("testdata", "semianalytic", name+".json"), + vals: map[string]interface{}{}, + semi: ephem.Active() == ephem.Semianalytic, + update: Update(), + } + if !v.semi { + return v + } + if data, err := os.ReadFile(v.path); err == nil { + if err := json.Unmarshal(data, &v.vals); err != nil { + tb.Fatal(err) + } + } else if !v.update { + tb.Fatalf("%v (regenerate with ASTRO_UPDATE_GOLDEN=1 go test)", err) + } + if v.update { + tb.Cleanup(v.flush) + } + return v +} + +// Want 返回 key 的期望值;legacy 为 0 表示该项不检查,原样返回 0 / expected value for key. +func (v *Values) Want(key string, legacy, got float64) float64 { + v.tb.Helper() + if !v.semi || legacy == 0 { + return legacy + } + if v.update { + v.vals[key], v.dirty = got, true + return got + } + want, ok := v.vals[key].(float64) + if !ok { + v.tb.Fatalf("%s: no numeric expected value for %q", v.path, key) + } + return want +} + +// WantString 同 Want,用于文本期望值;legacy 为空表示不检查 / string variant of Want. +func (v *Values) WantString(key, legacy, got string) string { + v.tb.Helper() + if !v.semi || legacy == "" { + return legacy + } + if v.update { + v.vals[key], v.dirty = got, true + return got + } + want, ok := v.vals[key].(string) + if !ok { + v.tb.Fatalf("%s: no string expected value for %q", v.path, key) + } + return want +} + +func (v *Values) flush() { + if !v.dirty { + return + } + data, err := json.MarshalIndent(v.vals, "", " ") + if err != nil { + v.tb.Fatal(err) + } + if err := os.MkdirAll(filepath.Dir(v.path), 0o755); err != nil { + v.tb.Fatal(err) + } + if err := os.WriteFile(v.path, append(data, '\n'), 0o644); err != nil { + v.tb.Fatal(err) + } + v.tb.Logf("rewrote %s: %d values", v.path, len(v.vals)) +} diff --git a/internal/geodata/earth.go b/internal/geodata/earth.go new file mode 100644 index 0000000..0738524 --- /dev/null +++ b/internal/geodata/earth.go @@ -0,0 +1,60 @@ +package geodata + +import "math" + +// 地球尺度常量 / Earth size constants. +const ( + // EarthEquatorialRadiusKM 地球赤道半径 / Earth equatorial radius. + EarthEquatorialRadiusKM = 6378.1366 + // EarthMeanRadiusKM 地球平均半径 / Earth mean radius. + EarthMeanRadiusKM = 6371.0088 + // EarthWGS84RadiusKM WGS84 椭球长半轴 / WGS84 semi-major axis. + EarthWGS84RadiusKM = 6378.137 + // EarthKMPerDegree 大圆每度的近似长度 / approximate great-circle length per degree. + EarthKMPerDegree = 111.195 + // WebMercatorMaxLatitude Web Mercator 可投影的纬度上限 / Web Mercator latitude limit. + WebMercatorMaxLatitude = 85.05112878 +) + +// NormalizeLongitude 把经度归一化到 [-180, 180) / wraps a longitude into [-180, 180). +func NormalizeLongitude(value float64) float64 { + value = math.Mod(value+180, 360) + if value < 0 { + value += 360 + } + return value - 180 +} + +// CentralAngle 两点间的球心角(弧度),参数为度 / haversine central angle in radians. +func CentralAngle(longitude1, latitude1, longitude2, latitude2 float64) float64 { + firstLatitude := latitude1 * math.Pi / 180 + secondLatitude := latitude2 * math.Pi / 180 + deltaLatitude := secondLatitude - firstLatitude + deltaLongitude := math.Remainder((longitude2-longitude1)*math.Pi/180, 2*math.Pi) + haversine := math.Sin(deltaLatitude/2)*math.Sin(deltaLatitude/2) + + math.Cos(firstLatitude)*math.Cos(secondLatitude)*math.Sin(deltaLongitude/2)*math.Sin(deltaLongitude/2) + return 2 * math.Asin(math.Sqrt(math.Max(0, math.Min(1, haversine)))) +} + +// EquatorialDistanceKM 以赤道半径计的大圆距离 / great-circle distance on the equatorial-radius sphere. +func EquatorialDistanceKM(first, second GeoPoint) float64 { + return EarthEquatorialRadiusKM * CentralAngle(first.Longitude, first.Latitude, second.Longitude, second.Latitude) +} + +// MeanDistanceKM 以平均半径计的大圆距离 / great-circle distance on the mean-radius sphere. +func MeanDistanceKM(first, second GeoPoint) float64 { + return EarthMeanRadiusKM * CentralAngle(first.Longitude, first.Latitude, second.Longitude, second.Latitude) +} + +// WebMercatorDistanceKM 两点在 Web Mercator 平面上的距离,经差取短弧 / Web Mercator planar distance. +func WebMercatorDistanceKM(first, second GeoPoint) float64 { + clampLatitude := func(value float64) float64 { + return math.Max(-WebMercatorMaxLatitude, math.Min(WebMercatorMaxLatitude, value)) + } + longitude := math.Remainder(second.Longitude-first.Longitude, 360) * math.Pi / 180 + firstLatitude := clampLatitude(first.Latitude) * math.Pi / 180 + secondLatitude := clampLatitude(second.Latitude) * math.Pi / 180 + firstY := math.Log(math.Tan(math.Pi/4 + firstLatitude/2)) + secondY := math.Log(math.Tan(math.Pi/4 + secondLatitude/2)) + return EarthEquatorialRadiusKM * math.Hypot(longitude, secondY-firstY) +} diff --git a/internal/geodata/linework.go b/internal/geodata/linework.go index f02b162..8ea39f1 100644 --- a/internal/geodata/linework.go +++ b/internal/geodata/linework.go @@ -103,7 +103,7 @@ func newVisibleLineworkArcIndex(lines [][]GeoPoint) visibleLineworkArcIndex { func (index visibleLineworkArcIndex) pointDistanceKM(point GeoPoint) float64 { if source, ok := index.vertices[visibleLineworkIndexPointKey(point)]; ok { - return geoPointDistanceKM(point, source) + return EquatorialDistanceKM(point, source) } pointVector := geoPointVector(point) minimum := math.Inf(1) @@ -130,10 +130,10 @@ func (index visibleLineworkArcIndex) edgePointDistanceKM( matched := false for _, arcIndex := range arcIndices { arc := index.arcs[arcIndex] - direct := geoPointDistanceKM(first, arc.start) <= 0.001 && - geoPointDistanceKM(second, arc.end) <= 0.001 - reverse := geoPointDistanceKM(first, arc.end) <= 0.001 && - geoPointDistanceKM(second, arc.start) <= 0.001 + direct := EquatorialDistanceKM(first, arc.start) <= 0.001 && + EquatorialDistanceKM(second, arc.end) <= 0.001 + reverse := EquatorialDistanceKM(first, arc.end) <= 0.001 && + EquatorialDistanceKM(second, arc.start) <= 0.001 if !direct && !reverse { continue } @@ -151,12 +151,12 @@ func (arc visibleLineworkArc) pointDistanceKM(point GeoPoint, pointVector geoVec for _, candidate := range []geoVector3{projected, geoVectorScale(projected, -1)} { if sphericalPointOnArcVectors(candidate, arc.startVector, arc.endVector, arc.arcRadians) { angle := math.Acos(math.Max(-1, math.Min(1, geoVectorDot(pointVector, candidate)))) - return angle * 6378.1366 + return angle * EarthEquatorialRadiusKM } } } } - return math.Min(geoPointDistanceKM(point, arc.start), geoPointDistanceKM(point, arc.end)) + return math.Min(EquatorialDistanceKM(point, arc.start), EquatorialDistanceKM(point, arc.end)) } func visibleLineworkIndexPointKey(point GeoPoint) visibleLineworkPointKey { @@ -278,7 +278,7 @@ func visibleLineworkPolygons( // curves, but using that same radius for point-in-face tests makes adjacent // polar faces overlap and selects the wrong side of a narrow band. faceToleranceKM := math.Min(5, math.Max(0.5, snapDistanceKM/10)) - projectedTolerance := faceToleranceKM / 111.195 + projectedTolerance := faceToleranceKM / EarthKMPerDegree cycleProbes := visibleLineworkFilledCycleProbes(cycles, projectedFill, projectedTolerance) if len(cycleProbes) > 0 { for _, probe := range cycleProbes { @@ -403,7 +403,7 @@ func visibleLineworkProjectedEdgeDistanceKM(first, second GeoPoint) float64 { latitude := (first.Latitude + second.Latitude) * math.Pi / 360 deltaLongitude := math.Remainder(second.Longitude-first.Longitude, 360) * math.Cos(latitude) deltaLatitude := second.Latitude - first.Latitude - return math.Hypot(deltaLongitude, deltaLatitude) * 111.195 + return math.Hypot(deltaLongitude, deltaLatitude) * EarthKMPerDegree } func visibleLineworkBoundarySourceMissDistanceKM( @@ -516,7 +516,7 @@ func visibleLineworkBoundaryBridge( if !visibleLineworkBridgeIsSimple(bridge, toleranceKM) { continue } - if geoPointDistanceKM(originalStart, bridge[0]) > geoPointDistanceKM(originalStart, bridge[len(bridge)-1]) { + if EquatorialDistanceKM(originalStart, bridge[0]) > EquatorialDistanceKM(originalStart, bridge[len(bridge)-1]) { reverseGeoPoints(bridge) } length := visibleLineworkBridgeLength(bridge) @@ -554,10 +554,10 @@ func visibleLineworkCrossLineBoundaryBridge( continue } for pointIndex, point := range line { - if distance := geoPointDistanceKM(start, point); distance <= toleranceKM { + if distance := EquatorialDistanceKM(start, point); distance <= toleranceKM { startLocations = append(startLocations, location{lineIndex, pointIndex, distance}) } - if distance := geoPointDistanceKM(end, point); distance <= toleranceKM { + if distance := EquatorialDistanceKM(end, point); distance <= toleranceKM { endLocations = append(endLocations, location{lineIndex, pointIndex, distance}) } } @@ -574,7 +574,7 @@ func visibleLineworkCrossLineBoundaryBridge( secondIndices := []int{0, len(secondLine) - 1} for _, firstIndex := range firstIndices { for _, secondIndex := range secondIndices { - if geoPointDistanceKM(firstLine[firstIndex], secondLine[secondIndex]) > toleranceKM { + if EquatorialDistanceKM(firstLine[firstIndex], secondLine[secondIndex]) > toleranceKM { continue } firstPath := visibleLineworkLinePath(firstLine, first.pointIndex, firstIndex, start, firstLine[firstIndex]) @@ -609,8 +609,8 @@ func visibleLineworkBridgeIsSimple(points []GeoPoint, toleranceKM float64) bool } endpointRepeatTolerance := math.Min(1, toleranceKM/10) for index := 1; index+1 < len(points); index++ { - if geoPointDistanceKM(points[index], points[0]) <= endpointRepeatTolerance || - geoPointDistanceKM(points[index], points[len(points)-1]) <= endpointRepeatTolerance { + if EquatorialDistanceKM(points[index], points[0]) <= endpointRepeatTolerance || + EquatorialDistanceKM(points[index], points[len(points)-1]) <= endpointRepeatTolerance { return false } } @@ -633,7 +633,7 @@ func visibleLineworkLinePath( path = append(path, line[index]) } } - if len(path) == 0 || geoPointDistanceKM(path[len(path)-1], end) > 1e-9 { + if len(path) == 0 || EquatorialDistanceKM(path[len(path)-1], end) > 1e-9 { path = append(path, end) } return path @@ -642,7 +642,7 @@ func visibleLineworkLinePath( func visibleLineworkBridgeLength(points []GeoPoint) float64 { length := 0.0 for index := 1; index < len(points); index++ { - length += geoPointDistanceKM(points[index-1], points[index]) + length += EquatorialDistanceKM(points[index-1], points[index]) } return length } @@ -654,7 +654,7 @@ func visibleLineworkNearestVertex( bestIndex := -1 bestDistance := math.Inf(1) for index, candidate := range line { - distance := geoPointDistanceKM(point, candidate) + distance := EquatorialDistanceKM(point, candidate) if distance < bestDistance { bestIndex = index bestDistance = distance @@ -1805,7 +1805,7 @@ func buildVisibleLineworkGraph( if index == otherIndex || len(other.outgoing) == 0 { continue } - if distance := geoPointDistanceKM(point, chart.unproject(other.point)); distance < minimum { + if distance := EquatorialDistanceKM(point, chart.unproject(other.point)); distance < minimum { first, second, minimum = index, otherIndex, distance } } @@ -1923,7 +1923,7 @@ func visibleLineworkNodeIndex( bestIndex := -1 bestDistance := math.Inf(1) for index, node := range nodes { - distance := geoPointDistanceKM(chart.unproject(node.point), target) + distance := EquatorialDistanceKM(chart.unproject(node.point), target) if distance <= snapDistanceKM && distance < bestDistance { bestIndex, bestDistance = index, distance } diff --git a/internal/geodata/orthographic_test.go b/internal/geodata/orthographic_test.go index 785f9d7..37fafdf 100644 --- a/internal/geodata/orthographic_test.go +++ b/internal/geodata/orthographic_test.go @@ -15,7 +15,7 @@ func orthographicTestBorderline(ring []GeoPoint, point GeoPoint) bool { base := orthographicTestContainsRing(ring, point) for _, bearing := range []float64{0, 90, 180, 270} { offset := InterpolateGreatCircle(point, GeoPoint{ - Longitude: normalizeLongitude(point.Longitude + 0.05*math.Cos(bearing*rad)), + Longitude: NormalizeLongitude(point.Longitude + 0.05*math.Cos(bearing*rad)), Latitude: point.Latitude + 0.05*math.Sin(bearing*rad), }, 0.001) if orthographicTestContainsRing(ring, offset) != base { diff --git a/internal/geodata/polygon_union.go b/internal/geodata/polygon_union.go index 284d525..10fcce3 100644 --- a/internal/geodata/polygon_union.go +++ b/internal/geodata/polygon_union.go @@ -370,15 +370,10 @@ func polygonUnionRingForPoints(points []polygonUnionPoint) polygonUnionRing { minY, maxY = math.Min(minY, point.y), math.Max(maxY, point.y) } locationEdges := append([]polygonUnionSourceEdge(nil), edges...) - sort.Slice(locationEdges, func(first, second int) bool { - if locationEdges[first].minY != locationEdges[second].minY { - return locationEdges[first].minY < locationEdges[second].minY - } - return locationEdges[first].maxY < locationEdges[second].maxY - }) + sort.Sort(polygonUnionEdgesByY(locationEdges)) return polygonUnionRing{ points: points, edges: edges, locationEdges: locationEdges, - locationBins: polygonUnionBuildLocationBins(edges, minY, maxY), + locationBins: polygonUnionBuildLocationBins(locationEdges, minY, maxY), minX: minX, maxX: maxX, minY: minY, maxY: maxY, } } @@ -394,19 +389,7 @@ func polygonUnionAddIntersections(rings []polygonUnionRing) { }) } } - sort.Slice(edges, func(first, second int) bool { - a, b := edges[first], edges[second] - if a.edge.minX != b.edge.minX { - return a.edge.minX < b.edge.minX - } - if a.edge.maxX != b.edge.maxX { - return a.edge.maxX < b.edge.maxX - } - if a.ringIndex != b.ringIndex { - return a.ringIndex < b.ringIndex - } - return a.edgeIndex < b.edgeIndex - }) + sort.Sort(polygonUnionEdgeRefsByX(edges)) for firstIndex, first := range edges { for secondIndex := firstIndex + 1; secondIndex < len(edges); secondIndex++ { second := edges[secondIndex] @@ -421,13 +404,13 @@ func polygonUnionAddIntersections(rings []polygonUnionRing) { if !polygonUnionEdgeBoundsOverlap(first.edge, second.edge) { continue } - firstParams, secondParams := polygonUnionSegmentIntersections( + firstParams, firstCount, secondParams, secondCount := polygonUnionSegmentIntersections( first.edge.start, first.edge.end, second.edge.start, second.edge.end, ) firstRing := &rings[first.ringIndex] secondRing := &rings[second.ringIndex] - firstRing.params[first.edgeIndex] = append(firstRing.params[first.edgeIndex], firstParams...) - secondRing.params[second.edgeIndex] = append(secondRing.params[second.edgeIndex], secondParams...) + firstRing.params[first.edgeIndex] = append(firstRing.params[first.edgeIndex], firstParams[:firstCount]...) + secondRing.params[second.edgeIndex] = append(secondRing.params[second.edgeIndex], secondParams[:secondCount]...) } } } @@ -443,7 +426,7 @@ func polygonUnionEdgesAdjacent(first, second, count int) bool { func polygonUnionSegmentIntersections( firstStart, firstEnd, secondStart, secondEnd polygonUnionPoint, -) ([]float64, []float64) { +) (firstParams [2]float64, firstCount int, secondParams [2]float64, secondCount int) { firstDelta := polygonUnionSubtract(firstEnd, firstStart) secondDelta := polygonUnionSubtract(secondEnd, secondStart) offset := polygonUnionSubtract(secondStart, firstStart) @@ -453,27 +436,29 @@ func polygonUnionSegmentIntersections( secondParam := polygonUnionCross(offset, firstDelta) / denominator if firstParam < -polygonUnionEpsilon || firstParam > 1+polygonUnionEpsilon || secondParam < -polygonUnionEpsilon || secondParam > 1+polygonUnionEpsilon { - return nil, nil + return firstParams, 0, secondParams, 0 } - return []float64{polygonUnionClampParam(firstParam)}, []float64{polygonUnionClampParam(secondParam)} + firstParams[0] = polygonUnionClampParam(firstParam) + secondParams[0] = polygonUnionClampParam(secondParam) + return firstParams, 1, secondParams, 1 } if math.Abs(polygonUnionCross(offset, firstDelta)) > polygonUnionEpsilon { - return nil, nil + return firstParams, 0, secondParams, 0 } - firstParams := make([]float64, 0, 2) - secondParams := make([]float64, 0, 2) - for _, point := range []polygonUnionPoint{secondStart, secondEnd} { + for _, point := range [2]polygonUnionPoint{secondStart, secondEnd} { if value, ok := polygonUnionPointSegmentParam(point, firstStart, firstEnd); ok { - firstParams = append(firstParams, value) + firstParams[firstCount] = value + firstCount++ } } - for _, point := range []polygonUnionPoint{firstStart, firstEnd} { + for _, point := range [2]polygonUnionPoint{firstStart, firstEnd} { if value, ok := polygonUnionPointSegmentParam(point, secondStart, secondEnd); ok { - secondParams = append(secondParams, value) + secondParams[secondCount] = value + secondCount++ } } - return firstParams, secondParams + return firstParams, firstCount, secondParams, secondCount } func polygonUnionPointSegmentParam(point, start, end polygonUnionPoint) (float64, bool) { @@ -551,11 +536,11 @@ func polygonUnionOuterEdges(rings []polygonUnionRing) []polygonUnionEdge { } func polygonUnionInsideAnyRing(point polygonUnionPoint, rings []polygonUnionRing, preferred int) bool { - if preferred >= 0 && preferred < len(rings) && polygonUnionRingContainsPoint(point, rings[preferred]) { + if preferred >= 0 && preferred < len(rings) && polygonUnionRingRefContainsPoint(point, &rings[preferred]) { return true } - for index, ring := range rings { - if index != preferred && polygonUnionRingContainsPoint(point, ring) { + for index := range rings { + if index != preferred && polygonUnionRingRefContainsPoint(point, &rings[index]) { return true } } @@ -563,12 +548,16 @@ func polygonUnionInsideAnyRing(point polygonUnionPoint, rings []polygonUnionRing } func polygonUnionRingContainsPoint(point polygonUnionPoint, ring polygonUnionRing) bool { + return polygonUnionRingRefContainsPoint(point, &ring) +} + +func polygonUnionRingRefContainsPoint(point polygonUnionPoint, ring *polygonUnionRing) bool { margin := polygonUnionProbeOffset + polygonUnionEpsilon if point.x < ring.minX-margin || point.x > ring.maxX+margin || point.y < ring.minY-margin || point.y > ring.maxY+margin { return false } - return polygonUnionPointLocationInRing(point, ring) >= 0 + return polygonUnionPointLocationInRingRef(point, ring) >= 0 } func polygonUnionJoinEdges(edges []polygonUnionEdge) ([][]GeoPoint, error) { @@ -633,7 +622,7 @@ func polygonUnionJoinEdges(edges []polygonUnionEdge) ([][]GeoPoint, error) { geographic := make([]GeoPoint, len(points)) for index, point := range points { geographic[index] = GeoPoint{ - Longitude: polygonUnionNormalizeLongitude(point.x), + Longitude: NormalizeLongitude(point.x), Latitude: point.y, } } @@ -763,30 +752,43 @@ func polygonUnionPointLocationOnEdges( return -1 } +// polygonUnionBuildLocationBins 要求 sortedEdges 已按 (minY, maxY) 升序,各分箱沿用该顺序。 func polygonUnionBuildLocationBins( - edges []polygonUnionSourceEdge, + sortedEdges []polygonUnionSourceEdge, minimumY, maximumY float64, ) [][]polygonUnionSourceEdge { + var counts [polygonUnionLocationBinCount]int + total := 0 + for _, edge := range sortedEdges { + first := polygonUnionLocationBin(edge.minY-polygonUnionEpsilon, minimumY, maximumY) + last := polygonUnionLocationBin(edge.maxY+polygonUnionEpsilon, minimumY, maximumY) + for index := first; index <= last; index++ { + counts[index]++ + } + total += last - first + 1 + } + backing := make([]polygonUnionSourceEdge, total) bins := make([][]polygonUnionSourceEdge, polygonUnionLocationBinCount) - for _, edge := range edges { + offset := 0 + for index, count := range counts { + bins[index] = backing[offset : offset : offset+count] + offset += count + } + for _, edge := range sortedEdges { first := polygonUnionLocationBin(edge.minY-polygonUnionEpsilon, minimumY, maximumY) last := polygonUnionLocationBin(edge.maxY+polygonUnionEpsilon, minimumY, maximumY) for index := first; index <= last; index++ { bins[index] = append(bins[index], edge) } } - for index := range bins { - sort.Slice(bins[index], func(first, second int) bool { - if bins[index][first].minY != bins[index][second].minY { - return bins[index][first].minY < bins[index][second].minY - } - return bins[index][first].maxY < bins[index][second].maxY - }) - } return bins } func polygonUnionPointLocationInRing(point polygonUnionPoint, ring polygonUnionRing) int { + return polygonUnionPointLocationInRingRef(point, &ring) +} + +func polygonUnionPointLocationInRingRef(point polygonUnionPoint, ring *polygonUnionRing) int { if len(ring.locationBins) != polygonUnionLocationBinCount { return polygonUnionPointLocationOnEdges(point, ring.locationEdges) } @@ -893,10 +895,31 @@ func polygonUnionKey(edge polygonUnionEdge) polygonUnionEdgeKey { } } -func polygonUnionNormalizeLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 +type polygonUnionEdgesByY []polygonUnionSourceEdge + +func (edges polygonUnionEdgesByY) Len() int { return len(edges) } +func (edges polygonUnionEdgesByY) Swap(i, j int) { edges[i], edges[j] = edges[j], edges[i] } +func (edges polygonUnionEdgesByY) Less(i, j int) bool { + if edges[i].minY != edges[j].minY { + return edges[i].minY < edges[j].minY } - return value - 180 + return edges[i].maxY < edges[j].maxY +} + +type polygonUnionEdgeRefsByX []polygonUnionSourceEdgeRef + +func (edges polygonUnionEdgeRefsByX) Len() int { return len(edges) } +func (edges polygonUnionEdgeRefsByX) Swap(i, j int) { edges[i], edges[j] = edges[j], edges[i] } +func (edges polygonUnionEdgeRefsByX) Less(i, j int) bool { + a, b := &edges[i], &edges[j] + if a.edge.minX != b.edge.minX { + return a.edge.minX < b.edge.minX + } + if a.edge.maxX != b.edge.maxX { + return a.edge.maxX < b.edge.maxX + } + if a.ringIndex != b.ringIndex { + return a.ringIndex < b.ringIndex + } + return a.edgeIndex < b.edgeIndex } diff --git a/internal/geodata/seam_test.go b/internal/geodata/seam_test.go index 7153429..2f02772 100644 --- a/internal/geodata/seam_test.go +++ b/internal/geodata/seam_test.go @@ -33,7 +33,7 @@ func TestPolygonFragmentsCentredSeamKeepsGeometry(t *testing.T) { } for _, point := range fragment { // 分段用地图窗口坐标表示(居中经线 ±180),折回 -180…180 后再比对。 - if longitude := normalizeLongitude180(point.Longitude); longitude < -60-1e-6 || longitude > 60+1e-6 { + if longitude := NormalizeLongitude(point.Longitude); longitude < -60-1e-6 || longitude > 60+1e-6 { t.Fatalf("fragment longitude %.3f escaped the ring", longitude) } if math.Abs(point.Latitude) > 30+0.5 { diff --git a/internal/geodata/sphere.go b/internal/geodata/sphere.go index 74062f4..14bd6a5 100644 --- a/internal/geodata/sphere.go +++ b/internal/geodata/sphere.go @@ -22,7 +22,7 @@ func SphericalCircle(center GeoPoint, radiusDegrees float64, points int) []GeoPo math.Cos(radius)-math.Sin(latitude)*math.Sin(lat), ) result[index] = GeoPoint{ - Longitude: normalizeLongitude(lon * 180 / math.Pi), + Longitude: NormalizeLongitude(lon * 180 / math.Pi), Latitude: lat * 180 / math.Pi, } } @@ -53,7 +53,7 @@ func geoVectorPoint(vector geoVector3) GeoPoint { vector.y /= length vector.z /= length return GeoPoint{ - Longitude: normalizeLongitude(math.Atan2(vector.y, vector.x) * 180 / math.Pi), + Longitude: NormalizeLongitude(math.Atan2(vector.y, vector.x) * 180 / math.Pi), Latitude: math.Asin(math.Max(-1, math.Min(1, vector.z))) * 180 / math.Pi, } } @@ -103,7 +103,7 @@ func sphericalInterpolate(first, second GeoPoint, fraction float64) GeoPoint { // never intentionally create one, but retain a finite fallback for // malformed caller input. return GeoPoint{ - Longitude: normalizeLongitude(first.Longitude + fraction*normalizeLongitude(second.Longitude-first.Longitude)), + Longitude: NormalizeLongitude(first.Longitude + fraction*NormalizeLongitude(second.Longitude-first.Longitude)), Latitude: first.Latitude + fraction*(second.Latitude-first.Latitude), } } @@ -376,12 +376,12 @@ func sphericalPointArcDistanceKM(point, start, end GeoPoint) float64 { candidatePoint := geoVectorPoint(candidate) if sphericalPointOnArc(candidatePoint, start, end) { angle := math.Acos(math.Max(-1, math.Min(1, geoVectorDot(pointVector, candidate)))) - return angle * 6378.1366 + return angle * EarthEquatorialRadiusKM } } } } - return math.Min(geoPointDistanceKM(point, start), geoPointDistanceKM(point, end)) + return math.Min(EquatorialDistanceKM(point, start), EquatorialDistanceKM(point, end)) } // JoinPolylineSegments 按最近端点连接无序边界线段 / JoinPolylineSegments joins unordered boundary segments by their nearest @@ -482,7 +482,7 @@ func ShortestCircleArc(circle []GeoPoint, from, to GeoPoint) []GeoPoint { // SameGeoPoint 判断两个经纬度点是否在拓扑所需精度内相等 / SameGeoPoint reports whether two longitude/latitude points are equal within // 地图拓扑辅助函数所需的精度内相等 / the precision needed by the map topology helpers. func SameGeoPoint(a, b GeoPoint) bool { - return math.Abs(normalizeLongitude(a.Longitude-b.Longitude)) < 1e-9 && + return math.Abs(NormalizeLongitude(a.Longitude-b.Longitude)) < 1e-9 && math.Abs(a.Latitude-b.Latitude) < 1e-9 } @@ -500,7 +500,7 @@ func nearestGeoPointIndex(points []GeoPoint, target GeoPoint) int { func angularDistanceDegrees(a, b GeoPoint) float64 { lat1 := a.Latitude * math.Pi / 180 lat2 := b.Latitude * math.Pi / 180 - dLongitude := normalizeLongitude(b.Longitude-a.Longitude) * math.Pi / 180 + dLongitude := NormalizeLongitude(b.Longitude-a.Longitude) * math.Pi / 180 cosine := math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(dLongitude) return math.Acos(math.Max(-1, math.Min(1, cosine))) * 180 / math.Pi diff --git a/internal/geodata/sphere_test.go b/internal/geodata/sphere_test.go index 4e10539..d86fcb3 100644 --- a/internal/geodata/sphere_test.go +++ b/internal/geodata/sphere_test.go @@ -62,7 +62,7 @@ func absoluteLongitude(value float64) float64 { func TestSphericalContainmentRejectsAntipodalInterior(t *testing.T) { for _, center := range []GeoPoint{{}, {Longitude: 179, Latitude: 70}, {Latitude: -90}} { polygon := SphericalCircle(center, 12, 48) - antipode := GeoPoint{Longitude: normalizeLongitude(center.Longitude + 180), Latitude: -center.Latitude} + antipode := GeoPoint{Longitude: NormalizeLongitude(center.Longitude + 180), Latitude: -center.Latitude} for _, reversed := range []bool{false, true} { if reversed { for first, last := 0, len(polygon)-1; first < last; first, last = first+1, last-1 { @@ -73,7 +73,7 @@ func TestSphericalContainmentRejectsAntipodalInterior(t *testing.T) { t.Errorf("center=%+v reversed=%v: direct containment must distinguish the antipode", center, reversed) } points := []GeoPoint{center, antipode, polygon[0], { - Longitude: normalizeLongitude(polygon[0].Longitude + 180), Latitude: -polygon[0].Latitude, + Longitude: NormalizeLongitude(polygon[0].Longitude + 180), Latitude: -polygon[0].Latitude, }} got := SphericalPolygonsContainPoints([][]GeoPoint{polygon}, points) for index, want := range []bool{true, false, true, false} { diff --git a/internal/geodata/sweep.go b/internal/geodata/sweep.go index c07aebf..82b97e7 100644 --- a/internal/geodata/sweep.go +++ b/internal/geodata/sweep.go @@ -106,7 +106,7 @@ func sweepEndpointTracksMonotone(group []openBoundarySweepSample) bool { if index > 1 { previousLength := vectorNorm(previousStep) currentLength := vectorNorm(step) - if math.Min(previousLength, currentLength)*6371.0088 > openBoundaryEndpointStepNoiseKM && + if math.Min(previousLength, currentLength)*EarthMeanRadiusKM > openBoundaryEndpointStepNoiseKM && vectorDot(previousStep, step) < -0.1*previousLength*currentLength { return false } @@ -116,7 +116,7 @@ func sweepEndpointTracksMonotone(group []openBoundarySweepSample) bool { // tangent chord even while every local step remains forward. Reserve a // few kilometres for that projection effect; larger gradual reversals // still invalidate the endpoint envelope. - if progress+openBoundaryEndpointProgressBackKM/6371.0088 < previousProgress { + if progress+openBoundaryEndpointProgressBackKM/EarthMeanRadiusKM < previousProgress { return false } previousProgress = progress @@ -266,7 +266,7 @@ func OpenBoundarySweepInnerCaps( caps := make([][]GeoPoint, 0, len(indices)) for _, index := range indices { boundary := openGeoRing(JoinPolylineSegments(samples[index].Boundaries)) - if len(boundary) < 3 || geoPointDistanceKM(boundary[0], boundary[len(boundary)-1]) > maximumGapKM { + if len(boundary) < 3 || EquatorialDistanceKM(boundary[0], boundary[len(boundary)-1]) > maximumGapKM { continue } caps = append(caps, boundary) @@ -281,7 +281,7 @@ func decimateOpenBoundaryPoints(points []GeoPoint, targetSpacingKM float64) []Ge result := make([]GeoPoint, 1, len(points)) result[0] = points[0] for _, point := range points[1 : len(points)-1] { - if geoPointDistanceKM(result[len(result)-1], point) >= targetSpacingKM { + if EquatorialDistanceKM(result[len(result)-1], point) >= targetSpacingKM { result = append(result, point) } } @@ -330,17 +330,17 @@ func openBoundarySweepGroups(samples []OpenBoundarySweepSample) ([][]openBoundar } value := openBoundarySweepSample{boundary: boundary, start: boundary[0], end: boundary[len(boundary)-1]} if len(current) > 0 { - keep := geoPointDistanceKM(previous.start, value.start) + - geoPointDistanceKM(previous.end, value.end) - reverse := geoPointDistanceKM(previous.start, value.end) + - geoPointDistanceKM(previous.end, value.start) + keep := EquatorialDistanceKM(previous.start, value.start) + + EquatorialDistanceKM(previous.end, value.end) + reverse := EquatorialDistanceKM(previous.start, value.end) + + EquatorialDistanceKM(previous.end, value.start) if reverse < keep { reverseSweepGeoPoints(value.boundary) value.start, value.end = value.end, value.start } if math.Max( - geoPointDistanceKM(previous.start, value.start), - geoPointDistanceKM(previous.end, value.end), + EquatorialDistanceKM(previous.start, value.start), + EquatorialDistanceKM(previous.end, value.end), ) > 2000 { groups = append(groups, current) current = nil @@ -567,7 +567,7 @@ func sweepTouchingPolygonPair( key := sweepTouchingCellKeyFromCoordinates(x+deltaX, y+deltaY, z+deltaZ) for _, candidate := range cells[key] { if candidate.polygon <= polygonIndex || - geoPointDistanceKM(point, candidate.point) > thresholdKM { + EquatorialDistanceKM(point, candidate.point) > thresholdKM { continue } pairs[[2]int{polygonIndex, candidate.polygon}] = struct{}{} @@ -588,7 +588,7 @@ func sweepTouchingPolygonPair( } for _, first := range polygons[firstIndex] { for _, second := range polygons[secondIndex] { - if !(geoPointDistanceKM(first, second) > thresholdKM) { + if !(EquatorialDistanceKM(first, second) > thresholdKM) { return firstIndex, secondIndex, GeoPoint{ Longitude: first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)/2, Latitude: (first.Latitude + second.Latitude) / 2, @@ -660,13 +660,3 @@ func reverseSweepGeoPoints(points []GeoPoint) { points[left], points[right] = points[right], points[left] } } - -func geoPointDistanceKM(first, second GeoPoint) float64 { - firstLatitude := first.Latitude * math.Pi / 180 - secondLatitude := second.Latitude * math.Pi / 180 - deltaLatitude := secondLatitude - firstLatitude - deltaLongitude := math.Remainder((second.Longitude-first.Longitude)*math.Pi/180, 2*math.Pi) - haversine := math.Sin(deltaLatitude/2)*math.Sin(deltaLatitude/2) + - math.Cos(firstLatitude)*math.Cos(secondLatitude)*math.Sin(deltaLongitude/2)*math.Sin(deltaLongitude/2) - return 2 * 6378.1366 * math.Asin(math.Sqrt(math.Min(1, haversine))) -} diff --git a/internal/geodata/topology.go b/internal/geodata/topology.go index b9427e1..ddbf939 100644 --- a/internal/geodata/topology.go +++ b/internal/geodata/topology.go @@ -80,7 +80,7 @@ func prepareTopologyPoints(points []GeoPoint) ([]GeoPoint, bool) { normalized := make([]GeoPoint, len(points)) copy(normalized, points) for index := range normalized { - normalized[index].Longitude = normalizeLongitude(normalized[index].Longitude) + normalized[index].Longitude = NormalizeLongitude(normalized[index].Longitude) } return normalized, true } @@ -382,17 +382,17 @@ func hemisphereIntersection(a, b GeoPoint) GeoPoint { firstSign := firstVector.z secondSign := secondVector.z if math.Abs(firstSign) <= 1e-15 { - return GeoPoint{Longitude: normalizeLongitude(first.Longitude), Latitude: 0} + return GeoPoint{Longitude: NormalizeLongitude(first.Longitude), Latitude: 0} } if math.Abs(secondSign) <= 1e-15 { - return GeoPoint{Longitude: normalizeLongitude(second.Longitude), Latitude: 0} + return GeoPoint{Longitude: NormalizeLongitude(second.Longitude), Latitude: 0} } left, right := 0.0, 1.0 for iteration := 0; iteration < 64; iteration++ { middle := (left + right) / 2 point := sphericalInterpolate(first, second, middle) if point.Latitude == 0 || right-left <= 1e-13 { - return GeoPoint{Longitude: normalizeLongitude(point.Longitude), Latitude: 0} + return GeoPoint{Longitude: NormalizeLongitude(point.Longitude), Latitude: 0} } if point.Latitude*first.Latitude > 0 { left = middle @@ -401,7 +401,7 @@ func hemisphereIntersection(a, b GeoPoint) GeoPoint { } } point := sphericalInterpolate(first, second, (left+right)/2) - return GeoPoint{Longitude: normalizeLongitude(point.Longitude), Latitude: 0} + return GeoPoint{Longitude: NormalizeLongitude(point.Longitude), Latitude: 0} } func sphericalLongitudeIntersection(a, b GeoPoint, boundary, adjustedLongitude float64) GeoPoint { @@ -434,14 +434,6 @@ func sphericalLongitudeIntersection(a, b GeoPoint, boundary, adjustedLongitude f return GeoPoint{Longitude: boundary, Latitude: point.Latitude} } -func normalizeLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} - // equirectangularSeamShift 返回把等经纬接缝从 ±180 搬到视图中心对面所需的经度旋转量。 // 视图中心为 0 时返回 0,调用方即可沿用未旋转的原有路径,保证既有输出逐字节不变。 func equirectangularSeamShift(view ClipView) float64 { @@ -473,7 +465,7 @@ func rotateLongitudes(points []GeoPoint, shift float64) []GeoPoint { rotated := make([]GeoPoint, len(points)) copy(rotated, points) for index := range rotated { - rotated[index].Longitude = normalizeLongitude180(rotated[index].Longitude + shift) + rotated[index].Longitude = NormalizeLongitude(rotated[index].Longitude + shift) } return rotated } @@ -493,11 +485,3 @@ func unshiftLongitudesInPlace(points []GeoPoint, shift float64) { points[index].Longitude = value } } - -func normalizeLongitude180(longitude float64) float64 { - value := math.Mod(longitude+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} diff --git a/internal/iau2006/data.go b/internal/iau2006/data.go new file mode 100644 index 0000000..004aa72 --- /dev/null +++ b/internal/iau2006/data.go @@ -0,0 +1,1399 @@ +// Code generated by a table generator; DO NOT EDIT. + +package iau2006 + +var lunisolarTerms = [...]lunisolarTerm{ + {[5]int8{0, 0, 0, 0, 1}, -172064161.0, -174666.0, 33386.0, 92052331.0, 9086.0, 15377.0}, + {[5]int8{0, 0, 2, -2, 2}, -13170906.0, -1675.0, -13696.0, 5730336.0, -3015.0, -4587.0}, + {[5]int8{0, 0, 2, 0, 2}, -2276413.0, -234.0, 2796.0, 978459.0, -485.0, 1374.0}, + {[5]int8{0, 0, 0, 0, 2}, 2074554.0, 207.0, -698.0, -897492.0, 470.0, -291.0}, + {[5]int8{0, 1, 0, 0, 0}, 1475877.0, -3633.0, 11817.0, 73871.0, -184.0, -1924.0}, + {[5]int8{0, 1, 2, -2, 2}, -516821.0, 1226.0, -524.0, 224386.0, -677.0, -174.0}, + {[5]int8{1, 0, 0, 0, 0}, 711159.0, 73.0, -872.0, -6750.0, 0.0, 358.0}, + {[5]int8{0, 0, 2, 0, 1}, -387298.0, -367.0, 380.0, 200728.0, 18.0, 318.0}, + {[5]int8{1, 0, 2, 0, 2}, -301461.0, -36.0, 816.0, 129025.0, -63.0, 367.0}, + {[5]int8{0, -1, 2, -2, 2}, 215829.0, -494.0, 111.0, -95929.0, 299.0, 132.0}, + {[5]int8{0, 0, 2, -2, 1}, 128227.0, 137.0, 181.0, -68982.0, -9.0, 39.0}, + {[5]int8{-1, 0, 2, 0, 2}, 123457.0, 11.0, 19.0, -53311.0, 32.0, -4.0}, + {[5]int8{-1, 0, 0, 2, 0}, 156994.0, 10.0, -168.0, -1235.0, 0.0, 82.0}, + {[5]int8{1, 0, 0, 0, 1}, 63110.0, 63.0, 27.0, -33228.0, 0.0, -9.0}, + {[5]int8{-1, 0, 0, 0, 1}, -57976.0, -63.0, -189.0, 31429.0, 0.0, -75.0}, + {[5]int8{-1, 0, 2, 2, 2}, -59641.0, -11.0, 149.0, 25543.0, -11.0, 66.0}, + {[5]int8{1, 0, 2, 0, 1}, -51613.0, -42.0, 129.0, 26366.0, 0.0, 78.0}, + {[5]int8{-2, 0, 2, 0, 1}, 45893.0, 50.0, 31.0, -24236.0, -10.0, 20.0}, + {[5]int8{0, 0, 0, 2, 0}, 63384.0, 11.0, -150.0, -1220.0, 0.0, 29.0}, + {[5]int8{0, 0, 2, 2, 2}, -38571.0, -1.0, 158.0, 16452.0, -11.0, 68.0}, + {[5]int8{0, -2, 2, -2, 2}, 32481.0, 0.0, 0.0, -13870.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 2, 0}, -47722.0, 0.0, -18.0, 477.0, 0.0, -25.0}, + {[5]int8{2, 0, 2, 0, 2}, -31046.0, -1.0, 131.0, 13238.0, -11.0, 59.0}, + {[5]int8{1, 0, 2, -2, 2}, 28593.0, 0.0, -1.0, -12338.0, 10.0, -3.0}, + {[5]int8{-1, 0, 2, 0, 1}, 20441.0, 21.0, 10.0, -10758.0, 0.0, -3.0}, + {[5]int8{2, 0, 0, 0, 0}, 29243.0, 0.0, -74.0, -609.0, 0.0, 13.0}, + {[5]int8{0, 0, 2, 0, 0}, 25887.0, 0.0, -66.0, -550.0, 0.0, 11.0}, + {[5]int8{0, 1, 0, 0, 1}, -14053.0, -25.0, 79.0, 8551.0, -2.0, -45.0}, + {[5]int8{-1, 0, 0, 2, 1}, 15164.0, 10.0, 11.0, -8001.0, 0.0, -1.0}, + {[5]int8{0, 2, 2, -2, 2}, -15794.0, 72.0, -16.0, 6850.0, -42.0, -5.0}, + {[5]int8{0, 0, -2, 2, 0}, 21783.0, 0.0, 13.0, -167.0, 0.0, 13.0}, + {[5]int8{1, 0, 0, -2, 1}, -12873.0, -10.0, -37.0, 6953.0, 0.0, -14.0}, + {[5]int8{0, -1, 0, 0, 1}, -12654.0, 11.0, 63.0, 6415.0, 0.0, 26.0}, + {[5]int8{-1, 0, 2, 2, 1}, -10204.0, 0.0, 25.0, 5222.0, 0.0, 15.0}, + {[5]int8{0, 2, 0, 0, 0}, 16707.0, -85.0, -10.0, 168.0, -1.0, 10.0}, + {[5]int8{1, 0, 2, 2, 2}, -7691.0, 0.0, 44.0, 3268.0, 0.0, 19.0}, + {[5]int8{-2, 0, 2, 0, 0}, -11024.0, 0.0, -14.0, 104.0, 0.0, 2.0}, + {[5]int8{0, 1, 2, 0, 2}, 7566.0, -21.0, -11.0, -3250.0, 0.0, -5.0}, + {[5]int8{0, 0, 2, 2, 1}, -6637.0, -11.0, 25.0, 3353.0, 0.0, 14.0}, + {[5]int8{0, -1, 2, 0, 2}, -7141.0, 21.0, 8.0, 3070.0, 0.0, 4.0}, + {[5]int8{0, 0, 0, 2, 1}, -6302.0, -11.0, 2.0, 3272.0, 0.0, 4.0}, + {[5]int8{1, 0, 2, -2, 1}, 5800.0, 10.0, 2.0, -3045.0, 0.0, -1.0}, + {[5]int8{2, 0, 2, -2, 2}, 6443.0, 0.0, -7.0, -2768.0, 0.0, -4.0}, + {[5]int8{-2, 0, 0, 2, 1}, -5774.0, -11.0, -15.0, 3041.0, 0.0, -5.0}, + {[5]int8{2, 0, 2, 0, 1}, -5350.0, 0.0, 21.0, 2695.0, 0.0, 12.0}, + {[5]int8{0, -1, 2, -2, 1}, -4752.0, -11.0, -3.0, 2719.0, 0.0, -3.0}, + {[5]int8{0, 0, 0, -2, 1}, -4940.0, -11.0, -21.0, 2720.0, 0.0, -9.0}, + {[5]int8{-1, -1, 0, 2, 0}, 7350.0, 0.0, -8.0, -51.0, 0.0, 4.0}, + {[5]int8{2, 0, 0, -2, 1}, 4065.0, 0.0, 6.0, -2206.0, 0.0, 1.0}, + {[5]int8{1, 0, 0, 2, 0}, 6579.0, 0.0, -24.0, -199.0, 0.0, 2.0}, + {[5]int8{0, 1, 2, -2, 1}, 3579.0, 0.0, 5.0, -1900.0, 0.0, 1.0}, + {[5]int8{1, -1, 0, 0, 0}, 4725.0, 0.0, -6.0, -41.0, 0.0, 3.0}, + {[5]int8{-2, 0, 2, 0, 2}, -3075.0, 0.0, -2.0, 1313.0, 0.0, -1.0}, + {[5]int8{3, 0, 2, 0, 2}, -2904.0, 0.0, 15.0, 1233.0, 0.0, 7.0}, + {[5]int8{0, -1, 0, 2, 0}, 4348.0, 0.0, -10.0, -81.0, 0.0, 2.0}, + {[5]int8{1, -1, 2, 0, 2}, -2878.0, 0.0, 8.0, 1232.0, 0.0, 4.0}, + {[5]int8{0, 0, 0, 1, 0}, -4230.0, 0.0, 5.0, -20.0, 0.0, -2.0}, + {[5]int8{-1, -1, 2, 2, 2}, -2819.0, 0.0, 7.0, 1207.0, 0.0, 3.0}, + {[5]int8{-1, 0, 2, 0, 0}, -4056.0, 0.0, 5.0, 40.0, 0.0, -2.0}, + {[5]int8{0, -1, 2, 2, 2}, -2647.0, 0.0, 11.0, 1129.0, 0.0, 5.0}, + {[5]int8{-2, 0, 0, 0, 1}, -2294.0, 0.0, -10.0, 1266.0, 0.0, -4.0}, + {[5]int8{1, 1, 2, 0, 2}, 2481.0, 0.0, -7.0, -1062.0, 0.0, -3.0}, + {[5]int8{2, 0, 0, 0, 1}, 2179.0, 0.0, -2.0, -1129.0, 0.0, -2.0}, + {[5]int8{-1, 1, 0, 1, 0}, 3276.0, 0.0, 1.0, -9.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, 0, 0}, -3389.0, 0.0, 5.0, 35.0, 0.0, -2.0}, + {[5]int8{1, 0, 2, 0, 0}, 3339.0, 0.0, -13.0, -107.0, 0.0, 1.0}, + {[5]int8{-1, 0, 2, -2, 1}, -1987.0, 0.0, -6.0, 1073.0, 0.0, -2.0}, + {[5]int8{1, 0, 0, 0, 2}, -1981.0, 0.0, 0.0, 854.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 1, 0}, 4026.0, 0.0, -353.0, -553.0, 0.0, -139.0}, + {[5]int8{0, 0, 2, 1, 2}, 1660.0, 0.0, -5.0, -710.0, 0.0, -2.0}, + {[5]int8{-1, 0, 2, 4, 2}, -1521.0, 0.0, 9.0, 647.0, 0.0, 4.0}, + {[5]int8{-1, 1, 0, 1, 1}, 1314.0, 0.0, 0.0, -700.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, -2, 1}, -1283.0, 0.0, 0.0, 672.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 2, 1}, -1331.0, 0.0, 8.0, 663.0, 0.0, 4.0}, + {[5]int8{-2, 0, 2, 2, 2}, 1383.0, 0.0, -2.0, -594.0, 0.0, -2.0}, + {[5]int8{-1, 0, 0, 0, 2}, 1405.0, 0.0, 4.0, -610.0, 0.0, 2.0}, + {[5]int8{1, 1, 2, -2, 2}, 1290.0, 0.0, 0.0, -556.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 4, 2}, -1214.0, 0.0, 5.0, 518.0, 0.0, 2.0}, + {[5]int8{-1, 0, 4, 0, 2}, 1146.0, 0.0, -3.0, -490.0, 0.0, -1.0}, + {[5]int8{2, 0, 2, -2, 1}, 1019.0, 0.0, -1.0, -527.0, 0.0, -1.0}, + {[5]int8{2, 0, 2, 2, 2}, -1100.0, 0.0, 9.0, 465.0, 0.0, 4.0}, + {[5]int8{1, 0, 0, 2, 1}, -970.0, 0.0, 2.0, 496.0, 0.0, 1.0}, + {[5]int8{3, 0, 0, 0, 0}, 1575.0, 0.0, -6.0, -50.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, -2, 2}, 934.0, 0.0, -3.0, -399.0, 0.0, -1.0}, + {[5]int8{0, 0, 4, -2, 2}, 922.0, 0.0, -1.0, -395.0, 0.0, -1.0}, + {[5]int8{0, 1, 2, 0, 1}, 815.0, 0.0, -1.0, -422.0, 0.0, -1.0}, + {[5]int8{0, 0, -2, 2, 1}, 834.0, 0.0, 2.0, -440.0, 0.0, 1.0}, + {[5]int8{0, 0, 2, -2, 3}, 1248.0, 0.0, 0.0, -170.0, 0.0, 1.0}, + {[5]int8{-1, 0, 0, 4, 0}, 1338.0, 0.0, -5.0, -39.0, 0.0, 0.0}, + {[5]int8{2, 0, -2, 0, 1}, 716.0, 0.0, -2.0, -389.0, 0.0, -1.0}, + {[5]int8{-2, 0, 0, 4, 0}, 1282.0, 0.0, -3.0, -23.0, 0.0, 1.0}, + {[5]int8{-1, -1, 0, 2, 1}, 742.0, 0.0, 1.0, -391.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 1, 1}, 1020.0, 0.0, -25.0, -495.0, 0.0, -10.0}, + {[5]int8{0, 1, 0, 0, 2}, 715.0, 0.0, -4.0, -326.0, 0.0, 2.0}, + {[5]int8{0, 0, -2, 0, 1}, -666.0, 0.0, -3.0, 369.0, 0.0, -1.0}, + {[5]int8{0, -1, 2, 0, 1}, -667.0, 0.0, 1.0, 346.0, 0.0, 1.0}, + {[5]int8{0, 0, 2, -1, 2}, -704.0, 0.0, 0.0, 304.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 4, 2}, -694.0, 0.0, 5.0, 294.0, 0.0, 2.0}, + {[5]int8{-2, -1, 0, 2, 0}, -1014.0, 0.0, -1.0, 4.0, 0.0, -1.0}, + {[5]int8{1, 1, 0, -2, 1}, -585.0, 0.0, -2.0, 316.0, 0.0, -1.0}, + {[5]int8{-1, 1, 0, 2, 0}, -949.0, 0.0, 1.0, 8.0, 0.0, -1.0}, + {[5]int8{-1, 1, 0, 1, 2}, -595.0, 0.0, 0.0, 258.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 0, 1}, 528.0, 0.0, 0.0, -279.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 2, 2}, -590.0, 0.0, 4.0, 252.0, 0.0, 2.0}, + {[5]int8{-1, 1, 2, 2, 2}, 570.0, 0.0, -2.0, -244.0, 0.0, -1.0}, + {[5]int8{3, 0, 2, 0, 1}, -502.0, 0.0, 3.0, 250.0, 0.0, 2.0}, + {[5]int8{0, 1, -2, 2, 0}, -875.0, 0.0, 1.0, 29.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, -2, 1}, -492.0, 0.0, -3.0, 275.0, 0.0, -1.0}, + {[5]int8{0, 1, 2, 2, 2}, 535.0, 0.0, -2.0, -228.0, 0.0, -1.0}, + {[5]int8{-1, -1, 2, 2, 1}, -467.0, 0.0, 1.0, 240.0, 0.0, 1.0}, + {[5]int8{0, -1, 0, 0, 2}, 591.0, 0.0, 0.0, -253.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -4, 1}, -453.0, 0.0, -1.0, 244.0, 0.0, -1.0}, + {[5]int8{-1, 0, -2, 2, 0}, 766.0, 0.0, 1.0, 9.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 2, 1}, -446.0, 0.0, 2.0, 225.0, 0.0, 1.0}, + {[5]int8{2, -1, 2, 0, 2}, -488.0, 0.0, 2.0, 207.0, 0.0, 1.0}, + {[5]int8{0, 0, 0, 2, 2}, -468.0, 0.0, 0.0, 201.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 0, 1}, -421.0, 0.0, 1.0, 216.0, 0.0, 1.0}, + {[5]int8{-1, 1, 2, 0, 2}, 463.0, 0.0, 0.0, -200.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 2, 0}, -673.0, 0.0, 2.0, 14.0, 0.0, 0.0}, + {[5]int8{0, -1, -2, 2, 0}, 658.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 3, 2, -2, 2}, -438.0, 0.0, 0.0, 188.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 1, 1}, -390.0, 0.0, 0.0, 205.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 2, 0}, 639.0, -11.0, -2.0, -19.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 0, 2}, 412.0, 0.0, -2.0, -176.0, 0.0, -1.0}, + {[5]int8{1, 1, 0, 0, 1}, -361.0, 0.0, 0.0, 189.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 0, 1}, 360.0, 0.0, -1.0, -185.0, 0.0, -1.0}, + {[5]int8{2, 0, 0, 2, 0}, 588.0, 0.0, -3.0, -24.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, 2, 0}, -578.0, 0.0, 1.0, 5.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 2, 2}, -396.0, 0.0, 0.0, 171.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 1, 0}, 565.0, 0.0, -1.0, -6.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, -2, 1}, -335.0, 0.0, -1.0, 184.0, 0.0, -1.0}, + {[5]int8{-1, 0, 2, -2, 2}, 357.0, 0.0, 1.0, -154.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, -1, 1}, 321.0, 0.0, 1.0, -174.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 0, 1}, -301.0, 0.0, -1.0, 162.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -1, 2}, -334.0, 0.0, 0.0, 144.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 2, 0}, 493.0, 0.0, -2.0, -15.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 4, 0}, 494.0, 0.0, -2.0, -19.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 1, 2}, 337.0, 0.0, -1.0, -143.0, 0.0, -1.0}, + {[5]int8{0, 0, 2, 1, 1}, 280.0, 0.0, -1.0, -144.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -2, 2}, 309.0, 0.0, 1.0, -134.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 4, 1}, -263.0, 0.0, 2.0, 131.0, 0.0, 1.0}, + {[5]int8{1, 0, -2, 0, 1}, 253.0, 0.0, 1.0, -138.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, -2, 1}, 245.0, 0.0, 0.0, -128.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 2, 0}, 416.0, 0.0, -2.0, -17.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, -1, 1}, -229.0, 0.0, 0.0, 128.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 2, 1}, 231.0, 0.0, 0.0, -120.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, 0, 2}, -259.0, 0.0, 2.0, 109.0, 0.0, 1.0}, + {[5]int8{2, -1, 0, 0, 0}, 375.0, 0.0, -1.0, -8.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, -2, 2}, 252.0, 0.0, 0.0, -108.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 1, 2}, -245.0, 0.0, 1.0, 104.0, 0.0, 0.0}, + {[5]int8{1, 0, 4, -2, 2}, 243.0, 0.0, -1.0, -104.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 0, 1}, 208.0, 0.0, 1.0, -112.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 2, 1}, 199.0, 0.0, 0.0, -102.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 4, 1}, -208.0, 0.0, 1.0, 105.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 0, 0}, 335.0, 0.0, -2.0, -14.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 1, 0}, -325.0, 0.0, 1.0, 7.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 4, 1}, -187.0, 0.0, 0.0, 96.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, 0, 1}, 197.0, 0.0, -1.0, -100.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 2, 1}, -192.0, 0.0, 2.0, 94.0, 0.0, 1.0}, + {[5]int8{0, 0, 2, -3, 2}, -188.0, 0.0, 0.0, 83.0, 0.0, 0.0}, + {[5]int8{-1, -2, 0, 2, 0}, 276.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, 0, 0}, -286.0, 0.0, 1.0, 6.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, 0, 2}, 186.0, 0.0, -1.0, -79.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 0, 3}, -219.0, 0.0, 0.0, 43.0, 0.0, 0.0}, + {[5]int8{0, 3, 0, 0, 0}, 276.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, -4, 1}, -153.0, 0.0, -1.0, 84.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, 2, 1}, -156.0, 0.0, 0.0, 81.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 4, 1}, -154.0, 0.0, 1.0, 78.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 4, 2}, -174.0, 0.0, 1.0, 75.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 4, 2}, -163.0, 0.0, 2.0, 69.0, 0.0, 1.0}, + {[5]int8{-2, 2, 0, 2, 0}, -228.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 0, 1}, 91.0, 0.0, -4.0, -54.0, 0.0, -2.0}, + {[5]int8{-2, 0, 0, 2, 2}, 175.0, 0.0, 0.0, -75.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 0, 2}, -159.0, 0.0, 0.0, 69.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -2, 1}, 141.0, 0.0, 0.0, -72.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, -2, 1}, 147.0, 0.0, 0.0, -75.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 2, 1}, -132.0, 0.0, 0.0, 69.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -1, 1}, 159.0, 0.0, -28.0, -54.0, 0.0, 11.0}, + {[5]int8{0, -2, 0, 2, 0}, 213.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 4, 1}, 123.0, 0.0, 0.0, -64.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 0, 1}, -118.0, 0.0, -1.0, 66.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 2, 2}, 144.0, 0.0, -1.0, -61.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 4, 1}, -121.0, 0.0, 1.0, 60.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, 2, 2}, -134.0, 0.0, 1.0, 56.0, 0.0, 1.0}, + {[5]int8{-1, 1, 2, -2, 1}, -105.0, 0.0, 0.0, 57.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, -4, 1}, -102.0, 0.0, 0.0, 56.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, -2, 2}, 120.0, 0.0, 0.0, -52.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, -4, 1}, 101.0, 0.0, 0.0, -54.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 2, 1}, -113.0, 0.0, 0.0, 59.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, -1, 1}, -106.0, 0.0, 0.0, 61.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, 2, 2}, -129.0, 0.0, 1.0, 55.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 2, 1}, -114.0, 0.0, 0.0, 57.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, -2, 2}, 113.0, 0.0, -1.0, -49.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, -2, 2}, -102.0, 0.0, 0.0, 44.0, 0.0, 0.0}, + {[5]int8{0, 2, 0, 0, 1}, -94.0, 0.0, 0.0, 51.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -4, 1}, -100.0, 0.0, -1.0, 56.0, 0.0, 0.0}, + {[5]int8{0, 2, 2, -2, 1}, 87.0, 0.0, 0.0, -47.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 4, 0}, 161.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 0, 1}, 96.0, 0.0, 0.0, -50.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 4, 0}, 151.0, 0.0, -1.0, -5.0, 0.0, 0.0}, + {[5]int8{-1, -2, 2, 2, 2}, -104.0, 0.0, 0.0, 44.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 4, 2}, -110.0, 0.0, 0.0, 48.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 2, 1}, -100.0, 0.0, 1.0, 50.0, 0.0, 0.0}, + {[5]int8{-2, 1, 0, 2, 0}, 92.0, 0.0, -5.0, 12.0, 0.0, -2.0}, + {[5]int8{-2, 1, 2, 0, 1}, 82.0, 0.0, 0.0, -45.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, -2, 1}, 82.0, 0.0, 0.0, -45.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 0, 1}, -78.0, 0.0, 0.0, 41.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, -2, 1}, -77.0, 0.0, 0.0, 43.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 2, 2}, 2.0, 0.0, 0.0, 54.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, -1, 2}, 94.0, 0.0, 0.0, -40.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, -2, 2}, -93.0, 0.0, 0.0, 40.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, 0, 2}, -83.0, 0.0, 10.0, 40.0, 0.0, -2.0}, + {[5]int8{-1, 0, 2, 1, 2}, 83.0, 0.0, 0.0, -36.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 0, 2}, -91.0, 0.0, 0.0, 39.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 0, 3}, 128.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, 0, 2}, -79.0, 0.0, 0.0, 34.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 0, 1}, -83.0, 0.0, 0.0, 47.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 2, 1}, 84.0, 0.0, 0.0, -44.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, 0, 1}, 83.0, 0.0, 0.0, -43.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 3, 2}, 91.0, 0.0, 0.0, -39.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, 0, 1}, -77.0, 0.0, 0.0, 39.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 2, 1}, 84.0, 0.0, 0.0, -43.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 4, 2}, -92.0, 0.0, 1.0, 39.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, 2, 2}, -92.0, 0.0, 1.0, 39.0, 0.0, 0.0}, + {[5]int8{0, 2, -2, 2, 0}, -94.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, -1, 1}, 68.0, 0.0, 0.0, -36.0, 0.0, 0.0}, + {[5]int8{0, -2, 0, 0, 1}, -61.0, 0.0, 0.0, 32.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -4, 2}, 71.0, 0.0, 0.0, -31.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, -2, 1}, 62.0, 0.0, 0.0, -34.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 0, 1}, -63.0, 0.0, 0.0, 33.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, -2, 2}, -73.0, 0.0, 0.0, 32.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 4, 0}, 115.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 3, 0}, -103.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 2, 2}, 63.0, 0.0, 0.0, -28.0, 0.0, 0.0}, + {[5]int8{0, 2, 2, 0, 2}, 74.0, 0.0, 0.0, -32.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, 2, 0}, -103.0, 0.0, -3.0, 3.0, 0.0, -1.0}, + {[5]int8{2, 0, 2, -1, 2}, -69.0, 0.0, 0.0, 30.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 1, 1}, 57.0, 0.0, 0.0, -29.0, 0.0, 0.0}, + {[5]int8{4, 0, 0, 0, 0}, 94.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 0, 1}, 64.0, 0.0, 0.0, -33.0, 0.0, 0.0}, + {[5]int8{3, -1, 2, 0, 2}, -63.0, 0.0, 0.0, 26.0, 0.0, 0.0}, + {[5]int8{-2, 2, 0, 2, 1}, -38.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -3, 1}, -43.0, 0.0, 0.0, 24.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, -4, 1}, -45.0, 0.0, 0.0, 23.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, -2, 1}, 47.0, 0.0, 0.0, -24.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, -1, 1}, -48.0, 0.0, 0.0, 25.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, -2, 1}, 45.0, 0.0, 0.0, -26.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 0, 2}, 56.0, 0.0, 0.0, -25.0, 0.0, 0.0}, + {[5]int8{-2, 0, -2, 2, 0}, 88.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 4, 0}, -75.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -2, 0, 0, 0}, 85.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 1, 1}, 49.0, 0.0, 0.0, -26.0, 0.0, 0.0}, + {[5]int8{-1, 2, 0, 2, 0}, -74.0, 0.0, -3.0, -1.0, 0.0, -1.0}, + {[5]int8{1, -1, 2, -2, 1}, -39.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + {[5]int8{1, 2, 2, -2, 2}, 45.0, 0.0, 0.0, -20.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, -2, 2}, 51.0, 0.0, 0.0, -22.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -1, 1}, -40.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, -2, 1}, 41.0, 0.0, 0.0, -21.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, -2, 1}, -42.0, 0.0, 0.0, 24.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, 0, 2}, -51.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 1, 1}, -42.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + {[5]int8{1, 0, 4, -2, 1}, 39.0, 0.0, 0.0, -21.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, 2, 2}, 46.0, 0.0, 0.0, -18.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 1, 2}, -53.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 4, 0}, 82.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 2, 0}, 81.0, 0.0, -1.0, -4.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 1, 2}, 47.0, 0.0, 0.0, -19.0, 0.0, 0.0}, + {[5]int8{3, 1, 2, 0, 2}, 53.0, 0.0, 0.0, -23.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, 0, 1}, -45.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 0, 0}, -44.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 1, -2, 2, 1}, -33.0, 0.0, 0.0, 16.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, 1, 0}, -61.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{0, -1, -2, 2, 1}, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {[5]int8{2, -1, 0, -2, 1}, -38.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, -1, 2}, -33.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -3, 2}, -60.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, -2, 3}, 48.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, -3, 1}, 27.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 2, 1}, 38.0, 0.0, 0.0, -20.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, -4, 2}, 31.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + {[5]int8{-2, 1, 0, 0, 1}, -29.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, -1, 1}, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, -4, 2}, -32.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -4, 4}, 45.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -4, 2}, -44.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {[5]int8{-1, -2, 0, 2, 1}, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 3, 0}, -51.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, 2, 1}, -36.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 2, 2}, 44.0, 0.0, 0.0, -19.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 2, 1}, 26.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 2, 0}, -60.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, -1, 0, 0, 1}, 35.0, 0.0, 0.0, -18.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, 2, 2}, -27.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, 1, 0}, 47.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, 1, 4, -2, 2}, 36.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, -2, 1}, -36.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, -4, 1}, -35.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 2, 1}, -37.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, 2, 1}, 32.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {[5]int8{-1, 2, 2, 2, 2}, 35.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + {[5]int8{3, 1, 2, -2, 2}, 32.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, 4, 0}, 65.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, -1, 0, 2, 0}, 47.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, 0, 1}, 32.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {[5]int8{2, 0, 4, -2, 2}, 37.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 4, 1}, -30.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 4, 1}, -32.0, 0.0, 0.0, 16.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, 2, 2}, -31.0, 0.0, 0.0, 13.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 3, 2}, 37.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 4, 2}, 31.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, 2, 0}, 49.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, 2, 2}, 32.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 2, 1}, 23.0, 0.0, 0.0, -12.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 6, 2}, -43.0, 0.0, 0.0, 18.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 2, 2}, 26.0, 0.0, 0.0, -11.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 6, 2}, -32.0, 0.0, 0.0, 14.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 4, 1}, -29.0, 0.0, 0.0, 14.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 4, 2}, -27.0, 0.0, 0.0, 12.0, 0.0, 0.0}, + {[5]int8{1, 1, -2, 1, 0}, 30.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 1, 2, 1, 2}, -11.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{2, 0, -2, 0, 2}, -21.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 1, 2}, -34.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {[5]int8{-4, 0, 2, 2, 1}, -10.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 1, 0}, -36.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, 2, 2}, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -1, 2}, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, -2, 3}, -21.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, 0, 0}, -29.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, -2, 4}, -15.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-2, -2, 0, 2, 0}, -20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, -2, 4, 0}, 28.0, 0.0, 0.0, 0.0, 0.0, -2.0}, + {[5]int8{0, -2, -2, 2, 0}, 17.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 2, 0, -2, 1}, -22.0, 0.0, 0.0, 12.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, -4, 1}, -14.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, -2, 2}, 24.0, 0.0, 0.0, -11.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, -4, 1}, 11.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, -2, 2}, 14.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 0, 0}, 24.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 0, 2}, 18.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 1, 0}, -38.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, 1, 0}, -31.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 2, 1}, -16.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {[5]int8{-1, -1, -2, 2, 0}, 29.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, -4, 1}, -18.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, -4, 1}, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{0, 2, 0, -2, 1}, -17.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -3, 1}, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, -2, 2}, 16.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 0, 1}, 22.0, 0.0, 0.0, -12.0, 0.0, 0.0}, + {[5]int8{-4, 0, 0, 2, 0}, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, -4, 1}, -13.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, -4, 1}, -17.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -4, 1}, -14.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {[5]int8{0, 3, 2, -2, 2}, 0.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + {[5]int8{-3, -1, 0, 4, 0}, 14.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 4, 1}, 19.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + {[5]int8{1, -1, -2, 2, 0}, -34.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 2, 2}, -20.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {[5]int8{1, -2, 0, 0, 1}, 9.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 0, 2}, -18.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 1, 2}, 13.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 0, 0}, 17.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, -2, 2}, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, -1, 1}, 15.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 0, 3}, -11.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, 0, 2}, 13.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 0, 0}, -18.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 2, 0, 0, 0}, -35.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 2, 2, 0, 2}, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, -2, 1}, -19.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, -4, 2}, -26.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + {[5]int8{1, 2, 2, -2, 1}, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{1, 0, 4, -4, 2}, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 4, 1}, 10.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, 2, 2}, -21.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{-2, 1, 0, 4, 0}, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 2, 1}, 9.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{2, 0, -2, 2, 0}, -29.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 1, 1}, -19.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 2, 2}, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, -1, 2}, 22.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, 0, 1}, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, 0, 1}, -20.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 0, 0}, -20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -1, 4, -2, 2}, -17.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -2, 4}, 15.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{0, 2, 2, 0, 1}, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 6, 0}, 14.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 4, 1}, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{1, -2, 0, 2, 0}, 25.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 4, 2}, -13.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{-1, -2, 2, 2, 1}, -14.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, -2, 2}, 13.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, -2, 1}, -17.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, -2, 1}, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{-2, 0, -2, 0, 1}, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 3, 1}, 10.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 3, 0}, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 4, 0}, -22.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 2, 0}, 28.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 3, 2}, 15.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 2, 2}, 23.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 1, 2}, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{3, -1, 0, 0, 0}, 29.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 1, 0}, -25.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 0, 0}, 22.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 1, 0}, -18.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 0, 3}, 15.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{3, 1, 0, 0, 0}, -23.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{3, -1, 2, -2, 2}, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, -1, 1}, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 0, 0}, -19.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -1, 2}, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{1, 2, 2, 0, 2}, 21.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 6, 0}, 23.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, 4, 1}, -16.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 4, 1}, -19.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, 2, 1}, -22.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 2, 0}, 27.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 3, 1}, 16.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, 4, 2}, 19.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 2, 2}, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{2, -2, 2, 0, 2}, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 3, 2}, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, -1, 2}, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, -2, 1}, 18.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 6, 0}, 16.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-1, -2, 2, 4, 2}, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 6, 2}, -23.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 4, 0}, 16.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, 2, 1}, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {[5]int8{3, -1, 2, 0, 1}, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, 0, 0}, 30.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{1, 0, 4, 0, 2}, 24.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + {[5]int8{5, 0, 2, -2, 2}, 10.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 4, 1}, -16.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, 2, 1}, -16.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 4, 2}, 17.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 4, 2}, -24.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {[5]int8{3, -1, 2, 2, 2}, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, 2, 1}, -24.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + {[5]int8{5, 0, 2, 0, 2}, -23.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 6, 2}, -13.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, 2, 2}, -15.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {[5]int8{0, -1, 1, -1, 1}, 0.0, 0.0, -1988.0, 0.0, 0.0, -1679.0}, + {[5]int8{-1, 0, 1, 0, 3}, 0.0, 0.0, -63.0, 0.0, 0.0, -27.0}, + {[5]int8{0, -2, 2, -2, 3}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, -1, 0, 1}, 0.0, 0.0, 5.0, 0.0, 0.0, 4.0}, + {[5]int8{2, -2, 0, -2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-1, 0, 1, 0, 2}, 0.0, 0.0, 364.0, 0.0, 0.0, 176.0}, + {[5]int8{-1, 0, 1, 0, 1}, 0.0, 0.0, -1044.0, 0.0, 0.0, -891.0}, + {[5]int8{-1, -1, 2, -1, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-2, 2, 0, 2, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 1, 0, 0}, 0.0, 0.0, 330.0, 0.0, 0.0, 0.0}, + {[5]int8{-4, 1, 2, 2, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 1, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 0, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, 1, 1}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, -1, -2, 0, 1}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-4, 0, 2, 2, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 1, 0, 3, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, -1, 2, 0}, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -2, 0, 0, 2}, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{0, -2, 0, 0, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 3, 0}, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 2, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 3, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-4, 0, 0, 4, 0}, -12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 1, -2, 0, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{2, -1, 0, -2, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, 0, 1, -1, 0}, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 2, 0, 1, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, 0, 2}, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{1, 1, 0, -1, 1}, 7.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{1, 0, 1, -2, 1}, 0.0, 0.0, -12.0, 0.0, 0.0, -10.0}, + {[5]int8{0, 2, 0, 0, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, -3, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, -1, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, -2, 2}, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, -2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, -2, 0, 2, 1}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-2, 0, -2, 4, 0}, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 2, 2, -4, 1}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, -4, 2}, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-1, 2, 2, -2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, -3, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 2, 0, 0, 1}, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, -2, 0}, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, -2, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 0, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, -1, 2}, -8.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-2, 1, 0, 1, 0}, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -2, 0, -2, 1}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{1, 0, -2, 0, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-3, 1, 0, 2, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 1, -2, 2, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 2, 0}, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, -1, 0, 2, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, -6, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, -4, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, -4, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, -2, 1}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, -4, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, -2, 2}, 9.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, -2, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 0, -2, -2, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-4, 0, 2, 0, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, -1, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, 0, 2}, 9.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 1, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 1, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, -2, 2, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 0, -4, 2, 0}, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, -1, -2, 2, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -6, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, -4, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, -4, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, -4, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, -4, 1}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{0, 1, 4, -4, 4}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 4, -4, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-1, -1, -2, 4, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -3, 0, 2, 0}, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, -2, 4, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 3, 0}, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, 3, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 3, 1}, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{0, -1, 0, 1, 0}, -13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 2, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 1, -2, 2, 0}, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 2, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, -2, 1}, 10.0, 0.0, 13.0, 6.0, 0.0, -5.0}, + {[5]int8{0, 0, 1, 0, 2}, 0.0, 0.0, 30.0, 0.0, 0.0, 14.0}, + {[5]int8{0, 0, 1, 0, 1}, 0.0, 0.0, -162.0, 0.0, 0.0, -138.0}, + {[5]int8{0, 0, 1, 0, 0}, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 2, 0, 2, 1}, -7.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 0, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 0, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, -1, 1}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, -2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, -2, 3}, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 2, 0, 0, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, -3, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 1, 4, -2, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, -2, 0, 4, 0}, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -3, 0, 2, 0}, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, -2, 4, 0}, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 3, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 4, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 3, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, -2, 0, 0, 0}, 7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 1, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 2, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, 0, 1}, -6.0, 0.0, -3.0, 3.0, 0.0, 1.0}, + {[5]int8{-1, 0, 1, 2, 1}, 0.0, 0.0, -3.0, 0.0, 0.0, -2.0}, + {[5]int8{-1, 1, 0, 3, 0}, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 1, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, -1, 2, 0, 0}, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 1, 2, 2, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, -2, 2, -2, 2}, -1.0, 0.0, 3.0, 3.0, 0.0, -1.0}, + {[5]int8{1, 1, 0, 1, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{1, 0, 1, 0, 1}, 0.0, 0.0, -13.0, 0.0, 0.0, -11.0}, + {[5]int8{1, 0, 1, 0, 0}, 3.0, 0.0, 6.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 2, 0, 2, 0}, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, -2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{0, -1, 4, -2, 1}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, -2, 3}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 4, -2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{4, 0, 2, -4, 2}, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{2, 2, 2, -2, 2}, 8.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{2, 0, 4, -4, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, -2, 0, 4, 0}, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, -3, 2, 2, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 4, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, -2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, -2, 1}, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-3, 0, -2, 2, 0}, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, -4, 1}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-2, 1, 0, -2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-4, 0, 0, 0, 1}, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, -4, 1}, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-3, 0, 0, -2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 3, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-1, 1, 0, 4, 1}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, 0, 1}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 3, 0}, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 2, 3}, 6.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 2, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 2, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 2, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{3, 0, 0, 0, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, 1, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, -1, 2}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 0, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 3, 0, 3}, 0.0, 0.0, -26.0, 0.0, 0.0, -11.0}, + {[5]int8{0, 0, 3, 0, 2}, 0.0, 0.0, -10.0, 0.0, 0.0, -5.0}, + {[5]int8{-1, 2, 2, 2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, 0, 0}, -13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 2, 2, 0, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{3, 1, 2, -2, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{1, 1, 4, -2, 2}, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-2, -1, 0, 6, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -2, 0, 4, 0}, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, 0, 6, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, -2, 2, 4, 2}, -6.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, -3, 2, 2, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 4, 2}, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 3, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 4, 0}, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, -1, 0, 2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{1, 0, 0, 3, 0}, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 4, 1}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 1, 0, 4, 0}, -11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 1, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 2, 3}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 2, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 2, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-2, 0, 4, 2, 1}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, 2, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 1, 0, 2, 0}, -12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, 0, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 1, 0}, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 2, 0}, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 0, 3}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, 0, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 0, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{1, 0, 3, 0, 3}, 0.0, 0.0, -5.0, 0.0, 0.0, -2.0}, + {[5]int8{1, 1, 2, 1, 1}, -7.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {[5]int8{0, 2, 2, 2, 2}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 0, 0}, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 0, 4, -2, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{4, 1, 2, -2, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{-1, -1, 0, 6, 0}, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-3, -1, 2, 6, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{-1, 0, 0, 6, 1}, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-3, 0, 2, 6, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 4, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{1, -1, 0, 4, 0}, 12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 5, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{1, -2, 2, 2, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{3, -1, 0, 2, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 2, 0}, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 3, 1}, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{-1, 1, 2, 4, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{0, 1, 2, 3, 2}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-1, 0, 4, 2, 1}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 1, 1}, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{5, 0, 0, 0, 0}, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 1, 2}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{1, 0, 4, 0, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{3, 1, 2, 0, 1}, 7.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {[5]int8{3, 0, 4, -2, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-2, -1, 2, 6, 2}, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 0, 6, 0}, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, -2, 2, 4, 2}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{-2, 0, 2, 6, 1}, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 4, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{2, 0, 0, 4, 0}, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, -2, 2, 2, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{0, 0, 2, 4, 0}, 7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{1, 0, 2, 3, 2}, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {[5]int8{4, 0, 0, 2, 0}, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 2, 0}, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {[5]int8{0, 0, 4, 2, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{4, -1, 2, 0, 2}, -6.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{3, 0, 2, 1, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{2, 1, 2, 2, 1}, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{4, 1, 2, 0, 2}, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{-1, -1, 2, 6, 2}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{-1, 0, 2, 6, 1}, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{1, -1, 2, 4, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {[5]int8{1, 1, 2, 4, 2}, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {[5]int8{3, 1, 2, 2, 2}, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {[5]int8{5, 0, 2, 0, 1}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{2, -1, 2, 4, 2}, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {[5]int8{2, 0, 2, 4, 1}, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, +} + +var planetaryTerms = [...]planetaryTerm{ + {[14]int8{0, 0, 0, 0, 0, 0, 0, 8, -16, 4, 5, 0, 0, 0}, 1440.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -8, 16, -4, -5, 0, 0, 2}, 56.0, -117.0, -42.0, -40.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 8, -16, 4, 5, 0, 0, 2}, 125.0, -43.0, 0.0, -54.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 2, 2}, 0.0, 5.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 8, -1, -5, 0, 0, 2}, 3.0, -7.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, 3, -8, 3, 0, 0, 0, 0}, -114.0, 0.0, 0.0, 61.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 10, -3, 0, 0, 0, 0, 0, 0}, -219.0, 89.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 6, -3, 0, 2}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, -462.0, 1604.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -5, 8, -3, 0, 0, 0, 0}, 99.0, 0.0, 0.0, -53.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 8, -3, 0, 0, 0, 1}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -8, 1, 5, 0, 0, 2}, 0.0, 6.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 6, 4, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 2}, -12.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 1}, 14.0, -218.0, 117.0, 8.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 2, -5, 0, 0, 0}, 31.0, -481.0, -257.0, -17.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0}, -491.0, 128.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 5, 0, 0, 0}, -3084.0, 5123.0, 2735.0, 1647.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 1}, -1444.0, 2409.0, -1286.0, -771.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 2}, 11.0, -24.0, -11.0, -9.0}, + {[14]int8{2, 0, -1, -1, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0}, 26.0, -9.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 19, -21, 3, 0, 0, 0, 0, 0}, 103.0, -60.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 2, -4, 0, -3, 0, 0, 0, 0}, 0.0, -13.0, -7.0, 0.0}, + {[14]int8{1, 0, 0, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, -26.0, -29.0, -16.0, 14.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -4, 10, 0, 0, 0}, 9.0, -27.0, -14.0, -5.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 2, 0, 0, -5, 0, 0, 0}, 12.0, 0.0, 0.0, -6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -7, 4, 0, 0, 0, 0, 0}, -7.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 1, -1, 0, 0, 0}, 0.0, 24.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 284.0, 0.0, 0.0, -151.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 18, -16, 0, 0, 0, 0, 0, 0}, 226.0, 101.0, 0.0, 0.0}, + {[14]int8{-2, 0, 1, 1, 2, 0, 0, 1, 0, -2, 0, 0, 0, 0}, 0.0, -8.0, -2.0, 0.0}, + {[14]int8{-1, 0, 1, -1, 1, 0, 18, -17, 0, 0, 0, 0, 0, 0}, 0.0, -6.0, -3.0, 0.0}, + {[14]int8{-1, 0, 0, 1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0}, 5.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 2}, -41.0, 175.0, 76.0, 17.0}, + {[14]int8{0, 0, 2, -2, 2, 0, -8, 11, 0, 0, 0, 0, 0, 0}, 0.0, 15.0, 6.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 1}, 425.0, 212.0, -133.0, 269.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -8, 12, 0, 0, 0, 0, 0, 0}, 1200.0, 598.0, 319.0, -641.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 8, -13, 0, 0, 0, 0, 0, 0}, 235.0, 334.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 8, -14, 0, 0, 0, 0, 0, 0}, 11.0, -12.0, -7.0, -6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 8, -13, 0, 0, 0, 0, 0, 1}, 5.0, -6.0, 3.0, 3.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 2, 0, -4, 5, 0, 0, 0}, -5.0, 0.0, 0.0, 3.0}, + {[14]int8{-2, 0, 0, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0}, 6.0, 0.0, 0.0, -3.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -3, 1, 0, 0, 0}, 15.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 3, -5, 0, 2, 0, 0, 0, 0}, 13.0, 0.0, 0.0, -7.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -4, 3, 0, 0, 0}, -6.0, -9.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, 266.0, -78.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -1, 2, 0, 0, 0, 0, 0}, -460.0, -435.0, -232.0, 246.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -2, 2, 0, 0, 0, 0, 0}, 0.0, 15.0, 7.0, 0.0}, + {[14]int8{-1, 0, 1, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{-1, 0, 0, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, 0.0, 131.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -2, -2, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{-2, 0, 2, 0, 2, 0, 0, -5, 9, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, -1, 0, 0}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 2, 0}, -17.0, -19.0, -10.0, 9.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1}, -9.0, -11.0, 6.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2}, -6.0, 0.0, 0.0, 3.0}, + {[14]int8{-1, 0, 0, 1, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0}, -16.0, 8.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 2, 0, 0, 0}, 11.0, 24.0, 11.0, -5.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -9, 17, 0, 0, 0, 0, 0}, -3.0, -4.0, -2.0, 1.0}, + {[14]int8{0, 0, 0, 0, 2, 0, -3, 5, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 2, 0, 0, 0}, 0.0, -8.0, -4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 17, -16, 0, -2, 0, 0, 0, 0}, 0.0, 5.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 1, -3, 0, 0, 0}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 5, -6, 0, 0, 0, 0, 0}, -6.0, 4.0, 2.0, 3.0}, + {[14]int8{0, 0, -2, 2, 0, 0, 0, 9, -13, 0, 0, 0, 0, 0}, -3.0, -5.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 1, 0, 0, 0}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 4.0, 24.0, 13.0, -2.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0}, -42.0, 20.0, 0.0, 0.0}, + {[14]int8{0, 0, -2, 2, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0}, -10.0, 233.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 1, 0, 5, -7, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0}, 78.0, -18.0, 0.0, 0.0}, + {[14]int8{2, 0, 1, -3, 1, 0, -6, 7, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, -1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0}, 0.0, -4.0, -2.0, 1.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 2, 0, 0}, 0.0, -8.0, -4.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1}, 0.0, -5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2}, -7.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 2}, -14.0, 8.0, 3.0, 6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 1}, 0.0, 8.0, -4.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -9, 15, 0, 0, 0, 0, 0}, 0.0, 19.0, 10.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 8, -15, 0, 0, 0, 0, 0}, 45.0, -22.0, 0.0, 0.0}, + {[14]int8{1, 0, -1, -1, 0, 0, 0, 8, -15, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{2, 0, 0, -2, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -5, 5, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{2, 0, 0, -2, 1, 0, 0, -6, 8, 0, 0, 0, 0, 0}, 3.0, 5.0, 3.0, -2.0}, + {[14]int8{2, 0, 0, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, 89.0, -16.0, -9.0, -48.0}, + {[14]int8{-2, 0, 1, 1, 0, 0, 0, 1, 0, -3, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{-2, 0, 1, 1, 1, 0, 0, 1, 0, -3, 0, 0, 0, 0}, -3.0, 7.0, 4.0, 2.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, -349.0, -62.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0}, -15.0, 22.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -1, -5, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, -53.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 1, 1, 1, 0, -20, 20, 0, 0, 0, 0, 0, 0}, 5.0, 0.0, 0.0, -3.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 20, -21, 0, 0, 0, 0, 0, 0}, 0.0, -8.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 8, -15, 0, 0, 0, 0, 0}, 15.0, -7.0, -4.0, -8.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -10, 15, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0}, -21.0, -78.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 20.0, -70.0, -37.0, -11.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 0.0, 6.0, 3.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 4, 0, 0, 0}, 5.0, 3.0, 2.0, -2.0}, + {[14]int8{2, 0, 0, -2, 1, 0, -6, 8, 0, 0, 0, 0, 0, 0}, -17.0, -4.0, -2.0, 9.0}, + {[14]int8{0, 0, -2, 2, 1, 0, 5, -6, 0, 0, 0, 0, 0, 0}, 0.0, 6.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1}, 32.0, 15.0, -8.0, 17.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0}, 174.0, 84.0, 45.0, -93.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 11.0, 56.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0}, -66.0, -12.0, -6.0, 35.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, 47.0, 8.0, 4.0, -25.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2}, 0.0, 8.0, 4.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -9, 13, 0, 0, 0, 0, 0}, 10.0, -22.0, -12.0, -5.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 7, -13, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0}, -24.0, 12.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 9, -17, 0, 0, 0, 0, 0}, 5.0, -6.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -9, 17, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{1, 0, 0, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0}, 4.0, 3.0, 1.0, -2.0}, + {[14]int8{1, 0, 0, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, 0.0, 29.0, 15.0, 0.0}, + {[14]int8{0, 0, 0, 0, 2, 0, 0, -1, 2, 0, 0, 0, 0, 0}, -5.0, -4.0, -2.0, 2.0}, + {[14]int8{0, 0, -1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0}, 8.0, -3.0, -1.0, -5.0}, + {[14]int8{0, 0, -2, 2, 0, 1, 0, -2, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -5, 0, 2, 0, 0, 0, 0}, 10.0, 0.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 2, 0, -3, 1, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 3, -3, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 8, -13, 0, 0, 0, 0, 0, 0}, 46.0, 66.0, 35.0, -25.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 8, -12, 0, 0, 0, 0, 0, 0}, -14.0, 7.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -8, 11, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{-1, 0, 0, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 1, 0, 18, -16, 0, 0, 0, 0, 0, 0}, -68.0, -34.0, -18.0, 36.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 1, 0, 0, 0}, 0.0, 14.0, 7.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 3, -7, 4, 0, 0, 0, 0, 0}, 10.0, -6.0, -3.0, -5.0}, + {[14]int8{-2, 0, 1, 1, 1, 0, 0, -3, 7, 0, 0, 0, 0, 0}, -5.0, -4.0, -2.0, 3.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, -2, 5, 0, 0, 0}, -3.0, 5.0, 2.0, 1.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, -2, 5, 0, 0, 0}, 76.0, 17.0, 9.0, -41.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0}, 84.0, 298.0, 159.0, -45.0}, + {[14]int8{1, 0, 0, 0, 1, 0, -10, 3, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{-1, 0, 0, 0, 1, 0, 10, -3, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0}, -82.0, 292.0, 156.0, 44.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, 2, -5, 0, 0, 0}, -73.0, 17.0, 9.0, 39.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 2, -5, 0, 0, 0}, -9.0, -16.0, 0.0, 0.0}, + {[14]int8{2, 0, -1, -1, 1, 0, 0, 3, -7, 0, 0, 0, 0, 0}, 3.0, 0.0, -1.0, -2.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, 0, -5, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -3, 7, -4, 0, 0, 0, 0, 0}, -9.0, -5.0, -3.0, 5.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -439.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, 0, 1, 0, -18, 16, 0, 0, 0, 0, 0, 0}, 57.0, -28.0, -15.0, -30.0}, + {[14]int8{-2, 0, 1, 1, 1, 0, 0, 1, 0, -2, 0, 0, 0, 0}, 0.0, -6.0, -3.0, 0.0}, + {[14]int8{0, 0, 1, -1, 2, 0, -8, 12, 0, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -8, 13, 0, 0, 0, 0, 0, 0}, -40.0, 57.0, 30.0, 21.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 1}, 23.0, 7.0, 3.0, -13.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 0, 0, 0, 0}, 273.0, 80.0, 43.0, -146.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0}, -449.0, 430.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0}, -8.0, -47.0, -25.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 1}, 6.0, 47.0, 25.0, -3.0}, + {[14]int8{-1, 0, 0, 1, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0}, 0.0, 23.0, 13.0, 0.0}, + {[14]int8{-1, 0, 0, 1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -2, 0, 0, 0}, 3.0, -4.0, -2.0, -2.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 2, 0, 0, 0}, -48.0, -110.0, -59.0, 26.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1}, 51.0, 114.0, 61.0, -27.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2}, -133.0, 0.0, 0.0, 57.0}, + {[14]int8{0, 0, 1, -1, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0}, 0.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0, 0, 0, 0}, -21.0, -6.0, -3.0, 11.0}, + {[14]int8{0, 0, 1, -1, 2, 0, -3, 4, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -2, 4, 0, 0, 0, 0, 0}, -11.0, -21.0, -11.0, 6.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0}, -18.0, -436.0, -233.0, 9.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0}, 35.0, -7.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 5, -8, 0, 0, 0, 0, 0, 0}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 6, -8, 0, 0, 0, 0, 0, 0}, 11.0, -3.0, -1.0, -6.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -8, 15, 0, 0, 0, 0, 0}, -5.0, -3.0, -1.0, 3.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 2, 0, -3, 0, 0, 0, 0}, -53.0, -9.0, -5.0, 28.0}, + {[14]int8{-2, 0, 0, 2, 1, 0, 0, 6, -8, 0, 0, 0, 0, 0}, 0.0, 3.0, 2.0, 1.0}, + {[14]int8{1, 0, 0, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0}, -50.0, 194.0, 103.0, 27.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 1}, -13.0, 52.0, 28.0, 7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, -91.0, 248.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, 6.0, 49.0, 26.0, -3.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, -6.0, -47.0, -25.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2}, 52.0, 23.0, 10.0, -23.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, 0, -1, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -7, 13, 0, 0, 0, 0, 2}, -4.0, 8.0, 3.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -13, 0, 0, 0, 0, 0}, 10.0, 0.0, 0.0, 0.0}, + {[14]int8{2, 0, 0, -2, 1, 0, 0, -5, 6, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -8, 11, 0, 0, 0, 0, 0}, 0.0, 8.0, 4.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, -1, 0, 2, 0, 0, 0, 0, 0, 0}, 0.0, 8.0, 4.0, 1.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 3, 0, 0, 0}, -8.0, 4.0, 2.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1}, 8.0, -4.0, -2.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2}, 0.0, 15.0, 7.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, -138.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, 0.0, -7.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, -7.0, -3.0, 0.0}, + {[14]int8{2, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, 54.0, 0.0, 0.0, -29.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, 2, 0, 0, 0, 0}, 0.0, 10.0, 4.0, 0.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0}, -7.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 1, -2, 0, 0, 0, 0, 0}, -37.0, 35.0, 19.0, 20.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, 0.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, 0, -2, 0, 0, 0}, -4.0, 9.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 2, 0, 0, 0}, 8.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 3, -6, 0, 0, 0, 0, 0, 0}, -9.0, -14.0, -8.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 1}, -3.0, -9.0, -5.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, -145.0, 47.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, -10.0, 40.0, 21.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 1}, 11.0, -49.0, -26.0, -7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2}, -2150.0, 0.0, 0.0, 932.0}, + {[14]int8{0, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0}, -12.0, 0.0, 0.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2}, 85.0, 0.0, 0.0, -37.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 1}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, 1, -4, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0}, -86.0, 153.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1}, -6.0, 9.0, 5.0, 3.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0}, 9.0, -13.0, -7.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1}, -8.0, 12.0, 6.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 2}, -51.0, 0.0, 0.0, 22.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2}, -11.0, -268.0, -116.0, 5.0}, + {[14]int8{0, 0, 2, -2, 2, 0, -5, 6, 0, 0, 0, 0, 0, 0}, 0.0, 12.0, 5.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2}, 0.0, 7.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1}, 31.0, 6.0, 3.0, -17.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -5, 7, 0, 0, 0, 0, 0, 0}, 140.0, 27.0, 14.0, -75.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1}, 57.0, 11.0, 6.0, -30.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, 0}, -14.0, -39.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, -1, 2, 0, 0, -1, 0, -1, 0, 0, 0, 0}, 0.0, -6.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0}, 4.0, 15.0, 8.0, -2.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 0.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -2, 0, 1, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 11, 0, 0, 0, 0, 2}, 0.0, 11.0, 5.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -11, 0, 0, 0, 0, 0}, 9.0, 6.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, -1, 0, 4, 0, 0, 0, 0, 0, 2}, -4.0, 10.0, 4.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0, 0, 0}, 5.0, 3.0, 0.0, 0.0}, + {[14]int8{2, 0, 0, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0}, 16.0, 0.0, 0.0, -9.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -7, 9, 0, 0, 0, 0, 0}, 0.0, 3.0, 2.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 2}, 7.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, -25.0, 22.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1}, 42.0, 223.0, 119.0, -22.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, -27.0, -143.0, -77.0, 14.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1}, 9.0, 49.0, 26.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2}, -1166.0, 0.0, 0.0, 505.0}, + {[14]int8{0, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2}, -6.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0}, -8.0, 0.0, 1.0, 4.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0}, 117.0, 0.0, 0.0, -63.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 2, -4, 0, 0, 0, 0, 0}, -4.0, 8.0, 4.0, 2.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -4, 4, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 1, -1, 2, 0, -5, 7, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0}, 0.0, 31.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1}, -5.0, 0.0, 1.0, 3.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -4, 6, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 2}, -24.0, -13.0, -6.0, 10.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 2, -3, 0, 0, 0, 0, 0, 0}, 0.0, -32.0, -17.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 2}, 8.0, 12.0, 5.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, 0}, 7.0, 13.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0}, -3.0, 16.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, 50.0, 0.0, 0.0, -27.0}, + {[14]int8{-2, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, -5.0, -3.0, 0.0}, + {[14]int8{0, 0, -2, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, 13.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 1}, 0.0, 5.0, 3.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 2}, 24.0, 5.0, 2.0, -11.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 2}, 5.0, -11.0, -5.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 1}, 30.0, -3.0, -2.0, -16.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, 18.0, 0.0, 0.0, -9.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0}, 8.0, 614.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 1}, 3.0, -3.0, -1.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}, 6.0, 17.0, 9.0, -3.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, -1, 0, 3, 0, 0, 0, 0}, -3.0, -9.0, -5.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}, 0.0, 6.0, 3.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2}, -127.0, 21.0, 9.0, 55.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, 0}, 3.0, 5.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 2}, -6.0, -10.0, -4.0, 3.0}, + {[14]int8{0, 0, -2, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 5.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 2}, 16.0, 9.0, 4.0, -7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0}, 0.0, 22.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0}, 0.0, 19.0, 10.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, 7.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 2}, 0.0, -5.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2}, -9.0, 3.0, 1.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 2}, 17.0, 0.0, 0.0, -7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 1}, 0.0, -3.0, -2.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0}, -20.0, 34.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 1}, -10.0, 0.0, 1.0, 5.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 1, -3, 0, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0}, 22.0, -87.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 1}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 2}, -3.0, -6.0, -2.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 2}, -16.0, -3.0, -1.0, 7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 1}, 0.0, -3.0, -2.0, 0.0}, + {[14]int8{0, 0, -2, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0}, -68.0, 39.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -4, 4, 0, 0, 0, 0, 0, 0}, 27.0, 0.0, 0.0, -14.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0}, -25.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 1}, -12.0, -3.0, -2.0, 6.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -4, 6, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 2}, 3.0, 66.0, 29.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 2}, 490.0, 0.0, 0.0, -213.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1}, -22.0, 93.0, 49.0, 12.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -4, 5, 0, 0, 0, 0, 0, 0}, -7.0, 28.0, 15.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1}, -3.0, 13.0, 7.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, 0}, -46.0, 14.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, 2.0, 1.0, 0.0, 0.0}, + {[14]int8{0, 0, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0}, -28.0, 0.0, 0.0, 15.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 5, 0, 0, 0, 2}, 5.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 2}, -11.0, 0.0, 0.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -7, 12, 0, 0, 0, 0, 2}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1}, 25.0, 106.0, 57.0, -13.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0}, 5.0, 21.0, 11.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, 1485.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 1}, -7.0, -32.0, -17.0, 4.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 1, -2, 0, 0, 0, 0, 0, 0}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 2}, -6.0, -3.0, -2.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 4, 0, 0, 0, 2}, 30.0, -6.0, -2.0, -13.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0}, -4.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0}, -19.0, 0.0, 0.0, 10.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 2}, 0.0, 4.0, 2.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -3, 0, 3, 0, 0, 0, 0}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 2}, 5.0, 3.0, 1.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0}, 0.0, 11.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 2}, 118.0, 0.0, 0.0, -52.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 1}, 0.0, -5.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 0, 0, 0, 0}, -28.0, 36.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0}, 5.0, -5.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 1}, 14.0, -59.0, -31.0, -8.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0}, 0.0, 9.0, 5.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 2}, -458.0, 0.0, 0.0, 198.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 2}, 0.0, -45.0, -20.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 1}, 9.0, 0.0, 0.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 1, 0, -2, 0, 0, 0, 0}, 0.0, -4.0, -2.0, -1.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, 11.0, 0.0, 0.0, -6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 2}, 6.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0}, -16.0, 23.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 2, 0, 0, 0, 2}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0}, -166.0, 269.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 15.0, 0.0, 0.0, -8.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 0, 2}, 10.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0}, -78.0, 45.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 2}, 0.0, -5.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 1}, 7.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, -5.0, 328.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0}, 5.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -3, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -5, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, -1223.0, -26.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 1}, 0.0, 7.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -2, 0, 0, 0}, -6.0, 20.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, -368.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0}, -75.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 11.0, 0.0, 0.0, -6.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 14, 0, 0, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 2, -5, 0, 0, 0}, -13.0, -30.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 0}, 21.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 8.0, -27.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -8, 3, 0, 0, 0, 0}, -19.0, -11.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 8, -3, 0, 0, 0, 2}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 5, 0, 0, 2}, 0.0, 5.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 2}, -6.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 0}, -8.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -2, 0, 0, 0}, -1.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2}, -14.0, 0.0, 0.0, 6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, 6.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2}, -74.0, 0.0, 0.0, 32.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, -5, 5, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0}, 8.0, 11.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2}, -262.0, 0.0, 0.0, 114.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 1}, -7.0, 0.0, 0.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 2}, 0.0, -27.0, -12.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 2}, -19.0, -8.0, -4.0, 8.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 2}, 202.0, 0.0, 0.0, -87.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 1}, -8.0, 35.0, 19.0, 5.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0}, 16.0, -5.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 5.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 0, 0, 2}, 1.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2}, -35.0, -48.0, -21.0, 15.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 2}, -3.0, -5.0, -2.0, 1.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 2, -2, 0, 0, 0, 0, 0, 0}, 6.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0}, 0.0, -5.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 1}, 12.0, 55.0, 29.0, -6.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -2, 1, 0, 0, 0, 0, 0, 0}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, -598.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 1}, -3.0, -13.0, -7.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 2}, -5.0, -7.0, -3.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0}, 5.0, -7.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0}, 16.0, -6.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0}, 8.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 1}, 8.0, -31.0, -16.0, -4.0}, + {[14]int8{0, 0, 1, -1, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 2}, 113.0, 0.0, 0.0, -49.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 2}, 0.0, -24.0, -10.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 1}, 4.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0}, 27.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 2}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 1}, 5.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2}, -13.0, 0.0, 0.0, 6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 0, 5, 0, 0, 0, 2}, 5.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2}, -18.0, -10.0, -4.0, 8.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, -4.0, -28.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2}, -5.0, 6.0, 3.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 13, 0, 0, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 5, 0, 0, 0, 0, 2}, -5.0, -9.0, -4.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 0, 4, 0, 0, 0, 2}, 17.0, 0.0, 0.0, -7.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -4, 0, 0, 0, 0}, 11.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 7, 0, 0, 0, 0, 2}, 0.0, -6.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, 83.0, 15.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 1}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 2}, 0.0, -114.0, -49.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 2}, 117.0, 0.0, 0.0, -51.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 1}, -5.0, 19.0, 10.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 9, 0, 0, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 2}, 0.0, -6.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 393.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 1}, -4.0, 21.0, 11.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 2}, -6.0, 0.0, -1.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 0, 2}, -3.0, 8.0, 4.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0}, 8.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 2}, 18.0, -29.0, -13.0, -8.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 1}, 8.0, 34.0, 18.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, 89.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 1}, 3.0, 12.0, 6.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 2}, 54.0, -15.0, -7.0, -24.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -3, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 13, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 0}, 0.0, 35.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 2}, -154.0, -30.0, -13.0, 67.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0}, 15.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 1}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0}, 0.0, 9.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 2}, 80.0, -71.0, -31.0, -35.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -1, 0, 0, 2}, 0.0, -20.0, -9.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 15, 0, 0, 0, 0, 2}, 11.0, 5.0, 2.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 0, 2}, 61.0, -96.0, -42.0, -27.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 9, -4, 0, 0, 0, 0, 2}, 14.0, 9.0, 4.0, -6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 2, -5, 0, 0, 2}, -11.0, -6.0, -3.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 8, -1, -5, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -8, 3, 0, 0, 0, 2}, 123.0, -415.0, -180.0, -53.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, 0.0, 0.0, 0.0, -35.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1}, 7.0, -32.0, -17.0, -4.0}, + {[14]int8{0, 0, 1, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, -9.0, -5.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1}, 0.0, -4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2}, -89.0, 0.0, 0.0, 38.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -6, 16, -4, -5, 0, 0, 2}, 0.0, -86.0, -19.0, -6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 8, -3, 0, 0, 0, 2}, 0.0, 0.0, -19.0, 6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -2, 8, -3, 0, 0, 0, 2}, -123.0, -416.0, -180.0, 53.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -8, 1, 5, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 5, 0, 0, 2}, 12.0, -6.0, -3.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -5, 4, 0, 0, 0, 0, 2}, -13.0, 9.0, 4.0, 6.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2}, 0.0, -15.0, -7.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2}, -62.0, -97.0, -42.0, 27.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 2}, -11.0, 5.0, 2.0, 5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 2}, 0.0, -19.0, -8.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -3, 0, 2, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 2}, -85.0, -70.0, -31.0, 37.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 2}, 163.0, -12.0, -5.0, -72.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 0, 2}, -63.0, -16.0, -7.0, 28.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2}, -21.0, -32.0, -14.0, 9.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 2}, 0.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0}, 0.0, 8.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 2}, 3.0, 10.0, 4.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 6, 0, 0, 0, 0, 2}, 0.0, -7.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 2}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 0}, 6.0, 19.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 2}, 5.0, -173.0, -75.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 2}, 0.0, -7.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 2}, 7.0, -12.0, -5.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 1}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 2}, 3.0, -4.0, -2.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 2}, 74.0, 0.0, 0.0, -32.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 1}, -3.0, 12.0, 6.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 2}, 26.0, -14.0, -6.0, -11.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 2}, 19.0, 0.0, 0.0, -8.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -4, 4, 0, 0, 0, 0, 0, 1}, 6.0, 24.0, 13.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, 83.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 1}, 0.0, -10.0, -5.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 2}, 11.0, -3.0, -1.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 2}, 3.0, 0.0, 1.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -3, 0, 5, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1}, 5.0, -23.0, -12.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2}, -339.0, 0.0, 0.0, 147.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 12, 0, 0, 0, 0, 0, 2}, 0.0, -10.0, -5.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -4, 0, 0, 0, 0}, 5.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 2}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 0}, 18.0, -3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 2}, 9.0, -11.0, -5.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 0, 2}, -8.0, 0.0, 0.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 7, 0, 0, 0, 0, 0, 1}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 0, 0}, 0.0, 9.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 2}, 6.0, -9.0, -4.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 0}, -4.0, -12.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 2}, 67.0, -91.0, -39.0, -29.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 2}, 30.0, -18.0, -8.0, -13.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 0}, 0.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 2}, 0.0, -114.0, -50.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 0, 0, 2}, 0.0, 0.0, 0.0, 23.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 0, 0, 2}, 517.0, 16.0, 7.0, -224.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -2, 0, 0, 2}, 0.0, -7.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 2}, 143.0, -3.0, -1.0, -62.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1, 0, 0, 2}, 29.0, 0.0, 0.0, -13.0}, + {[14]int8{0, 0, 2, -2, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 16, 0, 0, 0, 0, 0, 2}, -6.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, 2, -5, 0, 0, 2}, 5.0, 12.0, 5.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -8, 3, 0, 0, 0, 2}, -25.0, 0.0, 0.0, 11.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -5, 16, -4, -5, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, -1, 8, -3, 0, 0, 0, 2}, -22.0, 12.0, 5.0, 10.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2}, 50.0, 0.0, 0.0, -22.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 1}, 0.0, 7.0, 4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2}, -4.0, 4.0, 2.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2}, -5.0, -11.0, -5.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -3, 8, 0, 0, 0, 0, 0, 2}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -5, 5, 0, 0, 0, 0, 0, 1}, 4.0, 17.0, 9.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 0}, 59.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 1}, 0.0, -4.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 2}, -8.0, 0.0, 0.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1}, 4.0, -15.0, -8.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2}, 370.0, -8.0, 0.0, -160.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2}, 0.0, 0.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -5, 0, 0, 0, 0, 2}, -6.0, 3.0, 1.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 0, 0}, 0.0, 6.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 2}, -10.0, 0.0, 0.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 0, 2}, 0.0, 9.0, 4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 2}, 4.0, 17.0, 7.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 2}, 34.0, 0.0, 0.0, -15.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 1}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, 0, 0, 2}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, -3, 0, 0, 0, 2}, -37.0, -7.0, -3.0, 16.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 0, 0, 0, 1}, 3.0, 13.0, 7.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0}, 40.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 1}, 0.0, -3.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, -2, 0, 0, 0, 2}, -184.0, -3.0, -1.0, 80.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 1}, 0.0, -10.0, -6.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 2}, 31.0, -6.0, 0.0, -13.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, -1, 0, 0, 0, 2}, -3.0, -32.0, -14.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, 0, -2, 0, 0, 2}, -7.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, -2, 0, 0, 0, 0, 2}, 0.0, -8.0, -4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, 3.0, -4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 8, -9, 0, 0, 0, 0, 0, 0}, 0.0, 4.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 0, 2}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 2}, 19.0, -23.0, -10.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1}, 0.0, 0.0, 0.0, -10.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -7, 7, 0, 0, 0, 0, 0, 1}, 0.0, 9.0, 5.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 0, 0}, 28.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 1}, 0.0, -7.0, -4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 2}, 8.0, -4.0, 0.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0}, 0.0, 0.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, 0, -4, 0, 0, 0, 2}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, 0, -3, 0, 0, 0, 2}, -9.0, 0.0, 1.0, 4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 5, 0, -2, 0, 0, 0, 2}, 3.0, 12.0, 5.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2}, 17.0, -3.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -8, 8, 0, 0, 0, 0, 0, 1}, 0.0, 7.0, 4.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 8, -8, 0, 0, 0, 0, 0, 0}, 19.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 1}, 0.0, -5.0, -3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 2}, 14.0, -3.0, 0.0, -1.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, 0.0, 0.0, -1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, 0.0, 0.0, 0.0, -5.0}, + {[14]int8{0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 9, -9, 0, 0, 0, 0, 0, 0}, 13.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 0, 1}, 0.0, -3.0, -2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, 2.0, 9.0, 4.0, 3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, 0.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, 8.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1}, 0.0, 4.0, 2.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, 6.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, 6.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, 5.0, 0.0, 0.0, -2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, 6.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 7.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 6.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 5.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, 13.0, 0.0, 0.0, 0.0}, + {[14]int8{-2, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, 21.0, 11.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, -5.0, 0.0, 0.0}, + {[14]int8{-1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0}, 0.0, -5.0, -2.0, 0.0}, + {[14]int8{1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0}, 0.0, 5.0, 3.0, 0.0}, + {[14]int8{-1, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, -5.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 2.0}, + {[14]int8{0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 20.0, 10.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -34.0, 0.0, 0.0, 0.0}, + {[14]int8{-1, 0, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, -19.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -2.0}, + {[14]int8{1, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{1, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0}, -6.0, 0.0, 0.0, 3.0}, + {[14]int8{1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, -4.0, 0.0, 0.0, 0.0}, + {[14]int8{1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 0, -2, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, 4.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 2, 0, 2, 0, -2, 2, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 6.0, 0.0, 0.0, -3.0}, + {[14]int8{0, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0}, -8.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 2, 0, 2, 0, -2, 3, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 0.0}, + {[14]int8{0, 0, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, -3.0, -2.0, 0.0}, + {[14]int8{1, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 126.0, -63.0, -27.0, -55.0}, + {[14]int8{-1, 0, 2, 0, 2, 0, 10, -3, 0, 0, 0, 0, 0, 0}, -5.0, 0.0, 1.0, 2.0}, + {[14]int8{0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, -3.0, 28.0, 15.0, 2.0}, + {[14]int8{1, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 5.0, 0.0, 1.0, -2.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, 9.0, 4.0, 1.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, 0.0, 9.0, 4.0, -1.0}, + {[14]int8{-1, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, -126.0, -63.0, -27.0, 55.0}, + {[14]int8{2, 0, 2, -2, 2, 0, 0, -2, 0, 3, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{1, 0, 2, 0, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, 21.0, -11.0, -6.0, -11.0}, + {[14]int8{0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, -4.0, 0.0, 0.0}, + {[14]int8{-1, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, -21.0, -11.0, -6.0, 11.0}, + {[14]int8{-2, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 2, -3, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0}, 8.0, 0.0, 0.0, -4.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 0, 1, 0, -1, 0, 0, 0, 0}, -6.0, 0.0, 0.0, 3.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 2, -2, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{-1, 0, 2, 2, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{1, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0}, -3.0, 0.0, 0.0, 1.0}, + {[14]int8{-1, 0, 2, 2, 2, 0, 0, 2, 0, -3, 0, 0, 0, 0}, -5.0, 0.0, 0.0, 2.0}, + {[14]int8{2, 0, 2, 0, 2, 0, 0, 2, 0, -3, 0, 0, 0, 0}, 24.0, -12.0, -5.0, -11.0}, + {[14]int8{1, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{1, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, 0.0, 3.0, 1.0, 0.0}, + {[14]int8{1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 0.0, 3.0, 2.0, 0.0}, + {[14]int8{0, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, -24.0, -12.0, -5.0, 10.0}, + {[14]int8{2, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 4.0, 0.0, -1.0, -2.0}, + {[14]int8{-1, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 13.0, 0.0, 0.0, -6.0}, + {[14]int8{-1, 0, 2, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0}, 7.0, 0.0, 0.0, -3.0}, + {[14]int8{1, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, + {[14]int8{0, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0}, 3.0, 0.0, 0.0, -1.0}, +} + +var fundamentalPolynomials = [5][5]float64{ + {485868.249036, 1717915923.2178, 31.8792, 0.051635, -0.0002447}, + {1287104.79305, 129596581.0481, -0.5532, 0.000136, -1.149e-05}, + {335779.526232, 1739527262.8478, -12.7512, -0.001037, 4.17e-06}, + {1072260.70369, 1602961601.209, -6.3706, 0.006593, -3.169e-05}, + {450160.398036, -6962890.5431, 7.4722, 0.007702, -5.939e-05}, +} + +var planetaryArguments = [14][2]float64{ + {2.35555598, 8328.6914269554}, + {6.24006013, 628.301955}, + {1.627905234, 8433.466158131}, + {5.198466741, 7771.3771468121}, + {2.1824392, -33.757045}, + {4.402608842, 2608.7903141574}, + {3.176146697, 1021.3285546211}, + {1.753470314, 628.3075849991}, + {6.203480913, 334.06124267}, + {0.599546497, 52.9690962641}, + {0.874016757, 21.329910496}, + {5.481293871, 7.4781598567}, + {5.321159, 3.8127774}, + {0.02438175, 5.38691e-06}, +} diff --git a/internal/iau2006/iau2006.go b/internal/iau2006/iau2006.go new file mode 100644 index 0000000..97cb443 --- /dev/null +++ b/internal/iau2006/iau2006.go @@ -0,0 +1,155 @@ +// Package iau2006 IAU 2006 岁差、IAU 2000A 章动与 ICRS 到真赤道的旋转;系数表单独成包,只有调用时才链接。 +package iau2006 + +import "math" + +// Matrix 3×3 旋转矩阵,按行存放,作用于列向量 / 3×3 rotation matrix, row-major, acting on column vectors. +type Matrix [3][3]float64 + +const ( + j2000 = 2451545.0 + arcsecToRad = math.Pi / 648000 + arcsecCircle = 1296000.0 + obliquityJ2K = 84381.406 +) + +type lunisolarTerm struct { + n [5]int8 + lonSin, lonSinT, lonCos float64 + oblCos, oblCosT, oblSin float64 +} + +type planetaryTerm struct { + n [14]int8 + lonSin, lonCos float64 + oblSin, oblCos float64 +} + +func centuries(jdeTT float64) float64 { return (jdeTT - j2000) / 36525 } + +// Nutation IAU 2000A 黄经章动 Δψ 与交角章动 Δε(角秒),jdeTT 为 TT 儒略日 / nutation in longitude and obliquity in arcseconds. +func Nutation(jdeTT float64) (deltaPsi, deltaEpsilon float64) { + t := centuries(jdeTT) + var fa [5]float64 + for i, p := range fundamentalPolynomials { + v := (((p[4]*t+p[3])*t+p[2])*t+p[1])*t + p[0] + fa[i] = math.Mod(v, arcsecCircle) * arcsecToRad + } + var dpsi, deps float64 + for _, term := range lunisolarTerms { + arg := 0.0 + for i, n := range term.n { + arg += float64(n) * fa[i] + } + s, c := math.Sincos(arg) + dpsi += s*(term.lonSin+term.lonSinT*t) + c*term.lonCos + deps += c*(term.oblCos+term.oblCosT*t) + s*term.oblSin + } + var pa [14]float64 + for i, p := range planetaryArguments { + pa[i] = p[0] + p[1]*t + } + pa[13] *= t + for _, term := range planetaryTerms { + arg := 0.0 + for i, n := range term.n { + if n != 0 { + arg += float64(n) * pa[i] + } + } + s, c := math.Sincos(arg) + dpsi += s*term.lonSin + c*term.lonCos + deps += s*term.oblSin + c*term.oblCos + } + // 系数单位为 0.1 微角秒。 + return dpsi * 1e-7, deps * 1e-7 +} + +// MeanObliquity IAU 2006 平黄赤交角(角秒)/ IAU 2006 mean obliquity in arcseconds. +func MeanObliquity(jdeTT float64) float64 { + t := centuries(jdeTT) + return ((((-0.0000000434*t-0.000000576)*t+0.00200340)*t-0.0001831)*t-46.836769)*t + obliquityJ2K +} + +// Precession J2000 平赤道到历元平赤道的 IAU 2006 岁差矩阵 / IAU 2006 precession matrix from J2000 to the mean equator of date. +func Precession(jdeTT float64) Matrix { + t := centuries(jdeTT) + psi := ((((-0.0000000951*t+0.000132851)*t-0.00114045)*t-1.0790069)*t + 5038.481507) * t + omega := ((((0.0000003337*t-0.000000467)*t-0.00772503)*t+0.0512623)*t-0.025754)*t + obliquityJ2K + chi := ((((-0.0000000560*t+0.000170663)*t-0.00121197)*t-2.3814292)*t + 10.556403) * t + // R3(χ) R1(−ω) R3(−ψ) R1(ε0) + return multiply(rotZ(chi*arcsecToRad), multiply(rotX(-omega*arcsecToRad), multiply(rotZ(-psi*arcsecToRad), rotX(obliquityJ2K*arcsecToRad)))) +} + +// NutationMatrix 历元平赤道到真赤道真春分点的章动矩阵 / nutation matrix from mean to true equator of date. +func NutationMatrix(jdeTT float64) Matrix { + dpsi, deps := Nutation(jdeTT) + mean := MeanObliquity(jdeTT) * arcsecToRad + // R1(−εA−Δε) R3(−Δψ) R1(εA) + return multiply(rotX(-(mean + deps*arcsecToRad)), multiply(rotZ(-dpsi*arcsecToRad), rotX(mean))) +} + +// FrameBias ICRS 到 J2000 平赤道的框架偏差矩阵 / frame bias from ICRS to the J2000 mean equator. +func FrameBias() Matrix { + const ( + xi0 = -0.0166170 * arcsecToRad + eta0 = -0.0068192 * arcsecToRad + da0 = -0.01460 * arcsecToRad + ) + return Matrix{ + {1 - 0.5*(da0*da0+xi0*xi0), da0, -xi0}, + {-da0, 1 - 0.5*(da0*da0+eta0*eta0), -eta0}, + {xi0, eta0, 1 - 0.5*(eta0*eta0+xi0*xi0)}, + } +} + +// BiasPrecessionNutation ICRS 到真赤道真春分点的完整旋转 N·P·B / full rotation from ICRS to the true equator and equinox of date. +func BiasPrecessionNutation(jdeTT float64) Matrix { + return multiply(NutationMatrix(jdeTT), multiply(Precession(jdeTT), FrameBias())) +} + +// EquationOfEquinoxes 春分点方程的主项 Δψ·cos εA(角秒),不含补充项 / equation of the equinoxes, principal term only. +func EquationOfEquinoxes(jdeTT float64) float64 { + dpsi, _ := Nutation(jdeTT) + return dpsi * math.Cos(MeanObliquity(jdeTT)*arcsecToRad) +} + +// Apply 把矩阵作用于向量 / apply the matrix to a vector. +func (m Matrix) Apply(v [3]float64) [3]float64 { + var out [3]float64 + for i := range out { + out[i] = m[i][0]*v[0] + m[i][1]*v[1] + m[i][2]*v[2] + } + return out +} + +// Transpose 逆旋转 / inverse rotation. +func (m Matrix) Transpose() Matrix { + var out Matrix + for i := range out { + for j := range out[i] { + out[i][j] = m[j][i] + } + } + return out +} + +func rotX(a float64) Matrix { + s, c := math.Sincos(a) + return Matrix{{1, 0, 0}, {0, c, s}, {0, -s, c}} +} + +func rotZ(a float64) Matrix { + s, c := math.Sincos(a) + return Matrix{{c, s, 0}, {-s, c, 0}, {0, 0, 1}} +} + +func multiply(a, b Matrix) Matrix { + var out Matrix + for i := range out { + for j := range out[i] { + out[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + } + } + return out +} diff --git a/internal/iau2006/iau2006_test.go b/internal/iau2006/iau2006_test.go new file mode 100644 index 0000000..06fb8f9 --- /dev/null +++ b/internal/iau2006/iau2006_test.go @@ -0,0 +1,61 @@ +// 被测契约:章动、平黄赤交角、岁差与完整旋转矩阵和独立参考实现逐点一致。 +package iau2006 + +import ( + "math" + "testing" +) + +type referenceCase struct { + jd float64 + dpsi, deps float64 + obliquity float64 + precession Matrix + fullRotation Matrix +} + +func maxDiff(a, b Matrix) float64 { + worst := 0.0 + for i := range a { + for j := range a[i] { + worst = math.Max(worst, math.Abs(a[i][j]-b[i][j])) + } + } + return worst +} + +func TestMatchesReference(t *testing.T) { + if d := maxDiff(FrameBias(), referenceBias); d > 1e-15 { + t.Errorf("frame bias differs by %.3g", d) + } + for _, c := range referenceCases { + dpsi, deps := Nutation(c.jd) + if math.Abs(dpsi-c.dpsi) > 1e-9 || math.Abs(deps-c.deps) > 1e-9 { + t.Errorf("jd %.1f nutation %.12f %.12f want %.12f %.12f", c.jd, dpsi, deps, c.dpsi, c.deps) + } + if d := math.Abs(MeanObliquity(c.jd) - c.obliquity); d > 1e-9 { + t.Errorf("jd %.1f obliquity differs by %.3g arcsec", c.jd, d) + } + if d := maxDiff(Precession(c.jd), c.precession); d > 1e-14 { + t.Errorf("jd %.1f precession differs by %.3g", c.jd, d) + } + // 1e-12 rad ≈ 0.2 µas + if d := maxDiff(BiasPrecessionNutation(c.jd), c.fullRotation); d > 1e-12 { + t.Errorf("jd %.1f full rotation differs by %.3g", c.jd, d) + } + } +} + +func TestMatrixIsRotation(t *testing.T) { + m := BiasPrecessionNutation(2460676.5) + v := m.Transpose().Apply(m.Apply([3]float64{0.3, -0.5, 0.8})) + if math.Abs(v[0]-0.3)+math.Abs(v[1]+0.5)+math.Abs(v[2]-0.8) > 1e-14 { + t.Errorf("round trip %v", v) + } +} + +func BenchmarkNutation(b *testing.B) { + for i := 0; i < b.N; i++ { + Nutation(2460676.5 + float64(i%100)) + } +} diff --git a/internal/iau2006/reference_data_test.go b/internal/iau2006/reference_data_test.go new file mode 100644 index 0000000..1ebb139 --- /dev/null +++ b/internal/iau2006/reference_data_test.go @@ -0,0 +1,13 @@ +// Code generated by a table generator; DO NOT EDIT. + +package iau2006 + +var referenceCases = []referenceCase{ + {2451545.0, -13.93199633096007, -5.769398076465288, 84381.406, Matrix{{1.0, 0.0, 0.0}, {0.0, 0.9999999999999999, 0.0}, {0.0, 0.0, 0.9999999999999999}}, Matrix{{0.9999999977211051, 6.189983589748964e-05, 2.6948100326663827e-05}, {-6.190059052214238e-05, 0.9999999976920728, 2.800305410167079e-05}, {-2.694636687767602e-05, -2.8004722146821665e-05, 0.9999999992448143}}}, + {2460676.5, 0.19738588530580575, 8.503864482236587, 84369.69650702881, Matrix{{0.9999814220288329, -0.005590636237325088, -0.002429070533135733}, {0.005590636312457951, 0.9999843722478545, -6.759157331337473e-06}, {0.0024290703602134075, -6.8210181682348825e-06, 0.9999970497809775}}, Matrix{{0.9999814159951593, -0.005591584931777626, -0.002429370755271923}, {0.0055914849254004905, 0.999984366376092, -4.7955556641352665e-05}, {0.002429600922973451, 3.4370875472927915e-05, 0.9999970479246415}}}, + {2415020.0, 17.346361008950204, -2.29290423829325, 84428.2405819674, Matrix{{0.9997029457826774, 0.022351425259090313, 0.009717714871532048}, {-0.0223514242285073, 0.9997501697993981, -0.0001087245685850724}, {-0.00971771724194486, -0.00010851249613186287, 0.9999527759832679}}, Matrix{{0.9997049927128934, 0.022274226618768416, 0.00968433650024539}, {-0.022274333560550526, 0.999751891565383, -9.682908399335538e-05}, {-0.009684090527633827, -0.00011891162281681589, 0.9999531010255822}}}, + {2488069.5, 3.2884077167359154, 8.564340841229999, 84334.5716917638, Matrix{{0.9997026927073499, -0.022364607649351796, -0.009713419499048586}, {0.022364608681680095, 0.9997498749687979, -0.00010852828337304388}, {0.009713417122169804, -0.00010874080893430449, 0.9999528177385406}}, Matrix{{0.999702303051242, -0.022379302275393698, -0.009719676111325164}, {0.0223789000494167, 0.9997495497804156, -0.00015015470219950598}, {0.00972060217377973, -6.740565861091262e-05, 0.9999527515587205}}}, + {2378496.5, -8.527561806308212, 7.228130386554576, 84475.0608481095, Matrix{{0.9988125303022144, 0.04467517714381087, 0.019433421172230077}, {-0.04467517520562372, 0.9990014714556796, -0.00043445337347491897}, {-0.019433425627896527, -0.00043425402245517164, 0.9998110588465248}}, Matrix{{0.9988105168729325, 0.04471298454442168, 0.01944994594224395}, {-0.04471230157295813, 0.9989997942954159, -0.00047019758158968824}, {-0.01945151593255477, -0.00040001355905300756, 0.9998107213453347}}}, +} + +var referenceBias = [3][3]float64{{0.9999999999999942, -7.078279744199226e-08, 8.056148938997159e-08}, {7.078279744199226e-08, 0.999999999999997, 3.3060414542221477e-08}, {-8.056148938997159e-08, -3.3060414542221477e-08, 0.9999999999999962}} diff --git a/internal/limbnote/limbnote.go b/internal/limbnote/limbnote.go new file mode 100644 index 0000000..224a9ae --- /dev/null +++ b/internal/limbnote/limbnote.go @@ -0,0 +1,96 @@ +// Package limbnote 生成出图时写入图注的月面口径声明。 +package limbnote + +import "strings" + +const english = "en" + +// Contacts 接触时刻所用月面口径的短标签,保持站心图页脚平衡 / short tag for the contact-time limb convention. +func Contacts(applied bool, language string) string { + if applied { + if language == english { + return "Limb profile." + } + return "月缘剖面。" + } + if language == english { + return "Circular limb." + } + return "圆月面。" +} + +// Limits 界线所用月面口径;可见域与等时线始终按圆月面 / limb convention for limit tracks only. +func Limits(applied bool, language string) string { + if applied { + if language == english { + return "Limits use the lunar limb profile (areas and isolines circular)." + } + return "界线按月缘剖面修正(可见域与等时线仍按圆月面)。" + } + if language == english { + return "Circular lunar limb." + } + return "月面按圆月面计算。" +} + +// SolarMapLayers 日食全球图各层是否真正按月缘剖面修正 / which global solar map layers the lunar limb actually corrected. +type SolarMapLayers struct { + // GlobalContacts 全球偏食始末与食甚点中心食时长 / global P1/P4 and the central duration at greatest eclipse. + GlobalContacts bool + // LocalPanel 食甚点站心面板 / the local panel at greatest eclipse. + LocalPanel bool + // CentralLimits 中心带南北界与带宽 / central-band limits and width. + CentralLimits bool + // PartialBoundary 偏食可见域外边界 / the outer partial visibility boundary. + PartialBoundary bool +} + +// SolarMap 按实际生效的层列出日食全球图的月缘修正,食分线与等时线始终按圆月面 / lists the corrected layers of the global solar map. +func SolarMap(layers SolarMapLayers, language string) string { + type layer struct { + applied bool + zh, english string + } + var names []string + for _, item := range []layer{ + {layers.GlobalContacts, "全球始末", "global contacts"}, + {layers.LocalPanel, "食甚点站心值", "local values at greatest eclipse"}, + {layers.CentralLimits, "中心带界线", "central limits"}, + {layers.PartialBoundary, "可见域边界", "visibility boundary"}, + } { + if !item.applied { + continue + } + if language == english { + names = append(names, item.english) + } else { + names = append(names, item.zh) + } + } + if len(names) == 0 { + return Limits(false, language) + } + if language == english { + return "Lunar limb profile applied to " + strings.Join(names, ", ") + " (isolines circular)." + } + return "按月缘剖面修正" + strings.Join(names, "、") + "(食分线与等时线仍按圆月面)。" +} + +// Join 把月面口径声明接在时标声明之后,二者同占一行 / appends the limb declaration to the time-scale note. +func Join(scaleNote, limbNote, language string) string { + switch { + case scaleNote == "": + return limbNote + case limbNote == "": + return scaleNote + case strings.HasSuffix(scaleNote, "。") || strings.HasSuffix(scaleNote, "."): + if language == english { + return scaleNote + " " + limbNote + } + return scaleNote + limbNote + case language == english: + return scaleNote + ". " + limbNote + default: + return scaleNote + ";" + limbNote + } +} diff --git a/internal/limbnote/limbnote_test.go b/internal/limbnote/limbnote_test.go new file mode 100644 index 0000000..ff50d12 --- /dev/null +++ b/internal/limbnote/limbnote_test.go @@ -0,0 +1,23 @@ +package limbnote + +import "testing" + +func TestSolarMapListsOnlyAppliedLayers(t *testing.T) { + cases := []struct { + layers SolarMapLayers + language string + want string + }{ + {SolarMapLayers{}, "zh", "月面按圆月面计算。"}, + {SolarMapLayers{}, english, "Circular lunar limb."}, + {SolarMapLayers{PartialBoundary: true}, "zh", "按月缘剖面修正可见域边界(食分线与等时线仍按圆月面)。"}, + {SolarMapLayers{GlobalContacts: true, CentralLimits: true}, "zh", "按月缘剖面修正全球始末、中心带界线(食分线与等时线仍按圆月面)。"}, + {SolarMapLayers{LocalPanel: true, PartialBoundary: true}, english, + "Lunar limb profile applied to local values at greatest eclipse, visibility boundary (isolines circular)."}, + } + for _, c := range cases { + if got := SolarMap(c.layers, c.language); got != c.want { + t.Errorf("SolarMap(%+v, %s) = %q, want %q", c.layers, c.language, got, c.want) + } + } +} diff --git a/internal/ltp/basis.go b/internal/ltp/basis.go new file mode 100644 index 0000000..0d810d5 --- /dev/null +++ b/internal/ltp/basis.go @@ -0,0 +1,77 @@ +package ltp + +import ( + "math" + "sync" +) + +// 分段 Chebyshev 缓存的覆盖与精度:最短周期项 157.87 年,200 年段 16 阶误差远小于 1e-12 rad。 + +const ( + basisStart = -10200.0 + basisSpan = 200.0 + basisSegs = 102 + basisDegree = 16 +) + +type basisSeg struct { + once sync.Once + // 6 个分量(x 的 3 个、z 的 3 个)各 basisDegree+1 个系数,c0 已减半。 + c [6][basisDegree + 1]float64 +} + +var basisTable [basisSegs]basisSeg + +// EclipticBasisExact 历元平黄道在 J2000 平赤道下的正交基:x 指向平春分点,z 为黄道极 / exact basis of the mean ecliptic of date. +func EclipticBasisExact(epj float64) (x, y, z Vector3) { + z = EclipticPole(epj) + x, _ = Normalize(Cross(EquatorPole(epj), z)) + return x, Cross(z, x), z +} + +// EclipticBasis 同 EclipticBasisExact,覆盖区间内用分段 Chebyshev 近似(差 < 1e-12 rad),区间外精确计算 / cached form of EclipticBasisExact. +func EclipticBasis(epj float64) (x, y, z Vector3) { + u := (epj - basisStart) / basisSpan + i := int(math.Floor(u)) + if i < 0 || i >= basisSegs { + return EclipticBasisExact(epj) + } + seg := &basisTable[i] + seg.once.Do(func() { seg.fit(basisStart + float64(i)*basisSpan) }) + s := 2*(u-float64(i)) - 1 + var v [6]float64 + for k := range v { + v[k] = clenshaw(&seg.c[k], s) + } + x = Vector3{v[0], v[1], v[2]} + z = Vector3{v[3], v[4], v[5]} + return x, Cross(z, x), z +} + +func (seg *basisSeg) fit(start float64) { + const n = basisDegree + 1 + var f [n][6]float64 + for j := 0; j < n; j++ { + s := math.Cos(math.Pi * (float64(j) + 0.5) / n) + x, _, z := EclipticBasisExact(start + (s+1)/2*basisSpan) + f[j] = [6]float64{x[0], x[1], x[2], z[0], z[1], z[2]} + } + for k := 0; k < 6; k++ { + for m := 0; m < n; m++ { + sum := 0.0 + for j := 0; j < n; j++ { + sum += f[j][k] * math.Cos(math.Pi*float64(m)*(float64(j)+0.5)/n) + } + seg.c[k][m] = 2 * sum / n + } + seg.c[k][0] /= 2 + } +} + +func clenshaw(c *[basisDegree + 1]float64, s float64) float64 { + b1, b2 := 0.0, 0.0 + for m := basisDegree; m >= 1; m-- { + b1, b2 = 2*s*b1-b2+c[m], b1 + } + return s*b1 - b2 + c[0] +} diff --git a/internal/ltp/basis_test.go b/internal/ltp/basis_test.go new file mode 100644 index 0000000..4caa099 --- /dev/null +++ b/internal/ltp/basis_test.go @@ -0,0 +1,43 @@ +// 被测契约:缓存基与精确基在覆盖区间内相差 < 1e-12,区间外逐位相同。 +package ltp + +import ( + "math" + "testing" +) + +func TestEclipticBasisMatchesExact(t *testing.T) { + worst := 0.0 + for epj := -10199.9; epj < 10199.9; epj += 3.137 { + ax, ay, az := EclipticBasis(epj) + ex, ey, ez := EclipticBasisExact(epj) + for i := 0; i < 3; i++ { + worst = math.Max(worst, math.Abs(ax[i]-ex[i])) + worst = math.Max(worst, math.Abs(ay[i]-ey[i])) + worst = math.Max(worst, math.Abs(az[i]-ez[i])) + } + } + t.Logf("max component diff %.3g", worst) + if worst > 1e-12 { + t.Fatalf("cached basis differs by %.3g", worst) + } + for _, epj := range []float64{-10300, 10300} { + ax, _, az := EclipticBasis(epj) + ex, _, ez := EclipticBasisExact(epj) + if ax != ex || az != ez { + t.Fatalf("epj %v: outside the cache the exact basis must be used", epj) + } + } +} + +func BenchmarkEclipticBasis(b *testing.B) { + for i := 0; i < b.N; i++ { + EclipticBasis(2000 + float64(i%7300)*0.27) + } +} + +func BenchmarkEclipticBasisExact(b *testing.B) { + for i := 0; i < b.N; i++ { + EclipticBasisExact(2000 + float64(i%7300)*0.27) + } +} diff --git a/internal/ltp/ltp.go b/internal/ltp/ltp.go new file mode 100644 index 0000000..b027e85 --- /dev/null +++ b/internal/ltp/ltp.go @@ -0,0 +1,119 @@ +// Package ltp 长期岁差的黄极与赤道极向量(J2000 平赤道坐标,±20 万年有效)。 +package ltp + +import "math" + +const ( + as2r = 4.848136811095359935899141e-6 + d2pi = 6.283185307179586476925287 + eps0 = 84381.406 * as2r +) + +// Vector3 三维向量 / a 3-vector. +type Vector3 [3]float64 + +// EclipticPole 历元平黄道极 / mean ecliptic pole of epoch, epj in Julian years. +func EclipticPole(epj float64) Vector3 { + pqPol := [2][4]float64{ + {+5851.607687, -0.1189000, -0.00028913, +0.000000101}, + {-1600.886300, +1.1689818, -0.00000020, -0.000000437}, + } + pqPer := [8][5]float64{ + {708.15, -5486.751211, -684.661560, 667.666730, -5523.863691}, + {2309.00, -17.127623, 2446.283880, -2354.886252, -549.747450}, + {1620.00, -617.517403, 399.671049, -428.152441, -310.998056}, + {492.20, 413.442940, -356.652376, 376.202861, 421.535876}, + {1183.00, 78.614193, -186.387003, 184.778874, -36.776172}, + {622.00, -180.732815, -316.800070, 335.321713, -145.278396}, + {882.00, -87.676083, 198.296071, -185.138669, -34.744450}, + {547.00, 46.140315, 101.135679, -120.972830, 22.885731}, + } + t := (epj - 2000.0) / 100.0 + p, q := 0.0, 0.0 + for i := 0; i < 8; i++ { + w := d2pi * t + a := w / pqPer[i][0] + s := math.Sin(a) + c := math.Cos(a) + p += c*pqPer[i][1] + s*pqPer[i][3] + q += c*pqPer[i][2] + s*pqPer[i][4] + } + w := 1.0 + for i := 0; i < 4; i++ { + p += pqPol[0][i] * w + q += pqPol[1][i] * w + w *= t + } + p *= as2r + q *= as2r + z := math.Sqrt(math.Max(1.0-p*p-q*q, 0.0)) + s := math.Sin(eps0) + c := math.Cos(eps0) + return Vector3{p, -q*c - z*s, -q*s + z*c} +} + +// EquatorPole 历元平赤道极 / mean equator pole of epoch, epj in Julian years. +func EquatorPole(epj float64) Vector3 { + xyPol := [2][4]float64{ + {+5453.282155, +0.4252841, -0.00037173, -0.000000152}, + {-73750.930350, -0.7675452, -0.00018725, +0.000000231}, + } + xyPer := [14][5]float64{ + {256.75, -819.940624, 75004.344875, 81491.287984, 1558.515853}, + {708.15, -8444.676815, 624.033993, 787.163481, 7774.939698}, + {274.20, 2600.009459, 1251.136893, 1251.296102, -2219.534038}, + {241.45, 2755.175630, -1102.212834, -1257.950837, -2523.969396}, + {2309.00, -167.659835, -2660.664980, -2966.799730, 247.850422}, + {492.20, 871.855056, 699.291817, 639.744522, -846.485643}, + {396.10, 44.769698, 153.167220, 131.600209, -1393.124055}, + {288.90, -512.313065, -950.865637, -445.040117, 368.526116}, + {231.10, -819.415595, 499.754645, 584.522874, 749.045012}, + {1610.00, -538.071099, -145.188210, -89.756563, 444.704518}, + {620.00, -189.793622, 558.116553, 524.429630, 235.934465}, + {157.87, -402.922932, -23.923029, -13.549067, 374.049623}, + {220.30, 179.516345, -165.405086, -210.157124, -171.330180}, + {1200.00, -9.814756, 9.344131, -44.919798, -22.899655}, + } + t := (epj - 2000.0) / 100.0 + x, y := 0.0, 0.0 + for i := 0; i < 14; i++ { + w := d2pi * t + a := w / xyPer[i][0] + s := math.Sin(a) + c := math.Cos(a) + x += c*xyPer[i][1] + s*xyPer[i][3] + y += c*xyPer[i][2] + s*xyPer[i][4] + } + w := 1.0 + for i := 0; i < 4; i++ { + x += xyPol[0][i] * w + y += xyPol[1][i] * w + w *= t + } + x *= as2r + y *= as2r + w = x*x + y*y + z := 0.0 + if w < 1.0 { + z = math.Sqrt(1.0 - w) + } + return Vector3{x, y, z} +} + +// Cross 叉乘 / cross product. +func Cross(a, b Vector3) Vector3 { + return Vector3{ + a[1]*b[2] - a[2]*b[1], + a[2]*b[0] - a[0]*b[2], + a[0]*b[1] - a[1]*b[0], + } +} + +// Normalize 单位向量与原模长,零向量返回零 / unit vector and norm; zero stays zero. +func Normalize(p Vector3) (Vector3, float64) { + w := math.Sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]) + if w == 0.0 { + return Vector3{0, 0, 0}, 0 + } + return Vector3{p[0] / w, p[1] / w, p[2] / w}, w +} diff --git a/internal/lunarhorizon/lunarhorizon.go b/internal/lunarhorizon/lunarhorizon.go index 01fe78e..732a367 100644 --- a/internal/lunarhorizon/lunarhorizon.go +++ b/internal/lunarhorizon/lunarhorizon.go @@ -18,18 +18,10 @@ import ( "time" "b612.me/astro/basic" + "b612.me/astro/internal/ephem" "b612.me/astro/internal/geodata" ) -// normalizeLongitude 把经度归一化到 [-180, 180);internal/geodata 与 geojson 各有一份等价的未导出实现,无法跨包复用。 -func normalizeLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} - // defaultToleranceDegrees 是 GeoJSON 导出使用的误差门限。GeoJSON 客户端可以无限放大, // 因此这里取远小于任何地图像素的角度。 // defaultToleranceDegrees is the error tolerance used by the GeoJSON export. GeoJSON clients @@ -47,7 +39,12 @@ const DefaultToleranceDegrees = defaultToleranceDegrees // longitude. Refine in map coordinates before clipping; extra vertices are // corrected to the same topocentric zero-altitude curve as the source ring. func Refine(points []geodata.GeoPoint, at time.Time) []geodata.GeoPoint { - return RefineWithin(points, at, defaultToleranceDegrees) + return RefineWithInstance(points, at, nil) +} + +// RefineWithInstance 指定星历实例的站心地平线细分 / topocentric horizon refinement with an explicit instance. +func RefineWithInstance(points []geodata.GeoPoint, at time.Time, inst *ephem.Instance) []geodata.GeoPoint { + return RefineWithinWithInstance(points, at, defaultToleranceDegrees, inst) } // RefineWithin 用给定误差门限(单位:度)细分一条站心地平线。插入的顶点被校正回零高度, @@ -58,6 +55,11 @@ func Refine(points []geodata.GeoPoint, at time.Time) []geodata.GeoPoint { // accuracy without moving the curve. A fixed-resolution target such as an SVG map can pass a // tolerance matched to its pixel scale instead of emitting vertices no display can resolve. func RefineWithin(points []geodata.GeoPoint, at time.Time, toleranceDegrees float64) []geodata.GeoPoint { + return RefineWithinWithInstance(points, at, toleranceDegrees, nil) +} + +// RefineWithinWithInstance 指定星历实例和误差门限的站心地平线细分 / topocentric horizon refinement with an explicit instance and tolerance. +func RefineWithinWithInstance(points []geodata.GeoPoint, at time.Time, toleranceDegrees float64, inst *ephem.Instance) []geodata.GeoPoint { if !(toleranceDegrees > 0) { toleranceDegrees = defaultToleranceDegrees } @@ -66,10 +68,10 @@ func RefineWithin(points []geodata.GeoPoint, at time.Time, toleranceDegrees floa } jd := basic.Date2JD(at.UTC()) tt := basic.UTC2TT(jd) - ra, dec := basic.HMoonTrueRaDec(tt) - distanceAU := basic.HMoonAway(tt) / 149597870.7 + ra, dec := basic.HMoonTrueRaDecWithInstance(tt, inst) + distanceAU := basic.HMoonAwayWithInstance(tt, inst) / 149597870.7 sidereal := basic.ApparentSiderealTime(basic.UTC2UT1(jd)) * 15 - center := geodata.GeoPoint{Longitude: normalizeLongitude(ra - sidereal), Latitude: dec} + center := geodata.GeoPoint{Longitude: geodata.NormalizeLongitude(ra - sidereal), Latitude: dec} // Every correction is at the same instant. Reuse its full ephemeris while // retaining the ellipsoid and topocentric transform used by HMoonHeight. altitudeAt := func(point geodata.GeoPoint) float64 { diff --git a/internal/occultationgeo/band_cleanup.go b/internal/occultationgeo/band_cleanup.go index efd6b4e..a3d62a3 100644 --- a/internal/occultationgeo/band_cleanup.go +++ b/internal/occultationgeo/band_cleanup.go @@ -86,7 +86,7 @@ func smoothOccultationOrdinaryWobbles(points []geodata.GeoPoint) []geodata.GeoPo y += weights[offset+2] * projected[1] } candidate := occultationUnprojectedPoint(x/35, y/35) - move := geoDistanceKM(original[index], candidate) + move := geodata.EquatorialDistanceKM(original[index], candidate) if move > maximumMoveKM { continue } @@ -109,20 +109,15 @@ func smoothOccultationOrdinaryWobbles(points []geodata.GeoPoint) []geodata.GeoPo func occultationProjectedPoint(point geodata.GeoPoint) [2]float64 { latitude := math.Max(-85.05112878, math.Min(85.05112878, point.Latitude)) * math.Pi / 180 return [2]float64{ - EarthRadiusKM * point.Longitude * math.Pi / 180, - EarthRadiusKM * math.Log(math.Tan(math.Pi/4+latitude/2)), + geodata.EarthEquatorialRadiusKM * point.Longitude * math.Pi / 180, + geodata.EarthEquatorialRadiusKM * math.Log(math.Tan(math.Pi/4+latitude/2)), } } func occultationUnprojectedPoint(pointX, pointY float64) geodata.GeoPoint { - longitude := pointX / EarthRadiusKM * 180 / math.Pi - longitude = math.Mod(longitude+180, 360) - if longitude < 0 { - longitude += 360 - } return geodata.GeoPoint{ - Longitude: longitude - 180, - Latitude: (2*math.Atan(math.Exp(pointY/EarthRadiusKM)) - math.Pi/2) * 180 / math.Pi, + Longitude: geodata.NormalizeLongitude(pointX / geodata.EarthEquatorialRadiusKM * 180 / math.Pi), + Latitude: (2*math.Atan(math.Exp(pointY/geodata.EarthEquatorialRadiusKM)) - math.Pi/2) * 180 / math.Pi, } } @@ -533,7 +528,7 @@ func roundOccultationBandJunctionRing( continue } previous, next := result[index-1], result[index+1] - if occultationProjectedEdgeDistanceKM(previous, next) > maximumChordKM { + if geodata.WebMercatorDistanceKM(previous, next) > maximumChordKM { continue } turn := occultationTurnAngleDegrees(previous, point, next) @@ -716,9 +711,9 @@ func removeOccultationPolarKinks(points []geodata.GeoPoint) []geodata.GeoPoint { for index := 1; index+1 < len(result); index++ { first, middle, last := result[index-1], result[index], result[index+1] if math.Abs(middle.Latitude) < minimumPolarLatitudeDegrees || - occultationProjectedEdgeDistanceKM(first, middle) > maximumAdjacentEdgeKM || - occultationProjectedEdgeDistanceKM(middle, last) > maximumAdjacentEdgeKM || - occultationProjectedEdgeDistanceKM(first, last) > maximumEndpointChordKM { + geodata.WebMercatorDistanceKM(first, middle) > maximumAdjacentEdgeKM || + geodata.WebMercatorDistanceKM(middle, last) > maximumAdjacentEdgeKM || + geodata.WebMercatorDistanceKM(first, last) > maximumEndpointChordKM { continue } // A reversal in either projected axis is required. A merely curved @@ -896,20 +891,20 @@ func occultationCurveCoverageProbes( next = len(segment) - 1 } first, second := segment[index], segment[next] - deltaLongitude := normalizeGeoLongitude(second.Longitude - first.Longitude) + deltaLongitude := geodata.NormalizeLongitude(second.Longitude - first.Longitude) deltaLatitude := second.Latitude - first.Latitude length := math.Hypot(deltaLongitude, deltaLatitude) if length <= 1e-12 { continue } midpoint := geodata.GeoPoint{ - Longitude: normalizeGeoLongitude(first.Longitude + deltaLongitude/2), + Longitude: geodata.NormalizeLongitude(first.Longitude + deltaLongitude/2), Latitude: first.Latitude + deltaLatitude/2, } for _, offset := range []float64{0.00005, 0.0005, 0.002} { for _, side := range []float64{-1, 1} { candidates = append(candidates, geodata.GeoPoint{ - Longitude: normalizeGeoLongitude(midpoint.Longitude - side*deltaLatitude*offset/length), + Longitude: geodata.NormalizeLongitude(midpoint.Longitude - side*deltaLatitude*offset/length), Latitude: midpoint.Latitude + side*deltaLongitude*offset/length, }) } @@ -925,7 +920,7 @@ func occultationCurveCoverageProbes( } duplicate := false for _, existing := range probes { - if len(existing) > 0 && geoDistanceKM(existing[0], probe) < 1 { + if len(existing) > 0 && geodata.EquatorialDistanceKM(existing[0], probe) < 1 { duplicate = true break } diff --git a/internal/occultationgeo/band_linework.go b/internal/occultationgeo/band_linework.go index 005501e..ad89fc7 100644 --- a/internal/occultationgeo/band_linework.go +++ b/internal/occultationgeo/band_linework.go @@ -216,7 +216,7 @@ func occultationPhaseBoundaryCyclesForEdges( validEdges := make([]bool, len(edges)) nodeFor := func(point geodata.GeoPoint) int { for index := range nodes { - if geoDistanceKM(nodes[index].point, point) <= curveBoundaryJoinDistanceKM { + if geodata.EquatorialDistanceKM(nodes[index].point, point) <= curveBoundaryJoinDistanceKM { return index } } @@ -294,7 +294,7 @@ func occultationPhaseBoundaryCyclesForEdges( reverseGeoPointRing(next) } nextRing := append([]geodata.GeoPoint(nil), ring...) - if len(nextRing) > 0 && geoDistanceKM(nextRing[len(nextRing)-1], next[0]) <= curveBoundaryJoinDistanceKM { + if len(nextRing) > 0 && geodata.EquatorialDistanceKM(nextRing[len(nextRing)-1], next[0]) <= curveBoundaryJoinDistanceKM { next[0] = nextRing[len(nextRing)-1] } nextRing = append(nextRing, next[1:]...) @@ -500,7 +500,7 @@ func curveBoundaryEndpointsMatch( } func curveBoundaryEndpointMatch(first geodata.GeoPoint, firstTime time.Time, second geodata.GeoPoint, secondTime time.Time) bool { - return geoDistanceKM(first, second) <= curveBoundaryJoinDistanceKM && + return geodata.EquatorialDistanceKM(first, second) <= curveBoundaryJoinDistanceKM && !firstTime.IsZero() && !secondTime.IsZero() && absDuration(firstTime.Sub(secondTime)) <= curveBoundaryJoinTime } @@ -596,7 +596,7 @@ func densifyOccultationPolygons( continue } next := polygon[index+1] - distance := occultationProjectedEdgeDistanceKM(point, next) + distance := geodata.WebMercatorDistanceKM(point, next) steps := int(math.Ceil(distance / maximumEdgeKM)) if steps < 2 { continue @@ -610,7 +610,7 @@ func densifyOccultationPolygons( // fallback can still render one long closing chord. if len(polygon) > 2 && !geodata.SameGeoPoint(polygon[0], polygon[len(polygon)-1]) { first, last := polygon[0], polygon[len(polygon)-1] - distance := occultationProjectedEdgeDistanceKM(last, first) + distance := geodata.WebMercatorDistanceKM(last, first) steps := int(math.Ceil(distance / maximumEdgeKM)) if steps >= 2 { for step := 1; step < steps; step++ { @@ -623,24 +623,6 @@ func densifyOccultationPolygons( return result } -// occultationProjectedEdgeDistanceKM measures an edge in the Web Mercator -// chart used by the GeoJSON/OpenLayers consumer, while retaining the shortest -// wrapped longitude. At high latitude a small spherical edge expands strongly -// in this chart; using only the great-circle distance leaves visible polygon -// chords even though the source contact contour is densely sampled. -func occultationProjectedEdgeDistanceKM(first, second geodata.GeoPoint) float64 { - const maxLatitude = 85.05112878 - clampLatitude := func(value float64) float64 { - return math.Max(-maxLatitude, math.Min(maxLatitude, value)) - } - longitude := math.Remainder(second.Longitude-first.Longitude, 360) * math.Pi / 180 - firstLatitude := clampLatitude(first.Latitude) * math.Pi / 180 - secondLatitude := clampLatitude(second.Latitude) * math.Pi / 180 - firstY := math.Log(math.Tan(math.Pi/4 + firstLatitude/2)) - secondY := math.Log(math.Tan(math.Pi/4 + secondLatitude/2)) - return EarthRadiusKM * math.Hypot(longitude, secondY-firstY) -} - func interpolateOccultationGeoPoint( first, second geodata.GeoPoint, fraction float64, @@ -747,7 +729,7 @@ func interpolateOccultationHorizonPoint( } point := first point.Time = first.Time.Add(time.Duration(float64(second.Time.Sub(first.Time)) * fraction)) - point.Longitude = normalizeGeoLongitude(first.Longitude + fraction*deltaLongitude) + point.Longitude = geodata.NormalizeLongitude(first.Longitude + fraction*deltaLongitude) point.Latitude = first.Latitude + fraction*(second.Latitude-first.Latitude) point.MoonAltitude = 0 point.WidthKM = first.WidthKM + fraction*(second.WidthKM-first.WidthKM) @@ -837,13 +819,13 @@ func occultationVisibleFootprintProbes( continue } midpoint := geodata.GeoPoint{ - Longitude: normalizeGeoLongitude(first.Longitude + deltaLongitude/2), + Longitude: geodata.NormalizeLongitude(first.Longitude + deltaLongitude/2), Latitude: first.Latitude + deltaLatitude/2, } for _, offset := range []float64{0.0002, 0.002, 0.01, 0.03} { for _, side := range []float64{-1, 1} { edgeCandidates = append(edgeCandidates, geodata.GeoPoint{ - Longitude: normalizeGeoLongitude(midpoint.Longitude - side*deltaLatitude*offset/length), + Longitude: geodata.NormalizeLongitude(midpoint.Longitude - side*deltaLatitude*offset/length), Latitude: midpoint.Latitude + side*deltaLongitude*offset/length, }) } @@ -922,15 +904,7 @@ func limitOccultationCoveragePaths( func sameOccultationPoint(first, second basic.OccultationPathPoint) bool { return math.Abs(first.Latitude-second.Latitude) <= 1e-10 && - math.Abs(normalizeGeoLongitude(first.Longitude-second.Longitude)) <= 1e-10 -} - -func normalizeGeoLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 + math.Abs(geodata.NormalizeLongitude(first.Longitude-second.Longitude)) <= 1e-10 } func occultationPolarFootprintProbes( @@ -1007,11 +981,11 @@ func removeOccultationHairpins(points []geodata.GeoPoint, maximumClosureKM, mini arcLength := 0.0 best := -1 for end := start + 1; end <= limit; end++ { - arcLength += geoDistanceKM(result[end-1], result[end]) + arcLength += geodata.EquatorialDistanceKM(result[end-1], result[end]) if end < start+3 { continue } - closure := geoDistanceKM(result[start], result[end]) + closure := geodata.EquatorialDistanceKM(result[start], result[end]) if closure <= maximumClosureKM && arcLength-closure >= minimumDetourKM { best = end } @@ -1053,11 +1027,11 @@ func smoothOccultationHairpins( best := -1 bestDetour := minimumDetourKM for end := start + 1; end <= limit; end++ { - arcLength += geoDistanceKM(result[end-1], result[end]) + arcLength += geodata.EquatorialDistanceKM(result[end-1], result[end]) if end < start+3 { continue } - closure := geoDistanceKM(result[start], result[end]) + closure := geodata.EquatorialDistanceKM(result[start], result[end]) detour := arcLength - closure if closure <= maximumClosureKM && detour >= bestDetour { best = end @@ -1068,7 +1042,7 @@ func smoothOccultationHairpins( continue } first, last := result[start], result[best] - steps := int(math.Ceil(geoDistanceKM(first, last) / maximumEdgeKM)) + steps := int(math.Ceil(geodata.EquatorialDistanceKM(first, last) / maximumEdgeKM)) if steps < 1 { steps = 1 } @@ -1108,7 +1082,7 @@ func removeOccultationSharpCorners( changed := false for index := 1; index+1 < len(result); index++ { first, middle, last := result[index-1], result[index], result[index+1] - if geoDistanceKM(first, last) > maximumChordKM { + if geodata.EquatorialDistanceKM(first, last) > maximumChordKM { continue } if occultationTurnAngleDegrees(first, middle, last) >= minimumAngleDegrees { @@ -1142,7 +1116,7 @@ func removeOccultationPolarSharpCorners( for index := 1; index+1 < len(result); index++ { first, middle, last := result[index-1], result[index], result[index+1] if math.Abs(middle.Latitude) < minimumLatitudeDegrees || - geoDistanceKM(first, last) > maximumChordKM || + geodata.EquatorialDistanceKM(first, last) > maximumChordKM || occultationTurnAngleDegrees(first, middle, last) >= minimumAngleDegrees { continue } @@ -1170,7 +1144,7 @@ func smoothOccultationPolarCorners(points []geodata.GeoPoint) []geodata.GeoPoint for index := 1; index+1 < len(result); index++ { first, middle, last := result[index-1], result[index], result[index+1] if math.Abs(middle.Latitude) < 70 || - occultationProjectedEdgeDistanceKM(first, last) > 140 || + geodata.WebMercatorDistanceKM(first, last) > 140 || occultationTurnAngleDegrees(first, middle, last) >= 165 { continue } @@ -1180,7 +1154,7 @@ func smoothOccultationPolarCorners(points []geodata.GeoPoint) []geodata.GeoPoint if !longitudeReversal && !latitudeReversal { continue } - steps := int(math.Ceil(occultationProjectedEdgeDistanceKM(first, last) / 40)) + steps := int(math.Ceil(geodata.WebMercatorDistanceKM(first, last) / 40)) if steps < 2 { steps = 2 } @@ -1233,7 +1207,7 @@ func smoothOccultationPolarWobbles(points []geodata.GeoPoint) []geodata.GeoPoint first, last := result[start], result[end] if math.Abs(first.Latitude) < minimumPolarLatitudeDegrees || math.Abs(last.Latitude) < minimumPolarLatitudeDegrees || - occultationProjectedEdgeDistanceKM(first, last) > maximumEndpointSpanKM { + geodata.WebMercatorDistanceKM(first, last) > maximumEndpointSpanKM { continue } longitudeDirection := 0.0 @@ -1271,7 +1245,7 @@ func smoothOccultationPolarWobbles(points []geodata.GeoPoint) []geodata.GeoPoint for index := start + 1; index < end; index++ { fraction := float64(index-start) / float64(windowPoints) baseline := interpolateOccultationGeoPoint(first, last, fraction) - if geoDistanceKM(result[index], baseline) > maximumDeviationKM { + if geodata.EquatorialDistanceKM(result[index], baseline) > maximumDeviationKM { valid = false break } diff --git a/internal/occultationgeo/continuity.go b/internal/occultationgeo/continuity.go index 83a1ccd..346351e 100644 --- a/internal/occultationgeo/continuity.go +++ b/internal/occultationgeo/continuity.go @@ -11,7 +11,6 @@ import ( // 掩星边界连续性判定的几何门限 / geometric thresholds for occultation-boundary continuity checks. const ( - EarthRadiusKM = 6378.1366 BoundaryBranchJumpKM = 750.0 // BoundaryBranchSpeedKMPerSecond 是判定"物理上不可能的支路跳变"的地面速度门限: // 掩星边界随月球影子移动,地面速度上限约 1.1 km/s,取 2 km/s 留约两倍余量。 @@ -287,7 +286,7 @@ func nearestPolygonVertex( distance := math.Inf(1) for _, polygon := range polygons { for _, candidate := range polygon { - value := geoDistanceKM(point, candidate) + value := geodata.EquatorialDistanceKM(point, candidate) if value < distance { nearest, distance = candidate, value } @@ -327,7 +326,7 @@ func nearestPolygonBoundaryPoint( )) } candidate := interpolateOccultationGeoPoint(start, end, fraction) - value := geoDistanceKM(point, candidate) + value := geodata.EquatorialDistanceKM(point, candidate) if value < distance { nearest, distance = candidate, value } @@ -742,12 +741,21 @@ func visibleBandPolygonsFromAnalyticContours( } return footprintFill } - var selectionFill [][]geodata.GeoPoint + var selectionFill, footprintSelection [][]geodata.GeoPoint if pointSource || len(visibilityContours) == 0 { // Geocentric limit strips can extend beyond the station-corrected // stellar envelope or omit a finite-disk horizon extremum. Use visible // footprints as witnesses; the analytic linework supplies every edge. selectionFill = getFootprintFill() + if pointSource && len(visibilityContours) == 0 { + // 密集恒星时间轴含大量重叠环;连续界限用于选面,逐环仍用于覆盖见证;界限条带选面失败时退回逐环。 + footprintSelection = selectionFill + if limit := occultationLimitVisibleFillPolygons(northern, southern); len(limit) > 0 { + selectionFill = limit + } else if union := footprintVisibleUnionPolygons(footprints); len(union) > 0 { + selectionFill = union + } + } } if len(selectionFill) == 0 { selectionFill = occultationLimitVisibleFillPolygons(northern, southern) @@ -779,13 +787,21 @@ func visibleBandPolygonsFromAnalyticContours( if pointSource && len(visibilityContours) == 0 { initialCoverage = phaseLines } - polygons, err := polygonize(initialCoverage) - if err != nil && len(phaseLines) > 0 { - // A multi-branch rise/set network can contain several closed faces with - // identical physical junctions. If fill-only selection chooses the - // adjacent face, require every exported phase line as a coverage witness - // and retry without changing the boundary network or snap tolerance. - polygons, err = polygonize(phaseLines) + selectFaces := func() ([][]geodata.GeoPoint, error) { + polygons, err := polygonize(initialCoverage) + if err != nil && len(phaseLines) > 0 { + // A multi-branch rise/set network can contain several closed faces with + // identical physical junctions. If fill-only selection chooses the + // adjacent face, require every exported phase line as a coverage witness + // and retry without changing the boundary network or snap tolerance. + polygons, err = polygonize(phaseLines) + } + return polygons, err + } + polygons, err := selectFaces() + if err != nil && len(footprintSelection) > 0 { + selectionFill = footprintSelection + polygons, err = selectFaces() } if err != nil { if fallback, ok := fallbackVisible(); ok { diff --git a/internal/occultationgeo/continuity_test.go b/internal/occultationgeo/continuity_test.go index 9ff02f4..35f7593 100644 --- a/internal/occultationgeo/continuity_test.go +++ b/internal/occultationgeo/continuity_test.go @@ -238,13 +238,13 @@ func TestSmoothOccultationHairpinsKeepsShortDisplayEdges(t *testing.T) { maximumEdge := 0.0 maximumDetour := 0.0 for index := 1; index < len(smoothed); index++ { - maximumEdge = math.Max(maximumEdge, geoDistanceKM(smoothed[index-1], smoothed[index])) + maximumEdge = math.Max(maximumEdge, geodata.EquatorialDistanceKM(smoothed[index-1], smoothed[index])) } for index := 1; index+1 < len(smoothed); index++ { maximumDetour = math.Max(maximumDetour, - geoDistanceKM(smoothed[index-1], smoothed[index])+ - geoDistanceKM(smoothed[index], smoothed[index+1])- - geoDistanceKM(smoothed[index-1], smoothed[index+1])) + geodata.EquatorialDistanceKM(smoothed[index-1], smoothed[index])+ + geodata.EquatorialDistanceKM(smoothed[index], smoothed[index+1])- + geodata.EquatorialDistanceKM(smoothed[index-1], smoothed[index+1])) } if maximumEdge > 50.01 { t.Fatalf("smoothed maximum edge=%.1f km, want <=50 km", maximumEdge) @@ -300,10 +300,10 @@ func TestSmoothOccultationOrdinaryWobblesHandlesHighLatitudeArc(t *testing.T) { } changed := false for index := 1; index+1 < len(points); index++ { - if geoDistanceKM(points[index], smoothed[index]) > 0.001 { + if geodata.EquatorialDistanceKM(points[index], smoothed[index]) > 0.001 { changed = true - if geoDistanceKM(points[index], smoothed[index]) > 20 { - t.Fatalf("high-latitude smoothing moved point %d by %.2f km", index, geoDistanceKM(points[index], smoothed[index])) + if geodata.EquatorialDistanceKM(points[index], smoothed[index]) > 20 { + t.Fatalf("high-latitude smoothing moved point %d by %.2f km", index, geodata.EquatorialDistanceKM(points[index], smoothed[index])) } } } @@ -321,7 +321,7 @@ func TestDensifyOccultationPolygonsIncludesClosingEdge(t *testing.T) { } densified := densifyOccultationPolygons([][]geodata.GeoPoint{points}, 50) ring := densified[0] - maximumEdge := geoDistanceKM(ring[len(ring)-1], ring[0]) + maximumEdge := geodata.EquatorialDistanceKM(ring[len(ring)-1], ring[0]) if maximumEdge > 50.01 { t.Fatalf("closing edge=%.1f km, want <=50 km", maximumEdge) } @@ -449,7 +449,7 @@ func TestVisibleBandPolygons20250105SaturnEnvelopeCandidates(t *testing.T) { } for _, polygon := range final { for index := 1; index < len(polygon); index++ { - if distance := geoDistanceKM(polygon[index-1], polygon[index]); distance > BoundaryBranchJumpKM { + if distance := geodata.EquatorialDistanceKM(polygon[index-1], polygon[index]); distance > BoundaryBranchJumpKM { t.Fatalf("band ring spans a %.1f km edge", distance) } } @@ -519,7 +519,7 @@ func TestSmoothOccultationOrdinaryWobblesPreservesEndpointsAndBoundedMotion(t *t t.Fatalf("ordinary smoothing changed open endpoints: %#v", cleaned) } for index, point := range cleaned { - if move := geoDistanceKM(point, ring[index]); move > 20.001 { + if move := geodata.EquatorialDistanceKM(point, ring[index]); move > 20.001 { t.Fatalf("point %d moved %.2f km, want <=20 km", index, move) } } @@ -585,7 +585,7 @@ func occultationTestWindowTouchesHorizon( } for offset := -2; offset <= 2; offset++ { for _, horizon := range horizons { - if geoDistanceKM(polygon[index+offset], horizon) <= windowKM { + if geodata.EquatorialDistanceKM(polygon[index+offset], horizon) <= windowKM { return true } } @@ -716,7 +716,7 @@ func TestVisibleBandPolygons20250729MarsStitchesEndRiseFold(t *testing.T) { minimumTurn := 180.0 for index := 1; index+1 < len(polygon); index++ { first, middle, last := polygon[index-1], polygon[index], polygon[index+1] - detour := geoDistanceKM(first, middle) + geoDistanceKM(middle, last) - geoDistanceKM(first, last) + detour := geodata.EquatorialDistanceKM(first, middle) + geodata.EquatorialDistanceKM(middle, last) - geodata.EquatorialDistanceKM(first, last) if detour > maximumDetour { maximumDetour = detour } @@ -1027,7 +1027,7 @@ func TestOccultationPhaseBoundaryCycleExcludesGreatestInteriorLine(t *testing.T) minimum := math.Inf(1) for _, line := range phaseLines { for _, candidate := range line { - minimum = math.Min(minimum, geoDistanceKM(point, candidate)) + minimum = math.Min(minimum, geodata.EquatorialDistanceKM(point, candidate)) } } if minimum > 1 { @@ -1135,7 +1135,7 @@ func TestVisibleBandPolygons20250729MarsTotalRiseSetRemovesPolarBacktrack(t *tes limit = len(polygon) - 1 } for endIndex := startIndex + 1; endIndex <= limit; endIndex++ { - edge := geoDistanceKM(polygon[endIndex-1], polygon[endIndex]) + edge := geodata.EquatorialDistanceKM(polygon[endIndex-1], polygon[endIndex]) arcLength += edge if edge > maximumEdge { maximumEdge = edge @@ -1143,7 +1143,7 @@ func TestVisibleBandPolygons20250729MarsTotalRiseSetRemovesPolarBacktrack(t *tes if endIndex < startIndex+3 { continue } - closure := geoDistanceKM(polygon[startIndex], polygon[endIndex]) + closure := geodata.EquatorialDistanceKM(polygon[startIndex], polygon[endIndex]) if closure <= 180 && arcLength-closure >= 35 { t.Fatalf("total polygon %d retains a large polar backtrack: start=%d end=%d closure=%.1f km detour=%.1f km", polygonIndex, startIndex, endIndex, closure, arcLength-closure) diff --git a/internal/occultationgeo/curve_sampling.go b/internal/occultationgeo/curve_sampling.go index 2cab3a5..dc1df29 100644 --- a/internal/occultationgeo/curve_sampling.go +++ b/internal/occultationgeo/curve_sampling.go @@ -91,15 +91,8 @@ func densifyRiseSetSegment( } func projectedGeoPointDistanceKM(first, second basic.OccultationPathPoint) float64 { - const maxLatitude = 85.05112878 - const radiusKM = 6378.1366 - clampLatitude := func(value float64) float64 { - return math.Max(-maxLatitude, math.Min(maxLatitude, value)) - } - longitude := math.Remainder(second.Longitude-first.Longitude, 360) * math.Pi / 180 - firstLatitude := clampLatitude(first.Latitude) * math.Pi / 180 - secondLatitude := clampLatitude(second.Latitude) * math.Pi / 180 - firstY := math.Log(math.Tan(math.Pi/4 + firstLatitude/2)) - secondY := math.Log(math.Tan(math.Pi/4 + secondLatitude/2)) - return radiusKM * math.Hypot(longitude, secondY-firstY) + return geodata.WebMercatorDistanceKM( + geodata.GeoPoint{Longitude: first.Longitude, Latitude: first.Latitude}, + geodata.GeoPoint{Longitude: second.Longitude, Latitude: second.Latitude}, + ) } diff --git a/internal/occultationgeo/differential_test.go b/internal/occultationgeo/differential_test.go index bc009f7..ea7913f 100644 --- a/internal/occultationgeo/differential_test.go +++ b/internal/occultationgeo/differential_test.go @@ -142,7 +142,7 @@ func TestSamePhaseHorizonConnectorAllowedMatchesReferenceRule(t *testing.T) { } last := horizonConnectorEndpoint{ point: basic.OccultationPathPoint{ - Longitude: distanceKM / (EarthRadiusKM * math.Pi / 180), + Longitude: distanceKM / (geodata.EarthEquatorialRadiusKM * math.Pi / 180), }, curveIndex: 3, segmentIndex: 1, atStart: true, } diff --git a/internal/occultationgeo/direct_band.go b/internal/occultationgeo/direct_band.go index 5a87cba..dc9459f 100644 --- a/internal/occultationgeo/direct_band.go +++ b/internal/occultationgeo/direct_band.go @@ -39,8 +39,8 @@ func DirectVisibleBandPolygons( } if len(samples) > 0 { previous := samples[len(samples)-1] - direct := geoDistanceKM(previous.a, boundary[0]) + geoDistanceKM(previous.b, boundary[len(boundary)-1]) - swapped := geoDistanceKM(previous.a, boundary[len(boundary)-1]) + geoDistanceKM(previous.b, boundary[0]) + direct := geodata.EquatorialDistanceKM(previous.a, boundary[0]) + geodata.EquatorialDistanceKM(previous.b, boundary[len(boundary)-1]) + swapped := geodata.EquatorialDistanceKM(previous.a, boundary[len(boundary)-1]) + geodata.EquatorialDistanceKM(previous.b, boundary[0]) if swapped < direct { reverseGeoPoints(boundary) } @@ -140,7 +140,7 @@ func removeDirectProjectedSharpCorners( changed := false for index := 1; index+1 < len(result); index++ { first, middle, last := result[index-1], result[index], result[index+1] - if geoDistanceKM(first, last) > maximumChordKM { + if geodata.EquatorialDistanceKM(first, last) > maximumChordKM { continue } scale := math.Cos(middle.Latitude * math.Pi / 180) @@ -281,7 +281,7 @@ func reverseGeoPoints(points []geodata.GeoPoint) { } func appendDirectRingPoint(ring *[]geodata.GeoPoint, point geodata.GeoPoint) { - if len(*ring) > 0 && geoDistanceKM((*ring)[len(*ring)-1], point) <= 1e-6 { + if len(*ring) > 0 && geodata.EquatorialDistanceKM((*ring)[len(*ring)-1], point) <= 1e-6 { return } *ring = append(*ring, point) @@ -299,7 +299,7 @@ func densifyDirectRing(ring []geodata.GeoPoint, maximumEdgeKM float64) []geodata } next := ring[index+1] steps := int(math.Ceil(math.Max( - geoDistanceKM(point, next), occultationProjectedEdgeDistanceKM(point, next), + geodata.EquatorialDistanceKM(point, next), geodata.WebMercatorDistanceKM(point, next), ) / maximumEdgeKM)) if steps < 2 { continue diff --git a/internal/occultationgeo/footprint_sweep.go b/internal/occultationgeo/footprint_sweep.go index e909b29..2a8a663 100644 --- a/internal/occultationgeo/footprint_sweep.go +++ b/internal/occultationgeo/footprint_sweep.go @@ -128,7 +128,7 @@ func closedFootprintRingAlignmentScore( score := 0.0 for sample := 0; sample < samples; sample++ { index := sample * len(previous) / samples - score += geoDistanceKM(previous[index], current[(index+shift)%len(current)]) + score += geodata.EquatorialDistanceKM(previous[index], current[(index+shift)%len(current)]) } return score } @@ -153,7 +153,7 @@ func footprintClosedSweepMaximumStep( } maximum := 0.0 for index := 0; index < count; index++ { - maximum = math.Max(maximum, geoDistanceKM(first[index], second[index])) + maximum = math.Max(maximum, geodata.EquatorialDistanceKM(first[index], second[index])) } return maximum } @@ -337,7 +337,7 @@ func occultationUnitBoundsNear(first, second occultationUnitBounds, limitKM floa dy := math.Max(0, math.Max(first.minY-second.maxY, second.minY-first.maxY)) dz := math.Max(0, math.Max(first.minZ-second.maxZ, second.minZ-first.maxZ)) // 大圆距离不小于弦长,因此弦距下界可以作为 60 km 近邻判定的必要条件。 - return EarthRadiusKM*math.Sqrt(dx*dx+dy*dy+dz*dz) <= limitKM + return geodata.EarthEquatorialRadiusKM*math.Sqrt(dx*dx+dy*dy+dz*dz) <= limitKM } func mergeTouchingVisiblePolygons(polygons [][]geodata.GeoPoint) [][]geodata.GeoPoint { @@ -367,7 +367,7 @@ func mergeTouchingVisiblePolygons(polygons [][]geodata.GeoPoint) [][]geodata.Geo } for _, left := range polygons[first] { for _, right := range polygons[second] { - if geoDistanceKM(left, right) > touchingDistanceKM { + if geodata.EquatorialDistanceKM(left, right) > touchingDistanceKM { continue } firstIndex, secondIndex = first, second @@ -388,7 +388,7 @@ func mergeTouchingVisiblePolygons(polygons [][]geodata.GeoPoint) [][]geodata.Geo break } bridgeHalfDeg := minimumBridgeHalfDeg - if gap := geoDistanceKM(leftTouch, rightTouch) / EarthRadiusKM * 180 / math.Pi; gap/2+0.002 > bridgeHalfDeg { + if gap := geodata.EquatorialDistanceKM(leftTouch, rightTouch) / geodata.EarthEquatorialRadiusKM * 180 / math.Pi; gap/2+0.002 > bridgeHalfDeg { bridgeHalfDeg = gap/2 + 0.002 } bridge := []geodata.GeoPoint{ @@ -629,7 +629,7 @@ func nearestFootprintPointIndex(points []geodata.GeoPoint, target geodata.GeoPoi nearest := 0 distance := math.Inf(1) for index, point := range points { - candidate := geoDistanceKM(point, target) + candidate := geodata.EquatorialDistanceKM(point, target) if candidate < distance { nearest, distance = index, candidate } @@ -672,7 +672,7 @@ func footprintArcMatchScore(candidate, reference []geodata.GeoPoint) float64 { for index := 0; index < samples; index++ { candidateIndex := index * (len(candidate) - 1) / (samples - 1) referenceIndex := index * (len(reference) - 1) / (samples - 1) - score += geoDistanceKM(candidate[candidateIndex], reference[referenceIndex]) + score += geodata.EquatorialDistanceKM(candidate[candidateIndex], reference[referenceIndex]) } return score } @@ -721,7 +721,7 @@ func footprintStaticInteriorPolygons(footprints []basic.OccultationFootprint) [] } centerCap := geodata.SphericalCircle( center, - staticCenterCapRadiusKM/EarthRadiusKM*180/math.Pi, + staticCenterCapRadiusKM/geodata.EarthEquatorialRadiusKM*180/math.Pi, staticCenterCapPoints, ) if len(centerCap) >= 3 { @@ -782,8 +782,8 @@ func footprintPolygons(footprints []basic.OccultationFootprint) [][]geodata.GeoP func geoPointSegmentDistanceKM(point, start, end geodata.GeoPoint) float64 { latitude := point.Latitude * math.Pi / 180 - scaleX := math.Cos(latitude) * EarthRadiusKM * math.Pi / 180 - scaleY := EarthRadiusKM * math.Pi / 180 + scaleX := math.Cos(latitude) * geodata.EarthEquatorialRadiusKM * math.Pi / 180 + scaleY := geodata.EarthEquatorialRadiusKM * math.Pi / 180 x := func(value geodata.GeoPoint) float64 { return math.Remainder(value.Longitude-point.Longitude, 360) * scaleX } @@ -855,18 +855,8 @@ func BoundaryBranchChanged(first, second basic.OccultationPathPoint) bool { // DistanceKM 返回两个边界样本之间的最短球面距离。 // DistanceKM returns the shortest spherical surface distance between two boundary samples. func DistanceKM(first, second basic.OccultationPathPoint) float64 { - return geoDistanceKM( + return geodata.EquatorialDistanceKM( geodata.GeoPoint{Longitude: first.Longitude, Latitude: first.Latitude}, geodata.GeoPoint{Longitude: second.Longitude, Latitude: second.Latitude}, ) } - -func geoDistanceKM(first, second geodata.GeoPoint) float64 { - firstLatitude := first.Latitude * math.Pi / 180 - secondLatitude := second.Latitude * math.Pi / 180 - deltaLatitude := secondLatitude - firstLatitude - deltaLongitude := math.Remainder((second.Longitude-first.Longitude)*math.Pi/180, 2*math.Pi) - haversine := math.Sin(deltaLatitude/2)*math.Sin(deltaLatitude/2) + - math.Cos(firstLatitude)*math.Cos(secondLatitude)*math.Sin(deltaLongitude/2)*math.Sin(deltaLongitude/2) - return 2 * EarthRadiusKM * math.Asin(math.Sqrt(math.Min(1, haversine))) -} diff --git a/internal/occultationgeo/reference_test.go b/internal/occultationgeo/reference_test.go index 8e7d404..469d1ea 100644 --- a/internal/occultationgeo/reference_test.go +++ b/internal/occultationgeo/reference_test.go @@ -22,7 +22,7 @@ func mergeTouchingVisiblePolygonsReference(polygons [][]geodata.GeoPoint) [][]ge for second := first + 1; second < len(polygons) && firstIndex < 0; second++ { for _, left := range polygons[first] { for _, right := range polygons[second] { - if geoDistanceKM(left, right) > touchingDistanceKM { + if geodata.EquatorialDistanceKM(left, right) > touchingDistanceKM { continue } firstIndex, secondIndex = first, second @@ -43,7 +43,7 @@ func mergeTouchingVisiblePolygonsReference(polygons [][]geodata.GeoPoint) [][]ge break } bridgeHalfDeg := minimumBridgeHalfDeg - if gap := geoDistanceKM(leftTouch, rightTouch) / EarthRadiusKM * 180 / math.Pi; gap/2+0.002 > bridgeHalfDeg { + if gap := geodata.EquatorialDistanceKM(leftTouch, rightTouch) / geodata.EarthEquatorialRadiusKM * 180 / math.Pi; gap/2+0.002 > bridgeHalfDeg { bridgeHalfDeg = gap/2 + 0.002 } bridge := []geodata.GeoPoint{ diff --git a/internal/occultationgeo/visible_band.go b/internal/occultationgeo/visible_band.go index 850ea3c..0f051b1 100644 --- a/internal/occultationgeo/visible_band.go +++ b/internal/occultationgeo/visible_band.go @@ -1274,7 +1274,7 @@ func occultationCoverageProbeExists( probe geodata.GeoPoint, ) bool { for _, existing := range coveragePaths { - if geoDistanceKM(existing[0], probe) < 10 { + if geodata.EquatorialDistanceKM(existing[0], probe) < 10 { return true } } diff --git a/internal/refraction/refraction.go b/internal/refraction/refraction.go new file mode 100644 index 0000000..dad6054 --- /dev/null +++ b/internal/refraction/refraction.go @@ -0,0 +1,167 @@ +// Package refraction Saemundsson 大气折射及其数值逆解,供 basic 与 formula 共用。 +package refraction + +import "math" + +// StandardPressureHPa 与 StandardTemperatureC 是折射公式的标准气压与气温 / standard pressure and temperature of the refraction formula. +const ( + StandardPressureHPa = 1010.0 + StandardTemperatureC = 10.0 +) + +const ( + refractionStandardTemperatureK = 283.15 + refractionAbsoluteZeroC = -273.15 + refractionLowerLimitAltitudeDeg = -5.0 + refractionUpperLimitAltitudeDeg = 90.0 +) + +// RefractionFromApparentAltitude 由视高度角求折射量(度,视高度减去它得真高度),无逆解时为 NaN / refraction from apparent altitude. +func RefractionFromApparentAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { + if !validRefractionInputs(apparentAltitude, pressureHPa, temperatureC) { + return math.NaN() + } + if apparentAltitude <= refractionLowerLimitAltitudeDeg || apparentAltitude >= refractionUpperLimitAltitudeDeg { + return 0 + } + trueAltitude := trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC) + return apparentAltitude - trueAltitude +} + +// TrueAltitude 视高度角转真高度角(度),无逆解时为 NaN / true altitude from apparent altitude. +func TrueAltitude(apparentAltitude, pressureHPa, temperatureC float64) float64 { + if !validRefractionInputs(apparentAltitude, pressureHPa, temperatureC) { + return math.NaN() + } + if apparentAltitude <= refractionLowerLimitAltitudeDeg || apparentAltitude >= refractionUpperLimitAltitudeDeg { + return apparentAltitude + } + return trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC) +} + +// ApparentAltitude 真高度角转视高度角(度) / apparent altitude from true altitude. +func ApparentAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + if !validRefractionInputs(trueAltitude, pressureHPa, temperatureC) { + return math.NaN() + } + return trueAltitude + refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) +} + +// RefractionFromTrueAltitude 由真高度角求折射量(度,加到真高度上得视高度) / refraction from true altitude. +func RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + if !validRefractionInputs(trueAltitude, pressureHPa, temperatureC) { + return math.NaN() + } + return refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) +} + +// Saemundsson 公式以真高度角为输入;逆函数对同一模型数值求解,保证真/视高度互逆。 +func refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + if trueAltitude <= refractionLowerLimitAltitudeDeg || trueAltitude >= refractionUpperLimitAltitudeDeg { + return 0 + } + angle := (trueAltitude + 10.3/(trueAltitude+5.11)) * math.Pi / 180 + return refractionScale(pressureHPa, temperatureC) * (1.02 / math.Tan(angle)) / 60 +} + +func trueAltitudeFromApparent(apparentAltitude, pressureHPa, temperatureC float64) float64 { + lower := math.Nextafter(refractionLowerLimitAltitudeDeg, math.Inf(1)) + upper := apparentAltitude + // 近 90° 时折射略为负,真高度高于视高度,根区间上界须放宽到 90°。 + if refractionFromTrueAltitude(apparentAltitude, pressureHPa, temperatureC) < 0 { + upper = math.Nextafter(refractionUpperLimitAltitudeDeg, math.Inf(-1)) + } + if estimate, ok := trueAltitudeFromApparentNewton(apparentAltitude, pressureHPa, temperatureC, lower, upper); ok { + return estimate + } + return trueAltitudeFromApparentBisection(apparentAltitude, pressureHPa, temperatureC, lower, upper) +} + +func trueAltitudeFromApparentNewton(apparentAltitude, pressureHPa, temperatureC, lower, upper float64) (float64, bool) { + estimate := apparentAltitude - refractionFromTrueAltitude(apparentAltitude, pressureHPa, temperatureC) + scale := refractionScale(pressureHPa, temperatureC) * 1.02 / 60 + for i := 0; i < 12; i++ { + if estimate < lower || estimate > upper || !finiteRefractionValue(estimate) { + return 0, false + } + value := refractionInverseResidual(estimate, apparentAltitude, pressureHPa, temperatureC) + if math.Abs(value) < 1e-12 { + return estimate, true + } + shifted := estimate + 5.11 + sinAngle := math.Sin((estimate + 10.3/shifted) * math.Pi / 180) + // d/dh cot(A) = −(dA/dh)/sin²A,A = h + 10.3/(h+5.11)(度)。 + derivative := 1 - scale*(1-10.3/(shifted*shifted))*(math.Pi/180)/(sinAngle*sinAngle) + if math.Abs(derivative) < 1e-12 || !finiteRefractionValue(derivative) { + return 0, false + } + next := estimate - value/derivative + if next < lower || next > upper || !finiteRefractionValue(next) { + return 0, false + } + estimate = next + } + return 0, false +} + +func trueAltitudeFromApparentBisection(apparentAltitude, pressureHPa, temperatureC, lower, upper float64) float64 { + lowerValue := refractionInverseResidual(lower, apparentAltitude, pressureHPa, temperatureC) + upperValue := refractionInverseResidual(upper, apparentAltitude, pressureHPa, temperatureC) + if !finiteRefractionValue(lowerValue) || !finiteRefractionValue(upperValue) || lowerValue > 0 || upperValue < 0 { + return math.NaN() + } + if math.Abs(lowerValue) < 1e-12 { + return lower + } + if math.Abs(upperValue) < 1e-12 { + return upper + } + + best, bestResidual := lower, math.Abs(lowerValue) + if math.Abs(upperValue) < bestResidual { + best, bestResidual = upper, math.Abs(upperValue) + } + for i := 0; i < 96; i++ { + midpoint := lower + (upper-lower)/2 + if midpoint == lower || midpoint == upper { + break + } + value := refractionInverseResidual(midpoint, apparentAltitude, pressureHPa, temperatureC) + if !finiteRefractionValue(value) { + return math.NaN() + } + if residual := math.Abs(value); residual < bestResidual { + best, bestResidual = midpoint, residual + } + if math.Abs(value) < 1e-12 { + return midpoint + } + if value > 0 { + upper = midpoint + } else { + lower = midpoint + } + } + if bestResidual <= 1e-9 { + return best + } + return math.NaN() +} + +func refractionInverseResidual(trueAltitude, apparentAltitude, pressureHPa, temperatureC float64) float64 { + return trueAltitude + refractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC) - apparentAltitude +} + +func finiteRefractionValue(value float64) bool { + return !math.IsNaN(value) && !math.IsInf(value, 0) +} + +func validRefractionInputs(altitude, pressureHPa, temperatureC float64) bool { + return !(math.IsNaN(altitude) || math.IsInf(altitude, 0) || + math.IsNaN(pressureHPa) || math.IsInf(pressureHPa, 0) || pressureHPa <= 0 || + math.IsNaN(temperatureC) || math.IsInf(temperatureC, 0) || temperatureC <= refractionAbsoluteZeroC) +} + +func refractionScale(pressureHPa, temperatureC float64) float64 { + return pressureHPa / StandardPressureHPa * refractionStandardTemperatureK / (temperatureC - refractionAbsoluteZeroC) +} diff --git a/internal/refraction/refraction_test.go b/internal/refraction/refraction_test.go new file mode 100644 index 0000000..ddc9f01 --- /dev/null +++ b/internal/refraction/refraction_test.go @@ -0,0 +1,21 @@ +package refraction + +import ( + "math" + "testing" +) + +func TestRefractionRoundTrip(t *testing.T) { + for _, alt := range []float64{-1, 0, 0.5, 5, 30, 80} { + apparent := ApparentAltitude(alt, StandardPressureHPa, StandardTemperatureC) + if back := TrueAltitude(apparent, StandardPressureHPa, StandardTemperatureC); math.Abs(back-alt) > 1e-9 { + t.Errorf("alt %v: round trip %v", alt, back) + } + } + if r := RefractionFromTrueAltitude(0, 1010, 10) * 60; r < 28 || r > 36 { + t.Errorf("horizon refraction %.2f arcmin", r) + } + if !math.IsNaN(RefractionFromTrueAltitude(0, 0, 10)) { + t.Error("zero pressure must be NaN") + } +} diff --git a/internal/solarclosure/solarclosure.go b/internal/solarclosure/solarclosure.go index f0e1bc9..aac75d9 100644 --- a/internal/solarclosure/solarclosure.go +++ b/internal/solarclosure/solarclosure.go @@ -6,8 +6,6 @@ package solarclosure import ( - "math" - "b612.me/astro/internal/geodata" ) @@ -35,8 +33,8 @@ func HorizonEnds(footprint Footprint) []geodata.GeoPoint { return nil } points := []geodata.GeoPoint{footprint.HorizonEnds[0], footprint.HorizonEnds[1]} - if pointDistanceKM(points[0], footprint.Boundaries[0][0]) > - pointDistanceKM(points[1], footprint.Boundaries[0][0]) { + if geodata.MeanDistanceKM(points[0], footprint.Boundaries[0][0]) > + geodata.MeanDistanceKM(points[1], footprint.Boundaries[0][0]) { points[0], points[1] = points[1], points[0] } return points @@ -118,7 +116,7 @@ func BandPolygons( for polygonIndex := range polygons { for pointIndex := range polygons[polygonIndex] { polygons[polygonIndex][pointIndex].Longitude = - normalizeLongitude(polygons[polygonIndex][pointIndex].Longitude) + geodata.NormalizeLongitude(polygons[polygonIndex][pointIndex].Longitude) } } return polygons, true @@ -161,19 +159,3 @@ func openRing(points []geodata.GeoPoint) []geodata.GeoPoint { } return points } - -func normalizeLongitude(value float64) float64 { - value = math.Mod(value+180, 360) - if value < 0 { - value += 360 - } - return value - 180 -} - -func pointDistanceKM(first, second geodata.GeoPoint) float64 { - lat1, lat2 := first.Latitude*math.Pi/180, second.Latitude*math.Pi/180 - dlat := lat2 - lat1 - dlon := math.Remainder((second.Longitude-first.Longitude)*math.Pi/180, 2*math.Pi) - h := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlon/2)*math.Sin(dlon/2) - return 6371.0088 * 2 * math.Asin(math.Sqrt(math.Max(0, math.Min(1, h)))) -} diff --git a/internal/timenote/timenote.go b/internal/timenote/timenote.go index 18c7119..fcc666a 100644 --- a/internal/timenote/timenote.go +++ b/internal/timenote/timenote.go @@ -7,15 +7,15 @@ import ( "strings" "time" - "b612.me/astro" + "b612.me/astro/basic" ) const english = "en" // Scale 生成图内时标声明:UTC 口径写显示时区与 UTC 偏移,UT1 口径补 DUT1 差值 / builds the in-figure time-scale declaration. -func Scale(scale astro.TimeScale, instant time.Time, location *time.Location, language string) string { - if scale == astro.TimeScaleUT1 { - dut1 := astro.DUT1(instant) +func Scale(scale basic.TimeScale, instant time.Time, location *time.Location, language string) string { + if scale == basic.TimeScaleUT1 { + dut1 := basic.DUT1(instant) if language == english { return fmt.Sprintf("Times are UT1 (Universal Time 1); DUT1 = UT1−UTC = %+.2f s.", dut1) } diff --git a/internal/timenote/timenote_test.go b/internal/timenote/timenote_test.go index 9ac7a47..64fbd92 100644 --- a/internal/timenote/timenote_test.go +++ b/internal/timenote/timenote_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "b612.me/astro" + "b612.me/astro/basic" ) // 时标声明里同一个 UTC 偏移只写一次:时区名本身是偏移标签(FixedZone("UTC+08:00") 之类)时不再追加偏移。 @@ -27,7 +27,7 @@ func TestScaleWritesTheOffsetOnce(t *testing.T) { {name: "UTC", location: time.UTC, want: "图中时刻为 UTC"}, } { t.Run(test.name, func(t *testing.T) { - got := Scale(astro.TimeScaleUTC, instant, test.location, test.language) + got := Scale(basic.TimeScaleUTC, instant, test.location, test.language) if got != test.want { t.Fatalf("Scale() = %q, want %q", got, test.want) } diff --git a/jupiter/api_default.go b/jupiter/api_default.go new file mode 100644 index 0000000..d2a2390 --- /dev/null +++ b/jupiter/api_default.go @@ -0,0 +1,306 @@ +package jupiter + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 木星升交点黄经 / ascending node longitude of Jupiter. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 木星升交点黄经(n 变体) / n-parameterised ascending node longitude of Jupiter. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// CentralMeridians 木星 System I/II/III 中央经线 / Jupiter System I/II/III central meridians. +func CentralMeridians(date time.Time) CentralMeridianInfo { + return defaultModule.CentralMeridians(date) +} + +// CentralMeridiansN 木星 System I/II/III 中央经线(n 变体) / n-parameterised Jupiter System I/II/III central meridians. +func CentralMeridiansN(date time.Time, n int) CentralMeridianInfo { + return defaultModule.CentralMeridiansN(date, n) +} + +// ClosestGalileanPhenomenonContactEvent 最近一次 IMCCE 风格接触事件 / closest IMCCE-style contact event. +func ClosestGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { + return defaultModule.ClosestGalileanPhenomenonContactEvent(date, satellite, phenomenonType) +} + +// ClosestGalileanPhenomenonEvent 最近一次伽利略卫星现象 / closest Galilean-satellite event. +func ClosestGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { + return defaultModule.ClosestGalileanPhenomenonEvent(date, satellite, phenomenonType) +} + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 木星降交点黄经 / descending node longitude of Jupiter. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 木星降交点黄经(n 变体) / n-parameterised descending node longitude of Jupiter. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 木星视直径,单位角秒 / apparent Jupiter diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 木星视直径(n 变体),单位角秒 / n-parameterised apparent Jupiter diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastEasternQuadrature 上一次东方照,取 date 所在当地民用日 / previous eastern quadrature, on the local civil day of date. +func LastEasternQuadrature(date time.Time) time.Time { + return defaultModule.LastEasternQuadrature(date) +} + +// LastGalileanPhenomenonContactEvent 上一次 IMCCE 风格接触事件 / previous IMCCE-style contact event. +func LastGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { + return defaultModule.LastGalileanPhenomenonContactEvent(date, satellite, phenomenonType) +} + +// LastGalileanPhenomenonEvent 上一次伽利略卫星现象,satellite 取 1=Io 2=Europa 3=Ganymede 4=Callisto;phenomenonType 取 transit/occultation/eclipse/shadow_transit / previous Galilean-satellite event, satellite is 1=Io 2=Europa 3=Ganymede 4=Callisto; phenomenonType is transit/occultation/eclipse/shadow_transit. +func LastGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { + return defaultModule.LastGalileanPhenomenonEvent(date, satellite, phenomenonType) +} + +// LastOpposition 上一次冲日,取 date 所在当地民用日 / previous opposition, on the local civil day of date. +func LastOpposition(date time.Time) time.Time { return defaultModule.LastOpposition(date) } + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastWesternQuadrature 上一次西方照,取 date 所在当地民用日 / previous western quadrature, on the local civil day of date. +func LastWesternQuadrature(date time.Time) time.Time { + return defaultModule.LastWesternQuadrature(date) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextEasternQuadrature 下一次东方照,取 date 所在当地民用日 / next eastern quadrature, on the local civil day of date. +func NextEasternQuadrature(date time.Time) time.Time { + return defaultModule.NextEasternQuadrature(date) +} + +// NextGalileanPhenomenonContactEvent 下一次 IMCCE 风格接触事件 / next IMCCE-style contact event. +func NextGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { + return defaultModule.NextGalileanPhenomenonContactEvent(date, satellite, phenomenonType) +} + +// NextGalileanPhenomenonEvent 下一次伽利略卫星现象 / next Galilean-satellite event. +func NextGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { + return defaultModule.NextGalileanPhenomenonEvent(date, satellite, phenomenonType) +} + +// NextOpposition 下一次冲日,取 date 所在当地民用日 / next opposition, on the local civil day of date. +func NextOpposition(date time.Time) time.Time { return defaultModule.NextOpposition(date) } + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextWesternQuadrature 下一次西方照,取 date 所在当地民用日 / next western quadrature, on the local civil day of date. +func NextWesternQuadrature(date time.Time) time.Time { + return defaultModule.NextWesternQuadrature(date) +} + +// ParallacticAngle 木星视差角(天顶方向角) / Jupiter parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 木星视差角(天顶方向角)(n 变体) / n-parameterised Jupiter parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 木星物理观测参数 / physical observing parameters of Jupiter. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 木星物理观测参数(n 变体) / n-parameterised physical observing parameters of Jupiter. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// SatellitePhenomena 木星四颗伽利略卫星瞬时现象,内部换算为 TT/TDB 历元求值 / instantaneous phenomena of Jupiter's four Galilean satellites, converted internally to the TT/TDB epoch. +func SatellitePhenomena(date time.Time) GalileanPhenomenaInfo { + return defaultModule.SatellitePhenomena(date) +} + +// Satellites 木星四颗伽利略卫星视位置,内部换算为 TT/TDB 历元求值 / apparent positions of Jupiter's four Galilean satellites, converted internally to the TT/TDB epoch. +func Satellites(date time.Time) GalileanSatellitesInfo { return defaultModule.Satellites(date) } + +// Semidiameter 木星视半径,单位角秒 / apparent Jupiter semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 木星视半径(n 变体),单位角秒 / n-parameterised apparent Jupiter semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/jupiter/context.go b/jupiter/context.go new file mode 100644 index 0000000..887fce8 --- /dev/null +++ b/jupiter/context.go @@ -0,0 +1,13 @@ +package jupiter + +import ( + "b612.me/astro/internal/ephem" +) + +// Jupiter 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Jupiter struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Jupiter { return &Jupiter{inst: inst} } + +var defaultModule Jupiter diff --git a/jupiter/context_test.go b/jupiter/context_test.go new file mode 100644 index 0000000..a2455b6 --- /dev/null +++ b/jupiter/context_test.go @@ -0,0 +1,102 @@ +package jupiter + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + CentralMeridians(date time.Time) CentralMeridianInfo + CentralMeridiansN(date time.Time, n int) CentralMeridianInfo + ClosestGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent + ClosestGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastEasternQuadrature(date time.Time) time.Time + LastGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent + LastGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent + LastOpposition(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastWesternQuadrature(date time.Time) time.Time + NextConjunction(date time.Time) time.Time + NextEasternQuadrature(date time.Time) time.Time + NextGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent + NextGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent + NextOpposition(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextWesternQuadrature(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SatellitePhenomena(date time.Time) GalileanPhenomenaInfo + Satellites(date time.Time) GalileanSatellitesInfo + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Jupiter)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/jupiter/diameter.go b/jupiter/diameter.go index c94f370..01e8bde 100644 --- a/jupiter/diameter.go +++ b/jupiter/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 木星视半径,单位角秒 / apparent Jupiter semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Jupiter) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 木星视半径(截断版),单位角秒 / truncated apparent Jupiter semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 木星视半径(n 变体),单位角秒 / n-parameterised apparent Jupiter semidiameter in arcseconds. +func (module *Jupiter) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterSemidiameterN(basic.UTC2TT(jd), n) + return basic.JupiterSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 木星视直径,单位角秒 / apparent Jupiter diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Jupiter) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 木星视直径(截断版),单位角秒 / truncated apparent Jupiter diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 木星视直径(n 变体),单位角秒 / n-parameterised apparent Jupiter diameter in arcseconds. +func (module *Jupiter) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterDiameterN(basic.UTC2TT(jd), n) + return basic.JupiterDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/jupiter/doc.go b/jupiter/doc.go index 52e669c..d638928 100644 --- a/jupiter/doc.go +++ b/jupiter/doc.go @@ -1,3 +1,7 @@ // Package jupiter 木星位置、升落、合冲、留、方照、相位、视直径、物理星历,以及四颗伽利略卫星的位置、现象与接触事件;角度单位为度。 // Package jupiter covers Jupiter position, rise/set, conjunction, opposition, station, quadrature, phase, diameter and physical ephemeris, plus Galilean-satellite positions, phenomena and contact events; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package jupiter diff --git a/jupiter/jupiter.go b/jupiter/jupiter.go index 7fb739c..c5bbd71 100644 --- a/jupiter/jupiter.go +++ b/jupiter/jupiter.go @@ -1,11 +1,11 @@ package jupiter import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回木星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Jupiter at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Jupiter) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentLo(basic.UTC2TT(jd)) + return basic.JupiterApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回木星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Jupiter at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Jupiter) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentBo(basic.UTC2TT(jd)) + return basic.JupiterApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回木星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Jupiter at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Jupiter) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentRa(basic.UTC2TT(jd)) + return basic.JupiterApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回木星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Jupiter at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Jupiter) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentDec(basic.UTC2TT(jd)) + return basic.JupiterApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回木星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Jupiter at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Jupiter) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentRaDec(basic.UTC2TT(jd)) + return basic.JupiterApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回木星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Jupiter at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Jupiter) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterMag(basic.UTC2TT(jd)) + return basic.JupiterMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回木星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Jupiter to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Jupiter) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthJupiterAway(basic.UTC2TT(jd)) + return basic.EarthJupiterAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回木星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Jupiter to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Jupiter) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(4, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(4, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Jupiter) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterHeight(localJD, lon, lat, timezone) + return basic.JupiterHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Jupiter) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Jupiter) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterAzimuth(localJD, lon, lat, timezone) + return basic.JupiterAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Jupiter) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterHourAngle(localJD, lon, timezone) + return basic.JupiterHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Jupiter) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.JupiterCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.JupiterCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Jupiter) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.JupiterRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.JupiterRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Jupiter) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Jupiter) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,114 +146,84 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.JupiterSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.JupiterSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Jupiter) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Jupiter) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastOpposition 上一次冲日 / previous opposition. -// -// 返回 date 当前或之前最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or before date, keeping date's time zone. -func LastOpposition(date time.Time) time.Time { +func (module *Jupiter) LastOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterOppositionWithInstance(jde, module.inst), date.Location(), false) } // NextOpposition 下一次冲日 / next opposition. -// -// 返回 date 当前或之后最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or after date, keeping date's time zone. -func NextOpposition(date time.Time) time.Time { +func (module *Jupiter) NextOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterOppositionWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Jupiter) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Jupiter) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Jupiter) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Jupiter) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // LastEasternQuadrature 上一次东方照 / previous eastern quadrature. -// -// 返回 date 当前或之前最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or before date, keeping date's time zone. -func LastEasternQuadrature(date time.Time) time.Time { +func (module *Jupiter) LastEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextEasternQuadrature 下一次东方照 / next eastern quadrature. -// -// 返回 date 当前或之后最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or after date, keeping date's time zone. -func NextEasternQuadrature(date time.Time) time.Time { +func (module *Jupiter) NextEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // LastWesternQuadrature 上一次西方照 / previous western quadrature. -// -// 返回 date 当前或之前最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or before date, keeping date's time zone. -func LastWesternQuadrature(date time.Time) time.Time { +func (module *Jupiter) LastWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastJupiterWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastJupiterWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextWesternQuadrature 下一次西方照 / next western quadrature. -// -// 返回 date 当前或之后最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or after date, keeping date's time zone. -func NextWesternQuadrature(date time.Time) time.Time { +func (module *Jupiter) NextWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextJupiterWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextJupiterWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Jupiter) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.JupiterMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/jupiter/nodes.go b/jupiter/nodes.go index e75d517..5d0444a 100644 --- a/jupiter/nodes.go +++ b/jupiter/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 木星升交点黄经 / ascending node longitude of Jupiter. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Jupiter) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 木星升交点黄经(截断版) / truncated ascending node longitude of Jupiter. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 木星升交点黄经(n 变体) / n-parameterised ascending node longitude of Jupiter. +func (module *Jupiter) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterAscendingNodeN(basic.UTC2TT(jd), n) + return basic.JupiterAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 木星降交点黄经 / descending node longitude of Jupiter. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Jupiter) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 木星降交点黄经(截断版) / truncated descending node longitude of Jupiter. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 木星降交点黄经(n 变体) / n-parameterised descending node longitude of Jupiter. +func (module *Jupiter) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterDescendingNodeN(basic.UTC2TT(jd), n) + return basic.JupiterDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/jupiter/parallactic.go b/jupiter/parallactic.go index 3b212c0..dc30839 100644 --- a/jupiter/parallactic.go +++ b/jupiter/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 木星视差角(天顶方向角) / Jupiter parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Jupiter) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项木星视差角(天顶方向角) / truncated Jupiter parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 木星视差角(天顶方向角)(n 变体) / n-parameterised Jupiter parallactic angle. +func (module *Jupiter) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/jupiter/phase.go b/jupiter/phase.go index 30590ef..a4a515d 100644 --- a/jupiter/phase.go +++ b/jupiter/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Jupiter) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.JupiterPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Jupiter) PhaseAngleN(date time.Time, n int) float64 { + return basic.JupiterPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Jupiter) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.JupiterIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Jupiter) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.JupiterIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Jupiter) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Jupiter) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Jupiter) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.JupiterBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Jupiter) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.JupiterBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/jupiter/phenomena.go b/jupiter/phenomena.go index 504894b..97a75af 100644 --- a/jupiter/phenomena.go +++ b/jupiter/phenomena.go @@ -34,12 +34,9 @@ type GalileanPhenomenaInfo struct { } // SatellitePhenomena 木星四颗伽利略卫星瞬时现象 / instantaneous phenomena of Jupiter's four Galilean satellites. -// -// date 表示观测绝对时刻(UTC),内部换算为该时刻对应的 TT/TDB 历元求值。 -// date is the observing instant in UTC; it is converted to the corresponding TT/TDB epoch for evaluation. -func SatellitePhenomena(date time.Time) GalileanPhenomenaInfo { +func (module *Jupiter) SatellitePhenomena(date time.Time) GalileanPhenomenaInfo { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - phenomena := basic.JupiterGalileanSatellitePhenomena(jde) + phenomena := basic.JupiterGalileanSatellitePhenomenaWithInstance(jde, module.inst) return GalileanPhenomenaInfo{ Io: galileanPhenomenonFromBasic(phenomena[0]), Europa: galileanPhenomenonFromBasic(phenomena[1]), diff --git a/jupiter/phenomena_contact_events.go b/jupiter/phenomena_contact_events.go index fffb2bf..d4c01a6 100644 --- a/jupiter/phenomena_contact_events.go +++ b/jupiter/phenomena_contact_events.go @@ -56,36 +56,36 @@ type GalileanPhenomenonContactEvent struct { } // LastGalileanPhenomenonContactEvent 上一次 IMCCE 风格接触事件 / previous IMCCE-style contact event. -func LastGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { +func (module *Jupiter) LastGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { return galileanPhenomenonContactEventFromBasic( - basic.LastJupiterGalileanPhenomenonContactEvent( + basic.LastJupiterGalileanPhenomenonContactEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) } // NextGalileanPhenomenonContactEvent 下一次 IMCCE 风格接触事件 / next IMCCE-style contact event. -func NextGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { +func (module *Jupiter) NextGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { return galileanPhenomenonContactEventFromBasic( - basic.NextJupiterGalileanPhenomenonContactEvent( + basic.NextJupiterGalileanPhenomenonContactEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) } // ClosestGalileanPhenomenonContactEvent 最近一次 IMCCE 风格接触事件 / closest IMCCE-style contact event. -func ClosestGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { +func (module *Jupiter) ClosestGalileanPhenomenonContactEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonContactEvent { return galileanPhenomenonContactEventFromBasic( - basic.ClosestJupiterGalileanPhenomenonContactEvent( + basic.ClosestJupiterGalileanPhenomenonContactEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) diff --git a/jupiter/phenomena_events.go b/jupiter/phenomena_events.go index a31324e..4d13e5c 100644 --- a/jupiter/phenomena_events.go +++ b/jupiter/phenomena_events.go @@ -55,41 +55,36 @@ type GalileanPhenomenonEvent struct { } // LastGalileanPhenomenonEvent 上一次伽利略卫星现象 / previous Galilean-satellite event. -// -// date 表示查询绝对时刻;satellite 取 `1=Io, 2=Europa, 3=Ganymede, 4=Callisto`; -// phenomenonType 取 `transit/occultation/eclipse/shadow_transit` 中之一。 -// date is the query instant; satellite is `1=Io, 2=Europa, 3=Ganymede, 4=Callisto`; -// phenomenonType is one of `transit/occultation/eclipse/shadow_transit`. -func LastGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { +func (module *Jupiter) LastGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { return galileanPhenomenonEventFromBasic( - basic.LastJupiterGalileanPhenomenonEvent( + basic.LastJupiterGalileanPhenomenonEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) } // NextGalileanPhenomenonEvent 下一次伽利略卫星现象 / next Galilean-satellite event. -func NextGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { +func (module *Jupiter) NextGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { return galileanPhenomenonEventFromBasic( - basic.NextJupiterGalileanPhenomenonEvent( + basic.NextJupiterGalileanPhenomenonEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) } // ClosestGalileanPhenomenonEvent 最近一次伽利略卫星现象 / closest Galilean-satellite event. -func ClosestGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { +func (module *Jupiter) ClosestGalileanPhenomenonEvent(date time.Time, satellite int, phenomenonType GalileanPhenomenonType) GalileanPhenomenonEvent { return galileanPhenomenonEventFromBasic( - basic.ClosestJupiterGalileanPhenomenonEvent( + basic.ClosestJupiterGalileanPhenomenonEventWithInstance( basic.Date2JD(date.UTC()), satellite, - basic.JupiterGalileanPhenomenonType(phenomenonType), + basic.JupiterGalileanPhenomenonType(phenomenonType), module.inst, ), date.Location(), ) diff --git a/jupiter/physical.go b/jupiter/physical.go index 8f8d32f..049bc92 100644 --- a/jupiter/physical.go +++ b/jupiter/physical.go @@ -41,17 +41,17 @@ type CentralMeridianInfo struct { } // Physical 木星物理观测参数 / physical observing parameters of Jupiter. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Jupiter) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 木星物理观测参数(截断版) / truncated physical observing parameters of Jupiter. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 木星物理观测参数(n 变体) / n-parameterised physical observing parameters of Jupiter. +func (module *Jupiter) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) jde := basic.UTC2TT(jd) - info := basic.JupiterPhysicalN(jde, n) - meridians := basic.JupiterCentralMeridiansN(jde, n) - ds, de := basic.JupiterDSDEN(jde, n) + info := basic.JupiterPhysicalNWithInstance(jde, n, module.inst) + meridians := basic.JupiterCentralMeridiansNWithInstance(jde, n, module.inst) + ds, de := basic.JupiterDSDENWithInstance(jde, n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, @@ -67,14 +67,14 @@ func PhysicalN(date time.Time, n int) PhysicalInfo { } // CentralMeridians 木星 System I/II/III 中央经线 / Jupiter System I/II/III central meridians. -func CentralMeridians(date time.Time) CentralMeridianInfo { - return CentralMeridiansN(date, -1) +func (module *Jupiter) CentralMeridians(date time.Time) CentralMeridianInfo { + return module.CentralMeridiansN(date, -1) } -// CentralMeridiansN 木星 System I/II/III 中央经线(截断版) / truncated Jupiter System I/II/III central meridians. -func CentralMeridiansN(date time.Time, n int) CentralMeridianInfo { +// CentralMeridiansN 木星 System I/II/III 中央经线(n 变体) / n-parameterised Jupiter System I/II/III central meridians. +func (module *Jupiter) CentralMeridiansN(date time.Time, n int) CentralMeridianInfo { jd := basic.Date2JD(date.UTC()) - info := basic.JupiterCentralMeridiansN(basic.UTC2TT(jd), n) + info := basic.JupiterCentralMeridiansNWithInstance(basic.UTC2TT(jd), n, module.inst) return CentralMeridianInfo{ SystemI: info.SystemI, SystemII: info.SystemII, diff --git a/jupiter/rise_set_atmosphere.go b/jupiter/rise_set_atmosphere.go new file mode 100644 index 0000000..da9cd08 --- /dev/null +++ b/jupiter/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package jupiter + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 木星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Jupiter) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.JupiterRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 木星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Jupiter) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.JupiterSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 木星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 木星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/jupiter/satellites.go b/jupiter/satellites.go index 0ccae7c..ba22ffc 100644 --- a/jupiter/satellites.go +++ b/jupiter/satellites.go @@ -42,12 +42,9 @@ type GalileanSatellitesInfo struct { } // Satellites 木星四颗伽利略卫星视位置 / apparent positions of Jupiter's four Galilean satellites. -// -// date 表示观测绝对时刻(UTC),内部换算为该时刻对应的 TT/TDB 历元做 L1 星历求值。 -// date is the observing instant in UTC; it is converted to the corresponding TT/TDB epoch for the L1 ephemeris evaluation. -func Satellites(date time.Time) GalileanSatellitesInfo { +func (module *Jupiter) Satellites(date time.Time) GalileanSatellitesInfo { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - observations := basic.JupiterGalileanSatelliteObservations(jde) + observations := basic.JupiterGalileanSatelliteObservationsWithInstance(jde, module.inst) return GalileanSatellitesInfo{ Io: galileanSatellitePositionFromBasic(observations[0]), Europa: galileanSatellitePositionFromBasic(observations[1]), diff --git a/jupiter/truncated.go b/jupiter/truncated.go index 8b1ab68..499f481 100644 --- a/jupiter/truncated.go +++ b/jupiter/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Jupiter) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentLoN(basic.UTC2TT(jd), n) + return basic.JupiterApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Jupiter) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentBoN(basic.UTC2TT(jd), n) + return basic.JupiterApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Jupiter) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentRaN(basic.UTC2TT(jd), n) + return basic.JupiterApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Jupiter) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentDecN(basic.UTC2TT(jd), n) + return basic.JupiterApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Jupiter) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterApparentRaDecN(basic.UTC2TT(jd), n) + return basic.JupiterApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Jupiter) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.JupiterMagN(basic.UTC2TT(jd), n) + return basic.JupiterMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Jupiter) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthJupiterAwayN(basic.UTC2TT(jd), n) + return basic.EarthJupiterAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Jupiter) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(4, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(4, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Jupiter) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterHeightN(localJD, lon, lat, timezone, n) + return basic.JupiterHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Jupiter) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Jupiter) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterAzimuthN(localJD, lon, lat, timezone, n) + return basic.JupiterAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Jupiter) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.JupiterHourAngleN(localJD, lon, timezone, n) + return basic.JupiterHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Jupiter) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.JupiterCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.JupiterCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Jupiter) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.JupiterRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.JupiterRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Jupiter) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Jupiter) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.JupiterSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.JupiterSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/kml/kml.go b/kml/kml.go index cc75a0f..9d7e1de 100644 --- a/kml/kml.go +++ b/kml/kml.go @@ -8,6 +8,7 @@ // 默认只有高亮中心带与掩星带填充,其余面只描轮廓,跨半球且互相重叠的重图层可用 Options.SkipRoles 整层丢弃。 // 时间:带 times 的 MultiLineString 拆成逐段 Placemark 并各带 TimeStamp,逐点的 time 属性同理; // 集合带 time_scale=UT1 时先把标签折回 UTC 再写 (KML 时间轴必须是 UTC),原口径记进 ExtendedData。 +// 输入宽进:越界经度按 360° 回绕,真正跨反经线的面在 ±180 处切开;本库 geojson 的输出侧则严格拒绝越界经度。 // Layers and colors are dispatched by each feature's role property, and times become KML TimeStamps. package kml @@ -52,12 +53,24 @@ const ( penumbraMoonsetColor = "ffb57fab" penumbraMoonriseFill = "4dcc8874" penumbraMoonsetFill = "4db57fab" + // 凌日分区与等时线沿用凌日 SVG 的配色。 + transitEntireColor = "ff6d845e" + transitEntireFill = "576d845e" + transitSunriseColor = "ffa09d4e" + transitSunriseFill = "57a09d4e" + transitSunsetColor = "ff4baae2" + transitSunsetFill = "574baae2" + transitC1Color = "ff2f39a3" + transitC2Color = "ff2a70c0" + transitC3Color = "ffa04c7a" + transitC4Color = "ffa35e2f" ) const ( eventSolarEclipse = "solar-eclipse" eventLunarEclipse = "lunar-eclipse" eventLunarOccultation = "lunar-occultation" + eventPlanetTransit = "planet-transit" ) // Style 是一个 KML 样式;零值字段使用内置调色板。颜色为 KML 的 aabbggrr 十六进制(ff0000ff 是不透明红)。 @@ -423,7 +436,7 @@ func buildDocument(collection *rawCollection, options Options) (*document, error built.ExtendedData = &extendedData{Data: sortedDataEntries(documentEntries)} } built.Name = documentName(options, kept, earliest) - built.Description = descriptionText(options, language, collection.TimeScale) + built.Description = descriptionText(options, language, collection.TimeScale, eventCount) if !options.NoLookAt { built.LookAt = lookAtFor(lookAtElements(lookAtGroups)) } @@ -682,7 +695,11 @@ func decodeGeometry(raw rawGeometry) ([]element, error) { if err := json.Unmarshal(raw.Coordinates, &rings); err != nil { return nil, fmt.Errorf("Polygon coordinates: %w", err) } - return []element{{kind: "Polygon", rings: rings}}, nil + elements := []element{} + for _, piece := range splitAntimeridianPolygon(rings) { + elements = append(elements, element{kind: "Polygon", rings: piece}) + } + return elements, nil case "MultiPolygon": var polygons [][][][]float64 if err := json.Unmarshal(raw.Coordinates, &polygons); err != nil { @@ -690,7 +707,9 @@ func decodeGeometry(raw rawGeometry) ([]element, error) { } elements := make([]element, 0, len(polygons)) for _, rings := range polygons { - elements = append(elements, element{kind: "Polygon", rings: rings}) + for _, piece := range splitAntimeridianPolygon(rings) { + elements = append(elements, element{kind: "Polygon", rings: piece}) + } } return elements, nil case "GeometryCollection": @@ -936,6 +955,157 @@ func isSeamSegment(from, to []float64) bool { return math.Abs(math.Abs(from[0])-180) < tolerance && math.Abs(math.Abs(to[0])-180) < tolerance } +// splitAntimeridianPolygon 把真正跨过反经线(相邻顶点经度差超过 180°)的面在 ±180 处切成东西两块; +// 已按反经线拆好的面、包含极点的环和坐标非法的面原样返回,交给后续校验。 +func splitAntimeridianPolygon(rings [][][]float64) [][][][]float64 { + unchanged := [][][][]float64{rings} + if len(rings) == 0 { + return unchanged + } + unwrapped := make([][][]float64, 0, len(rings)) + crosses := false + for _, ring := range rings { + values, jumped, ok := unwrapRing(ring) + if !ok { + return unchanged + } + crosses = crosses || jumped + unwrapped = append(unwrapped, values) + } + if !crosses { + return unchanged + } + outerMin, outerMax := longitudeRange(unwrapped[0]) + cut := 180.0 + if outerMax <= 180 { + if outerMin >= -180 { + return unchanged + } + cut = -180 + } + center := (outerMin + outerMax) / 2 + for index := 1; index < len(unwrapped); index++ { + low, high := longitudeRange(unwrapped[index]) + shift := 360 * math.Round((center-(low+high)/2)/360) + for _, position := range unwrapped[index] { + position[0] += shift + } + } + // 落在 [-180,180] 之外的一侧平移 360° 回到合法区间。 + offsets := map[bool]float64{true: 0, false: -360} + if cut == -180 { + offsets = map[bool]float64{true: 360, false: 0} + } + pieces := [][][][]float64{} + for _, keepWest := range []bool{true, false} { + offset := offsets[keepWest] + piece := [][][]float64{} + for index, ring := range unwrapped { + clipped := clipRingAtLongitude(ring, cut, keepWest, offset) + if len(clipped) < 4 { + if index == 0 { + piece = nil + break + } + continue + } + piece = append(piece, clipped) + } + if len(piece) > 0 { + pieces = append(pieces, piece) + } + } + if len(pieces) == 0 { + return unchanged + } + return pieces +} + +// unwrapRing 把环的经度展开成连续序列;闭合后累计漂移不为零(环绕极点)时 ok=false。 +func unwrapRing(ring [][]float64) (values [][]float64, jumped, ok bool) { + if len(ring) < 2 { + return nil, false, false + } + values = make([][]float64, 0, len(ring)) + for index, position := range ring { + if len(position) < 2 || math.IsNaN(position[0]) || math.IsInf(position[0], 0) || + math.IsNaN(position[1]) || math.IsInf(position[1], 0) { + return nil, false, false + } + longitude := position[0] + if longitude > 180 || longitude < -180 { + longitude = math.Mod(longitude+180, 360) + if longitude < 0 { + longitude += 360 + } + longitude -= 180 + } + if index > 0 { + previous := values[index-1][0] + for longitude-previous > 180 { + longitude -= 360 + jumped = true + } + for previous-longitude > 180 { + longitude += 360 + jumped = true + } + } + values = append(values, []float64{longitude, position[1]}) + } + // 闭合边展开后若回不到起点经度,说明环绕了极点,无法按经线切开。 + first, last := values[0][0], values[len(values)-1][0] + closing := first + for closing-last > 180 { + closing -= 360 + } + for last-closing > 180 { + closing += 360 + } + if math.Abs(closing-first) > 1e-9 { + return nil, false, false + } + return values, jumped, true +} + +func longitudeRange(ring [][]float64) (float64, float64) { + low, high := math.Inf(1), math.Inf(-1) + for _, position := range ring { + low = math.Min(low, position[0]) + high = math.Max(high, position[0]) + } + return low, high +} + +// clipRingAtLongitude 用经线 cut 截取环的西侧或东侧(半平面裁剪),结果平移 offset 并闭合。 +func clipRingAtLongitude(ring [][]float64, cut float64, keepWest bool, offset float64) [][]float64 { + inside := func(position []float64) bool { + if keepWest { + return position[0] <= cut + } + return position[0] >= cut + } + points := ring + if len(points) > 1 && equalPosition(points[0], points[len(points)-1]) { + points = points[:len(points)-1] + } + clipped := [][]float64{} + for index, current := range points { + previous := points[(index+len(points)-1)%len(points)] + if inside(current) != inside(previous) { + fraction := (cut - previous[0]) / (current[0] - previous[0]) + clipped = append(clipped, []float64{cut + offset, previous[1] + (current[1]-previous[1])*fraction}) + } + if inside(current) { + clipped = append(clipped, []float64{current[0] + offset, current[1]}) + } + } + if len(clipped) == 0 { + return nil + } + return append(clipped, append([]float64(nil), clipped[0]...)) +} + func polygonFor(rings [][][]float64) (polygon, error) { if len(rings) == 0 { return polygon{}, fmt.Errorf("polygon has no rings") @@ -1214,6 +1384,22 @@ func paletteStyle(event, role string) Style { "visible-at-p1", "visible-at-p4", "visible-during-eclipse", "visible-throughout-eclipse": return Style{LineColor: ColorOrange, LineWidth: 2} + case "c1-horizon", "c4-horizon", "visible-at-c1", "visible-at-c4": + return Style{LineColor: ColorOrange, LineWidth: 2} + case "transit-entire": + return Style{LineColor: transitEntireColor, FillColor: transitEntireFill, LineWidth: 1.5} + case "transit-at-sunrise": + return Style{LineColor: transitSunriseColor, FillColor: transitSunriseFill, LineWidth: 1.5} + case "transit-at-sunset": + return Style{LineColor: transitSunsetColor, FillColor: transitSunsetFill, LineWidth: 1.5} + case "c1-isochrone": + return Style{LineColor: transitC1Color, LineWidth: 1} + case "c2-isochrone": + return Style{LineColor: transitC2Color, LineWidth: 1} + case "c3-isochrone": + return Style{LineColor: transitC3Color, LineWidth: 1} + case "c4-isochrone": + return Style{LineColor: transitC4Color, LineWidth: 1} case "penumbra-moonrise": return Style{LineColor: penumbraMoonriseColor, FillColor: penumbraMoonriseFill, LineWidth: 2} case "penumbra-moonset": @@ -1374,15 +1560,27 @@ func documentName(options Options, features []preparedFeature, earliest time.Tim return name } -func descriptionText(options Options, language, timeScale string) string { +func descriptionText(options Options, language, timeScale string, events map[string]bool) string { + transitOnly := len(events) == 1 && events[eventPlanetTransit] + transitMixed := events[eventPlanetTransit] && !transitOnly if language == "en" { text := "Converted from GeoJSON. Layers are grouped by role; red: solar central band; black: center line; green: solar greatest-time isochrone; yellow: magnitude contours and occultation total/partial bands; orange: rise/set phase lines." + if transitOnly { + text = "Converted from GeoJSON. Layers are grouped by role; transit regions, C1/C4 apparent horizons, C1-C4 topocentric isochrones and greatest transit are kept in separate layers." + } else if transitMixed { + text = "Converted from GeoJSON. Layers are grouped by event and role." + } if timeScale != "" { text += " Times were written in UTC (original scale: " + timeScale + ")." } return text } text := "由 GeoJSON 转换而来。图层按 role 分组;红:日食中心带;黑:中心线;绿:日食食甚等时线;黄:食分线与掩星全掩/半掩带;橙:日出日落阶段线(初亏/食甚/复圆)。" + if transitOnly { + text = "由 GeoJSON 转换而来。图层按 role 分组;凌日可见分区、一触/四触视地平线、C1–C4 站心等时线与凌甚点分别成层。" + } else if transitMixed { + text = "由 GeoJSON 转换而来。图层按事件和 role 分组。" + } if timeScale != "" { text += "时间已按 UTC 写入(原口径:" + timeScale + ")。" } @@ -1425,9 +1623,27 @@ var roleNames = map[string][2]string{ "time-marker": {"时间标记", "Time markers"}, "besselian-critical-envelope": {"贝塞尔临界包络", "Besselian critical envelope"}, "sublunar-track": {"月下点轨迹", "Sublunar track"}, + "subsolar-track": {"太阳直射点轨迹", "Subsolar track"}, + "c1-horizon": {"一触视地平线", "C1 apparent horizon"}, + "c4-horizon": {"四触视地平线", "C4 apparent horizon"}, + "visible-at-c1": {"一触可见区", "Visible at C1"}, + "visible-at-c4": {"四触可见区", "Visible at C4"}, + "transit-entire": {"全程可见", "Entire transit visible"}, + "transit-at-sunrise": {"日出时已在凌日", "Transit in progress at sunrise"}, + "transit-at-sunset": {"日落时仍在凌日", "Transit in progress at sunset"}, + "c1-isochrone": {"一触等时线", "C1 isochrones"}, + "c2-isochrone": {"二触等时线", "C2 isochrones"}, + "c3-isochrone": {"三触等时线", "C3 isochrones"}, + "c4-isochrone": {"四触等时线", "C4 isochrones"}, } func localizedRole(event, role, language string) string { + if role == "greatest" && event == eventPlanetTransit { + if language == "en" { + return "Greatest transit" + } + return "凌甚" + } if language == "en" { if name, ok := roleNames[role]; ok { return name[1] @@ -1466,6 +1682,11 @@ func localizedEvent(event, language string) string { return "Lunar occultation" } return "月掩" + case eventPlanetTransit: + if language == "en" { + return "Planetary transit" + } + return "凌日" } if event == "" { if language == "en" { diff --git a/kml/kml_test.go b/kml/kml_test.go index 793faee..97d8c50 100644 --- a/kml/kml_test.go +++ b/kml/kml_test.go @@ -1219,3 +1219,32 @@ func TestLunarPenumbraBandFeaturesCarryNames(t *testing.T) { } } } + +// 契约:第三方输入里真正跨过反经线的面在 ±180 处切成两块,填充环里不得出现跨 180° 的边。 +func TestCrossingPolygonIsSplitAtAntimeridian(t *testing.T) { + inputs := []string{ + `{"type":"Polygon","coordinates":[[[179,10],[-179,10],[-179,12],[179,12],[179,10]]]}`, + `{"type":"Polygon","coordinates":[[[-179,10],[179,10],[179,12],[-179,12],[-179,10]]]}`, + `{"type":"MultiPolygon","coordinates":[[[[170,0],[-170,0],[-170,20],[170,20],[170,0]],[[175,5],[175,15],[-175,15],[-175,5],[175,5]]]]}`, + } + for _, input := range inputs { + root := convert(t, collection(feature("solar-eclipse", "central-band", input, "")), Options{FillContext: true}) + polygons := 0 + for _, item := range placemarksOfRole(root, "central-band") { + for _, coordinates := range coordinatesOf(item.geometry) { + positions := splitPositions(coordinates) + for index := 1; index < len(positions); index++ { + if math.Abs(positions[index][0]-positions[index-1][0]) > 180 { + t.Fatalf("%s: edge %v → %v spans the globe", input, positions[index-1], positions[index]) + } + } + } + if item.MultiGeometry != nil { + polygons += len(item.MultiGeometry.Polygons) + } + } + if polygons != 2 { + t.Fatalf("%s: %d polygons, want 2", input, polygons) + } + } +} diff --git a/kml/transit_test.go b/kml/transit_test.go new file mode 100644 index 0000000..771d3f9 --- /dev/null +++ b/kml/transit_test.go @@ -0,0 +1,43 @@ +package kml + +import ( + "strings" + "testing" + "time" + + "b612.me/astro/geojson" + "b612.me/astro/mercury" +) + +// 凌日 GeoJSON 转 KML:图层名本地化、分区带填充、等时线逐段带 TimeStamp。 +func TestPlanetTransitConversion(t *testing.T) { + info := mercury.ClosestTransit(time.Date(2032, 11, 13, 0, 0, 0, 0, time.UTC)) + data, err := geojson.MarshalMercuryTransit(info, geojson.PlanetTransitOptions{IsochroneStep: time.Minute}) + if err != nil { + t.Fatal(err) + } + root := convert(t, string(data), Options{}) + if !strings.HasPrefix(root.Document.Name, "凌日 2032-11-13") { + t.Fatalf("Document 名 = %q", root.Document.Name) + } + if strings.Contains(root.Document.Description, "日食") || !strings.Contains(root.Document.Description, "凌日") { + t.Fatalf("凌日说明不匹配:%q", root.Document.Description) + } + folders := map[string]folder{} + for _, item := range root.Document.Folders { + folders[item.Name] = item + } + for _, name := range []string{"全程可见", "日出时已在凌日", "日落时仍在凌日", "一触视地平线", "二触等时线", "四触等时线", "凌甚"} { + if _, ok := folders[name]; !ok { + t.Fatalf("缺少图层 %s", name) + } + } + for _, placemark := range folders["二触等时线"].Placemarks { + if placemark.TimeStamp == nil { + t.Fatal("等时线 Placemark 必须带 TimeStamp") + } + } + if style := paletteStyle(eventPlanetTransit, "transit-entire"); style.FillColor != transitEntireFill { + t.Fatalf("全程可见区填充 = %q", style.FillColor) + } +} diff --git a/lite/internal/common.go b/lite/internal/common.go index 6678c30..0134e85 100644 --- a/lite/internal/common.go +++ b/lite/internal/common.go @@ -5,8 +5,6 @@ import ( "math" . "b612.me/astro/tools" - - "b612.me/astro/basic" ) const ( @@ -40,7 +38,7 @@ func EclipticToEquatorial(jde, lo, bo float64) (float64, float64) { } func HorizontalCoordinates(ra, dec, jd, lon, lat float64) (float64, float64, float64) { - lst := Limit360(MeanSiderealTime(basic.UTC2UT1(jd)) + lon) + lst := Limit360(MeanSiderealTime(UTC2UT1(jd)) + lon) hourAngle := Limit360(lst - ra) altitude := ArcSin(clampUnit(Sin(lat)*Sin(dec) + Cos(lat)*Cos(dec)*Cos(hourAngle))) @@ -65,7 +63,7 @@ func TopocentricRaDec(ra, dec, observerLat, observerLon, jd, distanceEarthRadii, rhoCos := math.Cos(u) + heightMeters/6378140.0*Cos(observerLat) parallax := math.Asin(1.0 / distanceEarthRadii) - hourAngle := (Limit360(MeanSiderealTime(basic.UTC2UT1(jd)) + observerLon - ra)) * math.Pi / 180.0 + hourAngle := (Limit360(MeanSiderealTime(UTC2UT1(jd)) + observerLon - ra)) * math.Pi / 180.0 decRad := dec * math.Pi / 180.0 numerator := -rhoCos * math.Sin(parallax) * math.Sin(hourAngle) diff --git a/lite/internal/horizon.go b/lite/internal/horizon.go new file mode 100644 index 0000000..d063a75 --- /dev/null +++ b/lite/internal/horizon.go @@ -0,0 +1,28 @@ +package internal + +import "math" + +const ( + earthEquatorialRadius = 6378137.0 + earthPolarRadius = 6356752.3 +) + +// HeightDegreeByLat 高度对应地平线下角 / horizon dip at latitude. +func HeightDegreeByLat(height, lat float64) float64 { + radius := geocentricRadius(lat) + return math.Acos(radius/(radius+height)) * 180 / math.Pi +} + +func geocentricRadius(lat float64) float64 { + cosLat := math.Cos(lat * math.Pi / 180) + sinLat := math.Sin(lat * math.Pi / 180) + a := earthEquatorialRadius * earthEquatorialRadius * cosLat + a *= a + b := earthPolarRadius * earthPolarRadius * sinLat + b *= b + c := earthEquatorialRadius * cosLat + c *= c + d := earthPolarRadius * sinLat + d *= d + return math.Sqrt((a + b) / (c + d)) +} diff --git a/lite/internal/horizon_test.go b/lite/internal/horizon_test.go new file mode 100644 index 0000000..54f9a00 --- /dev/null +++ b/lite/internal/horizon_test.go @@ -0,0 +1,18 @@ +package internal + +import ( + "math" + "testing" + + "b612.me/astro/basic" +) + +func TestHeightDegreeByLatTakesDegrees(t *testing.T) { + for _, lat := range []float64{0, 30, 60, -45, 89} { + got := HeightDegreeByLat(10000, lat) + want := basic.HeightDegreeByLat(10000, lat) + if math.Abs(got-want) > 1e-9 { + t.Fatalf("lat %v: lite %.9f, basic %.9f", lat, got, want) + } + } +} diff --git a/lite/internal/refraction.go b/lite/internal/refraction.go new file mode 100644 index 0000000..b18ac02 --- /dev/null +++ b/lite/internal/refraction.go @@ -0,0 +1,25 @@ +package internal + +import "math" + +const ( + refractionStandardPressureHPa = 1010.0 + refractionStandardTemperatureK = 283.15 + refractionAbsoluteZeroC = -273.15 + refractionLowerLimitAltitude = -5.0 + refractionUpperLimitAltitude = 90.0 +) + +// RefractionFromTrueAltitude 真高度到视高度的折射修正 / true-to-apparent altitude refraction. +func RefractionFromTrueAltitude(trueAltitude, pressureHPa, temperatureC float64) float64 { + if !validRefractionInputs(trueAltitude, pressureHPa, temperatureC) || trueAltitude <= refractionLowerLimitAltitude || trueAltitude >= refractionUpperLimitAltitude { + return 0 + } + angle := (trueAltitude + 10.3/(trueAltitude+5.11)) * math.Pi / 180 + scale := pressureHPa / refractionStandardPressureHPa * refractionStandardTemperatureK / (temperatureC - refractionAbsoluteZeroC) + return scale * (1.02 / math.Tan(angle)) / 60 +} + +func validRefractionInputs(altitude, pressureHPa, temperatureC float64) bool { + return !(math.IsNaN(altitude) || math.IsInf(altitude, 0) || math.IsNaN(pressureHPa) || math.IsInf(pressureHPa, 0) || pressureHPa <= 0 || math.IsNaN(temperatureC) || math.IsInf(temperatureC, 0) || temperatureC <= refractionAbsoluteZeroC) +} diff --git a/lite/internal/time.go b/lite/internal/time.go new file mode 100644 index 0000000..a1e165e --- /dev/null +++ b/lite/internal/time.go @@ -0,0 +1,114 @@ +package internal + +import ( + "errors" + "math" + "time" +) + +// ErrInvalidCivilDate 日期非法 / invalid civil date. +var ErrInvalidCivilDate = errors.New("invalid civil date") + +// Date2JD 日期转儒略日 / Julian day from a civil time. +func Date2JD(date time.Time) float64 { + day := float64(date.Day()) + float64(date.Hour())/24.0 + float64(date.Minute())/1440.0 + float64(date.Second())/86400.0 + float64(date.Nanosecond())/86400000000000.0 + return JDCalc(date.Year(), int(date.Month()), day) +} + +// JDCalc 由公历年月日计算儒略日 / Julian day from a civil date. +func JDCalc(year, month int, day float64) float64 { + if !validCivilDate(year, month, day) { + return math.NaN() + } + effectiveYear, effectiveMonth, effectiveDay := year, month, int(math.Floor(day)) + if month == 1 || month == 2 { + year-- + month += 12 + } + correction := 0 + if effectiveYear > 1582 || (effectiveYear == 1582 && (effectiveMonth > 10 || (effectiveMonth == 10 && effectiveDay >= 15))) { + century := int(year / 100) + correction = 2 - century + int(century/4) + } + return math.Floor(365.25*(float64(year)+4716.0)) + math.Floor(30.6001*float64(month+1)) + day + float64(correction) - 1524.5 +} + +func validCivilDate(year, month int, day float64) bool { + if math.IsNaN(day) || math.IsInf(day, 0) || month < 1 || month > 12 || day < 1 { + return false + } + dayInt := int(math.Floor(day)) + if dayInt > daysInCivilMonth(year, month) { + return false + } + return !(year == 1582 && month == 10 && day >= 5 && day < 15) +} + +func daysInCivilMonth(year, month int) int { + switch month { + case 1, 3, 5, 7, 8, 10, 12: + return 31 + case 4, 6, 9, 11: + return 30 + case 2: + if isCivilLeapYear(year, month, 1) { + return 29 + } + return 28 + default: + return 0 + } +} + +func isCivilLeapYear(year, month int, day float64) bool { + if year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day <= 4))) { + return year%4 == 0 + } + if year%400 == 0 { + return true + } + if year%100 == 0 { + return false + } + return year%4 == 0 +} + +// JD2DateByZone 儒略日转目标时区日期 / convert a Julian day to a target timezone. +func JD2DateByZone(jd float64, tz *time.Location, byZone bool) time.Time { + jd += 0.5 + z := float64(int(jd)) + fraction := jd - z + a := z + if z >= 2299161.0 { + alpha := math.Floor((z - 1867216.25) / 36524.25) + a = z + 1 + alpha - math.Floor(alpha/4) + } + b := a + 1524 + c := math.Floor((b - 122.1) / 365.25) + d := math.Floor(365.25 * c) + e := math.Floor((b - d) / 30.6001) + days := b - d - math.Floor(30.6001*e) + fraction + month := e - 1 + if e >= 14 { + month = e - 13 + } + year := c - 4716 + if month <= 2 { + year = c - 4715 + } + seconds := (days - math.Floor(days)) * 86400 + day := math.Floor(days) + date := time.Date(int(year), time.Month(int(month)), int(day), 0, 0, 0, 0, time.UTC). + Add(time.Duration(int64(1000000000 * seconds))) + if !byZone { + return date.In(tz) + } + y, m, dayInt := date.Date() + h, minute, second := date.Clock() + return time.Date(y, m, dayInt, h, minute, second, date.Nanosecond(), tz) +} + +// UTC2UT1 轻量民用时转世界时,忽略 DUT1(|UT1−UTC| < 0.9 s) / lightweight UTC to UT1, DUT1 ignored. +func UTC2UT1(jd float64) float64 { + return jd +} diff --git a/lite/internal/time_test.go b/lite/internal/time_test.go new file mode 100644 index 0000000..a06ab09 --- /dev/null +++ b/lite/internal/time_test.go @@ -0,0 +1,39 @@ +package internal + +import ( + "math" + "testing" + "time" +) + +func TestDateJulianRoundTrip(t *testing.T) { + cases := []struct { + date time.Time + jd float64 + }{ + {time.Date(2026, 10, 6, 8, 30, 15, 123000000, time.UTC), 2461319.8543417014}, + {time.Date(1582, 10, 4, 0, 0, 0, 0, time.UTC), 2299159.5}, + {time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC), 2299160.5}, + } + for _, tc := range cases { + if got := Date2JD(tc.date); math.Abs(got-tc.jd) > 1e-9 { + t.Fatalf("Date2JD(%s) = %.12f, want %.12f", tc.date, got, tc.jd) + } + } +} + +func TestJD2DateByZone(t *testing.T) { + zone := time.FixedZone("CST", 8*3600) + got := JD2DateByZone(2461320.5, zone, false) + want := time.Date(2026, 10, 7, 8, 0, 0, 0, zone) + if !got.Equal(want) { + t.Fatalf("JD2DateByZone = %s, want %s", got, want) + } +} + +func TestUTC2UT1IgnoresDUT1(t *testing.T) { + jd := 2461320.5 + if got := UTC2UT1(jd); got != jd { + t.Fatalf("UTC2UT1 = %.12f, want %.12f", got, jd) + } +} diff --git a/lite/moon/moon.go b/lite/moon/moon.go index d625c0f..beae83d 100644 --- a/lite/moon/moon.go +++ b/lite/moon/moon.go @@ -6,7 +6,6 @@ import ( "math" "time" - "b612.me/astro/basic" lite "b612.me/astro/lite/internal" . "b612.me/astro/tools" ) @@ -19,50 +18,50 @@ var ( // TrueLo 轻量真黄经 / lightweight true ecliptic longitude. func TrueLo(date time.Time) float64 { - return lite.MoonGeocentric(basic.Date2JD(date.UTC())).Longitude + return lite.MoonGeocentric(lite.Date2JD(date.UTC())).Longitude } // TrueBo 轻量真黄纬 / lightweight true ecliptic latitude. func TrueBo(date time.Time) float64 { - return lite.MoonGeocentric(basic.Date2JD(date.UTC())).Latitude + return lite.MoonGeocentric(lite.Date2JD(date.UTC())).Latitude } // TrueRa 轻量真赤经 / lightweight true right ascension. func TrueRa(date time.Time) float64 { - return lite.MoonGeocentric(basic.Date2JD(date.UTC())).RightAscension + return lite.MoonGeocentric(lite.Date2JD(date.UTC())).RightAscension } // TrueDec 轻量真赤纬 / lightweight true declination. func TrueDec(date time.Time) float64 { - return lite.MoonGeocentric(basic.Date2JD(date.UTC())).Declination + return lite.MoonGeocentric(lite.Date2JD(date.UTC())).Declination } // TrueRaDec 轻量真赤经、真赤纬 / lightweight true right ascension and declination. func TrueRaDec(date time.Time) (float64, float64) { - state := lite.MoonGeocentric(basic.Date2JD(date.UTC())) + state := lite.MoonGeocentric(lite.Date2JD(date.UTC())) return state.RightAscension, state.Declination } // ApparentRa 轻量站心视赤经 / lightweight topocentric apparent right ascension. func ApparentRa(date time.Time, lon, lat float64) float64 { - state := lite.MoonTopocentric(basic.Date2JD(date.UTC()), lon, lat, 0) + state := lite.MoonTopocentric(lite.Date2JD(date.UTC()), lon, lat, 0) return state.RightAscension } // ApparentDec 轻量站心视赤纬 / lightweight topocentric apparent declination. func ApparentDec(date time.Time, lon, lat float64) float64 { - state := lite.MoonTopocentric(basic.Date2JD(date.UTC()), lon, lat, 0) + state := lite.MoonTopocentric(lite.Date2JD(date.UTC()), lon, lat, 0) return state.Declination } // ApparentRaDec 轻量站心视赤经、视赤纬 / lightweight topocentric apparent right ascension and declination. func ApparentRaDec(date time.Time, lon, lat float64) (float64, float64) { - state := lite.MoonTopocentric(basic.Date2JD(date.UTC()), lon, lat, 0) + state := lite.MoonTopocentric(lite.Date2JD(date.UTC()), lon, lat, 0) return state.RightAscension, state.Declination } func topocentricHorizontal(date time.Time, lon, lat float64) (altitude, azimuth, hourAngle float64) { - jd := basic.Date2JD(date.UTC()) + jd := lite.Date2JD(date.UTC()) state := lite.MoonTopocentric(jd, lon, lat, 0) return lite.HorizontalCoordinates(state.RightAscension, state.Declination, jd, lon, lat) } @@ -93,7 +92,7 @@ func Zenith(date time.Time, lon, lat float64) float64 { // SunMoonLoDiff 轻量日月黄经差 / lightweight Moon-Sun ecliptic-longitude difference. func SunMoonLoDiff(date time.Time) float64 { - jdUTC := basic.Date2JD(date.UTC()) + jdUTC := lite.Date2JD(date.UTC()) return Limit360(lite.MoonGeocentric(jdUTC).Longitude - lite.SunApparentLo(jdUTC)) } @@ -129,11 +128,11 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (time.Time, error) { localMidnight := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) - localJD := basic.Date2JD(localMidnight) + localJD := lite.Date2JD(localMidnight) _, offset := localMidnight.Zone() timezone := float64(offset) / 3600.0 - horizonDip := basic.HeightDegreeByLat(height, lat) + horizonDip := lite.HeightDegreeByLat(height, lat) altitudeFn := func(localJD float64) float64 { utcJD := localJD - timezone/24.0 @@ -141,7 +140,7 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t altitude, _, _ := lite.HorizontalCoordinates(state.RightAscension, state.Declination, utcJD, lon, lat) residual := altitude + horizonDip if aero { - residual += basic.RefractionFromTrueAltitude(altitude, 1010, 10) + residual += lite.RefractionFromTrueAltitude(altitude, 1010, 10) residual += liteMoonSemidiameterDegrees(state.DistanceEarthRadii) } return residual @@ -160,7 +159,7 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t return time.Time{}, err } } - return basic.JD2DateByZone(eventJD-timezone/24, date.Location(), false), nil + return lite.JD2DateByZone(eventJD-timezone/24, date.Location(), false), nil } func liteMoonSemidiameterDegrees(distanceEarthRadii float64) float64 { diff --git a/lite/sun/sun.go b/lite/sun/sun.go index 6a50cb5..ba6daf2 100644 --- a/lite/sun/sun.go +++ b/lite/sun/sun.go @@ -6,7 +6,6 @@ import ( "math" "time" - "b612.me/astro/basic" lite "b612.me/astro/lite/internal" ) @@ -17,68 +16,68 @@ var ( // TrueLo 轻量真黄经 / lightweight true ecliptic longitude. func TrueLo(date time.Time) float64 { - return lite.SunTrueLo(basic.Date2JD(date.UTC())) + return lite.SunTrueLo(lite.Date2JD(date.UTC())) } // ApparentLo 轻量视黄经 / lightweight apparent ecliptic longitude. func ApparentLo(date time.Time) float64 { - return lite.SunApparentLo(basic.Date2JD(date.UTC())) + return lite.SunApparentLo(lite.Date2JD(date.UTC())) } // Distance 轻量日地距离 / lightweight Sun-Earth distance in AU. func Distance(date time.Time) float64 { - return lite.SunDistanceAU(basic.Date2JD(date.UTC())) + return lite.SunDistanceAU(lite.Date2JD(date.UTC())) } // TrueRa 轻量真赤经 / lightweight true right ascension. func TrueRa(date time.Time) float64 { - ra, _ := lite.SunTrueRaDec(basic.Date2JD(date.UTC())) + ra, _ := lite.SunTrueRaDec(lite.Date2JD(date.UTC())) return ra } // TrueDec 轻量真赤纬 / lightweight true declination. func TrueDec(date time.Time) float64 { - _, dec := lite.SunTrueRaDec(basic.Date2JD(date.UTC())) + _, dec := lite.SunTrueRaDec(lite.Date2JD(date.UTC())) return dec } // TrueRaDec 轻量真赤经、真赤纬 / lightweight true right ascension and declination. func TrueRaDec(date time.Time) (float64, float64) { - return lite.SunTrueRaDec(basic.Date2JD(date.UTC())) + return lite.SunTrueRaDec(lite.Date2JD(date.UTC())) } // ApparentRa 轻量视赤经 / lightweight apparent right ascension. func ApparentRa(date time.Time) float64 { - ra, _ := lite.SunApparentRaDec(basic.Date2JD(date.UTC())) + ra, _ := lite.SunApparentRaDec(lite.Date2JD(date.UTC())) return ra } // ApparentDec 轻量视赤纬 / lightweight apparent declination. func ApparentDec(date time.Time) float64 { - _, dec := lite.SunApparentRaDec(basic.Date2JD(date.UTC())) + _, dec := lite.SunApparentRaDec(lite.Date2JD(date.UTC())) return dec } // ApparentRaDec 轻量视赤经、视赤纬 / lightweight apparent right ascension and declination. func ApparentRaDec(date time.Time) (float64, float64) { - return lite.SunApparentRaDec(basic.Date2JD(date.UTC())) + return lite.SunApparentRaDec(lite.Date2JD(date.UTC())) } // HourAngle 轻量时角 / lightweight hour angle. func HourAngle(date time.Time, lon, lat float64) float64 { - _, _, hourAngle := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), basic.Date2JD(date.UTC()), lon, lat) + _, _, hourAngle := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), lite.Date2JD(date.UTC()), lon, lat) return hourAngle } // Azimuth 轻量方位角 / lightweight azimuth. func Azimuth(date time.Time, lon, lat float64) float64 { - _, azimuth, _ := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), basic.Date2JD(date.UTC()), lon, lat) + _, azimuth, _ := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), lite.Date2JD(date.UTC()), lon, lat) return azimuth } // Altitude 轻量高度角 / lightweight altitude. func Altitude(date time.Time, lon, lat float64) float64 { - altitude, _, _ := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), basic.Date2JD(date.UTC()), lon, lat) + altitude, _, _ := lite.HorizontalCoordinates(ApparentRa(date), ApparentDec(date), lite.Date2JD(date.UTC()), lon, lat) return altitude } @@ -89,7 +88,7 @@ func Zenith(date time.Time, lon, lat float64) float64 { // RiseTime 轻量日出时刻 / lightweight sunrise time. func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { + if result, err, handled := civiltime.Event(date, lite.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { return RiseTime(d, lon, lat, height, aero) }); handled { return result, err @@ -99,7 +98,7 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e // SetTime 轻量日落时刻 / lightweight sunset time. func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - if result, err, handled := civiltime.Event(date, basic.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { + if result, err, handled := civiltime.Event(date, lite.ErrNotOnThisDate, func(d time.Time) (time.Time, error) { return SetTime(d, lon, lat, height, aero) }); handled { return result, err @@ -109,11 +108,11 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (time.Time, error) { localMidnight := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) - localJD := basic.Date2JD(localMidnight) + localJD := lite.Date2JD(localMidnight) _, offset := localMidnight.Zone() timezone := float64(offset) / 3600.0 - horizonDip := basic.HeightDegreeByLat(height, lat) + horizonDip := lite.HeightDegreeByLat(height, lat) altitudeFn := func(localJD float64) float64 { utcJD := localJD - timezone/24.0 @@ -121,7 +120,7 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t altitude, _, _ := lite.HorizontalCoordinates(ra, dec, utcJD, lon, lat) residual := altitude + horizonDip if aero { - residual += basic.RefractionFromTrueAltitude(altitude, 1010, 10) + residual += lite.RefractionFromTrueAltitude(altitude, 1010, 10) residual += liteSunSemidiameterDegrees(lite.SunDistanceAU(utcJD)) } return residual @@ -138,7 +137,7 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t return time.Time{}, err } } - return basic.JD2DateByZone(eventJD-timezone/24, date.Location(), false), nil + return lite.JD2DateByZone(eventJD-timezone/24, date.Location(), false), nil } func liteSunSemidiameterDegrees(distanceAU float64) float64 { diff --git a/low_precision_regression_test.go b/low_precision_regression_test.go index 94c909c..0f0508f 100644 --- a/low_precision_regression_test.go +++ b/low_precision_regression_test.go @@ -9,6 +9,8 @@ import ( "b612.me/astro/basic" "b612.me/astro/planet" + + "b612.me/astro/internal/ephemtest" ) type lowPrecisionSunSnapshot struct { @@ -167,6 +169,7 @@ func derivedTruncationCases(samples []lowPrecisionSample) []truncationCase { } func TestDerivedHighPrecisionTruncationConverges(t *testing.T) { + ephemtest.PinLegacy(t) samples := loadLowPrecisionSamples(t) assertTruncationConverges(t, derivedTruncationCases(samples), len(samples)) } diff --git a/mars/api_default.go b/mars/api_default.go new file mode 100644 index 0000000..00669b2 --- /dev/null +++ b/mars/api_default.go @@ -0,0 +1,258 @@ +package mars + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 火星升交点黄经 / ascending node longitude of Mars. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 火星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mars. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 火星降交点黄经 / descending node longitude of Mars. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 火星降交点黄经(n 变体) / n-parameterised descending node longitude of Mars. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 火星视直径,单位角秒 / apparent Mars diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 火星视直径(n 变体),单位角秒 / n-parameterised apparent Mars diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastEasternQuadrature 上一次东方照,取 date 所在当地民用日 / previous eastern quadrature, on the local civil day of date. +func LastEasternQuadrature(date time.Time) time.Time { + return defaultModule.LastEasternQuadrature(date) +} + +// LastOpposition 上一次冲日,取 date 所在当地民用日 / previous opposition, on the local civil day of date. +func LastOpposition(date time.Time) time.Time { return defaultModule.LastOpposition(date) } + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastWesternQuadrature 上一次西方照,取 date 所在当地民用日 / previous western quadrature, on the local civil day of date. +func LastWesternQuadrature(date time.Time) time.Time { + return defaultModule.LastWesternQuadrature(date) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextEasternQuadrature 下一次东方照,取 date 所在当地民用日 / next eastern quadrature, on the local civil day of date. +func NextEasternQuadrature(date time.Time) time.Time { + return defaultModule.NextEasternQuadrature(date) +} + +// NextOpposition 下一次冲日,取 date 所在当地民用日 / next opposition, on the local civil day of date. +func NextOpposition(date time.Time) time.Time { return defaultModule.NextOpposition(date) } + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextWesternQuadrature 下一次西方照,取 date 所在当地民用日 / next western quadrature, on the local civil day of date. +func NextWesternQuadrature(date time.Time) time.Time { + return defaultModule.NextWesternQuadrature(date) +} + +// ParallacticAngle 火星视差角(天顶方向角) / Mars parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 火星视差角(天顶方向角)(n 变体) / n-parameterised Mars parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 火星物理观测参数 / physical observing parameters of Mars. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 火星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mars. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 火星视半径,单位角秒 / apparent Mars semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 火星视半径(n 变体),单位角秒 / n-parameterised apparent Mars semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/mars/context.go b/mars/context.go new file mode 100644 index 0000000..84a0ec0 --- /dev/null +++ b/mars/context.go @@ -0,0 +1,13 @@ +package mars + +import ( + "b612.me/astro/internal/ephem" +) + +// Mars 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Mars struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Mars { return &Mars{inst: inst} } + +var defaultModule Mars diff --git a/mars/context_test.go b/mars/context_test.go new file mode 100644 index 0000000..2dcc4b5 --- /dev/null +++ b/mars/context_test.go @@ -0,0 +1,92 @@ +package mars + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastEasternQuadrature(date time.Time) time.Time + LastOpposition(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastWesternQuadrature(date time.Time) time.Time + NextConjunction(date time.Time) time.Time + NextEasternQuadrature(date time.Time) time.Time + NextOpposition(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextWesternQuadrature(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Mars)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/mars/diameter.go b/mars/diameter.go index 196dc1a..72a9c9e 100644 --- a/mars/diameter.go +++ b/mars/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 火星视半径,单位角秒 / apparent Mars semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Mars) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 火星视半径(截断版),单位角秒 / truncated apparent Mars semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 火星视半径(n 变体),单位角秒 / n-parameterised apparent Mars semidiameter in arcseconds. +func (module *Mars) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsSemidiameterN(basic.UTC2TT(jd), n) + return basic.MarsSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 火星视直径,单位角秒 / apparent Mars diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Mars) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 火星视直径(截断版),单位角秒 / truncated apparent Mars diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 火星视直径(n 变体),单位角秒 / n-parameterised apparent Mars diameter in arcseconds. +func (module *Mars) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsDiameterN(basic.UTC2TT(jd), n) + return basic.MarsDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/mars/doc.go b/mars/doc.go index abc8307..c9d8ec8 100644 --- a/mars/doc.go +++ b/mars/doc.go @@ -1,3 +1,7 @@ // Package mars 火星位置、升落、合冲、留、方照、相位、视星等、视直径与物理星历,角度单位为度,距离单位为 AU。 // Package mars covers Mars position, rise/set, conjunction, opposition, station, quadrature, phase, magnitude, apparent diameter and physical ephemeris; angles are degrees, distances are AU. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package mars diff --git a/mars/mars.go b/mars/mars.go index 6c9e6c2..fb43245 100644 --- a/mars/mars.go +++ b/mars/mars.go @@ -1,11 +1,11 @@ package mars import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回火星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Mars at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Mars) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentLo(basic.UTC2TT(jd)) + return basic.MarsApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回火星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Mars at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Mars) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentBo(basic.UTC2TT(jd)) + return basic.MarsApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回火星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Mars at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Mars) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentRa(basic.UTC2TT(jd)) + return basic.MarsApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回火星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Mars at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Mars) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentDec(basic.UTC2TT(jd)) + return basic.MarsApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回火星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Mars at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Mars) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentRaDec(basic.UTC2TT(jd)) + return basic.MarsApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回火星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Mars at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Mars) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsMag(basic.UTC2TT(jd)) + return basic.MarsMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回火星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Mars to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Mars) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthMarsAway(basic.UTC2TT(jd)) + return basic.EarthMarsAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回火星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Mars to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Mars) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(3, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(3, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Mars) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsHeight(localJD, lon, lat, timezone) + return basic.MarsHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Mars) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Mars) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsAzimuth(localJD, lon, lat, timezone) + return basic.MarsAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Mars) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsHourAngle(localJD, lon, timezone) + return basic.MarsHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Mars) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.MarsCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.MarsCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Mars) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MarsRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.MarsRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Mars) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Mars) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,114 +146,84 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MarsSetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.MarsSetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Mars) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Mars) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastOpposition 上一次冲日 / previous opposition. -// -// 返回 date 当前或之前最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or before date, keeping date's time zone. -func LastOpposition(date time.Time) time.Time { +func (module *Mars) LastOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsOppositionWithInstance(jde, module.inst), date.Location(), false) } // NextOpposition 下一次冲日 / next opposition. -// -// 返回 date 当前或之后最近一次冲日时刻,结果保持 date 的时区。 -// Returns the nearest opposition at or after date, keeping date's time zone. -func NextOpposition(date time.Time) time.Time { +func (module *Mars) NextOpposition(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsOpposition(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsOppositionWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Mars) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Mars) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsProgradeToRetrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsProgradeToRetrogradeWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Mars) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Mars) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsRetrogradeToPrograde(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsRetrogradeToProgradeWithInstance(jde, module.inst), date.Location(), false) } // LastEasternQuadrature 上一次东方照 / previous eastern quadrature. -// -// 返回 date 当前或之前最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or before date, keeping date's time zone. -func LastEasternQuadrature(date time.Time) time.Time { +func (module *Mars) LastEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextEasternQuadrature 下一次东方照 / next eastern quadrature. -// -// 返回 date 当前或之后最近一次东方照时刻,结果保持 date 的时区。 -// Returns the nearest eastern quadrature at or after date, keeping date's time zone. -func NextEasternQuadrature(date time.Time) time.Time { +func (module *Mars) NextEasternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsEasternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsEasternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // LastWesternQuadrature 上一次西方照 / previous western quadrature. -// -// 返回 date 当前或之前最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or before date, keeping date's time zone. -func LastWesternQuadrature(date time.Time) time.Time { +func (module *Mars) LastWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMarsWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMarsWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) } // NextWesternQuadrature 下一次西方照 / next western quadrature. -// -// 返回 date 当前或之后最近一次西方照时刻,结果保持 date 的时区。 -// Returns the nearest western quadrature at or after date, keeping date's time zone. -func NextWesternQuadrature(date time.Time) time.Time { +func (module *Mars) NextWesternQuadrature(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMarsWesternQuadrature(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMarsWesternQuadratureWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Mars) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.MarsMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/mars/nodes.go b/mars/nodes.go index 7c28fb9..d0bfc9b 100644 --- a/mars/nodes.go +++ b/mars/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 火星升交点黄经 / ascending node longitude of Mars. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Mars) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 火星升交点黄经(截断版) / truncated ascending node longitude of Mars. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 火星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mars. +func (module *Mars) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsAscendingNodeN(basic.UTC2TT(jd), n) + return basic.MarsAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 火星降交点黄经 / descending node longitude of Mars. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Mars) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 火星降交点黄经(截断版) / truncated descending node longitude of Mars. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 火星降交点黄经(n 变体) / n-parameterised descending node longitude of Mars. +func (module *Mars) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsDescendingNodeN(basic.UTC2TT(jd), n) + return basic.MarsDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/mars/parallactic.go b/mars/parallactic.go index 3c2cd13..ae2cae6 100644 --- a/mars/parallactic.go +++ b/mars/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 火星视差角(天顶方向角) / Mars parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Mars) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项火星视差角(天顶方向角) / truncated Mars parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 火星视差角(天顶方向角)(n 变体) / n-parameterised Mars parallactic angle. +func (module *Mars) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/mars/phase.go b/mars/phase.go index 28e29b4..34d925e 100644 --- a/mars/phase.go +++ b/mars/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Mars) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.MarsPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Mars) PhaseAngleN(date time.Time, n int) float64 { + return basic.MarsPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Mars) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.MarsIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Mars) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.MarsIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Mars) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Mars) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Mars) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.MarsBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Mars) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.MarsBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/mars/physical.go b/mars/physical.go index 045c375..119433d 100644 --- a/mars/physical.go +++ b/mars/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 火星物理观测参数 / physical observing parameters of Mars. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Mars) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 火星物理观测参数(截断版) / truncated physical observing parameters of Mars. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 火星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mars. +func (module *Mars) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.MarsPhysicalN(basic.UTC2TT(jd), n) + info := basic.MarsPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, diff --git a/mars/rise_set_atmosphere.go b/mars/rise_set_atmosphere.go new file mode 100644 index 0000000..47f6bb6 --- /dev/null +++ b/mars/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package mars + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 火星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Mars) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.MarsRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 火星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Mars) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.MarsSetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 火星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 火星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/mars/rise_set_atmosphere_test.go b/mars/rise_set_atmosphere_test.go new file mode 100644 index 0000000..48f026a --- /dev/null +++ b/mars/rise_set_atmosphere_test.go @@ -0,0 +1,33 @@ +package mars + +import ( + "math" + "testing" + "time" +) + +func TestRiseTimeWithAtmosphere(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + date := time.Date(2025, 1, 1, 0, 0, 0, 0, loc) + plain, err := RiseTime(date, 116.4, 39.9, 0, true) + if err != nil { + t.Fatal(err) + } + std, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1010, 10) + if err != nil { + t.Fatal(err) + } + dense, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, 1040, -30) + if err != nil { + t.Fatal(err) + } + if d := math.Abs(std.Sub(plain).Seconds()); d > 5 { + t.Errorf("standard weather differs by %.2f s", d) + } + if !dense.Before(std) { + t.Errorf("dense air must rise earlier: %v vs %v", dense, std) + } + if _, err := RiseTimeWithAtmosphere(date, 116.4, 39.9, 0, -1, 10); err == nil { + t.Error("invalid pressure must fail") + } +} diff --git a/mars/truncated.go b/mars/truncated.go index bf9d4cc..17ee8fa 100644 --- a/mars/truncated.go +++ b/mars/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Mars) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentLoN(basic.UTC2TT(jd), n) + return basic.MarsApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Mars) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentBoN(basic.UTC2TT(jd), n) + return basic.MarsApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Mars) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentRaN(basic.UTC2TT(jd), n) + return basic.MarsApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Mars) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentDecN(basic.UTC2TT(jd), n) + return basic.MarsApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Mars) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.MarsApparentRaDecN(basic.UTC2TT(jd), n) + return basic.MarsApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Mars) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MarsMagN(basic.UTC2TT(jd), n) + return basic.MarsMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Mars) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthMarsAwayN(basic.UTC2TT(jd), n) + return basic.EarthMarsAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Mars) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(3, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(3, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Mars) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsHeightN(localJD, lon, lat, timezone, n) + return basic.MarsHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Mars) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Mars) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsAzimuthN(localJD, lon, lat, timezone, n) + return basic.MarsAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Mars) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MarsHourAngleN(localJD, lon, timezone, n) + return basic.MarsHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Mars) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.MarsCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.MarsCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Mars) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MarsRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.MarsRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Mars) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Mars) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MarsSetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.MarsSetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/mercury/api_default.go b/mercury/api_default.go new file mode 100644 index 0000000..98cb10c --- /dev/null +++ b/mercury/api_default.go @@ -0,0 +1,302 @@ +package mercury + +import ( + "time" +) + +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func AltitudeN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AltitudeN(date, lon, lat, n) +} + +// ApparentBo 视黄纬 / apparent ecliptic latitude. +func ApparentBo(date time.Time) float64 { return defaultModule.ApparentBo(date) } + +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func ApparentBoN(date time.Time, n int) float64 { return defaultModule.ApparentBoN(date, n) } + +// ApparentDec 视赤纬 / apparent declination. +func ApparentDec(date time.Time) float64 { return defaultModule.ApparentDec(date) } + +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func ApparentDecN(date time.Time, n int) float64 { return defaultModule.ApparentDecN(date, n) } + +// ApparentLo 视黄经 / apparent ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func ApparentLoN(date time.Time, n int) float64 { return defaultModule.ApparentLoN(date, n) } + +// ApparentMagnitude 视星等 / apparent magnitude. +func ApparentMagnitude(date time.Time) float64 { return defaultModule.ApparentMagnitude(date) } + +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func ApparentMagnitudeN(date time.Time, n int) float64 { + return defaultModule.ApparentMagnitudeN(date, n) +} + +// ApparentRa 视赤经 / apparent right ascension. +func ApparentRa(date time.Time) float64 { return defaultModule.ApparentRa(date) } + +// ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. +func ApparentRaDec(date time.Time) (float64, float64) { return defaultModule.ApparentRaDec(date) } + +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func ApparentRaDecN(date time.Time, n int) (float64, float64) { + return defaultModule.ApparentRaDecN(date, n) +} + +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func ApparentRaN(date time.Time, n int) float64 { return defaultModule.ApparentRaN(date, n) } + +// AscendingNode 水星升交点黄经 / ascending node longitude of Mercury. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 水星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mercury. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 站心方位角,自正北向东量 / topocentric azimuth, measured from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func AzimuthN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.AzimuthN(date, lon, lat, n) +} + +// BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// ClosestTransit 最近一次地心水星凌日 / closest geocentric Mercury transit. +func ClosestTransit(date time.Time) TransitInfo { return defaultModule.ClosestTransit(date) } + +// CulminationTime 中天时刻,取 date 所在当地民用日 / culmination time, on the local civil day of date. +func CulminationTime(date time.Time, lon float64) time.Time { + return defaultModule.CulminationTime(date, lon) +} + +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func CulminationTimeN(date time.Time, lon float64, n int) time.Time { + return defaultModule.CulminationTimeN(date, lon, n) +} + +// DescendingNode 水星降交点黄经 / descending node longitude of Mercury. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 水星降交点黄经(n 变体) / n-parameterised descending node longitude of Mercury. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 水星视直径,单位角秒 / apparent Mercury diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 水星视直径(n 变体),单位角秒 / n-parameterised apparent Mercury diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 落下时间别名 / deprecated set-time alias. +// +// Deprecated: use SetTime instead. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.DownTimeN(date, lon, lat, height, aero, n) +} + +// EarthDistance 地心距离 / Earth distance. +func EarthDistance(date time.Time) float64 { return defaultModule.EarthDistance(date) } + +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func EarthDistanceN(date time.Time, n int) float64 { return defaultModule.EarthDistanceN(date, n) } + +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func HourAngle(date time.Time, lon float64) float64 { return defaultModule.HourAngle(date, lon) } + +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func HourAngleN(date time.Time, lon float64, n int) float64 { + return defaultModule.HourAngleN(date, lon, n) +} + +// IlluminatedFraction 被照亮比例 / illuminated fraction. +func IlluminatedFraction(date time.Time) float64 { return defaultModule.IlluminatedFraction(date) } + +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func IlluminatedFractionN(date time.Time, n int) float64 { + return defaultModule.IlluminatedFractionN(date, n) +} + +// LastConjunction 上一次合日,取 date 所在当地民用日 / previous conjunction with the Sun, on the local civil day of date. +func LastConjunction(date time.Time) time.Time { return defaultModule.LastConjunction(date) } + +// LastGreatestElongation 上一次大距,取 date 所在当地民用日 / previous greatest elongation, on the local civil day of date. +func LastGreatestElongation(date time.Time) time.Time { + return defaultModule.LastGreatestElongation(date) +} + +// LastGreatestElongationEast 上一次东大距,取 date 所在当地民用日 / previous greatest eastern elongation, on the local civil day of date. +func LastGreatestElongationEast(date time.Time) time.Time { + return defaultModule.LastGreatestElongationEast(date) +} + +// LastGreatestElongationWest 上一次西大距,取 date 所在当地民用日 / previous greatest western elongation, on the local civil day of date. +func LastGreatestElongationWest(date time.Time) time.Time { + return defaultModule.LastGreatestElongationWest(date) +} + +// LastInferiorConjunction 上一次下合,取 date 所在当地民用日 / previous inferior conjunction, on the local civil day of date. +func LastInferiorConjunction(date time.Time) time.Time { + return defaultModule.LastInferiorConjunction(date) +} + +// LastProgradeToRetrograde 上一次顺行转逆行留,取 date 所在当地民用日 / previous station from prograde to retrograde, on the local civil day of date. +func LastProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.LastProgradeToRetrograde(date) +} + +// LastRetrograde 上一次留,取 date 所在当地民用日 / previous stationary point, on the local civil day of date. +func LastRetrograde(date time.Time) time.Time { return defaultModule.LastRetrograde(date) } + +// LastRetrogradeToPrograde 上一次逆行转顺行留,取 date 所在当地民用日 / previous station from retrograde to prograde, on the local civil day of date. +func LastRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.LastRetrogradeToPrograde(date) +} + +// LastSuperiorConjunction 上一次上合,取 date 所在当地民用日 / previous superior conjunction, on the local civil day of date. +func LastSuperiorConjunction(date time.Time) time.Time { + return defaultModule.LastSuperiorConjunction(date) +} + +// LastTransit 上一次地心水星凌日 / previous geocentric Mercury transit. +func LastTransit(date time.Time) TransitInfo { return defaultModule.LastTransit(date) } + +// LocalTransit 地心凌日在观测地的站心接触与凌甚 / topocentric contacts of a transit seen from the site. +func LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo { + return defaultModule.LocalTransit(transit, lon, lat, height) +} + +// NextConjunction 下一次合日,取 date 所在当地民用日 / next conjunction with the Sun, on the local civil day of date. +func NextConjunction(date time.Time) time.Time { return defaultModule.NextConjunction(date) } + +// NextGreatestElongation 下一次大距,取 date 所在当地民用日 / next greatest elongation, on the local civil day of date. +func NextGreatestElongation(date time.Time) time.Time { + return defaultModule.NextGreatestElongation(date) +} + +// NextGreatestElongationEast 下一次东大距,取 date 所在当地民用日 / next greatest eastern elongation, on the local civil day of date. +func NextGreatestElongationEast(date time.Time) time.Time { + return defaultModule.NextGreatestElongationEast(date) +} + +// NextGreatestElongationWest 下一次西大距,取 date 所在当地民用日 / next greatest western elongation, on the local civil day of date. +func NextGreatestElongationWest(date time.Time) time.Time { + return defaultModule.NextGreatestElongationWest(date) +} + +// NextInferiorConjunction 下一次下合,取 date 所在当地民用日 / next inferior conjunction, on the local civil day of date. +func NextInferiorConjunction(date time.Time) time.Time { + return defaultModule.NextInferiorConjunction(date) +} + +// NextProgradeToRetrograde 下一次顺行转逆行留,取 date 所在当地民用日 / next station from prograde to retrograde, on the local civil day of date. +func NextProgradeToRetrograde(date time.Time) time.Time { + return defaultModule.NextProgradeToRetrograde(date) +} + +// NextRetrograde 下一次留,取 date 所在当地民用日 / next stationary point, on the local civil day of date. +func NextRetrograde(date time.Time) time.Time { return defaultModule.NextRetrograde(date) } + +// NextRetrogradeToPrograde 下一次逆行转顺行留,取 date 所在当地民用日 / next station from retrograde to prograde, on the local civil day of date. +func NextRetrogradeToPrograde(date time.Time) time.Time { + return defaultModule.NextRetrogradeToPrograde(date) +} + +// NextSuperiorConjunction 下一次上合,取 date 所在当地民用日 / next superior conjunction, on the local civil day of date. +func NextSuperiorConjunction(date time.Time) time.Time { + return defaultModule.NextSuperiorConjunction(date) +} + +// NextTransit 下一次地心水星凌日 / next geocentric Mercury transit. +func NextTransit(date time.Time) TransitInfo { return defaultModule.NextTransit(date) } + +// ParallacticAngle 水星视差角(天顶方向角) / Mercury parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// ParallacticAngleN 水星视差角(天顶方向角)(n 变体) / n-parameterised Mercury parallactic angle. +func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ParallacticAngleN(date, lon, lat, n) +} + +// Phase 相位,被照亮比例 / phase, illuminated fraction. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseAngle 相位角,单位度 / phase angle in degrees. +func PhaseAngle(date time.Time) float64 { return defaultModule.PhaseAngle(date) } + +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func PhaseAngleN(date time.Time, n int) float64 { return defaultModule.PhaseAngleN(date, n) } + +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func PhaseN(date time.Time, n int) float64 { return defaultModule.PhaseN(date, n) } + +// Physical 水星物理观测参数 / physical observing parameters of Mercury. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 水星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mercury. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// RiseTime 升起时间,取 date 所在当地民用日 / rise time, on the local civil day of date. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.RiseTimeN(date, lon, lat, height, aero, n) +} + +// Semidiameter 水星视半径,单位角秒 / apparent Mercury semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 水星视半径(n 变体),单位角秒 / n-parameterised apparent Mercury semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 落下时间,取 date 所在当地民用日 / set time, on the local civil day of date. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return defaultModule.SetTimeN(date, lon, lat, height, aero, n) +} + +// SunDistance 日心距离 / Sun distance. +func SunDistance(date time.Time) float64 { return defaultModule.SunDistance(date) } + +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func SunDistanceN(date time.Time, n int) float64 { return defaultModule.SunDistanceN(date, n) } + +// Zenith 天顶距 / zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func ZenithN(date time.Time, lon, lat float64, n int) float64 { + return defaultModule.ZenithN(date, lon, lat, n) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func ApparentMagnitude2018(date time.Time) float64 { return defaultModule.ApparentMagnitude2018(date) } diff --git a/mercury/context.go b/mercury/context.go new file mode 100644 index 0000000..bd65ad7 --- /dev/null +++ b/mercury/context.go @@ -0,0 +1,13 @@ +package mercury + +import ( + "b612.me/astro/internal/ephem" +) + +// Mercury 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Mercury struct{ inst *ephem.Instance } + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Mercury { return &Mercury{inst: inst} } + +var defaultModule Mercury diff --git a/mercury/context_test.go b/mercury/context_test.go new file mode 100644 index 0000000..401464d --- /dev/null +++ b/mercury/context_test.go @@ -0,0 +1,102 @@ +package mercury + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + AltitudeN(date time.Time, lon, lat float64, n int) float64 + ApparentBo(date time.Time) float64 + ApparentBoN(date time.Time, n int) float64 + ApparentDec(date time.Time) float64 + ApparentDecN(date time.Time, n int) float64 + ApparentLo(date time.Time) float64 + ApparentLoN(date time.Time, n int) float64 + ApparentMagnitude(date time.Time) float64 + ApparentMagnitudeN(date time.Time, n int) float64 + ApparentRa(date time.Time) float64 + ApparentRaDec(date time.Time) (float64, float64) + ApparentRaDecN(date time.Time, n int) (float64, float64) + ApparentRaN(date time.Time, n int) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + AzimuthN(date time.Time, lon, lat float64, n int) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + ClosestTransit(date time.Time) TransitInfo + CulminationTime(date time.Time, lon float64) time.Time + CulminationTimeN(date time.Time, lon float64, n int) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + EarthDistance(date time.Time) float64 + EarthDistanceN(date time.Time, n int) float64 + HourAngle(date time.Time, lon float64) float64 + HourAngleN(date time.Time, lon float64, n int) float64 + IlluminatedFraction(date time.Time) float64 + IlluminatedFractionN(date time.Time, n int) float64 + LastConjunction(date time.Time) time.Time + LastGreatestElongation(date time.Time) time.Time + LastGreatestElongationEast(date time.Time) time.Time + LastGreatestElongationWest(date time.Time) time.Time + LastInferiorConjunction(date time.Time) time.Time + LastProgradeToRetrograde(date time.Time) time.Time + LastRetrograde(date time.Time) time.Time + LastRetrogradeToPrograde(date time.Time) time.Time + LastSuperiorConjunction(date time.Time) time.Time + LastTransit(date time.Time) TransitInfo + LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo + NextConjunction(date time.Time) time.Time + NextGreatestElongation(date time.Time) time.Time + NextGreatestElongationEast(date time.Time) time.Time + NextGreatestElongationWest(date time.Time) time.Time + NextInferiorConjunction(date time.Time) time.Time + NextProgradeToRetrograde(date time.Time) time.Time + NextRetrograde(date time.Time) time.Time + NextRetrogradeToPrograde(date time.Time) time.Time + NextSuperiorConjunction(date time.Time) time.Time + NextTransit(date time.Time) TransitInfo + ParallacticAngle(date time.Time, lon, lat float64) float64 + ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 + Phase(date time.Time) float64 + PhaseAngle(date time.Time) float64 + PhaseAngleN(date time.Time, n int) float64 + PhaseN(date time.Time, n int) float64 + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) + SunDistance(date time.Time) float64 + SunDistanceN(date time.Time, n int) float64 + Zenith(date time.Time, lon, lat float64) float64 + ZenithN(date time.Time, lon, lat float64, n int) float64 +} + +var _ contextAPI = (*Mercury)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/mercury/diameter.go b/mercury/diameter.go index cbc8b61..b8711cf 100644 --- a/mercury/diameter.go +++ b/mercury/diameter.go @@ -8,23 +8,23 @@ import ( ) // Semidiameter 水星视半径,单位角秒 / apparent Mercury semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Mercury) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 水星视半径(截断版),单位角秒 / truncated apparent Mercury semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 水星视半径(n 变体),单位角秒 / n-parameterised apparent Mercury semidiameter in arcseconds. +func (module *Mercury) SemidiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercurySemidiameterN(basic.UTC2TT(jd), n) + return basic.MercurySemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 水星视直径,单位角秒 / apparent Mercury diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Mercury) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 水星视直径(截断版),单位角秒 / truncated apparent Mercury diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 水星视直径(n 变体),单位角秒 / n-parameterised apparent Mercury diameter in arcseconds. +func (module *Mercury) DiameterN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryDiameterN(basic.UTC2TT(jd), n) + return basic.MercuryDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/mercury/doc.go b/mercury/doc.go index 9db789e..73b0bc1 100644 --- a/mercury/doc.go +++ b/mercury/doc.go @@ -1,3 +1,7 @@ // Package mercury 水星位置、升落、合日、留、大距、地心凌日、相位、视直径与物理星历,角度单位为度。 // Package mercury covers Mercury position, rise/set, conjunction, station, elongation, geocentric transit, phase, apparent diameter and physical ephemeris; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +// 时间参数:位置、距离、星等与物理量接口的 date 是绝对时刻,其所在时区参与地方时换算;升落、中天、晨昏与 Next/Last 事件接口按 date 所在时区的当地民用日求解,返回时刻保持该时区。 +// Time arguments: date is an absolute instant for position, distance, magnitude and physical-quantity entry points, and its zone takes part in local-time conversion; rise/set, culmination, twilight and Next/Last event entry points solve for the local civil day in date's zone and return instants keeping that zone. package mercury diff --git a/mercury/mercury.go b/mercury/mercury.go index 895093c..9672892 100644 --- a/mercury/mercury.go +++ b/mercury/mercury.go @@ -1,11 +1,11 @@ package mercury import ( - "b612.me/astro/basic" - "b612.me/astro/calendar" - "b612.me/astro/planet" "errors" "time" + + "b612.me/astro/basic" + "b612.me/astro/calendar" ) var ( @@ -31,136 +31,94 @@ func riseSetResult(date time.Time, jd float64, err error) (time.Time, error) { } // ApparentLo 视黄经 / apparent ecliptic longitude. -// -// 返回水星在 date 对应绝对时刻的瞬时视黄经,单位度。 -// Returns the apparent ecliptic longitude of Mercury at the instant represented by date, in degrees. -func ApparentLo(date time.Time) float64 { +func (module *Mercury) ApparentLo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentLo(basic.UTC2TT(jd)) + return basic.MercuryApparentLoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentBo 视黄纬 / apparent ecliptic latitude. -// -// 返回水星在 date 对应绝对时刻的瞬时视黄纬,单位度。 -// Returns the apparent ecliptic latitude of Mercury at the instant represented by date, in degrees. -func ApparentBo(date time.Time) float64 { +func (module *Mercury) ApparentBo(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentBo(basic.UTC2TT(jd)) + return basic.MercuryApparentBoWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRa 视赤经 / apparent right ascension. -// -// 返回水星在 date 对应绝对时刻的瞬时视赤经,单位度。 -// Returns the apparent right ascension of Mercury at the instant represented by date, in degrees. -func ApparentRa(date time.Time) float64 { +func (module *Mercury) ApparentRa(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentRa(basic.UTC2TT(jd)) + return basic.MercuryApparentRaWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentDec 视赤纬 / apparent declination. -// -// 返回水星在 date 对应绝对时刻的瞬时视赤纬,单位度。 -// Returns the apparent declination of Mercury at the instant represented by date, in degrees. -func ApparentDec(date time.Time) float64 { +func (module *Mercury) ApparentDec(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentDec(basic.UTC2TT(jd)) + return basic.MercuryApparentDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentRaDec 视赤经、视赤纬 / apparent right ascension and declination. -// -// 返回水星在 date 对应绝对时刻的瞬时视赤经与视赤纬,单位度。 -// Returns the apparent right ascension and declination of Mercury at the instant represented by date, in degrees. -func ApparentRaDec(date time.Time) (float64, float64) { +func (module *Mercury) ApparentRaDec(date time.Time) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentRaDec(basic.UTC2TT(jd)) + return basic.MercuryApparentRaDecWithInstance(basic.UTC2TT(jd), module.inst) } // ApparentMagnitude 视星等 / apparent magnitude. -// -// 返回水星在 date 对应绝对时刻的视星等。 -// Returns the apparent visual magnitude of Mercury at the instant represented by date. -func ApparentMagnitude(date time.Time) float64 { +func (module *Mercury) ApparentMagnitude(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryMag(basic.UTC2TT(jd)) + return basic.MercuryMagWithInstance(basic.UTC2TT(jd), module.inst) } // EarthDistance 地心距离 / Earth distance. -// -// 返回水星在 date 对应绝对时刻到地球的距离,单位 AU。 -// Returns the distance from Mercury to Earth at the instant represented by date, in astronomical units. -func EarthDistance(date time.Time) float64 { +func (module *Mercury) EarthDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthMercuryAway(basic.UTC2TT(jd)) + return basic.EarthMercuryAwayWithInstance(basic.UTC2TT(jd), module.inst) } // SunDistance 日心距离 / Sun distance. -// -// 返回水星在 date 对应绝对时刻到太阳的距离,单位 AU。 -// Returns the distance from Mercury to the Sun at the instant represented by date, in astronomical units. -func SunDistance(date time.Time) float64 { +func (module *Mercury) SunDistance(date time.Time) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanet(1, 2, basic.UTC2TT(jd)) + return basic.PlanetCoordinateWithInstance(1, 2, basic.UTC2TT(jd), module.inst) } -// Altitude 高度角 / altitude. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and the result is in degrees. -func Altitude(date time.Time, lon, lat float64) float64 { +// Altitude 站心视高度角,不含大气折射 / topocentric apparent altitude without refraction. +func (module *Mercury) Altitude(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryHeight(localJD, lon, lat, timezone) + return basic.MercuryHeightWithInstance(localJD, lon, lat, timezone, module.inst) } // Zenith 天顶距 / zenith distance. -// -// 参数与 Altitude 相同,返回值为对应时刻的天顶距,单位度。 -// Uses the same inputs as Altitude and returns the zenith distance in degrees. -func Zenith(date time.Time, lon, lat float64) float64 { - return 90 - Altitude(date, lon, lat) +func (module *Mercury) Zenith(date time.Time, lon, lat float64) float64 { + return 90 - module.Altitude(date, lon, lat) } -// Azimuth 方位角 / azimuth. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负;lat 为观测者纬度,北正南负。返回值按正北为 0°、向东增加。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude, lat is north-positive latitude, and azimuth is measured from north toward east. -func Azimuth(date time.Time, lon, lat float64) float64 { +// Azimuth 站心方位角 / topocentric azimuth. +func (module *Mercury) Azimuth(date time.Time, lon, lat float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryAzimuth(localJD, lon, lat, timezone) + return basic.MercuryAzimuthWithInstance(localJD, lon, lat, timezone, module.inst) } -// HourAngle 时角 / hour angle. -// -// date 表示观测时刻,会读取其时区参与地方时计算;lon 为观测者经度,东正西负。返回值单位度。 -// date is the observing instant and its zone offset participates in local-time calculations. lon is east-positive longitude and the returned hour angle is in degrees. -func HourAngle(date time.Time, lon float64) float64 { +// HourAngle 地心视时角,不含站心视差 / geocentric apparent hour angle without topocentric parallax. +func (module *Mercury) HourAngle(date time.Time, lon float64) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryHourAngle(localJD, lon, timezone) + return basic.MercuryHourAngleWithInstance(localJD, lon, timezone, module.inst) } // CulminationTime 中天时刻 / culmination time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为观测者经度,东正西负。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude. -func CulminationTime(date time.Time, lon float64) time.Time { +func (module *Mercury) CulminationTime(date time.Time, lon float64) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.MercuryCulminationTime(localJD, lon, timezone) - timezone/24.00 + calcJD := basic.MercuryCulminationTimeWithInstance(localJD, lon, timezone, module.inst) - timezone/24.00 return basic.JD2DateByZone(calcJD, date.Location(), false) } // RiseTime 升起时间 / rise time. -// -// date 取其所在时区的当地日期,返回值保持相同时区;lon 为东正西负经度,lat 为北正南负纬度;height 为观测点椭球高(大地高,米);aero 为 true 时加入标准大气折射。 -// date is interpreted on its local civil day and the result keeps the same time zone. lon is east-positive longitude, lat is north-positive latitude, height is observer elevation in meters, and aero enables standard atmospheric refraction. -func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Mercury) RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -169,25 +127,17 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MercuryRiseTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.MercuryRiseTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // DownTime 落下时间别名 / deprecated set-time alias. -// -// Deprecated: use SetTime instead. -// -// 参数与 SetTime 相同,仅为兼容旧接口保留。 -// Same as SetTime and kept only for backward compatibility. -func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { - return SetTime(date, lon, lat, height, aero) +func (module *Mercury) DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return module.SetTime(date, lon, lat, height, aero) } // SetTime 落下时间 / set time. -// -// 参数与 RiseTime 相同,返回给定当地日期内的落下时刻。 -// Uses the same inputs as RiseTime and returns the set time on the corresponding local civil day. -func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { +func (module *Mercury) SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -196,168 +146,120 @@ func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, er localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MercurySetTime(localJD, lon, lat, timezone, aeroFloat, height) + riseJD, err := basic.MercurySetTimeWithInstance(localJD, lon, lat, timezone, aeroFloat, height, module.inst) return riseSetResult(date, riseJD, err) } // LastConjunction 上一次合日 / previous conjunction with the Sun. -// -// 返回 date 当前或之前最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or before date, keeping date's time zone. -func LastConjunction(date time.Time) time.Time { +func (module *Mercury) LastConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryConjunctionWithInstance(jde, module.inst), date.Location(), false) } // NextConjunction 下一次合日 / next conjunction with the Sun. -// -// 返回 date 当前或之后最近一次与太阳的合日时刻,结果保持 date 的时区。 -// Returns the nearest conjunction with the Sun at or after date, keeping date's time zone. -func NextConjunction(date time.Time) time.Time { +func (module *Mercury) NextConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryConjunction(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryConjunctionWithInstance(jde, module.inst), date.Location(), false) } // LastInferiorConjunction 上一次下合 / previous inferior conjunction. -// -// 返回 date 当前或之前最近一次下合时刻,结果保持 date 的时区。 -// Returns the nearest inferior conjunction at or before date, keeping date's time zone. -func LastInferiorConjunction(date time.Time) time.Time { +func (module *Mercury) LastInferiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryInferiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryInferiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextInferiorConjunction 下一次下合 / next inferior conjunction. -// -// 返回 date 当前或之后最近一次下合时刻,结果保持 date 的时区。 -// Returns the nearest inferior conjunction at or after date, keeping date's time zone. -func NextInferiorConjunction(date time.Time) time.Time { +func (module *Mercury) NextInferiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryInferiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryInferiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastSuperiorConjunction 上一次上合 / previous superior conjunction. -// -// 返回 date 当前或之前最近一次上合时刻,结果保持 date 的时区。 -// Returns the nearest superior conjunction at or before date, keeping date's time zone. -func LastSuperiorConjunction(date time.Time) time.Time { +func (module *Mercury) LastSuperiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercurySuperiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercurySuperiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextSuperiorConjunction 下一次上合 / next superior conjunction. -// -// 返回 date 当前或之后最近一次上合时刻,结果保持 date 的时区。 -// Returns the nearest superior conjunction at or after date, keeping date's time zone. -func NextSuperiorConjunction(date time.Time) time.Time { +func (module *Mercury) NextSuperiorConjunction(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercurySuperiorConjunctionInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercurySuperiorConjunctionInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastRetrograde 上一次留 / previous stationary point. -// -// 返回 date 当前或之前最近一次留时刻,不区分顺转逆还是逆转顺,结果保持 date 的时区。 -// Returns the nearest stationary point at or before date, regardless of the direction change, keeping date's time zone. -func LastRetrograde(date time.Time) time.Time { +func (module *Mercury) LastRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextRetrograde 下一次留 / next stationary point. -// -// 返回 date 当前或之后最近一次留时刻,不区分顺转逆还是逆转顺,结果保持 date 的时区。 -// Returns the nearest stationary point at or after date, regardless of the direction change, keeping date's time zone. -func NextRetrograde(date time.Time) time.Time { +func (module *Mercury) NextRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastProgradeToRetrograde 上一次顺行转逆行留 / previous station from prograde to retrograde. -// -// 返回 date 当前或之前最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from prograde to retrograde, keeping date's time zone. -func LastProgradeToRetrograde(date time.Time) time.Time { +func (module *Mercury) LastProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryProgradeToRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryProgradeToRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextProgradeToRetrograde 下一次顺行转逆行留 / next station from prograde to retrograde. -// -// 返回 date 当前或之后最近一次由顺行转为逆行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from prograde to retrograde, keeping date's time zone. -func NextProgradeToRetrograde(date time.Time) time.Time { +func (module *Mercury) NextProgradeToRetrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryProgradeToRetrogradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryProgradeToRetrogradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastRetrogradeToPrograde 上一次逆行转顺行留 / previous station from retrograde to prograde. -// -// 返回 date 当前或之前最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or before date where motion changes from retrograde to prograde, keeping date's time zone. -func LastRetrogradeToPrograde(date time.Time) time.Time { +func (module *Mercury) LastRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryRetrogradeToProgradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryRetrogradeToProgradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextRetrogradeToPrograde 下一次逆行转顺行留 / next station from retrograde to prograde. -// -// 返回 date 当前或之后最近一次由逆行转为顺行的留时刻,结果保持 date 的时区。 -// Returns the nearest station at or after date where motion changes from retrograde to prograde, keeping date's time zone. -func NextRetrogradeToPrograde(date time.Time) time.Time { +func (module *Mercury) NextRetrogradeToPrograde(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryRetrogradeToProgradeInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryRetrogradeToProgradeInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongation 上一次大距 / previous greatest elongation. -// -// 返回 date 当前或之前最近一次大距时刻,不区分东西大距,结果保持 date 的时区。 -// Returns the nearest greatest elongation at or before date, regardless of east or west, keeping date's time zone. -func LastGreatestElongation(date time.Time) time.Time { +func (module *Mercury) LastGreatestElongation(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryGreatestElongationInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryGreatestElongationInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongation 下一次大距 / next greatest elongation. -// -// 返回 date 当前或之后最近一次大距时刻,不区分东西大距,结果保持 date 的时区。 -// Returns the nearest greatest elongation at or after date, regardless of east or west, keeping date's time zone. -func NextGreatestElongation(date time.Time) time.Time { +func (module *Mercury) NextGreatestElongation(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryGreatestElongationInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryGreatestElongationInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongationEast 上一次东大距 / previous greatest eastern elongation. -// -// 返回 date 当前或之前最近一次东大距时刻,结果保持 date 的时区。 -// Returns the nearest eastern greatest elongation at or before date, keeping date's time zone. -func LastGreatestElongationEast(date time.Time) time.Time { +func (module *Mercury) LastGreatestElongationEast(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryGreatestElongationEastInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryGreatestElongationEastInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongationEast 下一次东大距 / next greatest eastern elongation. -// -// 返回 date 当前或之后最近一次东大距时刻,结果保持 date 的时区。 -// Returns the nearest eastern greatest elongation at or after date, keeping date's time zone. -func NextGreatestElongationEast(date time.Time) time.Time { +func (module *Mercury) NextGreatestElongationEast(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryGreatestElongationEastInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryGreatestElongationEastInclusiveWithInstance(jde, module.inst), date.Location(), false) } // LastGreatestElongationWest 上一次西大距 / previous greatest western elongation. -// -// 返回 date 当前或之前最近一次西大距时刻,结果保持 date 的时区。 -// Returns the nearest western greatest elongation at or before date, keeping date's time zone. -func LastGreatestElongationWest(date time.Time) time.Time { +func (module *Mercury) LastGreatestElongationWest(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMercuryGreatestElongationWestInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.LastMercuryGreatestElongationWestInclusiveWithInstance(jde, module.inst), date.Location(), false) } // NextGreatestElongationWest 下一次西大距 / next greatest western elongation. -// -// 返回 date 当前或之后最近一次西大距时刻,结果保持 date 的时区。 -// Returns the nearest western greatest elongation at or after date, keeping date's time zone. -func NextGreatestElongationWest(date time.Time) time.Time { +func (module *Mercury) NextGreatestElongationWest(date time.Time) time.Time { jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMercuryGreatestElongationWestInclusive(jde), date.Location(), false) + return basic.JD2DateByZone(basic.NextMercuryGreatestElongationWestInclusiveWithInstance(jde, module.inst), date.Location(), false) +} + +// ApparentMagnitude2018 视星等(Mallama & Hilton 2018 模型)/ apparent magnitude, Mallama & Hilton 2018 model. +func (module *Mercury) ApparentMagnitude2018(date time.Time) float64 { + jd := calendar.Date2JD(date.UTC()) + return basic.MercuryMag2018WithInstance(basic.UTC2TT(jd), module.inst) } diff --git a/mercury/mercury_test.go b/mercury/mercury_test.go index 91a972b..fcba5c1 100644 --- a/mercury/mercury_test.go +++ b/mercury/mercury_test.go @@ -18,7 +18,7 @@ func TestMercury(t *testing.T) { if err != nil { t.Fatal(err) } - if date.Unix() != 1642636481 { + if date.Unix() != 1642636482 { t.Fatal(date.Unix()) } } diff --git a/mercury/nodes.go b/mercury/nodes.go index 106191f..32522ec 100644 --- a/mercury/nodes.go +++ b/mercury/nodes.go @@ -8,23 +8,23 @@ import ( ) // AscendingNode 水星升交点黄经 / ascending node longitude of Mercury. -func AscendingNode(date time.Time) float64 { - return AscendingNodeN(date, -1) +func (module *Mercury) AscendingNode(date time.Time) float64 { + return module.AscendingNodeN(date, -1) } -// AscendingNodeN 水星升交点黄经(截断版) / truncated ascending node longitude of Mercury. -func AscendingNodeN(date time.Time, n int) float64 { +// AscendingNodeN 水星升交点黄经(n 变体) / n-parameterised ascending node longitude of Mercury. +func (module *Mercury) AscendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryAscendingNodeN(basic.UTC2TT(jd), n) + return basic.MercuryAscendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } // DescendingNode 水星降交点黄经 / descending node longitude of Mercury. -func DescendingNode(date time.Time) float64 { - return DescendingNodeN(date, -1) +func (module *Mercury) DescendingNode(date time.Time) float64 { + return module.DescendingNodeN(date, -1) } -// DescendingNodeN 水星降交点黄经(截断版) / truncated descending node longitude of Mercury. -func DescendingNodeN(date time.Time, n int) float64 { +// DescendingNodeN 水星降交点黄经(n 变体) / n-parameterised descending node longitude of Mercury. +func (module *Mercury) DescendingNodeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryDescendingNodeN(basic.UTC2TT(jd), n) + return basic.MercuryDescendingNodeNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/mercury/parallactic.go b/mercury/parallactic.go index fb84798..227274d 100644 --- a/mercury/parallactic.go +++ b/mercury/parallactic.go @@ -7,11 +7,11 @@ import ( ) // ParallacticAngle 水星视差角(天顶方向角) / Mercury parallactic angle. -func ParallacticAngle(date time.Time, lon, lat float64) float64 { - return basic.ParallacticAngleByHourAngle(HourAngle(date, lon), ApparentDec(date), lat) +func (module *Mercury) ParallacticAngle(date time.Time, lon, lat float64) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngle(date, lon), module.ApparentDec(date), lat) } -// ParallacticAngleN 截断项水星视差角(天顶方向角) / truncated Mercury parallactic angle. -func ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { - return basic.ParallacticAngleByHourAngle(HourAngleN(date, lon, n), ApparentDecN(date, n), lat) +// ParallacticAngleN 水星视差角(天顶方向角)(n 变体) / n-parameterised Mercury parallactic angle. +func (module *Mercury) ParallacticAngleN(date time.Time, lon, lat float64, n int) float64 { + return basic.ParallacticAngleByHourAngle(module.HourAngleN(date, lon, n), module.ApparentDecN(date, n), lat) } diff --git a/mercury/phase.go b/mercury/phase.go index 46eef3a..ff8c02a 100644 --- a/mercury/phase.go +++ b/mercury/phase.go @@ -8,43 +8,43 @@ import ( ) // PhaseAngle 相位角,单位度 / phase angle in degrees. -func PhaseAngle(date time.Time) float64 { - return PhaseAngleN(date, -1) +func (module *Mercury) PhaseAngle(date time.Time) float64 { + return module.PhaseAngleN(date, -1) } -// PhaseAngleN 相位角(截断版),单位度 / truncated phase angle in degrees. -func PhaseAngleN(date time.Time, n int) float64 { - return basic.MercuryPhaseAngleN(phaseJD(date), n) +// PhaseAngleN 相位角(n 变体),单位度 / n-parameterised phase angle in degrees. +func (module *Mercury) PhaseAngleN(date time.Time, n int) float64 { + return basic.MercuryPhaseAngleNWithInstance(phaseJD(date), n, module.inst) } // IlluminatedFraction 被照亮比例 / illuminated fraction. -func IlluminatedFraction(date time.Time) float64 { - return IlluminatedFractionN(date, -1) +func (module *Mercury) IlluminatedFraction(date time.Time) float64 { + return module.IlluminatedFractionN(date, -1) } -// IlluminatedFractionN 被照亮比例(截断版) / truncated illuminated fraction. -func IlluminatedFractionN(date time.Time, n int) float64 { - return basic.MercuryIlluminatedFractionN(phaseJD(date), n) +// IlluminatedFractionN 被照亮比例(n 变体) / n-parameterised illuminated fraction. +func (module *Mercury) IlluminatedFractionN(date time.Time, n int) float64 { + return basic.MercuryIlluminatedFractionNWithInstance(phaseJD(date), n, module.inst) } // Phase 相位,被照亮比例 / phase, illuminated fraction. -func Phase(date time.Time) float64 { - return IlluminatedFraction(date) +func (module *Mercury) Phase(date time.Time) float64 { + return module.IlluminatedFraction(date) } -// PhaseN 相位(截断版),被照亮比例 / truncated phase, illuminated fraction. -func PhaseN(date time.Time, n int) float64 { - return IlluminatedFractionN(date, n) +// PhaseN 相位(n 变体),被照亮比例 / n-parameterised phase, illuminated fraction. +func (module *Mercury) PhaseN(date time.Time, n int) float64 { + return module.IlluminatedFractionN(date, n) } // BrightLimbPositionAngle 亮面中心位置角,单位度 / bright limb position angle in degrees. -func BrightLimbPositionAngle(date time.Time) float64 { - return BrightLimbPositionAngleN(date, -1) +func (module *Mercury) BrightLimbPositionAngle(date time.Time) float64 { + return module.BrightLimbPositionAngleN(date, -1) } -// BrightLimbPositionAngleN 亮面中心位置角(截断版),单位度 / truncated bright limb position angle in degrees. -func BrightLimbPositionAngleN(date time.Time, n int) float64 { - return basic.MercuryBrightLimbPositionAngleN(phaseJD(date), n) +// BrightLimbPositionAngleN 亮面中心位置角(n 变体),单位度 / n-parameterised bright limb position angle in degrees. +func (module *Mercury) BrightLimbPositionAngleN(date time.Time, n int) float64 { + return basic.MercuryBrightLimbPositionAngleNWithInstance(phaseJD(date), n, module.inst) } func phaseJD(date time.Time) float64 { diff --git a/mercury/physical.go b/mercury/physical.go index dd3f70a..97cfa01 100644 --- a/mercury/physical.go +++ b/mercury/physical.go @@ -21,14 +21,14 @@ type PhysicalInfo struct { } // Physical 水星物理观测参数 / physical observing parameters of Mercury. -func Physical(date time.Time) PhysicalInfo { - return PhysicalN(date, -1) +func (module *Mercury) Physical(date time.Time) PhysicalInfo { + return module.PhysicalN(date, -1) } -// PhysicalN 水星物理观测参数(截断版) / truncated physical observing parameters of Mercury. -func PhysicalN(date time.Time, n int) PhysicalInfo { +// PhysicalN 水星物理观测参数(n 变体) / n-parameterised physical observing parameters of Mercury. +func (module *Mercury) PhysicalN(date time.Time, n int) PhysicalInfo { jd := basic.Date2JD(date.UTC()) - info := basic.MercuryPhysicalN(basic.UTC2TT(jd), n) + info := basic.MercuryPhysicalNWithInstance(basic.UTC2TT(jd), n, module.inst) return PhysicalInfo{ SubEarthLongitude: info.SubEarthLongitude, SubEarthLatitude: info.SubEarthLatitude, diff --git a/mercury/rise_set_atmosphere.go b/mercury/rise_set_atmosphere.go new file mode 100644 index 0000000..1620073 --- /dev/null +++ b/mercury/rise_set_atmosphere.go @@ -0,0 +1,33 @@ +package mercury + +import ( + "time" + + "b612.me/astro/basic" +) + +// RiseTimeWithAtmosphere 水星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func (module *Mercury) RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.MercuryRiseTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// SetTimeWithAtmosphere 水星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func (module *Mercury) SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) + _, loc := date.Zone() + jd, err := basic.MercurySetTimeWithAtmosphere(basic.Date2JD(date), lon, lat, float64(loc)/3600.0, height, pressureHPa, temperatureC, module.inst) + return riseSetResult(date, jd, err) +} + +// RiseTimeWithAtmosphere 水星升起时刻,按给定气压(hPa)与气温(°C)计算大气折射 / rise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 水星落下时刻,按给定气压(hPa)与气温(°C)计算大气折射 / set time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/mercury/transit.go b/mercury/transit.go index a95f3b0..259109d 100644 --- a/mercury/transit.go +++ b/mercury/transit.go @@ -32,18 +32,18 @@ type TransitInfo struct { } // NextTransit 下一次地心水星凌日 / next geocentric Mercury transit. -func NextTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.NextMercuryTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Mercury) NextTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.NextMercuryTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } // LastTransit 上一次地心水星凌日 / previous geocentric Mercury transit. -func LastTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.LastMercuryTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Mercury) LastTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.LastMercuryTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } // ClosestTransit 最近一次地心水星凌日 / closest geocentric Mercury transit. -func ClosestTransit(date time.Time) TransitInfo { - return transitInfoFromBasic(basic.ClosestMercuryTransit(basic.Date2JD(date.UTC())), date.Location()) +func (module *Mercury) ClosestTransit(date time.Time) TransitInfo { + return transitInfoFromBasic(basic.ClosestMercuryTransitWithInstance(basic.Date2JD(date.UTC()), module.inst), date.Location()) } func transitInfoFromBasic(result basic.PlanetTransitResult, loc *time.Location) TransitInfo { @@ -71,3 +71,26 @@ func transitInfoFromBasic(result basic.PlanetTransitResult, loc *time.Location) } return info } + +// LocalTransitInfo 站心水星凌日信息 / topocentric Mercury transit information. +// +// 时刻保持调用者传入的 TransitInfo 的时区;太阳高度为几何中心高度,不含折射。 +// Times keep the timezone of the supplied TransitInfo; Sun altitudes are geometric centre altitudes without refraction. +type LocalTransitInfo = basic.PlanetTransitLocalInfo + +// LocalTransitContact 站心凌日接触 / topocentric transit contact. +type LocalTransitContact = basic.PlanetTransitLocalContactInfo + +// LocalTransit 地心凌日 transit 在观测地的站心接触与凌甚 / topocentric contacts of transit seen from the site. +// +// lon、lat 为度,东经、北纬为正;height 为海拔米。 +// lon and lat are in degrees, east and north positive; height is in meters. +func (module *Mercury) LocalTransit(transit TransitInfo, lon, lat, height float64) LocalTransitInfo { + if !transit.Valid { + return LocalTransitInfo{} + } + result := basic.PlanetTransitResultFromTimes(1, + transit.Start, transit.InternalStart, transit.Greatest, transit.InternalEnd, transit.End) + local := basic.PlanetTransitLocalWithInstance(result, basic.Observer{Longitude: lon, Latitude: lat, Height: height}, module.inst) + return basic.PlanetTransitLocalInfoFromResult(local, transit.Greatest.Location()) +} diff --git a/mercury/transit_test.go b/mercury/transit_test.go index f8dfb87..2f98dbb 100644 --- a/mercury/transit_test.go +++ b/mercury/transit_test.go @@ -21,3 +21,22 @@ func TestTransitWrappers(t *testing.T) { t.Fatalf("unexpected durations: %+v", info) } } + +func TestLocalTransitWrapper(t *testing.T) { + loc := time.FixedZone("CST", 8*3600) + info := NextTransit(time.Date(2019, 1, 1, 0, 0, 0, 0, loc)) + local := LocalTransit(info, -77.0, 38.9, 0) + if !local.Valid || !local.HasInternal || !local.Visible { + t.Fatalf("unexpected local transit: %+v", local) + } + if local.Greatest.Time.Location() != loc { + t.Fatalf("timezone mismatch: %v", local.Greatest.Time.Location()) + } + // 站心接触与地心接触只差视差引起的几十秒。 + if shift := local.Start.Time.Sub(info.Start); shift < -2*time.Minute || shift > 2*time.Minute { + t.Fatalf("C1 shift %v", shift) + } + if LocalTransit(TransitInfo{}, 0, 0, 0).Valid { + t.Fatal("invalid transit must give invalid local result") + } +} diff --git a/mercury/truncated.go b/mercury/truncated.go index f79e1e6..67718b5 100644 --- a/mercury/truncated.go +++ b/mercury/truncated.go @@ -5,100 +5,99 @@ import ( "b612.me/astro/basic" "b612.me/astro/calendar" - "b612.me/astro/planet" ) // N variants keep the same semantics as the non-N APIs; n < 0 means full series. -// ApparentLoN 视黄经(截断版) / truncated apparent ecliptic longitude. -func ApparentLoN(date time.Time, n int) float64 { +// ApparentLoN 视黄经(n 变体) / n-parameterised apparent ecliptic longitude. +func (module *Mercury) ApparentLoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentLoN(basic.UTC2TT(jd), n) + return basic.MercuryApparentLoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentBoN 视黄纬(截断版) / truncated apparent ecliptic latitude. -func ApparentBoN(date time.Time, n int) float64 { +// ApparentBoN 视黄纬(n 变体) / n-parameterised apparent ecliptic latitude. +func (module *Mercury) ApparentBoN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentBoN(basic.UTC2TT(jd), n) + return basic.MercuryApparentBoNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaN 视赤经(截断版) / truncated apparent right ascension. -func ApparentRaN(date time.Time, n int) float64 { +// ApparentRaN 视赤经(n 变体) / n-parameterised apparent right ascension. +func (module *Mercury) ApparentRaN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentRaN(basic.UTC2TT(jd), n) + return basic.MercuryApparentRaNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentDecN 视赤纬(截断版) / truncated apparent declination. -func ApparentDecN(date time.Time, n int) float64 { +// ApparentDecN 视赤纬(n 变体) / n-parameterised apparent declination. +func (module *Mercury) ApparentDecN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentDecN(basic.UTC2TT(jd), n) + return basic.MercuryApparentDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentRaDecN 视赤经赤纬(截断版) / truncated apparent right ascension and declination. -func ApparentRaDecN(date time.Time, n int) (float64, float64) { +// ApparentRaDecN 视赤经赤纬(n 变体) / n-parameterised apparent right ascension and declination. +func (module *Mercury) ApparentRaDecN(date time.Time, n int) (float64, float64) { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryApparentRaDecN(basic.UTC2TT(jd), n) + return basic.MercuryApparentRaDecNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// ApparentMagnitudeN 视星等(截断版) / truncated apparent magnitude. -func ApparentMagnitudeN(date time.Time, n int) float64 { +// ApparentMagnitudeN 视星等(n 变体) / n-parameterised apparent magnitude. +func (module *Mercury) ApparentMagnitudeN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.MercuryMagN(basic.UTC2TT(jd), n) + return basic.MercuryMagNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// EarthDistanceN 地球距离(截断版) / truncated Earth distance. -func EarthDistanceN(date time.Time, n int) float64 { +// EarthDistanceN 地球距离(n 变体) / n-parameterised Earth distance. +func (module *Mercury) EarthDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return basic.EarthMercuryAwayN(basic.UTC2TT(jd), n) + return basic.EarthMercuryAwayNWithInstance(basic.UTC2TT(jd), n, module.inst) } -// SunDistanceN 太阳距离(截断版) / truncated Sun distance. -func SunDistanceN(date time.Time, n int) float64 { +// SunDistanceN 太阳距离(n 变体) / n-parameterised Sun distance. +func (module *Mercury) SunDistanceN(date time.Time, n int) float64 { jd := calendar.Date2JD(date.UTC()) - return planet.WherePlanetN(1, 2, basic.UTC2TT(jd), n) + return basic.PlanetCoordinateNWithInstance(1, 2, basic.UTC2TT(jd), n, module.inst) } -// AltitudeN 高度角(截断版) / truncated altitude angle. -func AltitudeN(date time.Time, lon, lat float64, n int) float64 { +// AltitudeN 站心视高度角(n 变体) / n-parameterised topocentric apparent altitude. +func (module *Mercury) AltitudeN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryHeightN(localJD, lon, lat, timezone, n) + return basic.MercuryHeightNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// ZenithN 天顶距(截断版) / truncated zenith distance. -func ZenithN(date time.Time, lon, lat float64, n int) float64 { - return 90 - AltitudeN(date, lon, lat, n) +// ZenithN 天顶距(n 变体) / n-parameterised zenith distance. +func (module *Mercury) ZenithN(date time.Time, lon, lat float64, n int) float64 { + return 90 - module.AltitudeN(date, lon, lat, n) } -// AzimuthN 方位角(截断版) / truncated azimuth angle. -func AzimuthN(date time.Time, lon, lat float64, n int) float64 { +// AzimuthN 站心方位角(n 变体) / n-parameterised topocentric azimuth. +func (module *Mercury) AzimuthN(date time.Time, lon, lat float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryAzimuthN(localJD, lon, lat, timezone, n) + return basic.MercuryAzimuthNWithInstance(localJD, lon, lat, timezone, n, module.inst) } -// HourAngleN 时角(截断版) / truncated hour angle. -func HourAngleN(date time.Time, lon float64, n int) float64 { +// HourAngleN 时角(n 变体) / n-parameterised hour angle. +func (module *Mercury) HourAngleN(date time.Time, lon float64, n int) float64 { localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - return basic.MercuryHourAngleN(localJD, lon, timezone, n) + return basic.MercuryHourAngleNWithInstance(localJD, lon, timezone, n, module.inst) } -// CulminationTimeN 中天时间(截断版) / truncated culmination time. -func CulminationTimeN(date time.Time, lon float64, n int) time.Time { +// CulminationTimeN 中天时间(n 变体) / n-parameterised culmination time. +func (module *Mercury) CulminationTimeN(date time.Time, lon float64, n int) time.Time { date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - calcJD := basic.MercuryCulminationTimeN(localJD, lon, timezone, n) - timezone/24.0 + calcJD := basic.MercuryCulminationTimeNWithInstance(localJD, lon, timezone, n, module.inst) - timezone/24.0 return basic.JD2DateByZone(calcJD, date.Location(), false) } -// RiseTimeN 升起时间(截断版) / truncated rise time. -func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// RiseTimeN 升起时间(n 变体) / n-parameterised rise time. +func (module *Mercury) RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -107,17 +106,17 @@ func RiseTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MercuryRiseTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.MercuryRiseTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } -// DownTimeN 落下时间别名(截断版) / truncated down-time alias. -func DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { - return SetTimeN(date, lon, lat, height, aero, n) +// DownTimeN 落下时间别名(n 变体) / n-parameterised down-time alias. +func (module *Mercury) DownTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { + return module.SetTimeN(date, lon, lat, height, aero, n) } -// SetTimeN 落下时间(截断版) / truncated set time. -func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { +// SetTimeN 落下时间(n 变体) / n-parameterised set time. +func (module *Mercury) SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time.Time, error) { var aeroFloat float64 if aero { aeroFloat = 1 @@ -126,6 +125,6 @@ func SetTimeN(date time.Time, lon, lat, height float64, aero bool, n int) (time. localJD := basic.Date2JD(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 - riseJD, err := basic.MercurySetTimeN(localJD, lon, lat, timezone, aeroFloat, height, n) + riseJD, err := basic.MercurySetTimeNWithInstance(localJD, lon, lat, timezone, aeroFloat, height, n, module.inst) return riseSetResult(date, riseJD, err) } diff --git a/moon/api_default.go b/moon/api_default.go new file mode 100644 index 0000000..537eafa --- /dev/null +++ b/moon/api_default.go @@ -0,0 +1,417 @@ +package moon + +import ( + "time" +) + +// Altitude 月亮高度角,date 的时区参与地方时计算 / lunar altitude; the zone of date enters local time. +func Altitude(date time.Time, lon, lat float64) float64 { + return defaultModule.Altitude(date, lon, lat) +} + +// ApogeesInMonth 指定年月内的所有月球远地点 / all lunar apogees in the given Gregorian month. +func ApogeesInMonth(year int, month time.Month) []ApsisInfo { + return defaultModule.ApogeesInMonth(year, month) +} + +// ApparentAltitude 月亮视高度角 / apparent lunar altitude. +func ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return defaultModule.ApparentAltitude(date, lon, lat, pressureHPa, temperatureC) +} + +// ApparentDec 月亮站心视赤纬 / apparent topocentric declination. +func ApparentDec(date time.Time, lon, lat float64) float64 { + return defaultModule.ApparentDec(date, lon, lat) +} + +// ApparentLo 月亮地心视黄经 / apparent geocentric ecliptic longitude. +func ApparentLo(date time.Time) float64 { return defaultModule.ApparentLo(date) } + +// ApparentRa 月亮站心视赤经,date 的时区参与地方时计算 / apparent topocentric right ascension; the zone of date enters local time. +func ApparentRa(date time.Time, lon, lat float64) float64 { + return defaultModule.ApparentRa(date, lon, lat) +} + +// ApparentRaDec 月亮站心视赤经、视赤纬 / apparent topocentric right ascension and declination. +func ApparentRaDec(date time.Time, lon, lat float64) (float64, float64) { + return defaultModule.ApparentRaDec(date, lon, lat) +} + +// ApparentZenith 月亮视天顶距 / apparent lunar zenith distance. +func ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 { + return defaultModule.ApparentZenith(date, lon, lat, pressureHPa, temperatureC) +} + +// AscendingNode 月球升交点黄经 / ascending node longitude of the Moon. +func AscendingNode(date time.Time) float64 { return defaultModule.AscendingNode(date) } + +// AscendingNodeN 月球升交点黄经(n 变体) / n-parameterised ascending node longitude of the Moon. +func AscendingNodeN(date time.Time, n int) float64 { return defaultModule.AscendingNodeN(date, n) } + +// Azimuth 月亮方位角,正北为 0° 向东增加 / lunar azimuth from north through east. +func Azimuth(date time.Time, lon, lat float64) float64 { return defaultModule.Azimuth(date, lon, lat) } + +// BrightLimbPositionAngle 月亮明亮边缘位置角,单位度 / position angle of the Moon's bright limb in degrees. +func BrightLimbPositionAngle(date time.Time) float64 { + return defaultModule.BrightLimbPositionAngle(date) +} + +// BrightLimbPositionAngleN 月亮明亮边缘位置角(n 变体),单位度 / n-parameterised position angle of the Moon's bright limb in degrees. +func BrightLimbPositionAngleN(date time.Time, n int) float64 { + return defaultModule.BrightLimbPositionAngleN(date, n) +} + +// ClosestConjunctionWithPlanet 最近一次行星合月(赤经合) / closest Moon-planet conjunction. +func ClosestConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { + return defaultModule.ClosestConjunctionWithPlanet(date, planet) +} + +// ClosestFirstQuarter 最近上弦英文别名 / English alias for ClosestShangXianYue. +func ClosestFirstQuarter(date time.Time) time.Time { return defaultModule.ClosestFirstQuarter(date) } + +// ClosestFullMoon 最近望月英文别名 / English alias for ClosestWangYue. +func ClosestFullMoon(date time.Time) time.Time { return defaultModule.ClosestFullMoon(date) } + +// ClosestLastQuarter 最近下弦英文别名 / English alias for ClosestXiaXianYue. +func ClosestLastQuarter(date time.Time) time.Time { return defaultModule.ClosestLastQuarter(date) } + +// ClosestMaximumNorthDeclination 离指定时刻最近一次月球最大北赤纬 / closest maximum northern lunar declination to date. +func ClosestMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.ClosestMaximumNorthDeclination(date) +} + +// ClosestMaximumSouthDeclination 离指定时刻最近一次月球最大南赤纬 / closest maximum southern lunar declination to date. +func ClosestMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.ClosestMaximumSouthDeclination(date) +} + +// ClosestNewMoon 最近朔月英文别名 / English alias for ClosestShuoYue. +func ClosestNewMoon(date time.Time) time.Time { return defaultModule.ClosestNewMoon(date) } + +// ClosestShangXianYue 最近上弦,结果保持 date 的时区 / closest first quarter in date's zone. +func ClosestShangXianYue(date time.Time) time.Time { return defaultModule.ClosestShangXianYue(date) } + +// ClosestShuoYue 最近朔月,结果保持 date 的时区 / closest new moon in date's zone. +func ClosestShuoYue(date time.Time) time.Time { return defaultModule.ClosestShuoYue(date) } + +// ClosestWangYue 最近望月,结果保持 date 的时区 / closest full moon in date's zone. +func ClosestWangYue(date time.Time) time.Time { return defaultModule.ClosestWangYue(date) } + +// ClosestXiaXianYue 最近下弦,结果保持 date 的时区 / closest last quarter in date's zone. +func ClosestXiaXianYue(date time.Time) time.Time { return defaultModule.ClosestXiaXianYue(date) } + +// CulminationTime 月亮中天时刻,取 date 时区的当地日期 / culmination time on date's local day. +func CulminationTime(date time.Time, lon, lat float64) time.Time { + return defaultModule.CulminationTime(date, lon, lat) +} + +// DescendingNode 月球降交点黄经 / descending node longitude of the Moon. +func DescendingNode(date time.Time) float64 { return defaultModule.DescendingNode(date) } + +// DescendingNodeN 月球降交点黄经(n 变体) / n-parameterised descending node longitude of the Moon. +func DescendingNodeN(date time.Time, n int) float64 { return defaultModule.DescendingNodeN(date, n) } + +// Diameter 月亮视直径,单位角秒 / apparent lunar diameter in arcseconds. +func Diameter(date time.Time) float64 { return defaultModule.Diameter(date) } + +// DiameterN 月亮视直径(n 变体),单位角秒 / n-parameterised apparent lunar diameter in arcseconds. +func DiameterN(date time.Time, n int) float64 { return defaultModule.DiameterN(date, n) } + +// DownTime 月落时刻别名,同 SetTime / deprecated alias of SetTime. +func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.DownTime(date, lon, lat, height, aero) +} + +// EarthDistance 地月距离(千米) / Earth-Moon distance in kilometers. +func EarthDistance(date time.Time) float64 { + return defaultModule.EarthDistance(date) +} + +// FindBestPlanetOccultations 窗口内每次行星月掩的全球海平面几何掩甚点,可见性不参与选点 / global sea-level geometric greatest point of each planetary occultation. +func FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { + return defaultModule.FindBestPlanetOccultations(start, end, planet, options) +} + +// FindBestStarOccultations 窗口内每次恒星月掩的全球海平面几何掩甚点,可见性不参与选点 / global sea-level geometric greatest point of each stellar occultation. +func FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) { + return defaultModule.FindBestStarOccultations(start, end, star, options) +} + +// FindPlanetOccultationPaths 窗口内每次行星月掩的全球外接触掩带 / global outer-contact footprint of each planetary occultation. +func FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, + options OccultationPathOptions) ([]PlanetOccultationPath, error) { + return defaultModule.FindPlanetOccultationPaths(start, end, planet, options) +} + +// FindPlanetOccultations 固定地点的有限盘面行星月掩,忽略行星环 / planetary occultations at a fixed site, rings excluded. +func FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) { + return defaultModule.FindPlanetOccultations(start, end, planet, longitude, latitude, height, options) +} + +// FindStarOccultationPaths 点源恒星月掩的全球掩带,不加载内嵌星表 / global footprint of a stellar occultation; no embedded catalog is loaded. +func FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) { + return defaultModule.FindStarOccultationPaths(start, end, star, options) +} + +// FindStarOccultations 固定地点的点源恒星月掩,不加载内嵌星表 / stellar occultations at a fixed site; no embedded catalog is loaded. +func FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, + options OccultationSearchOptions) ([]StarOccultationInfo, error) { + return defaultModule.FindStarOccultations(start, end, star, longitude, latitude, height, options) +} + +// FirstQuarter 上弦英文别名 / English alias for ShangXianYue. +func FirstQuarter(year float64) time.Time { return defaultModule.FirstQuarter(year) } + +// FullMoon 望月英文别名 / English alias for WangYue. +func FullMoon(year float64) time.Time { return defaultModule.FullMoon(year) } + +// GeocentricApparentDec 月亮地心视赤纬 / apparent geocentric declination. +func GeocentricApparentDec(date time.Time) float64 { return defaultModule.GeocentricApparentDec(date) } + +// GeocentricApparentRa 月亮地心视赤经 / apparent geocentric right ascension. +func GeocentricApparentRa(date time.Time) float64 { return defaultModule.GeocentricApparentRa(date) } + +// GeocentricApparentRaDec 月亮地心视赤经、视赤纬 / apparent geocentric right ascension and declination. +func GeocentricApparentRaDec(date time.Time) (float64, float64) { + return defaultModule.GeocentricApparentRaDec(date) +} + +// HourAngle 月亮时角,date 的时区参与地方时计算 / lunar hour angle; the zone of date enters local time. +func HourAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.HourAngle(date, lon, lat) +} + +// LastConjunctionWithPlanet 上一次行星合月(赤经合) / previous Moon-planet conjunction. +func LastConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { + return defaultModule.LastConjunctionWithPlanet(date, planet) +} + +// LastFirstQuarter 上一次上弦英文别名 / English alias for LastShangXianYue. +func LastFirstQuarter(date time.Time) time.Time { return defaultModule.LastFirstQuarter(date) } + +// LastFullMoon 上一次望月英文别名 / English alias for LastWangYue. +func LastFullMoon(date time.Time) time.Time { return defaultModule.LastFullMoon(date) } + +// LastLastQuarter 上一次下弦英文别名 / English alias for LastXiaXianYue. +func LastLastQuarter(date time.Time) time.Time { return defaultModule.LastLastQuarter(date) } + +// LastMaximumNorthDeclination 指定时刻之前最近一次月球最大北赤纬 / last maximum northern lunar declination at or before date. +func LastMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.LastMaximumNorthDeclination(date) +} + +// LastMaximumSouthDeclination 指定时刻之前最近一次月球最大南赤纬 / last maximum southern lunar declination at or before date. +func LastMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.LastMaximumSouthDeclination(date) +} + +// LastNewMoon 上一次朔月英文别名 / English alias for LastShuoYue. +func LastNewMoon(date time.Time) time.Time { return defaultModule.LastNewMoon(date) } + +// LastQuarter 下弦英文别名 / English alias for XiaXianYue. +func LastQuarter(year float64) time.Time { return defaultModule.LastQuarter(year) } + +// LastShangXianYue 上一次上弦,结果保持 date 的时区 / previous first quarter in date's zone. +func LastShangXianYue(date time.Time) time.Time { return defaultModule.LastShangXianYue(date) } + +// LastShuoYue 上一次朔月,结果保持 date 的时区 / previous new moon in date's zone. +func LastShuoYue(date time.Time) time.Time { return defaultModule.LastShuoYue(date) } + +// LastWangYue 上一次望月,结果保持 date 的时区 / previous full moon in date's zone. +func LastWangYue(date time.Time) time.Time { return defaultModule.LastWangYue(date) } + +// LastXiaXianYue 上一次下弦,结果保持 date 的时区 / previous last quarter in date's zone. +func LastXiaXianYue(date time.Time) time.Time { return defaultModule.LastXiaXianYue(date) } + +// MaximumNorthDeclinationsInMonth 指定年月内的所有月球最大北赤纬事件 / all maximum northern lunar declination events in the given Gregorian month. +func MaximumNorthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { + return defaultModule.MaximumNorthDeclinationsInMonth(year, month) +} + +// MaximumSouthDeclinationsInMonth 指定年月内的所有月球最大南赤纬事件 / all maximum southern lunar declination events in the given Gregorian month. +func MaximumSouthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo { + return defaultModule.MaximumSouthDeclinationsInMonth(year, month) +} + +// NewMoon 朔月英文别名 / English alias for ShuoYue. +func NewMoon(year float64) time.Time { return defaultModule.NewMoon(year) } + +// NextConjunctionWithPlanet 下一次行星合月(赤经合) / next Moon-planet conjunction. +func NextConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { + return defaultModule.NextConjunctionWithPlanet(date, planet) +} + +// NextFirstQuarter 下一次上弦英文别名 / English alias for NextShangXianYue. +func NextFirstQuarter(date time.Time) time.Time { return defaultModule.NextFirstQuarter(date) } + +// NextFullMoon 下一次望月英文别名 / English alias for NextWangYue. +func NextFullMoon(date time.Time) time.Time { return defaultModule.NextFullMoon(date) } + +// NextLastQuarter 下一次下弦英文别名 / English alias for NextXiaXianYue. +func NextLastQuarter(date time.Time) time.Time { return defaultModule.NextLastQuarter(date) } + +// NextMaximumNorthDeclination 指定时刻之后最近一次月球最大北赤纬 / next maximum northern lunar declination after date. +func NextMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.NextMaximumNorthDeclination(date) +} + +// NextMaximumSouthDeclination 指定时刻之后最近一次月球最大南赤纬 / next maximum southern lunar declination after date. +func NextMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo { + return defaultModule.NextMaximumSouthDeclination(date) +} + +// NextNewMoon 下一次朔月英文别名 / English alias for NextShuoYue. +func NextNewMoon(date time.Time) time.Time { return defaultModule.NextNewMoon(date) } + +// NextShangXianYue 下一次上弦,结果保持 date 的时区 / next first quarter in date's zone. +func NextShangXianYue(date time.Time) time.Time { return defaultModule.NextShangXianYue(date) } + +// NextShuoYue 下一次朔月,结果保持 date 的时区 / next new moon in date's zone. +func NextShuoYue(date time.Time) time.Time { return defaultModule.NextShuoYue(date) } + +// NextWangYue 下一次望月,结果保持 date 的时区 / next full moon in date's zone. +func NextWangYue(date time.Time) time.Time { return defaultModule.NextWangYue(date) } + +// NextXiaXianYue 下一次下弦,结果保持 date 的时区 / next last quarter in date's zone. +func NextXiaXianYue(date time.Time) time.Time { return defaultModule.NextXiaXianYue(date) } + +// ParallacticAngle 月亮站心视差角(天顶方向角) / topocentric lunar parallactic angle. +func ParallacticAngle(date time.Time, lon, lat float64) float64 { + return defaultModule.ParallacticAngle(date, lon, lat) +} + +// PerigeesInMonth 指定年月内的所有月球近地点 / all lunar perigees in the given Gregorian month. +func PerigeesInMonth(year int, month time.Month) []ApsisInfo { + return defaultModule.PerigeesInMonth(year, month) +} + +// Phase 月面受照比例,取值 [0, 1] / illuminated fraction in [0, 1]. +func Phase(date time.Time) float64 { return defaultModule.Phase(date) } + +// PhaseDesc 中文月相名称 / Chinese lunar phase name. +func PhaseDesc(date time.Time) string { return defaultModule.PhaseDesc(date) } + +// Physical 月球物理观测参数 / physical observing parameters of the Moon. +func Physical(date time.Time) PhysicalInfo { return defaultModule.Physical(date) } + +// PhysicalN 月球物理观测参数(n 变体) / n-parameterised physical observing parameters of the Moon. +func PhysicalN(date time.Time, n int) PhysicalInfo { return defaultModule.PhysicalN(date, n) } + +// PlanetOccultationDiagram 已求解固定地点行星月掩的图形几何 / diagram geometry of a solved fixed-site planetary occultation. +func PlanetOccultationDiagram( + info PlanetOccultationInfo, + options PlanetOccultationDiagramOptions, +) PlanetOccultationDiagramResult { + return defaultModule.PlanetOccultationDiagram(info, options) +} + +// PlanetOccultationFootprintsAt 指定时刻行星偏掩、全掩可见足迹 / visible partial and total planetary footprints at one instant. +func PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) { + return defaultModule.PlanetOccultationFootprintsAt(at, planet) +} + +// RiseTime 月出时刻,取 date 时区的当地日期;height 为椭球高(米),aero 为真时按大气折射与实时视半径算上缘 / moonrise on date's local day. +func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.RiseTime(date, lon, lat, height, aero) +} + +// Semidiameter 月亮视半径,单位角秒 / apparent lunar semidiameter in arcseconds. +func Semidiameter(date time.Time) float64 { return defaultModule.Semidiameter(date) } + +// SemidiameterN 月亮视半径(n 变体),单位角秒 / n-parameterised apparent lunar semidiameter in arcseconds. +func SemidiameterN(date time.Time, n int) float64 { return defaultModule.SemidiameterN(date, n) } + +// SetTime 月落时刻,参数同 RiseTime / moonset on date's local day. +func SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) { + return defaultModule.SetTime(date, lon, lat, height, aero) +} + +// ShangXianYue 上弦锚点解,year 为公历小数年,结果为 UTC / first quarter near a decimal-year anchor, in UTC. +func ShangXianYue(year float64) time.Time { return defaultModule.ShangXianYue(year) } + +// ShuoYue 朔月锚点解,year 为公历小数年,结果为 UTC / new moon near a decimal-year anchor, in UTC. +func ShuoYue(year float64) time.Time { return defaultModule.ShuoYue(year) } + +// StarOccultationLimbProfile 精密恒星月掩:给出全部遮挡区间与带内采样,limb 为 nil 时用模块月缘 / precise stellar occultation; nil limb uses the module profile. +func StarOccultationLimbProfile(info StarOccultationInfo, star StarCoordinate, limb LunarLimb, options StarOccultationLimbProfileOptions) (StarOccultationLimbProfileResult, error) { + return defaultModule.StarOccultationLimbProfile(info, star, limb, options) +} + +// StarOccultationDiagram 已求解固定地点恒星月掩的图形几何 / diagram geometry of a solved fixed-site stellar occultation. +func StarOccultationDiagram( + info StarOccultationInfo, + star StarCoordinate, + options StarOccultationDiagramOptions, +) StarOccultationDiagramResult { + return defaultModule.StarOccultationDiagram(info, star, options) +} + +// StarOccultationFootprintAt 指定时刻点源恒星月掩可见足迹 / visible stellar-occultation footprint at one instant. +func StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) { + return defaultModule.StarOccultationFootprintAt(at, star) +} + +// SunMoonLoDiff 月日视黄经差,取 [0, 360),朔近 0°、望近 180° / Moon minus Sun apparent longitude in [0, 360). +func SunMoonLoDiff(date time.Time) float64 { return defaultModule.SunMoonLoDiff(date) } + +// TopocentricBrightLimbPositionAngle 月亮站心明亮边缘位置角(度),height 为椭球高(米) / topocentric bright-limb position angle in degrees. +func TopocentricBrightLimbPositionAngle(date time.Time, observerLon, observerLat, height float64) float64 { + return defaultModule.TopocentricBrightLimbPositionAngle(date, observerLon, observerLat, height) +} + +// TopocentricBrightLimbPositionAngleN 月亮站心明亮边缘位置角(n 变体),单位度 / n-parameterised topocentric position angle of the Moon's bright limb in degrees. +func TopocentricBrightLimbPositionAngleN(date time.Time, observerLon, observerLat, height float64, n int) float64 { + return defaultModule.TopocentricBrightLimbPositionAngleN(date, observerLon, observerLat, height, n) +} + +// TopocentricPhysical 月球站心物理观测参数,height 为椭球高(米) / topocentric physical observing parameters of the Moon. +func TopocentricPhysical(date time.Time, observerLon, observerLat, height float64) PhysicalInfo { + return defaultModule.TopocentricPhysical(date, observerLon, observerLat, height) +} + +// TopocentricPhysicalN 月球站心物理观测参数(n 变体) / n-parameterised topocentric physical observing parameters of the Moon. +func TopocentricPhysicalN(date time.Time, observerLon, observerLat, height float64, n int) PhysicalInfo { + return defaultModule.TopocentricPhysicalN(date, observerLon, observerLat, height, n) +} + +// TrueBo 月亮地心真黄纬 / geocentric true ecliptic latitude. +func TrueBo(date time.Time) float64 { return defaultModule.TrueBo(date) } + +// TrueBoN 月亮地心真黄纬,n<0 取完整级数,n>=0 时半解析模型用快速表、Legacy 按 n 截断 / geocentric true ecliptic latitude with series selector n. +func TrueBoN(date time.Time, n int) float64 { return defaultModule.TrueBoN(date, n) } + +// TrueDec 月亮地心真赤纬 / geocentric true declination. +func TrueDec(date time.Time) float64 { return defaultModule.TrueDec(date) } + +// TrueLo 月亮地心真黄经 / geocentric true ecliptic longitude. +func TrueLo(date time.Time) float64 { return defaultModule.TrueLo(date) } + +// TrueLoN 月亮地心真黄经,n<0 取完整级数,n>=0 时半解析模型用快速表、Legacy 按 n 截断 / geocentric true ecliptic longitude with series selector n. +func TrueLoN(date time.Time, n int) float64 { return defaultModule.TrueLoN(date, n) } + +// TrueRa 月亮地心真赤经 / geocentric true right ascension. +func TrueRa(date time.Time) float64 { return defaultModule.TrueRa(date) } + +// TrueRaDec 月亮地心真赤经、真赤纬 / geocentric true right ascension and declination. +func TrueRaDec(date time.Time) (float64, float64) { return defaultModule.TrueRaDec(date) } + +// WangYue 望月锚点解,year 为公历小数年,结果为 UTC / full moon near a decimal-year anchor, in UTC. +func WangYue(year float64) time.Time { return defaultModule.WangYue(year) } + +// XiaXianYue 下弦锚点解,year 为公历小数年,结果为 UTC / last quarter near a decimal-year anchor, in UTC. +func XiaXianYue(year float64) time.Time { return defaultModule.XiaXianYue(year) } + +// Zenith 月亮天顶距 / lunar zenith distance. +func Zenith(date time.Time, lon, lat float64) float64 { return defaultModule.Zenith(date, lon, lat) } + +// RiseTimeWithAtmosphere 月出时刻,按给定气压(hPa)与气温(°C)计算大气折射 / moonrise time with explicit pressure and temperature. +func RiseTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.RiseTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} + +// SetTimeWithAtmosphere 月落时刻,按给定气压(hPa)与气温(°C)计算大气折射 / moonset time with explicit pressure and temperature. +func SetTimeWithAtmosphere(date time.Time, lon, lat, height, pressureHPa, temperatureC float64) (time.Time, error) { + return defaultModule.SetTimeWithAtmosphere(date, lon, lat, height, pressureHPa, temperatureC) +} diff --git a/moon/apsis.go b/moon/apsis.go index 333ed27..60471c7 100644 --- a/moon/apsis.go +++ b/moon/apsis.go @@ -15,13 +15,13 @@ type ApsisInfo struct { } // PerigeesInMonth 指定年月内的所有月球近地点 / all lunar perigees in the given Gregorian month. -func PerigeesInMonth(year int, month time.Month) []ApsisInfo { - return convertMoonApsisInfos(basic.MoonPerigees(year, month)) +func (module *Moon) PerigeesInMonth(year int, month time.Month) []ApsisInfo { + return convertMoonApsisInfos(basic.MoonPerigeesWithInstance(year, month, module.inst)) } // ApogeesInMonth 指定年月内的所有月球远地点 / all lunar apogees in the given Gregorian month. -func ApogeesInMonth(year int, month time.Month) []ApsisInfo { - return convertMoonApsisInfos(basic.MoonApogees(year, month)) +func (module *Moon) ApogeesInMonth(year int, month time.Month) []ApsisInfo { + return convertMoonApsisInfos(basic.MoonApogeesWithInstance(year, month, module.inst)) } func convertMoonApsisInfos(events []basic.ApsisEvent) []ApsisInfo { diff --git a/moon/conjunction.go b/moon/conjunction.go index 9266086..8c94048 100644 --- a/moon/conjunction.go +++ b/moon/conjunction.go @@ -45,28 +45,28 @@ func validConjunctionPlanet(planet ConjunctionPlanet) bool { } // LastConjunctionWithPlanet 上一次行星合月(赤经合) / previous Moon-planet conjunction. -func LastConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { +func (module *Moon) LastConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { if !validConjunctionPlanet(planet) { return time.Time{} } jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.LastMoonPlanetConjunction(jde, conjunctionPlanetToBasic(planet)), date.Location(), false) + return basic.JD2DateByZone(basic.LastMoonPlanetConjunctionWithInstance(jde, conjunctionPlanetToBasic(planet), module.inst), date.Location(), false) } // NextConjunctionWithPlanet 下一次行星合月(赤经合) / next Moon-planet conjunction. -func NextConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { +func (module *Moon) NextConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { if !validConjunctionPlanet(planet) { return time.Time{} } jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.NextMoonPlanetConjunction(jde, conjunctionPlanetToBasic(planet)), date.Location(), false) + return basic.JD2DateByZone(basic.NextMoonPlanetConjunctionWithInstance(jde, conjunctionPlanetToBasic(planet), module.inst), date.Location(), false) } // ClosestConjunctionWithPlanet 最近一次行星合月(赤经合) / closest Moon-planet conjunction. -func ClosestConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { +func (module *Moon) ClosestConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time { if !validConjunctionPlanet(planet) { return time.Time{} } jde := basic.UTC2TT(basic.Date2JD(date.UTC())) - return basic.JD2DateByZone(basic.ClosestMoonPlanetConjunction(jde, conjunctionPlanetToBasic(planet)), date.Location(), false) + return basic.JD2DateByZone(basic.ClosestMoonPlanetConjunctionWithInstance(jde, conjunctionPlanetToBasic(planet), module.inst), date.Location(), false) } diff --git a/moon/context.go b/moon/context.go new file mode 100644 index 0000000..e469d17 --- /dev/null +++ b/moon/context.go @@ -0,0 +1,73 @@ +package moon + +import ( + "time" + + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" +) + +// Moon 指定星历实例的计算模块 / computation module bound to an ephemeris instance. +type Moon struct { + inst *ephem.Instance + // lunarLimb 是月掩搜索的默认月缘剖面,nil 为圆月面。 + lunarLimb basic.LunarLimb +} + +// NewWithInstance 创建指定星历实例的模块,nil 使用全局模型 / create a module; nil uses the global model. +func NewWithInstance(inst *ephem.Instance) *Moon { return &Moon{inst: inst} } + +// WithLunarLimb 返回使用指定月缘剖面的副本,选项中未设置月缘时采用它 / copy with a default lunar limb for occultations. +func (module *Moon) WithLunarLimb(limb basic.LunarLimb) *Moon { + copied := *module + copied.lunarLimb = limb + return &copied +} + +// occultationOptions 在选项未设置月缘时补上模块默认值。 +func (module *Moon) occultationOptions(options OccultationSearchOptions) OccultationSearchOptions { + if options.LunarLimb == nil { + options.LunarLimb = module.lunarLimb + } + return options +} + +var defaultModule Moon + +// EarthDistance 地月距离 / Earth-Moon distance. +func (module *Moon) EarthDistance(date time.Time) float64 { + jde := basic.UTC2TT(basic.Date2JD(date)) + model := ephem.Active() + if module.inst != nil { + model = module.inst.Model() + } + if model == ephem.Legacy { + return basic.MoonAway(jde) + } + return basic.HMoonAwayWithInstance(jde, module.inst) +} + +// PlanetOccultationInfoInUT1 返回固定地点行星月掩的 UT1 时刻副本 / copy in UT1 of a local planetary result. +func (module *Moon) PlanetOccultationInfoInUT1(info basic.PlanetOccultationInfo) basic.PlanetOccultationInfo { + return PlanetOccultationInfoInUT1(info) +} + +// PlanetOccultationPathInUT1 返回行星月掩全球掩带的 UT1 时刻副本 / copy in UT1 of a planetary path. +func (module *Moon) PlanetOccultationPathInUT1(path basic.PlanetOccultationPath) basic.PlanetOccultationPath { + return PlanetOccultationPathInUT1(path) +} + +// StarCoordinateFromStarData 将星表记录转换为恒星月掩搜索接受的 J2000 输入;不会加载星表 / convert a catalog record to a J2000 occultation input. +func (module *Moon) StarCoordinateFromStarData(star StarData) (StarCoordinate, error) { + return StarCoordinateFromStarData(star) +} + +// StarOccultationInfoInUT1 返回固定地点恒星月掩的 UT1 时刻副本 / copy in UT1 of a local stellar result. +func (module *Moon) StarOccultationInfoInUT1(info basic.StarOccultationInfo) basic.StarOccultationInfo { + return StarOccultationInfoInUT1(info) +} + +// StarOccultationPathInUT1 返回恒星月掩全球路径的 UT1 时刻副本 / copy in UT1 of a stellar path. +func (module *Moon) StarOccultationPathInUT1(path basic.StarOccultationPath) basic.StarOccultationPath { + return StarOccultationPathInUT1(path) +} diff --git a/moon/context_test.go b/moon/context_test.go new file mode 100644 index 0000000..5dc2fcf --- /dev/null +++ b/moon/context_test.go @@ -0,0 +1,144 @@ +package moon + +import ( + "b612.me/astro/basic" + "b612.me/astro/internal/ephem" + "testing" + "time" +) + +type contextAPI interface { + Altitude(date time.Time, lon, lat float64) float64 + ApogeesInMonth(year int, month time.Month) []ApsisInfo + ApparentAltitude(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 + ApparentDec(date time.Time, lon, lat float64) float64 + ApparentLo(date time.Time) float64 + ApparentRa(date time.Time, lon, lat float64) float64 + ApparentRaDec(date time.Time, lon, lat float64) (float64, float64) + ApparentZenith(date time.Time, lon, lat, pressureHPa, temperatureC float64) float64 + AscendingNode(date time.Time) float64 + AscendingNodeN(date time.Time, n int) float64 + Azimuth(date time.Time, lon, lat float64) float64 + BrightLimbPositionAngle(date time.Time) float64 + BrightLimbPositionAngleN(date time.Time, n int) float64 + ClosestConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time + ClosestFirstQuarter(date time.Time) time.Time + ClosestFullMoon(date time.Time) time.Time + ClosestLastQuarter(date time.Time) time.Time + ClosestMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo + ClosestMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo + ClosestNewMoon(date time.Time) time.Time + ClosestShangXianYue(date time.Time) time.Time + ClosestShuoYue(date time.Time) time.Time + ClosestWangYue(date time.Time) time.Time + ClosestXiaXianYue(date time.Time) time.Time + CulminationTime(date time.Time, lon, lat float64) time.Time + DescendingNode(date time.Time) float64 + DescendingNodeN(date time.Time, n int) float64 + Diameter(date time.Time) float64 + DiameterN(date time.Time, n int) float64 + DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + EarthDistance(date time.Time) float64 + FindBestPlanetOccultations(start, end time.Time, planet OccultationPlanet, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) + FindBestStarOccultations(start, end time.Time, star StarCoordinate, options OccultationSearchOptions) ([]StarOccultationInfo, error) + FindPlanetOccultationPaths(start, end time.Time, planet OccultationPlanet, + options OccultationPathOptions) ([]PlanetOccultationPath, error) + FindPlanetOccultations(start, end time.Time, planet OccultationPlanet, longitude, latitude, height float64, + options OccultationSearchOptions) ([]PlanetOccultationInfo, error) + FindStarOccultationPaths(start, end time.Time, star StarCoordinate, options OccultationPathOptions) ([]StarOccultationPath, error) + FindStarOccultations(start, end time.Time, star StarCoordinate, longitude, latitude, height float64, + options OccultationSearchOptions) ([]StarOccultationInfo, error) + FirstQuarter(year float64) time.Time + FullMoon(year float64) time.Time + GeocentricApparentDec(date time.Time) float64 + GeocentricApparentRa(date time.Time) float64 + GeocentricApparentRaDec(date time.Time) (float64, float64) + HourAngle(date time.Time, lon, lat float64) float64 + LastConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time + LastFirstQuarter(date time.Time) time.Time + LastFullMoon(date time.Time) time.Time + LastLastQuarter(date time.Time) time.Time + LastMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo + LastMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo + LastNewMoon(date time.Time) time.Time + LastQuarter(year float64) time.Time + LastShangXianYue(date time.Time) time.Time + LastShuoYue(date time.Time) time.Time + LastWangYue(date time.Time) time.Time + LastXiaXianYue(date time.Time) time.Time + MaximumNorthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo + MaximumSouthDeclinationsInMonth(year int, month time.Month) []MaximumDeclinationInfo + NewMoon(year float64) time.Time + NextConjunctionWithPlanet(date time.Time, planet ConjunctionPlanet) time.Time + NextFirstQuarter(date time.Time) time.Time + NextFullMoon(date time.Time) time.Time + NextLastQuarter(date time.Time) time.Time + NextMaximumNorthDeclination(date time.Time) MaximumDeclinationInfo + NextMaximumSouthDeclination(date time.Time) MaximumDeclinationInfo + NextNewMoon(date time.Time) time.Time + NextShangXianYue(date time.Time) time.Time + NextShuoYue(date time.Time) time.Time + NextWangYue(date time.Time) time.Time + NextXiaXianYue(date time.Time) time.Time + ParallacticAngle(date time.Time, lon, lat float64) float64 + PerigeesInMonth(year int, month time.Month) []ApsisInfo + Phase(date time.Time) float64 + PhaseDesc(date time.Time) string + Physical(date time.Time) PhysicalInfo + PhysicalN(date time.Time, n int) PhysicalInfo + PlanetOccultationDiagram( + info PlanetOccultationInfo, + options PlanetOccultationDiagramOptions, + ) PlanetOccultationDiagramResult + PlanetOccultationFootprintsAt(at time.Time, planet OccultationPlanet) (PlanetOccultationInstant, error) + PlanetOccultationInfoInUT1(info basic.PlanetOccultationInfo) basic.PlanetOccultationInfo + PlanetOccultationPathInUT1(path basic.PlanetOccultationPath) basic.PlanetOccultationPath + RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + Semidiameter(date time.Time) float64 + SemidiameterN(date time.Time, n int) float64 + SetTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, error) + ShangXianYue(year float64) time.Time + ShuoYue(year float64) time.Time + StarCoordinateFromStarData(star StarData) (StarCoordinate, error) + StarOccultationDiagram( + info StarOccultationInfo, + star StarCoordinate, + options StarOccultationDiagramOptions, + ) StarOccultationDiagramResult + StarOccultationFootprintAt(at time.Time, star StarCoordinate) (StarOccultationInstant, error) + StarOccultationInfoInUT1(info basic.StarOccultationInfo) basic.StarOccultationInfo + StarOccultationPathInUT1(path basic.StarOccultationPath) basic.StarOccultationPath + SunMoonLoDiff(date time.Time) float64 + TopocentricBrightLimbPositionAngle(date time.Time, observerLon, observerLat, height float64) float64 + TopocentricBrightLimbPositionAngleN(date time.Time, observerLon, observerLat, height float64, n int) float64 + TopocentricPhysical(date time.Time, observerLon, observerLat, height float64) PhysicalInfo + TopocentricPhysicalN(date time.Time, observerLon, observerLat, height float64, n int) PhysicalInfo + TrueBo(date time.Time) float64 + TrueBoN(date time.Time, n int) float64 + TrueDec(date time.Time) float64 + TrueLo(date time.Time) float64 + TrueLoN(date time.Time, n int) float64 + TrueRa(date time.Time) float64 + TrueRaDec(date time.Time) (float64, float64) + WangYue(year float64) time.Time + XiaXianYue(year float64) time.Time + Zenith(date time.Time, lon, lat float64) float64 +} + +var _ contextAPI = (*Moon)(nil) + +func TestContextModuleIsolation(t *testing.T) { + previous := basic.GetEphemerisModel() + t.Cleanup(func() { basic.SetEphemerisModel(previous) }) + date := time.Date(2024, 10, 6, 12, 0, 0, 0, time.UTC) + for _, model := range []ephem.Model{ephem.Legacy, ephem.Semianalytic} { + basic.SetEphemerisModel(basic.EphemerisModel(model)) + want := ApparentLo(date) + module := NewWithInstance(ephem.NewModelInstance(model)) + basic.SetEphemerisModel(basic.EphemerisModel(1 - int(model))) + if got := module.ApparentLo(date); got != want { + t.Fatalf("model %d: longitude %.12f, want %.12f", model, got, want) + } + } +} diff --git a/moon/diameter.go b/moon/diameter.go index 7e32b59..f802921 100644 --- a/moon/diameter.go +++ b/moon/diameter.go @@ -7,23 +7,23 @@ import ( ) // Semidiameter 月亮视半径,单位角秒 / apparent lunar semidiameter in arcseconds. -func Semidiameter(date time.Time) float64 { - return SemidiameterN(date, -1) +func (module *Moon) Semidiameter(date time.Time) float64 { + return module.SemidiameterN(date, -1) } -// SemidiameterN 月亮视半径(截断版),单位角秒 / truncated apparent lunar semidiameter in arcseconds. -func SemidiameterN(date time.Time, n int) float64 { +// SemidiameterN 月亮视半径(n 变体),单位角秒 / n-parameterised apparent lunar semidiameter in arcseconds. +func (module *Moon) SemidiameterN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.MoonSemidiameterN(basic.UTC2TT(jd), n) + return basic.MoonSemidiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } // Diameter 月亮视直径,单位角秒 / apparent lunar diameter in arcseconds. -func Diameter(date time.Time) float64 { - return DiameterN(date, -1) +func (module *Moon) Diameter(date time.Time) float64 { + return module.DiameterN(date, -1) } -// DiameterN 月亮视直径(截断版),单位角秒 / truncated apparent lunar diameter in arcseconds. -func DiameterN(date time.Time, n int) float64 { +// DiameterN 月亮视直径(n 变体),单位角秒 / n-parameterised apparent lunar diameter in arcseconds. +func (module *Moon) DiameterN(date time.Time, n int) float64 { jd := basic.Date2JD(date.UTC()) - return basic.MoonDiameterN(basic.UTC2TT(jd), n) + return basic.MoonDiameterNWithInstance(basic.UTC2TT(jd), n, module.inst) } diff --git a/moon/doc.go b/moon/doc.go new file mode 100644 index 0000000..04b6ebe --- /dev/null +++ b/moon/doc.go @@ -0,0 +1,5 @@ +// Package moon 月球视位置、升落、月相、天平动、视直径与物理观测参数;角度单位为度。 +// Package moon covers lunar apparent place, rise/set, phase, libration, apparent diameter and physical observing parameters; angles are degrees. +// 求值接口的 N 后缀是 n 变体:n < 0 取完整级数;半解析模型下所有 n >= 0 使用同一张快速表;Legacy 按 n 截断。 +// The N suffix marks an n-variant: n < 0 uses the full series, every n >= 0 uses one fast table under the semianalytic model, and Legacy truncates by n. +package moon diff --git a/moon/limb/NOTICE b/moon/limb/NOTICE new file mode 100644 index 0000000..6128443 --- /dev/null +++ b/moon/limb/NOTICE @@ -0,0 +1,28 @@ +本包内嵌的月缘剖面数据不适用本仓库的 Apache-2.0,遵循其来源条款。完整声明见仓库根目录 /NOTICE(第 1 节)。 +The lunar limb profile embedded in this package is NOT covered by this repository's Apache-2.0 licence; it follows the terms of its source. The full notice is section 1 of /NOTICE in the repository root. + +SELENE (KAGUYA) lunar limb profile — kaguya_lalt.bin + Source data: SELENE Global Grid Topographic Data of the Moon (LALT_GGT_NUM), + dataset sln-l-lalt-5-topo-ggt-num-v2.0, version 2.0, published 2017. + Origin of the data: ISAS/JAXA. + Provided by the SELENE (KAGUYA) Data Archive (DARTS), ISAS/JAXA: + https://darts.isas.jaxa.jp/datasets/darts:sln-l-lalt-5-topo-ggt-num-v2.0 + + Acknowledgement, as requested by the SELENE Data Archive: + "We thank the SELENE(KAGUYA) LALT team and the SELENE Data Archive for + providing the SELENE(KAGUYA) data. SELENE is a Japanese mission developed + and operated by JAXA." + + Modifications: the original PDS3 fixed-width table (1/16 degree global grid, + 2880 x 5760 samples) was converted to a float32 elevation grid and resampled + into a limb-radius profile table on a 1 degree grid of selenographic + (libration) longitude and latitude and a 0.1 degree grid of position angle, + taking max (R + h) cos(alpha) over a +/-9 degree arc per direction; the + result is stored in this project's own zlib-compressed LLB1 container. + + Licence: ISAS Data Policy, + https://www.isas.jaxa.jp/en/researchers/data-policy/ + which states that its rules are compatible with Creative Commons Attribution + 4.0 International, https://creativecommons.org/licenses/by/4.0/ + Provided "as is", without warranties of any kind. ISAS/JAXA is not + responsible for the actions of data users and does not endorse this project. diff --git a/moon/limb/kaguya_lalt.bin b/moon/limb/kaguya_lalt.bin new file mode 100644 index 0000000000000000000000000000000000000000..1d1a336e3a32939a0189659d8e73893a009b2c6e GIT binary patch literal 1456965 zcmV)0K+eBROhPde0000H0000G4gdfE0000003yHu000000PsHm0000003g5s00000 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