mirror of
https://github.com/cosinekitty/astronomy.git
synced 2026-01-03 21:20:27 -05:00
The newer version of cppcheck reported that I was assigning a value to a variable that was never used before another assignment occurred. Fixed this to eliminate the warning.
922 lines
26 KiB
C
922 lines
26 KiB
C
/*
|
|
top2013.c - Don Cross - 2020-06-30
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "codegen.h"
|
|
#include "top2013.h"
|
|
|
|
static const double dpi = 6.283185307179586476925287;
|
|
|
|
static int FormatTermLine(int lnum, char *line, size_t size, const top_term_t *term);
|
|
|
|
|
|
void TopInitModel(top_model_t *model)
|
|
{
|
|
memset(model, 0, sizeof(top_model_t));
|
|
}
|
|
|
|
|
|
void TopFreeModel(top_model_t *model)
|
|
{
|
|
int f, s;
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
free(model->series[f][s].terms);
|
|
|
|
TopInitModel(model);
|
|
}
|
|
|
|
|
|
int TopTermCount(const top_model_t *model)
|
|
{
|
|
int f, s, count;
|
|
|
|
count = 0;
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
count += model->series[f][s].nterms_calc;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
int TopTermCountF(const top_model_t *model, int f)
|
|
{
|
|
int s, count;
|
|
|
|
if (f < 0 || f >= TOP_NCOORDS)
|
|
return 0;
|
|
|
|
count = 0;
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
count += model->series[f][s].nterms_calc;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static int CloneSeries(top_series_t *copy, const top_series_t *original)
|
|
{
|
|
int error;
|
|
size_t size = sizeof(top_term_t) * (size_t)(original->nterms_total);
|
|
|
|
copy->nterms_total = original->nterms_total;
|
|
copy->nterms_calc = original->nterms_calc;
|
|
copy->terms = malloc(size);
|
|
|
|
if (copy->terms == NULL)
|
|
FAIL("CloneSeries: out of memory trying to allocate %lu bytes.\n", (unsigned long)size);
|
|
|
|
memcpy(copy->terms, original->terms, size);
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopCloneModel(top_model_t *copy, const top_model_t* original)
|
|
{
|
|
int error = 1;
|
|
int f, s;
|
|
|
|
TopInitModel(copy);
|
|
copy->planet = original->planet;
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
CHECK(CloneSeries(©->series[f][s], &original->series[f][s]));
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
static int RoundingAdjustment(char original, char regen, int *diff)
|
|
{
|
|
switch (original - regen)
|
|
{
|
|
case 0:
|
|
*diff = 0;
|
|
return 0;
|
|
|
|
case -1:
|
|
case +9:
|
|
*diff = -1;
|
|
return 0;
|
|
|
|
case +1:
|
|
case -9:
|
|
*diff = +1;
|
|
return 0;
|
|
|
|
default:
|
|
fprintf(stderr, "RoundingAdjustment: original=%c, regen=%c\n", original, regen);
|
|
*diff = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
int TopLoadModel(top_model_t *model, const char *filename, int planet)
|
|
{
|
|
int error = 1;
|
|
FILE *infile = NULL;
|
|
int nterms_remaining, lnum, nscanned, check_planet, check_var, tpower;
|
|
int count = 0; /* total number of terms we found for this planet */
|
|
top_series_t *series = NULL;
|
|
top_term_t *term = NULL;
|
|
char line[100];
|
|
char gline[100];
|
|
|
|
TopInitModel(model);
|
|
model->planet = planet;
|
|
|
|
infile = fopen(filename, "rt");
|
|
if (infile == NULL)
|
|
FAIL("TopLoadModel: cannot open file: %s\n", filename);
|
|
|
|
lnum = 0;
|
|
nterms_remaining = 0;
|
|
check_planet = 0;
|
|
check_var = 0;
|
|
while (fgets(line, sizeof(line), infile))
|
|
{
|
|
++lnum;
|
|
if (nterms_remaining == 0)
|
|
{
|
|
nscanned = sscanf(line,
|
|
" TOP2013ELL PLANET %d VARIABLE %d T**%d %d term(s)",
|
|
&check_planet, &check_var, &tpower, &nterms_remaining);
|
|
|
|
if (nscanned != 4)
|
|
FAIL("TopLoadModel(%s line %d): invalid data format.\n", filename, lnum);
|
|
|
|
if (check_var < 1 || check_var > TOP_NCOORDS)
|
|
FAIL("TopLoadModel(%s line %d): invalid variable number %d\n", filename, lnum, check_var);
|
|
|
|
--check_var; /* convert one-based to zero-based indexing */
|
|
|
|
if (tpower < 0 || tpower >= TOP_NSERIES)
|
|
FAIL("TopLoadModel(%s line %d): invalid power of t: %d\n", filename, lnum, tpower);
|
|
|
|
if (check_planet == planet)
|
|
{
|
|
if (series != NULL)
|
|
{
|
|
/* make sure the previous series is complete */
|
|
if (series->nterms_calc != series->nterms_total)
|
|
FAIL("TopLoadModel(%s line %d): previous series has %d terms; expected %d\n", filename, lnum, series->nterms_calc, series->nterms_total);
|
|
}
|
|
|
|
/* Allocate room for the new terms. */
|
|
series = &model->series[check_var][tpower];
|
|
series->nterms_total = nterms_remaining;
|
|
series->nterms_calc = 0;
|
|
series->terms = calloc(sizeof(top_term_t), nterms_remaining);
|
|
if (series->terms == NULL)
|
|
FAIL("TopLoadModel(%s line %d): out of memory\n", filename, lnum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
--nterms_remaining;
|
|
|
|
if (check_planet == planet)
|
|
{
|
|
if (series == NULL)
|
|
FAIL("TopLoadModel(%s line %d): series == NULL\n", filename, lnum);
|
|
|
|
if (series->nterms_calc >= series->nterms_total)
|
|
FAIL("TopLoadModel(%s line %d): too many terms\n", filename, lnum);
|
|
|
|
term = &series->terms[series->nterms_calc++];
|
|
|
|
if (strlen(line) < 61)
|
|
FAIL("TopLoadModel(%s line %d) line is too short.\n", filename, lnum);
|
|
|
|
/* patch in 'e' to make numbers in scientific notation. */
|
|
if (line[31] != ' ' || line[57] != ' ')
|
|
FAIL("TopLoadModel(%s line %d): expected spaces between mantissas and exponents.\n", filename, lnum);
|
|
|
|
line[31] = line[57] = 'e';
|
|
nscanned = sscanf(line, "%lf %lf %lf %lf", &term->k, &term->c, &term->s, &term->p);
|
|
if (nscanned == 3)
|
|
term->p = 0.0;
|
|
else if (nscanned != 4)
|
|
FAIL("TopLoadModel(%s line %d): invalid term data format.\n", filename, lnum);
|
|
|
|
/*
|
|
Super ugly hack: the weird exponential notation used in TOP2013.dat
|
|
makes it really difficult to regenerate exactly in all cases.
|
|
So I generate each line back as text, and remember what I will have to do later
|
|
to reconstruct the input exactly.
|
|
*/
|
|
CHECK(FormatTermLine(lnum, gline, sizeof(gline), term));
|
|
CHECK(RoundingAdjustment(line[30], gline[30], &term->rc));
|
|
CHECK(RoundingAdjustment(line[56], gline[56], &term->rs));
|
|
|
|
/* Sanity check the rounding hack. */
|
|
CHECK(FormatTermLine(lnum, gline, sizeof(gline), term));
|
|
line[31] = line[57] = ' ';
|
|
if (strcmp(line, gline))
|
|
{
|
|
fprintf(stderr, "INPUT:%s", line);
|
|
fprintf(stderr, "REGEN:%s", gline);
|
|
FAIL("TopLoadModel(%s line %d): unable to reconstruct identical term line.\n", filename, lnum);
|
|
}
|
|
|
|
++count;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nterms_remaining != 0)
|
|
FAIL("TopLoadModel(%s): missing %d terms at the end.\n", filename, nterms_remaining);
|
|
|
|
if (count == 0)
|
|
FAIL("TopLoadModel(%s): could not find any terms for planet %d.\n", filename, planet);
|
|
|
|
error = 0;
|
|
fail:
|
|
if (infile != NULL) fclose(infile);
|
|
if (error) TopFreeModel(model);
|
|
return error;
|
|
}
|
|
|
|
|
|
static int AppendTrigCoeff(char *line, int lnum, double x, int rounding_adjust)
|
|
{
|
|
int error = 1;
|
|
int m, length, exponent;
|
|
char polarity;
|
|
char buffer[40];
|
|
|
|
if (rounding_adjust < -1 || rounding_adjust > +1)
|
|
FAIL("AppendTrigCoeff(%d): invalid rounding_adjust = %d\n", lnum, rounding_adjust);
|
|
|
|
/*
|
|
The data format for TOP2013 has a weird form of scientific notation.
|
|
It always has a 0 to the left of the decimal point, which wastes a digit of precision.
|
|
"-5.2026032025158849e+00" <== C scientific notation
|
|
" -0.5202603202515885 +01" <== output
|
|
*/
|
|
|
|
snprintf(buffer, sizeof(buffer), "%23.16le", x);
|
|
length = (int)strlen(buffer);
|
|
if (length != 23)
|
|
FAIL("AppendTrigCoeff(%d): output string '%s' has incorrect length %d.\n", lnum, buffer, length);
|
|
|
|
if (buffer[19] != 'e')
|
|
FAIL("AppendTrigCoeff(%d): expected 'e' at index 19 in string '%s'\n", lnum, buffer);
|
|
|
|
buffer[19] = '\0'; /* truncate the string at the 'e' */
|
|
|
|
if (1 != sscanf(buffer+20, "%d", &exponent))
|
|
FAIL("AppendTrigCoeff(%d): cannot scan exponent from '%s'\n", lnum, buffer+20);
|
|
|
|
++exponent;
|
|
if (x == 0.0)
|
|
exponent = 0;
|
|
if (exponent >= 0)
|
|
{
|
|
polarity = '+';
|
|
}
|
|
else
|
|
{
|
|
polarity = '-';
|
|
exponent = -exponent;
|
|
}
|
|
|
|
/* Copy digits and shift decimal point */
|
|
buffer[22] = '\0';
|
|
for (m = 17; m >= 0 && buffer[m] != '.'; --m)
|
|
buffer[m+4] = buffer[m];
|
|
|
|
if (m != 2 || buffer[2] != '.')
|
|
FAIL("AppendTrigCoeff(%d): decimal point is in the wrong place: '%s'\n", lnum, buffer);
|
|
|
|
buffer[6] = buffer[1];
|
|
buffer[5] = '.';
|
|
buffer[4] = '0';
|
|
buffer[3] = buffer[0];
|
|
buffer[2] = ' ';
|
|
buffer[1] = ' ';
|
|
buffer[0] = ' ';
|
|
|
|
if (rounding_adjust != 0)
|
|
{
|
|
for (m=21; m >= 0; --m)
|
|
{
|
|
if (buffer[m] != '.')
|
|
{
|
|
if (buffer[m] < '0' || buffer[m] > '9')
|
|
FAIL("AppendTrigCoeff(%d): rounding failure\n", lnum);
|
|
buffer[m] += (char)rounding_adjust;
|
|
if (buffer[m] < '0')
|
|
buffer[m] = '9';
|
|
else if (buffer[m] > '9')
|
|
buffer[m] = '0';
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(buffer+22, " %c%02d", polarity, exponent);
|
|
length = (int)strlen(buffer);
|
|
if (length != 26)
|
|
FAIL("AppendTrigCoeff(%d): generated incorrect length %d in string '%s' for x=%lg\n", lnum, length, buffer, x);
|
|
|
|
strcat(line, buffer);
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
static int FormatTermLine(int lnum, char *line, size_t size, const top_term_t *term)
|
|
{
|
|
int error = 1;
|
|
int length;
|
|
|
|
snprintf(line, size, "%9.0lf", term->k);
|
|
CHECK(AppendTrigCoeff(line, lnum, term->c, term->rc));
|
|
CHECK(AppendTrigCoeff(line, lnum, term->s, term->rs));
|
|
length = (int)strlen(line);
|
|
if (61 != length)
|
|
FAIL("FormatTermLine(%d): incorrect output line length = %d.\n", lnum, length);
|
|
if (term->k != 0)
|
|
snprintf(line + length, size - (size_t)length, " %11.6lf", term->p);
|
|
strcat(line, "\n");
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopWriteModel(const top_model_t *model, FILE *outfile)
|
|
{
|
|
int error = 1;
|
|
int f, s, t;
|
|
int lnum = 0;
|
|
char line[100];
|
|
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
{
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
{
|
|
const top_series_t *series = &model->series[f][s];
|
|
|
|
if (series->nterms_calc == 0)
|
|
continue;
|
|
|
|
++lnum;
|
|
if (0 > fprintf(outfile, " TOP2013ELL PLANET %d VARIABLE %d T**%02d %7d term(s)\n", model->planet, f+1, s, series->nterms_calc))
|
|
FAIL("TopWriteModel(%d): error writing header record to output stream.\n", lnum);
|
|
|
|
for (t=0; t < series->nterms_calc; ++t)
|
|
{
|
|
const top_term_t *term = &series->terms[t];
|
|
++lnum;
|
|
CHECK(FormatTermLine(lnum, line, sizeof(line), term));
|
|
if (0 > fprintf(outfile, "%s", line))
|
|
FAIL("TopWriteModel(%d): error writing term record to output stream.\n", lnum);
|
|
}
|
|
}
|
|
}
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopSaveModel(const top_model_t *model, const char *filename)
|
|
{
|
|
int error;
|
|
FILE *outfile = fopen(filename, "wt");
|
|
if (outfile == NULL)
|
|
FAIL("TopSaveModel: Cannot open output file: %s\n", filename);
|
|
CHECK(TopWriteModel(model, outfile));
|
|
fail:
|
|
if (outfile) fclose(outfile);
|
|
return error;
|
|
}
|
|
|
|
|
|
static int ContribCompare(const void *aptr, const void *bptr)
|
|
{
|
|
const top_contrib_t *a = aptr;
|
|
const top_contrib_t *b = bptr;
|
|
|
|
if (a->magnitude < b->magnitude)
|
|
return -1;
|
|
|
|
if (a->magnitude > b->magnitude)
|
|
return +1;
|
|
|
|
/* tie-breaker: keep elements in reverse order as much as possible */
|
|
|
|
if (a->s != b->s)
|
|
return b->s - a->s;
|
|
|
|
return b->t - a->t;
|
|
}
|
|
|
|
|
|
static int MakeContribList(const top_series_t *formula, top_contrib_list_t *list, double millennia)
|
|
{
|
|
int error;
|
|
int nterms, s, t;
|
|
double tpower;
|
|
|
|
/* Count up the number of terms in the elliptical coordinate formula 'f'. */
|
|
|
|
nterms = 0;
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
nterms += formula[s].nterms_calc;
|
|
|
|
/* Allocate space for the array. */
|
|
list->array = calloc(nterms, sizeof(top_contrib_t));
|
|
if (list->array == NULL)
|
|
FAIL("MakeContribList: out of memory allocating %d elements.\n", nterms);
|
|
|
|
/* Fill up the array. */
|
|
list->skip = 0;
|
|
list->nterms = nterms;
|
|
nterms = 0;
|
|
tpower = 1.0;
|
|
millennia = fabs(millennia);
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
{
|
|
const top_series_t *series = &formula[s];
|
|
for (t=0; t < series->nterms_calc; ++t)
|
|
{
|
|
top_contrib_t *contrib;
|
|
const top_term_t *term = &series->terms[t];
|
|
if (nterms >= list->nterms)
|
|
FAIL("MakeContribList: internal error -- overflow %d items\n", nterms);
|
|
contrib = &list->array[nterms++];
|
|
contrib->s = s;
|
|
contrib->t = t;
|
|
contrib->magnitude = tpower * sqrt(term->c*term->c + term->s*term->s);
|
|
}
|
|
tpower *= millennia;
|
|
}
|
|
|
|
if (nterms != list->nterms)
|
|
FAIL("MakeContribList: internal error: expected %d terms, found %d\n", list->nterms, nterms);
|
|
|
|
/* Sort the list in ascending order of magnitude. */
|
|
qsort(list->array, list->nterms, sizeof(list->array[0]), ContribCompare);
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
void TopInitContribMap(top_contrib_map_t *map)
|
|
{
|
|
memset(map, 0, sizeof(top_contrib_map_t));
|
|
}
|
|
|
|
|
|
void TopFreeContribMap(top_contrib_map_t *map)
|
|
{
|
|
int f;
|
|
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
free(map->list[f].array);
|
|
|
|
TopInitContribMap(map);
|
|
}
|
|
|
|
|
|
int TopMakeContribMap(top_contrib_map_t *map, const top_model_t *model, double millennia)
|
|
{
|
|
int error, f;
|
|
|
|
TopInitContribMap(map);
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
CHECK(MakeContribList(model->series[f], &map->list[f], millennia));
|
|
|
|
fail:
|
|
if (error) TopFreeContribMap(map);
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopCalcElliptical(const top_model_t *model, double tt, top_elliptical_t *ellip)
|
|
{
|
|
/* Translated from: TOP2013.f */
|
|
/* See: https://github.com/cosinekitty/ephemeris/tree/master/top2013 */
|
|
/* Copied from: ftp://ftp.imcce.fr/pub/ephem/planets/top2013 */
|
|
static const double freq[] =
|
|
{
|
|
0.5296909622785881e+03,
|
|
0.2132990811942489e+03,
|
|
0.7478166163181234e+02,
|
|
0.3813297236217556e+02,
|
|
0.2533566020437000e+02
|
|
};
|
|
int error = 1;
|
|
int i, f, s, t;
|
|
double time[TOP_NSERIES];
|
|
double el[6];
|
|
double arg, dmu, xl;
|
|
|
|
/* Check planet index */
|
|
if (model->planet < 5 || model->planet > 9)
|
|
FAIL("TopCalcElliptical: invalid planet = %d\n", model->planet);
|
|
|
|
/* Time */
|
|
time[0] = 1.0;
|
|
time[1] = tt / 365250.0;
|
|
for (i=1; i < TOP_NSERIES; ++i)
|
|
time[i] = time[i-1] * time[1];
|
|
|
|
dmu = (freq[0] - freq[1]) / 880.0;
|
|
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
{
|
|
el[f] = 0.0;
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
{
|
|
const top_series_t *series = &model->series[f][s];
|
|
for (t=0; t < series->nterms_calc; ++t)
|
|
{
|
|
const top_term_t *term = &series->terms[t];
|
|
if (f==1 && s==1 && term->k==0)
|
|
continue;
|
|
arg = term->k * dmu * time[1];
|
|
el[f] += time[s] * (term->c*cos(arg) + term->s*sin(arg));
|
|
}
|
|
}
|
|
}
|
|
|
|
xl = el[1] + freq[model->planet - 5] * time[1];
|
|
xl = fmod(xl, dpi);
|
|
if (xl < 0.0)
|
|
xl += dpi;
|
|
el[1] = xl;
|
|
|
|
/* Convert elliptical elements from array 'el' to friendly struct layout. */
|
|
ellip->a = el[0];
|
|
ellip->lambda = el[1];
|
|
ellip->k = el[2];
|
|
ellip->h = el[3];
|
|
ellip->q = el[4];
|
|
ellip->p = el[5];
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopEcliptic(int planet, const top_elliptical_t *ellip, top_rectangular_t *ecl)
|
|
{
|
|
static const double gmp[] =
|
|
{
|
|
0, /* placeholder */
|
|
4.9125474514508118699e-11,
|
|
7.2434524861627027000e-10,
|
|
8.9970116036316091182e-10,
|
|
9.5495351057792580598e-11,
|
|
2.8253458420837780000e-07,
|
|
8.4597151856806587398e-08,
|
|
1.2920249167819693900e-08,
|
|
1.5243589007842762800e-08,
|
|
2.1886997654259696800e-12
|
|
};
|
|
static const double gmsol = 2.9591220836841438269e-04;
|
|
double rgm;
|
|
double xa, xl, xk, xh, xq, xp;
|
|
double xfi, xki, u, ex, ex2, ex3;
|
|
double zr, zi;
|
|
double z1r, z1i;
|
|
double z2r, z2i;
|
|
double z3r, z3i;
|
|
double zteta_r, zteta_i;
|
|
double zto_r, zto_i;
|
|
double gl, gm, e, dl, rsa;
|
|
double xcw, xsw, xm, xr, xms, xmc, xn;
|
|
int error = 1;
|
|
|
|
if (planet < 1 || planet > 9)
|
|
FAIL("TopEcliptic: invalid planet = %d\n", planet);
|
|
|
|
rgm = sqrt(gmp[planet] + gmsol);
|
|
xa = ellip->a;
|
|
xl = ellip->lambda;
|
|
xk = ellip->k;
|
|
xh = ellip->h;
|
|
xq = ellip->q;
|
|
xp = ellip->p;
|
|
|
|
xfi = sqrt(1.0 - xk*xk - xh*xh);
|
|
xki = sqrt(1.0 - xq*xq - xp*xp);
|
|
zr = xk; zi = xh; /* z = dcmplx(xk,xh) */
|
|
u = 1.0 / (1.0 + xfi);
|
|
ex2 = zr*zr + zi*zi;
|
|
ex = sqrt(ex2); /* ex = cdabs(z) */
|
|
ex3 = ex * ex2;
|
|
z1r = zr; z1i = -zi;
|
|
|
|
gl = fmod(xl, dpi);
|
|
gm = gl - atan2(xh, xk);
|
|
e = gl + (ex - 0.125*ex3)*sin(gm) + 0.5*ex2*sin(2.0*gm) + 0.375*ex3*sin(3.0*gm);
|
|
|
|
do
|
|
{
|
|
z2i = e;
|
|
zteta_r = cos(z2i);
|
|
zteta_i = sin(z2i);
|
|
z3r = z1r*zteta_r - z1i*zteta_i;
|
|
z3i = z1r*zteta_i + z1i*zteta_r;
|
|
dl = gl - e + z3i;
|
|
rsa = 1.0 - z3r;
|
|
e += dl/rsa;
|
|
} while (fabs(dl) >= 1.0e-15);
|
|
|
|
z1r = z3i * u * zr;
|
|
z1i = z3i * u * zi;
|
|
z2r = +z1i;
|
|
z2i = -z1r;
|
|
zto_r = (-zr + zteta_r + z2r) / rsa;
|
|
zto_i = (-zi + zteta_i + z2i) / rsa;
|
|
xcw = zto_r;
|
|
xsw = zto_i;
|
|
xm = xp*xcw - xq*xsw;
|
|
xr = xa*rsa;
|
|
|
|
ecl->x = xr*(xcw - 2.0*xp*xm);
|
|
ecl->y = xr*(xsw + 2.0*xq*xm);
|
|
ecl->z = -2.0*xr*xki*xm;
|
|
|
|
xms = xa*(xh + xsw)/xfi;
|
|
xmc = xa*(xk + xcw)/xfi;
|
|
xn = rgm / (xa * sqrt(xa));
|
|
|
|
ecl->vx = xn*((2.0*xp*xp - 1.0)*xms + 2.0*xp*xq*xmc);
|
|
ecl->vy = xn*((1.0 - 2.0*xq*xq)*xmc - 2.0*xp*xq*xms);
|
|
ecl->vz = 2.0*xn*xki*(xp*xms + xq*xmc);
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopEquatorial(const top_rectangular_t *ecl, top_rectangular_t *equ)
|
|
{
|
|
static int initialized;
|
|
static double rot[3][3];
|
|
|
|
if (!initialized)
|
|
{
|
|
const double pi = dpi / 2.0;
|
|
const double dgrad = pi / 180.0;
|
|
const double sdrad = dgrad / 3600.0;
|
|
const double eps = (23.0 + 26.0/60.0 + 21.41136/3600.0)*dgrad;
|
|
const double phi = -0.05188 * sdrad;
|
|
const double ceps = cos(eps);
|
|
const double seps = sin(eps);
|
|
const double cphi = cos(phi);
|
|
const double sphi = sin(phi);
|
|
|
|
rot[0][0] = cphi;
|
|
rot[0][1] = -sphi*ceps;
|
|
rot[0][2] = sphi*seps;
|
|
rot[1][0] = sphi;
|
|
rot[1][1] = cphi*ceps;
|
|
rot[1][2] = -cphi*seps;
|
|
rot[2][0] = 0.0;
|
|
rot[2][1] = seps;
|
|
rot[2][2] = ceps;
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
equ->x = (rot[0][0] * ecl->x) + (rot[0][1] * ecl->y) + (rot[0][2] * ecl->z);
|
|
equ->y = (rot[1][0] * ecl->x) + (rot[1][1] * ecl->y) + (rot[1][2] * ecl->z);
|
|
equ->z = (rot[2][0] * ecl->x) + (rot[2][1] * ecl->y) + (rot[2][2] * ecl->z);
|
|
|
|
equ->vx = (rot[0][0] * ecl->vx) + (rot[0][1] * ecl->vy) + (rot[0][2] * ecl->vz);
|
|
equ->vy = (rot[1][0] * ecl->vx) + (rot[1][1] * ecl->vy) + (rot[1][2] * ecl->vz);
|
|
equ->vz = (rot[2][0] * ecl->vx) + (rot[2][1] * ecl->vy) + (rot[2][2] * ecl->vz);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int TopPosition(const top_model_t *model, double tt, top_rectangular_t *equ)
|
|
{
|
|
int error;
|
|
top_elliptical_t ellip;
|
|
top_rectangular_t ecl;
|
|
|
|
CHECK(TopCalcElliptical(model, tt, &ellip));
|
|
CHECK(TopEcliptic(model->planet, &ellip, &ecl));
|
|
CHECK(TopEquatorial(&ecl, equ));
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopSquash(top_model_t *copy, const top_model_t *original, const top_contrib_map_t *map)
|
|
{
|
|
int f, s, t, i, n;
|
|
int error;
|
|
|
|
/*
|
|
Use 'map' (and its embedded skip list) to copy the selected
|
|
most-significant terms from 'original' into 'map'.
|
|
*/
|
|
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
if (map->list[f].skip < 0 || map->list[f].skip >= map->list[f].nterms)
|
|
FAIL("TopSquash: invalid skip count %d for term count %d at f=%d\n", map->list[f].skip, map->list[f].nterms, f);
|
|
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
{
|
|
top_series_t *cform = copy->series[f];
|
|
const top_series_t *oform = original->series[f];
|
|
const top_contrib_list_t *list = &map->list[f];
|
|
|
|
/* Start over with all the series in 'copy' empty. */
|
|
for (s=0; s < TOP_NSERIES; ++s)
|
|
cform[s].nterms_calc = 0;
|
|
|
|
/* Append the remaining terms one at a time, in descending order of importance. */
|
|
for (i = list->nterms - 1; i >= list->skip; --i)
|
|
{
|
|
s = list->array[i].s;
|
|
t = list->array[i].t;
|
|
n = cform[s].nterms_calc++;
|
|
cform[s].terms[n] = oform[s].terms[t];
|
|
}
|
|
}
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopSetDistance(
|
|
top_model_t *copy,
|
|
top_contrib_map_t *map,
|
|
int *term_count,
|
|
const top_model_t *original,
|
|
double distance,
|
|
const top_direction_t *dir)
|
|
{
|
|
int error = 1;
|
|
int f;
|
|
double xterms;
|
|
int nterms;
|
|
|
|
/*
|
|
Use the scalar 'distance', multiplied by the vector 'dir' to produce a position vector.
|
|
That position vector is rounded to a vector with integer components, which becomes
|
|
the skip list. Force the skip list to be valid by keeping each component in the range
|
|
[0, nterms-1]. Then squash 'original' into 'map' using the resulting map.
|
|
*/
|
|
|
|
*term_count = 0;
|
|
for (f=0; f < TOP_NCOORDS; ++f)
|
|
{
|
|
/* Calculate a real-valued number of terms. */
|
|
xterms = distance * dir->x[f];
|
|
|
|
/* Clamp and round to the allowed range of values. */
|
|
if (xterms < 1.0)
|
|
nterms = 1;
|
|
else if (xterms > map->list[f].nterms)
|
|
nterms = map->list[f].nterms;
|
|
else
|
|
nterms = (int)lround(xterms);
|
|
|
|
/* Set the skip value so as to keep 'nterms' terms. */
|
|
map->list[f].skip = map->list[f].nterms - nterms;
|
|
|
|
*term_count += nterms;
|
|
}
|
|
|
|
CHECK(TopSquash(copy, original, map));
|
|
fail:
|
|
return error;
|
|
}
|
|
|
|
|
|
void TopInitRandomBuffer(top_random_buffer_t *buffer)
|
|
{
|
|
memset(buffer, 0, sizeof(top_random_buffer_t));
|
|
}
|
|
|
|
|
|
void TopFreeRandomBuffer(top_random_buffer_t *buffer)
|
|
{
|
|
free(buffer->array);
|
|
TopInitRandomBuffer(buffer);
|
|
}
|
|
|
|
|
|
int TopGetRandomNumber(top_random_buffer_t *buffer, double *r)
|
|
{
|
|
int error = 1;
|
|
int i;
|
|
FILE *randfile = NULL;
|
|
|
|
if (buffer->array == NULL)
|
|
{
|
|
/* First call: allocate array. */
|
|
buffer->gencount = 0;
|
|
buffer->length = 0x10000;
|
|
buffer->offset = buffer->length; /* indicate buffer is empty... force a read below. */
|
|
buffer->array = malloc(buffer->length * sizeof(buffer->array[0]));
|
|
if (buffer->array == NULL)
|
|
FAIL("TopGetRandomNumber: out of memory trying to allocate %d bytes.\n", buffer->length);
|
|
}
|
|
|
|
if (buffer->offset == buffer->length)
|
|
{
|
|
const char *filename = "/dev/urandom";
|
|
randfile = fopen(filename, "rb");
|
|
if (randfile == NULL)
|
|
FAIL("TopGetRandomNumber: cannot open input file: %s\n", filename);
|
|
|
|
for (i=0; i < buffer->length; ++i)
|
|
{
|
|
uint64_t x = 0;
|
|
while (x == 0) /* must read a number in the range [1, 2^64 - 1]. */
|
|
{
|
|
size_t count = fread(&x, sizeof(x), 1, randfile);
|
|
if (count != 1)
|
|
FAIL("TopGetRandomNumber: failure to read from file: %s\n", filename);
|
|
}
|
|
/* Convert to floating point value in half-open range (0, 1]. */
|
|
buffer->array[i] = (double)x / (double)0xffffffffffffffffUL;
|
|
|
|
/* Sanity check that the required range is satisfied. */
|
|
if (buffer->array[i] <= 0.0 || buffer->array[i] > 1.0)
|
|
FAIL("TopGetRandomNumber: generated value %lf is illegal.\n", buffer->array[i]);
|
|
}
|
|
|
|
buffer->offset = 0;
|
|
}
|
|
|
|
*r = buffer->array[(buffer->offset)++];
|
|
++(buffer->gencount);
|
|
error = 0;
|
|
fail:
|
|
if (randfile != NULL) fclose(randfile);
|
|
return error;
|
|
}
|
|
|
|
|
|
int TopGetDirection(top_direction_t *dir, top_random_buffer_t *buffer)
|
|
{
|
|
int error = 1;
|
|
int f;
|
|
double u, v, sum;
|
|
double r2, radius, angle;
|
|
|
|
/*
|
|
Use the Box-Muller transform to get 3 pairs of normally-distributed, uniform random variables.
|
|
https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
|
|
|
|
Turn this into a random unit direction vector in 6-dimensional space
|
|
by picking a uniformly distributed point on the surface of a 6-dimensional hypersphere.
|
|
https://mathworld.wolfram.com/HyperspherePointPicking.html
|
|
*/
|
|
|
|
sum = 0.0;
|
|
for (f = 0; f < TOP_NCOORDS; f += 2)
|
|
{
|
|
CHECK(TopGetRandomNumber(buffer, &u));
|
|
CHECK(TopGetRandomNumber(buffer, &v));
|
|
r2 = -2.0 * log(u);
|
|
sum += r2;
|
|
radius = sqrt(r2);
|
|
angle = dpi * v;
|
|
dir->x[f] = radius * cos(angle);
|
|
dir->x[f+1] = radius * sin(angle);
|
|
}
|
|
|
|
/* Convert to a unit vector with all components non-negative. */
|
|
sum = sqrt(sum);
|
|
for (f = 0; f < TOP_NCOORDS; ++f)
|
|
dir->x[f] = fabs(dir->x[f] / sum);
|
|
|
|
error = 0;
|
|
fail:
|
|
return error;
|
|
}
|