common.nas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. # common constants and functions for the Helionix MFD framework
  2. var DEBUG = 0; # very (!) verbose debug info on console
  3. var FAST= 0; # timer interval for fast updated instruments, such a compass, speed, needles
  4. var SLOW = 1/2; # timer interval for slow indications such as Nav Frequencies or IDs as text
  5. var ENABLE_THREADS = 0; # unused, not implemented anymore in mfd.nas
  6. var USE_CENTRAL_UPDATE_LOOP = 0; # collects all sensors' timers into one central timer loop
  7. # those need to be in the global scope
  8. var debugloop = 0;
  9. var page_setup={};
  10. var mfdctrl = [1,1,1,1];
  11. var mfdroot = "/instrumentation/efis/";
  12. var mfd = [];
  13. var updaters = []; #this is a list containing all objects that need periodic update
  14. # some constants needed for the XML code
  15. props.globals.initNode("/constants/true",1,"BOOL");
  16. props.globals.initNode("/constants/false",0,"BOOL");
  17. props.globals.initNode("/constants/zero",0,"DOUBLE");
  18. # init some configuration parameters for the MFDs
  19. var rngprop = mfdroot ~ "navd/range";
  20. var scaleprop = mfdroot ~ "navd/scale";
  21. var numprop = mfdroot ~ "vmd/nums";
  22. var datapageprop = mfdroot ~ "vmd/datapage";
  23. var mapprop = mfdroot ~ "dmap/terrainmode";
  24. var planprop = mfdroot ~ "dmap/planmode";
  25. var dataprop = mfdroot ~ "dmap/db-mode";
  26. var databpage = mfdroot ~ "dmap/db-page";
  27. var datafltr = mfdroot ~ "dmap/db-filter";
  28. setprop(rngprop, 20);
  29. setprop(mapprop, 0);
  30. setprop(planprop, 1);
  31. setprop(dataprop, 1);
  32. setprop(databpage, 0);
  33. setprop(datafltr, 1);
  34. setprop(datapageprop, 0);
  35. # helper functions
  36. #
  37. #
  38. var max = func(a, b) a > b ? a : b;
  39. var min = func(a, b) a < b ? a : b;
  40. var clamp = func(v, min = 0, max = 1) v < min ? min : v > max ? max : v;
  41. var rem = func(val, div) { return val-int(val/div)*div;}
  42. # air data computer and its methods, the actual sensors of the ADC are defined in airdata.nas
  43. #
  44. #
  45. var adc = {
  46. update_items: [],
  47. sensor_idx: 0,
  48. add : func (params) {
  49. params.name = "idx_"~me.sensor_idx;
  50. me[params.name] = Sensor.new(params);
  51. me.sensor_idx += 1;
  52. return me[params.name];
  53. },
  54. _recurse_ : func (i, f) {
  55. # recurse deep into all sensors in airdatacomputer
  56. # and apply a function f to all sensor objects
  57. var k = keys(i);
  58. foreach (var x; k) {
  59. if (contains(i[x], "is_a_sensor")) {
  60. f( i[x] );
  61. } else {
  62. if (typeof(i[x]) == "hash") me._recurse_(i[x], f);
  63. }
  64. }
  65. },
  66. _benchmark_ : func () {
  67. me._recurse_(me, func (x) { benchmark_output(x, debugloop) } );
  68. print("- - - - - -");
  69. },
  70. _del_ : func () {
  71. if (DEBUG) print("adc ... del");
  72. me._recurse_(me, func (x) { x._del_() } );
  73. },
  74. _refresh_ : func () {
  75. if (DEBUG) print("adc ... refresh");
  76. me._recurse_(me, func (x) { x.compile() } );
  77. me._recurse_(me, func (x) { x.refresh() } );
  78. },
  79. initUpdates : func (dt) {
  80. # collect all sensors, that have no listeners in update_items
  81. me._recurse_(me, func (x) {
  82. if (x.li == nil) {
  83. append( me.update_items, x);
  84. }
  85. }
  86. );
  87. me._timer_ = maketimer(dt, func {
  88. foreach (var update_item; me.update_items) {
  89. update_item.update();
  90. }
  91. }
  92. );
  93. me._timer_.start();
  94. }
  95. };
  96. var mfd_add = func(s1, s2) {
  97. var i = size(mfd);
  98. append(mfd, Canvas_mfd.new(i, s1, s2) );
  99. return i;
  100. };
  101. var colored = func(a) {
  102. # color code the terminal output
  103. if (a>20)
  104. return "\x1b[7;49;91m" ~ a ~ "\x1b[0m"
  105. elsif (a>5)
  106. return "\x1b[7;49;92m" ~ a ~ "\x1b[0m"
  107. elsif (a>0)
  108. return "\x1b[7;49;39m" ~ a ~ "\x1b[0m"
  109. else
  110. return a
  111. };
  112. var benchmark_output = func(a,k) {
  113. if (a.calls>0) print(a.name, ":", colored(int(a.calls/k)), "/", int(a.freq/k) );
  114. };
  115. # find an element in a list
  116. #
  117. #
  118. var isin = func (needle, haystack) {
  119. var i=0;
  120. foreach (x; haystack) {
  121. if (x==needle) return i;
  122. i += 1;
  123. }
  124. return -1;
  125. }
  126. var pad2strings = func(a, b, l) {
  127. var d = l-size(a)-size(b);
  128. if (d>0) {
  129. var spc = left(" ",d);
  130. return (a ~ spc ~ b);
  131. } else {
  132. return substr(a, d) ~ " " ~ b;
  133. }
  134. }
  135. var toggleprop = func (prop) setprop(prop, getprop(prop) == nil ? 0 : !getprop(prop) );
  136. var incrprop = func (prop) setprop(prop, getprop(prop) == nil ? 0 : getprop(prop)+1 );
  137. var decrprop = func (prop) setprop(prop, getprop(prop) == nil ? 0 : max(0, getprop(prop)-1) );
  138. var cycleprop = func (prop, opts) {
  139. k = getprop(prop);
  140. if (k==nil) {
  141. setprop(prop, opts[0]);
  142. return;
  143. }
  144. i = isin(k, opts);
  145. if (i<0) {
  146. setprop(prop, opts[0]);
  147. return;
  148. }
  149. # cycle to next element in list
  150. i = rem(i+1, size(opts));
  151. setprop(prop, opts[i]);
  152. }
  153. # simple timer class with methods to inherit from
  154. # for periodic execution and a start delay
  155. #
  156. var Timerloop = {
  157. new: func {
  158. return { parents: [Timerloop] };
  159. },
  160. run: func (period, delay=0) {
  161. me.period=period;
  162. settimer( func me._loop() , delay);
  163. },
  164. _loop: func {
  165. # update method must be provided by the derived class
  166. me.update();
  167. settimer( func me._loop(), me.period);
  168. },
  169. };
  170. var font_mapper = func(family, weight)
  171. {
  172. if( weight == "normal" ) {
  173. return "Helionix.ttf"
  174. } else {
  175. return "LiberationFonts/LiberationSansNarrow-Bold.ttf";
  176. }
  177. };
  178. var ghostcopy = func (gin) {
  179. out = {};
  180. out.id = gin.id;
  181. if (ghosttype(gin) != "flightplan-leg") {
  182. out.type = gin.type;
  183. out.name = gin.name;
  184. out.elevation = gin.elevation or 0;
  185. out.frequency = gin.frequency or 0;
  186. out.course = gin.course or 0;
  187. } else {
  188. out.type = "waypoint";
  189. out.name = "";
  190. out.elevation = 0;
  191. out.frequency = 0;
  192. out.course = 0;
  193. }
  194. return out;
  195. }
  196. var sortNavaidByDistance = func (listofnav) {
  197. #return the sorted list
  198. return sort(listofnav,
  199. func (a,b) {return a.distance_nm > b.distance_nm ? 1 : -1}
  200. );
  201. }
  202. # linear interpolator class
  203. # by litzi
  204. # adopted from Zivko Edge
  205. # original author Torsten Dreyer
  206. var LUT = {
  207. new: func(pairs, nn=100)
  208. {
  209. var m = { parents: [ LUT ] };
  210. m.pairs = pairs;
  211. m.pairs2 = [];
  212. m.d = [];
  213. m.n = size(pairs)-1;
  214. m.resample(nn);
  215. return m;
  216. },
  217. resample: func (nn) {
  218. # resample pairs with equal spacing for
  219. # improved speed
  220. me.res = (me.pairs[-1][0]-me.pairs[0][0])/(nn-1);
  221. for (var x=me.pairs[0][0]; x<me.pairs[-1][0]; x+=me.res)
  222. append(me.pairs2, [x, me._get(x)]);
  223. me.nn = size(me.pairs2)-1;
  224. for (var i=0; i<me.nn; i+=1)
  225. append(me.d, (me.pairs2[i+1][1]-me.pairs2[i][1])/me.res);
  226. },
  227. _get: func(x)
  228. {
  229. if( x <= me.pairs[0][0] ) {
  230. return me.pairs[0][1];
  231. }
  232. if( x >= me.pairs[me.n][0] ) {
  233. return me.pairs[me.n][1];
  234. }
  235. for( var i = 0; i < me.n; i+=1 ) {
  236. if( x > me.pairs[i][0] and x <= me.pairs[i+1][0] ) {
  237. var x1 = me.pairs[i][0];
  238. var x2 = me.pairs[i+1][0];
  239. var y1 = me.pairs[i][1];
  240. var y2 = me.pairs[i+1][1];
  241. return (x-x1)/(x2-x1)*(y2-y1)+y1;
  242. }
  243. }
  244. return me.pairs[i][1];
  245. },
  246. get: func (x)
  247. {
  248. x = math.clamp(x,me.pairs2[0][0],me.pairs2[-1][0]);
  249. var i = math.clamp( int( (x-me.pairs2[0][0]) / me.res), 0, me.nn-1);
  250. return me.pairs2[i][1]+(x-me.pairs2[i][0])*me.d[i];
  251. },
  252. del: func() {}
  253. };
  254. var checkrules = func (p) {
  255. if (p.prop != nil)
  256. return 1;
  257. if (p.sensor != nil)
  258. if (p.sensor.prop != nil)
  259. return 1;
  260. return 0;
  261. };
  262. var change_all_ids = func (node, post) {
  263. if(node == nil)
  264. return;
  265. var children = node.getChildren();
  266. foreach(var c; children) {
  267. var name = c.getName();
  268. if (name=="group" or name=="text" or name=="path") {
  269. # recurse deeper
  270. change_all_ids(c, post);
  271. } elsif (name == "id") {
  272. var val = c.getValue();
  273. c.setValue(val~post);
  274. }
  275. }
  276. };
  277. var splitpath = func (p, a, b) {
  278. var out = "";
  279. foreach (var x; split("/", p)[a:b])
  280. out = out~x~"/";
  281. return out;
  282. };
  283. var compile_filter_function = func (f) {
  284. # construct a string to compile a filter function from
  285. # use sensor value as a argument to a function
  286. if (f.funcof)
  287. var functionbody = "var x = " ~ f.funcof ~ "(arg[0]);";
  288. else
  289. var functionbody = "var x = arg[0];";
  290. if (f.mod)
  291. functionbody ~= "x = math.mod(x," ~ f.mod ~ ");";
  292. if (f.max != 1e20 or f.min != -1e20)
  293. functionbody ~= "x = math.clamp(x," ~ f.min ~ "," ~ f.max ~ ");";
  294. if (f.scale != 1 or f.offset != 0)
  295. functionbody ~= "x = x * " ~ f.scale ~ " + " ~ f.offset ~";";
  296. if (f.trunc)
  297. functionbody ~= "x = int(x);";
  298. if (f.abs)
  299. functionbody ~= "x = abs(x);";
  300. # check if we need a string output
  301. if (f.format == nil) {
  302. functionbody ~= "";
  303. } else {
  304. functionbody ~= "x= sprintf(\"" ~ f.format ~"\", x);";
  305. }
  306. if (DEBUG)
  307. print(functionbody);
  308. return functionbody;
  309. };
  310. io.include(HELIONIXPATH ~ "Nasal/sensor.nas");
  311. io.include(HELIONIXPATH ~ "Nasal/mfd.nas");
  312. # start the air data computer sensor loops
  313. io.include(HELIONIXPATH ~ "Nasal/airdata.nas");