47#include "acfutils/perf.h"
49#include "acfutils/types.h"
51#define RWY_PROXIMITY_LAT_FRACT 3
52#define RWY_PROXIMITY_LON_DISPL 609.57
54#define RWY_APCH_PROXIMITY_LAT_ANGLE 3.3
55#define RWY_APCH_PROXIMITY_LON_DISPL 5500
57#define RWY_APCH_PROXIMITY_LAT_DISPL (RWY_APCH_PROXIMITY_LON_DISPL * \
58 __builtin_tan(DEG2RAD(RWY_APCH_PROXIMITY_LAT_ANGLE)))
59#define ARPTDB_CACHE_VERSION 21
61#define VGSI_LAT_DISPL_FACT 2
62#define VGSI_HDG_MATCH_THRESH 5
63#define ILS_HDG_MATCH_THRESH 2
68#define ILS_GS_GND_OFFSET 5
71#define RWY_GPA_LIMIT 10
72#define RWY_TCH_LIMIT 200
73#define TCH_IS_VALID(tch) (tch > 0 && tch < RWY_TCH_LIMIT)
75#define TILE_NAME_FMT "%+03.0f%+04.0f"
77#define ARPT_LOAD_LIMIT NM2MET(8)
91typedef struct tile_s {
106 {
"AFG",
"Afghanistan" },
107 {
"ALA",
"Åland Islands" },
108 {
"ALB",
"Albania" },
109 {
"DZA",
"Algeria" },
110 {
"ASM",
"American Samoa" },
111 {
"AND",
"Andorra" },
113 {
"AIA",
"Anguilla" },
114 {
"ATA",
"Antarctica" },
115 {
"ATG",
"Antigua and Barbuda" },
116 {
"ARG",
"Argentina" },
117 {
"ARM",
"Armenia" },
119 {
"AUS",
"Australia" },
120 {
"AUT",
"Austria" },
121 {
"AZE",
"Azerbaijan" },
122 {
"BHS",
"Bahamas" },
123 {
"BHR",
"Bahrain" },
124 {
"BGD",
"Bangladesh" },
125 {
"BRB",
"Barbados" },
126 {
"BLR",
"Belarus" },
127 {
"BEL",
"Belgium" },
130 {
"BMU",
"Bermuda" },
132 {
"BOL",
"Bolivia" },
133 {
"BES",
"Bonaire, Sint Eustatius and Saba" },
134 {
"BIH",
"Bosnia and Herzegovina" },
135 {
"BWA",
"Botswana" },
136 {
"BVT",
"Bouvet Island" },
138 {
"IOT",
"British Indian Ocean Territory" },
139 {
"BRN",
"Brunei Darussalam" },
140 {
"BGR",
"Bulgaria" },
141 {
"BFA",
"Burkina Faso" },
142 {
"BDI",
"Burundi" },
143 {
"CPV",
"Cabo Verde" },
144 {
"KHM",
"Cambodia" },
145 {
"CMR",
"Cameroon" },
147 {
"CYM",
"Cayman Islands" },
148 {
"CAF",
"Central African Republic" },
152 {
"CXR",
"Christmas Island" },
153 {
"CCK",
"Cocos Islands" },
154 {
"COL",
"Colombia" },
155 {
"COM",
"Comoros" },
156 {
"COD",
"Democratic Republic of the Congo" },
158 {
"COK",
"Cook Islands" },
159 {
"CRI",
"Costa Rica" },
160 {
"CIV",
"Côte d'Ivoire" },
161 {
"HRV",
"Croatia" },
163 {
"CUW",
"Curaçao" },
165 {
"CZE",
"Czechia" },
166 {
"DNK",
"Denmark" },
167 {
"DJI",
"Djibouti" },
168 {
"DMA",
"Dominica" },
169 {
"DOM",
"Dominican Republic" },
170 {
"ECU",
"Ecuador" },
172 {
"SLV",
"El Salvador" },
173 {
"GNQ",
"Equatorial Guinea" },
174 {
"ERI",
"Eritrea" },
175 {
"EST",
"Estonia" },
176 {
"SWZ",
"Eswatini" },
177 {
"ETH",
"Ethiopia" },
178 {
"FLK",
"Falkland Islands" },
179 {
"FRO",
"Faroe Islands" },
181 {
"FIN",
"Finland" },
183 {
"GUF",
"French Guiana" },
184 {
"PYF",
"French Polynesia" },
185 {
"ATF",
"French Southern Territories" },
188 {
"GEO",
"Georgia" },
189 {
"DEU",
"Germany" },
191 {
"GIB",
"Gibraltar" },
193 {
"GRL",
"Greenland" },
194 {
"GRD",
"Grenada" },
195 {
"GLP",
"Guadeloupe" },
197 {
"GTM",
"Guatemala" },
198 {
"GGY",
"Guernsey" },
200 {
"GNB",
"Guinea-Bissau" },
203 {
"HMD",
"Heard Island and McDonald Islands" },
204 {
"VAT",
"Holy See" },
205 {
"HND",
"Honduras" },
206 {
"HKG",
"Hong Kong" },
207 {
"HUN",
"Hungary" },
208 {
"ISL",
"Iceland" },
210 {
"IDN",
"Indonesia" },
213 {
"IRL",
"Ireland" },
214 {
"IMN",
"Isle of Man" },
217 {
"JAM",
"Jamaica" },
221 {
"KAZ",
"Kazakhstan" },
223 {
"KIR",
"Kiribati" },
224 {
"PRK",
"Democratic People's Republic of Korea" },
225 {
"KOR",
"Republic of Korea" },
227 {
"KGZ",
"Kyrgyzstan" },
230 {
"LBN",
"Lebanon" },
231 {
"LSO",
"Lesotho" },
232 {
"LBR",
"Liberia" },
234 {
"LIE",
"Liechtenstein" },
235 {
"LTU",
"Lithuania" },
236 {
"LUX",
"Luxembourg" },
238 {
"MKD",
"Republic of North Macedonia" },
239 {
"MDG",
"Madagascar" },
241 {
"MYS",
"Malaysia" },
242 {
"MDV",
"Maldives" },
245 {
"MHL",
"Marshall Islands" },
246 {
"MTQ",
"Martinique" },
247 {
"MRT",
"Mauritania" },
248 {
"MUS",
"Mauritius" },
249 {
"MYT",
"Mayotte" },
251 {
"FSM",
"Micronesia" },
252 {
"MDA",
"Moldova" },
254 {
"MNG",
"Mongolia" },
255 {
"MNE",
"Montenegro" },
256 {
"MSR",
"Montserrat" },
257 {
"MAR",
"Morocco" },
258 {
"MOZ",
"Mozambique" },
259 {
"MMR",
"Myanmar" },
260 {
"NAM",
"Namibia" },
263 {
"NLD",
"Netherlands" },
264 {
"NCL",
"New Caledonia" },
265 {
"NZL",
"New Zealand" },
266 {
"NIC",
"Nicaragua" },
268 {
"NGA",
"Nigeria" },
270 {
"NFK",
"Norfolk Island" },
271 {
"MNP",
"Northern Mariana Islands" },
274 {
"PAK",
"Pakistan" },
276 {
"PSE",
"Palestine, State of" },
278 {
"PNG",
"Papua New Guinea" },
279 {
"PRY",
"Paraguay" },
281 {
"PHL",
"Philippines" },
282 {
"PCN",
"Pitcairn" },
284 {
"PRT",
"Portugal" },
285 {
"PRI",
"Puerto Rico" },
287 {
"REU",
"Réunion" },
288 {
"ROU",
"Romania" },
289 {
"RUS",
"Russian Federation" },
291 {
"BLM",
"Saint Barthélemy" },
292 {
"SHN",
"Saint Helena" },
293 {
"KNA",
"Saint Kitts and Nevis" },
294 {
"LCA",
"Saint Lucia" },
295 {
"MAF",
"Saint Martin" },
296 {
"SPM",
"Saint Pierre and Miquelon" },
297 {
"VCT",
"Saint Vincent and the Grenadines" },
299 {
"SMR",
"San Marino" },
300 {
"STP",
"Sao Tome and Principe" },
301 {
"SAU",
"Saudi Arabia" },
302 {
"SEN",
"Senegal" },
304 {
"SYC",
"Seychelles" },
305 {
"SLE",
"Sierra Leone" },
306 {
"SGP",
"Singapore" },
307 {
"SXM",
"Sint Maarten" },
308 {
"SVK",
"Slovakia" },
309 {
"SVN",
"Slovenia" },
310 {
"SLB",
"Solomon Islands" },
311 {
"SOM",
"Somalia" },
312 {
"ZAF",
"South Africa" },
313 {
"SGS",
"South Georgia and the South Sandwich Islands" },
314 {
"SSD",
"South Sudan" },
316 {
"LKA",
"Sri Lanka" },
318 {
"SUR",
"Suriname" },
319 {
"SJM",
"Svalbard and Jan Mayen" },
321 {
"CHE",
"Switzerland" },
322 {
"SYR",
"Syrian Arab Republic" },
324 {
"TJK",
"Tajikistan" },
325 {
"TZA",
"Tanzania" },
326 {
"THA",
"Thailand" },
327 {
"TLS",
"Timor-Leste" },
329 {
"TKL",
"Tokelau" },
331 {
"TTO",
"Trinidad and Tobago" },
332 {
"TUN",
"Tunisia" },
334 {
"TKM",
"Turkmenistan" },
335 {
"TCA",
"Turks and Caicos Islands" },
338 {
"UKR",
"Ukraine" },
339 {
"ARE",
"United Arab Emirates" },
341 {
"UMI",
"United States Minor Outlying Islands" },
342 {
"USA",
"United States of America" },
343 {
"URY",
"Uruguay" },
344 {
"UZB",
"Uzbekistan" },
345 {
"VUT",
"Vanuatu" },
346 {
"VEN",
"Venezuela" },
347 {
"VNM",
"Viet Nam" },
348 {
"VGB",
"British Virgin Islands" },
349 {
"VIR",
"U.S. Virgin Islands" },
350 {
"WLF",
"Wallis and Futuna" },
351 {
"ESH",
"Western Sahara" },
354 {
"ZWE",
"Zimbabwe" }
357static airport_t *apt_dat_lookup(
airportdb_t *db,
const char *ident);
358static void apt_dat_insert(
airportdb_t *db, airport_t *arpt);
359static void free_airport(airport_t *arpt);
361static bool_t load_airport(airport_t *arpt);
362static void load_rwy_info(runway_t *rwy);
367recreate_icao_iata_tables(
airportdb_t *db,
unsigned cap)
371 htbl2_empty(&db->icao_index, sizeof (
arpt_index_t), NULL, NULL);
372 htbl2_destroy(&db->icao_index);
373 htbl2_empty(&db->iata_index, sizeof (
arpt_index_t), NULL, NULL);
374 htbl2_destroy(&db->iata_index);
376 htbl2_create(&db->icao_index, MAX(
P2ROUNDUP(cap), 16),
378 htbl2_create(&db->iata_index, MAX(
P2ROUNDUP(cap), 16),
389geo_pos2tile_pos(
geo_pos2_t pos, bool_t div_by_10)
393 floor(pos.
lon / 10) * 10));
402airport_compar(
const void *a,
const void *b)
404 const airport_t *aa = a, *ab = b;
405 int res = strcmp(aa->ident, ab->ident);
417tile_compar(
const void *a,
const void *b)
419 const tile_t *ta = a, *tb = b;
421 if (ta->pos.
lat < tb->pos.lat) {
423 }
else if (ta->pos.
lat == tb->pos.lat) {
424 if (ta->pos.
lon < tb->pos.lon)
426 else if (ta->pos.
lon == tb->pos.lon)
439runway_compar(
const void *a,
const void *b)
441 const runway_t *ra = a, *rb = b;
442 int res = strcmp(ra->joint_id, rb->joint_id);
444 if (res != 0 && strcmp(ra->joint_id, rb->rev_joint_id) == 0)
455ramp_start_compar(
const void *a,
const void *b)
458 int res = strcmp(rs_a->
name, rs_b->name);
480 bool_t created = B_FALSE;
481 tile_t srch = { .pos = pos };
488 tile =
avl_find(&db->geo_table, &srch, &where);
489 if (tile == NULL && create) {
492 avl_create(&tile->arpts, airport_compar, sizeof (airport_t),
493 offsetof(airport_t, tile_node));
497 if (created_p != NULL)
498 *created_p = created;
509make_rwy_bbox(
vect2_t thresh_v,
vect2_t dir_v,
double width,
double len,
519 ASSERT(!isnan(long_displ));
545 bbox[1] =
vect2_add(bbox[0], len_displ_v);
546 bbox[2] =
vect2_add(bbox[3], len_displ_v);
557rwy_is_hard(rwy_surf_t surf)
568 case RWY_SURF_GRAVEL:
569 case RWY_SURF_DRY_LAKEBED:
571 case RWY_SURF_SNOWICE:
586 airport_t search, *result;
591 lacf_strlcpy(search.ident, ident, sizeof (search.ident));
593 result =
avl_find(&db->apt_dat, &search, NULL);
595 load_airport(result);
624 tile = geo_table_get_tile(db,
GEO3_TO_GEO2(arpt->refpt), B_TRUE, NULL);
625 ASSERT(!arpt->geo_linked);
628 arpt->geo_linked = B_TRUE;
636geo_unlink_airport(
airportdb_t *db, airport_t *arpt)
642 tile = geo_table_get_tile(db,
GEO3_TO_GEO2(arpt->refpt), B_TRUE, NULL);
645 arpt->geo_linked = B_FALSE;
655airport_auto_refpt(airport_t *arpt)
663 ASSERT(isnan(arpt->refpt.lat) && isnan(arpt->refpt.lon));
664 ASSERT(!arpt->load_complete);
665 ASSERT(!arpt->geo_linked);
666 ASSERT(!isnan(arpt->refpt.elev));
669 p1 = rwy->ends[0].thr;
670 p2 = rwy->ends[1].thr;
673 arpt->refpt.lat = (p1.
lat + p2.
lat) / 2;
674 arpt->refpt.lon = (p1.
lon + p2.
lon) / 2;
675 arpt->refpt_m.lat = arpt->refpt.lat;
676 arpt->refpt_m.lon = arpt->refpt.lon;
688 pos = geo_pos2tile_pos(pos, B_TRUE);
689 snprintf(lat_lon,
sizeof (lat_lon), TILE_NAME_FMT, pos.
lat, pos.
lon);
692 return (
mkpathname(db->cachedir, lat_lon, suffix, NULL));
694 return (
mkpathname(db->cachedir, lat_lon, NULL));
712 FILE *scenery_packs_ini;
718 fname =
mkpathname(db->xpdir,
"Custom Scenery",
"scenery_packs.ini",
720 scenery_packs_ini = fopen(fname,
"r");
724 if (scenery_packs_ini != NULL) {
728 while (!feof(scenery_packs_ini)) {
731 if (getline(&line, &linecap, scenery_packs_ini) <= 0)
734 if (strstr(line,
"SCENERY_PACK ") != line)
736 scn_name = &line[13];
741 "Earth nav data",
"apt.dat", NULL);
744 fclose(scenery_packs_ini);
749 e->fname =
mkpathname(db->xpdir,
"Resources",
"default scenery",
750 "default apt dat",
"Earth nav data",
"apt.dat", NULL);
754 e->fname =
mkpathname(db->xpdir,
"Global Scenery",
755 "Global Airports",
"Earth nav data",
"apt.dat", NULL);
765read_apt_dat_insert(
airportdb_t *db, airport_t *arpt)
772 ASSERT(!isnan(arpt->refpt.lat) && !isnan(arpt->refpt.lon));
773 apt_dat_insert(db, arpt);
774 geo_link_airport(db, arpt);
781normalize_name(iconv_t *cd_p,
char *str_in,
char *str_out,
size_t cap)
787 unsigned l = strlen(str_in) + 1;
788 char *str_conv =
safe_calloc(l,
sizeof (*str_conv));
789 char *conv_in = str_in, *conv_out = str_conv;
790 size_t conv_in_sz = strlen(str_in);
791 size_t conv_out_sz = l;
793 iconv(*cd_p, &conv_in, &conv_in_sz, &conv_out, &conv_out_sz);
794 for (
size_t i = 0, j = 0; str_conv[i] !=
'\0' && j + 1 < cap; i++) {
795 if (str_conv[i] !=
'\'' && str_conv[i] !=
'`' &&
796 str_conv[i] !=
'^' && str_conv[i] !=
'\\' &&
797 str_conv[i] !=
'"') {
798 str_out[j++] = str_conv[i];
805concat_comps(
char **comps,
size_t count)
812 for (
size_t i = 0; i < count; i++) {
815 comps[i], i + 1 < count ?
" " :
"");
829parse_apt_dat_1_line(
airportdb_t *db,
const char *line, iconv_t *cd_p,
830 airport_t **dup_arpt_p)
837 const char *new_ident;
840 char **comps =
strsplit(line,
" ", B_TRUE, &ncomps);
841 airport_t *arpt = NULL;
846 if (dup_arpt_p != NULL)
849 ASSERT(strcmp(comps[0],
"1") == 0);
853 new_ident = comps[4];
854 pos.
elev = atof(comps[1]);
858 name = concat_comps(&comps[5], ncomps - 5);
861 arpt = apt_dat_lookup(db, new_ident);
867 if (dup_arpt_p != NULL)
873 avl_create(&arpt->rwys, runway_compar, sizeof (runway_t),
874 offsetof(runway_t, node));
877 lacf_strlcpy(arpt->ident, new_ident, sizeof (arpt->ident));
884 strlcpy(arpt->icao, arpt->ident, sizeof (arpt->icao));
892 avl_create(&arpt->ramp_starts, ramp_start_compar,
903 normalize_name(cd_p, name, arpt->name, sizeof (arpt->name));
929runway_vgsi_fuzzy_match(runway_t *rwy,
int end, vgsi_t type,
vect2_t pos_v,
932 runway_end_t *re = &rwy->ends[end], *ore = &rwy->ends[!end];
937 double lat_displ =
vect2_dotprod(thr2light_v, thr2thr_norm_uv),
952 if (fabs(lat_displ) > VGSI_LAT_DISPL_FACT * rwy->width ||
953 lon_displ < 0 || lon_displ > rwy->length ||
954 fabs(
rel_hdg(re->hdg, true_hdg)) > VGSI_HDG_MATCH_THRESH ||
955 (lat_displ > 0 && type == VGSI_PAPI_4L) ||
956 (lat_displ < 0 && type == VGSI_PAPI_4R))
962find_nearest_runway_to_vgsi(airport_t *arpt, vgsi_t type,
vect2_t pos_v,
963 double true_hdg, runway_t **rwy, runway_end_t **re, runway_end_t **ore)
965 double max_displ = 100000;
978 for (runway_t *crwy =
avl_first(&arpt->rwys); crwy != NULL;
979 crwy =
AVL_NEXT(&arpt->rwys, crwy)) {
981 if ((displ = runway_vgsi_fuzzy_match(crwy, 0,
982 type, pos_v, true_hdg)) < max_displ) {
984 *re = &crwy->ends[0];
985 *ore = &crwy->ends[1];
987 }
else if ((displ = runway_vgsi_fuzzy_match(crwy, 1,
988 type, pos_v, true_hdg)) < max_displ) {
990 *re = &crwy->ends[1];
991 *ore = &crwy->ends[0];
1002parse_apt_dat_21_line(airport_t *arpt,
const char *line)
1008 double gpa, tch, displ, true_hdg;
1010 runway_t *rwy = NULL;
1011 runway_end_t *re = NULL, *ore = NULL;
1012 vect2_t pos_v, thr2light_v, thr2thr_v;
1018 if (!load_airport(arpt))
1021 comps =
strsplit(line,
" ", B_TRUE, &ncomps);
1022 ASSERT(strcmp(comps[0],
"21") == 0);
1026 type = atoi(comps[3]);
1027 if (type < VGSI_VASI || type > VGSI_PAPI_3C || type == VGSI_PAPI_20DEG)
1029 pos =
GEO_POS2(atof(comps[1]), atof(comps[2]));
1030 pos_v =
geo2fpp(pos, &arpt->fpp);
1031 true_hdg = atof(comps[4]);
1034 gpa = atof(comps[5]);
1035 if (isnan(gpa) || gpa <= 0.0 || gpa > RWY_GPA_LIMIT)
1043 for (rwy =
avl_first(&arpt->rwys); rwy != NULL;
1044 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
1045 if (strcmp(rwy->ends[0].id, rwy_id) == 0) {
1047 ore = &rwy->ends[1];
1049 }
else if (strcmp(rwy->ends[1].id, rwy_id) == 0) {
1050 ore = &rwy->ends[0];
1056 find_nearest_runway_to_vgsi(arpt, type, pos_v, true_hdg,
1065 thr2light_v =
vect2_sub(pos_v, re->thr_v);
1066 thr2thr_v =
vect2_sub(ore->thr_v, re->thr_v);
1075 if (displ < 0 || displ > rwy->length ||
1076 fabs(
rel_hdg(true_hdg, re->hdg)) > VGSI_HDG_MATCH_THRESH) {
1081 find_nearest_runway_to_vgsi(arpt, type, pos_v, true_hdg,
1085 thr2light_v =
vect2_sub(pos_v, re->thr_v);
1086 thr2thr_v =
vect2_sub(ore->thr_v, re->thr_v);
1091 tch = MET2FEET(sin(
DEG2RAD(gpa)) * displ);
1093 if (TCH_IS_VALID(tch)) {
1113validate_rwy_end(
const runway_end_t *re,
char error_descr[128])
1116 ASSERT(error_descr != NULL);
1117#define VALIDATE(cond, ...) \
1120 snprintf(error_descr, 128, __VA_ARGS__); \
1124 VALIDATE(
is_valid_rwy_ID(re->id),
"Runway ID \"%s\" invalid", re->id);
1125 VALIDATE(
is_valid_lat(re->thr.lat),
"Latitude \"%g\" is invalid",
1127 VALIDATE(
is_valid_lon(re->thr.lon),
"Longitude \"%g\" is invalid",
1129 VALIDATE(isnan(re->thr.elev) ||
is_valid_elev(re->thr.elev),
1130 "Threshold elevation \"%g\" is invalid", re->thr.elev);
1131 VALIDATE(re->displ >= 0.0,
"Displacement \"%g\" is invalid", re->displ);
1132 VALIDATE(re->blast >= 0.0,
"Blastpad \"%g\" is invalid", re->displ);
1133 VALIDATE(re->gpa >= 0.0 && re->gpa < RWY_GPA_LIMIT,
1134 "GPA \"%g\" is invalid", re->gpa);
1135 VALIDATE(re->tch >= 0.0 && re->tch < RWY_TCH_LIMIT,
1136 "TCH \"%g\" is invalid", re->tch);
1142parse_apt_dat_freq_line(airport_t *arpt,
char *line, bool_t use833)
1155 for (
size_t i = 0, n = strlen(line); i < n; i++) {
1156 if (line[i] ==
'_' || line[i] ==
'-')
1160 comps =
strsplit(line,
" ", B_TRUE, &ncomps);
1169 freq->
type = atoi(comps[0]) - (use833 ? 1050 : 50);
1170 freq->
freq = atoll(comps[1]) * (use833 ? 1000 : 10000);
1171 for (
size_t i = 2; i < ncomps; i++) {
1180 if ((strcmp(comps[i], arpt->icao) == 0 ||
1181 strcmp(comps[i],
"FREQUENCY") == 0) && ncomps > 3) {
1184 if (freq->
name[0] !=
'\0') {
1185 strncat(&freq->
name[strlen(freq->
name)],
" ",
1186 sizeof (freq->
name) - strlen(freq->
name) - 1);
1188 strncat(&freq->
name[strlen(freq->
name)], comps[i],
1189 sizeof (freq->
name) - strlen(freq->
name) - 1);
1207parse_apt_dat_100_line(airport_t *arpt,
const char *line, bool_t hard_surf_only)
1213 char error_descr[128];
1218 comps =
strsplit(line,
" ", B_TRUE, &ncomps);
1219 ASSERT(strcmp(comps[0],
"100") == 0);
1220 if (ncomps < 8 + 9 + 5 ||
1221 (hard_surf_only && !rwy_is_hard(atoi(comps[2]))))
1227 rwy->width = atof(comps[1]);
1228 rwy->surf = atoi(comps[2]);
1231 rwy->ends[0].thr =
GEO_POS3(atof(comps[8 + 1]), atof(comps[8 + 2]),
1233 rwy->ends[0].thr_m =
GEO3_FT2M(rwy->ends[0].thr);
1234 rwy->ends[0].displ = atof(comps[8 + 3]);
1235 rwy->ends[0].blast = atof(comps[8 + 4]);
1238 rwy->ends[1].thr =
GEO_POS3(atof(comps[8 + 9 + 1]),
1239 atof(comps[8 + 9 + 2]), arpt->refpt.elev);
1240 rwy->ends[1].thr_m =
GEO3_FT2M(rwy->ends[1].thr);
1241 rwy->ends[1].displ = atof(comps[8 + 9 + 3]);
1242 rwy->ends[1].blast = atof(comps[8 + 9 + 4]);
1254 rwy->ends[0].tch = 50;
1255 rwy->ends[1].tch = 50;
1257 snprintf(rwy->joint_id, sizeof (rwy->joint_id),
"%s%s",
1258 rwy->ends[0].id, rwy->ends[1].id);
1259 snprintf(rwy->rev_joint_id, sizeof (rwy->rev_joint_id),
"%s%s",
1260 rwy->ends[1].id, rwy->ends[0].id);
1263 if (ncomps >= 28 && strstr(comps[22],
"GPA1:") == comps[22] &&
1264 strstr(comps[23],
"GPA2:") == comps[23] &&
1265 strstr(comps[24],
"TCH1:") == comps[24] &&
1266 strstr(comps[25],
"TCH2:") == comps[25] &&
1267 strstr(comps[26],
"TELEV1:") == comps[26] &&
1268 strstr(comps[27],
"TELEV2:") == comps[27]) {
1269 rwy->ends[0].gpa = atof(&comps[22][5]);
1270 rwy->ends[1].gpa = atof(&comps[23][5]);
1271 rwy->ends[0].tch = atof(&comps[24][5]);
1272 rwy->ends[1].tch = atof(&comps[25][5]);
1273 rwy->ends[0].thr.elev = atof(&comps[26][7]);
1274 rwy->ends[1].thr.elev = atof(&comps[27][7]);
1275 rwy->ends[0].thr_m.elev = FEET2MET(rwy->ends[0].thr.elev);
1276 rwy->ends[1].thr_m.elev = FEET2MET(rwy->ends[1].thr.elev);
1280 if (!validate_rwy_end(&rwy->ends[0], error_descr) ||
1281 !validate_rwy_end(&rwy->ends[1], error_descr)) {
1295 if (
avl_find(&arpt->rwys, rwy, &where) != NULL) {
1300 if (arpt->load_complete) {
1303 }
else if (isnan(arpt->refpt.lat) || isnan(arpt->refpt.lon)) {
1304 arpt->refpt.lat = NAN;
1305 arpt->refpt.lon = NAN;
1306 arpt->refpt_m.lat = NAN;
1307 arpt->refpt_m.lon = NAN;
1308 airport_auto_refpt(arpt);
1315is_normal_gate_name(
const char *str)
1318 for (
size_t i = 0, n = strlen(str); i < n; i++) {
1319 if ((str[i] <
'A' || str[i] >
'Z') &&
1320 (str[i] <
'0' || str[i] >
'9')) {
1328parse_apt_dat_1300_line(airport_t *arpt,
const char *line,
1329 bool_t normalize_name)
1340 comps =
strsplit(line,
" ", B_TRUE, &n_comps);
1343 if (!normalize_name) {
1345 for (
size_t i = 6; i < n_comps; i++) {
1347 sizeof (srch.
name) - l);
1348 l = MIN(l + strlen(comps[i]),
sizeof (srch.
name) - 1);
1349 if (i + 1 < n_comps) {
1351 sizeof (srch.
name) - l);
1352 l = MIN(l + 1,
sizeof (srch.
name) - 1);
1356 for (
size_t i = 6; i < n_comps; i++) {
1357 if (is_normal_gate_name(comps[i])) {
1358 strlcpy(srch.
name, comps[i],
1359 sizeof (srch.
name));
1363 if (srch.
name[0] ==
'\0')
1366 rs =
avl_find(&arpt->ramp_starts, &srch, &where);
1371 rs->
pos =
GEO_POS2(atof(comps[1]), atof(comps[2]));
1372 rs->
hdgt = atof(comps[3]);
1378 if (strcmp(comps[4],
"gate") == 0)
1379 rs->
type = RAMP_START_GATE;
1380 else if (strcmp(comps[4],
"hangar") == 0)
1381 rs->
type = RAMP_START_HANGAR;
1382 else if (strcmp(comps[4],
"tie-down") == 0)
1383 rs->
type = RAMP_START_TIEDOWN;
1385 rs->
type = RAMP_START_MISC;
1393extract_TA_TL_ft(
char *
const*comps,
size_t n_comps)
1402 ASSERT(strlen(comps[2]) != 0);
1404 bool metric =
false;
1406 ASSERT(strlen(comps[3]) != 0);
1407 metric = (tolower(comps[3][0]) ==
'm');
1409 }
else if (tolower(comps[2][strlen(comps[2]) - 1]) ==
'm') {
1423 memmove(buf, &buf[2], strlen(&buf[2]) + 1);
1428 for (
unsigned i = 0, n = strlen(buf); i < n; i++) {
1429 if (!isdigit(buf[i])) {
1430 memmove(&buf[i], &buf[i + 1], strlen(&buf[i + 1]) + 1);
1434 if (sscanf(buf,
"%f", &alt) == 1) {
1435 if (is_fl || (alt < 600 && !metric)) {
1440 if (metric && MET2FEET(alt) < 60000) {
1441 alt = MET2FEET(alt);
1446 return (
NONE(
float));
1459fill_dup_arpt_info(airport_t *arpt,
const char *line,
int row_code)
1464 if (row_code == 1302) {
1466 char **comps =
strsplit(line,
" ", B_TRUE, &ncomps);
1473 if (strcmp(comps[1],
"iata_code") == 0 && ncomps >= 3 &&
1476 lacf_strlcpy(arpt->iata, comps[2], sizeof (arpt->iata));
1477 }
else if (strcmp(comps[1],
"transition_alt") == 0 &&
1478 ncomps >= 3 && arpt->TA == 0) {
1479 IF_LET(
float, TA_ft, extract_TA_TL_ft(comps, ncomps))
1481 arpt->TA_m = FEET2MET(TA_ft);
1483 }
else if (strcmp(comps[1],
"transition_level") == 0 &&
1484 ncomps >= 3 && arpt->TL == 0) {
1485 IF_LET(
float, TL_ft, extract_TA_TL_ft(comps, ncomps))
1487 arpt->TL_m = FEET2MET(TL_ft);
1489 }
else if (strcmp(comps[1],
"region_code") == 0 &&
1490 ncomps >= 3 && strcmp(comps[2],
"-") != 0) {
1492 }
else if (strcmp(comps[1],
"country") == 0 &&
1493 ncomps >= 3 && strcmp(comps[2],
"-") != 0) {
1495 arpt->country = concat_comps(&comps[2], ncomps - 2);
1496 }
else if (strcmp(comps[1],
"city") == 0 &&
1497 ncomps >= 3 && strcmp(comps[2],
"-") != 0) {
1499 arpt->city = concat_comps(&comps[2], ncomps - 2);
1506iso3166_cc3_to_name(
const char *cc3)
1511 if (strcmp(cc3, iso3166_codes[i].code) == 0)
1518parse_attr_country(
char **comps,
size_t n_comps,
int version, airport_t *arpt)
1524 arpt->cc3[0] =
'\0';
1528 if (version < 1200) {
1529 arpt->country = concat_comps(comps, n_comps);
1531 if (strlen(comps[0]) == 3 && isupper(comps[0][0]) &&
1532 isupper(comps[0][1]) && isupper(comps[0][2])) {
1533 arpt->country = iso3166_cc3_to_name(comps[0]);
1535 if (arpt->country == NULL)
1536 arpt->country = concat_comps(comps, n_comps);
1545read_apt_dat(
airportdb_t *db,
const char *apt_dat_fname, bool_t fail_ok,
1546 iconv_t *cd_p, bool_t fill_in_dups)
1549 airport_t *arpt = NULL, *dup_arpt = NULL;
1552 int line_num = 0, version = 0;
1557 ASSERT(apt_dat_fname != NULL);
1559 apt_dat_f = fopen(apt_dat_fname,
"r");
1560 if (apt_dat_f == NULL) {
1562 logMsg(
"Can't open %s: %s", apt_dat_fname,
1567 while (!feof(apt_dat_f)) {
1571 if (getline(&line, &linecap, apt_dat_f) <= 0)
1575 if (sscanf(line,
"%d", &row_code) != 1)
1578 if (line_num == 2) {
1586 if (strlen(line) == 0 || row_code == 1 || row_code == 16 ||
1589 read_apt_dat_insert(db, arpt);
1593 if (row_code == 1) {
1594 arpt = parse_apt_dat_1_line(db, line, cd_p,
1595 fill_in_dups ? &dup_arpt : NULL);
1598 if (dup_arpt != NULL)
1599 fill_dup_arpt_info(dup_arpt, line, row_code);
1605 parse_apt_dat_21_line(arpt, line);
1608 parse_apt_dat_freq_line(arpt, line, B_FALSE);
1611 parse_apt_dat_100_line(arpt, line, db->ifr_only);
1614 parse_apt_dat_freq_line(arpt, line, B_TRUE);
1617 parse_apt_dat_1300_line(arpt, line,
1618 db->normalize_gate_names);
1621 comps =
strsplit(line,
" ", B_TRUE, &ncomps);
1632 ASSERT(!arpt->geo_linked);
1637 if (strcmp(comps[1],
"icao_code") == 0 &&
1640 sizeof (arpt->icao));
1641 }
else if (strcmp(comps[1],
"iata_code") == 0 &&
1644 sizeof (arpt->iata));
1645 }
else if (strcmp(comps[1],
"country") == 0) {
1646 parse_attr_country(&comps[2], ncomps - 2,
1648 }
else if (strcmp(comps[1],
"city") == 0) {
1650 arpt->city = concat_comps(&comps[2],
1652 }
else if (strcmp(comps[1],
"name_orig") == 0) {
1654 arpt->name_orig = concat_comps(&comps[2],
1656 }
else if (strcmp(comps[1],
"transition_alt") == 0) {
1657 IF_LET(
float, TA_ft, extract_TA_TL_ft(comps,
1660 arpt->TA_m = FEET2MET(TA_ft);
1662 }
else if (strcmp(comps[1],
"transition_level") == 0) {
1663 IF_LET(
float, TL_ft, extract_TA_TL_ft(comps,
1666 arpt->TL_m = FEET2MET(TL_ft);
1668 }
else if (strcmp(comps[1],
"datum_lat") == 0) {
1669 double lat = atof(comps[2]);
1671 arpt->refpt.lat = lat;
1672 arpt->refpt_m.lat = lat;
1677 }
else if (strcmp(comps[1],
"datum_lon") == 0) {
1678 double lon = atof(comps[2]);
1680 arpt->refpt.lon = lon;
1681 arpt->refpt_m.lon = lon;
1683 }
else if (strcmp(comps[1],
"region_code") == 0 &&
1684 strcmp(comps[1],
"-") != 0) {
1695 read_apt_dat_insert(db, arpt);
1702write_apt_dat(
const airportdb_t *db,
const airport_t *arpt)
1713 p = geo_pos2tile_pos(
GEO3_TO_GEO2(arpt->refpt), B_FALSE);
1714 snprintf(lat_lon,
sizeof (lat_lon), TILE_NAME_FMT, p.
lat, p.
lon);
1715 fname = apt_dat_cache_dir(db,
GEO3_TO_GEO2(arpt->refpt), lat_lon);
1718 fp = fopen(fname,
"a");
1720 logMsg(
"Error writing file %s: %s", fname, strerror(errno));
1725 "1200 libacfutils airportdb version %d\n"
1726 "\n", ARPTDB_CACHE_VERSION);
1730 fprintf(fp,
"1 %.0f 0 0 %s %s\n"
1731 "1302 datum_lat %f\n"
1732 "1302 datum_lon %f\n",
1733 arpt->refpt.elev, arpt->ident, arpt->name, arpt->refpt.lat,
1735 if (arpt->name_orig != NULL)
1736 fprintf(fp,
"1302 name_orig %s\n", arpt->name_orig);
1737 if (arpt->icao[0] !=
'\0')
1738 fprintf(fp,
"1302 icao_code %s\n", arpt->icao);
1739 if (arpt->iata[0] !=
'\0')
1740 fprintf(fp,
"1302 iata_code %s\n", arpt->iata);
1741 if (arpt->country != NULL)
1742 fprintf(fp,
"1302 country %s\n", arpt->country);
1743 if (arpt->city != NULL)
1744 fprintf(fp,
"1302 city %s\n", arpt->city);
1746 fprintf(fp,
"1302 transition_alt %.0f\n", arpt->TA);
1748 fprintf(fp,
"1302 transition_level %.0f\n", arpt->TL);
1750 fprintf(fp,
"1302 region_code %s\n", arpt->cc);
1751 for (
const runway_t *rwy =
avl_first(&arpt->rwys); rwy != NULL;
1752 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
1753 ASSERT(!isnan(rwy->ends[0].gpa));
1754 ASSERT(!isnan(rwy->ends[1].gpa));
1755 ASSERT(!isnan(rwy->ends[0].tch));
1756 ASSERT(!isnan(rwy->ends[1].tch));
1757 ASSERT(!isnan(rwy->ends[0].thr.elev));
1758 ASSERT(!isnan(rwy->ends[1].thr.elev));
1759 fprintf(fp,
"100 %.2f %d 0 0 0 0 0 "
1760 "%s %f %f %.1f %.1f 0 0 0 0 "
1761 "%s %f %f %.1f %.1f "
1762 "GPA1:%.02f GPA2:%.02f TCH1:%.0f TCH2:%.0f "
1763 "TELEV1:%.0f TELEV2:%.0f\n",
1764 rwy->width, rwy->surf,
1765 rwy->ends[0].id, rwy->ends[0].thr.lat,
1766 rwy->ends[0].thr.lon, rwy->ends[0].displ,
1768 rwy->ends[1].id, rwy->ends[1].thr.lat,
1769 rwy->ends[1].thr.lon, rwy->ends[1].displ,
1771 rwy->ends[0].gpa, rwy->ends[1].gpa,
1772 rwy->ends[0].tch, rwy->ends[1].tch,
1773 rwy->ends[0].thr.elev, rwy->ends[1].thr.elev);
1776 rs != NULL; rs =
AVL_NEXT(&arpt->ramp_starts, rs)) {
1777 static const char *type2name[] = {
1778 [RAMP_START_GATE] =
"gate",
1779 [RAMP_START_HANGAR] =
"hangar",
1780 [RAMP_START_TIEDOWN] =
"tie-down",
1781 [RAMP_START_MISC] =
"misc",
1783 fprintf(fp,
"1300 %f %f %.2f %s all %s\n",
1793 fprintf(fp,
"%d %ld %s\n", freq->
type + 1050,
1794 (
unsigned long)floor(freq->
freq / 1000), freq->
name);
1804load_arinc424_arpt_data(
const char *filename, airport_t *arpt)
1811 ASSERT(filename != NULL);
1818 fp = fopen(filename,
"r");
1820 logMsg(
"Can't open %s: %s", filename, strerror(errno));
1824 arpt->in_navdb = B_TRUE;
1828 if (getline(&line, &linecap, fp) <= 0)
1830 if (strstr(line,
"APPCH:") == line) {
1841 arpt->have_iaps = B_TRUE;
1843 comps =
strsplit(line + 6,
",", B_FALSE, &ncomps);
1844 if (strstr(comps[4],
"RW") != comps[4] ||
1845 sscanf(comps[28],
"%f", &gpa) != 1 ||
1846 gpa >= 0 || gpa < RWY_GPA_LIMIT * -100)
1854 for (rwy =
avl_first(&arpt->rwys); rwy != NULL;
1855 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
1858 if (strcmp(rwy->ends[0].id, rwy_id) == 0)
1860 else if (strcmp(rwy->ends[1].id, rwy_id) == 0)
1874 }
else if (strstr(line,
"RWY:") == line) {
1884 comps =
strsplit(line + 4,
",", B_FALSE, &ncomps);
1887 for (
size_t i = 0; i < ncomps; i++)
1889 if (strstr(comps[0],
"RW") != comps[0])
1892 for (rwy =
avl_first(&arpt->rwys); rwy != NULL;
1893 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
1897 if (strcmp(rwy->ends[0].id, rwy_id) == 0)
1899 else if (strcmp(rwy->ends[1].id, rwy_id) == 0)
1903 if (sscanf(comps[3],
"%d", &telev) == 1 &&
1905 re->thr.elev = telev;
1906 if (sscanf(comps[7],
"%d", &tch) == 1 &&
1907 tch > 0 && tch < RWY_TCH_LIMIT)
1923load_CIFP_file(
airportdb_t *db,
const char *dirpath,
const char *filename)
1932 ASSERT(filename != NULL);
1935 if (strlen(filename) < 4 ||
1936 strcmp(&filename[strlen(filename) - 4],
".dat") != 0) {
1940 ident[strlen(filename) - 4] =
'\0';
1941 arpt = apt_dat_lookup(db, ident);
1944 filepath =
mkpathname(dirpath, filename, NULL);
1945 res = load_arinc424_arpt_data(filepath, arpt);
1959load_CIFP_dir(
airportdb_t *db,
const char *dirpath)
1961 int dirpath_len = strlen(dirpath);
1962 TCHAR *dirnameT =
safe_calloc(dirpath_len + 1,
sizeof (*dirnameT));
1963 TCHAR *srchnameT =
safe_calloc(dirpath_len + 4,
sizeof (*srchnameT));
1964 WIN32_FIND_DATA find_data;
1970 MultiByteToWideChar(CP_UTF8, 0, dirpath, -1, dirnameT, dirpath_len + 1);
1971 StringCchPrintf(srchnameT, dirpath_len + 4, TEXT(
"%s\\*"), dirnameT);
1972 h_find = FindFirstFile(srchnameT, &find_data);
1973 if (h_find == INVALID_HANDLE_VALUE) {
1977 if (wcscmp(find_data.cFileName, TEXT(
".")) == 0 ||
1978 wcscmp(find_data.cFileName, TEXT(
"..")) == 0) {
1981 unsigned l = wcslen(find_data.cFileName) + 1;
1982 char *filename =
safe_calloc(l,
sizeof (*filename));
1983 WideCharToMultiByte(CP_UTF8, 0, find_data.cFileName, -1,
1984 filename, l, NULL, NULL);
1985 (void) load_CIFP_file(db, dirpath, filename);
1987 }
while (FindNextFile(h_find, &find_data));
2002load_CIFP_dir(
airportdb_t *db,
const char *dirpath)
2004 DIR *dp = opendir(dirpath);
2013 while ((de = readdir(dp)) != NULL) {
2014 if (strcmp(de->d_name,
".") == 0 ||
2015 strcmp(de->d_name,
"..") == 0)
2017 (void) load_CIFP_file(db, dirpath, de->d_name);
2038 bool_t success = B_FALSE;
2042 dirpath =
mkpathname(db->xpdir,
"Custom Data",
"CIFP", NULL);
2044 if (!load_CIFP_dir(db, dirpath))
2045 logMsg(
"%s: error parsing navdata, falling "
2046 "back to default data.", dirpath);
2050 dirpath =
mkpathname(db->xpdir,
"Resources",
"default data",
"CIFP",
2052 success = load_CIFP_dir(db, dirpath);
2054 logMsg(
"%s: error parsing navdata, please check your install",
2066check_cache_version(
const airportdb_t *db,
int app_version)
2073 if ((version_str =
file2str(db->cachedir,
"version", NULL)) != NULL) {
2074 version = atoi(version_str);
2081 if (app_version == 0)
2083 return (version == (ARPTDB_CACHE_VERSION | (app_version << 16)));
2105 bool_t success = B_FALSE;
2111 filename =
mkpathname(xpdir,
"Custom Data",
"earth_nav.dat", NULL);
2112 fp = fopen(filename,
"r");
2115 filename =
mkpathname(xpdir,
"Resources",
"default data",
2116 "earth_nav.dat", NULL);
2117 fp = fopen(filename,
"r");
2123 const char *word_start;
2128 if (getline(&line, &linecap, fp) <= 0 ||
2129 (strstr(line,
"1100 ") != line &&
2130 strstr(line,
"1150 ") != line &&
2131 strstr(line,
"1200 ") != line) ||
2132 (word_start = strstr(line,
" data cycle ")) == NULL) {
2136 success = (sscanf(word_start + 12,
"%d", cycle) == 1);
2157 int db_cycle = -1, xp_cycle = -1;
2161 if ((cycle_str =
file2str(db->cachedir,
"airac_cycle", NULL)) != NULL) {
2162 db_cycle = atoi(cycle_str);
2165 if (!airportdb_xp11_airac_cycle(db->xpdir, &xp_cycle)) {
2166 if ((cycle_str =
file2str(db->xpdir,
"Custom Data",
"GNS430",
2167 "navdata",
"cycle_info.txt", NULL)) == NULL)
2168 cycle_str =
file2str(db->xpdir,
"Resources",
"GNS430",
2169 "navdata",
"cycle_info.txt", NULL);
2170 if (cycle_str != NULL) {
2171 char *sep = strstr(cycle_str,
"AIRAC cycle");
2173 sep = strstr(&sep[11],
": ");
2175 xp_cycle = atoi(&sep[2]);
2181 db->xp_airac_cycle = xp_cycle;
2183 return (db_cycle == xp_cycle);
2197 filename =
mkpathname(db->cachedir,
"apt_dats", NULL);
2198 fp = fopen(filename,
"r");
2206 if (getline(&line, &cap, fp) <= 0)
2210 entry->fname = strdup(line);
2237 bool_t result = B_TRUE;
2239 bool_t vers_ok, cycle_ok;
2242 ASSERT(xp_apt_dats != NULL);
2248 vers_ok = check_cache_version(db, app_version);
2249 cycle_ok = check_airac_cycle(db);
2250 if (!vers_ok || !cycle_ok)
2255 read_apt_dats_list(db, &db_apt_dats);
2257 xp_e != NULL && db_e != NULL; xp_e =
list_next(xp_apt_dats, xp_e),
2259 if (strcmp(xp_e->fname, db_e->fname) != 0) {
2264 if (db_e != NULL || xp_e != NULL)
2266 destroy_apt_dats_list(&db_apt_dats);
2272create_arpt_index(
airportdb_t *db,
const airport_t *arpt)
2281 if (arpt->iata[0] !=
'\0')
2285 if (arpt->cc[0] !=
'\0')
2290 for (
const runway_t *rwy =
avl_first(&arpt->rwys); rwy != NULL;
2291 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
2292 if (rwy_is_hard(rwy->surf)) {
2294 MET2FEET(rwy->ends[0].land_len));
2296 MET2FEET(rwy->ends[1].land_len));
2302 avl_add(&db->arpt_index, idx);
2303 if (idx->
icao[0] !=
'\0') {
2304 htbl2_set(&db->icao_index, idx->
icao, sizeof (idx->
icao),
2305 idx, sizeof (*idx));
2307 if (idx->
iata[0] !=
'\0') {
2308 htbl2_set(&db->iata_index, idx->
iata, sizeof (idx->
iata),
2309 idx, sizeof (*idx));
2317 char *index_filename;
2320 size_t line_cap = 0;
2321 size_t num_lines = 0;
2324 index_filename =
mkpathname(db->cachedir,
"index.dat", NULL);
2325 index_file = fopen(index_filename,
"r");
2327 if (index_file == NULL)
2330 if (!db->override_settings) {
2332 char *filename =
mkpathname(db->cachedir,
"settings.conf",
2339 &db->normalize_gate_names);
2348 recreate_icao_iata_tables(db, num_lines);
2350 while (
lacf_getline(&line, &line_cap, index_file) > 0) {
2354 if (sscanf(line,
"%7s %7s %3s %2s %f %f %f %hu %hu %hu",
2361 if (
avl_find(&db->arpt_index, idx, &where) == NULL) {
2363 htbl2_set(&db->icao_index, idx->
icao,
2364 sizeof (idx->
icao), idx, sizeof (*idx));
2365 if (strcmp(idx->
iata,
"-") != 0) {
2366 htbl2_set(&db->iata_index, idx->
iata,
2367 sizeof (idx->
iata), idx, sizeof (*idx));
2370 logMsg(
"WARNING: found duplicate airport ident %s "
2371 "in index. Skipping it. This shouldn't happen "
2372 "unless the index is damaged.", idx->
ident);
2378 free(index_filename);
2384write_index_dat(
const arpt_index_t *idx, FILE *index_file)
2387 ASSERT(index_file != NULL);
2388 return (fprintf(index_file,
"%s\t%s\t%s\t%s\t%f\t%f\t%.0f\t"
2391 idx->iata[0] !=
'\0' ? idx->iata :
"-",
2392 idx->cc[0] !=
'\0' ? idx->cc :
"-",
2393 idx->pos.lat, idx->pos.lon, idx->pos.elev,
2394 idx->max_rwy_len, idx->TA, idx->TL) > 0);
2402 bool_t exists, isdir;
2405 ASSERT(apt_dat_files != NULL);
2409 (!isdir && !
remove_file(db->cachedir, B_FALSE)))) ||
2413 filename =
mkpathname(db->cachedir,
"version", NULL);
2414 fp = fopen(filename,
"w");
2416 logMsg(
"Error writing new airport database, can't open "
2417 "%s for writing: %s", filename, strerror(errno));
2421 fprintf(fp,
"%d", (app_version << 16) | ARPTDB_CACHE_VERSION);
2425 filename =
mkpathname(db->cachedir,
"airac_cycle", NULL);
2426 fp = fopen(filename,
"w");
2428 logMsg(
"Error writing new airport database, can't open "
2429 "%s for writing: %s", filename, strerror(errno));
2433 fprintf(fp,
"%d", db->xp_airac_cycle);
2437 filename =
mkpathname(db->cachedir,
"apt_dats", NULL);
2438 fp = fopen(filename,
"w");
2440 logMsg(
"Error writing new airport database, can't open "
2441 "%s for writing: %s", filename, strerror(errno));
2447 fprintf(fp,
"%s\n", e->fname);
2451 if (db->override_settings) {
2456 db->normalize_gate_names);
2457 filename =
mkpathname(db->cachedir,
"settings.conf", NULL);
2502 bool_t success = B_TRUE;
2503 char *index_filename = NULL;
2504 FILE *index_file = NULL;
2506 char *prev_locale = NULL, *saved_locale = NULL;
2512 find_all_apt_dats(db, &apt_dat_files);
2513 if (cache_up_to_date(db, &apt_dat_files, app_version) &&
2514 read_index_dat(db)) {
2518 prev_locale = setlocale(LC_CTYPE, NULL);
2519 if (prev_locale != NULL)
2521 setlocale(LC_CTYPE,
"");
2523 cd = iconv_open(
"ASCII//TRANSLIT",
"UTF-8");
2526 bool_t fill_in_dups = (
list_next(&apt_dat_files, e) == NULL);
2527 read_apt_dat(db, e->fname, B_TRUE, &cd, fill_in_dups);
2530 if (saved_locale != NULL) {
2531 setlocale(LC_CTYPE, saved_locale);
2534 if (!load_xp11_navdata(db)) {
2539 logMsg(
"navdata error: it appears your simulator's "
2540 "navigation database is broken, or your simulator "
2541 "contains no airport scenery. Please reinstall the "
2542 "database and retry.");
2547 if (!recreate_cache_skeleton(db, &apt_dat_files, app_version)) {
2551 index_filename =
mkpathname(db->cachedir,
"index.dat", NULL);
2552 index_file = fopen(index_filename,
"w");
2553 if (index_file == NULL) {
2554 logMsg(
"Error creating airport database index file %s: %s",
2555 index_filename, strerror(errno));
2559 recreate_icao_iata_tables(db,
avl_numnodes(&db->apt_dat));
2560 for (airport_t *arpt =
avl_first(&db->apt_dat), *next_arpt;
2561 arpt != NULL; arpt = next_arpt) {
2562 next_arpt =
AVL_NEXT(&db->apt_dat, arpt);
2563 ASSERT(arpt->geo_linked);
2569 if (!arpt->have_iaps && db->ifr_only) {
2570 geo_unlink_airport(db, arpt);
2575 write_index_dat(idx, index_file);
2578 for (airport_t *arpt =
avl_first(&db->apt_dat); arpt != NULL;
2579 arpt =
AVL_NEXT(&db->apt_dat, arpt)) {
2582 ASSERT(arpt->geo_linked);
2585 dirname = apt_dat_cache_dir(db,
GEO3_TO_GEO2(arpt->refpt),
2596 destroy_apt_dats_list(&apt_dat_files);
2597 free(index_filename);
2598 if (index_file != NULL)
2628make_apch_prox_bbox(
const runway_t *rwy,
int end_i)
2630 const runway_end_t *end, *oend;
2632 double limit_left = 1000000, limit_right = 1000000;
2633 vect2_t x, a, b, b1, c, c1, d, thr_v, othr_v, dir_v;
2638 fpp = &rwy->arpt->fpp;
2639 ASSERT(end_i == 0 || end_i == 1);
2646 for (
int i = 0; i < 7; i++)
2649 end = &rwy->ends[end_i];
2650 oend = &rwy->ends[!end_i];
2652 othr_v = oend->thr_v;
2656 RWY_APCH_PROXIMITY_LON_DISPL));
2658 rwy->width / 2 + RWY_APCH_PROXIMITY_LAT_DISPL));
2664 rwy->width / 2 + RWY_APCH_PROXIMITY_LAT_DISPL));
2673 if (strlen(end->id) >= 3) {
2674 int my_num_id = atoi(end->id);
2676 for (
const runway_t *orwy =
avl_first(&rwy->arpt->rwys);
2677 orwy != NULL; orwy =
AVL_NEXT(&rwy->arpt->rwys, orwy)) {
2678 const runway_end_t *orwy_end;
2684 if (atoi(orwy->ends[0].id) == my_num_id)
2685 orwy_end = &orwy->ends[0];
2686 else if (atoi(orwy->ends[1].id) == my_num_id)
2687 orwy_end = &orwy->ends[1];
2698 logMsg(
"CAUTION: your nav DB is looking very "
2699 "strange: runways %s and %s at %s are on "
2700 "top of each other (coords: %fx%f)",
2701 end->id, orwy_end->id, rwy->arpt->icao,
2702 orwy_end->thr.lat, orwy_end->thr.lon);
2709 limit_left = MIN(dist / 2, limit_left);
2711 limit_right = MIN(dist / 2, limit_right);
2715 if (limit_left < RWY_APCH_PROXIMITY_LAT_DISPL) {
2718 limit_left)), B_FALSE);
2722 if (limit_right < RWY_APCH_PROXIMITY_LAT_DISPL) {
2725 limit_right)), B_FALSE);
2747load_rwy_info(runway_t *rwy)
2750 ASSERT(rwy->arpt->load_complete);
2771 double displ1 = rwy->ends[0].displ;
2772 double displ2 = rwy->ends[1].displ;
2773 double blast1 = rwy->ends[0].blast;
2774 double blast2 = rwy->ends[1].blast;
2785 double prox_lon_bonus1 = MAX(displ1, RWY_PROXIMITY_LON_DISPL - displ1);
2786 double prox_lon_bonus2 = MAX(displ2, RWY_PROXIMITY_LON_DISPL - displ2);
2788 rwy->ends[0].thr_v = t1v;
2789 rwy->ends[1].thr_v = t2v;
2790 rwy->ends[0].dthr_v = dt1v;
2791 rwy->ends[1].dthr_v = dt2v;
2792 rwy->ends[0].hdg = hdg1;
2793 rwy->ends[1].hdg = hdg2;
2798 ASSERT(rwy->rwy_bbox == NULL);
2800 rwy->rwy_bbox = make_rwy_bbox(t1v, dir_v, rwy->width, len, 0);
2801 rwy->tora_bbox = make_rwy_bbox(dt1v, dir_v, rwy->width, dlen, 0);
2802 rwy->asda_bbox = make_rwy_bbox(dt1v, dir_v, rwy->width,
2803 dlen + blast2, blast1);
2804 rwy->prox_bbox = make_rwy_bbox(t1v, dir_v, RWY_PROXIMITY_LAT_FRACT *
2805 rwy->width, len + prox_lon_bonus2, prox_lon_bonus1);
2807 rwy->ends[0].apch_bbox = make_apch_prox_bbox(rwy, 0);
2808 rwy->ends[1].apch_bbox = make_apch_prox_bbox(rwy, 1);
2812unload_rwy_info(runway_t *rwy)
2815 ASSERT(rwy->rwy_bbox != NULL);
2817 free(rwy->rwy_bbox);
2818 rwy->rwy_bbox = NULL;
2819 free(rwy->tora_bbox);
2820 rwy->tora_bbox = NULL;
2821 free(rwy->asda_bbox);
2822 rwy->asda_bbox = NULL;
2823 free(rwy->prox_bbox);
2824 rwy->prox_bbox = NULL;
2826 free(rwy->ends[0].apch_bbox);
2827 rwy->ends[0].apch_bbox = NULL;
2828 free(rwy->ends[1].apch_bbox);
2829 rwy->ends[1].apch_bbox = NULL;
2841load_airport(airport_t *arpt)
2845 if (arpt->load_complete)
2848 if (isnan(arpt->refpt.lat) || isnan(arpt->refpt.lon) ||
2849 isnan(arpt->refpt.elev))
2853 arpt->load_complete = B_TRUE;
2859 for (runway_t *rwy =
avl_first(&arpt->rwys); rwy != NULL;
2867unload_airport(airport_t *arpt)
2870 if (!arpt->load_complete)
2872 for (runway_t *rwy =
avl_first(&arpt->rwys); rwy != NULL;
2874 unload_rwy_info(rwy);
2875 arpt->load_complete = B_FALSE;
2879free_airport(airport_t *arpt)
2887 if (arpt->load_complete)
2888 unload_airport(arpt);
2922 tile = geo_table_get_tile(db, tile_coord, B_FALSE, NULL);
2926 for (airport_t *arpt =
avl_first(&tile->arpts); arpt != NULL;
2927 arpt =
AVL_NEXT(&tile->arpts, arpt)) {
2931 VERIFY(load_airport(arpt));
2963 list_create(l,
sizeof (airport_t), offsetof(airport_t, cur_arpts_node));
2964 for (
int i = -1; i <= 1; i++) {
2965 for (
int j = -1; j <= 1; j++)
2966 find_nearest_airports_tile(db, ecef,
2997 char *cache_dir, *fname;
3003 (void) geo_table_get_tile(db, tile_pos, B_TRUE, &created);
3007 tile_pos = geo_pos2tile_pos(tile_pos, B_FALSE);
3008 cache_dir = apt_dat_cache_dir(db, tile_pos, NULL);
3009 snprintf(lat_lon,
sizeof (lat_lon), TILE_NAME_FMT,
3010 tile_pos.
lat, tile_pos.
lon);
3011 fname =
mkpathname(cache_dir, lat_lon, NULL);
3013 read_apt_dat(db, fname, B_FALSE, NULL, B_FALSE);
3021 void *cookie = NULL;
3052 db->load_limit = limit;
3071 for (
int i = -1; i <= 1; i++) {
3072 for (
int j = -1; j <= 1; j++)
3073 load_airports_in_tile(db,
GEO_POS2(my_pos.
lat + i,
3079lon_delta(
double x,
double y)
3081 double u = MAX(x, y), d = MIN(x, y);
3084 return (fabs(u - d));
3086 return (fabs((180 - u) - (-180 - d)));
3095 fabs(tile->pos.
lat - floor(my_pos.
lat)) > 1 ||
3096 lon_delta(tile->pos.
lon, floor(my_pos.
lon)) > 1)
3097 free_tile(db, tile, B_TRUE);
3116 tile_t *tile, *next_tile;
3121 for (tile =
avl_first(&db->geo_table); tile != NULL; tile = next_tile) {
3122 next_tile =
AVL_NEXT(&db->geo_table, tile);
3123 unload_distant_airport_tiles_i(db, tile, my_pos);
3133arpt_index_compar(
const void *a,
const void *b)
3136 int res = strcmp(ia->
ident, ib->ident);
3158 VERIFY(cachedir != NULL);
3160 db->inited = B_TRUE;
3161 db->xpdir = strdup(xpdir);
3162 db->cachedir = strdup(cachedir);
3163 db->load_limit = ARPT_LOAD_LIMIT;
3164 db->ifr_only = B_TRUE;
3165 db->normalize_gate_names = B_FALSE;
3169 avl_create(&db->apt_dat, airport_compar, sizeof (airport_t),
3170 offsetof(airport_t, apt_dat_node));
3173 avl_create(&db->arpt_index, arpt_index_compar,
3179 htbl2_create(&db->icao_index, 16, AIRPORTDB_ICAO_LEN,
3181 htbl2_create(&db->iata_index, 16, AIRPORTDB_IATA_LEN,
3208 free_tile(db, tile, B_FALSE);
3212 htbl2_empty(&db->icao_index, sizeof (
arpt_index_t), NULL, NULL);
3213 htbl2_destroy(&db->icao_index);
3214 htbl2_empty(&db->iata_index, sizeof (
arpt_index_t), NULL, NULL);
3215 htbl2_destroy(&db->iata_index);
3221 memset(db, 0,
sizeof (*db));
3271 idx =
avl_find(&db->arpt_index, &srch, NULL);
3279 void (*found_cb)(airport_t *
airport,
void *userinfo),
void *userinfo)
3286 arpt_index_t *idx = htbl2_value_multi(mv,
sizeof (*idx));
3288 if (found_cb != NULL) {
3300 found_cb(apt, userinfo);
3302 logMsg(
"WARNING: airport database index is "
3303 "damaged: index contains ICAO %s, but "
3304 "the associated database tile doesn't "
3305 "appear to contain this airport.",
3329 void (*found_cb)(airport_t *
airport,
void *userinfo),
void *userinfo)
3332 char icao_srch[AIRPORTDB_ICAO_LEN] = {};
3337 strlcpy(icao_srch, icao,
sizeof (icao_srch));
3338 list = htbl2_lookup_multi(&db->icao_index, icao_srch,
3339 sizeof (icao_srch));
3341 airport_lookup_htbl_multi(db,
list, found_cb, userinfo);
3356 void (*found_cb)(airport_t *
airport,
void *userinfo),
void *userinfo)
3359 char iata_srch[AIRPORTDB_IATA_LEN] = {};
3364 strlcpy(iata_srch, iata,
sizeof (iata_srch));
3365 list = htbl2_lookup_multi(&db->iata_index, iata_srch,
3366 sizeof (iata_srch));
3368 airport_lookup_htbl_multi(db,
list, found_cb, userinfo);
3386 load_airports_in_tile(db, pos);
3387 return (apt_dat_lookup(db, icao));
3391save_arpt_cb(airport_t *
airport,
void *userinfo)
3393 airport_t **out_arpt = userinfo;
3413 airport_t *found = NULL;
3416 (void)airport_lookup_by_icao(db, icao, save_arpt_cb, &found);
3444 void (*found_cb)(
const arpt_index_t *idx,
void *userinfo),
void *userinfo)
3448 if (found_cb != NULL) {
3450 idx != NULL; idx =
AVL_NEXT(&db->arpt_index, idx)) {
3451 found_cb(idx, userinfo);
3486 for (runway_t *rwy =
avl_first(&arpt->rwys); rwy != NULL;
3487 rwy =
AVL_NEXT(&arpt->rwys, rwy)) {
3488 for (
unsigned i = 0; i < 2; i++) {
3489 if (strcmp(rwy->ends[i].id, rwy_id) == 0) {
3502 const char *search_icao)
3505 char const *search_cc;
3508 ASSERT(search_icao != NULL);
3511 load_airports_in_tile(db, pos);
3512 tile = geo_table_get_tile(db, pos, B_FALSE, NULL);
3516 for (airport_t *arpt =
avl_first(&tile->arpts); arpt != NULL;
3517 arpt =
AVL_NEXT(&tile->arpts, arpt)) {
3524 if ((arpt->TA != 0 || arpt->TL != 0) && (search_cc == NULL ||
size_t adb_airport_lookup_by_iata(airportdb_t *db, const char *iata, void(*found_cb)(airport_t *airport, void *userinfo), void *userinfo)
airport_t * adb_airport_lookup(airportdb_t *db, const char *icao, geo_pos2_t pos)
airport_t * adb_airport_lookup_by_ident(airportdb_t *db, const char *ident)
airport_t * adb_airport_lookup_global(airportdb_t *db, const char *icao)
void airportdb_lock(airportdb_t *db)
bool_t adb_airportdb_xp_airac_cycle(const char *xpdir, int *cycle)
bool_t adb_recreate_cache(airportdb_t *db, int app_version)
size_t adb_airport_index_walk(const airportdb_t *db, void(*found_cb)(const arpt_index_t *idx, void *userinfo), void *userinfo)
list_t * adb_find_nearest_airports(airportdb_t *db, geo_pos2_t my_pos)
void adb_free_nearest_airport_list(list_t *l)
void adb_set_airport_load_limit(airportdb_t *db, double limit)
void airportdb_create(airportdb_t *db, const char *xpdir, const char *cachedir)
bool_t adb_airport_find_runway(airport_t *arpt, const char *rwy_id, runway_t **rwy_p, unsigned *end_p)
void adb_unload_distant_airport_tiles(airportdb_t *db, geo_pos2_t my_pos)
size_t adb_airport_lookup_by_icao(airportdb_t *db, const char *icao, void(*found_cb)(airport_t *airport, void *userinfo), void *userinfo)
void airportdb_unlock(airportdb_t *db)
void airportdb_destroy(airportdb_t *db)
#define ASSERT3U(x, op, y)
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
void * avl_first(const avl_tree_t *tree)
void avl_remove(avl_tree_t *tree, void *node)
void avl_add(avl_tree_t *tree, void *node)
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
#define AVL_NEXT(tree, node)
unsigned long avl_numnodes(const avl_tree_t *tree)
void avl_destroy(avl_tree_t *tree)
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
void conf_set_b(conf_t *conf, const char *key, bool_t value)
bool_t conf_get_b(const conf_t *conf, const char *key, bool_t *value)
conf_t * conf_read_file(const char *filename, int *errline)
void conf_free(conf_t *conf)
bool_t conf_write_file(const conf_t *conf, const char *filename)
conf_t * conf_create_empty(void)
#define ARRAY_NUM_ELEM(_array)
void lacf_free(void *buf)
#define LACF_DESTROY(ptr)
vect2_t vect2vect_isect(vect2_t da, vect2_t oa, vect2_t db, vect2_t ob, bool_t confined)
vect2_t vect2_add(vect2_t a, vect2_t b)
vect2_t geo2fpp(geo_pos2_t pos, const fpp_t *fpp)
vect2_t vect2_sub(vect2_t a, vect2_t b)
double vect2_abs(vect2_t a)
#define GEO_POS3(lat, lon, elev)
vect2_t vect2_neg(vect2_t v)
#define IS_NULL_GEO_POS(a)
double dir2hdg(vect2_t dir)
#define GEO_POS2(lat, lon)
vect3_t vect3_sub(vect3_t a, vect3_t b)
vect2_t vect2_set_abs(vect2_t a, double abs)
double vect3_dist(vect3_t a, vect3_t b)
vect2_t vect2_norm(vect2_t v, bool_t right)
double vect3_abs(vect3_t a)
fpp_t ortho_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip, bool_t allow_inv)
double vect2_dotprod(vect2_t a, vect2_t b)
vect3_t geo2ecef_ft(geo_pos3_t pos, const ellip_t *ellip)
vect2_t vect2_unit(vect2_t a, double *l)
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
static ssize_t lacf_getline(char **lineptr, size_t *n, FILE *stream)
void fix_pathsep(char *str)
char * mkpathname(const char *comp,...)
void strtoupper(char *str)
void copy_rwy_ID(const char *src, char dst[4])
char * file2str(const char *comp,...)
bool_t remove_directory(const char *dirname)
bool_t is_valid_icao_code(const char *icao)
const char * extract_icao_country_code(const char *icao)
static bool_t is_valid_elev(double elev)
static bool_t is_valid_hdg(double hdg)
bool_t create_directory(const char *dirname)
static int lacf_strcasecmp(const char *s1, const char *s2)
void append_format(char **str, size_t *sz, const char *format,...)
void lacf_strlcpy(char *dest, const char *src, size_t cap)
void free_strlist(char **comps, size_t num)
bool_t create_directory_recursive(const char *dirname)
bool_t is_valid_rwy_ID(const char *rwy_ID)
static bool_t is_valid_lat(double lat)
bool_t remove_file(const char *filename, bool_t notfound_ok)
bool_t file_exists(const char *path, bool_t *isdir)
#define rel_hdg(hdg1, hdg2)
static bool_t is_valid_lon(double lon)
bool_t is_valid_iata_code(const char *iata)
static char * lacf_strcasestr(const char *haystack, const char *needle)
int list_link_active(const list_node_t *)
void list_destroy(list_t *)
void * list_head(const list_t *)
void list_create(list_t *, size_t, size_t)
void * list_next(const list_t *, const void *)
size_t list_count(const list_t *)
void list_remove(list_t *, void *)
void * list_remove_head(list_t *)
void list_insert_tail(list_t *, void *)
#define NONE(type_name)
Constructs a new optional value in the NONE state.
#define SOME(expr)
Constructs a new optional value in the SOME state.
#define IF_LET(vartype, varname, opt)
Allows constructing Rust-like if-let statements in C.
static void strip_space(char *line)
static char * safe_strdup(const char *str2)
static void * safe_calloc(size_t nmemb, size_t size)
static void * safe_malloc(size_t size)
An optional type for wrapping a non-NAN float value.
static void mutex_destroy(mutex_t *mtx)
static void mutex_enter(mutex_t *mtx)
static void mutex_exit(mutex_t *mtx)
static void mutex_init(mutex_t *mtx)