💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
7.6 KiB

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "resource-loader.hpp"
#include "physics/orbit/ephemeris.hpp"
#include "utility/bit-math.hpp"
#include <cstdint>
#include <physfs.h>
/// 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<double>* resource_loader<physics::orbit::ephemeris<double>>::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<std::size_t>((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<double>* ephemeris = new physics::orbit::ephemeris<double>();
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<double>(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;
}