Skip to content

radarcontrol.io ATC - API Reference

Complete reference for radarcontrol.io's ATC API.

Sandbox

Your code runs in a sandboxed environment. Only the documented APIs below are available.

Table of Contents


Event Handlers

onTick(callback)

Called every simulation tick (~0.5 seconds by default).

Parameters:

  • callback: (ctx: APIContext) => void - Function called each frame

Context Object (ctx):

javascript
{
  time: number,        // Simulation time (seconds)
  dt: number,          // Time step (seconds, usually 0.5)
  traffic: TrafficView,
  weather: WeatherView,
  sectors: SectorsView,
  fixes: FixesView
}

Example:

javascript
onTick(({ traffic, time }) => {
  const flights = traffic.all();
  log(`Time: ${time}s, Aircraft: ${flights.length}`);

  flights.forEach(ac => {
    // Check altitude and issue climb clearance
    if (ac.altFL < 300) {
      ac.climb(fl(350)); // Climb to FL350
    }
  });
});

onSpawn(callback)

Called when a new aircraft enters your sector.

Parameters:

  • callback: (aircraft: Aircraft) => void

Example:

javascript
onSpawn((aircraft) => {
  log(`${aircraft.cs} (${aircraft.type}) at FL${aircraft.altFL}`);
  log(`Exit: ${aircraft.plan.exitPoint} at FL${aircraft.plan.exitFL}`);

  // Initial clearance
  aircraft.speed(400);

  // Climb to cruise altitude if needed
  if (aircraft.plan.cruiseFL > aircraft.altFL) {
    aircraft.climb(fl(aircraft.plan.cruiseFL));
  }

  // Set up handover at exit fix
  aircraft.over(aircraft.plan.exitPoint, (ac) => {
    ac.handover();
  });

  // Ensure aircraft reaches exit altitude before exit fix
  if (aircraft.plan.exitAltitude) {
    aircraft.reach(aircraft.plan.exitPoint).altitude(aircraft.plan.exitAltitude);
  }
});

onConflict(callback)

Called when two aircraft are predicted to lose separation (< 5nm lateral or < 1000ft vertical).

Parameters:

  • callback: (pair: ConflictEvent) => void

ConflictEvent Object:

javascript
{
  a: string,              // First aircraft callsign
  b: string,              // Second aircraft callsign
  predMinSepNm: number,   // Predicted minimum separation
  timeToCPA: number       // Time to closest point (seconds)
}

Example:

javascript
onConflict((pair) => {
  log(`Conflict: ${pair.a} and ${pair.b}`);
  log(`Min separation: ${pair.predMinSepNm.toFixed(1)}nm in ${pair.timeToCPA}s`);

  // Resolve with vertical separation
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (acA && acB) {
    const verticalSep = Math.abs(acA.altFt - acB.altFt);

    if (verticalSep < 1000) {
      // Climb the higher aircraft
      if (acA.altFt > acB.altFt) {
        acA.climb(acA.altFt + 2000);
      } else {
        acB.climb(acB.altFt + 2000);
      }
    }
  }
});

onMeterAlert(callback) Not Implemented

Called when demand is high at a metering fix.

Real-world usage: Traffic Management Initiatives (TMI) use metering to control arrival/departure rates and manage sector demand. When too many aircraft converge on a fix, controllers implement miles-in-trail restrictions, speed control, or reroutes to smooth the flow. This can apply to arrival fixes at busy airports, sector boundaries, or any congested waypoint.

Example:

javascript
onMeterAlert((fixName) => {
  log(`High demand at ${fixName} - implementing metering`);

  // Get all aircraft heading to this fix
  const inbound = traffic.all().filter(ac =>
    ac.plan.route.includes(fixName)
  );

  // Implement 10nm miles-in-trail restriction
  inbound.sort((a, b) => {
    const distA = distance(a.pos, fixes.get(fixName).pos);
    const distB = distance(b.pos, fixes.get(fixName).pos);
    return distA - distB;
  });

  inbound.forEach((ac, i) => {
    if (i > 0) {
      const lead = inbound[i - 1];
      const spacing = distance(ac.pos, lead.pos);

      if (spacing < 10) {
        // Reduce speed to achieve 10nm spacing
        ac.speed(Math.max(lead.targetIASKts - 40, 250));
      }
    }
  });
});

onHandoffRequest(callback) Not Implemented

Called when an aircraft needs handoff to next sector.

