Files
GR-raytracing/src/observer.c
T

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;
}