1 // NasalPositioned.cxx -- expose FGPositioned classes to Nasal
3 // Written by James Turner, started 2012.
5 // Copyright (C) 2012 James Turner
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "NasalPositioned.hxx"
29 #include <boost/foreach.hpp>
31 #include <simgear/scene/material/mat.hxx>
32 #include <simgear/magvar/magvar.hxx>
33 #include <simgear/timing/sg_time.hxx>
34 #include <simgear/bucket/newbucket.hxx>
36 #include <Airports/runways.hxx>
37 #include <Airports/simple.hxx>
38 #include <Airports/dynamics.hxx>
39 #include <Airports/parking.hxx>
40 #include <Navaids/navlist.hxx>
41 #include <Navaids/procedure.hxx>
42 #include <Main/globals.hxx>
43 #include <Main/fg_props.hxx>
44 #include <Scenery/scenery.hxx>
45 #include <ATC/CommStation.hxx>
46 #include <Navaids/route.hxx>
47 #include <Autopilot/route_mgr.hxx>
48 #include <Navaids/procedure.hxx>
50 static void positionedGhostDestroy(void* g);
51 static void wayptGhostDestroy(void* g);
52 naGhostType PositionedGhostType = { positionedGhostDestroy, "positioned" };
54 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
56 naGhostType WayptGhostType = { wayptGhostDestroy,
61 static void hashset(naContext c, naRef hash, const char* key, naRef val)
63 naRef s = naNewString(c);
64 naStr_fromdata(s, (char*)key, strlen(key));
65 naHash_set(hash, s, val);
68 static naRef stringToNasal(naContext c, const std::string& s)
70 return naStr_fromdata(naNewString(c),
71 const_cast<char *>(s.c_str()),
75 static FGPositioned* positionedGhost(naRef r)
77 if (naGhost_type(r) == &PositionedGhostType)
78 return (FGPositioned*) naGhost_ptr(r);
82 static void positionedGhostDestroy(void* g)
84 FGPositioned* pos = (FGPositioned*)g;
85 if (!FGPositioned::put(pos)) // unref
89 static flightgear::Waypt* wayptGhost(naRef r)
91 if (naGhost_type(r) == &WayptGhostType)
92 return (flightgear::Waypt*) naGhost_ptr(r);
96 static void wayptGhostDestroy(void* g)
98 flightgear::Waypt* wpt = (flightgear::Waypt*)g;
99 if (!flightgear::Waypt::put(wpt)) // unref
103 static naRef airportPrototype;
104 static naRef routePrototype;
105 static naRef waypointPrototype;
106 static naRef geoCoordClass;
108 naRef ghostForPositioned(naContext c, const FGPositioned* pos)
114 FGPositioned::get(pos); // take a ref
115 return naNewGhost(c, &PositionedGhostType, (void*) pos);
118 naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt)
124 flightgear::Waypt::get(wpt); // take a ref
125 return naNewGhost2(c, &WayptGhostType, (void*) wpt);
128 naRef hashForAirport(naContext c, const FGAirport* apt)
130 std::string id = apt->ident();
131 std::string name = apt->name();
133 // build runways hash
134 naRef rwys = naNewHash(c);
135 for(unsigned int r=0; r<apt->numRunways(); ++r) {
136 FGRunway* rwy(apt->getRunwayByIndex(r));
137 naRef rwyid = stringToNasal(c, rwy->ident());
138 naRef rwydata = hashForRunway(c, rwy);
139 naHash_set(rwys, rwyid, rwydata);
142 naRef aptdata = naNewHash(c);
143 hashset(c, aptdata, "id", stringToNasal(c, id));
144 hashset(c, aptdata, "name", stringToNasal(c, name));
145 hashset(c, aptdata, "lat", naNum(apt->getLatitude()));
146 hashset(c, aptdata, "lon", naNum(apt->getLongitude()));
147 hashset(c, aptdata, "elevation", naNum(apt->getElevation() * SG_FEET_TO_METER));
148 hashset(c, aptdata, "has_metar", naNum(apt->getMetar()));
149 hashset(c, aptdata, "runways", rwys);
150 hashset(c, aptdata, "_positioned", ghostForPositioned(c, apt));
151 naRef parents = naNewVector(c);
152 naVec_append(parents, airportPrototype);
153 hashset(c, aptdata, "parents", parents);
158 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
160 const char* fieldName = naStr_data(field);
161 flightgear::Waypt* wpt = (flightgear::Waypt*) g;
163 if (!strcmp(fieldName, "parents")) {
164 *out = naNewVector(c);
165 naVec_append(*out, waypointPrototype);
166 } else if (!strcmp(fieldName, "wp_name")) *out =stringToNasal(c, wpt->ident());
167 else if (!strcmp(fieldName, "wp_type")) *out = stringToNasal(c, wpt->type());
168 else if (!strcmp(fieldName, "wp_lat")) *out = naNum(wpt->position().getLatitudeDeg());
169 else if (!strcmp(fieldName, "wp_lon")) *out = naNum(wpt->position().getLongitudeDeg());
170 else if (!strcmp(fieldName, "wp_parent_name")) {
171 flightgear::Procedure* proc = dynamic_cast<flightgear::Procedure*>(wpt->owner());
172 *out = proc ? stringToNasal(c, proc->ident()) : naNil();
173 } else if (!strcmp(fieldName, "fly_type")) {
174 if (wpt->type() == "hold") {
175 *out = stringToNasal(c, "Hold");
177 *out = stringToNasal(c, wpt->flag(flightgear::WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
179 } else if (!strcmp(fieldName, "alt_cstr")) *out = naNum(wpt->altitudeFt());
180 else if (!strcmp(fieldName, "speed_cstr")) {
181 double s = (wpt->speedRestriction() == flightgear::SPEED_RESTRICT_MACH)
182 ? wpt->speedMach() : wpt->speedKts();
184 } else if (!strcmp(fieldName, "leg_distance")) {
185 return "please implement me";
186 } else if (!strcmp(fieldName, "leg_bearing")) {
187 return "please implement me";
189 return NULL; // member not found
192 return ""; // success
195 naRef hashForRunway(naContext c, FGRunway* rwy)
197 naRef rwyid = stringToNasal(c, rwy->ident());
198 naRef rwydata = naNewHash(c);
199 #define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
200 HASHSET("id", 2, rwyid);
201 HASHSET("lat", 3, naNum(rwy->latitude()));
202 HASHSET("lon", 3, naNum(rwy->longitude()));
203 HASHSET("heading", 7, naNum(rwy->headingDeg()));
204 HASHSET("length", 6, naNum(rwy->lengthM()));
205 HASHSET("width", 5, naNum(rwy->widthM()));
206 HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
207 HASHSET("stopway", 7, naNum(rwy->stopwayM()));
210 HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
211 HASHSET("ils", 3, hashForNavRecord(c, rwy->ILS(), SGGeod()));
214 HASHSET("_positioned", 11, ghostForPositioned(c, rwy));
219 naRef hashForNavRecord(naContext c, const FGNavRecord* nav, const SGGeod& rel)
221 naRef navdata = naNewHash(c);
222 #define HASHSET(s,l,n) naHash_set(navdata, naStr_fromdata(naNewString(c),s,l),n)
223 HASHSET("id", 2, stringToNasal(c, nav->ident()));
224 HASHSET("name", 4, stringToNasal(c, nav->name()));
225 HASHSET("frequency", 9, naNum(nav->get_freq()));
226 HASHSET("lat", 3, naNum(nav->get_lat()));
227 HASHSET("lon", 3, naNum(nav->get_lon()));
228 HASHSET("elevation", 9, naNum(nav->get_elev_ft() * SG_FEET_TO_METER));
229 HASHSET("type", 4, stringToNasal(c, nav->nameForType(nav->type())));
231 // FIXME - get rid of these, people should use courseAndDistance instead
232 HASHSET("distance", 8, naNum(SGGeodesy::distanceNm( rel, nav->geod() ) * SG_NM_TO_METER ) );
233 HASHSET("bearing", 7, naNum(SGGeodesy::courseDeg( rel, nav->geod() ) ) );
235 // record the real object as a ghost for further operations
236 HASHSET("_positioned",11, ghostForPositioned(c, nav));
242 static bool hashIsCoord(naRef h)
244 naRef parents = naHash_cget(h, (char*) "parents");
245 if (!naIsVector(parents)) {
249 return naEqual(naVec_get(parents, 0), geoCoordClass);
252 bool geodFromHash(naRef ref, SGGeod& result)
254 if (!naIsHash(ref)) {
258 // first, see if the hash contains a FGPositioned ghost - in which case
259 // we can read off its position directly
260 naRef posGhost = naHash_cget(ref, (char*) "_positioned");
261 if (!naIsNil(posGhost)) {
262 FGPositioned* pos = positionedGhost(posGhost);
263 result = pos->geod();
267 naRef ghost = naHash_cget(ref, (char*) "_waypt");
268 if (!naIsNil(ghost)) {
269 flightgear::Waypt* w = wayptGhost(ghost);
270 result = w->position();
274 // then check for manual latitude / longitude names
275 naRef lat = naHash_cget(ref, (char*) "lat");
276 naRef lon = naHash_cget(ref, (char*) "lon");
277 if (naIsNum(lat) && naIsNum(lon)) {
278 result = SGGeod::fromDeg(naNumValue(lon).num, naNumValue(lat).num);
282 if (hashIsCoord(ref)) {
283 naRef lat = naHash_cget(ref, (char*) "_lat");
284 naRef lon = naHash_cget(ref, (char*) "_lon");
285 if (naIsNum(lat) && naIsNum(lon)) {
286 result = SGGeod::fromRad(naNumValue(lon).num, naNumValue(lat).num);
291 // check for any synonyms?
292 // latitude + longitude?
297 static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
299 if (offset >= argc) {
303 if (geodFromHash(args[offset], result)) {
307 if (((argc - offset) >= 2) && naIsNum(args[offset]) && naIsNum(args[offset + 1])) {
308 double lat = naNumValue(args[0]).num,
309 lon = naNumValue(args[1]).num;
310 result = SGGeod::fromDeg(lon, lat);
317 // Convert a cartesian point to a geodetic lat/lon/altitude.
318 static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
320 double lat, lon, alt, xyz[3];
321 if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
322 for(int i=0; i<3; i++)
323 xyz[i] = naNumValue(args[i]).num;
324 sgCartToGeod(xyz, &lat, &lon, &alt);
325 lat *= SG_RADIANS_TO_DEGREES;
326 lon *= SG_RADIANS_TO_DEGREES;
327 naRef vec = naNewVector(c);
328 naVec_append(vec, naNum(lat));
329 naVec_append(vec, naNum(lon));
330 naVec_append(vec, naNum(alt));
334 // Convert a geodetic lat/lon/altitude to a cartesian point.
335 static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
337 if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
338 double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
339 double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
340 double alt = naNumValue(args[2]).num;
342 sgGeodToCart(lat, lon, alt, xyz);
343 naRef vec = naNewVector(c);
344 naVec_append(vec, naNum(xyz[0]));
345 naVec_append(vec, naNum(xyz[1]));
346 naVec_append(vec, naNum(xyz[2]));
350 // For given geodetic point return array with elevation, and a material data
351 // hash, or nil if there's no information available (tile not loaded). If
352 // information about the material isn't available, then nil is returned instead
354 static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
356 #define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
357 if(argc < 2 || argc > 3)
358 naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
359 double lat = naNumValue(args[0]).num;
360 double lon = naNumValue(args[1]).num;
361 double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
362 const SGMaterial *mat;
363 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
364 if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
366 naRef vec = naNewVector(c);
367 naVec_append(vec, naNum(elev));
368 naRef matdata = naNil();
370 matdata = naNewHash(c);
371 naRef names = naNewVector(c);
372 BOOST_FOREACH(const std::string& n, mat->get_names())
373 naVec_append(names, stringToNasal(c, n));
375 HASHSET("names", 5, names);
376 HASHSET("solid", 5, naNum(mat->get_solid()));
377 HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
378 HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
379 HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
380 HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
381 HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
383 naVec_append(vec, matdata);
389 class AirportInfoFilter : public FGAirport::AirportFilter
392 AirportInfoFilter() : type(FGPositioned::AIRPORT) {
395 bool fromArg(naRef arg)
397 const char *s = naStr_data(arg);
398 if(!strcmp(s, "airport")) type = FGPositioned::AIRPORT;
399 else if(!strcmp(s, "seaport")) type = FGPositioned::SEAPORT;
400 else if(!strcmp(s, "heliport")) type = FGPositioned::HELIPORT;
407 virtual FGPositioned::Type minType() const {
411 virtual FGPositioned::Type maxType() const {
415 FGPositioned::Type type;
418 // Returns data hash for particular or nearest airport of a <type>, or nil
421 // airportinfo(<id>); e.g. "KSFO"
422 // airportinfo(<type>); type := ("airport"|"seaport"|"heliport")
423 // airportinfo() same as airportinfo("airport")
424 // airportinfo(<lat>, <lon> [, <type>]);
425 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
427 SGGeod pos = globals->get_aircraft_position();
428 FGAirport* apt = NULL;
430 if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
431 pos = SGGeod::fromDeg(args[1].num, args[0].num);
436 double maxRange = 10000.0; // expose this? or pick a smaller value?
438 AirportInfoFilter filter; // defaults to airports only
441 // fall through and use AIRPORT
442 } else if(argc == 1 && naIsString(args[0])) {
443 if (filter.fromArg(args[0])) {
446 // user provided an <id>, hopefully
447 apt = FGAirport::findByIdent(naStr_data(args[0]));
449 // return nil here, but don't raise a runtime error; this is a
450 // legitamate way to validate an ICAO code, for example in a
451 // dialog box or similar.
456 naRuntimeError(c, "airportinfo() with invalid function arguments");
461 apt = FGAirport::findClosest(pos, maxRange, &filter);
462 if(!apt) return naNil();
465 return hashForAirport(c, apt);
468 static naRef f_findAirportsWithinRange(naContext c, naRef me, int argc, naRef* args)
471 SGGeod pos = globals->get_aircraft_position();
472 argOffset += geodFromArgs(args, 0, argc, pos);
474 if (!naIsNum(args[argOffset])) {
475 naRuntimeError(c, "findAirportsWithinRange expected range (in nm) as arg %d", argOffset);
478 AirportInfoFilter filter; // defaults to airports only
479 double rangeNm = args[argOffset++].num;
480 if (argOffset < argc) {
481 filter.fromArg(args[argOffset++]);
484 naRef r = naNewVector(c);
486 FGPositioned::List apts = FGPositioned::findWithinRange(pos, rangeNm, &filter);
487 FGPositioned::sortByRange(apts, pos);
489 BOOST_FOREACH(FGPositionedRef a, apts) {
490 FGAirport* apt = (FGAirport*) a.get();
491 naVec_append(r, hashForAirport(c, apt));
497 static naRef f_findAirportsByICAO(naContext c, naRef me, int argc, naRef* args)
499 if (!naIsString(args[0])) {
500 naRuntimeError(c, "findAirportsByICAO expects string as arg 0");
504 string prefix(naStr_data(args[argOffset++]));
505 AirportInfoFilter filter; // defaults to airports only
506 if (argOffset < argc) {
507 filter.fromArg(args[argOffset++]);
510 naRef r = naNewVector(c);
512 FGPositioned::List apts = FGPositioned::findAllWithIdent(prefix, &filter, false);
514 BOOST_FOREACH(FGPositionedRef a, apts) {
515 FGAirport* apt = (FGAirport*) a.get();
516 naVec_append(r, hashForAirport(c, apt));
522 static FGAirport* airportFromMe(naRef me)
524 naRef ghost = naHash_cget(me, (char*) "_positioned");
525 if (naIsNil(ghost)) {
529 FGPositioned* pos = positionedGhost(ghost);
530 if (pos && FGAirport::isAirportType(pos)) {
531 return (FGAirport*) pos;
537 static naRef f_airport_tower(naContext c, naRef me, int argc, naRef* args)
539 FGAirport* apt = airportFromMe(me);
541 naRuntimeError(c, "airport.tower called on non-airport object");
544 // build a hash for the tower position
545 SGGeod towerLoc = apt->getTowerLocation();
546 naRef tower = naNewHash(c);
547 hashset(c, tower, "lat", naNum(towerLoc.getLatitudeDeg()));
548 hashset(c, tower, "lon", naNum(towerLoc.getLongitudeDeg()));
549 hashset(c, tower, "elevation", naNum(towerLoc.getElevationM()));
553 static naRef f_airport_comms(naContext c, naRef me, int argc, naRef* args)
555 FGAirport* apt = airportFromMe(me);
557 naRuntimeError(c, "airport.comms called on non-airport object");
559 naRef comms = naNewVector(c);
561 // if we have an explicit type, return a simple vector of frequencies
562 if (argc > 0 && naIsScalar(args[0])) {
563 std::string commName = naStr_data(args[0]);
564 FGPositioned::Type commType = FGPositioned::typeFromName(commName);
566 BOOST_FOREACH(flightgear::CommStation* comm, apt->commStationsOfType(commType)) {
567 naVec_append(comms, naNum(comm->freqMHz()));
570 // otherwise return a vector of hashes, one for each comm station.
571 BOOST_FOREACH(flightgear::CommStation* comm, apt->commStations()) {
572 naRef commHash = naNewHash(c);
573 hashset(c, commHash, "frequency", naNum(comm->freqMHz()));
574 hashset(c, commHash, "ident", stringToNasal(c, comm->ident()));
575 naVec_append(comms, commHash);
582 static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args)
584 FGAirport* apt = airportFromMe(me);
586 naRuntimeError(c, "airport.runway called on non-airport object");
589 if ((argc < 1) || !naIsString(args[0])) {
590 naRuntimeError(c, "airport.runway expects a runway ident argument");
593 std::string ident(naStr_data(args[0]));
594 if (!apt->hasRunwayWithIdent(ident)) {
598 return hashForRunway(c, apt->getRunwayByIdent(ident));
601 static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
603 FGAirport* apt = airportFromMe(me);
605 naRuntimeError(c, "airport.sids called on non-airport object");
608 naRef sids = naNewVector(c);
610 if (argc > 0 && naIsString(args[0])) {
611 if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
615 FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
616 BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) {
617 naRef procId = stringToNasal(c, sid->ident());
618 naVec_append(sids, procId);
621 for (unsigned int s=0; s<apt->numSIDs(); ++s) {
622 flightgear::SID* sid = apt->getSIDByIndex(s);
623 naRef procId = stringToNasal(c, sid->ident());
624 naVec_append(sids, procId);
631 static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
633 FGAirport* apt = airportFromMe(me);
635 naRuntimeError(c, "airport.stars called on non-airport object");
638 naRef stars = naNewVector(c);
640 if (argc > 0 && naIsString(args[0])) {
641 if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
645 FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
646 BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) {
647 naRef procId = stringToNasal(c, s->ident());
648 naVec_append(stars, procId);
651 for (unsigned int s=0; s<apt->numSTARs(); ++s) {
652 flightgear::STAR* star = apt->getSTARByIndex(s);
653 naRef procId = stringToNasal(c, star->ident());
654 naVec_append(stars, procId);
661 static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
663 FGAirport* apt = airportFromMe(me);
665 naRuntimeError(c, "airport.parking called on non-airport object");
668 naRef r = naNewVector(c);
670 bool onlyAvailable = false;
672 if (argc > 0 && naIsString(args[0])) {
673 type = naStr_data(args[0]);
676 if ((argc > 1) && naIsNum(args[1])) {
677 onlyAvailable = (args[1].num != 0.0);
680 FGAirportDynamics* dynamics = apt->getDynamics();
681 for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
682 FGParking* park = dynamics->getParking(i);
683 // filter out based on availability and type
684 if (onlyAvailable && !park->isAvailable()) {
688 if (!type.empty() && (park->getType() != type)) {
692 naRef nm = stringToNasal(c, park->getName());
699 // Returns vector of data hash for navaid of a <type>, nil on error
700 // navaids sorted by ascending distance
701 // navinfo([<lat>,<lon>],[<type>],[<id>])
702 // lat/lon (numeric): use latitude/longitude instead of ac position
703 // type: ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
704 // id: (partial) id of the fix
706 // navinfo("vor") returns all vors
707 // navinfo("HAM") return all navaids who's name start with "HAM"
708 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
709 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM"
710 // sorted by distance relative to lat=34, lon=48
711 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
715 if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
716 pos = SGGeod::fromDeg(args[1].num, args[0].num);
720 pos = globals->get_aircraft_position();
723 FGPositioned::Type type = FGPositioned::INVALID;
724 nav_list_type navlist;
725 const char * id = "";
727 if(argc > 0 && naIsString(args[0])) {
728 const char *s = naStr_data(args[0]);
729 if(!strcmp(s, "any")) type = FGPositioned::INVALID;
730 else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
731 else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
732 else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
733 else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
734 else if(!strcmp(s, "dme")) type = FGPositioned::DME;
735 else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
736 else id = s; // this is an id
741 if(argc > 0 && naIsString(args[0])) {
743 naRuntimeError(c, "navinfo() called with navaid id");
746 id = naStr_data(args[0]);
752 naRuntimeError(c, "navinfo() called with too many arguments");
756 navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
758 naRef reply = naNewVector(c);
759 for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
760 naVec_append( reply, hashForNavRecord(c, *it, pos) );
765 static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
768 SGGeod pos = globals->get_aircraft_position();
769 argOffset += geodFromArgs(args, 0, argc, pos);
771 if (!naIsNum(args[argOffset])) {
772 naRuntimeError(c, "findNavaidsWithinRange expected range (in nm) as arg %d", argOffset);
775 FGPositioned::Type type = FGPositioned::INVALID;
776 double rangeNm = args[argOffset++].num;
777 if (argOffset < argc) {
778 type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
781 naRef r = naNewVector(c);
782 FGNavList::TypeFilter filter(type);
783 FGPositioned::List navs = FGPositioned::findWithinRange(pos, rangeNm, &filter);
784 FGPositioned::sortByRange(navs, pos);
786 BOOST_FOREACH(FGPositionedRef a, navs) {
787 FGNavRecord* nav = (FGNavRecord*) a.get();
788 naVec_append(r, hashForNavRecord(c, nav, pos));
794 static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
797 SGGeod pos = globals->get_aircraft_position();
798 argOffset += geodFromArgs(args, 0, argc, pos);
800 if (!naIsNum(args[argOffset])) {
801 naRuntimeError(c, "findNavaidByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
804 FGPositioned::Type type = FGPositioned::INVALID;
805 double freqMhz = args[argOffset++].num;
806 if (argOffset < argc) {
807 type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
810 nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
815 return hashForNavRecord(c, navs.front().ptr(), pos);
818 static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* args)
821 SGGeod pos = globals->get_aircraft_position();
822 argOffset += geodFromArgs(args, 0, argc, pos);
824 if (!naIsNum(args[argOffset])) {
825 naRuntimeError(c, "findNavaidsByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
828 FGPositioned::Type type = FGPositioned::INVALID;
829 double freqMhz = args[argOffset++].num;
830 if (argOffset < argc) {
831 type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
834 naRef r = naNewVector(c);
835 nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
837 BOOST_FOREACH(nav_rec_ptr a, navs) {
838 naVec_append(r, hashForNavRecord(c, a.ptr(), pos));
844 static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
847 SGGeod pos = globals->get_aircraft_position();
848 argOffset += geodFromArgs(args, 0, argc, pos);
850 if (!naIsString(args[argOffset])) {
851 naRuntimeError(c, "findNavaidsByIdent expectes ident string as arg %d", argOffset);
854 FGPositioned::Type type = FGPositioned::INVALID;
855 string ident = naStr_data(args[argOffset++]);
856 if (argOffset < argc) {
857 type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
860 naRef r = naNewVector(c);
861 nav_list_type navs = globals->get_navlist()->findByIdentAndFreq(pos, ident, 0.0, type);
863 BOOST_FOREACH(nav_rec_ptr a, navs) {
864 naVec_append(r, hashForNavRecord(c, a.ptr(), pos));
871 // Convert a cartesian point to a geodetic lat/lon/altitude.
872 static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
874 SGGeod pos = globals->get_aircraft_position();
876 // fine, use aircraft position
877 } else if (geodFromArgs(args, 0, argc, pos)) {
880 naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
883 double jd = globals->get_time_params()->getJD();
884 double magvarDeg = sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
885 return naNum(magvarDeg);
888 static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
890 SGGeod from = globals->get_aircraft_position(), to, p;
891 int argOffset = geodFromArgs(args, 0, argc, p);
892 if (geodFromArgs(args, argOffset, argc, to)) {
893 from = p; // we parsed both FROM and TO args, so first was from
895 to = p; // only parsed one arg, so FROM is current
898 if (argOffset == 0) {
899 naRuntimeError(c, "invalid arguments to courseAndDistance");
902 double course, course2, d;
903 SGGeodesy::inverse(from, to, course, course2, d);
905 naRef result = naNewVector(c);
906 naVec_append(result, naNum(course));
907 naVec_append(result, naNum(d * SG_METER_TO_NM));
911 static naRef f_greatCircleMove(naContext c, naRef me, int argc, naRef* args)
913 SGGeod from = globals->get_aircraft_position(), to;
916 // complication - don't inerpret two doubles (as the only args)
917 // as a lat,lon pair - only do so if we have at least three args.
919 argOffset = geodFromArgs(args, 0, argc, from);
922 if ((argOffset + 1) >= argc) {
923 naRuntimeError(c, "isufficent arguments to greatCircleMove");
926 if (!naIsNum(args[argOffset]) || !naIsNum(args[argOffset+1])) {
927 naRuntimeError(c, "invalid arguments %d and %d to greatCircleMove",
928 argOffset, argOffset + 1);
931 double course = args[argOffset].num, course2;
932 double distanceNm = args[argOffset + 1].num;
933 SGGeodesy::direct(from, course, distanceNm * SG_NM_TO_METER, to, course2);
936 naRef coord = naNewHash(c);
937 hashset(c, coord, "lat", naNum(to.getLatitudeDeg()));
938 hashset(c, coord, "lon", naNum(to.getLongitudeDeg()));
942 static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
944 SGGeod pos = globals->get_aircraft_position();
945 geodFromArgs(args, 0, argc, pos);
947 return stringToNasal(c, b.gen_base_path());
950 static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
952 SGGeod pos = globals->get_aircraft_position();
953 geodFromArgs(args, 0, argc, pos);
955 return naNum(b.gen_index());
958 static naRef f_route(naContext c, naRef me, int argc, naRef* args)
960 naRef route = naNewHash(c);
962 // return active route hash by default,
963 // other routes in the future
965 naRef parents = naNewVector(c);
966 naVec_append(parents, routePrototype);
967 hashset(c, route, "parents", parents);
972 static naRef f_route_getWP(naContext c, naRef me, int argc, naRef* args)
974 FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
978 index = rm->currentIndex();
980 index = (int) naNumValue(args[0]).num;
983 if ((index < 0) || (index >= rm->numWaypts())) {
987 return ghostForWaypt(c, rm->wayptAtIndex(index));
990 static naRef f_route_currentWP(naContext c, naRef me, int argc, naRef* args)
992 FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
993 return ghostForWaypt(c, rm->currentWaypt());
996 static naRef f_route_nextWP(naContext c, naRef me, int argc, naRef* args)
998 FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
999 flightgear::WayptRef wp = rm->nextWaypt();
1003 return ghostForWaypt(c, wp);
1006 static naRef f_route_currentIndex(naContext c, naRef me, int argc, naRef* args)
1008 FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
1009 return naNum(rm->currentIndex());
1012 static naRef f_route_numWaypoints(naContext c, naRef me, int argc, naRef* args)
1014 FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
1015 return naNum(rm->numWaypts());
1018 static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args)
1020 flightgear::Waypt* w = wayptGhost(me);
1022 naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1025 FGPositioned* pos = w->source();
1030 switch (pos->type()) {
1031 case FGPositioned::VOR:
1032 case FGPositioned::NDB:
1033 case FGPositioned::ILS:
1034 case FGPositioned::LOC:
1035 case FGPositioned::GS:
1036 case FGPositioned::DME:
1037 case FGPositioned::TACAN: {
1038 FGNavRecord* nav = (FGNavRecord*) pos;
1039 return hashForNavRecord(c, nav, globals->get_aircraft_position());
1047 static naRef f_waypoint_airport(naContext c, naRef me, int argc, naRef* args)
1049 flightgear::Waypt* w = wayptGhost(me);
1051 naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1054 FGPositioned* pos = w->source();
1055 if (!pos || FGAirport::isAirportType(pos)) {
1059 return hashForAirport(c, (FGAirport*) pos);
1062 static naRef f_waypoint_runway(naContext c, naRef me, int argc, naRef* args)
1064 flightgear::Waypt* w = wayptGhost(me);
1066 naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1069 FGPositioned* pos = w->source();
1070 if (!pos || (pos->type() != FGPositioned::RUNWAY)) {
1074 return hashForRunway(c, (FGRunway*) pos);
1077 // Table of extension functions. Terminate with zeros.
1078 static struct { const char* name; naCFunction func; } funcs[] = {
1079 { "carttogeod", f_carttogeod },
1080 { "geodtocart", f_geodtocart },
1081 { "geodinfo", f_geodinfo },
1082 { "airportinfo", f_airportinfo },
1083 { "findAirportsWithinRange", f_findAirportsWithinRange },
1084 { "findAirportsByICAO", f_findAirportsByICAO },
1085 { "navinfo", f_navinfo },
1086 { "findNavaidsWithinRange", f_findNavaidsWithinRange },
1087 { "findNavaidByFrequency", f_findNavaidByFrequency },
1088 { "findNavaidsByFrequency", f_findNavaidsByFrequency },
1089 { "findNavaidsByID", f_findNavaidsByIdent },
1090 { "route", f_route },
1091 { "magvar", f_magvar },
1092 { "courseAndDistance", f_courseAndDistance },
1093 { "greatCircleMove", f_greatCircleMove },
1094 { "tileIndex", f_tileIndex },
1095 { "tilePath", f_tilePath },
1100 naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
1102 airportPrototype = naNewHash(c);
1103 hashset(c, gcSave, "airportProto", airportPrototype);
1105 hashset(c, airportPrototype, "runway", naNewFunc(c, naNewCCode(c, f_airport_runway)));
1106 hashset(c, airportPrototype, "tower", naNewFunc(c, naNewCCode(c, f_airport_tower)));
1107 hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
1108 hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
1109 hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
1110 hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking)));
1112 routePrototype = naNewHash(c);
1113 hashset(c, gcSave, "routeProto", routePrototype);
1115 hashset(c, routePrototype, "getWP", naNewFunc(c, naNewCCode(c, f_route_getWP)));
1116 hashset(c, routePrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_route_currentWP)));
1117 hashset(c, routePrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_route_nextWP)));
1118 hashset(c, routePrototype, "currentIndex", naNewFunc(c, naNewCCode(c, f_route_currentIndex)));
1119 hashset(c, routePrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_route_numWaypoints)));
1121 waypointPrototype = naNewHash(c);
1122 hashset(c, gcSave, "wayptProto", waypointPrototype);
1124 hashset(c, waypointPrototype, "navaid", naNewFunc(c, naNewCCode(c, f_waypoint_navaid)));
1125 hashset(c, waypointPrototype, "runway", naNewFunc(c, naNewCCode(c, f_waypoint_runway)));
1126 hashset(c, waypointPrototype, "airport", naNewFunc(c, naNewCCode(c, f_waypoint_airport)));
1128 for(int i=0; funcs[i].name; i++) {
1129 hashset(c, globals, funcs[i].name,
1130 naNewFunc(c, naNewCCode(c, funcs[i].func)));
1136 void postinitNasalPositioned(naRef globals, naContext c)
1138 naRef geoModule = naHash_cget(globals, (char*) "geo");
1139 if (naIsNil(geoModule)) {
1140 SG_LOG(SG_GENERAL, SG_WARN, "postinitNasalPositioned: geo.nas not loaded");
1144 geoCoordClass = naHash_cget(geoModule, (char*) "Coord");