Real-world usage: Handoffs are coordinated between controllers, often with verbal or automated coordination. The receiving controller must accept the handoff before the transferring controller switches the aircraft to the new frequency. Controllers verify the aircraft meets all crossing restrictions and separation standards before handoff.

Example:

javascript
onHandoffRequest((req) => {
  log(`${req.cs} requesting handoff to ${req.nextSector}`);
  const ac = traffic.byCallsign(req.cs);

  if (!ac) return;

  // Check aircraft is stable and meets requirements
  const isStable = ac.altFt === ac.targetAltFt;
  const meetsAltitude = !ac.plan.exitFL || ac.altFL === ac.plan.exitFL;
  const atExitFix = ac.plan.exitPoint &&
    distance(ac.pos, fixes.get(ac.plan.exitPoint).pos) < 5;

  if (isStable && meetsAltitude && atExitFix) {
    ac.handover();
    log(`${ac.cs} handed to ${req.nextSector}`);
  } else {
    log(`${ac.cs} not ready - waiting for compliance`);

    // Ensure aircraft meets exit requirements
    if (!meetsAltitude && ac.plan.exitFL) {
      if (ac.altFL < ac.plan.exitFL) {
        ac.climb(fl(ac.plan.exitFL));
      } else {
        ac.descend(fl(ac.plan.exitFL));
      }
    }
  }
});

Aircraft Control API

Aircraft objects provide methods for issuing clearances. All methods return this for chaining.

aircraft.climb(altitude)

Issue climb clearance.

Real-world usage: Controllers use climb clearances to establish vertical separation, allow aircraft to reach more efficient cruise altitudes, or clear aircraft above weather. Standard phraseology: "Climb and maintain FL350."

Parameters:

  • altitude: number - Target altitude in feet OR use fl() helper for flight levels

Returns: this (for method chaining)

Examples:

javascript
// Using feet
aircraft.climb(35000);

// Using flight level helper
aircraft.climb(fl(350));  // FL350 = 35,000ft

// Climb to cruise altitude
aircraft.climb(fl(aircraft.plan.cruiseFL));

// Establish vertical separation in conflict
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);
  if (acA && acB && Math.abs(acA.altFt - acB.altFt) < 1000) {
    // Climb higher aircraft to establish 2000ft separation
    if (acA.altFt > acB.altFt) {
      acA.climb(acA.altFt + 2000);
    }
  }
});

// Method chaining
aircraft.climb(fl(350)).speed(420);

aircraft.descend(altitude)

Issue descent clearance.

Real-world usage: Controllers issue descent clearances to prepare aircraft for arrival, establish vertical separation below conflicting traffic, or comply with airspace restrictions. Standard phraseology: "Descend and maintain FL240."

Parameters:

  • altitude: number - Target altitude (feet or use fl() helper)

Returns: this (for method chaining)

Examples:

javascript
aircraft.descend(24000);
aircraft.descend(fl(240));  // FL240

// Descend to exit altitude for next sector
if (aircraft.plan.exitFL) {
  aircraft.descend(fl(aircraft.plan.exitFL));
}

// Descend below conflicting traffic
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);
  if (acA && acB) {
    // Descend lower aircraft below the conflict
    if (acA.altFt < acB.altFt) {
      acA.descend(acA.altFt - 2000);
    }
  }
});

aircraft.speed(speed)

Issue speed restriction.

Real-world usage: Speed control is one of the most important tools for maintaining separation. Controllers use speed adjustments to manage in-trail spacing, sequence arrivals, or slow aircraft before a turn. Standard phraseology: "Reduce speed to 320 knots" or "Maintain maximum forward speed."

Parameters:

  • speed: number - Target indicated airspeed in knots (180-450 typical range)

Returns: this (for method chaining)

Examples:

javascript
// Standard cruise speed
aircraft.speed(400);

// Maintain in-trail separation (common technique)
const lead = traffic.byCallsign("AAL123");
const trail = traffic.byCallsign("DAL456");

if (lead && trail) {
  const dist = distance(lead.pos, trail.pos);

  // Target 8-10nm spacing
  if (dist < 8) {
    // Slow trailing aircraft to open spacing
    trail.speed(Math.max(lead.targetIASKts - 30, 250));
  } else if (dist > 12) {
    // Speed up to close gap
    trail.speed(Math.min(lead.targetIASKts + 20, 450));
  }
}

