Skip to content
nickdambrPublic

About

C++17 interceptor-vs-drone GNC engagement simulator: proportional-navigation guidance, seeker/EKF navigation, and Monte Carlo analysis

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

DMSSB

A C++17 interceptor-vs-drone GNC engagement simulator.

C++17 CMake License: MIT

DMSSB simulates the guidance, navigation, and control of an interceptor engaging small quadrotor UAS that loiter or fly trajectories inside a protected zone. It is built as a portfolio piece for aerospace GNC work: proportional- navigation-family guidance, a seeker-plus-EKF navigation chain, an inertial navigation model, and Monte Carlo campaigns that compare guidance laws on kill probability and miss distance — all in dependency-free, tested, cross-platform C++.

The engagement loop is the classic one:

        target truth ──► seeker ──► EKF tracker ──► guidance law ──► autopilot
             ▲              (az/el/range,          (PN / APN / ZEM /   (accel cmd →
             │               noise, FOV)            optimal)            achieved accel)
        quadrotor  ◄──────────────── engagement manager ◄─── interceptor dynamics
        (loiter / patrol / evade)     (launch, assignment, intercept adjudication)

Design goals

  • Zero third-party dependencies in the simulation core. Vectors, fixed-size matrices, quaternions, the RK4 integrator, the seeded RNG, the CSV writer and the config parser are all written from scratch — the fundamentals an aerospace GNC review looks at. GoogleTest is used for the test suite only.
  • Deterministic and reproducible. Integer-tick time, fixed-step integration, named per-run RNG streams: same seed → same result, independent of thread count. Every output directory is stamped with its seed and config hash.
  • Physically honest. SI units, an explicit and documented set of frame and quaternion conventions (see docs/), and analytic-reference unit tests for the math and the guidance laws.
  • Comparative, not just demonstrative. Five guidance laws behind one interface, driven through the same seeker/EKF chain, compared over Monte Carlo dispersions.

Status

Built in independently shippable phases, complete through P7 — 1-vs-N zone defence. The full stack now runs from a rigid-body interceptor with an inertial navigation system up to a single air-defence fire unit defending an asset against a raid of attacking drones: threat prioritisation, sequential shoot-look-shoot re-engagement through one seeker/EKF/guidance channel, and defence-effectiveness metrics. Every earlier configuration — down to the 1-vs-1 point-mass engagement — still reproduces bit-for-bit. See the roadmap.

Results so far

Guidance (P2), on a loitering drone with perfect navigation — the classic lead-versus-tail-chase contrast:

Guidance law Outcome
True PN hit
Pure pursuit miss (tail-chase)

Estimation (P3): the same weaving drone, Augmented PN throughout, 25 seeds. The miss distance is the true closest approach, not the first sample inside the lethal radius.

Navigation Target-motion model Mean miss Worst
Perfect (guidance sees truth) — 0.11 m 0.18 m
Seeker + EKF Singer (correlated acceleration) 0.34 m 0.65 m
Seeker + EKF Constant velocity (no acceleration state) 0.22 m 0.47 m

All 75 runs hit. Two things worth reading carefully:

The estimation chain costs about 3x in miss distance. That gap — not the guidance law — is what a real interceptor pays for having to estimate the target rather than being told where it is.

The more sophisticated motion model is not automatically the better one. Against this 2-second weave, Singer does worse than a plain constant-velocity model. At this seeker noise level the target's acceleration is only weakly observable, so Singer's acceleration estimate lags the weave in phase — and feeding Augmented PN a mis-phased acceleration is worse than feeding it none. Singer's advantage is real but it appears on sustained manoeuvres, where the acceleration is observable; the unit tests show it clearly beating constant-velocity on both velocity and acceleration error there. Which model wins is a property of the manoeuvre and the sensor, not of the model's sophistication — and settling it properly is what the P4 Monte Carlo campaign is for.

The filter is consistent in the live engagement, not only in a synthetic test: its NIS averages 2.87 against an expected 3.0, with 96% of samples inside the 95% chi-square bounds.

The comparative study (P4)

