File indexing completed on 2024-05-05 15:54:19

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 }