1 /*
2 	Enigma route and waypoint file format.
3         http://www.mglavionics.co.za/Docs/Enigma%20Waypoint%20format.pdf
4         Binary data are stored in little endian (Intel)
5 
6 	Copyright (C) 2009 Tobias Kretschmar, tobias.kretschmar@gmx.de
7 
8 	This program is free software; you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation; either version 2 of the License, or
11 	(at your option) any later version.
12 
13 	This program is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with this program; if not, write to the Free Software
20 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 
22  */
23 
24 #include "defs.h"
25 #include <cmath>
26 #include <cstdlib>
27 
28 #define MYNAME "Enigma binary route and waypoint file format"
29 
30 #define WTYPE_WAYPOINT        0 // Waypoint of unspecified type
31 #define WTYPE_AIRPORT         1 // Typical assignment for medium sized airports
32 #define WTYPE_MAJORAIRPORT    2 // Typical assignment for large and international airports
33 #define WTYPE_SEAPLANEBASE    3
34 #define WTYPE_AIRFIELD        4 // Typical assignment for smaller municipal airfields, glider fields etc
35 #define WTYPE_PRIVATEAIRFIELD 5
36 #define WTYPE_ULTRALIGHTFIELD 6
37 #define WTYPE_INTERSECTION    7 // (reporting point, boundary crossing)
38 #define WTYPE_HELIPORT        8
39 #define WTYPE_TACAN           9
40 #define WTYPE_NDBDME          10
41 #define WTYPE_NDB             11
42 #define WTYPE_VORDME          12
43 #define WTYPE_VORTAC          13
44 #define WTYPE_FANMARKER       14
45 #define WTYPE_VOR             15
46 #define WTYPE_REPPT           16
47 #define WTYPE_LFR             17
48 #define WTYPE_UHFNDB          18
49 #define WTYPE_MNDB            19
50 #define WTYPE_MNDBDME         20
51 #define WTYPE_LOM             21
52 #define WTYPE_LMM             22
53 #define WTYPE_LOCSDF          23
54 #define WTYPE_MLSISMLS        24
55 #define WTYPE_OTHERNAV        25 // Navaid not falling into any of the above types
56 #define WTYPE_ALTITUDECHANGE  26 // Location at which altitude should be changed
57 
58 union wpt_data {
59   int32_t     wp_altitude;  // Waypoint type 0-6,8: waypoint altitude in feet
60   int32_t     tg_altitude;  // Waypoint type 26: target altitude in feet
61   uint32_t    frequency;    // Waypoint type 9-25: freq in steps of 1000Hz (118Mhz = 180000)
62   int32_t     dummy;        // waypoint type 7, unused
63 };
64 
65 struct enigma_wpt {
66   int32_t			latitude;
67   int32_t			longitude;
68   union wpt_data  data;
69   uint8_t         waypoint_type;
70   uint8_t         shortname_len;  // number of used characters in shortname
71   char            shortname[6];   // ASCII, unused characters are "don't care" values
72   uint8_t         longname_len;   // number of used characters in longname
73   char            longname[27];   // ASCII, unused characters are "don't care" values
74 };
75 
76 static gbfile* file_in, *file_out;
77 
78 static void
rd_init(const QString & fname)79 rd_init(const QString& fname)
80 {
81   file_in = gbfopen_le(fname, "rb", MYNAME);
82 }
83 
decToEnigmaPosition(double val)84 static int32_t decToEnigmaPosition(double val)
85 {
86   int degrees = fabs(val);
87   double frac = fabs(val) - degrees;
88   int enigmadeg = degrees * 180000;
89   int enigmafrac = 180000 * frac;
90   int sign = (val < 0) ? -1 : +1;
91   return sign * (enigmadeg + enigmafrac);
92 }
93 
enigmaPositionToDec(int32_t val)94 static float enigmaPositionToDec(int32_t val)
95 {
96   int deg = abs(val) / 180000;
97   int enigmafrac = abs(val) % 180000;
98   double frac = (double)enigmafrac / 180000;
99   int sign = (val < 0) ? -1 : +1;
100   return sign * (deg + frac);
101 }
102 
103 static void
data_read()104 data_read()
105 {
106   enigma_wpt ewpt;
107   auto* route = new route_head;
108   route_add_head(route);
109 
110   while (1 == gbfread(&ewpt, sizeof(ewpt), 1, file_in)) {
111     auto* wpt = new Waypoint;
112     wpt->latitude = enigmaPositionToDec(le_read32(&ewpt.latitude));
113     wpt->longitude = enigmaPositionToDec(le_read32(&ewpt.longitude));
114     char*sn = xstrndup(ewpt.shortname, ewpt.shortname_len);
115     wpt->shortname = sn;
116     xfree(sn);
117 
118     char* ds = xstrndup(ewpt.longname, ewpt.longname_len);
119     wpt->description = ds;
120     xfree(ds);
121 
122     switch (ewpt.waypoint_type) {
123     case WTYPE_WAYPOINT:        // 0
124     case WTYPE_AIRPORT:         // 1
125     case WTYPE_MAJORAIRPORT:    // 2
126     case WTYPE_SEAPLANEBASE:    // 3
127     case WTYPE_AIRFIELD:        // 4
128     case WTYPE_PRIVATEAIRFIELD: // 5
129     case WTYPE_ULTRALIGHTFIELD: // 6
130     case WTYPE_HELIPORT:        // 8
131       // waypoint altitude
132       wpt->altitude = FEET_TO_METERS(le_read32(&ewpt.data.wp_altitude) - 1000);
133       break;
134     case WTYPE_ALTITUDECHANGE:  // 26
135       // target altitude
136       wpt->altitude = FEET_TO_METERS(le_read32(&ewpt.data.tg_altitude) - 1000);
137       break;
138     case WTYPE_INTERSECTION:    // 7
139       // unused
140       break;
141     default:
142       // frequency
143       // wpt->frequency = wpt.le_readu32(ewpt.data.frequency);
144       ;
145     }
146     route_add_wpt(route, wpt);
147   }
148 }
149 
150 static void
rd_deinit()151 rd_deinit()
152 {
153   gbfclose(file_in);
154 }
155 
156 static void
wr_init(const QString & fname)157 wr_init(const QString& fname)
158 {
159   file_out = gbfopen_le(fname, "wb", MYNAME);
160 }
161 
162 #ifndef min
163 #define min(a,b) (((a) < (b)) ? (a) : (b))
164 #endif
165 #ifndef max
166 #define max(a,b) (((a) > (b)) ? (a) : (b))
167 #endif
168 
169 static void
enigma_waypt_disp(const Waypoint * wpt)170 enigma_waypt_disp(const Waypoint* wpt)
171 {
172   enigma_wpt ewpt;
173 
174   memset(&ewpt, 0, sizeof(ewpt));
175 
176   le_write32(&ewpt.latitude, decToEnigmaPosition(wpt->latitude));
177   le_write32(&ewpt.longitude, decToEnigmaPosition(wpt->longitude));
178   ewpt.waypoint_type = WTYPE_WAYPOINT;
179   if (wpt->altitude != unknown_alt) {
180     le_write32(&ewpt.data.wp_altitude, METERS_TO_FEET(wpt->altitude) + 1000);
181   }
182   if (wpt->shortname != nullptr) {
183     ewpt.shortname_len = (uint8_t) min(6, strlen(CSTRc(wpt->shortname)));
184     memcpy(ewpt.shortname, CSTRc(wpt->shortname), ewpt.shortname_len);
185   }
186   if (wpt->description != nullptr) {
187     ewpt.longname_len = (uint8_t) min(27, strlen(CSTRc(wpt->description)));
188     memcpy(ewpt.longname, CSTRc(wpt->description), ewpt.longname_len);
189   }
190   gbfwrite(&ewpt, sizeof(ewpt), 1, file_out);
191 }
192 
193 static void
data_write()194 data_write()
195 {
196   route_disp_all(nullptr, nullptr, enigma_waypt_disp);
197 }
198 
199 static void
wr_deinit()200 wr_deinit()
201 {
202   gbfclose(file_out);
203 }
204 
205 ff_vecs_t enigma_vecs = {
206   ff_type_file,
207   {
208     ff_cap_none,                    /* waypoints */
209     ff_cap_none,                    /* tracks */
210     (ff_cap)(ff_cap_read | ff_cap_write) 	/* routes */
211   },
212   rd_init,
213   wr_init,
214   rd_deinit,
215   wr_deinit,
216   data_read,
217   data_write,
218   nullptr,
219   nullptr,
220   CET_CHARSET_ASCII, 0,	/* CET-REVIEW */
221   NULL_POS_OPS,
222   nullptr
223 };
224