]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned.cxx
208c34e8fef66fb6075fb562fe8a93d9578f34d2
[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 #include <boost/algorithm/string/case_conv.hpp>
31
32 #include <simgear/sg_inlines.h>
33 #include <simgear/scene/material/mat.hxx>
34 #include <simgear/magvar/magvar.hxx>
35 #include <simgear/timing/sg_time.hxx>
36 #include <simgear/bucket/newbucket.hxx>
37
38 #include <Airports/runways.hxx>
39 #include <Airports/simple.hxx>
40 #include <Airports/dynamics.hxx>
41 #include <Airports/parking.hxx>
42 #include <Navaids/navlist.hxx>
43 #include <Navaids/procedure.hxx>
44 #include <Main/globals.hxx>
45 #include <Main/fg_props.hxx>
46 #include <Scenery/scenery.hxx>
47 #include <ATC/CommStation.hxx>
48 #include <Navaids/route.hxx>
49 #include <Autopilot/route_mgr.hxx>
50 #include <Navaids/procedure.hxx>
51
52 using namespace flightgear;
53
54 static void positionedGhostDestroy(void* g);
55 static void wayptGhostDestroy(void* g);
56 static void legGhostDestroy(void* g);
57 static void routeBaseGhostDestroy(void* g);
58
59 naGhostType PositionedGhostType = { positionedGhostDestroy, "positioned" };
60
61 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out);
62 naGhostType AirportGhostType = { positionedGhostDestroy, "airport", airportGhostGetMember, 0 };
63
64 static const char* navaidGhostGetMember(naContext c, void* g, naRef field, naRef* out);
65 naGhostType NavaidGhostType = { positionedGhostDestroy, "navaid", navaidGhostGetMember, 0 };
66
67 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out);
68 naGhostType RunwayGhostType = { positionedGhostDestroy, "runway", runwayGhostGetMember, 0 };
69
70 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
71 naGhostType WayptGhostType = { wayptGhostDestroy, 
72   "waypoint",
73   wayptGhostGetMember,
74   0};
75
76 static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out);
77 naGhostType FPLegGhostType = { legGhostDestroy, 
78   "flightplan-leg",
79   legGhostGetMember,
80   0};
81
82 static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out);
83 static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value);
84
85 naGhostType FlightPlanGhostType = { routeBaseGhostDestroy, 
86   "flightplan",
87   flightplanGhostGetMember,
88   flightplanGhostSetMember
89 };
90
91 static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out);
92 naGhostType ProcedureGhostType = { routeBaseGhostDestroy, 
93   "procedure",
94   procedureGhostGetMember,
95   0};
96
97 static void hashset(naContext c, naRef hash, const char* key, naRef val)
98 {
99   naRef s = naNewString(c);
100   naStr_fromdata(s, (char*)key, strlen(key));
101   naHash_set(hash, s, val);
102 }
103
104 static naRef stringToNasal(naContext c, const std::string& s)
105 {
106     return naStr_fromdata(naNewString(c),
107                    const_cast<char *>(s.c_str()), 
108                    s.length());
109 }
110
111 static FGPositioned* positionedGhost(naRef r)
112 {
113     if (naGhost_type(r) == &PositionedGhostType)
114         return (FGPositioned*) naGhost_ptr(r);
115     return 0;
116 }
117
118 static FGAirport* airportGhost(naRef r)
119 {
120   if (naGhost_type(r) == &AirportGhostType)
121     return (FGAirport*) naGhost_ptr(r);
122   return 0;
123 }
124
125 static FGNavRecord* navaidGhost(naRef r)
126 {
127   if (naGhost_type(r) == &NavaidGhostType)
128     return (FGNavRecord*) naGhost_ptr(r);
129   return 0;
130 }
131
132 static FGRunway* runwayGhost(naRef r)
133 {
134   if (naGhost_type(r) == &RunwayGhostType)
135     return (FGRunway*) naGhost_ptr(r);
136   return 0;
137 }
138
139 static void positionedGhostDestroy(void* g)
140 {
141     FGPositioned* pos = (FGPositioned*)g;
142     if (!FGPositioned::put(pos)) // unref
143         delete pos;
144 }
145
146 static Waypt* wayptGhost(naRef r)
147 {
148   if (naGhost_type(r) == &WayptGhostType)
149     return (Waypt*) naGhost_ptr(r);
150   return 0;
151 }
152
153 static void wayptGhostDestroy(void* g)
154 {
155   Waypt* wpt = (Waypt*)g;
156   if (!Waypt::put(wpt)) // unref
157     delete wpt;
158 }
159
160 static void legGhostDestroy(void* g)
161 {
162   // nothing for now
163 }
164
165
166 static FlightPlan::Leg* fpLegGhost(naRef r)
167 {
168   if (naGhost_type(r) == &FPLegGhostType)
169     return (FlightPlan::Leg*) naGhost_ptr(r);
170   return 0;
171 }
172
173 static Procedure* procedureGhost(naRef r)
174 {
175   if (naGhost_type(r) == &ProcedureGhostType)
176     return (Procedure*) naGhost_ptr(r);
177   return 0;
178 }
179
180 static FlightPlan* flightplanGhost(naRef r)
181 {
182   if (naGhost_type(r) == &FlightPlanGhostType)
183     return (FlightPlan*) naGhost_ptr(r);
184   return 0;
185 }
186
187 static void routeBaseGhostDestroy(void* g)
188 {
189   // nothing for now
190 }
191
192 static naRef airportPrototype;
193 static naRef flightplanPrototype;
194 static naRef waypointPrototype;
195 static naRef geoCoordClass;
196 static naRef fpLegPrototype;
197 static naRef procedurePrototype;
198
199 naRef ghostForPositioned(naContext c, const FGPositioned* pos)
200 {
201     if (!pos) {
202         return naNil();
203     }
204     
205     FGPositioned::get(pos); // take a ref
206     return naNewGhost(c, &PositionedGhostType, (void*) pos);
207 }
208
209 naRef ghostForAirport(naContext c, const FGAirport* apt)
210 {
211   if (!apt) {
212     return naNil();
213   }
214   
215   FGPositioned::get(apt); // take a ref
216   return naNewGhost2(c, &AirportGhostType, (void*) apt);
217 }
218
219 naRef ghostForNavaid(naContext c, const FGNavRecord* n)
220 {
221   if (!n) {
222     return naNil();
223   }
224   
225   FGPositioned::get(n); // take a ref
226   return naNewGhost2(c, &NavaidGhostType, (void*) n);
227 }
228
229 naRef ghostForRunway(naContext c, const FGRunway* r)
230 {
231   if (!r) {
232     return naNil();
233   }
234   
235   FGPositioned::get(r); // take a ref
236   return naNewGhost2(c, &RunwayGhostType, (void*) r);
237 }
238
239 naRef ghostForWaypt(naContext c, const Waypt* wpt)
240 {
241   if (!wpt) {
242     return naNil();
243   }
244   
245   Waypt::get(wpt); // take a ref
246   return naNewGhost2(c, &WayptGhostType, (void*) wpt);
247 }
248
249 naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg)
250 {
251   if (!leg) {
252     return naNil();
253   }
254   
255   return naNewGhost2(c, &FPLegGhostType, (void*) leg);
256 }
257
258 naRef ghostForFlightPlan(naContext c, const FlightPlan* fp)
259 {
260   if (!fp) {
261     return naNil();
262   }
263   
264   return naNewGhost2(c, &FlightPlanGhostType, (void*) fp);
265 }
266
267 naRef ghostForProcedure(naContext c, const Procedure* proc)
268 {
269   if (!proc) {
270     return naNil();
271   }
272   
273   return naNewGhost2(c, &ProcedureGhostType, (void*) proc);
274 }
275
276 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out)
277 {
278   const char* fieldName = naStr_data(field);
279   FGAirport* apt = (FGAirport*) g;
280   
281   if (!strcmp(fieldName, "parents")) {
282     *out = naNewVector(c);
283     naVec_append(*out, airportPrototype);
284   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, apt->ident());
285   else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, apt->name());
286   else if (!strcmp(fieldName, "lat")) *out = naNum(apt->getLatitude());
287   else if (!strcmp(fieldName, "lon")) *out = naNum(apt->getLongitude());
288   else if (!strcmp(fieldName, "elevation")) {
289     *out = naNum(apt->getElevation() * SG_FEET_TO_METER);
290   } else if (!strcmp(fieldName, "has_metar")) {
291     *out = naNum(apt->getMetar());
292   } else if (!strcmp(fieldName, "runways")) {
293     *out = naNewHash(c);
294     for(unsigned int r=0; r<apt->numRunways(); ++r) {
295       FGRunway* rwy(apt->getRunwayByIndex(r));
296       naRef rwyid = stringToNasal(c, rwy->ident());
297       naRef rwydata = ghostForRunway(c, rwy);
298       naHash_set(*out, rwyid, rwydata);
299     }
300
301   } else {
302     return 0;
303   }
304   
305   return "";
306 }
307
308 static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char* fieldName, naRef* out)
309 {
310   if (!strcmp(fieldName, "wp_name")) *out = stringToNasal(c, wpt->ident());
311   else if (!strcmp(fieldName, "wp_type")) *out = stringToNasal(c, wpt->type());
312   else if (!strcmp(fieldName, "wp_lat")) *out = naNum(wpt->position().getLatitudeDeg());
313   else if (!strcmp(fieldName, "wp_lon")) *out = naNum(wpt->position().getLongitudeDeg());
314   else if (!strcmp(fieldName, "wp_parent_name")) {
315     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
316     *out = proc ? stringToNasal(c, proc->ident()) : naNil();
317   } else if (!strcmp(fieldName, "wp_parent")) {
318     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
319     *out = ghostForProcedure(c, proc);
320   } else if (!strcmp(fieldName, "fly_type")) {
321     if (wpt->type() == "hold") {
322       *out = stringToNasal(c, "Hold");
323     } else {
324       *out = stringToNasal(c, wpt->flag(WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
325     }
326   } else {
327     return NULL; // member not found
328   }
329
330   return "";
331 }
332
333 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
334 {
335   const char* fieldName = naStr_data(field);
336   Waypt* wpt = (flightgear::Waypt*) g;
337   return waypointCommonGetMember(c, wpt, fieldName, out);
338 }
339
340 static RouteRestriction routeRestrictionFromString(const char* s)
341 {
342   string u(s);
343   boost::to_lower(u);
344   if (u == "computed") return RESTRICT_COMPUTED;
345   if (u == "at") return RESTRICT_AT;
346   if (u == "mach") return SPEED_RESTRICT_MACH;
347   if (u == "computed-mach") return SPEED_COMPUTED_MACH;
348   if (u == "delete") return RESTRICT_DELETE;
349   return RESTRICT_NONE;
350 };
351
352 naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
353 {
354   switch (rr) {
355     case RESTRICT_NONE: return naNil();
356     case RESTRICT_AT: return stringToNasal(c, "at");
357     case RESTRICT_ABOVE: return stringToNasal(c, "above");
358     case RESTRICT_BELOW: return stringToNasal(c, "below");
359     case SPEED_RESTRICT_MACH: return stringToNasal(c, "mach");
360     case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
361     case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
362     case RESTRICT_DELETE: return stringToNasal(c, "delete");
363   }
364   
365   return naNil();
366 }
367
368 static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out)
369 {
370   const char* fieldName = naStr_data(field);
371   FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
372   Waypt* wpt = leg->waypoint();
373   
374   if (!strcmp(fieldName, "parents")) {
375     *out = naNewVector(c);
376     naVec_append(*out, fpLegPrototype);
377   } else if (!strcmp(fieldName, "alt_cstr")) {
378     *out = naNum(leg->altitudeFt());
379   } else if (!strcmp(fieldName, "alt_cstr_type")) {
380     *out = routeRestrictionToNasal(c, leg->altitudeRestriction());
381   } else if (!strcmp(fieldName, "speed_cstr")) {
382     double s = isMachRestrict(leg->speedRestriction()) ? leg->speedMach() : leg->speedKts();
383     *out = naNum(s);
384   } else if (!strcmp(fieldName, "speed_cstr_type")) {
385     *out = routeRestrictionToNasal(c, leg->speedRestriction());  
386   } else if (!strcmp(fieldName, "leg_distance")) {
387     *out = naNum(leg->distanceNm());
388   } else if (!strcmp(fieldName, "leg_bearing")) {
389     *out = naNum(leg->courseDeg());
390   } else if (!strcmp(fieldName, "distance_along_route")) {
391     *out = naNum(leg->distanceAlongRoute());
392   } else { // check for fields defined on the underlying waypoint
393     return waypointCommonGetMember(c, wpt, fieldName, out);
394   }
395   
396   return ""; // success
397 }
398
399 static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out)
400 {
401   const char* fieldName = naStr_data(field);
402   FlightPlan* fp = (FlightPlan*) g;
403   
404   if (!strcmp(fieldName, "parents")) {
405     *out = naNewVector(c);
406     naVec_append(*out, flightplanPrototype);
407   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, fp->ident());
408   else if (!strcmp(fieldName, "departure")) *out = ghostForAirport(c, fp->departureAirport());
409   else if (!strcmp(fieldName, "destination")) *out = ghostForAirport(c, fp->destinationAirport());
410   else if (!strcmp(fieldName, "departure_runway")) *out = ghostForRunway(c, fp->departureRunway());
411   else if (!strcmp(fieldName, "destination_runway")) *out = ghostForRunway(c, fp->destinationRunway());
412   else if (!strcmp(fieldName, "sid")) *out = ghostForProcedure(c, fp->sid());
413   else if (!strcmp(fieldName, "sid_trans")) *out = ghostForProcedure(c, fp->sidTransition());
414   else if (!strcmp(fieldName, "star")) *out = ghostForProcedure(c, fp->star());
415   else if (!strcmp(fieldName, "star_trans")) *out = ghostForProcedure(c, fp->starTransition());
416   else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
417   else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex());
418   else {
419     return 0;
420   }
421   
422   return "";
423 }
424
425 static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value)
426 {
427   const char* fieldName = naStr_data(field);
428   FlightPlan* fp = (FlightPlan*) g;
429   
430   if (!strcmp(fieldName, "id")) {
431     if (!naIsString(value)) naRuntimeError(c, "flightplan.id must be a string");
432     fp->setIdent(naStr_data(value));
433   } else if (!strcmp(fieldName, "current")) {
434     int index = value.num;
435     if ((index < 0) || (index >= fp->numLegs())) {
436       return;
437     }
438     fp->setCurrentIndex(index);
439   } else if (!strcmp(fieldName, "departure")) {
440     FGAirport* apt = airportGhost(value);
441     if (apt) {
442       fp->setDeparture(apt);
443       return;
444     }
445     
446     FGRunway* rwy = runwayGhost(value);
447     if (rwy){
448       fp->setDeparture(rwy);
449       return;
450     }
451     
452     naRuntimeError(c, "bad argument type setting departure");
453   } else if (!strcmp(fieldName, "destination")) {
454     FGAirport* apt = airportGhost(value);
455     if (apt) {
456       fp->setDestination(apt);
457       return;
458     }
459     
460     FGRunway* rwy = runwayGhost(value);
461     if (rwy){
462       fp->setDestination(rwy);
463       return;
464     }
465     
466     naRuntimeError(c, "bad argument type setting destination");
467   } else if (!strcmp(fieldName, "departure_runway")) {
468     FGRunway* rwy = runwayGhost(value);
469     if (rwy){
470       fp->setDeparture(rwy);
471       return;
472     }
473     
474     naRuntimeError(c, "bad argument type setting departure");
475   } else if (!strcmp(fieldName, "destination_runway")) {
476     FGRunway* rwy = runwayGhost(value);
477     if (rwy){
478       fp->setDestination(rwy);
479       return;
480     }
481     
482     naRuntimeError(c, "bad argument type setting departure");
483   } else if (!strcmp(fieldName, "sid")) {
484     Procedure* proc = procedureGhost(value);
485     if (proc && (proc->type() == PROCEDURE_SID)) {
486       fp->setSID((SID*) proc);
487       return;
488     }
489     // allow a SID transition to be set, implicitly include the SID itself
490     if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
491       fp->setSID((Transition*) proc);
492       return;
493     }
494         
495     if (naIsString(value)) {
496       FGAirport* apt = fp->departureAirport();
497       fp->setSID(apt->findSIDWithIdent(naStr_data(value)));
498       return;
499     }
500     
501     naRuntimeError(c, "bad argument type setting SID");
502   } else if (!strcmp(fieldName, "star")) {
503     Procedure* proc = procedureGhost(value);
504     if (proc && (proc->type() == PROCEDURE_STAR)) {
505       fp->setSTAR((STAR*) proc);
506       return;
507     }
508     
509     if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
510       fp->setSTAR((Transition*) proc);
511       return;
512     }
513     
514     if (naIsString(value)) {
515       FGAirport* apt = fp->destinationAirport();
516       fp->setSTAR(apt->findSTARWithIdent(naStr_data(value)));
517       return;
518     }
519     
520     naRuntimeError(c, "bad argument type setting STAR");
521   } else if (!strcmp(fieldName, "approach")) {
522     Procedure* proc = procedureGhost(value);
523     if (proc && Approach::isApproach(proc->type())) {
524       fp->setApproach((Approach*) proc);
525       return;
526     }
527     
528     if (naIsString(value)) {
529       FGAirport* apt = fp->destinationAirport();
530       fp->setApproach(apt->findApproachWithIdent(naStr_data(value)));
531       return;
532     }
533     
534     naRuntimeError(c, "bad argument type setting approach");
535   }
536 }
537
538
539 static naRef procedureTpType(naContext c, ProcedureType ty)
540 {
541   switch (ty) {
542     case PROCEDURE_SID: return stringToNasal(c, "sid");
543     case PROCEDURE_STAR: return stringToNasal(c, "star");
544     case PROCEDURE_APPROACH_VOR: 
545     case PROCEDURE_APPROACH_ILS: 
546     case PROCEDURE_APPROACH_RNAV: 
547     case PROCEDURE_APPROACH_NDB:
548       return stringToNasal(c, "IAP");
549     default:
550       return naNil();
551   }
552 }
553
554 static naRef procedureRadioType(naContext c, ProcedureType ty)
555 {
556   switch (ty) {
557     case PROCEDURE_APPROACH_VOR: return stringToNasal(c, "VOR");
558     case PROCEDURE_APPROACH_ILS: return stringToNasal(c, "ILS");
559     case PROCEDURE_APPROACH_RNAV: return stringToNasal(c, "RNAV");
560     case PROCEDURE_APPROACH_NDB: return stringToNasal(c, "NDB");
561     default:
562       return naNil();
563   }
564 }
565
566 static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out)
567 {
568   const char* fieldName = naStr_data(field);
569   Procedure* proc = (Procedure*) g;
570   
571   if (!strcmp(fieldName, "parents")) {
572     *out = naNewVector(c);
573     naVec_append(*out, procedurePrototype);
574   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, proc->ident());
575   else if (!strcmp(fieldName, "airport")) *out = ghostForAirport(c, proc->airport());
576   else if (!strcmp(fieldName, "tp_type")) *out = procedureTpType(c, proc->type());
577   else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type());
578   else if (!strcmp(fieldName, "runways")) {
579     *out = naNewVector(c);
580     BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) {
581       naVec_append(*out, stringToNasal(c, rwy->ident()));
582     }
583   } else if (!strcmp(fieldName, "transitions")) {
584     if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
585       *out = naNil();
586       return "";
587     }
588         
589     ArrivalDeparture* ad = static_cast<ArrivalDeparture*>(proc);
590     *out = naNewVector(c);
591     BOOST_FOREACH(string id, ad->transitionIdents()) {
592       naVec_append(*out, stringToNasal(c, id));
593     }
594   } else {
595     return 0;
596   }
597   
598   return "";
599 }
600
601 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
602 {
603   const char* fieldName = naStr_data(field);
604   FGRunway* rwy = (FGRunway*) g;
605   
606   if (!strcmp(fieldName, "id")) *out = stringToNasal(c, rwy->ident());
607   else if (!strcmp(fieldName, "lat")) *out = naNum(rwy->latitude());
608   else if (!strcmp(fieldName, "lon")) *out = naNum(rwy->longitude());
609   else if (!strcmp(fieldName, "heading")) *out = naNum(rwy->headingDeg());
610   else if (!strcmp(fieldName, "length")) *out = naNum(rwy->lengthM());
611   else if (!strcmp(fieldName, "width")) *out = naNum(rwy->widthM());
612   else if (!strcmp(fieldName, "threshold")) *out = naNum(rwy->displacedThresholdM());
613   else if (!strcmp(fieldName, "stopway")) *out = naNum(rwy->stopwayM());
614   else if (!strcmp(fieldName, "ils_frequency_mhz")) {
615     *out = rwy->ILS() ? naNum(rwy->ILS()->get_freq() / 100.0) : naNil();
616   } else if (!strcmp(fieldName, "ils")) {
617     *out = ghostForNavaid(c, rwy->ILS());
618   } else {
619     return 0;
620   }
621   
622   return "";
623 }
624
625 static const char* navaidGhostGetMember(naContext c, void* g, naRef field, naRef* out)
626 {
627   const char* fieldName = naStr_data(field);
628   FGNavRecord* nav = (FGNavRecord*) g;
629   
630   if (!strcmp(fieldName, "id")) *out = stringToNasal(c, nav->ident());
631   else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, nav->name());
632   else if (!strcmp(fieldName, "lat")) *out = naNum(nav->get_lat());
633   else if (!strcmp(fieldName, "lon")) *out = naNum(nav->get_lon());
634   else if (!strcmp(fieldName, "elevation")) {
635     *out = naNum(nav->get_elev_ft() * SG_FEET_TO_METER);
636   } else if (!strcmp(fieldName, "type")) {
637     *out = stringToNasal(c, nav->nameForType(nav->type()));
638   } else if (!strcmp(fieldName, "frequency")) {
639     *out = naNum(nav->get_freq()); 
640   } else if (!strcmp(fieldName, "range_nm")) {
641     *out = naNum(nav->get_range()); 
642   } else if (!strcmp(fieldName, "course")) {
643     if ((nav->type() == FGPositioned::ILS) || (nav->type() == FGPositioned::LOC)) {
644       double radial = nav->get_multiuse();
645       SG_NORMALIZE_RANGE(radial, 0.0, 360.0);
646       *out = naNum(radial);
647     } else {
648       *out = naNil();
649     }
650   } else {
651     return 0;
652   }
653   
654   return "";
655 }
656
657 static bool hashIsCoord(naRef h)
658 {
659   naRef parents = naHash_cget(h, (char*) "parents");
660   if (!naIsVector(parents)) {
661     return false;
662   }
663   
664   return naEqual(naVec_get(parents, 0), geoCoordClass);
665 }
666
667 bool geodFromHash(naRef ref, SGGeod& result)
668 {
669   if (!naIsHash(ref)) {
670     return false;
671   }
672   
673 // first, see if the hash contains a FGPositioned ghost - in which case
674 // we can read off its position directly
675   naRef posGhost = naHash_cget(ref, (char*) "_positioned");
676   if (!naIsNil(posGhost)) {
677     FGPositioned* pos = positionedGhost(posGhost);
678     result = pos->geod();
679     return true;
680   }
681   
682 // then check for manual latitude / longitude names
683   naRef lat = naHash_cget(ref, (char*) "lat");
684   naRef lon = naHash_cget(ref, (char*) "lon");
685   if (naIsNum(lat) && naIsNum(lon)) {
686     result = SGGeod::fromDeg(naNumValue(lon).num, naNumValue(lat).num);
687     return true;
688   }
689   
690   if (hashIsCoord(ref)) {
691     naRef lat = naHash_cget(ref, (char*) "_lat");
692     naRef lon = naHash_cget(ref, (char*) "_lon");
693     if (naIsNum(lat) && naIsNum(lon)) {
694       result = SGGeod::fromRad(naNumValue(lon).num, naNumValue(lat).num);
695       return true;
696     }
697   }
698     
699 // check for any synonyms?
700     // latitude + longitude?
701   
702   return false;
703 }
704
705 static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
706 {
707   if (offset >= argc) {
708     return 0;
709   }
710   
711   if (naIsGhost(args[offset])) {
712     naGhostType* gt = naGhost_type(args[offset]);
713     if (gt == &AirportGhostType) {
714       result = airportGhost(args[offset])->geod();
715       return 1;
716     }
717     
718     if (gt == &NavaidGhostType) {
719       result = navaidGhost(args[offset])->geod();
720       return 1;
721     }
722     
723     if (gt == &RunwayGhostType) {
724       result = runwayGhost(args[offset])->geod();
725       return 1;
726     }
727     
728     if (gt == &WayptGhostType) {
729       result = wayptGhost(args[offset])->position();
730       return 1;
731     }
732   }
733   
734   if (geodFromHash(args[offset], result)) {
735     return 1;
736   }
737   
738   if (((argc - offset) >= 2) && naIsNum(args[offset]) && naIsNum(args[offset + 1])) {
739     double lat = naNumValue(args[0]).num,
740     lon = naNumValue(args[1]).num;
741     result = SGGeod::fromDeg(lon, lat);
742     return 2;
743   }
744   
745   return 0;
746 }
747
748 // Convert a cartesian point to a geodetic lat/lon/altitude.
749 static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
750 {
751   double lat, lon, alt, xyz[3];
752   if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
753   for(int i=0; i<3; i++)
754     xyz[i] = naNumValue(args[i]).num;
755   sgCartToGeod(xyz, &lat, &lon, &alt);
756   lat *= SG_RADIANS_TO_DEGREES;
757   lon *= SG_RADIANS_TO_DEGREES;
758   naRef vec = naNewVector(c);
759   naVec_append(vec, naNum(lat));
760   naVec_append(vec, naNum(lon));
761   naVec_append(vec, naNum(alt));
762   return vec;
763 }
764
765 // Convert a geodetic lat/lon/altitude to a cartesian point.
766 static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
767 {
768   if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
769   double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
770   double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
771   double alt = naNumValue(args[2]).num;
772   double xyz[3];
773   sgGeodToCart(lat, lon, alt, xyz);
774   naRef vec = naNewVector(c);
775   naVec_append(vec, naNum(xyz[0]));
776   naVec_append(vec, naNum(xyz[1]));
777   naVec_append(vec, naNum(xyz[2]));
778   return vec;
779 }
780
781 // For given geodetic point return array with elevation, and a material data
782 // hash, or nil if there's no information available (tile not loaded). If
783 // information about the material isn't available, then nil is returned instead
784 // of the hash.
785 static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
786 {
787 #define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
788   if(argc < 2 || argc > 3)
789     naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
790   double lat = naNumValue(args[0]).num;
791   double lon = naNumValue(args[1]).num;
792   double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
793   const SGMaterial *mat;
794   SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
795   if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
796     return naNil();
797   naRef vec = naNewVector(c);
798   naVec_append(vec, naNum(elev));
799   naRef matdata = naNil();
800   if(mat) {
801     matdata = naNewHash(c);
802     naRef names = naNewVector(c);
803     BOOST_FOREACH(const std::string& n, mat->get_names())
804       naVec_append(names, stringToNasal(c, n));
805       
806     HASHSET("names", 5, names);
807     HASHSET("solid", 5, naNum(mat->get_solid()));
808     HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
809     HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
810     HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
811     HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
812     HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
813   }
814   naVec_append(vec, matdata);
815   return vec;
816 #undef HASHSET
817 }
818
819
820 class AirportInfoFilter : public FGAirport::AirportFilter
821 {
822 public:
823   AirportInfoFilter() : type(FGPositioned::AIRPORT) {
824   }
825   
826   bool fromArg(naRef arg)
827   {
828     const char *s = naStr_data(arg);
829     if(!strcmp(s, "airport")) type = FGPositioned::AIRPORT;
830     else if(!strcmp(s, "seaport")) type = FGPositioned::SEAPORT;
831     else if(!strcmp(s, "heliport")) type = FGPositioned::HELIPORT;
832     else
833       return false;
834     
835     return true;
836   }
837   
838   virtual FGPositioned::Type minType() const {
839     return type;
840   }
841   
842   virtual FGPositioned::Type maxType() const {
843     return type;
844   }
845   
846   FGPositioned::Type type;
847 };
848
849 // Returns data hash for particular or nearest airport of a <type>, or nil
850 // on error.
851 //
852 // airportinfo(<id>);                   e.g. "KSFO"
853 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
854 // airportinfo()                        same as  airportinfo("airport")
855 // airportinfo(<lat>, <lon> [, <type>]);
856 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
857 {
858   SGGeod pos = globals->get_aircraft_position();
859   FGAirport* apt = NULL;
860   
861   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
862     pos = SGGeod::fromDeg(args[1].num, args[0].num);
863     args += 2;
864     argc -= 2;
865   }
866   
867   double maxRange = 10000.0; // expose this? or pick a smaller value?
868   
869   AirportInfoFilter filter; // defaults to airports only
870   
871   if(argc == 0) {
872     // fall through and use AIRPORT
873   } else if(argc == 1 && naIsString(args[0])) {
874     if (filter.fromArg(args[0])) {
875       // done!
876     } else {
877       // user provided an <id>, hopefully
878       apt = FGAirport::findByIdent(naStr_data(args[0]));
879       if (!apt) {
880         // return nil here, but don't raise a runtime error; this is a
881         // legitamate way to validate an ICAO code, for example in a
882         // dialog box or similar.
883         return naNil();
884       }
885     }
886   } else {
887     naRuntimeError(c, "airportinfo() with invalid function arguments");
888     return naNil();
889   }
890   
891   if(!apt) {
892     apt = FGAirport::findClosest(pos, maxRange, &filter);
893     if(!apt) return naNil();
894   }
895   
896   return ghostForAirport(c, apt);
897 }
898
899 static naRef f_findAirportsWithinRange(naContext c, naRef me, int argc, naRef* args)
900 {
901   int argOffset = 0;
902   SGGeod pos = globals->get_aircraft_position();
903   argOffset += geodFromArgs(args, 0, argc, pos);
904   
905   if (!naIsNum(args[argOffset])) {
906     naRuntimeError(c, "findAirportsWithinRange expected range (in nm) as arg %d", argOffset);
907   }
908   
909   AirportInfoFilter filter; // defaults to airports only
910   double rangeNm = args[argOffset++].num;
911   if (argOffset < argc) {
912     filter.fromArg(args[argOffset++]);
913   }
914   
915   naRef r = naNewVector(c);
916   
917   FGPositioned::List apts = FGPositioned::findWithinRange(pos, rangeNm, &filter);
918   FGPositioned::sortByRange(apts, pos);
919   
920   BOOST_FOREACH(FGPositionedRef a, apts) {
921     FGAirport* apt = (FGAirport*) a.get();
922     naVec_append(r, ghostForAirport(c, apt));
923   }
924   
925   return r;
926 }
927
928 static naRef f_findAirportsByICAO(naContext c, naRef me, int argc, naRef* args)
929 {
930   if (!naIsString(args[0])) {
931     naRuntimeError(c, "findAirportsByICAO expects string as arg 0");
932   }
933   
934   int argOffset = 0;
935   string prefix(naStr_data(args[argOffset++]));
936   AirportInfoFilter filter; // defaults to airports only
937   if (argOffset < argc) {
938     filter.fromArg(args[argOffset++]);
939   }
940   
941   naRef r = naNewVector(c);
942   
943   FGPositioned::List apts = FGPositioned::findAllWithIdent(prefix, &filter, false);
944   
945   BOOST_FOREACH(FGPositionedRef a, apts) {
946     FGAirport* apt = (FGAirport*) a.get();
947     naVec_append(r, ghostForAirport(c, apt));
948   }
949   
950   return r;
951 }
952
953 static naRef f_airport_tower(naContext c, naRef me, int argc, naRef* args)
954 {
955     FGAirport* apt = airportGhost(me);
956     if (!apt) {
957       naRuntimeError(c, "airport.tower called on non-airport object");
958     }
959   
960     // build a hash for the tower position    
961     SGGeod towerLoc = apt->getTowerLocation();
962     naRef tower = naNewHash(c);
963     hashset(c, tower, "lat", naNum(towerLoc.getLatitudeDeg()));
964     hashset(c, tower, "lon", naNum(towerLoc.getLongitudeDeg()));
965     hashset(c, tower, "elevation", naNum(towerLoc.getElevationM()));
966     return tower;
967 }
968
969 static naRef f_airport_comms(naContext c, naRef me, int argc, naRef* args)
970 {
971     FGAirport* apt = airportGhost(me);
972     if (!apt) {
973       naRuntimeError(c, "airport.comms called on non-airport object");
974     }
975     naRef comms = naNewVector(c);
976     
977 // if we have an explicit type, return a simple vector of frequencies
978     if (argc > 0 && naIsScalar(args[0])) {
979         std::string commName = naStr_data(args[0]);
980         FGPositioned::Type commType = FGPositioned::typeFromName(commName);
981         
982         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStationsOfType(commType)) {
983             naVec_append(comms, naNum(comm->freqMHz()));
984         }
985     } else {
986 // otherwise return a vector of hashes, one for each comm station.
987         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStations()) {
988             naRef commHash = naNewHash(c);
989             hashset(c, commHash, "frequency", naNum(comm->freqMHz()));
990             hashset(c, commHash, "ident", stringToNasal(c, comm->ident()));
991             naVec_append(comms, commHash);
992         }
993     }
994     
995     return comms;
996 }
997
998 static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args)
999 {
1000   FGAirport* apt = airportGhost(me);
1001   if (!apt) {
1002     naRuntimeError(c, "airport.runway called on non-airport object");
1003   }
1004   
1005   if ((argc < 1) || !naIsString(args[0])) {
1006     naRuntimeError(c, "airport.runway expects a runway ident argument");
1007   }
1008   
1009   std::string ident(naStr_data(args[0]));
1010   boost::to_upper(ident);
1011   if (!apt->hasRunwayWithIdent(ident)) {
1012     return naNil();
1013   }
1014   
1015   return ghostForRunway(c, apt->getRunwayByIdent(ident));
1016 }
1017
1018 static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
1019 {
1020   FGAirport* apt = airportGhost(me);
1021   if (!apt) {
1022     naRuntimeError(c, "airport.sids called on non-airport object");
1023   }
1024   
1025   naRef sids = naNewVector(c);
1026   
1027   FGRunway* rwy = NULL;
1028   if (argc > 0 && naIsString(args[0])) {
1029     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1030       return naNil();
1031     }
1032
1033     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1034   } else if (argc > 0) {
1035     rwy = runwayGhost(args[0]);
1036   }
1037
1038   if (rwy) {
1039     BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) {
1040       naRef procId = stringToNasal(c, sid->ident());
1041       naVec_append(sids, procId);
1042     }
1043   } else {
1044     for (unsigned int s=0; s<apt->numSIDs(); ++s) {
1045       flightgear::SID* sid = apt->getSIDByIndex(s);
1046       naRef procId = stringToNasal(c, sid->ident());
1047       naVec_append(sids, procId);
1048     }
1049   }
1050   
1051   return sids;
1052 }
1053
1054 static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
1055 {
1056   FGAirport* apt = airportGhost(me);
1057   if (!apt) {
1058     naRuntimeError(c, "airport.stars called on non-airport object");
1059   }
1060   
1061   naRef stars = naNewVector(c);
1062   
1063   FGRunway* rwy = NULL;
1064   if (argc > 0 && naIsString(args[0])) {
1065     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1066       return naNil();
1067     }
1068         
1069     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1070   } else if (argc > 0) {
1071     rwy = runwayGhost(args[0]);
1072   }
1073   
1074   if (rwy) {
1075     BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) {
1076       naRef procId = stringToNasal(c, s->ident());
1077       naVec_append(stars, procId);
1078     }
1079   } else {
1080     for (unsigned int s=0; s<apt->numSTARs(); ++s) {
1081       flightgear::STAR* star = apt->getSTARByIndex(s);
1082       naRef procId = stringToNasal(c, star->ident());
1083       naVec_append(stars, procId);
1084     }
1085   }
1086   
1087   return stars;
1088 }
1089
1090 static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args)
1091 {
1092   FGAirport* apt = airportGhost(me);
1093   if (!apt) {
1094     naRuntimeError(c, "airport.getApproachList called on non-airport object");
1095   }
1096   
1097   naRef approaches = naNewVector(c);
1098   
1099   ProcedureType ty = PROCEDURE_INVALID;
1100   if ((argc > 1) && naIsString(args[1])) {
1101     std::string u(naStr_data(args[1]));
1102     boost::to_upper(u);
1103     if (u == "NDB") ty = PROCEDURE_APPROACH_NDB;
1104     if (u == "VOR") ty = PROCEDURE_APPROACH_VOR;
1105     if (u == "ILS") ty = PROCEDURE_APPROACH_ILS;
1106     if (u == "RNAV") ty = PROCEDURE_APPROACH_RNAV;
1107   }
1108   
1109   FGRunway* rwy = NULL;
1110   if (argc > 0 && (rwy = runwayGhost(args[0]))) {
1111     // ok
1112   } else if (argc > 0 && naIsString(args[0])) {
1113     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1114       return naNil();
1115     }
1116     
1117     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1118   }
1119   
1120   if (rwy) {
1121     BOOST_FOREACH(Approach* s, rwy->getApproaches()) {
1122       if ((ty != PROCEDURE_INVALID) && (s->type() != ty)) {
1123         continue;
1124       }
1125       
1126       naRef procId = stringToNasal(c, s->ident());
1127       naVec_append(approaches, procId);
1128     }
1129   } else {
1130     // no runway specified, report them all
1131     for (unsigned int s=0; s<apt->numApproaches(); ++s) {
1132       Approach* app = apt->getApproachByIndex(s);
1133       if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) {
1134         continue;
1135       }
1136       
1137       naRef procId = stringToNasal(c, app->ident());
1138       naVec_append(approaches, procId);
1139     }
1140   }
1141   
1142   return approaches;
1143 }
1144
1145 static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
1146 {
1147   FGAirport* apt = airportGhost(me);
1148   if (!apt) {
1149     naRuntimeError(c, "airport.parking called on non-airport object");
1150   }
1151   
1152   naRef r = naNewVector(c);
1153   std::string type;
1154   bool onlyAvailable = false;
1155   
1156   if (argc > 0 && naIsString(args[0])) {
1157     type = naStr_data(args[0]);
1158   }
1159   
1160   if ((argc > 1) && naIsNum(args[1])) {
1161     onlyAvailable = (args[1].num != 0.0);
1162   }
1163   
1164   FGAirportDynamics* dynamics = apt->getDynamics();
1165   for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
1166     FGParking* park = dynamics->getParking(i);
1167   // filter out based on availability and type
1168     if (onlyAvailable && !park->isAvailable()) {
1169       continue;
1170     }
1171     
1172     if (!type.empty() && (park->getType() != type)) {
1173       continue;
1174     }
1175     
1176     naRef nm = stringToNasal(c, park->getName());
1177     naVec_append(r, nm);
1178   }
1179   
1180   return r;
1181 }
1182
1183 static naRef f_airport_getSid(naContext c, naRef me, int argc, naRef* args)
1184 {
1185   FGAirport* apt = airportGhost(me);
1186   if (!apt) {
1187     naRuntimeError(c, "airport.getSid called on non-airport object");
1188   }
1189   
1190   if ((argc != 1) || !naIsString(args[0])) {
1191     naRuntimeError(c, "airport.getSid passed invalid argument");
1192   }
1193   
1194   string ident = naStr_data(args[0]);
1195   return ghostForProcedure(c, apt->findSIDWithIdent(ident));
1196 }
1197
1198 static naRef f_airport_getStar(naContext c, naRef me, int argc, naRef* args)
1199 {
1200   FGAirport* apt = airportGhost(me);
1201   if (!apt) {
1202     naRuntimeError(c, "airport.getStar called on non-airport object");
1203   }
1204   
1205   if ((argc != 1) || !naIsString(args[0])) {
1206     naRuntimeError(c, "airport.getStar passed invalid argument");
1207   }
1208   
1209   string ident = naStr_data(args[0]);
1210   return ghostForProcedure(c, apt->findSTARWithIdent(ident));
1211 }
1212
1213 static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args)
1214 {
1215   FGAirport* apt = airportGhost(me);
1216   if (!apt) {
1217     naRuntimeError(c, "airport.getIAP called on non-airport object");
1218   }
1219   
1220   if ((argc != 1) || !naIsString(args[0])) {
1221     naRuntimeError(c, "airport.getIAP passed invalid argument");
1222   }
1223   
1224   string ident = naStr_data(args[0]);
1225   return ghostForProcedure(c, apt->findApproachWithIdent(ident));
1226 }
1227
1228 // Returns vector of data hash for navaid of a <type>, nil on error
1229 // navaids sorted by ascending distance 
1230 // navinfo([<lat>,<lon>],[<type>],[<id>])
1231 // lat/lon (numeric): use latitude/longitude instead of ac position
1232 // type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
1233 // id:                (partial) id of the fix
1234 // examples:
1235 // navinfo("vor")     returns all vors
1236 // navinfo("HAM")     return all navaids who's name start with "HAM"
1237 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
1238 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM" 
1239 //                           sorted by distance relative to lat=34, lon=48
1240 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
1241 {
1242   SGGeod pos;
1243   
1244   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
1245     pos = SGGeod::fromDeg(args[1].num, args[0].num);
1246     args += 2;
1247     argc -= 2;
1248   } else {
1249     pos = globals->get_aircraft_position();
1250   }
1251   
1252   FGPositioned::Type type = FGPositioned::INVALID;
1253   nav_list_type navlist;
1254   const char * id = "";
1255   
1256   if(argc > 0 && naIsString(args[0])) {
1257     const char *s = naStr_data(args[0]);
1258     if(!strcmp(s, "any")) type = FGPositioned::INVALID;
1259     else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
1260     else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
1261     else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
1262     else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
1263     else if(!strcmp(s, "dme")) type = FGPositioned::DME;
1264     else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
1265     else id = s; // this is an id
1266     ++args;
1267     --argc;
1268   } 
1269   
1270   if(argc > 0 && naIsString(args[0])) {
1271     if( *id != 0 ) {
1272       naRuntimeError(c, "navinfo() called with navaid id");
1273       return naNil();
1274     }
1275     id = naStr_data(args[0]);
1276     ++args;
1277     --argc;
1278   }
1279   
1280   if( argc > 0 ) {
1281     naRuntimeError(c, "navinfo() called with too many arguments");
1282     return naNil();
1283   }
1284   
1285   navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
1286   
1287   naRef reply = naNewVector(c);
1288   for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
1289     naVec_append( reply, ghostForNavaid(c, *it) );
1290   }
1291   return reply;
1292 }
1293
1294 static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
1295 {
1296   int argOffset = 0;
1297   SGGeod pos = globals->get_aircraft_position();
1298   argOffset += geodFromArgs(args, 0, argc, pos);
1299   
1300   if (!naIsNum(args[argOffset])) {
1301     naRuntimeError(c, "findNavaidsWithinRange expected range (in nm) as arg %d", argOffset);
1302   }
1303   
1304   FGPositioned::Type type = FGPositioned::INVALID;
1305   double rangeNm = args[argOffset++].num;
1306   if (argOffset < argc) {
1307     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1308   }
1309   
1310   naRef r = naNewVector(c);
1311   FGNavList::TypeFilter filter(type);
1312   FGPositioned::List navs = FGPositioned::findWithinRange(pos, rangeNm, &filter);
1313   FGPositioned::sortByRange(navs, pos);
1314   
1315   BOOST_FOREACH(FGPositionedRef a, navs) {
1316     FGNavRecord* nav = (FGNavRecord*) a.get();
1317     naVec_append(r, ghostForNavaid(c, nav));
1318   }
1319   
1320   return r;
1321 }
1322
1323 static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
1324 {
1325   int argOffset = 0;
1326   SGGeod pos = globals->get_aircraft_position();
1327   argOffset += geodFromArgs(args, 0, argc, pos);
1328   
1329   if (!naIsNum(args[argOffset])) {
1330     naRuntimeError(c, "findNavaidByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
1331   }
1332   
1333   FGPositioned::Type type = FGPositioned::INVALID;
1334   double freqMhz = args[argOffset++].num;
1335   if (argOffset < argc) {
1336     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1337   }
1338   
1339   nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
1340   if (navs.empty()) {
1341     return naNil();
1342   }
1343   
1344   return ghostForNavaid(c, navs.front().ptr());
1345 }
1346
1347 static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* args)
1348 {
1349   int argOffset = 0;
1350   SGGeod pos = globals->get_aircraft_position();
1351   argOffset += geodFromArgs(args, 0, argc, pos);
1352   
1353   if (!naIsNum(args[argOffset])) {
1354     naRuntimeError(c, "findNavaidsByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
1355   }
1356   
1357   FGPositioned::Type type = FGPositioned::INVALID;
1358   double freqMhz = args[argOffset++].num;
1359   if (argOffset < argc) {
1360     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1361   }
1362   
1363   naRef r = naNewVector(c);
1364   nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
1365   
1366   BOOST_FOREACH(nav_rec_ptr a, navs) {
1367     naVec_append(r, ghostForNavaid(c, a.ptr()));
1368   }
1369   
1370   return r;
1371 }
1372
1373 static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
1374 {
1375   int argOffset = 0;
1376   SGGeod pos = globals->get_aircraft_position();
1377   argOffset += geodFromArgs(args, 0, argc, pos);
1378   
1379   if (!naIsString(args[argOffset])) {
1380     naRuntimeError(c, "findNavaidsByIdent expectes ident string as arg %d", argOffset);
1381   }
1382   
1383   FGPositioned::Type type = FGPositioned::INVALID;
1384   string ident = naStr_data(args[argOffset++]);
1385   if (argOffset < argc) {
1386     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1387   }
1388   
1389   naRef r = naNewVector(c);
1390   nav_list_type navs = globals->get_navlist()->findByIdentAndFreq(pos, ident, 0.0, type);
1391   
1392   BOOST_FOREACH(nav_rec_ptr a, navs) {
1393     naVec_append(r, ghostForNavaid(c, a.ptr()));
1394   }
1395   
1396   return r;
1397 }
1398
1399
1400 // Convert a cartesian point to a geodetic lat/lon/altitude.
1401 static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
1402 {
1403   SGGeod pos = globals->get_aircraft_position();
1404   if (argc == 0) {
1405     // fine, use aircraft position
1406   } else if (geodFromArgs(args, 0, argc, pos)) {
1407     // okay
1408   } else {
1409     naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
1410   }
1411   
1412   double jd = globals->get_time_params()->getJD();
1413   double magvarDeg = sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
1414   return naNum(magvarDeg);
1415 }
1416
1417 static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
1418 {
1419     SGGeod from = globals->get_aircraft_position(), to, p;
1420     int argOffset = geodFromArgs(args, 0, argc, p);
1421     if (geodFromArgs(args, argOffset, argc, to)) {
1422       from = p; // we parsed both FROM and TO args, so first was from
1423     } else {
1424       to = p; // only parsed one arg, so FROM is current
1425     }
1426   
1427     if (argOffset == 0) {
1428         naRuntimeError(c, "invalid arguments to courseAndDistance");
1429     }
1430     
1431     double course, course2, d;
1432     SGGeodesy::inverse(from, to, course, course2, d);
1433     
1434     naRef result = naNewVector(c);
1435     naVec_append(result, naNum(course));
1436     naVec_append(result, naNum(d * SG_METER_TO_NM));
1437     return result;
1438 }
1439
1440 static naRef f_greatCircleMove(naContext c, naRef me, int argc, naRef* args)
1441 {
1442   SGGeod from = globals->get_aircraft_position(), to;
1443   int argOffset = 0;
1444   
1445   // complication - don't inerpret two doubles (as the only args)
1446   // as a lat,lon pair - only do so if we have at least three args.
1447   if (argc > 2) {
1448     argOffset = geodFromArgs(args, 0, argc, from);
1449   }
1450   
1451   if ((argOffset + 1) >= argc) {
1452     naRuntimeError(c, "isufficent arguments to greatCircleMove");
1453   }
1454   
1455   if (!naIsNum(args[argOffset]) || !naIsNum(args[argOffset+1])) {
1456     naRuntimeError(c, "invalid arguments %d and %d to greatCircleMove",
1457                    argOffset, argOffset + 1);
1458   }
1459   
1460   double course = args[argOffset].num, course2;
1461   double distanceNm = args[argOffset + 1].num;
1462   SGGeodesy::direct(from, course, distanceNm * SG_NM_TO_METER, to, course2);
1463   
1464   // return geo.Coord
1465   naRef coord = naNewHash(c);
1466   hashset(c, coord, "lat", naNum(to.getLatitudeDeg()));
1467   hashset(c, coord, "lon", naNum(to.getLongitudeDeg()));
1468   return coord;
1469 }
1470
1471 static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
1472 {
1473     SGGeod pos = globals->get_aircraft_position();
1474     geodFromArgs(args, 0, argc, pos);
1475     SGBucket b(pos);
1476     return stringToNasal(c, b.gen_base_path());
1477 }
1478
1479 static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
1480 {
1481   SGGeod pos = globals->get_aircraft_position();
1482   geodFromArgs(args, 0, argc, pos);
1483   SGBucket b(pos);
1484   return naNum(b.gen_index());
1485 }
1486
1487 static naRef f_route(naContext c, naRef me, int argc, naRef* args)
1488 {
1489   if (argc == 0) {
1490     FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));  
1491     return ghostForFlightPlan(c, rm->flightPlan());
1492   }
1493   
1494   if ((argc > 0) && naIsString(args[0])) {
1495     flightgear::FlightPlan* fp = new flightgear::FlightPlan;
1496     SGPath path(naStr_data(args[0]));
1497     if (!path.exists()) {
1498       naRuntimeError(c, "flightplan, no file at path %s", path.c_str());
1499     }
1500     
1501     if (!fp->load(path)) {
1502       SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path);
1503       delete fp;
1504       return naNil();
1505     }
1506     
1507     return ghostForFlightPlan(c, fp);
1508   }
1509   
1510   naRuntimeError(c, "bad arguments to flightplan()");
1511   return naNil();
1512 }
1513
1514 static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef* args)
1515 {
1516   FlightPlan* fp = flightplanGhost(me);
1517   if (!fp) {
1518     naRuntimeError(c, "flightplan.getWP called on non-flightplan object");
1519   }
1520
1521   int index;
1522   if (argc == 0) {
1523     index = fp->currentIndex();
1524   } else {
1525     index = (int) naNumValue(args[0]).num;
1526   }
1527   
1528   if ((index < 0) || (index >= fp->numLegs())) {
1529     return naNil();
1530   }
1531   
1532   return ghostForLeg(c, fp->legAtIndex(index));
1533 }
1534
1535 static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef* args)
1536 {
1537   FlightPlan* fp = flightplanGhost(me);
1538   if (!fp) {
1539     naRuntimeError(c, "flightplan.currentWP called on non-flightplan object");
1540   }
1541   return ghostForLeg(c, fp->currentLeg());
1542 }
1543
1544 static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef* args)
1545 {
1546   FlightPlan* fp = flightplanGhost(me);
1547   if (!fp) {
1548     naRuntimeError(c, "flightplan.nextWP called on non-flightplan object");
1549   }
1550   return ghostForLeg(c, fp->nextLeg());
1551 }
1552
1553 static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef* args)
1554 {
1555   FlightPlan* fp = flightplanGhost(me);
1556   if (!fp) {
1557     naRuntimeError(c, "flightplan.numWaypoints called on non-flightplan object");
1558   }
1559   return naNum(fp->numLegs());
1560 }
1561
1562 static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef* args)
1563 {
1564   FlightPlan* fp = flightplanGhost(me);
1565   if (!fp) {
1566     naRuntimeError(c, "flightplan.appendWP called on non-flightplan object");
1567   }
1568   
1569   WayptRef wp = wayptGhost(args[0]);
1570   int index = fp->numLegs();
1571   fp->insertWayptAtIndex(wp.get(), index);
1572   return naNum(index);
1573 }
1574
1575 static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef* args)
1576 {
1577   FlightPlan* fp = flightplanGhost(me);
1578   if (!fp) {
1579     naRuntimeError(c, "flightplan.insertWP called on non-flightplan object");
1580   }
1581   
1582   WayptRef wp = wayptGhost(args[0]);
1583   int index = -1; // append
1584   if ((argc > 1) && naIsNum(args[1])) {
1585     index = (int) args[1].num;
1586   }
1587   
1588   fp->insertWayptAtIndex(wp.get(), index);
1589   return naNil();
1590 }
1591
1592 static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef* args)
1593 {
1594   FlightPlan* fp = flightplanGhost(me);
1595   if (!fp) {
1596     naRuntimeError(c, "flightplan.insertWPAfter called on non-flightplan object");
1597   }
1598   
1599   WayptRef wp = wayptGhost(args[0]);
1600   int index = -1; // append
1601   if ((argc > 1) && naIsNum(args[1])) {
1602     index = (int) args[1].num;
1603   }
1604   
1605   fp->insertWayptAtIndex(wp.get(), index + 1);
1606   return naNil();
1607 }
1608
1609 static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef* args)
1610 {
1611   FlightPlan* fp = flightplanGhost(me);
1612   if (!fp) {
1613     naRuntimeError(c, "flightplan.insertWaypoints called on non-flightplan object");
1614   }
1615   
1616   WayptVec wps;
1617   if (!naIsVector(args[0])) {
1618     naRuntimeError(c, "flightplan.insertWaypoints expects vector as first arg");
1619   }
1620
1621   int count = naVec_size(args[0]);
1622   for (int i=0; i<count; ++i) {
1623     Waypt* wp = wayptGhost(naVec_get(args[0], i));
1624     if (wp) {
1625       wps.push_back(wp);
1626     }
1627   }
1628   
1629   int index = -1; // append
1630   if ((argc > 1) && naIsNum(args[1])) {
1631     index = (int) args[1].num;
1632   }
1633
1634   fp->insertWayptsAtIndex(wps, index);
1635   return naNil();
1636 }
1637
1638 static naRef f_flightplan_clearPlan(naContext c, naRef me, int argc, naRef* args)
1639 {
1640   FlightPlan* fp = flightplanGhost(me);
1641   if (!fp) {
1642     naRuntimeError(c, "flightplan.clearPlan called on non-flightplan object");
1643   }
1644   
1645   fp->clear();
1646   return naNil();
1647 }
1648
1649 static WayptFlag wayptFlagFromString(const char* s)
1650 {
1651   if (!strcmp(s, "sid")) return WPT_DEPARTURE;
1652   if (!strcmp(s, "star")) return WPT_ARRIVAL;
1653   if (!strcmp(s, "approach")) return WPT_APPROACH;
1654   if (!strcmp(s, "missed")) return WPT_MISS;
1655   if (!strcmp(s, "pseudo")) return WPT_PSEUDO;
1656   
1657   return (WayptFlag) 0;
1658 }
1659
1660 static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef* args)
1661 {
1662   FlightPlan* fp = flightplanGhost(me);
1663   if (!fp) {
1664     naRuntimeError(c, "flightplan.clearWPType called on non-flightplan object");
1665   }
1666   
1667   if (argc < 1) {
1668     naRuntimeError(c, "insufficent args to flightplan.clearWPType");
1669   }
1670   
1671   WayptFlag flag = wayptFlagFromString(naStr_data(args[0]));
1672   fp->clearWayptsWithFlag(flag);
1673   return naNil();
1674 }
1675
1676 static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args)
1677 {
1678   FlightPlan* fp = flightplanGhost(me);
1679   if (!fp) {
1680     naRuntimeError(c, "flightplan.clone called on non-flightplan object");
1681   }
1682   
1683   return ghostForFlightPlan(c, fp->clone());
1684 }
1685
1686 static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
1687 {
1688   FlightPlan::Leg* leg = fpLegGhost(me);
1689   if (!leg) {
1690     naRuntimeError(c, "leg.setSpeed called on non-flightplan-leg object");
1691   }
1692   
1693   if (argc < 2) {
1694     naRuntimeError(c, "bad arguments to leg.setSpeed");
1695   }
1696   
1697   RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
1698   leg->setSpeed(rr, args[0].num);
1699   return naNil();
1700 }
1701
1702 static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
1703 {
1704   FlightPlan::Leg* leg = fpLegGhost(me);
1705   if (!leg) {
1706     naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
1707   }
1708   
1709   if (argc < 2) {
1710     naRuntimeError(c, "bad arguments to leg.setAltitude");
1711   }
1712   
1713   RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
1714   leg->setAltitude(rr, args[0].num);
1715   return naNil();
1716 }
1717
1718 static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args)
1719 {
1720   flightgear::Waypt* w = wayptGhost(me);
1721   if (!w) {
1722     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1723   }
1724   
1725   FGPositioned* pos = w->source();
1726   if (!pos) {
1727     return naNil();
1728   }
1729   
1730   switch (pos->type()) {
1731   case FGPositioned::VOR:
1732   case FGPositioned::NDB:
1733   case FGPositioned::ILS:
1734   case FGPositioned::LOC:
1735   case FGPositioned::GS:
1736   case FGPositioned::DME:
1737   case FGPositioned::TACAN: {
1738     FGNavRecord* nav = (FGNavRecord*) pos;
1739     return ghostForNavaid(c, nav);
1740   }
1741       
1742   default:
1743     return naNil();
1744   }
1745 }
1746
1747 static naRef f_waypoint_airport(naContext c, naRef me, int argc, naRef* args)
1748 {
1749   flightgear::Waypt* w = wayptGhost(me);
1750   if (!w) {
1751     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1752   }
1753   
1754   FGPositioned* pos = w->source();
1755   if (!pos || FGAirport::isAirportType(pos)) {
1756     return naNil();
1757   }
1758   
1759   return ghostForAirport(c, (FGAirport*) pos);
1760 }
1761
1762 static naRef f_waypoint_runway(naContext c, naRef me, int argc, naRef* args)
1763 {
1764   flightgear::Waypt* w = wayptGhost(me);
1765   if (!w) {
1766     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
1767   }
1768   
1769   FGPositioned* pos = w->source();
1770   if (!pos || (pos->type() != FGPositioned::RUNWAY)) {
1771     return naNil();
1772   }
1773   
1774   return ghostForRunway(c, (FGRunway*) pos);
1775 }
1776
1777 static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef* args)
1778 {
1779   Procedure* proc = procedureGhost(me);
1780   if (!proc) {
1781     naRuntimeError(c, "procedure.transition called on non-procedure object");
1782   }
1783   
1784   if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
1785     naRuntimeError(c, "procedure.transition called on non-SID or -STAR");
1786   }
1787   
1788   ArrivalDeparture* ad = (ArrivalDeparture*) proc;
1789   Transition* trans = ad->findTransitionByName(naStr_data(args[0]));
1790   
1791   return ghostForProcedure(c, trans);
1792 }
1793
1794 // Table of extension functions.  Terminate with zeros.
1795 static struct { const char* name; naCFunction func; } funcs[] = {
1796   { "carttogeod", f_carttogeod },
1797   { "geodtocart", f_geodtocart },
1798   { "geodinfo", f_geodinfo },
1799   { "airportinfo", f_airportinfo },
1800   { "findAirportsWithinRange", f_findAirportsWithinRange },
1801   { "findAirportsByICAO", f_findAirportsByICAO },
1802   { "navinfo", f_navinfo },
1803   { "findNavaidsWithinRange", f_findNavaidsWithinRange },
1804   { "findNavaidByFrequency", f_findNavaidByFrequency },
1805   { "findNavaidsByFrequency", f_findNavaidsByFrequency },
1806   { "findNavaidsByID", f_findNavaidsByIdent },
1807   { "flightplan", f_route },
1808   { "magvar", f_magvar },
1809   { "courseAndDistance", f_courseAndDistance },
1810   { "greatCircleMove", f_greatCircleMove },
1811   { "tileIndex", f_tileIndex },
1812   { "tilePath", f_tilePath },
1813   { 0, 0 }
1814 };
1815
1816
1817 naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
1818 {
1819     airportPrototype = naNewHash(c);
1820     hashset(c, gcSave, "airportProto", airportPrototype);
1821   
1822     hashset(c, airportPrototype, "runway", naNewFunc(c, naNewCCode(c, f_airport_runway)));
1823     hashset(c, airportPrototype, "tower", naNewFunc(c, naNewCCode(c, f_airport_tower)));
1824     hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
1825     hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
1826     hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
1827     hashset(c, airportPrototype, "getApproachList", naNewFunc(c, naNewCCode(c, f_airport_approaches)));
1828     hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking)));
1829     hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid)));
1830     hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar)));
1831     hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach)));
1832   
1833     flightplanPrototype = naNewHash(c);
1834     hashset(c, gcSave, "flightplanProto", flightplanPrototype);
1835       
1836     hashset(c, flightplanPrototype, "getWP", naNewFunc(c, naNewCCode(c, f_flightplan_getWP)));
1837     hashset(c, flightplanPrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_flightplan_currentWP))); 
1838     hashset(c, flightplanPrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_flightplan_nextWP))); 
1839     hashset(c, flightplanPrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints)));
1840     hashset(c, flightplanPrototype, "appendWP", naNewFunc(c, naNewCCode(c, f_flightplan_appendWP))); 
1841     hashset(c, flightplanPrototype, "insertWP", naNewFunc(c, naNewCCode(c, f_flightplan_insertWP))); 
1842     hashset(c, flightplanPrototype, "insertWPAfter", naNewFunc(c, naNewCCode(c, f_flightplan_insertWPAfter))); 
1843     hashset(c, flightplanPrototype, "insertWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_insertWaypoints))); 
1844     hashset(c, flightplanPrototype, "cleanPlan", naNewFunc(c, naNewCCode(c, f_flightplan_clearPlan))); 
1845     hashset(c, flightplanPrototype, "clearWPType", naNewFunc(c, naNewCCode(c, f_flightplan_clearWPType))); 
1846     hashset(c, flightplanPrototype, "clone", naNewFunc(c, naNewCCode(c, f_flightplan_clone))); 
1847   
1848     waypointPrototype = naNewHash(c);
1849     hashset(c, gcSave, "wayptProto", waypointPrototype);
1850     
1851     hashset(c, waypointPrototype, "navaid", naNewFunc(c, naNewCCode(c, f_waypoint_navaid)));
1852     hashset(c, waypointPrototype, "runway", naNewFunc(c, naNewCCode(c, f_waypoint_runway)));
1853     hashset(c, waypointPrototype, "airport", naNewFunc(c, naNewCCode(c, f_waypoint_airport)));
1854   
1855     procedurePrototype = naNewHash(c);
1856     hashset(c, gcSave, "procedureProto", procedurePrototype);
1857  //   hashset(c, procedurePrototype, "runwayTransition", naNewFunc(c, naNewCCode(c, f_procedure_runwayTransition)));
1858     hashset(c, procedurePrototype, "transition", naNewFunc(c, naNewCCode(c, f_procedure_transition)));
1859    // hashset(c, procedurePrototype, "buildPath", naNewFunc(c, naNewCCode(c, f_procedure_build)));
1860   
1861     fpLegPrototype = naNewHash(c);
1862     hashset(c, gcSave, "fpLegProto", fpLegPrototype);
1863     hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
1864     hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude)));
1865   
1866     for(int i=0; funcs[i].name; i++) {
1867       hashset(c, globals, funcs[i].name,
1868       naNewFunc(c, naNewCCode(c, funcs[i].func)));
1869     }
1870   
1871   return naNil();
1872 }
1873
1874 void postinitNasalPositioned(naRef globals, naContext c)
1875 {
1876   naRef geoModule = naHash_cget(globals, (char*) "geo");
1877   if (naIsNil(geoModule)) {
1878     SG_LOG(SG_GENERAL, SG_WARN, "postinitNasalPositioned: geo.nas not loaded");
1879     return;
1880   }
1881   
1882   geoCoordClass = naHash_cget(geoModule, (char*) "Coord");
1883 }
1884
1885