/* * Copyright (C) 2021 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include "resource-loader.hpp" #include "physics/orbit/ephemeris.hpp" #include "utility/bit-math.hpp" #include #include /// Offset to time data in the JPL DE header, in bytes. static constexpr std::size_t jpl_de_offset_time = 0xA5C; /// Offset to the first coefficient table in the JPL DE header, in bytes. static constexpr std::size_t jpl_de_offset_table1 = 0xA88; /// Offset to the DE version number in the JPL DE header, in bytes. static constexpr std::size_t jpl_de_offset_denum = 0xB18; /// Offset to the second coefficient table in the JPL DE header, in bytes. static constexpr std::size_t jpl_de_offset_table2 = 0xB1C; /// Offset to the third coefficient table in the JPL DE header, in bytes, if the constant limit has not been exceeded. static constexpr std::size_t jpl_de_offset_table3 = 0xB28; /// Mask to detect bytes in the most significant word of the JPL DE version number. static constexpr std::int32_t jpl_de_denum_endian_mask = 0xFFFF0000; /// Number of items in the first coefficient table. static constexpr std::size_t jpl_de_table1_count = 12; /// Number of items in the second coefficient table. static constexpr std::size_t jpl_de_table2_count = 1; /// Number of items in the third coefficient table. static constexpr std::size_t jpl_de_table3_count = 2; /// Maximum number of items in a JPL DE file. static constexpr std::size_t jpl_de_max_item_count = jpl_de_table1_count + jpl_de_table2_count + jpl_de_table3_count; /// Maximum number of constants in the first set of constant names. static constexpr std::size_t jpl_de_constant_limit = 400; /// Length of a constant name, in bytes. static constexpr std::size_t jpl_de_constant_length = 6; /// Enumerated IDs of the JPL DE items. enum { /// Mercury jpl_de_id_mercury, /// Venus jpl_de_id_venus, /// Earth-Moon barycenter jpl_de_id_embary, /// Mars jpl_de_id_mars, /// Jupiter jpl_de_id_jupiter, /// Saturn jpl_de_id_saturn, /// Uranus jpl_de_id_uranus, /// Neptune jpl_de_id_neptune, /// Pluto jpl_de_id_pluto, /// Moon jpl_de_id_moon, /// Sun jpl_de_id_sun, /// Earth nutation jpl_de_id_earth_nutation, /// Lunar mantle libration jpl_de_id_luma_libration, /// Lunar mantle angular velocity jpl_de_id_luma_angular_velocity, /// TT-TDB jpl_de_id_tt_tdb }; /// Number of components for each JPL DE item. static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] = { 3, // Mercury: x,y,z (km) 3, // Venus: x,y,z (km) 3, // Earth-Moon barycenter: x,y,z (km) 3, // Mars: x,y,z (km) 3, // Jupiter: x,y,z (km) 3, // Saturn: x,y,z (km) 3, // Uranus: x,y,z (km) 3, // Neptune: x,y,z (km) 3, // Pluto: x,y,z (km) 3, // Moon: x,y,z (km) 3, // Sun: x,y,z (km) 2, // Earth nutation: d_psi,d_epsilon (radians) 3, // Lunar mantle libration: phi,theta,ps (radians) 3, // Lunar mantle angular velocity: omega_x,omega_y,omega_z (radians/day) 1 // TT-TDB: t (seconds) }; /// Reads and swaps the byte order of 32-bit numbers. static PHYSFS_sint64 read_swap32(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) { PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); for (std::uint32_t* ptr32 = (uint32_t*)buffer; len >= 8; len -= 8, ++ptr32) *ptr32 = bit::swap32(*ptr32); return status; } /// Reads and swaps the byte order of 64-bit numbers. static PHYSFS_sint64 read_swap64(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) { PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); for (std::uint64_t* ptr64 = (uint64_t*)buffer; len >= 8; len -= 8, ++ptr64) *ptr64 = bit::swap64(*ptr64); return status; } template <> physics::orbit::ephemeris* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { // Init file reading function pointers PHYSFS_sint64 (*read32)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; PHYSFS_sint64 (*read64)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; // Read DE version number std::int32_t denum; PHYSFS_seek(file, jpl_de_offset_denum); PHYSFS_readBytes(file, &denum, sizeof(std::int32_t)); // If file endianness does not match host if (denum & jpl_de_denum_endian_mask) { // Use endian-swapping read functions read32 = &read_swap32; read64 = &read_swap64; } // Read ephemeris time double ephemeris_time[3]; PHYSFS_seek(file, jpl_de_offset_time); read64(file, ephemeris_time, sizeof(double) * 3); // Make time relative to J2000 epoch const double epoch = 2451545.0; ephemeris_time[0] -= epoch; ephemeris_time[1] -= epoch; // Read number of constants std::int32_t constant_count; read32(file, &constant_count, sizeof(std::int32_t)); // Read first coefficient table std::int32_t coeff_table[jpl_de_max_item_count][3]; PHYSFS_seek(file, jpl_de_offset_table1); read32(file, coeff_table, sizeof(std::int32_t) * jpl_de_table1_count * 3); // Read second coefficient table PHYSFS_seek(file, jpl_de_offset_table2); read32(file, &coeff_table[jpl_de_table1_count][0], sizeof(std::int32_t) * jpl_de_table2_count * 3); // Seek past extra constant names if (constant_count > jpl_de_constant_limit) PHYSFS_seek(file, jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length); // Read third coefficient table read32(file, &coeff_table[jpl_de_table1_count + jpl_de_table2_count][0], sizeof(std::int32_t) * jpl_de_table3_count * 3); // Calculate number of coefficients per record std::int32_t record_coeff_count = 0; for (int i = 0; i < jpl_de_max_item_count; ++i) { std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * jpl_de_component_count[i] - 1; record_coeff_count = std::max(record_coeff_count, coeff_count); } // Calculate record size and record count std::size_t record_size = record_coeff_count * sizeof(double); std::size_t record_count = static_cast((ephemeris_time[1] - ephemeris_time[0]) / ephemeris_time[2]); // Calculate coefficient strides std::size_t strides[11]; for (int i = 0; i < 11; ++i) strides[i] = coeff_table[i][2] * coeff_table[i][1] * 3; // Allocate and resize ephemeris to accommodate items 0-10 physics::orbit::ephemeris* ephemeris = new physics::orbit::ephemeris(); ephemeris->resize(11); // Init trajectories for (int i = 0; i < 11; ++i) { auto& trajectory = (*ephemeris)[i]; trajectory.t0 = ephemeris_time[0]; trajectory.t1 = ephemeris_time[1]; trajectory.dt = ephemeris_time[2] / static_cast(coeff_table[i][2]); trajectory.n = coeff_table[i][1]; trajectory.a.resize(record_count * strides[i]); } // Read coefficients for (std::size_t i = 0; i < record_count; ++i) { // Seek to coefficient record PHYSFS_seek(file, (i + 2) * record_size + 2 * sizeof(double)); for (int j = 0; j < 11; ++j) { read64(file, &(*ephemeris)[j].a[i * strides[j]], sizeof(double) * strides[j]); } } return ephemeris; }