2000 engagements: five guidance laws crossed with two target-motion models, 200 dispersed runs each, every one driven through the full seeker + EKF chain. Run i of every law flies the same dispersed scenario with the same sensor noise, so the comparison is paired rather than confounded by scatter. The interceptor has a sluggish airframe (0.35 s acceleration lag) and a 20 g limit.

Guidance Motion model P(kill) CEP50 CEP90 Control effort
pursuit either 1.5% 9.64 m 12.79 m 7 500
True PN CV 93.5% 0.67 m 1.81 m 13 680
True PN Singer 87.0% 0.82 m 2.12 m 13 783
Augmented PN CV 93.5% 0.67 m 1.81 m 13 680
Augmented PN Singer 78.0% 0.92 m 2.54 m 14 273
ZEM CV 93.5% 0.67 m 1.81 m 13 680
ZEM Singer 78.0% 0.92 m 2.54 m 14 273
Optimal (LQ) CV 100% 0.14 m 0.31 m 12 045
Optimal (LQ) Singer 100% 0.13 m 0.30 m 12 329

Optimal guidance wins on both axes at once. It is the only law that knows the airframe has a lag, so instead of piling command on top of acceleration already in the pipe, it credits it. The result is a CEP nearly five times better and less acceleration spent — which is precisely the claim an LQ law has to make good on. Pure pursuit, at the other extreme, is essentially useless against a crossing target: it kills 1.5% of the time.

Two things fall out of the table that are worth more than the ranking itself:

Augmented PN, ZEM and True PN are bit-identical whenever the tracker is CV. That is not a coincidence and not a bug — a constant-velocity model estimates zero target acceleration, so the terms that distinguish those three laws vanish and they collapse onto the same law. It is a free internal consistency check that three independent code paths agree.

The manoeuvre-aware motion model makes things worse, and it hurts most the laws that trust it. Singer costs True PN 6.5 points of kill probability but costs Augmented PN and ZEM 15.5 — because those two consume its acceleration estimate. Against this 2-second weave the target's acceleration is only weakly observable at the seeker's noise level, so Singer's estimate lags in phase, and a mis-phased acceleration is worse than none. Optimal guidance is barely affected, because what it mainly exploits is the airframe, not the target. Model sophistication is not free, and it is not automatically an improvement.

The campaign also found a failure mode no single run would have shown. In 0.5% of runs the interceptor flew straight past the drone at 470 m with the guidance never engaging at all. A round launched slowly at a crossing target must fly a large lead angle — and a strapdown seeker looks along the velocity vector, so the target sits outside its 30° field of view, the track never starts, and the round never turns. Giving the seeker a gimbal (60° look angle) removed the failure entirely. That is what a Monte Carlo campaign is for.

Does the win survive a real airframe? (P5)

P4's headline — optimal (LQ) guidance wins on both axes — was made against a point mass with an idealised first-order acceleration lag. P5 asks whether it survives a 6DOF rigid-body airframe: real rigid-body dynamics on a synthetic transonic aero deck, a finite-bandwidth three-loop autopilot deflecting rate-limited fins, dynamic-pressure-limited manoeuvrability, and a seeker that now reports in the airframe's true attitude. The exact same campaign — 5 laws × 2 tracker models × 200 dispersed runs — is re-flown with model = sixdof, paired run-for-run against the point mass (same seeds, same dispersions, same sensor noise), so the only thing that changes is the airframe.

Guidance Motion model P(kill) 3DOF → 6DOF CEP50 3DOF → 6DOF
pursuit either 1.5% → 2.0% 9.64 → 9.81 m
True PN CV 93.5% → 80.0% 0.67 → 1.32 m
True PN Singer 87.0% → 67.5% 0.82 → 1.53 m
Augmented PN CV 93.5% → 80.0% 0.67 → 1.32 m
Augmented PN Singer 78.0% → 52.0% 0.92 → 1.96 m
ZEM CV 93.5% → 80.0% 0.67 → 1.32 m
ZEM Singer 78.0% → 52.0% 0.92 → 1.96 m
Optimal (LQ) CV 100% → 100% 0.14 → 0.31 m
Optimal (LQ) Singer 100% → 100% 0.13 → 0.41 m