// Speed restriction before waypoint (arrival sequencing)
aircraft.before("MERGE", 40, (ac) => {
  ac.speed(280); // Reduce to approach speed
});

aircraft.direct(...fixes)

Clear aircraft direct to waypoint(s), rerouting the flight plan.

Real-world usage: Direct routings are issued to shorten flight paths, expedite traffic flow, or provide separation through lateral spacing. This is one of the most common pilot requests and controller efficiency tools. Standard phraseology: "Proceed direct KMART" or "When able, direct ROBUC."

Parameters:

  • fixes: string[] - One or more waypoint names

Returns: this (for method chaining)

Behavior:

The direct() method intelligently modifies the aircraft's flight plan based on whether the waypoint(s) are in the current route:

  • Single waypoint in route: If the waypoint is already in the aircraft's route, truncates the route from that waypoint onward
    • Route [A, B, C, D]direct(C)[C, D]
  • Single waypoint not in route: Replaces entire route with just that waypoint
    • Route [A, B, C, D]direct(E)[E]
  • Multiple waypoints: Replaces entire route with specified waypoints
    • Route [A, B, C, D, E]direct(C, E)[C, E]
    • Route [A, B, C, D]direct(E, F)[E, F]

Examples:

javascript
// Direct to waypoint - truncates route from that point
aircraft.direct("KMART");

// Direct to exit fix - useful for shortcuts
aircraft.direct(aircraft.plan.exitPoint);

// Multiple waypoints - creates custom route
aircraft.direct("KMART", "ROBUC", "HIBER");

// Provide lateral separation via different routing
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (acA && acB) {
    // Route one aircraft to alternate fix for lateral separation
    const altFix = "ROBUC";
    if (distance(acA.pos, fixes.get(altFix).pos) < 30) {
      acA.direct(altFix);
      log(`${acA.cs} direct ${altFix} for separation`);
    }
  }
});

// Shortcut when aircraft is close to downstream fix
const targetFix = fixes.get("ROBUC");
if (targetFix) {
  const dist = distance(aircraft.pos, targetFix.pos);
  if (dist < 50) {
    aircraft.direct("ROBUC");
    log(`${aircraft.cs} cleared direct ROBUC`);
  }
}

// Send all aircraft direct to exit, bypassing intermediate fixes
onSpawn((aircraft) => {
  aircraft.direct(aircraft.plan.exitPoint);
});

Important Notes:

  • Clears any heading vector (returns aircraft to route following)
  • Resets active leg index to 0
  • The route displayed on the radar will update to show the new flight plan
  • The aircraft will fly the new route, with turn anticipation if enabled

aircraft.offset(offsetNm)

Apply lateral offset from route centerline.

Real-world usage: Strategic lateral offsets (SLO) are used in oceanic and high-altitude airspace to reduce the probability of collision, provide wake turbulence avoidance, and maintain separation on parallel tracks. Controllers may also use offsets to laterally separate aircraft on the same route. Standard phraseology: "Offset 2 miles right of course."

Parameters:

  • offsetNm: number - Offset in nautical miles (positive = right, negative = left)

Returns: this (for method chaining)

Examples:

javascript
// Offset right for lateral separation on same route
aircraft.offset(2);

// Offset left for wake turbulence avoidance
onSpawn((aircraft) => {
  const ahead = traffic.all().find(ac =>
    ac.wake === "H" && // Heavy aircraft ahead
    distance(ac.pos, aircraft.pos) < 10
  );

  if (ahead) {
    aircraft.offset(-2); // Offset to avoid wake
    log(`${aircraft.cs} offset for wake turbulence`);
  }
});

// Return to centerline when clear
aircraft.offset(0);

// Parallel routing for multiple aircraft on same airway
const flights = traffic.all()
  .filter(ac => ac.plan.route.includes("KMART"))
  .sort((a, b) => a.altFL - b.altFL);

flights.forEach((ac, i) => {
  ac.offset(i * 2); // 0, 2, 4, 6 nm lateral spacing
});

aircraft.handover()

Clear aircraft for handoff to next sector. Validates altitude is within exit fix constraints.

Real-world usage: Handoffs (or handovers) transfer control and communication of an aircraft between sectors or facilities. Controllers coordinate to ensure the receiving controller accepts the aircraft and that all altitude/routing requirements are met. Standard phraseology: "Contact [next sector] on [frequency]."

Returns: this (for method chaining)

Examples:

