1 // NasalPositioned_cppbind.cxx -- expose FGPositioned classes to Nasal
3 // Port of NasalPositioned.cpp to the new nasal/cppbind helpers. Will replace
4 // old NasalPositioned.cpp once finished.
6 // Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "NasalPositioned.hxx"
31 #include <boost/foreach.hpp>
32 #include <boost/algorithm/string/case_conv.hpp>
34 #include <simgear/nasal/cppbind/from_nasal.hxx>
35 #include <simgear/nasal/cppbind/to_nasal.hxx>
36 #include <simgear/nasal/cppbind/NasalHash.hxx>
37 #include <simgear/nasal/cppbind/Ghost.hxx>
39 #include <Airports/airport.hxx>
40 #include <Airports/dynamics.hxx>
41 #include <ATC/CommStation.hxx>
42 #include <Main/globals.hxx>
43 #include <Navaids/NavDataCache.hxx>
44 #include <Navaids/navlist.hxx>
45 #include <Navaids/navrecord.hxx>
47 typedef nasal::Ghost<FGPositionedRef> NasalPositioned;
48 typedef nasal::Ghost<FGRunwayRef> NasalRunway;
49 typedef nasal::Ghost<FGParkingRef> NasalParking;
50 typedef nasal::Ghost<FGAirportRef> NasalAirport;
51 typedef nasal::Ghost<flightgear::CommStationRef> NasalCommStation;
52 typedef nasal::Ghost<FGNavRecordRef> NasalNavRecord;
54 //------------------------------------------------------------------------------
55 naRef to_nasal_helper(naContext c, FGPositioned* positioned)
57 return NasalPositioned::create(c, positioned);
60 //------------------------------------------------------------------------------
61 naRef to_nasal_helper(naContext c, FGPavement* rwy)
63 return NasalPositioned::create(c, (FGPositioned*)rwy);
66 //------------------------------------------------------------------------------
67 naRef to_nasal_helper(naContext c, FGRunwayBase* rwy)
69 return NasalPositioned::create(c, (FGPositioned*)rwy);
72 //------------------------------------------------------------------------------
73 naRef to_nasal_helper(naContext c, FGParking* parking)
75 return NasalParking::create(c, parking);
78 //------------------------------------------------------------------------------
79 naRef to_nasal_helper(naContext c, flightgear::SID* sid)
82 return nasal::to_nasal(c, sid->ident());
85 //------------------------------------------------------------------------------
86 naRef to_nasal_helper(naContext c, flightgear::STAR* star)
89 return nasal::to_nasal(c, star->ident());
92 //------------------------------------------------------------------------------
93 naRef to_nasal_helper(naContext c, flightgear::Approach* iap)
95 // TODO Approach ghost
96 return nasal::to_nasal(c, iap->ident());
99 //------------------------------------------------------------------------------
100 naRef to_nasal_helper(naContext c, FGAirport* apt)
102 return NasalAirport::create(c, apt);
105 //------------------------------------------------------------------------------
106 naRef to_nasal_helper(naContext c, const SGGeod& pos)
109 hash.set("lat", pos.getLatitudeDeg());
110 hash.set("lon", pos.getLongitudeDeg());
111 hash.set("elevation", pos.getElevationM());
112 return hash.get_naRef();
115 //------------------------------------------------------------------------------
116 static naRef f_navaid_course(naContext, FGNavRecord& nav)
118 if( !( nav.type() == FGPositioned::ILS
119 || nav.type() == FGPositioned::LOC
123 double radial = nav.get_multiuse();
124 return naNum(SGMiscd::normalizePeriodic(0.5, 360.5, radial));
127 //------------------------------------------------------------------------------
128 static FGRunwayBase* f_airport_runway(FGAirport& apt, std::string ident)
130 boost::to_upper(ident);
132 if( apt.hasRunwayWithIdent(ident) )
133 return apt.getRunwayByIdent(ident);
134 else if( apt.hasHelipadWithIdent(ident) )
135 return apt.getHelipadByIdent(ident);
140 //------------------------------------------------------------------------------
141 template<class T, class C1, class C2>
142 std::vector<T> extract( const std::vector<C1*>& in,
143 T (C2::*getter)() const )
145 std::vector<T> ret(in.size());
146 std::transform(in.begin(), in.end(), ret.begin(), std::mem_fun(getter));
150 //------------------------------------------------------------------------------
151 static naRef f_airport_comms(FGAirport& apt, const nasal::CallContext& ctx)
153 FGPositioned::Type comm_type =
154 FGPositioned::typeFromName( ctx.getArg<std::string>(0) );
156 // if we have an explicit type, return a simple vector of frequencies
157 if( comm_type != FGPositioned::INVALID )
160 extract( apt.commStationsOfType(comm_type),
161 &flightgear::CommStation::freqMHz )
164 // otherwise return a vector of ghosts, one for each comm station.
165 return ctx.to_nasal(apt.commStations());
168 //------------------------------------------------------------------------------
169 FGRunway* runwayFromNasalArg( const FGAirport& apt,
170 const nasal::CallContext& ctx,
173 if( index >= ctx.argc )
178 std::string ident = ctx.getArg<std::string>(index);
181 if( !apt.hasRunwayWithIdent(ident) )
182 // TODO warning/exception?
185 return apt.getRunwayByIdent(ident);
191 // TODO warn/error if no runway?
192 return NasalRunway::fromNasal(ctx.c, ctx.args[index]);
195 //------------------------------------------------------------------------------
196 static naRef f_airport_sids(FGAirport& apt, const nasal::CallContext& ctx)
198 FGRunway* rwy = runwayFromNasalArg(apt, ctx);
201 extract(rwy ? rwy->getSIDs() : apt.getSIDs(), &flightgear::SID::ident)
205 //------------------------------------------------------------------------------
206 static naRef f_airport_stars(FGAirport& apt, const nasal::CallContext& ctx)
208 FGRunway* rwy = runwayFromNasalArg(apt, ctx);
211 extract(rwy ? rwy->getSTARs() : apt.getSTARs(), &flightgear::STAR::ident)
215 //------------------------------------------------------------------------------
216 static naRef f_airport_approaches(FGAirport& apt, const nasal::CallContext& ctx)
218 FGRunway* rwy = runwayFromNasalArg(apt, ctx);
220 flightgear::ProcedureType type = flightgear::PROCEDURE_INVALID;
221 std::string type_str = ctx.getArg<std::string>(1);
222 if( !type_str.empty() )
224 boost::to_upper(type_str);
225 if( type_str == "NDB" ) type = flightgear::PROCEDURE_APPROACH_NDB;
226 else if( type_str == "VOR" ) type = flightgear::PROCEDURE_APPROACH_VOR;
227 else if( type_str == "ILS" ) type = flightgear::PROCEDURE_APPROACH_ILS;
228 else if( type_str == "RNAV") type = flightgear::PROCEDURE_APPROACH_RNAV;
233 extract( rwy ? rwy->getApproaches(type)
234 // no runway specified, report them all
235 : apt.getApproaches(type),
236 &flightgear::Approach::ident )
240 //------------------------------------------------------------------------------
242 f_airport_parking(FGAirport& apt, nasal::CallContext ctx)
244 std::string type = ctx.getArg<std::string>(0);
245 bool only_available = ctx.getArg<bool>(1);
247 FGAirportDynamics* dynamics = apt.getDynamics();
248 PositionedIDVec parkings =
249 flightgear::NavDataCache::instance()
250 ->airportItemsOfType(apt.guid(), FGPositioned::PARKING);
253 BOOST_FOREACH(PositionedID parking, parkings)
255 // filter out based on availability and type
256 if( only_available && !dynamics->isParkingAvailable(parking) )
259 FGParking* park = dynamics->getParking(parking);
260 if( !type.empty() && (park->getType() != type) )
270 * Extract a SGGeod from a nasal function argument list.
273 * {"lat": <lat-deg>, "lon": <lon-deg>}
274 * geo.Coord.new() (aka. {"_lat": <lat-rad>, "_lon": <lon-rad>})
276 static bool extractGeod(nasal::CallContext& ctx, SGGeod& result)
284 NasalPositioned::fromNasal(ctx.c, ctx.requireArg<naRef>(0));
288 result = pos->geod();
293 else if( ctx.isHash(0) )
295 nasal::Hash pos_hash = ctx.requireArg<nasal::Hash>(0);
297 // check for manual latitude / longitude names
298 naRef lat = pos_hash.get("lat"),
299 lon = pos_hash.get("lon");
300 if( naIsNum(lat) && naIsNum(lon) )
302 result = SGGeod::fromDeg( ctx.from_nasal<double>(lon),
303 ctx.from_nasal<double>(lat) );
308 // geo.Coord uses _lat/_lon in radians
309 // TODO should we check if its really a geo.Coord?
310 lat = pos_hash.get("_lat");
311 lon = pos_hash.get("_lon");
312 if( naIsNum(lat) && naIsNum(lon) )
314 result = SGGeod::fromRad( ctx.from_nasal<double>(lon),
315 ctx.from_nasal<double>(lat) );
320 else if( ctx.isNumeric(0) && ctx.isNumeric(1) )
323 result = SGGeod::fromDeg( ctx.requireArg<double>(1),
324 ctx.requireArg<double>(0) );
333 * Extract position from ctx or return current aircraft position if not given.
335 static SGGeod getPosition(nasal::CallContext& ctx)
338 if( !extractGeod(ctx, pos) )
339 pos = globals->get_aircraft_position();
344 //------------------------------------------------------------------------------
345 // Returns Nasal ghost for particular or nearest airport of a <type>, or nil
348 // airportinfo(<id>); e.g. "KSFO"
349 // airportinfo(<type>); type := ("airport"|"seaport"|"heliport")
350 // airportinfo() same as airportinfo("airport")
351 // airportinfo(<lat>, <lon> [, <type>]);
352 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
354 nasal::CallContext ctx(c, argc, args);
356 SGGeod pos = getPosition(ctx);
359 naRuntimeError(ctx.c, "airportinfo() with invalid function arguments");
361 // optional type/ident
362 std::string ident("airport");
363 if( ctx.isString(0) )
364 ident = ctx.requireArg<std::string>(0);
366 FGAirport::TypeRunwayFilter filter;
367 if( !filter.fromTypeString(ident) )
368 // user provided an <id>, hopefully
369 return ctx.to_nasal(FGAirport::findByIdent(ident));
371 double maxRange = 10000.0; // expose this? or pick a smaller value?
372 return ctx.to_nasal( FGAirport::findClosest(pos, maxRange, &filter) );
376 * findAirportsWithinRange([<position>,] <range-nm> [, type])
378 static naRef f_findAirportsWithinRange(naContext c, naRef me, int argc, naRef* args)
380 nasal::CallContext ctx(c, argc, args);
382 SGGeod pos = getPosition(ctx);
383 double range_nm = ctx.requireArg<double>(0);
385 FGAirport::TypeRunwayFilter filter; // defaults to airports only
386 filter.fromTypeString( ctx.getArg<std::string>(1) );
388 FGPositioned::List apts =
389 FGPositioned::findWithinRange(pos, range_nm, &filter);
390 FGPositioned::sortByRange(apts, pos);
392 return ctx.to_nasal(apts);
396 * findAirportsByICAO(<ident/prefix> [, type])
398 static naRef f_findAirportsByICAO(naContext c, naRef me, int argc, naRef* args)
400 nasal::CallContext ctx(c, argc, args);
401 std::string prefix = ctx.requireArg<std::string>(0);
403 FGAirport::TypeRunwayFilter filter; // defaults to airports only
404 filter.fromTypeString( ctx.getArg<std::string>(1) );
406 return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, false) );
409 // Returns vector of data hash for navaid of a <type>, nil on error
410 // navaids sorted by ascending distance
411 // navinfo([<lat>,<lon>],[<type>],[<id>])
412 // lat/lon (numeric): use latitude/longitude instead of ac position
413 // type: ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
414 // id: (partial) id of the fix
416 // navinfo("vor") returns all vors
417 // navinfo("HAM") return all navaids who's name start with "HAM"
418 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
419 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM"
420 // sorted by distance relative to lat=34, lon=48
421 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
423 nasal::CallContext ctx(c, argc, args);
425 SGGeod pos = getPosition(ctx);
426 std::string id = ctx.getArg<std::string>(0);
428 FGNavList::TypeFilter filter;
429 if( filter.fromTypeString(id) )
430 id = ctx.getArg<std::string>(1);
431 else if( ctx.argc > 1 )
432 naRuntimeError(c, "navinfo() already got an ident");
434 return ctx.to_nasal( FGNavList::findByIdentAndFreq(pos, id, 0.0, &filter) );
437 //------------------------------------------------------------------------------
438 static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
440 nasal::CallContext ctx(c, argc, args);
442 SGGeod pos = getPosition(ctx);
443 double range_nm = ctx.requireArg<double>(0);
445 FGNavList::TypeFilter filter;
446 filter.fromTypeString(ctx.getArg<std::string>(0));
448 FGPositioned::List navs =
449 FGPositioned::findWithinRange(pos, range_nm, &filter);
450 FGPositioned::sortByRange(navs, pos);
452 return ctx.to_nasal(navs);
455 //------------------------------------------------------------------------------
456 naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c, naRef gcSave)
458 NasalPositioned::init("Positioned")
459 .member("id", &FGPositioned::ident)
460 .member("ident", &FGPositioned::ident) // TODO to we really need id and ident?
461 .member("name", &FGPositioned::name)
462 .member("type", &FGPositioned::typeString)
463 .member("lat", &FGPositioned::latitude)
464 .member("lon", &FGPositioned::longitude)
465 .member("elevation", &FGPositioned::elevationM);
466 NasalRunway::init("Runway")
467 .bases<NasalPositioned>();
468 NasalParking::init("Parking")
469 .bases<NasalPositioned>();
470 NasalCommStation::init("CommStation")
471 .bases<NasalPositioned>()
472 .member("frequency", &flightgear::CommStation::freqMHz);
473 NasalNavRecord::init("Navaid")
474 .bases<NasalPositioned>()
475 .member("frequency", &FGNavRecord::get_freq)
476 .member("range_nm", &FGNavRecord::get_range)
477 .member("course", &f_navaid_course);
479 NasalAirport::init("FGAirport")
480 .bases<NasalPositioned>()
481 .member("has_metar", &FGAirport::getMetar)
482 .member("runways", &FGAirport::getRunwayMap)
483 .member("helipads", &FGAirport::getHelipadMap)
484 .member("taxiways", &FGAirport::getTaxiways)
485 .member("pavements", &FGAirport::getPavements)
486 .method("runway", &f_airport_runway)
487 .method("helipad", &f_airport_runway)
488 .method("tower", &FGAirport::getTowerLocation)
489 .method("comms", &f_airport_comms)
490 .method("sids", &f_airport_sids)
491 .method("stars", &f_airport_stars)
492 .method("getApproachList", f_airport_approaches)
493 .method("parking", &f_airport_parking)
494 .method("getSid", &FGAirport::findSIDWithIdent)
495 .method("getStar", &FGAirport::findSTARWithIdent)
496 .method("getIAP", &FGAirport::findApproachWithIdent)
497 .method("tostring", &FGAirport::toString);
499 nasal::Hash globals(globalsRef, c),
500 positioned( globals.createHash("positioned") );
502 positioned.set("airportinfo", &f_airportinfo);
503 positioned.set("findAirportsWithinRange", f_findAirportsWithinRange);
504 positioned.set("findAirportsByICAO", &f_findAirportsByICAO);
505 positioned.set("navinfo", &f_navinfo);
506 positioned.set("findNavaidsWithinRange", &f_findNavaidsWithinRange);