File indexing completed on 2024-09-08 03:28:59
0001 /* 0002 SPDX-FileCopyrightText: 2008 Akarsh Simha <akarshsimha@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Program to convert star data stored in a MySQL database into 0008 // the binary file format used by KStars 0009 0010 #define DB_TBL "tycho2" 0011 #define DB_DB "stardb" 0012 #define VERBOSE 0 0013 #define LONG_NAME_LIMIT 32 0014 #define BAYER_LIMIT 8 0015 #define HTM_LEVEL 3 0016 #define NTRIXELS 512 // TODO: Change if HTM Level Changes 0017 #define INDEX_ENTRY_SIZE 12 0018 #define GLOBAL_MAG_LIMIT 8.00 0019 #define MYSQL_STARS_PER_QUERY 1000000 0020 0021 #include "binfile.h" 0022 #include <mysql/mysql.h> 0023 #include <sys/types.h> 0024 #include <stdlib.h> 0025 #include <stdio.h> 0026 #include <math.h> 0027 #include <string.h> 0028 0029 /* 0030 * struct to store star data, to be written in this format, into the binary file. 0031 */ 0032 0033 typedef struct starData 0034 { 0035 int32_t RA; 0036 int32_t Dec; 0037 int32_t dRA; 0038 int32_t dDec; 0039 int32_t parallax; 0040 int32_t HD; 0041 int16_t mag; 0042 int16_t bv_index; 0043 char spec_type[2]; 0044 char flags; 0045 char unused; 0046 } starData; 0047 0048 /* 0049 * struct to store star name data, to be written in this format, into the star name binary file. 0050 */ 0051 0052 typedef struct starName 0053 { 0054 char bayerName[BAYER_LIMIT]; 0055 char longName[LONG_NAME_LIMIT]; 0056 } starName; 0057 0058 /* 0059 * Dump the data file header. 0060 * 0061 * WARNING: Must edit every time the definition of the starData structures changes 0062 * 0063 * f : Data file handle 0064 */ 0065 0066 int writeDataFileHeader(FILE *f) 0067 { 0068 char ASCII_text[124]; 0069 u_int16_t nfields; 0070 u_int32_t nindexes; 0071 int16_t endian_id = 0x4B53; 0072 u_int8_t version_no = 1; 0073 0074 if (f == NULL) 0075 return 0; 0076 0077 nfields = 11; 0078 0079 str2charv(ASCII_text, "KStars Star Data v1.0. To be read using the 32-bit starData structure only.", 124); 0080 fwrite(ASCII_text, 124, 1, f); 0081 fwrite(&endian_id, 2, 1, f); 0082 fwrite(&version_no, 1, 1, f); 0083 fwrite(&nfields, sizeof(u_int16_t), 1, f); 0084 0085 writeDataElementDescription(f, "RA", 4, DT_INT32, 1000000); 0086 writeDataElementDescription(f, "Dec", 4, DT_INT32, 100000); 0087 writeDataElementDescription(f, "dRA", 4, DT_INT32, 10); 0088 writeDataElementDescription(f, "dDec", 4, DT_INT32, 10); 0089 writeDataElementDescription(f, "parallax", 4, DT_INT32, 10); 0090 writeDataElementDescription(f, "HD", 4, DT_INT32, 10); 0091 writeDataElementDescription(f, "mag", 2, DT_INT16, 100); 0092 writeDataElementDescription(f, "bv_index", 2, DT_INT16, 100); 0093 writeDataElementDescription(f, "spec_type", 2, DT_CHARV, 0); 0094 writeDataElementDescription(f, "flags", 1, DT_CHAR, 0); 0095 writeDataElementDescription(f, "unused", 1, DT_CHAR, 100); 0096 0097 nindexes = NTRIXELS; 0098 fwrite(&nindexes, sizeof(u_int32_t), 1, f); 0099 0100 return 1; 0101 } 0102 0103 /* 0104 * Dump the name file header. 0105 * 0106 * WARNING: Must edit every time the definition of the starName structures changes 0107 * 0108 * nf : Name file handle 0109 */ 0110 0111 int writeNameFileHeader(FILE *nf) 0112 { 0113 char ASCII_text[124]; 0114 u_int16_t nfields; 0115 u_int32_t nindexes; 0116 int16_t endian_id = 0x4B53; 0117 u_int32_t data_offset = 0; 0118 u_int8_t version_no = 1; 0119 0120 if (nf == NULL) 0121 return 0; 0122 0123 nfields = 2; 0124 0125 str2charv(ASCII_text, "KStars Star Name Data v1.0. Refer associated documentation for format description.", 124); 0126 fwrite(ASCII_text, 124, 1, nf); 0127 fwrite(&endian_id, 2, 1, nf); 0128 fwrite(&version_no, 1, 1, nf); 0129 fwrite(&nfields, sizeof(u_int16_t), 1, nf); 0130 0131 writeDataElementDescription(nf, "bayerName", BAYER_LIMIT, DT_STR, 0); 0132 writeDataElementDescription(nf, "longName", LONG_NAME_LIMIT, DT_STR, 0); 0133 0134 nindexes = 1; 0135 fwrite(&nindexes, sizeof(u_int32_t), 1, nf); 0136 0137 return 1; 0138 } 0139 0140 int main(int argc, char *argv[]) 0141 { 0142 /* === Declarations === */ 0143 0144 int ret, i, exitflag; 0145 long int lim; 0146 char named, shallow; 0147 0148 unsigned long ns_header_offset, us_header_offset, ds_header_offset; 0149 unsigned long nsf_trix_begin, usf_trix_begin, dsf_trix_begin; 0150 unsigned long nsf_trix_count, usf_trix_count, dsf_trix_count; 0151 unsigned long ntrixels; 0152 unsigned long nf_header_offset; 0153 unsigned int names_count; 0154 int16_t maglim; 0155 u_int8_t htm_level; 0156 u_int16_t MSpT_named, MSpT_unnamed, MSpT_deep; 0157 0158 char query[512]; 0159 u_int32_t current_trixel, new_trixel; 0160 0161 /* File streams */ 0162 FILE *f; /* Pointer to "current" file */ 0163 FILE *nsf, *usf, *dsf; /* Handles of named and unnamed star data files */ 0164 FILE *nsfhead, *usfhead, *dsfhead; /* Handles of named and unnamed star header files */ 0165 FILE *namefile; /* Pointer to the name file */ 0166 FILE *hdindex; /* Pointer to the HD Index file */ 0167 0168 /* starData and starName structures */ 0169 starData data; 0170 starName name; 0171 0172 /* MySQL structures */ 0173 MYSQL link; 0174 MYSQL_RES *result; 0175 MYSQL_ROW row; 0176 0177 /* Check the number of arguments */ 0178 if (argc <= 9) 0179 { 0180 fprintf(stderr, 0181 "USAGE %s DBUserName DBPassword DBName UnnamedStarDataFile UnnamedStarHeaderFile DeepStarDataFile " 0182 "DeepStarHeaderFile ShallowStarDataFile ShallowStarHeaderFile StarNameFile HDIndex [TableName]\n", 0183 argv[0]); 0184 fprintf(stderr, "The magnitude limit for a Shallow Star is set in the program source using GLOBAL_MAG_LIMIT\n"); 0185 fprintf(stderr, "The database used is a MySQL DB on localhost. The default table name is `allstars`\n"); 0186 fprintf(stderr, "HDIndex must contain 360000 blank records of 32 bits each. You can use dd if=/dev/zero of=... " 0187 "... to create it\n"); 0188 } 0189 0190 /* == Open all file streams required == */ 0191 /* Unnamed Star Handling */ 0192 usf = fopen(argv[4], "wb"); 0193 if (usf == NULL) 0194 { 0195 fprintf(stderr, "ERROR: Could not open %s [Deep Star Data File] for binary write.\n", argv[4]); 0196 return 1; 0197 } 0198 0199 usfhead = fopen(argv[5], "wb"); 0200 if (usfhead == NULL) 0201 { 0202 fprintf(stderr, "ERROR: Could not open %s [Deep Star Header File] for binary write.\n", argv[5]); 0203 fcloseall(); 0204 return 1; 0205 } 0206 0207 dsf = fopen(argv[6], "wb"); 0208 if (usfhead == NULL) 0209 { 0210 fprintf(stderr, "ERROR: Could not open %s [Deep Star Header File] for binary write.\n", argv[6]); 0211 fcloseall(); 0212 return 1; 0213 } 0214 0215 dsfhead = fopen(argv[7], "wb"); 0216 if (usfhead == NULL) 0217 { 0218 fprintf(stderr, "ERROR: Could not open %s [Deep Star Header File] for binary write.\n", argv[7]); 0219 fcloseall(); 0220 return 1; 0221 } 0222 0223 /* Bright / Named Star Handling */ 0224 nsf = fopen(argv[8], "wb"); 0225 if (nsf == NULL) 0226 { 0227 fprintf(stderr, "ERROR: Could not open %s [Shallow Star Data File] for binary write.\n", argv[8]); 0228 fcloseall(); 0229 return 1; 0230 } 0231 0232 nsfhead = fopen(argv[9], "wb"); 0233 if (nsfhead == NULL) 0234 { 0235 fprintf(stderr, "ERROR: Could not open %s [Shallow Star Header File] for binary write.\n", argv[9]); 0236 fcloseall(); 0237 return 1; 0238 } 0239 0240 namefile = fopen(argv[10], "wb"); 0241 if (namefile == NULL) 0242 { 0243 fprintf(stderr, "ERROR: Could not open %s [Star Name File] for binary write.\n", argv[7]); 0244 fcloseall(); 0245 return 1; 0246 } 0247 0248 hdindex = fopen(argv[11], "wb"); 0249 if (hdindex == NULL) 0250 { 0251 fprintf(stderr, "ERROR: Could not open %s [HD Index] for binary read/write.\n", argv[9]); 0252 fcloseall(); 0253 return 1; 0254 } 0255 u_int32_t foo = 0; 0256 for (i = 0; i < 360000; ++i) 0257 fwrite(&foo, sizeof(u_int32_t), 1, hdindex); 0258 0259 /* MySQL connect */ 0260 if (mysql_init(&link) == NULL) 0261 { 0262 fprintf(stderr, "ERROR: Failed to initialize MySQL connection!\n"); 0263 return 1; 0264 } 0265 0266 ret = mysql_real_connect(&link, "localhost", argv[1], argv[2], argv[3], 0, NULL, 0); 0267 0268 if (!ret) 0269 { 0270 fprintf(stderr, "ERROR: MySQL connect failed for the following reason: %s\n", mysql_error(&link)); 0271 fcloseall(); 0272 return 1; 0273 } 0274 0275 if (mysql_select_db(&link, argv[3])) 0276 { 0277 fprintf(stderr, "ERROR: Could not select MySQL database %s. MySQL said: %s", argv[3], mysql_error(&link)); 0278 fcloseall(); 0279 mysql_close(&link); 0280 return 1; 0281 } 0282 0283 /* Write file headers */ 0284 writeNameFileHeader(namefile); 0285 writeDataFileHeader(nsfhead); 0286 writeDataFileHeader(usfhead); 0287 writeDataFileHeader(dsfhead); 0288 ns_header_offset = ftell(nsfhead); 0289 us_header_offset = ftell(usfhead); 0290 nf_header_offset = ftell(namefile); 0291 ds_header_offset = ftell(dsfhead); 0292 0293 /* Write a bogus index entry into the namefile, to be filled with correct data later */ 0294 writeIndexEntry(namefile, 0, ftell(namefile) + INDEX_ENTRY_SIZE, 0); 0295 0296 /* Leave space for / write a deep magnitude limit specification in the data files */ 0297 maglim = GLOBAL_MAG_LIMIT * 100; 0298 fwrite(&maglim, 2, 1, nsf); // This is also a bogus entry, because it will be overwritten later 0299 maglim = GLOBAL_MAG_LIMIT * 100; 0300 fwrite(&maglim, 2, 1, dsf); // This is also a bogus entry, because it will be overwritten later 0301 maglim = (int)(-5.0 * 100); 0302 fwrite(&maglim, 2, 1, usf); // Bogus entry 0303 0304 /* Write a HTM level specification in the data file */ 0305 htm_level = HTM_LEVEL; 0306 fwrite(&htm_level, 1, 1, nsf); 0307 fwrite(&htm_level, 1, 1, usf); 0308 fwrite(&htm_level, 1, 1, dsf); 0309 0310 /* Leave space for a specification of MSpT (Maximum Stars per Trixel) in the data files */ 0311 MSpT_named = MSpT_unnamed = 0; 0312 fwrite(&MSpT_named, 2, 1, nsf); // Bogus entry 0313 fwrite(&MSpT_deep, 2, 1, dsf); // Bogus entry 0314 fwrite(&MSpT_unnamed, 2, 1, usf); // Bogus entry 0315 0316 /* Initialize some variables */ 0317 lim = 0; 0318 exitflag = 0; 0319 current_trixel = 0; 0320 nsf_trix_count = usf_trix_count = dsf_trix_count = 0; 0321 nsf_trix_begin = usf_trix_begin = dsf_trix_begin = 0322 2 + 1 + 0323 2; // The 2+1+2 is to leave space for deep magnitude limit, HTM Level and MSpT specification. TODO: Change this if things change. 0324 ntrixels = 0; 0325 names_count = 0; 0326 0327 /* Recurse over every MYSQL_STARS_PER_QUERY DB entries */ 0328 while (!exitflag) 0329 { 0330 /* Build MySQL query for next MYSQL_STARS_PER_QUERY stars */ 0331 sprintf(query, 0332 "SELECT `Trixel`, `RA`, `Dec`, `dRA`, `dDec`, `Parallax`, `Mag`, `BV_Index`, `Spec_Type`, `Mult`, " 0333 "`Var`, `HD`, `UID`, `Name`, `GName` FROM `%s` ORDER BY `trixel`, `mag` ASC LIMIT %ld, %d", 0334 (argc >= 13) ? argv[12] : DB_TBL, lim, MYSQL_STARS_PER_QUERY); 0335 0336 if (VERBOSE) 0337 { 0338 fprintf(stderr, "SQL Query: %s\n", query); 0339 } 0340 0341 /* Execute MySQL query and get the result */ 0342 mysql_real_query(&link, query, (unsigned int)strlen(query)); 0343 result = mysql_use_result(&link); 0344 0345 if (!result) 0346 { 0347 fprintf(stderr, "MySQL returned NULL result: %s\n", mysql_error(&link)); 0348 fcloseall(); 0349 mysql_close(&link); 0350 return 1; 0351 } 0352 0353 exitflag = -1; 0354 0355 /* Process the result row by row */ 0356 while (row = mysql_fetch_row(result)) 0357 { 0358 /* Very verbose details */ 0359 if (VERBOSE > 1) 0360 { 0361 fprintf(stderr, "UID = %s\n", row[12]); 0362 for (i = 0; i <= 14; ++i) 0363 fprintf(stderr, "\tField #%d = %s\n", i, row[i]); 0364 } 0365 0366 if (exitflag == -1) 0367 exitflag = 0; 0368 0369 /* Write index entries if we've changed trixel */ 0370 new_trixel = atoi(row[0]); 0371 if (new_trixel != current_trixel) 0372 { 0373 if (VERBOSE) 0374 { 0375 fprintf(stderr, "Trixel Changed from %d to %d!\n", current_trixel, new_trixel); 0376 } 0377 while (new_trixel != current_trixel) 0378 { 0379 writeIndexEntry(nsfhead, current_trixel, 0380 ns_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + nsf_trix_begin, nsf_trix_count); 0381 writeIndexEntry(dsfhead, current_trixel, 0382 ds_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + dsf_trix_begin, dsf_trix_count); 0383 writeIndexEntry(usfhead, current_trixel, 0384 us_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + usf_trix_begin, usf_trix_count); 0385 nsf_trix_begin = ftell(nsf); 0386 dsf_trix_begin = ftell(dsf); 0387 usf_trix_begin = ftell(usf); 0388 if (nsf_trix_count > MSpT_named) 0389 MSpT_named = nsf_trix_count; 0390 if (dsf_trix_count > MSpT_deep) 0391 MSpT_deep = dsf_trix_count; 0392 if (usf_trix_count > MSpT_unnamed) 0393 MSpT_unnamed = usf_trix_count; 0394 nsf_trix_count = dsf_trix_count = usf_trix_count = 0; 0395 current_trixel++; 0396 ntrixels++; 0397 } 0398 } 0399 0400 /* ==== Set up the starData structure ==== */ 0401 0402 /* Initializations */ 0403 data.flags = 0; 0404 0405 /* This data is required early! */ 0406 str2int16(&data.mag, row[6], 2); 0407 0408 /* Are we looking at a named star */ 0409 named = 0; 0410 if (!isblank(row[13]) || !isblank(row[14])) 0411 { 0412 named = 1; 0413 0414 /* Print out messages */ 0415 if (VERBOSE) 0416 fprintf(stderr, "Named Star!\n"); 0417 if (VERBOSE > 1) 0418 fprintf(stderr, "Bayer Name = %s, Long Name = %s\n", row[14], row[13]); 0419 0420 /* Check for overflows */ 0421 if (strlen(row[13]) > LONG_NAME_LIMIT) 0422 fprintf(stderr, "ERROR: Long Name %s with length %d exceeds LONG_NAME_LIMIT = %d\n", row[13], 0423 strlen(row[13]), LONG_NAME_LIMIT); 0424 if (strlen(row[14]) > BAYER_LIMIT) 0425 fprintf(stderr, "ERROR: Bayer designation %s with length %d exceeds BAYER_LIMIT = %d\n", row[14], 0426 strlen(row[14]), BAYER_LIMIT); 0427 0428 /* Set up the starName structure */ 0429 str2charv(name.bayerName, row[14], BAYER_LIMIT); 0430 str2charv(name.longName, row[13], LONG_NAME_LIMIT); 0431 data.flags = data.flags | 0x01; /* Switch on the 'named' bit */ 0432 } 0433 0434 /* Are we looking at a 'global' star [always in memory] or dynamically loaded star? */ 0435 if (named) 0436 { 0437 f = nsf; 0438 nsf_trix_count++; 0439 shallow = 1; 0440 } 0441 else if ((data.mag / 100.0) <= GLOBAL_MAG_LIMIT) 0442 { 0443 f = usf; 0444 usf_trix_count++; 0445 shallow = 1; 0446 } 0447 else 0448 { 0449 dsf_trix_count++; 0450 f = dsf; 0451 if (maglim < data.mag) 0452 maglim = data.mag; 0453 shallow = 0; 0454 } 0455 0456 /* Convert various fields and make entries into the starData structure */ 0457 str2int32(&data.RA, row[1], 6); 0458 str2int32(&data.Dec, row[2], 5); 0459 str2int32(&data.dRA, row[3], 1); 0460 str2int32(&data.dDec, row[4], 1); 0461 str2int32(&data.parallax, row[5], 1); 0462 str2int32(&data.HD, row[11], 0); 0463 str2int16(&data.bv_index, row[7], 2); 0464 if (str2charv(data.spec_type, row[8], 2) < 0) 0465 fprintf(stderr, "Spectral type entry %s in DB is possibly invalid for UID = %s\n", row[8], row[12]); 0466 if (row[9][0] != '0' && row[9][0] != '\0') 0467 data.flags = data.flags | 0x02; 0468 if (row[10][0] != '0' && row[10][0] != '\0') 0469 data.flags = data.flags | 0x04; 0470 0471 if (data.HD != 0 && !shallow) 0472 { 0473 // Use this information to generate the HD index we want to have. 0474 int32_t off; 0475 if (fseek(hdindex, (data.HD - 1) * sizeof(int32_t), SEEK_SET)) 0476 { 0477 fprintf(stderr, "Invalid seek ( to %X ) on HD Index file. Index will be corrupt.\n", 0478 (data.HD - 1) * sizeof(u_int32_t)); 0479 } 0480 off = ftell(f) + ds_header_offset + NTRIXELS * INDEX_ENTRY_SIZE; 0481 fprintf(stderr, "DEBUG: HD %d at %X. Writing at %X\n", data.HD, off, ftell(hdindex)); 0482 if (!fwrite(&off, sizeof(int32_t), 1, hdindex)) 0483 fprintf(stderr, "fwrite() on HD Index file failed. Index will be empty or incomplete.\n"); 0484 } 0485 0486 /* Write the data into the appropriate data file and any names into the name file */ 0487 if (VERBOSE) 0488 fprintf(stderr, "Writing UID = %s...", row[12]); 0489 fwrite(&data, sizeof(starData), 1, f); 0490 if (named) 0491 { 0492 fwrite(&name.bayerName, BAYER_LIMIT, 1, namefile); 0493 fwrite(&name.longName, LONG_NAME_LIMIT, 1, namefile); 0494 names_count++; 0495 if (VERBOSE) 0496 fprintf(stderr, "Named star count = %ul", names_count); 0497 } 0498 if (VERBOSE) 0499 fprintf(stderr, "Done.\n"); 0500 } 0501 mysql_free_result(result); 0502 lim += MYSQL_STARS_PER_QUERY; 0503 } 0504 0505 do 0506 { 0507 writeIndexEntry(nsfhead, current_trixel, ns_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + nsf_trix_begin, 0508 nsf_trix_count); 0509 writeIndexEntry(dsfhead, current_trixel, ds_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + dsf_trix_begin, 0510 dsf_trix_count); 0511 writeIndexEntry(usfhead, current_trixel, us_header_offset + NTRIXELS * INDEX_ENTRY_SIZE + usf_trix_begin, 0512 usf_trix_count); 0513 nsf_trix_begin = ftell(nsf); 0514 dsf_trix_begin = ftell(dsf); 0515 usf_trix_begin = ftell(usf); 0516 nsf_trix_count = usf_trix_count = dsf_trix_count = 0; 0517 current_trixel++; 0518 ntrixels++; 0519 } while (current_trixel != NTRIXELS); 0520 0521 fseek(namefile, nf_header_offset, SEEK_SET); 0522 writeIndexEntry(namefile, 0, nf_header_offset + INDEX_ENTRY_SIZE, names_count); 0523 0524 if (ntrixels != NTRIXELS) 0525 { 0526 fprintf(stderr, 0527 "ERROR: Expected %u trixels, but found %u instead. Please redefine NTRIXELS in this program, or check " 0528 "the source database for bogus trixels\n", 0529 NTRIXELS, ntrixels); 0530 } 0531 0532 rewind(usf); 0533 rewind(dsf); 0534 rewind(nsf); 0535 fwrite(&maglim, 2, 1, dsf); 0536 maglim = GLOBAL_MAG_LIMIT * 100; 0537 fwrite(&maglim, 2, 1, nsf); 0538 fwrite(&maglim, 2, 1, usf); 0539 fwrite(&htm_level, 1, 1, usf); 0540 fwrite(&htm_level, 1, 1, nsf); 0541 fwrite(&htm_level, 1, 1, dsf); 0542 fwrite(&MSpT_unnamed, 2, 1, usf); 0543 fwrite(&MSpT_deep, 2, 1, dsf); 0544 fwrite(&MSpT_named, 2, 1, nsf); 0545 0546 fcloseall(); 0547 mysql_close(&link); 0548 0549 return 0; 0550 }