File indexing completed on 2024-04-28 03:42:52

0001 /*
0002     SPDX-FileCopyrightText: 2011 Akarsh Simha <akarshsimha@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 /*
0008  * NOTE: I modified nomadbinfiletester.c to do this -- Akarsh
0009  */
0010 
0011 /*
0012  * TODO: VERY UGLY CODE. Please fix it some day. Preferably now.  This
0013  * file was created from a modified C file, and needs to be recast
0014  * into the C++ way of writing stuff, i.e. with classes etc.
0015  */
0016 
0017 #include "nomadbinfile2mysql.h"
0018 #include "binfile.h"
0019 #include "angconversion.h"
0020 #include "MeshIterator.h"
0021 #include <iostream>
0022 #include <string.h>
0023 #include <stdio.h>
0024 
0025 using namespace std;
0026 
0027 NOMADStarDataWriter::NOMADStarDataWriter(FILE *f, int HTMLevel, MYSQL *link, char *_db_tbl)
0028 {
0029     m_MySQLLink = link;
0030     DataFile    = f;
0031     // Create a new shiny HTMesh
0032     m_Mesh = new HTMesh(HTMLevel, HTMLevel);
0033     strcpy(db_tbl, _db_tbl);
0034     m_HeaderRead = false;
0035 }
0036 
0037 NOMADStarDataWriter::~NOMADStarDataWriter()
0038 {
0039     delete m_Mesh;
0040 }
0041 
0042 void NOMADStarDataWriter::bswap_stardata(DeepStarData *stardata)
0043 {
0044     stardata->RA   = bswap_32(stardata->RA);
0045     stardata->Dec  = bswap_32(stardata->Dec);
0046     stardata->dRA  = bswap_16(stardata->dRA);
0047     stardata->dDec = bswap_16(stardata->dDec);
0048     stardata->B    = bswap_16(stardata->B);
0049     stardata->V    = bswap_16(stardata->V);
0050 }
0051 
0052 /**
0053  *@short Create the table
0054  */
0055 bool NOMADStarDataWriter::createTable()
0056 {
0057     // TODO: This is not working. Investigate.
0058     char create_query[2048];
0059     sprintf(create_query,
0060             "CREATE TABLE IF NOT EXISTS `%s` (`Trixel` int(32) NOT NULL COMMENT 'Trixel Number', `RA` double NOT NULL "
0061             "COMMENT 'RA Hours', `Dec` double NOT NULL COMMENT 'Declination Degrees', `dRA` double NOT NULL COMMENT "
0062             "'Proper Motion along RA', `dDec` double NOT NULL COMMENT 'Proper Motion along Dec', `PM` double NOT NULL "
0063             "COMMENT 'Proper Motion (magnitude)', `V` float NOT NULL COMMENT 'Visual Magnitude', `B` float NOT NULL "
0064             "COMMENT 'Blue Magnitude', `Mag` float NOT NULL COMMENT 'Magnitude for sorting', `UID` int(64) NOT NULL "
0065             "auto_increment COMMENT 'Unique ID', `Copies` tinyint(8) NOT NULL COMMENT 'Number of Copies of the star', "
0066             "PRIMARY KEY  (`UID`), UNIQUE KEY `UID` (`UID`), KEY `Trixel` (`Trixel`,`PM`,`Mag`)) ENGINE=MyISAM DEFAULT "
0067             "CHARSET=latin1 AUTO_INCREMENT=1",
0068             db_tbl);
0069 
0070     if (mysql_query(m_MySQLLink, create_query))
0071     {
0072         cerr << "ERROR: Table creation failed!" << endl;
0073         return false;
0074     }
0075     return true;
0076 }
0077 
0078 /**
0079  *@short Calculate the final destination RA and Dec of a star with the
0080  *given initial RA, Dec and proper motion rates after 'years' number
0081  *of years
0082  */
0083 void NOMADStarDataWriter::calculatePMCoords(double startRA, double startDec, double dRA, double dDec, double *endRA,
0084                                             double *endDec, float years)
0085 {
0086     // (Translated from Perl)
0087     double theta0 = hour2rad(startRA);
0088     double lat0   = deg2rad(startDec);
0089 
0090     double PMperyear = sqrt(dRA * dRA + dDec * dDec);
0091 
0092     double dir0 = (years > 0.0) ? atan2(dRA, dDec) : atan2(-dRA, -dDec); // If years < 0, we're precessing backwards
0093     double PM   = PMperyear * fabs(years);
0094 
0095     double dst = deg2rad(arcsec2deg(PM / 1000.0)); // Milliarcsecond -> radian
0096 
0097     double phi0 = M_PI / 2.0 - lat0;
0098 
0099     double lat1   = asin(sin(lat0) * cos(dst) + cos(lat0) * sin(dst) * cos(dir0)); // Cosine rule on a sphere
0100     double dtheta = atan2(sin(dir0) * sin(dst) * cos(lat0), cos(dst) - sin(lat0) * sin(lat1));
0101 
0102     *endRA  = rad2hour(theta0 + dtheta);
0103     *endDec = rad2deg(lat1);
0104 }
0105 
0106 /**
0107  *@short Do some calculations and insert the star data into the database
0108  */
0109 bool NOMADStarDataWriter::insertStarData(unsigned int trixel, const DeepStarData *const data)
0110 {
0111     char query[2048];
0112     float mag;
0113     float B, V, RA, Dec, dRA, dDec;
0114 
0115     // Rescale the data from the structure
0116     B    = ((double)data->B) / 1000.0;
0117     V    = ((double)data->V) / 1000.0;
0118     RA   = ((double)data->RA) / 1000000.0;
0119     Dec  = ((double)data->Dec) / 100000.0;
0120     dRA  = ((double)data->dRA) / 1000.0;
0121     dDec = ((double)data->dDec) / 1000.0;
0122 
0123     // Check if the supplied trixel is really the trixel in which the
0124     // star is in according to its RA and Dec. If that's not the case,
0125     // this star is a duplicate and must be ignored
0126     unsigned int originalTrixelID = m_Mesh->index(RA, Dec);
0127     if (trixel != originalTrixelID)
0128         return true; // Ignore this star.
0129 
0130     // Magnitude for sorting.
0131     if (V == 30.0 && B != 30.0)
0132     {
0133         mag = B - 1.6;
0134     }
0135     else
0136     {
0137         mag = V;
0138     }
0139 
0140     // Compute the proper motion
0141     double RA1, Dec1, RA2, Dec2;
0142     double PM; // Magnitude of the proper motion in milliarcseconds per year
0143 
0144     PM = sqrt(dRA * dRA + dDec * dDec);
0145 
0146     calculatePMCoords(RA, Dec, dRA, dDec, &RA1, &Dec1, PM_MILLENIA * -1000.);
0147     calculatePMCoords(RA, Dec, dRA, dDec, &RA2, &Dec2, PM_MILLENIA * 1000.);
0148 
0149     unsigned int TrixelList[900];
0150     int ntrixels = 0;
0151 
0152     double separation = sqrt(hour2deg(RA1 - RA2) * hour2deg(RA1 - RA2) +
0153                              (Dec1 - Dec2) * (Dec1 - Dec2)); // Separation in degrees // ugly.
0154     if (separation > 50.0 / 60.0)                            // 50 arcminutes
0155     {
0156         m_Mesh->intersect(RA1, Dec1, RA2, Dec2);
0157         MeshIterator trixels(m_Mesh);
0158         while (trixels.hasNext())
0159         {
0160             TrixelList[ntrixels] = trixels.next();
0161             ntrixels++;
0162         }
0163     }
0164     else
0165     {
0166         TrixelList[0] = originalTrixelID;
0167         ntrixels      = 1;
0168     }
0169 
0170     if (ntrixels == 0)
0171     {
0172         cerr << "Ntrixels is zero in trixel " << originalTrixelID;
0173         return false;
0174     }
0175 
0176     for (int i = 0; i < ntrixels; ++i)
0177     {
0178         sprintf(query,
0179                 "INSERT INTO `%s` (`Trixel`, `RA`, `Dec`, `dRA`, `dDec`, `B`, `V`, `mag`, `PM`, `Copies`) VALUES "
0180                 "(\'%d\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%u\')",
0181                 db_tbl, TrixelList[i], RA, Dec, dRA, dDec, B, V, mag, PM,
0182                 ((TrixelList[i] == originalTrixelID) ?
0183                      ntrixels :
0184                      0) // Duplicates get a 'Copies' value of 0. The real star gets the actual value.
0185         );
0186         if (mysql_query(m_MySQLLink, query))
0187         {
0188             cerr << "MySQL INSERT INTO failed! Query was: " << endl << query << endl;
0189             return false;
0190         }
0191     }
0192     return true;
0193 }
0194 
0195 bool NOMADStarDataWriter::truncateTable()
0196 {
0197     // Truncate table. TODO: Issue warning etc
0198     char query[60];
0199     sprintf(query, "TRUNCATE TABLE `%s`", db_tbl);
0200     if (mysql_query(m_MySQLLink, query))
0201     {
0202         cerr << "Truncate table query \"" << query << "\" failed!" << endl;
0203         return false;
0204     }
0205     return true;
0206 }
0207 
0208 /**
0209  *@short Write star data to the database
0210  */
0211 bool NOMADStarDataWriter::writeStarDataToDB()
0212 {
0213     int8_t HTM_Level;
0214     u_int16_t MSpT;
0215     u_int32_t nstars;
0216     u_int32_t offset;
0217     unsigned int trixel;
0218     DeepStarData data;
0219     int16_t mag;
0220 
0221     /*
0222       // TODO: FIX THIS // FIXME
0223     // We must at least check if the HTM level matches
0224     fseek( DataFile, m_IndexOffset - 3, SEEK_SET );
0225     fread( &HTM_Level, 1, 1, DataFile );
0226     fprintf( stdout, "HTMesh Level: %d\n", HTM_Level );
0227     if( HTM_Level != m_Mesh->level() ) {
0228         cerr << "ERROR: HTMesh Level in file (" << HTM_Level << ") and HTM_LEVEL in program (" << m_Mesh->level() << ") differ." << endl
0229              << "Please set the define directive for HTM_LEVEL in the header file correctly and rebuild."
0230              << endl;
0231         return false;
0232     }
0233     */
0234 
0235     for (trixel = 0; trixel < ntrixels; ++trixel)
0236     {
0237         fseek(DataFile, m_IndexOffset + trixel * INDEX_ENTRY_SIZE + 4, SEEK_SET);
0238         fread(&offset, 4, 1, DataFile);
0239         fread(&nstars, 4, 1, DataFile);
0240 
0241         /* If offset > 2^31 - 1, do the fseek in two steps */
0242         if (offset > (unsigned)(pow2(31) - 1))
0243         {
0244             fseek(DataFile, pow2(31) - 1, SEEK_SET);
0245             fseek(DataFile, offset - pow2(31) + 1, SEEK_CUR);
0246         }
0247         else
0248             fseek(DataFile, offset, SEEK_SET);
0249 
0250         for (int i = 0; i < nstars; ++i)
0251         {
0252             fread(&data, sizeof(DeepStarData), 1, DataFile);
0253             if (byteswap)
0254                 bswap_stardata(&data);
0255 
0256             /** CODE FROM INSERTSTARDATA PASTED HERE FOR SPEED */
0257             {
0258                 char query[2048];
0259                 float mag;
0260                 float B, V, RA, Dec, dRA, dDec;
0261 
0262                 // Rescale the data from the structure
0263                 B    = ((double)data.B) / 1000.0;
0264                 V    = ((double)data.V) / 1000.0;
0265                 RA   = ((double)data.RA) / 1000000.0;
0266                 Dec  = ((double)data.Dec) / 100000.0;
0267                 dRA  = ((double)data.dRA) / 1000.0;
0268                 dDec = ((double)data.dDec) / 1000.0;
0269 
0270                 // Check if the supplied trixel is really the trixel in which the
0271                 // star is in according to its RA and Dec. If that's not the case,
0272                 // this star is a duplicate and must be ignored
0273                 unsigned int originalTrixelID = m_Mesh->index(hour2deg(RA), Dec);
0274                 if (trixel != originalTrixelID)
0275                 {
0276                     cout << "Trixel = " << trixel << ", but this is the original Trixel ID: " << originalTrixelID
0277                          << ". Skipping" << endl;
0278                     cout << "Skipped star has (RA, Dec) = " << RA << Dec << "; (dRA, dDec) = " << dRA << dDec
0279                          << "; and (B, V) = " << B << V << "." << endl;
0280                     cout << "This suspected duplicate is star " << i << "in trixel " << trixel;
0281                     continue;
0282                 }
0283 
0284                 // Magnitude for sorting.
0285                 if (V == 30.0 && B != 30.0)
0286                 {
0287                     mag = B - 1.6;
0288                 }
0289                 else
0290                 {
0291                     mag = V;
0292                 }
0293 
0294                 // Compute the proper motion
0295                 double RA1, Dec1, RA2, Dec2, RA1deg, RA2deg;
0296                 double PM; // Magnitude of the proper motion in milliarcseconds per year
0297 
0298                 PM = sqrt(dRA * dRA + dDec * dDec);
0299 
0300                 calculatePMCoords(RA, Dec, dRA, dDec, &RA1, &Dec1, PM_MILLENIA * -1000.);
0301                 calculatePMCoords(RA, Dec, dRA, dDec, &RA2, &Dec2, PM_MILLENIA * 1000.);
0302                 RA1deg = hour2deg(RA1);
0303                 RA2deg = hour2deg(RA2);
0304 
0305                 unsigned int TrixelList[60];
0306                 int nt = 0;
0307 
0308                 double separationsqr = (RA1deg - RA2deg) * (RA1deg - RA2deg) +
0309                                        (Dec1 - Dec2) * (Dec1 - Dec2); // Separation in degrees // ugly.
0310                 if (separationsqr >
0311                     0.69) // 50 arcminutes converted to degrees, squared and rounded below = 0.69. (This has nothing to do with sex positions.)
0312                 {
0313                     m_Mesh->intersect(RA1deg, Dec1, RA2deg, Dec2);
0314                     MeshIterator trixels(m_Mesh);
0315                     while (trixels.hasNext())
0316                     {
0317                         TrixelList[nt] = trixels.next();
0318                         nt++;
0319                     }
0320                 }
0321                 else
0322                 {
0323                     TrixelList[0] = originalTrixelID;
0324                     nt            = 1;
0325                 }
0326 
0327                 if (nt == 0)
0328                 {
0329                     cerr << "# of trixels is zero in trixel " << originalTrixelID;
0330                     return false;
0331                 }
0332 
0333                 for (int i = 0; i < nt; ++i)
0334                 {
0335                     sprintf(query,
0336                             "INSERT INTO `%s` (`Trixel`, `RA`, `Dec`, `dRA`, `dDec`, `B`, `V`, `mag`, `PM`, `Copies`) "
0337                             "VALUES (\'%d\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', \'%lf\', "
0338                             "\'%u\')",
0339                             db_tbl, TrixelList[i], RA, Dec, dRA, dDec, B, V, mag, PM,
0340                             ((TrixelList[i] == originalTrixelID) ?
0341                                  nt :
0342                                  0) // Duplicates get a 'Copies' value of 0. The real star gets the actual value.
0343                     );
0344                     if (mysql_query(m_MySQLLink, query))
0345                     {
0346                         cerr << "MySQL INSERT INTO failed! Query was: " << endl << query << endl;
0347                         return false;
0348                     }
0349                 }
0350             }
0351         }
0352         if (trixel % 100 == 0)
0353             cout << "Finished trixel " << trixel << endl;
0354     }
0355 
0356     return true;
0357 }
0358 
0359 bool NOMADStarDataWriter::readFileHeader()
0360 {
0361     int i;
0362     int16_t endian_id;
0363     char ASCII_text[125];
0364     u_int8_t version_no;
0365     u_int16_t nfields;
0366 
0367     if (!DataFile)
0368         return false;
0369 
0370     fread(ASCII_text, 124, 1, DataFile);
0371     ASCII_text[124] = '\0';
0372     printf("%s", ASCII_text);
0373 
0374     fread(&endian_id, 2, 1, DataFile);
0375     if (endian_id != 0x4B53)
0376     {
0377         fprintf(stdout, "Byteswapping required\n");
0378         byteswap = 1;
0379     }
0380     else
0381     {
0382         fprintf(stdout, "Byteswapping not required\n");
0383         byteswap = 0;
0384     }
0385 
0386     fread(&version_no, 1, 1, DataFile);
0387     fprintf(stdout, "Version number: %d\n", version_no);
0388 
0389     fread(&nfields, 2, 1, DataFile);
0390 
0391     // Just to read those many bytes
0392     // TODO: Don't waste time and memory. fseek.
0393     dataElement de;
0394     for (i = 0; i < nfields; ++i)
0395         fread(&de, sizeof(struct dataElement), 1, DataFile);
0396 
0397     fread(&ntrixels, 4, 1, DataFile);
0398     if (byteswap)
0399         ntrixels = bswap_32(ntrixels);
0400     fprintf(stdout, "Number of trixels reported = %d\n", ntrixels);
0401 
0402     m_IndexOffset = ftell(DataFile);
0403 
0404     m_HeaderRead = true;
0405 
0406     return true;
0407 }
0408 
0409 bool NOMADStarDataWriter::write()
0410 {
0411     if (!readFileHeader())
0412         return false;
0413     if (!createTable())
0414         return false;
0415     truncateTable();
0416     if (!writeStarDataToDB())
0417         return false;
0418 }
0419 
0420 int main(int argc, char *argv[])
0421 {
0422     MYSQL link;
0423     FILE *f;
0424     char db_tbl[20];
0425     char db_name[20];
0426 
0427     if (argc <= 5)
0428     {
0429         fprintf(stderr, "USAGE: %s <NOMAD bin file> <MySQL DB User> <Password> <DB Name> <Table Name>\n", argv[0]);
0430         return 1;
0431     }
0432 
0433     strcpy(db_tbl, argv[5]);
0434     strcpy(db_name, argv[4]);
0435 
0436     f = fopen(argv[1], "r");
0437 
0438     if (f == nullptr)
0439     {
0440         fprintf(stderr, "ERROR: Could not open file %s for binary read.\n", argv[1]);
0441         return 1;
0442     }
0443 
0444     /* Open the Database */
0445     if (mysql_init(&link) == nullptr)
0446     {
0447         fprintf(stderr, "ERROR: Failed to initialize MySQL connection!\n");
0448         return 1;
0449     }
0450     MYSQL *ret;
0451     ret = mysql_real_connect(&link, "localhost", argv[2], argv[3], db_name, 0, nullptr, 0);
0452 
0453     if (!ret)
0454     {
0455         fprintf(stderr, "ERROR: MySQL connect failed for the following reason: %s\n", mysql_error(&link));
0456         fcloseall();
0457         return 1;
0458     }
0459 
0460     if (mysql_select_db(&link, db_name))
0461     {
0462         fprintf(stderr, "ERROR: Could not select MySQL database %s. MySQL said: %s", db_name, mysql_error(&link));
0463         fcloseall();
0464         mysql_close(&link);
0465         return 1;
0466     }
0467 
0468     NOMADStarDataWriter writer(f, HTM_LEVEL, &link, db_tbl);
0469 
0470     writer.write();
0471 
0472     fclose(f);
0473     mysql_close(&link);
0474     return 0;
0475 }