javascript
// Handover when aircraft reaches exit fix (standard practice)
onSpawn((aircraft) => {
  aircraft.over(aircraft.plan.exitPoint, (ac) => {
    // Ensure aircraft meets exit requirements
    if (ac.altFt === ac.targetAltFt) {
      ac.handover();
      log(`${ac.cs} handed to next sector at ${aircraft.plan.exitPoint}`);
    }
  });
});

// Ensure aircraft is stable before handoff
const ac = traffic.byCallsign("AAL123");
if (ac) {
  const stable = ac.altFt === ac.targetAltFt &&
                 ac.gsKts === ac.targetIASKts;

  if (stable && ac.plan.exitPoint) {
    const exitFix = fixes.get(ac.plan.exitPoint);
    if (exitFix && distance(ac.pos, exitFix.pos) < 5) {
      ac.handover();
    }
  }
}

aircraft.turn.right(degrees)

Turn aircraft right by specified degrees.

Real-world usage: Heading assignments (vectors) are used for separation, sequencing, weather avoidance, and intercepts. Controllers typically use 10-30 degree turns for small adjustments and up to 90 degrees for significant lateral separation. Standard phraseology: "Turn right heading 090" or "Turn right 30 degrees."

Parameters:

  • degrees: number - Degrees to turn right

Returns: Aircraft (for method chaining)

Examples:

javascript
// Small turn for lateral separation
aircraft.turn.right(20);

// Larger turn to avoid traffic
aircraft.turn.right(45);

// 90-degree turn for significant separation
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  if (acA) {
    acA.turn.right(90);
    log(`${acA.cs} turn right 90 for traffic`);
  }
});

// Turn and climb together
aircraft.turn.right(30).climb(fl(370));

aircraft.turn.left(degrees)

Turn aircraft left by specified degrees.

Real-world usage: Left turns follow the same principles as right turns - used for separation, sequencing, and traffic flow. Controllers consider wind, aircraft performance, and traffic when choosing turn direction. Standard phraseology: "Turn left heading 270" or "Turn left 20 degrees."

Parameters:

  • degrees: number - Degrees to turn left

Returns: Aircraft (for method chaining)

Examples:

javascript
// Small adjustment for spacing
aircraft.turn.left(15);

// Turn for separation
aircraft.turn.left(30);

// Downwind turn for sequencing
aircraft.turn.left(45).speed(280);

// Turn away from traffic
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (acA && acB) {
    // Turn aircraft in opposite directions
    acA.turn.left(30);
    acB.turn.right(30);
    log(`${acA.cs} and ${acB.cs} diverging turns for separation`);
  }
});

aircraft.turn.rightTo(heading)

Turn aircraft right to specified heading.

Real-world usage: When controllers need to specify both the turn direction and final heading, they use phraseology like "Turn right heading 090" to ensure the aircraft turns the intended direction (useful when the heading could be reached by either direction).

Parameters:

  • heading: number - Target heading in degrees (0-360)

Returns: Aircraft (for method chaining)

Examples:

javascript
// Turn right to heading 090
aircraft.turn.rightTo(90);

// Ensure right turn for traffic flow
aircraft.turn.rightTo(180);

// Method chaining
aircraft.turn.rightTo(270).climb(fl(370));

aircraft.turn.leftTo(heading)

Turn aircraft left to specified heading.

Real-world usage: Like rightTo, but explicitly specifies a left turn. Used when the turn direction matters for separation or traffic flow. Standard phraseology: "Turn left heading 270."

Parameters:

  • heading: number - Target heading in degrees (0-360)

Returns: Aircraft (for method chaining)

Examples:

javascript
// Turn left to heading 270
aircraft.turn.leftTo(270);

// Ensure left turn for separation
aircraft.turn.leftTo(180);

// Diverging turns with explicit directions
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (acA && acB) {
    acA.turn.leftTo(270);
    acB.turn.rightTo(090);
    log(`${acA.cs} and ${acB.cs} diverging on reciprocal headings`);
  }
});

aircraft.turn.to(heading)

Turn aircraft to specified heading (shortest turn).

Real-world usage: The most common heading assignment - aircraft takes the shortest turn to reach the heading. Standard phraseology: "Fly heading 090" or "Turn heading 270."

Parameters:

  • heading: number - Target heading in degrees (0-360)

Returns: Aircraft (for method chaining)

Examples:

javascript
// Fly heading 090 (shortest turn)
aircraft.turn.to(90);

