Skip to content

Examples and best practices

Tips and 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);
});

Named parameter examples

Many aircraft methods accept both positional arguments and a single object with named parameters. The named style is often clearer, especially when multiple options are involved.

hold with named params

javascript
// Hold at a fix with default options
aircraft.hold({ fix: 'MERIT' });

// Hold with custom leg length and left turns
aircraft.hold({ fix: 'GREKI', legNm: 8, turn: 'L' });

// Hold with expect further clearance time
aircraft.hold({ fix: 'JUDDS', efc: 3600 });

// Combine hold with other clearances
aircraft
  .descend(fl(240))
  .hold({ fix: 'MERIT', legNm: 6, turn: 'R' });

sta with named params

javascript
// Assign a scheduled time of arrival
aircraft.sta({ fix: 'MERGE', time: 1800 });

// Space arrivals 2 minutes apart
onSpawn((aircraft) => {
  const slot = nextAvailableSlot; // your sequencing logic
  aircraft.sta({ fix: 'MERGE', time: slot });
});

crossFix with named params

javascript
// Override a STAR constraint
aircraft.crossFix({ fix: 'GREKI', altFt: 20000 });

// Use flight level shorthand (values < 2000 are treated as FL)
aircraft.crossFix({ fix: 'GREKI', altFt: 200 }); // FL200

// Override multiple constraints
aircraft
  .crossFix({ fix: 'GREKI', altFt: 20000 })
  .crossFix({ fix: 'ROBER', altFt: 12000 });

over with named params

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

// Step-climb at a waypoint
aircraft.over({
  waypoint: 'KMART',
  callback: (ac) => ac.climb(fl(380))
});

// Chain multiple waypoint callbacks
aircraft
  .over({
    waypoint: 'GREKI',
    callback: (ac) => ac.descend(fl(240))
  })
  .over({
    waypoint: aircraft.plan.exitPoint,
    callback: (ac) => ac.handover()
  });

before with named params

javascript
// Begin descent 40nm before arrival fix
aircraft.before({
  waypoint: 'KMART',
  distanceNm: 40,
  callback: (ac) => ac.descend(fl(240))
});

// Reduce speed before a merge point
aircraft.before({
  waypoint: 'MERGE',
  distanceNm: 20,
  callback: (ac) => ac.speed(280)
});

// Set up a full arrival sequence on spawn
onSpawn((aircraft) => {
  aircraft
    .climb(fl(aircraft.plan.cruiseFL))
    .speed(420)
    .before({
      waypoint: aircraft.plan.exitPoint,
      distanceNm: 50,
      callback: (ac) => ac.descend(fl(ac.plan.exitFL))
    })
    .over({
      waypoint: aircraft.plan.exitPoint,
      callback: (ac) => ac.handover()
    });
});

direct with array

javascript
// Reroute through specific waypoints
aircraft.direct(['KMART', 'ROBUC', 'HIBER']);

// Shortcut direct to exit
aircraft.direct(aircraft.plan.exitPoint);