+ if(argc > 0 && naIsString(args[0])) {
+ if( *id != 0 ) {
+ naRuntimeError(c, "navinfo() called with navaid id");
+ return naNil();
+ }
+ id = naStr_data(args[0]);
+ ++args;
+ --argc;
+ }
+
+ if( argc > 0 ) {
+ naRuntimeError(c, "navinfo() called with too many arguments");
+ return naNil();
+ }
+
+ FGNavList::TypeFilter filter(type);
+ navlist = FGNavList::findByIdentAndFreq( pos, id, 0.0, &filter );
+
+ naRef reply = naNewVector(c);
+ for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
+ naVec_append( reply, ghostForNavaid(c, *it) );
+ }
+ return reply;
+}
+
+static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsWithinRange expected range (in nm) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double rangeNm = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ naRef r = naNewVector(c);
+ FGNavList::TypeFilter filter(type);
+ FGPositioned::List navs = FGPositioned::findWithinRange(pos, rangeNm, &filter);
+ FGPositioned::sortByRange(navs, pos);
+
+ BOOST_FOREACH(FGPositionedRef a, navs) {
+ FGNavRecord* nav = (FGNavRecord*) a.get();
+ naVec_append(r, ghostForNavaid(c, nav));
+ }
+
+ return r;
+}
+
+static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double freqMhz = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ FGNavList::TypeFilter filter(type);
+ nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
+ if (navs.empty()) {
+ return naNil();
+ }
+
+ return ghostForNavaid(c, navs.front().ptr());
+}
+
+static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double freqMhz = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ naRef r = naNewVector(c);
+
+ FGNavList::TypeFilter filter(type);
+ nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
+
+ BOOST_FOREACH(nav_rec_ptr a, navs) {
+ naVec_append(r, ghostForNavaid(c, a.ptr()));
+ }
+
+ return r;
+}
+
+static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsString(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsByIdent expectes ident string as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ string ident = naStr_data(args[argOffset++]);
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ FGNavList::TypeFilter filter(type);
+ naRef r = naNewVector(c);
+ nav_list_type navs = FGNavList::findByIdentAndFreq(pos, ident, 0.0, &filter);
+
+ BOOST_FOREACH(nav_rec_ptr a, navs) {
+ naVec_append(r, ghostForNavaid(c, a.ptr()));
+ }
+
+ return r;
+}
+
+static naRef f_findFixesByIdent(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsString(args[argOffset])) {
+ naRuntimeError(c, "findFixesByIdent expectes ident string as arg %d", argOffset);
+ }
+
+ string ident(naStr_data(args[argOffset]));
+ naRef r = naNewVector(c);
+
+ FGPositioned::TypeFilter filter(FGPositioned::FIX);
+ FGPositioned::List fixes = FGPositioned::findAllWithIdent(ident, &filter);
+ FGPositioned::sortByRange(fixes, pos);
+
+ BOOST_FOREACH(FGPositionedRef f, fixes) {
+ naVec_append(r, ghostForFix(c, (FGFix*) f.ptr()));
+ }
+
+ return r;
+}
+
+// Convert a cartesian point to a geodetic lat/lon/altitude.
+static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
+{
+ SGGeod pos = globals->get_aircraft_position();
+ if (argc == 0) {
+ // fine, use aircraft position
+ } else if (geodFromArgs(args, 0, argc, pos)) {
+ // okay
+ } else {
+ naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
+ }
+
+ double jd = globals->get_time_params()->getJD();
+ double magvarDeg = sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
+ return naNum(magvarDeg);
+}
+
+static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
+{
+ SGGeod from = globals->get_aircraft_position(), to, p;
+ int argOffset = geodFromArgs(args, 0, argc, p);
+ if (geodFromArgs(args, argOffset, argc, to)) {
+ from = p; // we parsed both FROM and TO args, so first was from
+ } else {
+ to = p; // only parsed one arg, so FROM is current
+ }
+
+ if (argOffset == 0) {
+ naRuntimeError(c, "invalid arguments to courseAndDistance");
+ }
+
+ double course, course2, d;
+ SGGeodesy::inverse(from, to, course, course2, d);
+
+ naRef result = naNewVector(c);
+ naVec_append(result, naNum(course));
+ naVec_append(result, naNum(d * SG_METER_TO_NM));
+ return result;
+}
+
+static naRef f_greatCircleMove(naContext c, naRef me, int argc, naRef* args)
+{
+ SGGeod from = globals->get_aircraft_position(), to;
+ int argOffset = 0;
+
+ // complication - don't inerpret two doubles (as the only args)
+ // as a lat,lon pair - only do so if we have at least three args.
+ if (argc > 2) {
+ argOffset = geodFromArgs(args, 0, argc, from);
+ }
+
+ if ((argOffset + 1) >= argc) {
+ naRuntimeError(c, "isufficent arguments to greatCircleMove");
+ }
+
+ if (!naIsNum(args[argOffset]) || !naIsNum(args[argOffset+1])) {
+ naRuntimeError(c, "invalid arguments %d and %d to greatCircleMove",
+ argOffset, argOffset + 1);
+ }
+
+ double course = args[argOffset].num, course2;
+ double distanceNm = args[argOffset + 1].num;
+ SGGeodesy::direct(from, course, distanceNm * SG_NM_TO_METER, to, course2);
+
+ // return geo.Coord
+ naRef coord = naNewHash(c);
+ hashset(c, coord, "lat", naNum(to.getLatitudeDeg()));
+ hashset(c, coord, "lon", naNum(to.getLongitudeDeg()));
+ return coord;
+}
+
+static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
+{
+ SGGeod pos = globals->get_aircraft_position();
+ geodFromArgs(args, 0, argc, pos);
+ SGBucket b(pos);
+ return stringToNasal(c, b.gen_base_path());
+}
+
+static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
+{
+ SGGeod pos = globals->get_aircraft_position();
+ geodFromArgs(args, 0, argc, pos);
+ SGBucket b(pos);
+ return naNum(b.gen_index());
+}
+
+static naRef f_route(naContext c, naRef me, int argc, naRef* args)
+{
+ if (argc == 0) {
+ FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
+ return ghostForFlightPlan(c, rm->flightPlan());
+ }
+
+ if ((argc > 0) && naIsString(args[0])) {
+ flightgear::FlightPlan* fp = new flightgear::FlightPlan;
+ SGPath path(naStr_data(args[0]));
+ if (!path.exists()) {
+ naRuntimeError(c, "flightplan, no file at path %s", path.c_str());
+ }
+
+ if (!fp->load(path)) {
+ SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path);
+ delete fp;
+ return naNil();
+ }
+
+ return ghostForFlightPlan(c, fp);
+ }
+
+ naRuntimeError(c, "bad arguments to flightplan()");
+ return naNil();
+}
+
+class NasalFPDelegate : public FlightPlan::Delegate
+{
+public:
+ NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) :
+ _nasal(sys),
+ _plan(fp),
+ _instance(ins)
+ {
+ SG_LOG(SG_NASAL, SG_INFO, "created Nasal delegate for " << fp);
+ _gcSaveKey = _nasal->gcSave(ins);
+ }
+
+ virtual ~NasalFPDelegate()
+ {
+ SG_LOG(SG_NASAL, SG_INFO, "destroying Nasal delegate for " << _plan);
+ _nasal->gcRelease(_gcSaveKey);
+ }
+
+ virtual void departureChanged()
+ {
+ callDelegateMethod("departureChanged");
+ }
+
+ virtual void arrivalChanged()
+ {
+ callDelegateMethod("arrivalChanged");
+ }
+
+ virtual void waypointsChanged()
+ {
+ callDelegateMethod("waypointsChanged");
+ }
+
+ virtual void currentWaypointChanged()
+ {
+ callDelegateMethod("currentWaypointChanged");
+ }
+private:
+
+ void callDelegateMethod(const char* method)
+ {
+ naRef f;
+ naMember_cget(_nasal->context(), _instance, method, &f);
+ if (naIsNil(f)) {
+ return; // no method on the delegate
+ }
+
+ naRef arg[1];
+ arg[0] = ghostForFlightPlan(_nasal->context(), _plan);
+ _nasal->callMethod(f, _instance, 1, arg, naNil());
+ }
+
+ FGNasalSys* _nasal;
+ FlightPlan* _plan;
+ naRef _instance;
+ int _gcSaveKey;
+};
+
+class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
+{
+public:
+ NasalFPDelegateFactory(naRef code)
+ {
+ _nasal = (FGNasalSys*) globals->get_subsystem("nasal");
+ _func = code;
+ _gcSaveKey = _nasal->gcSave(_func);
+ }
+
+ ~NasalFPDelegateFactory()
+ {
+ _nasal->gcRelease(_gcSaveKey);
+ }
+
+ virtual FlightPlan::Delegate* createFlightPlanDelegate(FlightPlan* fp)
+ {
+ naRef args[1];
+ args[0] = ghostForFlightPlan(_nasal->context(), fp);
+ naRef instance = _nasal->call(_func, 1, args, naNil());
+ if (naIsNil(instance)) {
+ return NULL;