The optimal-guidance win survives — and widens. On the real airframe the LQ law still kills 100% on both tracker models, and its CEP stays roughly four to five times better than the whole PN family. Its miss distance about doubles (0.14 → 0.31 m), which is the honest price of a real airframe over an idealised lag — but it is the only law that does not lose kill probability, because the PN family loses a lot: True PN drops 13 points, and Augmented PN and ZEM fall from 78% to 52% against the weaving target. A finite-bandwidth autopilot on a rate-limited airframe is simply harder to steer precisely than a clean first-order lag, and the laws that do not model the airframe pay for it. The gap the LQ law opened in P4 is wider here, not narrower.

The APN ≡ ZEM ≡ True-PN-under-CV identity survives the airframe swap bit-for-bit (all three: 80.0% kill, 1.318 m CEP50, identical control effort) — the same free three-way consistency check, now across two entirely different dynamics models. Control effort is not compared across airframes here: the point mass clips at a 20 g load limit while the 6DOF is limited by dynamic pressure and fin authority, so the two integrate different quantities.

How much of that is the airframe, and how much is just less capability? The 6DOF round is not only a different airframe — it is also a less capable one: its terminal manoeuvrability is about 13 g (dynamic-pressure limited) against the point mass's flat 20 g, and its emergent acceleration lag — identified as an equivalent first-order time constant from the autopilot's closed-loop step response — is about 0.28 s at terminal speed, not the 0.35 s the optimal law was told to assume. A matched-capability control isolates the two effects: the point-mass campaign re-flown with accel_tau set to the measured 0.28 s and accel_limit to 13 g. For optimal guidance:

Round CEP50 (CV / Singer)
Idealised point mass (0.35 s lag, 20 g) 0.14 / 0.13 m
Matched point mass (0.28 s lag, 13 g) 0.18 / 0.18 m
6DOF airframe 0.31 / 0.41 m

Only about a third of the 6DOF's extra miss is explained by the reduced capability; the larger share is genuine airframe fidelity — the finite autopilot bandwidth, the fin rate limits, and the pitch/yaw/roll coupling that a first-order lag and a load clip do not capture. The single-run airframe plot (analysis/plot_6dof.py) shows the fins working against their deflection and rate limits through the terminal weave, which is where that residual comes from.

How much does navigation error cost? (P6)

Every phase so far gave the interceptor perfect knowledge of its own state. P6 removes that: a strapdown IMU feeds a mechanization that drifts, and both guidance and the seeker/EKF consume the drifting own-ship estimate. The study sweeps the own-ship source over perfect → tracked → ins, paired run-for-run (same dispersions, same sensor noise, same IMU realisation), so the change is purely navigation. Optimal guidance, 6DOF airframe, evasive drone, 200 dispersed runs.

Own-ship navigation P(kill) CEP50 CEP90
Perfect (own truth) 100% 0.02 m 0.04 m
Tracked (own truth, EKF target) 100% 0.41 m 0.70 m
Strapdown INS (tactical IMU) 100% 0.41 m 0.69 m

A realistic tactical-grade INS adds essentially nothing to the miss — and the reason is worth more than the number. The interceptor homes on angles and range, so its own-ship position error drops straight out of the relative geometry: the EKF reconstructs the target as own_position + line_of_sight, and that same own-position offset then cancels when guidance forms target − own. Only the own-attitude error (which rotates the reconstructed target across the line of sight) and part of the own-velocity error survive — and over this short 4-second fly-out they are swamped by the target-estimation error the EKF already carries (the jump from 0.02 m to 0.41 m is all target estimation; navigation adds under a centimetre to CEP50). At the IMU's true drift — about 7 m of position error and 0.35° of attitude error at intercept — the round barely notices.

