#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>
+#include <simgear/misc/ListDiff.hxx>
#include <simgear/nasal/cppbind/from_nasal.hxx>
#include <simgear/nasal/cppbind/to_nasal.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <Airports/airport.hxx>
#include <Airports/dynamics.hxx>
+#include <Airports/pavement.hxx>
#include <ATC/CommStation.hxx>
#include <Main/globals.hxx>
#include <Navaids/NavDataCache.hxx>
+#include <Navaids/navlist.hxx>
+#include <Navaids/navrecord.hxx>
+#include <Navaids/fix.hxx>
typedef nasal::Ghost<FGPositionedRef> NasalPositioned;
typedef nasal::Ghost<FGRunwayRef> NasalRunway;
typedef nasal::Ghost<FGParkingRef> NasalParking;
typedef nasal::Ghost<FGAirportRef> NasalAirport;
typedef nasal::Ghost<flightgear::CommStationRef> NasalCommStation;
-
-//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, FGPositioned* positioned)
-{
- return NasalPositioned::create(c, positioned);
-}
-
-//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, FGPavement* rwy)
-{
- return NasalPositioned::create(c, (FGPositioned*)rwy);
-}
-
-//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, FGRunwayBase* rwy)
-{
- return NasalPositioned::create(c, (FGPositioned*)rwy);
-}
-
-//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, FGParking* parking)
-{
- return NasalParking::create(c, parking);
-}
+typedef nasal::Ghost<FGNavRecordRef> NasalNavRecord;
+typedef nasal::Ghost<FGRunwayRef> NasalRunway;
+typedef nasal::Ghost<FGFixRef> NasalFix;
//------------------------------------------------------------------------------
naRef to_nasal_helper(naContext c, flightgear::SID* sid)
}
//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, FGAirport* apt)
+static naRef f_navaid_course(FGNavRecord& nav, naContext)
{
- return NasalAirport::create(c, apt);
-}
+ if( !( nav.type() == FGPositioned::ILS
+ || nav.type() == FGPositioned::LOC
+ ) )
+ return naNil();
-//------------------------------------------------------------------------------
-naRef to_nasal_helper(naContext c, const SGGeod& pos)
-{
- nasal::Hash hash(c);
- hash.set("lat", pos.getLatitudeDeg());
- hash.set("lon", pos.getLongitudeDeg());
- hash.set("elevation", pos.getElevationM());
- return hash.get_naRef();
+ double radial = nav.get_multiuse();
+ return naNum(SGMiscd::normalizePeriodic(0.5, 360.5, radial));
}
//------------------------------------------------------------------------------
-static FGRunwayBase* f_airport_runway(FGAirport& apt, std::string ident)
+static FGRunwayBaseRef f_airport_runway(FGAirport& apt, std::string ident)
{
boost::to_upper(ident);
//------------------------------------------------------------------------------
template<class T, class C1, class C2>
-std::vector<T> extract( const std::vector<C1*>& in,
+std::vector<T> extract( const std::vector<C1>& in,
T (C2::*getter)() const )
{
std::vector<T> ret(in.size());
}
//------------------------------------------------------------------------------
-FGRunway* runwayFromNasalArg( const FGAirport& apt,
+FGRunwayRef runwayFromNasalArg( const FGAirport& apt,
const nasal::CallContext& ctx,
size_t index = 0 )
{
if( index >= ctx.argc )
- return NULL;
+ return FGRunwayRef();
try
{
{
if( !apt.hasRunwayWithIdent(ident) )
// TODO warning/exception?
- return NULL;
+ return FGRunwayRef();
return apt.getRunwayByIdent(ident);
}
{}
// TODO warn/error if no runway?
- return NasalRunway::fromNasal(ctx.c, ctx.args[index]);
+ return ctx.from_nasal<FGRunwayRef>(ctx.args[index]);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static FGParkingList
-f_airport_parking(FGAirport& apt, const nasal::CallContext& ctx)
+f_airport_parking(FGAirport& apt, nasal::CallContext ctx)
{
std::string type = ctx.getArg<std::string>(0);
bool only_available = ctx.getArg<bool>(1);
return ret;
}
+/**
+ * Extract a SGGeod from a nasal function argument list.
+ *
+ * <lat>, <lon>
+ * {"lat": <lat-deg>, "lon": <lon-deg>}
+ * geo.Coord.new() (aka. {"_lat": <lat-rad>, "_lon": <lon-rad>})
+ */
+static bool extractGeod(nasal::CallContext& ctx, SGGeod& result)
+{
+ if( !ctx.argc )
+ return false;
+
+ if( ctx.isGhost(0) )
+ {
+ FGPositionedRef pos =
+ ctx.from_nasal<FGPositionedRef>(ctx.requireArg<naRef>(0));
+
+ if( pos )
+ {
+ result = pos->geod();
+ ctx.popFront();
+ return true;
+ }
+ }
+ else if( ctx.isHash(0) )
+ {
+ nasal::Hash pos_hash = ctx.requireArg<nasal::Hash>(0);
+
+ // check for manual latitude / longitude names
+ naRef lat = pos_hash.get("lat"),
+ lon = pos_hash.get("lon");
+ if( naIsNum(lat) && naIsNum(lon) )
+ {
+ result = SGGeod::fromDeg( ctx.from_nasal<double>(lon),
+ ctx.from_nasal<double>(lat) );
+ ctx.popFront();
+ return true;
+ }
+
+ // geo.Coord uses _lat/_lon in radians
+ // TODO should we check if its really a geo.Coord?
+ lat = pos_hash.get("_lat");
+ lon = pos_hash.get("_lon");
+ if( naIsNum(lat) && naIsNum(lon) )
+ {
+ result = SGGeod::fromRad( ctx.from_nasal<double>(lon),
+ ctx.from_nasal<double>(lat) );
+ ctx.popFront();
+ return true;
+ }
+ }
+ else if( ctx.isNumeric(0) && ctx.isNumeric(1) )
+ {
+ // lat, lon
+ result = SGGeod::fromDeg( ctx.requireArg<double>(1),
+ ctx.requireArg<double>(0) );
+ ctx.popFront(2);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Extract position from ctx or return current aircraft position if not given.
+ */
+static SGGeod getPosition(nasal::CallContext& ctx)
+{
+ SGGeod pos;
+ if( !extractGeod(ctx, pos) )
+ pos = globals->get_aircraft_position();
+
+ return pos;
+}
+
//------------------------------------------------------------------------------
// Returns Nasal ghost for particular or nearest airport of a <type>, or nil
// on error.
// airportinfo(<type>); type := ("airport"|"seaport"|"heliport")
// airportinfo() same as airportinfo("airport")
// airportinfo(<lat>, <lon> [, <type>]);
-static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
+static naRef f_airportinfo(nasal::CallContext ctx)
{
- nasal::CallContext ctx(c, argc, args);
- // TODO think of something comfortable to overload functions or use variable
- // number/types of arguments.
+ SGGeod pos = getPosition(ctx);
- std::string ident = "airport";
- SGGeod pos = globals->get_aircraft_position();
+ if( ctx.argc > 1 )
+ naRuntimeError(ctx.c, "airportinfo() with invalid function arguments");
- if( ctx.argc == 1 )
- {
+ // optional type/ident
+ std::string ident("airport");
+ if( ctx.isString(0) )
ident = ctx.requireArg<std::string>(0);
- }
- else if( ctx.argc >= 2 )
- {
- // Why are lat/lon swapped?
- pos = SGGeod::fromDeg( ctx.requireArg<double>(1),
- ctx.requireArg<double>(0) );
-
- if( ctx.argc >= 3 )
- ident = ctx.requireArg<std::string>(2);
-
- if( ctx.argc > 3 )
- naRuntimeError(ctx.c, "airportinfo() with invalid function arguments");
- }
FGAirport::TypeRunwayFilter filter;
if( !filter.fromTypeString(ident) )
return ctx.to_nasal( FGAirport::findClosest(pos, maxRange, &filter) );
}
+/**
+ * findAirportsWithinRange([<position>,] <range-nm> [, type])
+ */
+static naRef f_findAirportsWithinRange(nasal::CallContext ctx)
+{
+ SGGeod pos = getPosition(ctx);
+ double range_nm = ctx.requireArg<double>(0);
+
+ FGAirport::TypeRunwayFilter filter; // defaults to airports only
+ filter.fromTypeString( ctx.getArg<std::string>(1) );
+
+ FGPositionedList apts = FGPositioned::findWithinRange(pos, range_nm, &filter);
+ FGPositioned::sortByRange(apts, pos);
+
+ return ctx.to_nasal(apts);
+}
+
+/**
+ * findAirportsByICAO(<ident/prefix> [, type])
+ */
+static naRef f_findAirportsByICAO(nasal::CallContext ctx)
+{
+ std::string prefix = ctx.requireArg<std::string>(0);
+
+ FGAirport::TypeRunwayFilter filter; // defaults to airports only
+ filter.fromTypeString( ctx.getArg<std::string>(1) );
+
+ return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, false) );
+}
+
+// Returns vector of data hash for navaid of a <type>, nil on error
+// navaids sorted by ascending distance
+// navinfo([<lat>,<lon>],[<type>],[<id>])
+// lat/lon (numeric): use latitude/longitude instead of ac position
+// type: ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
+// id: (partial) id of the fix
+// examples:
+// navinfo("vor") returns all vors
+// navinfo("HAM") return all navaids who's name start with "HAM"
+// navinfo("vor", "HAM") return all vor who's name start with "HAM"
+//navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM"
+// sorted by distance relative to lat=34, lon=48
+static naRef f_navinfo(nasal::CallContext ctx)
+{
+ SGGeod pos = getPosition(ctx);
+ std::string id = ctx.getArg<std::string>(0);
+
+ FGNavList::TypeFilter filter;
+ if( filter.fromTypeString(id) )
+ id = ctx.getArg<std::string>(1);
+ else if( ctx.argc > 1 )
+ naRuntimeError(ctx.c, "navinfo() already got an ident");
+
+ return ctx.to_nasal( FGNavList::findByIdentAndFreq(pos, id, 0.0, &filter) );
+}
+
//------------------------------------------------------------------------------
-naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c, naRef gcSave)
+static naRef f_findWithinRange(nasal::CallContext ctx)
+{
+ SGGeod pos = getPosition(ctx);
+ double range_nm = ctx.requireArg<double>(0);
+
+ std::string typeSpec = ctx.getArg<std::string>(1);
+ FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
+
+ FGPositionedList items = FGPositioned::findWithinRange(pos, range_nm, &filter);
+ FGPositioned::sortByRange(items, pos);
+ return ctx.to_nasal(items);
+}
+
+static naRef f_findByIdent(nasal::CallContext ctx)
{
- NasalPositioned::init("FGPositioned")
+ std::string prefix = ctx.requireArg<std::string>(0);
+ std::string typeSpec = ctx.getArg<std::string>(1);
+ FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
+ bool exact = ctx.getArg<bool>(2, false);
+
+ return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, exact) );
+}
+
+static naRef f_findByName(nasal::CallContext ctx)
+{
+ std::string prefix = ctx.requireArg<std::string>(0);
+ std::string typeSpec = ctx.getArg<std::string>(1);
+ FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
+
+ return ctx.to_nasal( FGPositioned::findAllWithName(prefix, &filter, false) );
+}
+
+//------------------------------------------------------------------------------
+
+static naRef f_courseAndDistance(nasal::CallContext ctx)
+{
+ SGGeod from = globals->get_aircraft_position(), to, pos;
+ bool ok = extractGeod(ctx, pos);
+ if (!ok) {
+ naRuntimeError(ctx.c, "invalid arguments to courseAndDistance");
+ }
+
+ if (extractGeod(ctx, to)) {
+ from = pos; // we parsed both FROM and TO args, so first was FROM
+ } else {
+ to = pos; // only parsed one arg, so FROM is current
+ }
+
+ double course, course2, d;
+ SGGeodesy::inverse(from, to, course, course2, d);
+
+ naRef result = naNewVector(ctx.c);
+ naVec_append(result, naNum(course));
+ naVec_append(result, naNum(d * SG_METER_TO_NM));
+ return result;
+}
+
+static naRef f_sortByRange(nasal::CallContext ctx)
+{
+ FGPositionedList items = ctx.requireArg<FGPositionedList>(0);
+ ctx.popFront();
+ FGPositioned::sortByRange(items, getPosition(ctx));
+ return ctx.to_nasal(items);
+}
+
+//------------------------------------------------------------------------------
+// Get difference between two lists of positioned objects.
+//
+// For every element in old_list not in new_list the callback cb_remove is
+// called with the removed element as single argument. For every element in
+// new_list not in old_list cb_add is called.
+//
+// diff(old_list, new_list, cb_add[, cb_remove])
+//
+// example:
+// # Print all fixes within a distance of 320 to 640 miles
+// diff( findWithinRange(320, "fix"),
+// findWithinRange(640, "fix"),
+// func(p) print('found fix: ', p.id) );
+static naRef f_diff(nasal::CallContext ctx)
+{
+ typedef simgear::ListDiff<FGPositionedRef> Diff;
+ Diff::List old_list = ctx.requireArg<FGPositionedList>(0),
+ new_list = ctx.requireArg<FGPositionedList>(1);
+ Diff::Callback cb_add = ctx.requireArg<Diff::Callback>(2),
+ cb_rm = ctx.getArg<Diff::Callback>(3);
+
+ // Note that FGPositionedRef instances are only compared for pointer equality.
+ // As the NavCache caches every queried positioned instance it is guaranteed
+ // that only one instance of every positioned object can exist. Therefore we
+ // can make the comparison faster by just comparing pointers and not also the
+ // guid.
+ // (On my machine the difference is 0.27s vs 0.17s)
+ Diff::inplace(old_list, new_list, cb_add, cb_rm);
+
+ return naNil();
+}
+
+//------------------------------------------------------------------------------
+naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c)
+{
+ NasalPositioned::init("Positioned")
.member("id", &FGPositioned::ident)
.member("ident", &FGPositioned::ident) // TODO to we really need id and ident?
.member("name", &FGPositioned::name)
+ .member("type", &FGPositioned::typeString)
.member("lat", &FGPositioned::latitude)
.member("lon", &FGPositioned::longitude)
.member("elevation", &FGPositioned::elevationM);
- NasalRunway::init("FGRunway")
+ NasalRunway::init("Runway")
.bases<NasalPositioned>();
- NasalParking::init("FGParking")
+ NasalParking::init("Parking")
.bases<NasalPositioned>();
NasalCommStation::init("CommStation")
.bases<NasalPositioned>()
.member("frequency", &flightgear::CommStation::freqMHz);
+ NasalNavRecord::init("Navaid")
+ .bases<NasalPositioned>()
+ .member("frequency", &FGNavRecord::get_freq)
+ .member("range_nm", &FGNavRecord::get_range)
+ .member("course", &f_navaid_course);
+
+ NasalFix::init("Fix")
+ .bases<NasalPositioned>();
+
NasalAirport::init("FGAirport")
.bases<NasalPositioned>()
.member("has_metar", &FGAirport::getMetar)
.method("getStar", &FGAirport::findSTARWithIdent)
.method("getIAP", &FGAirport::findApproachWithIdent)
.method("tostring", &FGAirport::toString);
-
+
nasal::Hash globals(globalsRef, c),
positioned( globals.createHash("positioned") );
positioned.set("airportinfo", &f_airportinfo);
+ positioned.set("findAirportsWithinRange", f_findAirportsWithinRange);
+ positioned.set("findAirportsByICAO", &f_findAirportsByICAO);
+ positioned.set("navinfo", &f_navinfo);
+
+ positioned.set("findWithinRange", &f_findWithinRange);
+ positioned.set("findByIdent", &f_findByIdent);
+ positioned.set("findByName", &f_findByName);
+ positioned.set("courseAndDistance", &f_courseAndDistance);
+ positioned.set("sortByRange", &f_sortByRange);
+
+ positioned.set("diff", &f_diff);
return naNil();
}