00001 // jpleph.cpp 00002 // 00003 // Copyright (C) 2004, Chris Laurel <claurel@shatters.net> 00004 // 00005 // This program is free software; you can redistribute it and/or 00006 // modify it under the terms of the GNU General Public License 00007 // as published by the Free Software Foundation; either version 2 00008 // of the License, or (at your option) any later version. 00009 // 00010 // Load JPL's DE200, DE405, and DE406 ephemerides and compute planet 00011 // positions. 00012 00013 #include <fstream> 00014 #include <iomanip> 00015 #include <cstdio> 00016 #include <cassert> 00017 #include <celutil/bytes.h> 00018 #include <celutil/basictypes.h> 00019 #include "jpleph.h" 00020 00021 using namespace std; 00022 00023 static const unsigned int DE200RecordSize = 826; 00024 static const unsigned int DE405RecordSize = 1018; 00025 static const unsigned int DE406RecordSize = 728; 00026 00027 static const unsigned int NConstants = 400; 00028 static const unsigned int ConstantNameLength = 6; 00029 00030 static const unsigned int MaxChebyshevCoeffs = 32; 00031 00032 static const int LabelSize = 84; 00033 00034 00035 // Read a big-endian 32-bit unsigned integer 00036 static int32 readUint(istream& in) 00037 { 00038 int32 ret; 00039 in.read((char*) &ret, sizeof(int32)); 00040 BE_TO_CPU_INT32(ret, ret); 00041 return (uint32) ret; 00042 } 00043 00044 // TODO: This assumes that we've got to reverse endianness--won't work on 00045 // platforms that actually are big-endian. 00046 // Read a big-endian 64-bit IEEE double--if the native double format isn't 00047 // IEEE 754, there will be troubles. 00048 static double readDouble(istream& in) 00049 { 00050 unsigned char buf[8]; 00051 char c; 00052 00053 in.read((char*)buf, 8); 00054 c = buf[0]; buf[0] = buf[7]; buf[7] = c; 00055 c = buf[1]; buf[1] = buf[6]; buf[6] = c; 00056 c = buf[2]; buf[2] = buf[5]; buf[5] = c; 00057 c = buf[3]; buf[3] = buf[4]; buf[4] = c; 00058 00059 return *((double*) buf); 00060 } 00061 00062 00063 JPLEphRecord::~JPLEphRecord() 00064 { 00065 if (coeffs != NULL) 00066 delete coeffs; 00067 } 00068 00069 00070 00071 JPLEphemeris::JPLEphemeris() 00072 { 00073 } 00074 00075 00076 JPLEphemeris::~JPLEphemeris() 00077 { 00078 } 00079 00080 00081 unsigned int JPLEphemeris::getDENumber() const 00082 { 00083 return DENum; 00084 } 00085 00086 double JPLEphemeris::getStartDate() const 00087 { 00088 return startDate; 00089 } 00090 00091 double JPLEphemeris::getEndDate() const 00092 { 00093 return endDate; 00094 } 00095 00096 Point3d JPLEphemeris::getPlanetPosition(JPLEphemItem planet, double t) const 00097 { 00098 // Clamp time to [ startDate, endDate ] 00099 if (t < startDate) 00100 t = startDate; 00101 else if (t > endDate) 00102 t = endDate; 00103 00104 int recNo = (int) ((t - startDate) / daysPerInterval); 00105 // Make sure we don't go past the end of the array if t == endDate 00106 if (recNo >= records.size()) 00107 recNo = records.size() - 1; 00108 const JPLEphRecord* rec = &records[recNo]; 00109 00110 assert(coeffInfo[planet].nGranules >= 1); 00111 assert(coeffInfo[planet].nGranules <= 32); 00112 assert(coeffInfo[planet].nCoeffs <= MaxChebyshevCoeffs); 00113 00114 // u is the normalized time (in [-1, 1]) for interpolating 00115 // coeffs is a pointer to the Chebyshev coefficients 00116 double u = 0.0; 00117 double* coeffs = NULL; 00118 if (coeffInfo[planet].nGranules == -1) 00119 { 00120 coeffs = rec->coeffs + coeffInfo[planet].offset; 00121 u = 2.0 * (t - rec->t0) / daysPerInterval - 1.0; 00122 } 00123 else 00124 { 00125 double daysPerGranule = daysPerInterval / coeffInfo[planet].nGranules; 00126 int granule = (int) ((t - rec->t0) / daysPerGranule); 00127 double granuleStartDate = rec->t0 + 00128 daysPerGranule * (double) granule; 00129 coeffs = rec->coeffs + coeffInfo[planet].offset + 00130 granule * coeffInfo[planet].nCoeffs * 3; 00131 u = 2.0 * (t - granuleStartDate) / daysPerGranule - 1.0; 00132 } 00133 00134 double sum[3]; 00135 double cc[MaxChebyshevCoeffs]; 00136 unsigned int nCoeffs = coeffInfo[planet].nCoeffs; 00137 for (int i = 0; i < 3; i++) 00138 { 00139 cc[0] = 1.0; 00140 cc[1] = u; 00141 sum[i] = coeffs[i * nCoeffs] + coeffs[i * nCoeffs + 1] * u; 00142 for (unsigned int j = 2; j < nCoeffs; j++) 00143 { 00144 cc[j] = 2.0 * u * cc[j - 1] - cc[j - 2]; 00145 sum[i] += coeffs[i * nCoeffs + j] * cc[j]; 00146 } 00147 } 00148 00149 return Point3d(sum[0], sum[1], sum[2]); 00150 } 00151 00152 00153 JPLEphemeris* JPLEphemeris::load(istream& in) 00154 { 00155 JPLEphemeris* eph = NULL; 00156 00157 // Skip past three header labels 00158 in.ignore(LabelSize * 3); 00159 if (!in.good()) 00160 return NULL; 00161 00162 // Skip past the constant names 00163 in.ignore(NConstants * ConstantNameLength); 00164 if (!in.good()) 00165 return NULL; 00166 00167 eph = new JPLEphemeris(); 00168 if (eph == NULL) 00169 return NULL; 00170 00171 // Read the start time, end time, and time interval 00172 eph->startDate = readDouble(in); 00173 eph->endDate = readDouble(in); 00174 eph->daysPerInterval = readDouble(in); 00175 if (!in.good()) 00176 { 00177 delete eph; 00178 return NULL; 00179 } 00180 00181 // Number of constants with valid values; not useful for us 00182 (void) readUint(in); 00183 00184 eph->au = readDouble(in); // kilometers per astronomical unit 00185 eph->emrat = readDouble(in); // ??? 00186 00187 // Read the coefficient information for each item in the ephemeris 00188 unsigned int i; 00189 for (i = 0; i < JPLEph_NItems; i++) 00190 { 00191 eph->coeffInfo[i].offset = readUint(in) - 3; 00192 eph->coeffInfo[i].nCoeffs = readUint(in); 00193 eph->coeffInfo[i].nGranules = readUint(in); 00194 } 00195 if (!in.good()) 00196 { 00197 delete eph; 00198 return NULL; 00199 } 00200 00201 eph->DENum = readUint(in); 00202 00203 switch (eph->DENum) 00204 { 00205 case 200: 00206 eph->recordSize = DE200RecordSize; 00207 break; 00208 case 405: 00209 eph->recordSize = DE405RecordSize; 00210 break; 00211 case 406: 00212 eph->recordSize = DE406RecordSize; 00213 break; 00214 default: 00215 delete eph; 00216 return NULL; 00217 } 00218 00219 eph->librationCoeffInfo.offset = readUint(in); 00220 eph->librationCoeffInfo.nCoeffs = readUint(in); 00221 eph->librationCoeffInfo.nGranules = readUint(in); 00222 if (!in.good()) 00223 { 00224 delete eph; 00225 return NULL; 00226 } 00227 00228 // Skip past the rest of the record 00229 in.ignore(eph->recordSize * 8 - 2856); 00230 // The next record contains constant values (which we don't need) 00231 in.ignore(eph->recordSize * 8); 00232 if (!in.good()) 00233 { 00234 delete eph; 00235 return NULL; 00236 } 00237 00238 unsigned int nRecords = (unsigned int) ((eph->endDate - eph->startDate) / 00239 eph->daysPerInterval); 00240 eph->records.resize(nRecords); 00241 for (i = 0; i < nRecords; i++) 00242 { 00243 eph->records[i].t0 = readDouble(in); 00244 eph->records[i].t1 = readDouble(in); 00245 00246 // Allocate coefficient array for this record; the first two 00247 // 'coefficients' are actually the start and end time (t0 and t1) 00248 eph->records[i].coeffs = new double[eph->recordSize - 2]; 00249 for (unsigned int j = 0; j < eph->recordSize - 2; j++) 00250 eph->records[i].coeffs[j] = readDouble(in); 00251 00252 // Make sure that we read this record successfully 00253 if (!in.good()) 00254 { 00255 delete eph; 00256 return NULL; 00257 } 00258 } 00259 00260 return eph; 00261 }
1.4.1