Navigation error does eventually bite, but only for a far worse sensor: sweeping the IMU grade, kill probability holds at 100% out to ~10× the tactical error, falls to 98.5% (CEP50 0.62 m) at ~25×, and collapses to 48% only at ~60× — an alignment error of tens of degrees. The honest headline is the tolerance itself: for a short-flight, angle-plus-range homing intercept, the miss budget lives in the seeker and target track, not in a drifting INS — and the classic gyro-bias→mis-projected-gravity worry is doubly muted here, because gravity does not mis-project at all (the boost specific force is the real amplifier) and the homing geometry self-corrects for own-ship position. The INS is open-loop; a GPS/ZUPT aiding phase would bound its drift and is the natural follow-on.

Where does one fire unit break? (P7)

The final phase steps up from a single intercept to a single air-defence fire unit defending an asset against a raid. One battery, one seeker/EKF/guidance channel, firing one round at a time: it evaluates the live threats, engages the most imminent one (shortest time-to-asset), and — after each round resolves — reloads on a fire-control cadence and re-engages the next, until the raid is spent or a threat reaches the asset's keep-out sphere (a leaker). Sweeping the raid size against the cadence, 100 dispersed runs each, evasive attackers, full seeker + EKF chain:

Raid size Leak-through @ 1.0 s cadence Leak-through @ 3.0 s cadence
4 0% 0%
8 0% 0%
12 0% 22%
16 0% 42%
20 11% 53%

The result is a saturation knee, and the knee is set by fire-control channel time — not by how well the round flies. Below capacity the single channel defeats the whole raid; once the raid outpaces the rate at which one channel can service it (roughly the warning window divided by time-of-flight plus reload cadence), leak-through climbs sharply. Every engagement in the table hits — the miss distance stays sub-metre throughout — so the threats that get through are not missed, they are never engaged in time. That is why tripling the cadence (1.0 → 3.0 s) drags the knee from ~18 attackers down to ~10: shortening the reload buys more defended raid than a better guidance law would, because past the knee the bottleneck is the channel, not the shot. It is the reflective inversion of where P2–P6 spent their effort, and it is exactly the argument for layered, multi-unit defence — quantified honestly for one fire unit, without pretending to be an integrated air-defence system.

Running an engagement

# from the repo root, after building
./build/<preset>/src/apps/dmssb_sim scenarios/1v1_loiter_tpn.ini

# then turn the telemetry into figures (3D geometry + guidance time histories)
python -m pip install -r analysis/requirements.txt
python analysis/plot_engagement.py out/1v1_loiter_tpn \
    --compare out/1v1_loiter_pursuit     # overlay the pursuit baseline

# the full loop: noisy seeker -> EKF -> Augmented PN, against an evasive drone
./build/<preset>/src/apps/dmssb_sim scenarios/1v1_evasive_tracked_apn.ini
python analysis/plot_ekf.py out/1v1_evasive_tracked_apn   # track error vs its own 3-sigma, NIS

# the comparative study: 5 guidance laws x 2 motion models x 200 dispersed runs
./build/<preset>/src/apps/dmssb_mc scenarios/mc_law_study.ini
python analysis/plot_mc.py out/mc_law_study               # miss CDF, CEP, miss-vs-effort

# the P5 6DOF airframe: rigid body + aero deck + three-loop autopilot
./build/<preset>/src/apps/dmssb_sim scenarios/1v1_evasive_6dof.ini
python analysis/plot_6dof.py out/1v1_evasive_6dof         # alpha, fins, Mach, achieved-vs-commanded g
./build/<preset>/src/apps/dmssb_mc scenarios/mc_law_study_6dof.ini   # the airframe-fidelity study

# the P6 strapdown INS: the round no longer knows its own state perfectly
./build/<preset>/src/apps/dmssb_sim scenarios/1v1_ins_sixdof.ini
python analysis/plot_ins.py out/1v1_ins_sixdof            # INS position/velocity/attitude drift
./build/<preset>/src/apps/dmssb_mc scenarios/mc_ins_study.ini       # the navigation-error study

# the P7 zone defence: one fire unit against a raid of attacking drones
./build/<preset>/src/apps/dmssb_sim scenarios/zone_defense_raid.ini
./build/<preset>/src/apps/dmssb_mc scenarios/mc_raid_study.ini      # the saturation study
python analysis/plot_raid.py out/mc_raid_study            # leak-through vs raid size and cadence

