RIM-116 & Phalanx
Radar warning receiver
Three warning stages, derived from what the radar actually does. The search ping costs no network traffic at all – a whole run-in is a handful of messages.
Three stages
| Stage | What the pilot hears | Where it comes from |
|---|---|---|
| SEARCH | One ping per dish revolution, louder the closer the radar | Computed on the client from the shared bearing formula |
| LOCK | Steady tone while a mount holds the track | One server message, refreshed periodically |
| LAUNCH | Loud two-tone alarm | One server message |
The ping interval is constant, because it comes from the revolution and not from the range. Closer means louder, not faster.
Compute the ping, do not poll for it
Instead of repeatedly asking “is the beam on me now”, the client works out when it will be. That is not only cheaper, it is the only thing that works: a beam a few degrees wide stands on any one point for a few tens of milliseconds. Anything polling at a quarter of a second misses passes regularly.
float MyAD_Rwr_SecondsUntilBeam(MyAD_Radar radar, vector me)
{
vector to = me - radar.GetOwner().GetOrigin();
// Atan2(x, z): zero points to +Z, increasing towards +X
float toMe = SCR_Math.fmod(Math.Atan2(to[0], to[2]) * Math.RAD2DEG, 360.0);
float rest = SCR_Math.fmod(toMe - radar.Bearing(), 360.0);
// already inside the beam: the pass is NOW, not one full turn away
if (rest > 360.0 - radar.BeamWidth() * 0.5)
return 0;
return rest / radar.DegreesPerSecond();
}
Refresh interval versus hold time
The server refreshes the lock now and then, and the receiver keeps it alive for a while without a refresh. The refresh interval must stay below the hold time, or the tone lapses between two perfectly healthy messages.
Two sound handles, not one switched channel
Sharing one handle between the lock tone and the launch alarm means the lock ends when the alarm starts – the opposite of what a pilot needs. While the lock tone is still there, another launch can be expected. Give each its own handle and let them mix.
void MyAD_Rwr_UpdateTones()
{
bool wantLock = m_State != MyAD_ERwr.OFF;
bool wantAlarm = m_State == MyAD_ERwr.LAUNCH;
Sync(wantLock, m_hLock, "LOCK");
Sync(wantAlarm, m_hAlarm, "ALARM"); // independent of the lock tone
}
void Sync(bool want, out AudioHandle handle, string event)
{
if (want && handle == AudioHandle.Invalid)
handle = PlayEvent(event);
else if (!want && handle != AudioHandle.Invalid)
{
StopEvent(handle);
handle = AudioHandle.Invalid;
}
}
Volume 0. Changing the amplitude config of one event and not the others silently reorders the whole mix – a lock tone once ended up quieter than the pings it was meant to sit under.onRplName does not fire on the authority. A method that reacts to a replicated field has to be called on both paths, or the host behaves differently from every client – and on a listen server that is invisible until someone else joins.