// Vector for intercept
const fix = fixes.get("KMART");
if (fix) {
  const hdg = headingTo(aircraft.pos, fix.pos);
  aircraft.turn.to(hdg);
}

// Conflict resolution
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  if (acA) {
    // Turn 30 degrees left from current heading
    const newHdg = (acA.hdgDeg - 30 + 360) % 360;
    acA.turn.to(newHdg);
  }
});

// Method chaining
aircraft.turn.to(180).speed(420).climb(fl(350));

aircraft.over(waypoint, callback)

Register callback when aircraft passes over waypoint.

Parameters:

  • waypoint: string - Waypoint name
  • callback: (ac: Aircraft) => void - Function called when aircraft reaches waypoint

Returns: this (for method chaining)

Examples:

javascript
// Handover at exit fix
aircraft.over(aircraft.plan.exitPoint, (ac) => {
  ac.handover();
});

// Change altitude at waypoint
aircraft.over("KMART", (ac) => {
  ac.climb(fl(380));
});

// Log waypoint passage
aircraft.over("ROBUC", (ac) => {
  log(`${ac.cs} passed ROBUC at FL${ac.altFL}`);
});

aircraft.before(waypoint, distanceNm, callback)

Register callback before reaching waypoint.

Parameters:

  • waypoint: string - Waypoint name
  • distanceNm: number - Distance before waypoint (nm)
  • callback: (ac: Aircraft) => void - Function called when aircraft is within distance

Returns: this (for method chaining)

Examples:

javascript
// Start descent 30nm before waypoint
aircraft.before("KMART", 30, (ac) => {
  ac.descend(fl(240));
});

// Reduce speed before merge point
aircraft.before("MERGE", 20, (ac) => {
  ac.speed(320);
});

aircraft.reach(waypoint)

Build route with altitude/speed constraints at waypoint. Returns a ConstraintBuilder.

Returns: ConstraintBuilder (with .altitude() and .speed() methods)

Examples:

javascript
// Ensure aircraft reaches exit altitude before exit fix
aircraft.reach(aircraft.plan.exitPoint).altitude(aircraft.plan.exitAltitude);

// Altitude constraint at waypoint
aircraft.reach("KMART").altitude(fl(280));

// Speed constraint at waypoint
aircraft.reach("MERGE").speed(320);

// Both altitude and speed constraints (chaining)
aircraft
  .reach("KMART").altitude(fl(280))
  .reach("MERGE").speed(320);

ConstraintBuilder Methods:

.altitude(altFt)

Set altitude constraint - aircraft must reach altitude before waypoint.

Parameters:

  • altFt: number - Altitude in feet (or use fl() helper)

Returns: Aircraft (for method chaining)

.speed(kts)

Set speed constraint - aircraft must reach speed before waypoint.

Parameters:

  • kts: number - Speed in knots

Returns: Aircraft (for method chaining)


Traffic Management

Access via traffic object in onTick context.

traffic.all()

Get all aircraft in sector.

Returns: Aircraft[]

Example:

javascript
onTick(({ traffic }) => {
  const flights = traffic.all();
  log(`Managing ${flights.length} aircraft`);

  // Find highest aircraft
  const highest = flights.reduce((max, ac) =>
    ac.altFL > max.altFL ? ac : max
  );

  log(`Highest: ${highest.cs} at FL${highest.altFL}`);
});

traffic.byCallsign(callsign)

Get specific aircraft by callsign.

Returns: Aircraft | undefined

Example:

javascript
const ac = traffic.byCallsign("AAL123");
if (ac) {
  log(`${ac.cs} is at FL${ac.altFL}, ${ac.gsKts}kts`);

  // Check if climbing
  if (ac.altFL < ac.targetFL) {
    log(`Climbing to FL${ac.targetFL}`);
  }
}

Aircraft Object

Read-only Properties:

