/*
* cspice_ops.c — CSPICE operation implementations for spice_worker.
*
* Compiled only when HAVE_CSPICE is defined (i.e. CSPICE sources are present).
* Without CSPICE all operations return a clear "not available" error so the
* worker binary can still be built and the protocol exercised in tests.
*/
#include "cspice_ops.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#define RAD_TO_DEG (180.0 / 3.14159265358979323846)
/* ── Helpers ────────────────────────────────────────────────────────────── */
static void fill_error(char *buf, int size, const char *msg) {
if (buf && size > 0) {
strncpy(buf, msg, size - 1);
buf[size - 1] = '\0';
}
}
/* ── With CSPICE ─────────────────────────────────────────────────────────── */
#ifdef HAVE_CSPICE
/* Retrieve CSPICE long error message and reset the error state. */
static void get_cspice_error(char *buf, int size) {
SpiceChar msg[1024] = {0};
getmsg_c("LONG", 1024, msg);
reset_c();
fill_error(buf, size, msg);
}
void ops_init(void) {
/* Configure CSPICE to RETURN on error instead of aborting the process. */
erract_c("SET", 4096, "RETURN");
}
int ops_clear_kernels(char *error_buf, int buf_size) {
kclear_c();
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
return 0;
}
int ops_load_kernels(const char **paths, int count,
char *error_buf, int buf_size) {
for (int i = 0; i < count; i++) {
furnsh_c(paths[i]);
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
}
return 0;
}
int ops_utc_to_et(const char *iso8601, double *et_out,
char *error_buf, int buf_size) {
str2et_c(iso8601, et_out);
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
return 0;
}
int ops_state(const char *target, double et, const char *observer,
const char *frame, const char *abcorr,
SpiceState *out, char *error_buf, int buf_size) {
SpiceDouble state[6];
SpiceDouble lt;
spkezr_c(target, et, frame, abcorr, observer, state, <);
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
/* reclat_c: rectangular -> radius (km), longitude (rad), latitude (rad) */
SpiceDouble radius, lon_rad, lat_rad;
reclat_c(state, &radius, &lon_rad, &lat_rad);
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
/* Convert radius km -> AU using CSPICE convrt_c */
SpiceDouble radius_au;
convrt_c(radius, "KM", "AU", &radius_au);
if (failed_c()) {
get_cspice_error(error_buf, buf_size);
return -1;
}
/* lon_rad in (-pi, pi]; convert to degrees and normalize to [0, 360) */
double lon_deg = lon_rad * RAD_TO_DEG;
if (lon_deg < 0.0) lon_deg += 360.0;
double lat_deg = lat_rad * RAD_TO_DEG;
out->state_km[0] = state[0];
out->state_km[1] = state[1];
out->state_km[2] = state[2];
out->state_km[3] = state[3];
out->state_km[4] = state[4];
out->state_km[5] = state[5];
out->distance_au = radius_au;
out->ecliptic_longitude = lon_deg;
out->ecliptic_latitude = lat_deg;
out->light_time_seconds = lt;
out->et = et;
return 0;
}
/* ── Without CSPICE (stub) ──────────────────────────────────────────────── */
#else /* !HAVE_CSPICE */
void ops_init(void) { /* nothing */ }
int ops_clear_kernels(char *error_buf, int buf_size) {
fill_error(error_buf, buf_size, "CSPICE not available in this build");
return -1;
}
int ops_load_kernels(const char **paths, int count,
char *error_buf, int buf_size) {
(void)paths; (void)count;
fill_error(error_buf, buf_size, "CSPICE not available in this build");
return -1;
}
int ops_utc_to_et(const char *iso8601, double *et_out,
char *error_buf, int buf_size) {
(void)iso8601; (void)et_out;
fill_error(error_buf, buf_size, "CSPICE not available in this build");
return -1;
}
int ops_state(const char *target, double et, const char *observer,
const char *frame, const char *abcorr,
SpiceState *out, char *error_buf, int buf_size) {
(void)target; (void)et; (void)observer; (void)frame; (void)abcorr; (void)out;
fill_error(error_buf, buf_size, "CSPICE not available in this build");
return -1;
}
#endif /* HAVE_CSPICE */