]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned.cxx
266472e016eb7b9e62cf19f03966394c4c91b23c
[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 <simgear/scene/material/mat.hxx>
30 #include <simgear/magvar/magvar.hxx>
31 #include <simgear/timing/sg_time.hxx>
32
33 #include <Airports/runways.hxx>
34 #include <Airports/simple.hxx>
35 #include <Navaids/navlist.hxx>
36 #include <Navaids/procedure.hxx>
37 #include <Main/globals.hxx>
38 #include <Main/fg_props.hxx>
39 #include <Scenery/scenery.hxx>
40
41
42 static void ghostDestroy(void* g);
43 naGhostType PositionedGhostType = { ghostDestroy, "positioned" };
44
45 static FGPositioned* positionedGhost(naRef r)
46 {
47     if (naGhost_type(r) == &PositionedGhostType)
48         return (FGPositioned*) naGhost_ptr(r);
49     return 0;
50 }
51
52
53 static void ghostDestroy(void* g)
54 {
55     FGPositioned* pos = (FGPositioned*)g;
56     SGReferenced::put(pos); // unref
57 }
58
59 naRef ghostForPositioned(naContext c, const FGPositioned* pos)
60 {
61     if (!pos) {
62         return naNil();
63     }
64     
65     SGReferenced::get(pos); // take a ref
66     return naNewGhost(c, &PositionedGhostType, (void*) pos);
67 }
68
69 naRef hashForAirport(naContext c, const FGAirport* apt)
70 {
71     std::string id = apt->ident();
72     std::string name = apt->name();
73     
74   // build runways hash
75     naRef rwys = naNewHash(c);
76     for(unsigned int r=0; r<apt->numRunways(); ++r) {
77       FGRunway* rwy(apt->getRunwayByIndex(r));
78     
79       naRef rwyid = naStr_fromdata(naNewString(c),
80                                  const_cast<char *>(rwy->ident().c_str()),
81                                  rwy->ident().length());
82       naRef rwydata = hashForRunway(c, rwy);
83       naHash_set(rwys, rwyid, rwydata);
84     }
85   
86     naRef aptdata = naNewHash(c);
87 #define HASHSET(s,l,n) naHash_set(aptdata, naStr_fromdata(naNewString(c),s,l),n)
88     HASHSET("id", 2, naStr_fromdata(naNewString(c),
89             const_cast<char *>(id.c_str()), id.length()));
90     HASHSET("name", 4, naStr_fromdata(naNewString(c),
91             const_cast<char *>(name.c_str()), name.length()));
92     HASHSET("lat", 3, naNum(apt->getLatitude()));
93     HASHSET("lon", 3, naNum(apt->getLongitude()));
94     HASHSET("elevation", 9, naNum(apt->getElevation() * SG_FEET_TO_METER));
95     HASHSET("has_metar", 9, naNum(apt->getMetar()));
96     HASHSET("runways", 7, rwys);
97     
98     HASHSET("_positioned", 11, ghostForPositioned(c, apt));
99 #undef HASHSET
100     
101     return aptdata;
102 }
103
104 naRef hashForRunway(naContext c, FGRunway* rwy)
105 {
106     naRef rwyid = naStr_fromdata(naNewString(c),
107                   const_cast<char *>(rwy->ident().c_str()),
108                   rwy->ident().length());
109
110     naRef rwydata = naNewHash(c);
111 #define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
112     HASHSET("id", 2, rwyid);
113     HASHSET("lat", 3, naNum(rwy->latitude()));
114     HASHSET("lon", 3, naNum(rwy->longitude()));
115     HASHSET("heading", 7, naNum(rwy->headingDeg()));
116     HASHSET("length", 6, naNum(rwy->lengthM()));
117     HASHSET("width", 5, naNum(rwy->widthM()));
118     HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
119     HASHSET("stopway", 7, naNum(rwy->stopwayM()));
120         
121     if (rwy->ILS()) {
122       HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
123     }
124         
125     std::vector<flightgear::SID*> sids(rwy->getSIDs());
126     naRef sidVec = naNewVector(c);
127         
128     for (unsigned int s=0; s < sids.size(); ++s) {
129       naRef procId = naStr_fromdata(naNewString(c),
130                 const_cast<char *>(sids[s]->ident().c_str()),
131                 sids[s]->ident().length());
132       naVec_append(sidVec, procId);
133     }
134     HASHSET("sids", 4, sidVec); 
135         
136     std::vector<flightgear::STAR*> stars(rwy->getSTARs());
137     naRef starVec = naNewVector(c);
138       
139     for (unsigned int s=0; s < stars.size(); ++s) {
140       naRef procId = naStr_fromdata(naNewString(c),
141                 const_cast<char *>(stars[s]->ident().c_str()),
142                 stars[s]->ident().length());
143       naVec_append(starVec, procId);
144     }
145     HASHSET("stars", 5, starVec); 
146     
147     HASHSET("_positioned", 11, ghostForPositioned(c, rwy));
148 #undef HASHSET
149     return rwydata;
150 }
151
152 naRef hashForNavRecord(naContext c, const FGNavRecord* nav, const SGGeod& rel)
153 {
154     naRef navdata = naNewHash(c);
155 #define HASHSET(s,l,n) naHash_set(navdata, naStr_fromdata(naNewString(c),s,l),n)
156     HASHSET("id", 2, naStr_fromdata(naNewString(c),
157         const_cast<char *>(nav->ident().c_str()), nav->ident().length()));
158     HASHSET("name", 4, naStr_fromdata(naNewString(c),
159         const_cast<char *>(nav->name().c_str()), nav->name().length()));
160     HASHSET("frequency", 9, naNum(nav->get_freq()));
161     HASHSET("lat", 3, naNum(nav->get_lat()));
162     HASHSET("lon", 3, naNum(nav->get_lon()));
163     HASHSET("elevation", 9, naNum(nav->get_elev_ft() * SG_FEET_TO_METER));
164     HASHSET("type", 4, naStr_fromdata(naNewString(c),
165         const_cast<char *>(nav->nameForType(nav->type())), strlen(nav->nameForType(nav->type()))));
166     HASHSET("distance", 8, naNum(SGGeodesy::distanceNm( rel, nav->geod() ) * SG_NM_TO_METER ) );
167     HASHSET("bearing", 7, naNum(SGGeodesy::courseDeg( rel, nav->geod() ) ) );
168     
169     // record the real object as a ghost for further operations
170     HASHSET("_positioned",11, ghostForPositioned(c, nav));
171 #undef HASHSET
172     
173     return navdata;
174 }
175
176 bool geodFromHash(naRef ref, SGGeod& result)
177 {
178   if (!naIsHash(ref)) {
179     return false;
180   }
181   
182 // first, see if the hash contains a FGPositioned ghost - in which case
183 // we can read off its position directly
184   naRef posGhost = naHash_cget(ref, (char*) "_positioned");
185   if (!naIsNil(posGhost)) {
186     FGPositioned* pos = positionedGhost(posGhost);
187     result = pos->geod();
188     return true;
189   }
190   
191 // then check for manual latitude / longitude names
192   naRef lat = naHash_cget(ref, (char*) "lat");
193   naRef lon = naHash_cget(ref, (char*) "lon");
194   if (naIsNum(lat) && naIsNum(lon)) {
195     result = SGGeod::fromDeg(naNumValue(lat).num, naNumValue(lon).num);
196     return true;
197   }
198   
199 // check for any synonyms?
200 // latitude + longitude?
201   
202   return false;
203 }
204
205 // Convert a cartesian point to a geodetic lat/lon/altitude.
206 static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
207 {
208   double lat, lon, alt, xyz[3];
209   if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
210   for(int i=0; i<3; i++)
211     xyz[i] = naNumValue(args[i]).num;
212   sgCartToGeod(xyz, &lat, &lon, &alt);
213   lat *= SG_RADIANS_TO_DEGREES;
214   lon *= SG_RADIANS_TO_DEGREES;
215   naRef vec = naNewVector(c);
216   naVec_append(vec, naNum(lat));
217   naVec_append(vec, naNum(lon));
218   naVec_append(vec, naNum(alt));
219   return vec;
220 }
221
222 // Convert a geodetic lat/lon/altitude to a cartesian point.
223 static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
224 {
225   if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
226   double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
227   double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
228   double alt = naNumValue(args[2]).num;
229   double xyz[3];
230   sgGeodToCart(lat, lon, alt, xyz);
231   naRef vec = naNewVector(c);
232   naVec_append(vec, naNum(xyz[0]));
233   naVec_append(vec, naNum(xyz[1]));
234   naVec_append(vec, naNum(xyz[2]));
235   return vec;
236 }
237
238 // For given geodetic point return array with elevation, and a material data
239 // hash, or nil if there's no information available (tile not loaded). If
240 // information about the material isn't available, then nil is returned instead
241 // of the hash.
242 static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
243 {
244 #define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
245   if(argc < 2 || argc > 3)
246     naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
247   double lat = naNumValue(args[0]).num;
248   double lon = naNumValue(args[1]).num;
249   double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
250   const SGMaterial *mat;
251   SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
252   if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
253     return naNil();
254   naRef vec = naNewVector(c);
255   naVec_append(vec, naNum(elev));
256   naRef matdata = naNil();
257   if(mat) {
258     matdata = naNewHash(c);
259     naRef names = naNewVector(c);
260     const std::vector<std::string> n = mat->get_names();
261     for(unsigned int i=0; i<n.size(); i++)
262       naVec_append(names, naStr_fromdata(naNewString(c),
263                                          const_cast<char*>(n[i].c_str()), n[i].size()));
264     HASHSET("names", 5, names);
265     HASHSET("solid", 5, naNum(mat->get_solid()));
266     HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
267     HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
268     HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
269     HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
270     HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
271   }
272   naVec_append(vec, matdata);
273   return vec;
274 #undef HASHSET
275 }
276
277
278 class AirportInfoFilter : public FGAirport::AirportFilter
279 {
280 public:
281   AirportInfoFilter() : type(FGPositioned::AIRPORT) {
282   }
283   
284   virtual FGPositioned::Type minType() const {
285     return type;
286   }
287   
288   virtual FGPositioned::Type maxType() const {
289     return type;
290   }
291   
292   FGPositioned::Type type;
293 };
294
295 // Returns data hash for particular or nearest airport of a <type>, or nil
296 // on error.
297 //
298 // airportinfo(<id>);                   e.g. "KSFO"
299 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
300 // airportinfo()                        same as  airportinfo("airport")
301 // airportinfo(<lat>, <lon> [, <type>]);
302 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
303 {
304   static SGConstPropertyNode_ptr latn = fgGetNode("/position/latitude-deg", true);
305   static SGConstPropertyNode_ptr lonn = fgGetNode("/position/longitude-deg", true);
306   SGGeod pos;
307   FGAirport* apt = NULL;
308   
309   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
310     pos = SGGeod::fromDeg(args[1].num, args[0].num);
311     args += 2;
312     argc -= 2;
313   } else {
314     pos = SGGeod::fromDeg(lonn->getDoubleValue(), latn->getDoubleValue());
315   }
316   
317   double maxRange = 10000.0; // expose this? or pick a smaller value?
318   
319   AirportInfoFilter filter; // defaults to airports only
320   
321   if(argc == 0) {
322     // fall through and use AIRPORT
323   } else if(argc == 1 && naIsString(args[0])) {
324     const char *s = naStr_data(args[0]);
325     if(!strcmp(s, "airport")) filter.type = FGPositioned::AIRPORT;
326     else if(!strcmp(s, "seaport")) filter.type = FGPositioned::SEAPORT;
327     else if(!strcmp(s, "heliport")) filter.type = FGPositioned::HELIPORT;
328     else {
329       // user provided an <id>, hopefully
330       apt = FGAirport::findByIdent(s);
331       if (!apt) {
332         // return nil here, but don't raise a runtime error; this is a
333         // legitamate way to validate an ICAO code, for example in a
334         // dialog box or similar.
335         return naNil();
336       }
337     }
338   } else {
339     naRuntimeError(c, "airportinfo() with invalid function arguments");
340     return naNil();
341   }
342   
343   if(!apt) {
344     apt = FGAirport::findClosest(pos, maxRange, &filter);
345     if(!apt) return naNil();
346   }
347   
348   std::string id = apt->ident();
349   std::string name = apt->name();
350   
351   // set runway hash
352   naRef rwys = naNewHash(c);
353   for(unsigned int r=0; r<apt->numRunways(); ++r) {
354     FGRunway* rwy(apt->getRunwayByIndex(r));
355     
356     naRef rwyid = naStr_fromdata(naNewString(c),
357                                  const_cast<char *>(rwy->ident().c_str()),
358                                  rwy->ident().length());
359     
360     naRef rwydata = naNewHash(c);
361 #define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
362     HASHSET("id", 2, rwyid);
363     HASHSET("lat", 3, naNum(rwy->latitude()));
364     HASHSET("lon", 3, naNum(rwy->longitude()));
365     HASHSET("heading", 7, naNum(rwy->headingDeg()));
366     HASHSET("length", 6, naNum(rwy->lengthM()));
367     HASHSET("width", 5, naNum(rwy->widthM()));
368     HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
369     HASHSET("stopway", 7, naNum(rwy->stopwayM()));
370     
371     if (rwy->ILS()) {
372       HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
373     }
374     
375     std::vector<flightgear::SID*> sids(rwy->getSIDs());
376     naRef sidVec = naNewVector(c);
377     
378     for (unsigned int s=0; s < sids.size(); ++s) {
379       naRef procId = naStr_fromdata(naNewString(c),
380                                     const_cast<char *>(sids[s]->ident().c_str()),
381                                     sids[s]->ident().length());
382       naVec_append(sidVec, procId);
383     }
384     HASHSET("sids", 4, sidVec); 
385     
386     std::vector<flightgear::STAR*> stars(rwy->getSTARs());
387     naRef starVec = naNewVector(c);
388     
389     for (unsigned int s=0; s < stars.size(); ++s) {
390       naRef procId = naStr_fromdata(naNewString(c),
391                                     const_cast<char *>(stars[s]->ident().c_str()),
392                                     stars[s]->ident().length());
393       naVec_append(starVec, procId);
394     }
395     HASHSET("stars", 5, starVec); 
396     
397 #undef HASHSET
398     naHash_set(rwys, rwyid, rwydata);
399   }
400   
401   // set airport hash
402   naRef aptdata = naNewHash(c);
403 #define HASHSET(s,l,n) naHash_set(aptdata, naStr_fromdata(naNewString(c),s,l),n)
404   HASHSET("id", 2, naStr_fromdata(naNewString(c),
405                                   const_cast<char *>(id.c_str()), id.length()));
406   HASHSET("name", 4, naStr_fromdata(naNewString(c),
407                                     const_cast<char *>(name.c_str()), name.length()));
408   HASHSET("lat", 3, naNum(apt->getLatitude()));
409   HASHSET("lon", 3, naNum(apt->getLongitude()));
410   HASHSET("elevation", 9, naNum(apt->getElevation() * SG_FEET_TO_METER));
411   HASHSET("has_metar", 9, naNum(apt->getMetar()));
412   HASHSET("runways", 7, rwys);
413 #undef HASHSET
414   return aptdata;
415 }
416
417
418 // Returns vector of data hash for navaid of a <type>, nil on error
419 // navaids sorted by ascending distance 
420 // navinfo([<lat>,<lon>],[<type>],[<id>])
421 // lat/lon (numeric): use latitude/longitude instead of ac position
422 // type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
423 // id:                (partial) id of the fix
424 // examples:
425 // navinfo("vor")     returns all vors
426 // navinfo("HAM")     return all navaids who's name start with "HAM"
427 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
428 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM" 
429 //                           sorted by distance relative to lat=34, lon=48
430 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
431 {
432   static SGConstPropertyNode_ptr latn = fgGetNode("/position/latitude-deg", true);
433   static SGConstPropertyNode_ptr lonn = fgGetNode("/position/longitude-deg", true);
434   SGGeod pos;
435   
436   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
437     pos = SGGeod::fromDeg(args[1].num, args[0].num);
438     args += 2;
439     argc -= 2;
440   } else {
441     pos = SGGeod::fromDeg(lonn->getDoubleValue(), latn->getDoubleValue());
442   }
443   
444   FGPositioned::Type type = FGPositioned::INVALID;
445   nav_list_type navlist;
446   const char * id = "";
447   
448   if(argc > 0 && naIsString(args[0])) {
449     const char *s = naStr_data(args[0]);
450     if(!strcmp(s, "any")) type = FGPositioned::INVALID;
451     else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
452     else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
453     else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
454     else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
455     else if(!strcmp(s, "dme")) type = FGPositioned::DME;
456     else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
457     else id = s; // this is an id
458     ++args;
459     --argc;
460   } 
461   
462   if(argc > 0 && naIsString(args[0])) {
463     if( *id != 0 ) {
464       naRuntimeError(c, "navinfo() called with navaid id");
465       return naNil();
466     }
467     id = naStr_data(args[0]);
468     ++args;
469     --argc;
470   }
471   
472   if( argc > 0 ) {
473     naRuntimeError(c, "navinfo() called with too many arguments");
474     return naNil();
475   }
476   
477   navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
478   
479   naRef reply = naNewVector(c);
480   for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
481     const FGNavRecord * nav = *it;
482     
483     // set navdata hash
484     naRef navdata = naNewHash(c);
485 #define HASHSET(s,l,n) naHash_set(navdata, naStr_fromdata(naNewString(c),s,l),n)
486     HASHSET("id", 2, naStr_fromdata(naNewString(c),
487                                     const_cast<char *>(nav->ident().c_str()), nav->ident().length()));
488     HASHSET("name", 4, naStr_fromdata(naNewString(c),
489                                       const_cast<char *>(nav->name().c_str()), nav->name().length()));
490     HASHSET("frequency", 9, naNum(nav->get_freq()));
491     HASHSET("lat", 3, naNum(nav->get_lat()));
492     HASHSET("lon", 3, naNum(nav->get_lon()));
493     HASHSET("elevation", 9, naNum(nav->get_elev_ft() * SG_FEET_TO_METER));
494     HASHSET("type", 4, naStr_fromdata(naNewString(c),
495                                       const_cast<char *>(nav->nameForType(nav->type())), strlen(nav->nameForType(nav->type()))));
496     HASHSET("distance", 8, naNum(SGGeodesy::distanceNm( pos, nav->geod() ) * SG_NM_TO_METER ) );
497     HASHSET("bearing", 7, naNum(SGGeodesy::courseDeg( pos, nav->geod() ) ) );
498 #undef HASHSET
499     naVec_append( reply, navdata );
500   }
501   return reply;
502 }
503
504 // Convert a cartesian point to a geodetic lat/lon/altitude.
505 static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
506 {
507   SGGeod pos;
508   if ((argc == 1) && geodFromHash(args[0], pos)) {
509     // okay
510   } else if (argc == 2) {
511     double lat = naNumValue(args[0]).num,
512       lon = naNumValue(args[1]).num;
513     pos = SGGeod::fromDeg(lon, lat);
514   } else {
515     naRuntimeError(c, "magvar() expects 1 object arugment, or a lat/lon");
516   }
517   
518   double jd = globals->get_time_params()->getJD();
519   double magvarDeg = sgGetMagVar(pos, jd);
520   return naNum(magvarDeg);
521 }
522
523 // Table of extension functions.  Terminate with zeros.
524 static struct { const char* name; naCFunction func; } funcs[] = {
525   { "carttogeod", f_carttogeod },
526   { "geodtocart", f_geodtocart },
527   { "geodinfo", f_geodinfo },
528   { "airportinfo", f_airportinfo },
529   { "magvar", f_magvar },
530   { 0, 0 }
531 };
532
533 static void hashset(naContext c, naRef hash, const char* key, naRef val)
534 {
535   naRef s = naNewString(c);
536   naStr_fromdata(s, (char*)key, strlen(key));
537   naHash_set(hash, s, val);
538 }
539
540 naRef initNasalPositioned(naRef globals, naContext c)
541 {
542   for(int i=0; funcs[i].name; i++) {
543     hashset(c, globals, funcs[i].name,
544             naNewFunc(c, naNewCCode(c, funcs[i].func)));
545   }
546   
547   return naNil();
548 }
549