javascript
{
  cs: string,              // Callsign (e.g., "AAL123")
  type: string,            // Aircraft type ("A320", "B738", etc.)
  wake: "L"|"M"|"H"|"J",   // Wake turbulence category

  pos: {x, y},             // Position (nautical miles)
  hdgDeg: number,          // Heading 0-360
  gsKts: number,           // Ground speed (knots)

  altFt: number,           // Current altitude (feet)
  altFL: number,           // Current altitude (flight level)
  vsFpm: number,           // Vertical speed (feet per minute)

  plan: {
    route: string[],       // Waypoint names
    cruiseAltFt: number,   // Planned cruise altitude (feet)
    cruiseFL: number,      // Planned cruise altitude (flight level)
    exitPoint: string,     // Last waypoint (exit)
    exitAltitude?: number, // Required altitude at exit (feet)
    exitFL?: number        // Required altitude at exit (flight level)
  },

  activeLegIdx: number,    // Current leg in route
  targetAltFt: number,     // Cleared altitude (feet)
  targetFL: number,        // Cleared altitude (flight level)
  targetIASKts: number,    // Cleared airspeed
  targetHdgDeg?: number,   // Cleared heading (if vectoring)
  directTo?: string,       // Direct-to override
  offsetNm: number,        // Lateral offset from route
  sectorId: string         // Current sector
}

Key Features:

  • Flight level properties: altFL, targetFL, plan.cruiseFL, plan.exitFL
  • Exit constraints: plan.exitPoint, plan.exitAltitude, plan.exitFL
  • Aircraft objects have control methods: climb(), speed(), direct(), etc.

Weather API

Access via weather object in onTick context.

weather.windAt(altitude)

Get wind at specified altitude.

Parameters:

  • altitude: number - Altitude in feet

Returns: {dirDeg: number, kts: number}

Example:

javascript
onTick(({ weather, traffic }) => {
  const wind35000 = weather.windAt(35000);
  log(`Wind at FL350: ${wind35000.dirDeg}° at ${wind35000.kts}kts`);

  // Adjust for headwind/tailwind
  traffic.all().forEach(ac => {
    const wind = weather.windAt(ac.altFt);
    // Wind affects ground speed calculation
  });
});

Sectors API

Access via sectors object in onTick context.

sectors.currentOf(callsign)

Get current sector of aircraft.

Returns: string | undefined

Example:

javascript
onTick(({ sectors, traffic }) => {
  traffic.all().forEach(ac => {
    const sector = sectors.currentOf(ac.cs);
    log(`${ac.cs} in sector ${sector}`);
  });
});

Fixes API

Access via fixes object in onTick context.

fixes.get(fixName)

Get waypoint by name.

Returns: {name: string, pos: {x, y}} | undefined

Example:

javascript
onTick(({ fixes, traffic }) => {
  const kmart = fixes.get("KMART");
  if (kmart) {
    log(`KMART at ${kmart.pos.x.toFixed(1)}, ${kmart.pos.y.toFixed(1)}`);

    // Calculate distance from aircraft
    const ac = traffic.byCallsign("AAL123");
    if (ac) {
      const dist = distance(ac.pos, kmart.pos);
      log(`Distance to KMART: ${dist.toFixed(1)}nm`);
    }
  }
});

Data Types

Vec2

Position in nautical miles.

javascript
{
  x: number,  // East-west (positive = east)
  y: number   // North-south (positive = north)
}

WakeCat

Wake turbulence category.

javascript
"L" | "M" | "H" | "J"
  • L: Light (< 15,500 lbs)
  • M: Medium (15,500 - 300,000 lbs)
  • H: Heavy (> 300,000 lbs)
  • J: Super (A380, etc.)

Utility Functions

distance(a, b)

Calculate distance between two positions.

Parameters:

  • a: Vec2 - First position
  • b: Vec2 - Second position

Returns: number - Distance in nautical miles

Example:

javascript
const ac1 = traffic.byCallsign("AAL123");
const ac2 = traffic.byCallsign("DAL456");

if (ac1 && ac2) {
  const dist = distance(ac1.pos, ac2.pos);
  log(`Separation: ${dist.toFixed(1)}nm`);

  if (dist < 5) {
    log("Losing lateral separation!");
  }
}

headingTo(from, to)

Calculate heading from one position to another.

Parameters:

  • from: Vec2 - Starting position
  • to: Vec2 - Target position

Returns: number - Heading in degrees (0-360)

Example:

javascript
const ac = traffic.byCallsign("AAL123");
const fix = fixes.get("KMART");

if (ac && fix) {
  const hdg = headingTo(ac.pos, fix.pos);
  log(`Heading to KMART: ${hdg.toFixed(0)}°`);

  // Check if aircraft is on course
  const hdgDiff = Math.abs(ac.hdgDeg - hdg);
  if (hdgDiff > 10) {
    log(`${ac.cs} is ${hdgDiff.toFixed(0)}° off course`);
  }
}

flightlevel(fl) / fl(fl)

