]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned.cxx
5105a283c235fde5221d90700a90b7a2123d8677
[flightgear.git] / src / Scripting / NasalPositioned.cxx
1 // NasalPositioned.cxx -- expose FGPositioned classes to Nasal
2 //
3 // Written by James Turner, started 2012.
4 //
5 // Copyright (C) 2012 James Turner
6 //
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.
11 //
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.
16 //
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.
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include <string.h>
26
27 #include "NasalPositioned.hxx"
28
29 #include <boost/foreach.hpp>
30
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>
35
36 #include <Airports/runways.hxx>
37 #include <Airports/simple.hxx>
38 #include <Navaids/navlist.hxx>
39 #include <Navaids/procedure.hxx>
40 #include <Main/globals.hxx>
41 #include <Main/fg_props.hxx>
42 #include <Scenery/scenery.hxx>
43 #include <ATC/CommStation.hxx>
44 #include <Navaids/route.hxx>
45 #include <Autopilot/route_mgr.hxx>
46 #include <Navaids/procedure.hxx>
47
48 static void sgrefGhostDestroy(void* g);
49 naGhostType PositionedGhostType = { sgrefGhostDestroy, "positioned" };
50 naGhostType WayptGhostType = { sgrefGhostDestroy, "waypoint" };
51
52 static void hashset(naContext c, naRef hash, const char* key, naRef val)
53 {
54   naRef s = naNewString(c);
55   naStr_fromdata(s, (char*)key, strlen(key));
56   naHash_set(hash, s, val);
57 }
58
59 static naRef stringToNasal(naContext c, const std::string& s)
60 {
61     return naStr_fromdata(naNewString(c),
62                    const_cast<char *>(s.c_str()), 
63                    s.length());
64 }
65
66 static FGPositioned* positionedGhost(naRef r)
67 {
68     if (naGhost_type(r) == &PositionedGhostType)
69         return (FGPositioned*) naGhost_ptr(r);
70     return 0;
71 }
72
73 static flightgear::Waypt* wayptGhost(naRef r)
74 {
75   if (naGhost_type(r) == &WayptGhostType)
76     return (flightgear::Waypt*) naGhost_ptr(r);
77   return 0;
78 }
79
80 static void sgrefGhostDestroy(void* g)
81 {
82     SGReferenced* ref = (SGReferenced*)g;
83     SGReferenced::put(ref); // unref
84 }
85
86 static naRef airportPrototype;
87 static naRef routePrototype;
88 static naRef waypointPrototype;
89
90 naRef ghostForPositioned(naContext c, const FGPositioned* pos)
91 {
92     if (!pos) {
93         return naNil();
94     }
95     
96     SGReferenced::get(pos); // take a ref
97     return naNewGhost(c, &PositionedGhostType, (void*) pos);
98 }
99
100 naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt)
101 {
102   if (!wpt) {
103     return naNil();
104   }
105   
106   SGReferenced::get(wpt); // take a ref
107   return naNewGhost(c, &WayptGhostType, (void*) wpt);
108 }
109
110 naRef hashForAirport(naContext c, const FGAirport* apt)
111 {
112     std::string id = apt->ident();
113     std::string name = apt->name();
114     
115   // build runways hash
116     naRef rwys = naNewHash(c);
117     for(unsigned int r=0; r<apt->numRunways(); ++r) {
118       FGRunway* rwy(apt->getRunwayByIndex(r));
119       naRef rwyid = stringToNasal(c, rwy->ident());
120       naRef rwydata = hashForRunway(c, rwy);
121       naHash_set(rwys, rwyid, rwydata);
122     }
123   
124     naRef aptdata = naNewHash(c);
125     hashset(c, aptdata, "id", stringToNasal(c, id));
126     hashset(c, aptdata, "name", stringToNasal(c, name));
127     hashset(c, aptdata, "lat", naNum(apt->getLatitude()));
128     hashset(c, aptdata, "lon", naNum(apt->getLongitude()));
129     hashset(c, aptdata, "elevation", naNum(apt->getElevation() * SG_FEET_TO_METER));
130     hashset(c, aptdata, "has_metar", naNum(apt->getMetar()));
131     hashset(c, aptdata, "runways", rwys);
132     hashset(c, aptdata, "_positioned", ghostForPositioned(c, apt));
133     naRef parents = naNewVector(c);
134     naVec_append(parents, airportPrototype);
135     hashset(c, aptdata, "parents", parents);
136     
137     return aptdata;
138 }
139
140 naRef hashForWaypoint(naContext c, flightgear::Waypt* wpt, flightgear::Waypt* next)
141 {
142   SGGeod pos = wpt->position();
143   naRef h = naNewHash(c);
144   
145   flightgear::Procedure* proc = dynamic_cast<flightgear::Procedure*>(wpt->owner());
146   if (proc) {
147     hashset(c, h, "wp_parent_name", stringToNasal(c, proc->ident()));
148   }
149
150   if (wpt->type() == "hold") {
151     hashset(c, h, "fly_type", stringToNasal(c, "Hold"));
152   } else if (wpt->flag(flightgear::WPT_OVERFLIGHT)) {
153     hashset(c, h, "fly_type", stringToNasal(c, "flyOver"));
154   } else {
155     hashset(c, h, "fly_type", stringToNasal(c, "flyBy"));
156   }
157   
158   hashset(c, h, "wp_type", stringToNasal(c, wpt->type()));
159   hashset(c, h, "wp_name", stringToNasal(c, wpt->ident()));
160   hashset(c, h, "wp_lat", naNum(pos.getLatitudeDeg()));
161   hashset(c, h, "wp_lon", naNum(pos.getLongitudeDeg()));
162   hashset(c, h, "alt_cstr", naNum(wpt->altitudeFt()));
163   
164   if (wpt->speedRestriction() == flightgear::SPEED_RESTRICT_MACH) {
165     hashset(c, h, "spd_cstr", naNum(wpt->speedMach()));
166   } else {
167     hashset(c, h, "spd_cstr", naNum(wpt->speedKts()));
168   }
169   
170   if (next) {
171     std::pair<double, double> crsDist =
172       next->courseAndDistanceFrom(pos);
173     hashset(c, h, "leg_distance", naNum(crsDist.second * SG_METER_TO_NM));
174     hashset(c, h, "leg_bearing", naNum(crsDist.first));
175     hashset(c, h, "hdg_radial", naNum(crsDist.first));
176   }
177   
178 // leg bearing, distance, etc
179   
180   
181 // parents and ghost of the C++ object
182   hashset(c, h, "_waypt", ghostForWaypt(c, wpt));
183   naRef parents = naNewVector(c);
184   naVec_append(parents, waypointPrototype);
185   hashset(c, h, "parents", parents);
186   
187   return h;
188
189 }
190
191 naRef hashForRunway(naContext c, FGRunway* rwy)
192 {
193     naRef rwyid = stringToNasal(c, rwy->ident());
194     naRef rwydata = naNewHash(c);
195 #define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
196     HASHSET("id", 2, rwyid);
197     HASHSET("lat", 3, naNum(rwy->latitude()));
198     HASHSET("lon", 3, naNum(rwy->longitude()));
199     HASHSET("heading", 7, naNum(rwy->headingDeg()));
200     HASHSET("length", 6, naNum(rwy->lengthM()));
201     HASHSET("width", 5, naNum(rwy->widthM()));
202     HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
203     HASHSET("stopway", 7, naNum(rwy->stopwayM()));
204         
205     if (rwy->ILS()) {
206       HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
207       HASHSET("ils", 3, hashForNavRecord(c, rwy->ILS(), SGGeod()));
208     }
209     
210     HASHSET("_positioned", 11, ghostForPositioned(c, rwy));
211 #undef HASHSET
212     return rwydata;
213 }
214
215 naRef hashForNavRecord(naContext c, const FGNavRecord* nav, const SGGeod& rel)
216 {
217     naRef navdata = naNewHash(c);
218 #define HASHSET(s,l,n) naHash_set(navdata, naStr_fromdata(naNewString(c),s,l),n)
219     HASHSET("id", 2, stringToNasal(c, nav->ident()));
220     HASHSET("name", 4, stringToNasal(c, nav->name()));
221     HASHSET("frequency", 9, naNum(nav->get_freq()));
222     HASHSET("lat", 3, naNum(nav->get_lat()));
223     HASHSET("lon", 3, naNum(nav->get_lon()));
224     HASHSET("elevation", 9, naNum(nav->get_elev_ft() * SG_FEET_TO_METER));
225     HASHSET("type", 4, stringToNasal(c, nav->nameForType(nav->type())));
226     
227 // FIXME - get rid of these, people should use courseAndDistance instead
228     HASHSET("distance", 8, naNum(SGGeodesy::distanceNm( rel, nav->geod() ) * SG_NM_TO_METER ) );
229     HASHSET("bearing", 7, naNum(SGGeodesy::courseDeg( rel, nav->geod() ) ) );
230     
231     // record the real object as a ghost for further operations
232     HASHSET("_positioned",11, ghostForPositioned(c, nav));
233 #undef HASHSET
234     
235     return navdata;
236 }
237
238 bool geodFromHash(naRef ref, SGGeod& result)
239 {
240   if (!naIsHash(ref)) {
241     return false;
242   }
243   
244 // first, see if the hash contains a FGPositioned ghost - in which case
245 // we can read off its position directly
246   naRef posGhost = naHash_cget(ref, (char*) "_positioned");
247   if (!naIsNil(posGhost)) {
248     FGPositioned* pos = positionedGhost(posGhost);
249     result = pos->geod();
250     return true;
251   }
252   
253   naRef ghost = naHash_cget(ref, (char*) "_waypt");
254   if (!naIsNil(ghost)) {
255     flightgear::Waypt* w = wayptGhost(ghost);
256     result = w->position();
257     return true;
258   }
259   
260 // then check for manual latitude / longitude names
261   naRef lat = naHash_cget(ref, (char*) "lat");
262   naRef lon = naHash_cget(ref, (char*) "lon");
263   if (naIsNum(lat) && naIsNum(lon)) {
264     result = SGGeod::fromDeg(naNumValue(lat).num, naNumValue(lon).num);
265     return true;
266   }
267   
268 // check for geo.Coord type
269     
270 // check for any synonyms?
271     // latitude + longitude?
272   
273   return false;
274 }
275
276 // Convert a cartesian point to a geodetic lat/lon/altitude.
277 static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
278 {
279   double lat, lon, alt, xyz[3];
280   if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
281   for(int i=0; i<3; i++)
282     xyz[i] = naNumValue(args[i]).num;
283   sgCartToGeod(xyz, &lat, &lon, &alt);
284   lat *= SG_RADIANS_TO_DEGREES;
285   lon *= SG_RADIANS_TO_DEGREES;
286   naRef vec = naNewVector(c);
287   naVec_append(vec, naNum(lat));
288   naVec_append(vec, naNum(lon));
289   naVec_append(vec, naNum(alt));
290   return vec;
291 }
292
293 // Convert a geodetic lat/lon/altitude to a cartesian point.
294 static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
295 {
296   if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
297   double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
298   double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
299   double alt = naNumValue(args[2]).num;
300   double xyz[3];
301   sgGeodToCart(lat, lon, alt, xyz);
302   naRef vec = naNewVector(c);
303   naVec_append(vec, naNum(xyz[0]));
304   naVec_append(vec, naNum(xyz[1]));
305   naVec_append(vec, naNum(xyz[2]));
306   return vec;
307 }
308
309 // For given geodetic point return array with elevation, and a material data
310 // hash, or nil if there's no information available (tile not loaded). If
311 // information about the material isn't available, then nil is returned instead
312 // of the hash.
313 static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
314 {
315 #define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
316   if(argc < 2 || argc > 3)
317     naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
318   double lat = naNumValue(args[0]).num;
319   double lon = naNumValue(args[1]).num;
320   double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
321   const SGMaterial *mat;
322   SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
323   if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
324     return naNil();
325   naRef vec = naNewVector(c);
326   naVec_append(vec, naNum(elev));
327   naRef matdata = naNil();
328   if(mat) {
329     matdata = naNewHash(c);
330     naRef names = naNewVector(c);
331     BOOST_FOREACH(const std::string& n, mat->get_names())
332       naVec_append(names, stringToNasal(c, n));
333       
334     HASHSET("names", 5, names);
335     HASHSET("solid", 5, naNum(mat->get_solid()));
336     HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
337     HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
338     HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
339     HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
340     HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
341   }
342   naVec_append(vec, matdata);
343   return vec;
344 #undef HASHSET
345 }
346
347
348 class AirportInfoFilter : public FGAirport::AirportFilter
349 {
350 public:
351   AirportInfoFilter() : type(FGPositioned::AIRPORT) {
352   }
353   
354   virtual FGPositioned::Type minType() const {
355     return type;
356   }
357   
358   virtual FGPositioned::Type maxType() const {
359     return type;
360   }
361   
362   FGPositioned::Type type;
363 };
364
365 // Returns data hash for particular or nearest airport of a <type>, or nil
366 // on error.
367 //
368 // airportinfo(<id>);                   e.g. "KSFO"
369 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
370 // airportinfo()                        same as  airportinfo("airport")
371 // airportinfo(<lat>, <lon> [, <type>]);
372 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
373 {
374   SGGeod pos;
375   FGAirport* apt = NULL;
376   
377   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
378     pos = SGGeod::fromDeg(args[1].num, args[0].num);
379     args += 2;
380     argc -= 2;
381   } else {
382     pos = globals->get_aircraft_position();
383   }
384   
385   double maxRange = 10000.0; // expose this? or pick a smaller value?
386   
387   AirportInfoFilter filter; // defaults to airports only
388   
389   if(argc == 0) {
390     // fall through and use AIRPORT
391   } else if(argc == 1 && naIsString(args[0])) {
392     const char *s = naStr_data(args[0]);
393     if(!strcmp(s, "airport")) filter.type = FGPositioned::AIRPORT;
394     else if(!strcmp(s, "seaport")) filter.type = FGPositioned::SEAPORT;
395     else if(!strcmp(s, "heliport")) filter.type = FGPositioned::HELIPORT;
396     else {
397       // user provided an <id>, hopefully
398       apt = FGAirport::findByIdent(s);
399       if (!apt) {
400         // return nil here, but don't raise a runtime error; this is a
401         // legitamate way to validate an ICAO code, for example in a
402         // dialog box or similar.
403         return naNil();
404       }
405     }
406   } else {
407     naRuntimeError(c, "airportinfo() with invalid function arguments");
408     return naNil();
409   }
410   
411   if(!apt) {
412     apt = FGAirport::findClosest(pos, maxRange, &filter);
413     if(!apt) return naNil();
414   }
415   
416   return hashForAirport(c, apt);
417 }
418
419 static FGAirport* airportFromMe(naRef me)
420 {  
421     naRef ghost = naHash_cget(me, (char*) "_positioned");
422     if (naIsNil(ghost)) {
423         return NULL;
424     }
425   
426     FGPositioned* pos = positionedGhost(ghost);
427     if (pos && FGAirport::isAirportType(pos)) {
428         return (FGAirport*) pos;
429     }
430
431     return NULL;
432 }
433
434 static naRef f_airport_tower(naContext c, naRef me, int argc, naRef* args)
435 {
436     FGAirport* apt = airportFromMe(me);
437     if (!apt) {
438       naRuntimeError(c, "airport.tower called on non-airport object");
439     }
440   
441     // build a hash for the tower position    
442     SGGeod towerLoc = apt->getTowerLocation();
443     naRef tower = naNewHash(c);
444     hashset(c, tower, "lat", naNum(towerLoc.getLatitudeDeg()));
445     hashset(c, tower, "lon", naNum(towerLoc.getLongitudeDeg()));
446     hashset(c, tower, "elevation", naNum(towerLoc.getElevationM()));
447     return tower;
448 }
449
450 static naRef f_airport_comms(naContext c, naRef me, int argc, naRef* args)
451 {
452     FGAirport* apt = airportFromMe(me);
453     if (!apt) {
454       naRuntimeError(c, "airport.comms called on non-airport object");
455     }
456     naRef comms = naNewVector(c);
457     
458 // if we have an explicit type, return a simple vector of frequencies
459     if (argc > 0 && naIsScalar(args[0])) {
460         std::string commName = naStr_data(args[0]);
461         FGPositioned::Type commType = FGPositioned::typeFromName(commName);
462         
463         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStationsOfType(commType)) {
464             naVec_append(comms, naNum(comm->freqMHz()));
465         }
466     } else {
467 // otherwise return a vector of hashes, one for each comm station.
468         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStations()) {
469             naRef commHash = naNewHash(c);
470             hashset(c, commHash, "frequency", naNum(comm->freqMHz()));
471             hashset(c, commHash, "ident", stringToNasal(c, comm->ident()));
472             naVec_append(comms, commHash);
473         }
474     }
475     
476     return comms;
477 }
478
479 static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
480 {
481   FGAirport* apt = airportFromMe(me);
482   if (!apt) {
483     naRuntimeError(c, "airport.sids called on non-airport object");
484   }
485   
486   naRef sids = naNewVector(c);
487   
488   // if we have an explicit type, return a simple vector of frequencies
489   if (argc > 0 && naIsString(args[0])) {
490     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
491       return naNil();
492     }
493
494     FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
495     BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) {
496       naRef procId = stringToNasal(c, sid->ident());
497       naVec_append(sids, procId);
498     }
499   } else {
500     for (unsigned int s=0; s<apt->numSIDs(); ++s) {
501       flightgear::SID* sid = apt->getSIDByIndex(s);
502       naRef procId = stringToNasal(c, sid->ident());
503       naVec_append(sids, procId);
504     }
505   }
506   
507   return sids;
508 }
509
510 static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
511 {
512   FGAirport* apt = airportFromMe(me);
513   if (!apt) {
514     naRuntimeError(c, "airport.stars called on non-airport object");
515   }
516   
517   naRef stars = naNewVector(c);
518   
519   // if we have an explicit type, return a simple vector of frequencies
520   if (argc > 0 && naIsString(args[0])) {
521     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
522       return naNil();
523     }
524         
525     FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
526     BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) {
527       naRef procId = stringToNasal(c, s->ident());
528       naVec_append(stars, procId);
529     }
530   } else {
531     for (unsigned int s=0; s<apt->numSTARs(); ++s) {
532       flightgear::STAR* star = apt->getSTARByIndex(s);
533       naRef procId = stringToNasal(c, star->ident());
534       naVec_append(stars, procId);
535     }
536   }
537   
538   return stars;
539 }
540
541 // Returns vector of data hash for navaid of a <type>, nil on error
542 // navaids sorted by ascending distance 
543 // navinfo([<lat>,<lon>],[<type>],[<id>])
544 // lat/lon (numeric): use latitude/longitude instead of ac position
545 // type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
546 // id:                (partial) id of the fix
547 // examples:
548 // navinfo("vor")     returns all vors
549 // navinfo("HAM")     return all navaids who's name start with "HAM"
550 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
551 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM" 
552 //                           sorted by distance relative to lat=34, lon=48
553 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
554 {
555   SGGeod pos;
556   
557   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
558     pos = SGGeod::fromDeg(args[1].num, args[0].num);
559     args += 2;
560     argc -= 2;
561   } else {
562     pos = globals->get_aircraft_position();
563   }
564   
565   FGPositioned::Type type = FGPositioned::INVALID;
566   nav_list_type navlist;
567   const char * id = "";
568   
569   if(argc > 0 && naIsString(args[0])) {
570     const char *s = naStr_data(args[0]);
571     if(!strcmp(s, "any")) type = FGPositioned::INVALID;
572     else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
573     else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
574     else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
575     else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
576     else if(!strcmp(s, "dme")) type = FGPositioned::DME;
577     else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
578     else id = s; // this is an id
579     ++args;
580     --argc;
581   } 
582   
583   if(argc > 0 && naIsString(args[0])) {
584     if( *id != 0 ) {
585       naRuntimeError(c, "navinfo() called with navaid id");
586       return naNil();
587     }
588     id = naStr_data(args[0]);
589     ++args;
590     --argc;
591   }
592   
593   if( argc > 0 ) {
594     naRuntimeError(c, "navinfo() called with too many arguments");
595     return naNil();
596   }
597   
598   navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
599   
600   naRef reply = naNewVector(c);
601   for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
602     naVec_append( reply, hashForNavRecord(c, *it, pos) );
603   }
604   return reply;
605 }
606
607 // Convert a cartesian point to a geodetic lat/lon/altitude.
608 static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
609 {
610   SGGeod pos = globals->get_aircraft_position();
611   if (argc == 0) {
612     // fine, use aircraft position
613   } else if ((argc == 1) && geodFromHash(args[0], pos)) {
614     // okay
615   } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
616     double lat = naNumValue(args[0]).num,
617       lon = naNumValue(args[1]).num;
618     pos = SGGeod::fromDeg(lon, lat);
619   } else {
620     naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
621   }
622   
623   double jd = globals->get_time_params()->getJD();
624   double magvarDeg = sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
625   return naNum(magvarDeg);
626 }
627
628 static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
629 {
630     SGGeod from = globals->get_aircraft_position(), to;
631     if ((argc == 1) && geodFromHash(args[0], to)) {
632         // done
633     } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
634         // two number arguments, from = current pos, to = lat+lon
635         double lat = naNumValue(args[0]).num,
636             lon = naNumValue(args[1]).num;
637         to = SGGeod::fromDeg(lon, lat);
638     } else if ((argc == 2) && geodFromHash(args[0], from) && geodFromHash(args[1], to)) {
639         // done
640     } else if ((argc == 3) && geodFromHash(args[0], from) && naIsNum(args[1]) && naIsNum(args[2])) {
641         double lat = naNumValue(args[1]).num,
642             lon = naNumValue(args[2]).num;
643         to = SGGeod::fromDeg(lon, lat);
644     } else if ((argc == 3) && naIsNum(args[0]) && naIsNum(args[1]) && geodFromHash(args[2], to)) {
645         double lat = naNumValue(args[0]).num,
646             lon = naNumValue(args[1]).num;
647         from = SGGeod::fromDeg(lon, lat);
648     } else if (argc == 4) {
649         if (!naIsNum(args[0]) || !naIsNum(args[1]) || !naIsNum(args[2]) || !naIsNum(args[3])) {
650             naRuntimeError(c, "invalid arguments to courseAndDistance - expected four numbers");
651         }
652         
653         from = SGGeod::fromDeg(naNumValue(args[1]).num, naNumValue(args[0]).num);
654         to = SGGeod::fromDeg(naNumValue(args[3]).num, naNumValue(args[2]).num);
655     } else {
656         naRuntimeError(c, "invalid arguments to courseAndDistance");
657     }
658     
659     double course, course2, d;
660     SGGeodesy::inverse(from, to, course, course2, d);
661     
662     naRef result = naNewVector(c);
663     naVec_append(result, naNum(course));
664     naVec_append(result, naNum(d * SG_METER_TO_NM));
665     return result;
666 }
667
668 static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
669 {
670     SGGeod pos = globals->get_aircraft_position();
671     if (argc == 0) {
672         // fine, use aircraft position
673     } else if ((argc == 1) && geodFromHash(args[0], pos)) {
674         // okay
675     } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
676         double lat = naNumValue(args[0]).num,
677         lon = naNumValue(args[1]).num;
678         pos = SGGeod::fromDeg(lon, lat);
679     } else {
680         naRuntimeError(c, "bucketPath() expects no arguments, a positioned hash or lat,lon pair");
681     }
682     
683     SGBucket b(pos);
684     return stringToNasal(c, b.gen_base_path());
685 }
686
687 static naRef f_route(naContext c, naRef me, int argc, naRef* args)
688 {
689   naRef route = naNewHash(c);
690   
691   // return active route hash by default,
692   // other routes in the future
693   
694   naRef parents = naNewVector(c);
695   naVec_append(parents, routePrototype);
696   hashset(c, route, "parents", parents);
697   
698   return route;
699 }
700
701 static naRef f_route_getWP(naContext c, naRef me, int argc, naRef* args)
702 {
703   FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
704   
705   int index;
706   if (argc == 0) {
707     index = rm->currentIndex();
708   } else {
709     index = (int) naNumValue(args[0]).num;
710   }
711   
712   if ((index < 0) || (index >= rm->numWaypts())) {
713     return naNil();
714   }
715   
716   flightgear::Waypt* next = NULL;
717   if (index < (rm->numWaypts() - 1)) {
718     next = rm->wayptAtIndex(index + 1);
719   }
720   return hashForWaypoint(c, rm->wayptAtIndex(index), next);
721 }
722
723 static naRef f_route_currentWP(naContext c, naRef me, int argc, naRef* args)
724 {
725   FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
726   flightgear::Waypt* next = NULL;
727   if (rm->currentIndex() < (rm->numWaypts() - 1)) {
728     next = rm->wayptAtIndex(rm->currentIndex() + 1);
729   }
730   return hashForWaypoint(c, rm->currentWaypt(), next);
731 }
732
733 static naRef f_route_currentIndex(naContext c, naRef me, int argc, naRef* args)
734 {
735   FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
736   return naNum(rm->currentIndex());
737 }
738
739 static naRef f_route_numWaypoints(naContext c, naRef me, int argc, naRef* args)
740 {
741   FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
742   return naNum(rm->numWaypts());
743 }
744
745 // Table of extension functions.  Terminate with zeros.
746 static struct { const char* name; naCFunction func; } funcs[] = {
747   { "carttogeod", f_carttogeod },
748   { "geodtocart", f_geodtocart },
749   { "geodinfo", f_geodinfo },
750   { "airportinfo", f_airportinfo },
751   { "navinfo", f_navinfo },
752   { "route", f_route },
753   { "magvar", f_magvar },
754   { "courseAndDistance", f_courseAndDistance },
755   { "bucketPath", f_tilePath },
756   { 0, 0 }
757 };
758
759
760 naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
761 {
762     airportPrototype = naNewHash(c);
763     hashset(c, gcSave, "airportProto", airportPrototype);
764   
765     hashset(c, airportPrototype, "tower", naNewFunc(c, naNewCCode(c, f_airport_tower)));
766     hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
767     hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
768     hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
769   
770     routePrototype = naNewHash(c);
771     hashset(c, gcSave, "routeProto", routePrototype);
772     
773     hashset(c, routePrototype, "getWP", naNewFunc(c, naNewCCode(c, f_route_getWP)));
774     hashset(c, routePrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_route_currentWP)));
775     hashset(c, routePrototype, "currentIndex", naNewFunc(c, naNewCCode(c, f_route_currentIndex)));
776     hashset(c, routePrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_route_numWaypoints)));
777     
778     for(int i=0; funcs[i].name; i++) {
779       hashset(c, globals, funcs[i].name,
780             naNewFunc(c, naNewCCode(c, funcs[i].func)));
781     }
782   
783   return naNil();
784 }
785