Example 4: Plasmasphere and Ionosphere¶
Sample the GCPM v2.4 electron-density model along the equatorial radial
direction, then ray-trace a GNSS signal through the ionosphere/plasmasphere and
report the total electron content (TEC) and dispersive delay. This exercises the
pecsim backend that sits behind pylupnt.plasma (GCPM v2.4, IRI-2007, and
the Haselgrove ray-tracer).
Mirrors Example 4: Plasmasphere and Ionosphere Tutorial.
// Example 4: Plasmasphere and Ionosphere
// ------------------------------------------------------------------------------
// C++ counterpart of python/examples/ex4_plasmasphere.ipynb.
//
// Demonstrates LuPNT's plasma/ionosphere module (the `pecsim` backend behind
// `pylupnt.plasma`):
//
// Part 1 Sample the GCPM v2.4 electron-density model along the equatorial
// radial direction (a 1-D slice through the plasmasphere).
// Part 2 Ray-trace a GNSS signal through the ionosphere/plasmasphere and
// report the total electron content (TEC) and dispersive delay.
//
// References: GCPM v2.4 (Gallagher et al. 2000), IRI-2007 (Bilitza & Reinisch
// 2008), ray-tracing (Haselgrove 1955 / pecsim implementation).
#include <iomanip>
#include <iostream>
#include <vector>
#include "lupnt/environment/plasma/plasma.h"
using namespace pecsim;
int main() {
// --- Epoch and space-weather index ----------------------------------------
const int year = 2026, month = 1, day = 1, hour = 0;
const double mjd = gregorian_to_mjd(year, month, day, hour, 0, 0.0);
const double epoch_j2000 = mjd_to_tj2000(mjd);
const DateTime dt = mjd_to_datetime(mjd);
double kp = get_kp_index(dt);
if (kp < 0) kp = 2.0; // fall back if the Kp table is unavailable
set_iri_model(IRIModel::IRI_2007);
std::cout << "Epoch : " << year << "-" << month << "-" << day << " " << hour << ":00 UTC\n";
std::cout << "J2000 : " << std::fixed << std::setprecision(0) << epoch_j2000 << " s\n";
std::cout << "Kp : " << std::setprecision(2) << kp << "\n";
std::cout << "RE : " << RE << " km\n\n";
// --- Part 1: equatorial electron-density profile ---------------------------
// gcpm_v24(datetime, r_RE, amlt, alatr, kp) -> [ne_cm3, H+, He+, O+].
// r_RE : geocentric distance in Earth radii
// amlt : magnetic local time [h]; 12 = noon
// alatr : magnetic latitude [rad]; 0 = magnetic equator
const double amlt = 12.0; // noon meridian
const double alatr = 0.0; // magnetic equator
std::cout << "Equatorial electron density (noon meridian), GCPM v2.4:\n";
std::cout << " r [RE] ne [cm^-3]\n";
for (double r_RE = 1.5; r_RE <= 6.0 + 1e-9; r_RE += 0.5) {
std::vector<double> n = gcpm_v24(dt, r_RE, amlt, alatr, kp);
std::cout << " " << std::setw(5) << std::setprecision(1) << r_RE << " " << std::scientific
<< std::setprecision(3) << n[0] << std::fixed << "\n";
}
// --- Part 2: ray-trace a GNSS link through the plasmasphere -----------------
// Illustrative geometry: a horizontal ray skimming the plasmasphere at ~1000 km
// altitude, from a GNSS-altitude transmitter toward a distant (lunar-range)
// receiver, so the whole path samples the modelled electron content.
RayTraceConfig config;
config.freq_Hz = freq_L1; // GPS L1
config.step_size = 50.0; // ray-trace step [km]
config.cutoff_r = 6.0 * RE; // stop integrating beyond 6 RE [km]
config.gradn_dx = 1.0; // refractive-index gradient step [km]
config.integ_method = "Euler";
config.correction = false; // geometric straight-ray TEC (no bending correction)
config.fine_correction = false;
config.use_fortran_gcpm = false;
config.kp = kp;
const double h_km = RE + 1000.0; // tangent altitude ~1000 km
const Vec3d pos_tx{-20000.0, h_km, 0.0}; // transmitter [km, ECI]
const Vec3d pos_rx{380000.0, h_km, 0.0}; // receiver (lunar range) [km, ECI]
std::cout << "\nRay trace (GPS L1) from " << pos_tx.transpose() << " km\n"
<< " to " << pos_rx.transpose() << " km\n";
PathProfile pp = trace_ray(epoch_j2000, pos_tx, pos_rx, config, /*debug_prop=*/false,
/*debug_corr=*/false);
std::cout << std::setprecision(3);
std::cout << " TEC : " << pp.tecu << " TECU\n";
std::cout << " TEC delay : " << pp.tec_delay_m << " m\n";
std::cout << " Total delay: " << pp.total_delay_m << " m\n";
return 0;
}
pixi run build-examples
./build-examples-pixi/ex4_plasmasphere
The higher-fidelity, closed-loop ray-trace example (with a full GNSS
constellation and occultation gating) is cpp/examples/environment/ex_plasma_raytrace.cc.