126 lines
5.3 KiB
C
126 lines
5.3 KiB
C
#include "observer.h"
|
|
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <stddef.h>
|
|
|
|
static const double pi = 3.14159265358979323846;
|
|
|
|
ObserverState observer_fixed_at_origin(void) {
|
|
return (ObserverState){.coordinate_time = 0.0,
|
|
.coordinate_position = {0.0, 0.0, 0.0},
|
|
.tetrad = {{1.0, 0.0, 0.0, 0.0},
|
|
{0.0, 0.0, 0.0, -1.0},
|
|
{0.0, 0.0, 1.0, 0.0},
|
|
{0.0, 1.0, 0.0, 0.0}}};
|
|
}
|
|
|
|
ObserverState observer_fixed_at_origin_look_at(double ra_deg, double dec_deg) {
|
|
const double ra = ra_deg * pi / 180.0;
|
|
const double dec = dec_deg * pi / 180.0;
|
|
const double cos_ra = cos(ra), sin_ra = sin(ra);
|
|
const double cos_dec = cos(dec), sin_dec = sin(dec);
|
|
const double forward[3] = {cos_dec * cos_ra, cos_dec * sin_ra, sin_dec};
|
|
const double up[3] = {-sin_dec * cos_ra, -sin_dec * sin_ra, cos_dec};
|
|
/* forward cross celestial north is celestial west: with north up, image
|
|
* right is decreasing RA. */
|
|
const double right[3] = {sin_ra, -cos_ra, 0.0};
|
|
return (ObserverState){.coordinate_time = 0.0,
|
|
.coordinate_position = {0.0, 0.0, 0.0},
|
|
.tetrad = {{1.0, 0.0, 0.0, 0.0},
|
|
{0.0, forward[0], forward[1], forward[2]},
|
|
{0.0, up[0], up[1], up[2]},
|
|
{0.0, right[0], right[1], right[2]}}};
|
|
}
|
|
|
|
/* Use the 3+1 form directly, including the shift in every four-vector. */
|
|
static double inner(const MetricData *m, const double a[4], const double b[4]) {
|
|
double value = -m->alpha * m->alpha * a[0] * b[0];
|
|
for (int i = 0; i < 3; ++i)
|
|
for (int j = 0; j < 3; ++j)
|
|
value += m->gamma[i][j] * (a[i + 1] + m->beta[i] * a[0]) *
|
|
(b[j + 1] + m->beta[j] * b[0]);
|
|
return value;
|
|
}
|
|
|
|
static int valid_metric(const MetricData *m) {
|
|
if (!isfinite(m->alpha) || m->alpha <= 0.0) return 0;
|
|
double l[3][3] = {{0}};
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (!isfinite(m->beta[i])) return 0;
|
|
for (int j = 0; j <= i; ++j) {
|
|
double value = m->gamma[i][j];
|
|
const double transposed = m->gamma[j][i];
|
|
if (!isfinite(value) || !isfinite(transposed) ||
|
|
fabs(value - transposed) > 32 * DBL_EPSILON *
|
|
fmax(fabs(value), fabs(transposed)))
|
|
return 0;
|
|
for (int k = 0; k < j; ++k) value -= l[i][k] * l[j][k];
|
|
if (i == j) {
|
|
if (!isfinite(value) || value <= 0.0) return 0;
|
|
l[i][j] = sqrt(value);
|
|
} else l[i][j] = value / l[j][j];
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ObserverBuildResult observer_from_coordinate_camera(
|
|
const MetricData *metric, const ObserverCamera *camera,
|
|
ObserverState *out, double *q) {
|
|
if (q) *q = NAN;
|
|
if (!metric || !camera || !out || !valid_metric(metric) ||
|
|
!isfinite(camera->coordinate_time) ||
|
|
!isfinite(camera->look_ra_deg) || camera->look_ra_deg < 0.0 ||
|
|
camera->look_ra_deg >= 360.0 || !isfinite(camera->look_dec_deg) ||
|
|
fabs(camera->look_dec_deg) > 90.0 || !isfinite(camera->roll_deg))
|
|
return OBSERVER_BUILD_INVALID_INPUT;
|
|
ObserverState state = observer_fixed_at_origin_look_at(
|
|
camera->look_ra_deg, camera->look_dec_deg);
|
|
state.coordinate_time = camera->coordinate_time;
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (!isfinite(camera->position[i]) || !isfinite(camera->velocity[i]))
|
|
return OBSERVER_BUILD_INVALID_INPUT;
|
|
state.coordinate_position[i] = camera->position[i];
|
|
state.tetrad[0][i + 1] = camera->velocity[i];
|
|
}
|
|
const double norm = inner(metric, state.tetrad[0], state.tetrad[0]);
|
|
if (q) *q = norm;
|
|
if (!isfinite(norm) || norm >= 0.0) return OBSERVER_BUILD_NON_TIMELIKE;
|
|
for (int mu = 0; mu < 4; ++mu) state.tetrad[0][mu] /= sqrt(-norm);
|
|
for (int a = 1; a < 4; ++a) {
|
|
/* Modified Gram-Schmidt with reorthogonalization in the observer rest
|
|
* space; the coordinate forward seed is given first priority. */
|
|
for (int pass = 0; pass < 2; ++pass)
|
|
for (int b = 0; b < a; ++b) {
|
|
const double projection = inner(metric, state.tetrad[a], state.tetrad[b]);
|
|
for (int mu = 0; mu < 4; ++mu)
|
|
state.tetrad[a][mu] -= (b == 0 ? -projection : projection) *
|
|
state.tetrad[b][mu];
|
|
}
|
|
const double length2 = inner(metric, state.tetrad[a], state.tetrad[a]);
|
|
if (!isfinite(length2) || length2 <= 0.0)
|
|
return OBSERVER_BUILD_INVALID_TETRAD;
|
|
for (int mu = 0; mu < 4; ++mu) state.tetrad[a][mu] /= sqrt(length2);
|
|
}
|
|
const double roll = remainder(camera->roll_deg, 360.0) * pi / 180.0;
|
|
for (int mu = 0; mu < 4; ++mu) {
|
|
const double up = state.tetrad[2][mu], right = state.tetrad[3][mu];
|
|
state.tetrad[2][mu] = cos(roll) * up + sin(roll) * right;
|
|
state.tetrad[3][mu] = -sin(roll) * up + cos(roll) * right;
|
|
}
|
|
for (int a = 0; a < 4; ++a) {
|
|
for (int mu = 0; mu < 4; ++mu)
|
|
if (!isfinite(state.tetrad[a][mu])) return OBSERVER_BUILD_INVALID_TETRAD;
|
|
for (int b = 0; b <= a; ++b) {
|
|
const double error = inner(metric, state.tetrad[a], state.tetrad[b]) -
|
|
(a == b ? (a == 0 ? -1.0 : 1.0) : 0.0);
|
|
if (!isfinite(error) || fabs(error) > 1e-8)
|
|
return OBSERVER_BUILD_INVALID_TETRAD;
|
|
}
|
|
}
|
|
if (state.tetrad[0][0] <= 0.0) return OBSERVER_BUILD_INVALID_TETRAD;
|
|
*out = state;
|
|
return OBSERVER_BUILD_OK;
|
|
}
|