// C ABI wrapper around the C++ ITWOM entry points so Rust can FFI them // cleanly. The C++ originals take `double &` / `int &` reference params, // which have no C equivalent; we translate them to pointers. #include extern void point_to_point(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, char *strmode, int &errnum); extern void point_to_point_ITM(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, char *strmode, int &errnum); extern double ITWOMVersion(); extern "C" { // The C++ point_to_point / point_to_point_ITM signatures are non-const on // `elev`, but by inspection of itwom3.0.cpp neither routine writes into // the elev array — they only read samples (and use scratch space held in // the thread_local statics we patched in). Passing the caller's buffer // directly avoids a per-call heap alloc + memcpy that otherwise dominates // a full map sweep at ~200 M invocations. // // If a future upstream update to ITWOM starts mutating `elev`, Rust's // ownership guarantees still hold: the elev_buf in compute_radial gets // cleared and refilled at the top of every ITWOM call, so mid-call // scribbles would be overwritten before being observed. void splat_point_to_point(double* elev, double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double* dbloss, char* strmode, int strmode_len, int* errnum) { double loss = 0.0; int err = 0; char mode[64] = {0}; point_to_point(elev, tht_m, rht_m, eps_dielect, sgm_conductivity, eno_ns_surfref, frq_mhz, radio_climate, pol, conf, rel, loss, mode, err); *dbloss = loss; *errnum = err; if (strmode && strmode_len > 0) { std::strncpy(strmode, mode, (size_t)strmode_len - 1); strmode[strmode_len - 1] = '\0'; } } void splat_point_to_point_itm(double* elev, double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double* dbloss, char* strmode, int strmode_len, int* errnum) { double loss = 0.0; int err = 0; char mode[64] = {0}; point_to_point_ITM(elev, tht_m, rht_m, eps_dielect, sgm_conductivity, eno_ns_surfref, frq_mhz, radio_climate, pol, conf, rel, loss, mode, err); *dbloss = loss; *errnum = err; if (strmode && strmode_len > 0) { std::strncpy(strmode, mode, (size_t)strmode_len - 1); strmode[strmode_len - 1] = '\0'; } } double splat_itwom_version() { return ITWOMVersion(); } } // extern "C"