# open up any single run of the campaign for a post-mortem, with full telemetry
./build/<preset>/src/apps/dmssb_mc scenarios/mc_law_study.ini \
    --record-run 100 --record-point 3 --out out/autopsy

A campaign's outcome depends only on the master seed, the run index and the sweep point — never on the thread count. Running it on one thread and on eight produces byte-identical results, and a test pins that.

Each run writes telemetry.csv, events.csv, engagements.csv, and run_meta.csv (seed + config hash) into its output directory — the only contract the analysis layer reads. Scenarios are plain INI files; see scenarios/.

Building

Requires a C++17 compiler and CMake ≥ 3.20. GoogleTest is fetched automatically at configure time (network access needed on the first configure).

# Linux
cmake --preset linux-gcc
cmake --build --preset linux-gcc
ctest --preset linux-gcc

# Windows (Visual Studio 2026 / MSVC)
cmake --preset windows-vs
cmake --build --preset windows-vs
ctest --preset windows-vs

Sanitizer build (Clang, AddressSanitizer + UndefinedBehaviorSanitizer):

cmake --preset linux-asan && cmake --build --preset linux-asan && ctest --preset linux-asan

Conventions (summary)

Full detail in docs/README.md.

  • World frame NED (altitude = -z); body frame FRD.
  • Hamilton quaternion, scalar-first (w, x, y, z), passive NED→body.
  • Fixed-step RK4; time is an integer tick count (t = tick · dt, dt ≈ 1 ms).
  • GNC subsystems run at their own rates via a scheduler; rates must divide the tick rate exactly.
  • All randomness comes from named, seeded streams — never a global RNG.

Repository layout

src/core/     zero-dependency foundation (math, RNG, config, CSV, shared types)
src/models/   vehicle dynamics (interceptor, quadrotor target + behaviors)
src/gnc/      guidance laws, seeker, EKF tracker, navigation, autopilot
src/sim/      scheduler, world/entity, engagement manager, telemetry, Monte Carlo
src/apps/     dmssb_sim (single run), dmssb_mc (batch)
tests/        GoogleTest suites mirroring src/
scenarios/    engagement configuration files (.ini)
analysis/     Python post-processing and figures
docs/         normative conventions

Roadmap

Phase Content
P0 ✓ Skeleton, CMake + presets, CI (build/test, sanitizers, format, tidy), conventions.
P1 ✓ Math core: Vec3, fixed-size Mat, Quat, RK4, seeded RNG, INI config, CSV writer — with analytic-reference tests.
P2 ✓ 3DOF engagement MVP: point-mass interceptor, loiter/patrol quadrotor targets, perfect navigation, pursuit / True PN / Augmented PN, 1v1 engagement, telemetry + plots.
P3 ✓ Sensors and estimation: seeker (az/el/range, FOV + blind range), EKF target tracker (CV / CA / Singer), ZEM guidance, reactive evasion, nav = perfect | tracked switch.
P4 ✓ Optimal (LQ) guidance; multithreaded Monte Carlo runner with dispersions, sweeps and bit-reproducible results; the guidance-law comparative study (kill probability, CEP, control effort).
P5 ✓ 6DOF interceptor: rigid-body dynamics, synthetic transonic aero deck (ISA atmosphere, time-varying mass/inertia), rate-limited fin actuators, gain-scheduled three-loop acceleration autopilot; airframe-fidelity study (does the LQ win survive?).
P6 ✓ Inertial navigation: strapdown IMU error model (bias, scale factor, misalignment, random walk) + mechanization; own-ship error propagated end-to-end into guidance and the target track; navigation-error budget study.
P7 ✓ 1-vs-N zone defence: a single fire unit defending an asset — threat prioritisation (time-to-asset), sequential shoot-look-shoot re-engagement through one GNC chain (reset per engagement), leak-through / raid-defeat metrics; the fire-unit saturation study.

License

MIT © 2026 Niccolò D'Ambrosio

About

C++17 interceptor-vs-drone GNC engagement simulator: proportional-navigation guidance, seeker/EKF navigation, and Monte Carlo analysis

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages