double DLL_FUNC jpl_get_constant( const int idx, void *ephem, char *constant_name) { struct jpl_eph_data *eph = (struct jpl_eph_data *)ephem; double rval = 0.; *constant_name = '\0'; if( idx >= 0 && idx < (int)eph->ncon) { const long seek_loc = (idx < 400 ? 84L * 3L + (long)idx * 6 : START_400TH_CONSTANT_NAME + (idx - 400) * 6); fseek( eph->ifile, seek_loc, SEEK_SET); if( fread( constant_name, 1, 6, eph->ifile)) { constant_name[6] = '\0'; fseek( eph->ifile, eph->recsize + (long)idx * sizeof( double), SEEK_SET); if( fread( &rval, 1, sizeof( double), eph->ifile)) if( eph->swap_bytes) /* gotta swap the constants, too */ swap_64_bit_val( &rval, 1); } } return( rval); }
void * DLL_FUNC jpl_init_ephemeris(const char *ephemeris_filename, char nam[][6], double *val) { unsigned i, j; long de_version; char title[84]; FILE *ifile = fopen(ephemeris_filename, "rb"); struct jpl_eph_data *rval; struct jpl_eph_data temp_data; init_err_code = 0; temp_data.ifile = ifile; if(!ifile) init_err_code = JPL_INIT_FILE_NOT_FOUND; else if(fread(title, 84, 1, ifile) != 1) init_err_code = JPL_INIT_FREAD_FAILED; else if(FSeek(ifile, 2652L, SEEK_SET)) init_err_code = JPL_INIT_FSEEK_FAILED; else if(fread(&temp_data, JPL_HEADER_SIZE, 1, ifile) != 1) init_err_code = JPL_INIT_FREAD2_FAILED; if(init_err_code) { if(ifile) fclose(ifile); return(NULL); } de_version = atoi(title + 26); /* A small piece of trickery: in the binary file, data is stored */ /* for ipt[0...11], then the ephemeris version, then the */ /* remaining ipt[12] data. A little switching is required to get */ /* the correct order. */ temp_data.ipt[12][0] = temp_data.ipt[12][1]; temp_data.ipt[12][1] = temp_data.ipt[12][2]; temp_data.ipt[12][2] = temp_data.ipt[13][0]; temp_data.ephemeris_version = de_version; //qDebug() << "DE_Version: " << de_version; temp_data.swap_bytes = (temp_data.ncon > 65536L); if(temp_data.swap_bytes) /* byte order is wrong for current platform */ { qDebug() << "Byte order is wrong for current platform"; swap_64_bit_val(&temp_data.ephem_start, 1); swap_64_bit_val(&temp_data.ephem_end, 1); swap_64_bit_val(&temp_data.ephem_step, 1); swap_32_bit_val(&temp_data.ncon); swap_64_bit_val(&temp_data.au, 1); swap_64_bit_val(&temp_data.emrat, 1); } /* It's a little tricky to tell if an ephemeris really has */ /* TT-TDB data (with offsets in ipt[13][] and ipt[14][]). */ /* Essentially, we read the data and sanity-check it, and */ /* zero it if it "doesn't add up" correctly. */ /* Also: certain ephems I've generated with ncon capped */ /* at 400 have no TT-TDB data. So if ncon == 400, don't */ /* try to read such data; you may get garbage. */ if(de_version >= 430 && temp_data.ncon != 400) { /* If there are 400 or fewer constants, data for ipt[13][0...2] */ /* immediately follows that for ipt[12][0..2]; i.e., we don't */ /* need to fseek(). Otherwise, we gotta skip 6*(n_constants-400) */ /* bytes. */ if(temp_data.ncon > 400) FSeek(ifile, (size_t)(temp_data.ncon - 400) * 6, SEEK_CUR); if(fread(&temp_data.ipt[13][0], sizeof(int32_t), 6, ifile) != 6) init_err_code = JPL_INIT_FREAD5_FAILED; } else /* mark header data as invalid */ temp_data.ipt[13][0] = (uint32_t)-1; if(temp_data.swap_bytes) /* byte order is wrong for current platform */ { for(j = 0; j < 3; j++) { for(i = 0; i < 15; i++) { swap_32_bit_val(&temp_data.ipt[i][j]); } } } if(temp_data.ipt[13][0] != /* if these don't add up correctly, */ temp_data.ipt[12][0] + temp_data.ipt[12][1] * temp_data.ipt[12][2] * 3 || temp_data.ipt[14][0] != /* zero them out (they've garbage data) */ temp_data.ipt[13][0] + temp_data.ipt[13][1] * temp_data.ipt[13][2] * 3) { /* not valid pointers to TT-TDB data */ memset(&temp_data.ipt[13][0], 0, 6 * sizeof(int32_t)); } /* A sanity check: if the earth-moon ratio is outside reasonable */ /* bounds, we must be looking at a wrong or corrupted file. */ /* In DE-102, emrat = 81.3007; in DE-405/406, emrat = 81.30056. */ /* Those are the low and high ranges. We'll allow some slop in */ /* case the earth/moon mass ratio changes: */ if(temp_data.emrat > 81.3008 || temp_data.emrat < 81.30055) { init_err_code = JPL_INIT_FILE_CORRUPT; qWarning() << "temp_data: " << temp_data.emrat << "(should have been =~81). JPL_INIT_FILE_CORRUPT!"; } if(init_err_code) { fclose(ifile); return(NULL); } /* Once upon a time, the kernel size was determined from the */ /* DE version. This was not a terrible idea, except that it */ /* meant that when the code faced a new version, it broke. */ /* Now we use some logic to compute the kernel size. */ temp_data.kernel_size = 4; for(i = 0; i < 15; i++) temp_data.kernel_size += 2 * temp_data.ipt[i][1] * temp_data.ipt[i][2] * dimension(i); // for(i = 0; i < 13; i++) // temp_data.kernel_size += // temp_data.ipt[i][1] * temp_data.ipt[i][2] * ((i == 11) ? 4 : 6); // /* ...and then add in space required for the TT-TDB data : */ // temp_data.kernel_size += temp_data.ipt[14][1] * temp_data.ipt[14][2] * 2; temp_data.recsize = temp_data.kernel_size * 4L; temp_data.ncoeff = temp_data.kernel_size / 2L; /* Rather than do two separate allocations, everything */ /* we need is allocated in _one_ chunk, then parceled out. */ /* This looks a little weird, but it simplifies error */ /* handling and cleanup. */ rval = (struct jpl_eph_data *)calloc(sizeof(struct jpl_eph_data) + temp_data.recsize, 1); if(!rval) { init_err_code = JPL_INIT_MEMORY_FAILURE; fclose(ifile); return(NULL); } memcpy(rval, &temp_data, sizeof(struct jpl_eph_data)); rval->iinfo.posn_coeff[0] = 1.0; /* Seed a bogus value here. The first and subsequent calls to */ /* 'interp' will correct it to a value between -1 and +1. */ rval->iinfo.posn_coeff[1] = -2.0; rval->iinfo.vel_coeff[0] = 0.0; rval->iinfo.vel_coeff[1] = 1.0; rval->curr_cache_loc = (uint32_t)-1; /* The 'cache' data is right after the 'jpl_eph_data' struct: */ rval->cache = (double *)(rval + 1); /* If there are more than 400 constants, the names of */ /* the extra constants are stored in what would normally */ /* be zero-padding after the header record. However, */ /* older ephemeris-reading software won't know about that. */ /* So we store ncon=400, then actually check the names to */ /* see how many constants there _really_ are. Older readers */ /* will just see 400 names and won't know about the others. */ /* But on the upside, they won't crash. */ if(rval->ncon == 400) { char buff[7]; buff[6] = '\0'; FSeek(ifile, START_400TH_CONSTANT_NAME, SEEK_SET); while(fread(buff, 6, 1, ifile) && strlen(buff) == 6) { rval->ncon++; } } if(val) { FSeek(ifile, rval->recsize, SEEK_SET); if(fread(val, sizeof(double), (size_t)rval->ncon, ifile) != (size_t)rval->ncon) init_err_code = JPL_INIT_FREAD3_FAILED; else if(rval->swap_bytes) /* gotta swap the constants, too */ swap_64_bit_val(val, rval->ncon); } if(!init_err_code && nam) { FSeek(ifile, 84L * 3L, SEEK_SET); /* just after the 3 'title' lines */ for(i = 0; i < rval->ncon && !init_err_code; i++) { if(i == 400) FSeek(ifile, START_400TH_CONSTANT_NAME, SEEK_SET); if(fread(nam[i], 6, 1, ifile) != 1) init_err_code = JPL_INIT_FREAD4_FAILED; } } return(rval); }
/***************************************************************************** ** jpl_state(ephem,et2,list,pv,nut,bary) ** ****************************************************************************** ** This subroutine reads and interpolates the jpl planetary ephemeris file ** ** ** ** Calling sequence parameters: ** ** ** ** Input: ** ** ** ** et2[] double, 2-element JED epoch at which interpolation ** ** is wanted. Any combination of et2[0]+et2[1] which falls ** ** within the time span on the file is a permissible epoch. ** ** ** ** a. for ease in programming, the user may put the ** ** entire epoch in et2[0] and set et2[1]=0.0 ** ** ** ** b. for maximum interpolation accuracy, set et2[0] = ** ** the most recent midnight at or before interpolation ** ** epoch and set et2[1] = fractional part of a day ** ** elapsed between et2[0] and epoch. ** ** ** ** c. as an alternative, it may prove convenient to set ** ** et2[0] = some fixed epoch, such as start of integration,** ** and et2[1] = elapsed interval between then and epoch. ** ** ** ** list 13-element integer array specifying what interpolation ** ** is wanted for each of the "bodies" on the file. ** ** ** ** list[i]=0, no interpolation for body i ** ** =1, position only ** ** =2, position and velocity ** ** ** ** the designation of the astronomical bodies by i is: ** ** ** ** i = 0: mercury ** ** = 1: venus ** ** = 2: earth-moon barycenter ** ** = 3: mars ** ** = 4: jupiter ** ** = 5: saturn ** ** = 6: uranus ** ** = 7: neptune ** ** = 8: pluto ** ** = 9: geocentric moon ** ** =10: nutations in lon & obliq (if on file) ** ** =11: lunar librations (if on file) ** ** =12: lunar mantle omegas ** ** =13: TT-TDB (if on file) ** ** ** ** Note that I've not actually seen case 12 yet. It probably doesn't work. ** ** ** ** output: ** ** ** ** pv[][6] double array that will contain requested interpolated ** ** quantities. The body specified by list[i] will have its ** ** state in the array starting at pv[i][0] (on any given ** ** call, only those words in 'pv' which are affected by the ** ** first 10 'list' entries (and by list(11) if librations are ** ** on the file) are set. The rest of the 'pv' array ** ** is untouched.) The order of components in pv[][] is: ** ** pv[][0]=x,....pv[][5]=dz. ** ** ** ** All output vectors are referenced to the earth mean ** ** equator and equinox of epoch. The moon state is always ** ** geocentric; the other nine states are either heliocentric ** ** or solar-system barycentric, depending on the setting of ** ** global variables (see below). ** ** ** ** Lunar librations, if on file, are put into pv[10][k] if ** ** list[11] is 1 or 2. ** ** ** ** nut dp 4-word array that will contain nutations and rates, ** ** depending on the setting of list[10]. the order of ** ** quantities in nut is: ** ** ** ** d psi (nutation in longitude) ** ** d epsilon (nutation in obliquity) ** ** d psi dot ** ** d epsilon dot ** ** ** *****************************************************************************/ int DLL_FUNC jpl_state(void *ephem, const double et, const int list[14], double pv[][6], double nut[4], const int bary) { struct jpl_eph_data *eph = (struct jpl_eph_data *)ephem; unsigned i, j, n_intervals; uint32_t nr; double *buf = eph->cache; double t[2]; const double block_loc = (et - eph->ephem_start) / eph->ephem_step; bool recompute_pvsun; const double aufac = 1.0 / eph->au; /* error return for epoch out of range */ if(et < eph->ephem_start || et > eph->ephem_end) return(JPL_EPH_OUTSIDE_RANGE); /* calculate record # and relative time in interval */ nr = (uint32_t)block_loc; t[0] = block_loc - (double)nr; if(!t[0] && nr) { t[0] = 1.; nr--; } /* read correct record if not in core (static vector buf[]) */ if(nr != eph->curr_cache_loc) { eph->curr_cache_loc = nr; /* Read two blocks ahead to account for header: */ if(FSeek(eph->ifile, (nr + 2) * eph->recsize, SEEK_SET)) { // GZ: Make sure we will try again on next call... eph->curr_cache_loc=0; return(JPL_EPH_FSEEK_ERROR); } if(fread(buf, sizeof(double), (size_t)eph->ncoeff, eph->ifile) != (size_t)eph->ncoeff) return(JPL_EPH_READ_ERROR); if(eph->swap_bytes) swap_64_bit_val(buf, eph->ncoeff); } t[1] = eph->ephem_step; if(eph->pvsun_t != et) /* If several calls are made for the same et, */ { /* don't recompute pvsun each time... only on */ recompute_pvsun = true; /* the first run through. */ eph->pvsun_t = et; } else recompute_pvsun = false; /* Here, i loops through the "traditional" 14 listed items -- 10 solar system objects, nutations, librations, lunar mantle angles, and TT-TDT -- plus a fifteenth: the solar system barycenter. That last is quite different: it's computed 'as needed', rather than from list[]; the output goes to pvsun rather than the pv array; and three quantities (position, velocity, acceleration) are computed (nobody else gets accelerations at present.) */ for(n_intervals = 1; n_intervals <= 8; n_intervals *= 2) for(i = 0; i < 15; i++) { unsigned quantities; uint32_t *iptr = &eph->ipt[i + 1][0]; if(i == 14) { quantities = (recompute_pvsun ? 3 : 0); iptr = &eph->ipt[10][0]; } else { quantities = list[i]; iptr = &eph->ipt[i < 10 ? i : i + 1][0]; } if(n_intervals == iptr[2] && quantities) { double *dest = ((i == 10) ? eph->pvsun : pv[i]); if(i < 10) dest = pv[i]; else if(i == 14) dest = eph->pvsun; else dest = nut; interp(&eph->iinfo, &buf[iptr[0]-1], t, (int)iptr[1], dimension(i + 1), n_intervals, quantities, dest); if(i < 10 || i == 14) /* convert km to AU */ for(j = 0; j < quantities * 3; j++) dest[j] *= aufac; } } if(!bary) /* gotta correct everybody for */ for(i = 0; i < 9; i++) /* the solar system barycenter */ for(j = 0; j < (unsigned)list[i] * 3; j++) pv[i][j] -= eph->pvsun[j]; return(0); }