Convert flight level to feet.

Parameters:

  • fl: number - Flight level (e.g., 350 for FL350)

Returns: number - Altitude in feet

Example:

javascript
// These are equivalent:
aircraft.climb(flightlevel(350));
aircraft.climb(fl(350));  // Shorter alias
aircraft.climb(35000);    // Direct feet

// Useful for calculations
const targetFL = 350;
aircraft.climb(fl(targetFL));  // 35,000 feet

Activity Panel Functions

log(message)

Output messages to the activity panel.

Parameters:

  • message: string - Message to display

Examples:

javascript
log("Normal message");
log(`Aircraft ${ac.cs} at FL${ac.altFL}`);
log(`Warning: Conflict detected`);
log(`Separation: ${dist.toFixed(1)}nm`);

Tips & Best Practices

Separation Management

javascript
// Check both lateral AND vertical separation
const isSeparated = (a, b) => {
  const lateral = distance(a.pos, b.pos);
  const vertical = Math.abs(a.altFt - b.altFt);
  return lateral >= 5 || vertical >= 1000;
};

Efficient Querying

javascript
// Cache frequently used values
onTick(({ traffic }) => {
  const flights = traffic.all(); // Call once, reuse

  flights.forEach(ac => {
    // Process aircraft
  });
});

Method Chaining

javascript
// Issue multiple clearances together
onSpawn((ac) => {
  ac
    .climb(fl(ac.plan.cruiseFL))
    .speed(400)
    .direct(ac.plan.exitPoint);
});

Error Handling

javascript
onConflict((pair) => {
  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (!acA || !acB) {
    log(`Missing aircraft in conflict: ${pair.a}, ${pair.b}`);
    return;
  }

  // Resolve conflict...
});

Using Flight Levels

javascript
// Use FL properties directly
log(`Aircraft at FL${ac.altFL}`);
log(`Target: FL${ac.targetFL}`);
log(`Cruise: FL${ac.plan.cruiseFL}`);

// Use fl() helper for commands
ac.climb(fl(350));  // Much clearer than 35000

Complete Example Script

javascript
// Basic traffic management

onTick(({ traffic, time }) => {
  const flights = traffic.all();

  // Maintain spacing with speed control
  for (let i = 0; i < flights.length - 1; i++) {
    const lead = flights[i];
    const trail = flights[i + 1];

    const dist = distance(trail.pos, lead.pos);

    if (dist < 8) {
      // Too close - slow down trailing aircraft
      trail.speed(Math.max(lead.targetIASKts - 30, 320));
    } else if (dist > 12) {
      // Too far - speed up trailing aircraft
      trail.speed(Math.min(trail.targetIASKts + 20, 450));
    }
  }

  // Ensure vertical separation if lateral is tight
  for (let i = 0; i < flights.length - 1; i++) {
    const a = flights[i];
    const b = flights[i + 1];

    const lateral = distance(a.pos, b.pos);
    const vertical = Math.abs(a.altFt - b.altFt);

    if (lateral < 5 && vertical < 1000) {
      a.climb(a.altFt + 2000);
    }
  }
});

onConflict((pair) => {
  log(`Conflict: ${pair.a} and ${pair.b}`);

  const acA = traffic.byCallsign(pair.a);
  const acB = traffic.byCallsign(pair.b);

  if (!acA || !acB) return;

  // Resolve with vertical separation
  const verticalSep = Math.abs(acA.altFt - acB.altFt);
  if (verticalSep < 1000) {
    if (acA.altFt > acB.altFt) {
      acA.climb(acA.altFt + 2000);
    } else {
      acB.climb(acB.altFt + 2000);
    }
  }
});

onSpawn((aircraft) => {
  log(`${aircraft.cs} (${aircraft.type}) at FL${aircraft.altFL}`);
  log(`Route: ${aircraft.plan.route.join('-')}`);
  log(`Exit: ${aircraft.plan.exitPoint} at FL${aircraft.plan.exitFL}`);

  // Set up handover at exit fix
  aircraft.over(aircraft.plan.exitPoint, (ac) => {
    ac.handover();
  });

  // Ensure aircraft reaches exit altitude
  if (aircraft.plan.exitAltitude) {
    aircraft.reach(aircraft.plan.exitPoint).altitude(aircraft.plan.exitAltitude);
  }

  // Initial clearances
  aircraft
    .climb(fl(aircraft.plan.cruiseFL))
    .speed(420);
});