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

0001 /*
0002     SPDX-FileCopyrightText: 2011-2016 Akarsh Simha <akarsh@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 /*
0008  * NOTE: I modified nomadbinfile2mysql 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 "nomadbinfile2sqlite.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 #define DEBUG false
0025 
0026 using namespace std;
0027 
0028 NOMADStarDataWriter::NOMADStarDataWriter(FILE *f, int HTMLevel, sqlite3 *_db, char *_db_tbl)
0029 {
0030     db       = _db;
0031     DataFile = f;
0032     // Create a new shiny HTMesh
0033     m_Mesh = new HTMesh(HTMLevel, HTMLevel);
0034     strcpy(db_tbl, _db_tbl);
0035     m_HeaderRead = false;
0036 }
0037 
0038 NOMADStarDataWriter::~NOMADStarDataWriter()
0039 {
0040     delete m_Mesh;
0041 }
0042 
0043 void NOMADStarDataWriter::bswap_stardata(DeepStarData *stardata)
0044 {
0045     stardata->RA   = bswap_32(stardata->RA);
0046     stardata->Dec  = bswap_32(stardata->Dec);
0047     stardata->dRA  = bswap_16(stardata->dRA);
0048     stardata->dDec = bswap_16(stardata->dDec);
0049     stardata->B    = bswap_16(stardata->B);
0050     stardata->V    = bswap_16(stardata->V);
0051 }
0052 
0053 /**
0054  *@short Create the table
0055  */
0056 bool NOMADStarDataWriter::createTable()
0057 {
0058     // TODO: This is not working. Investigate.
0059     char create_query[2048];
0060     char index_query[2048];
0061     char *errorMessage = 0;
0062     sprintf(create_query,
0063             "CREATE TABLE IF NOT EXISTS `%s` (`Trixel` int(32) NOT NULL , `RA` double NOT NULL , `Dec` double NOT NULL "
0064             ", `dRA` double NOT NULL , `dDec` double NOT NULL , `PM` double NOT NULL , `V` float NOT NULL , `B` float "
0065             "NOT NULL , `Mag` float NOT NULL , `UID` INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT , `Copies` int(8) NOT "
0066             "NULL )",
0067             db_tbl);
0068 
0069     if (sqlite3_exec(db, create_query, 0, 0, &errorMessage) != SQLITE_OK)
0070     {
0071         cerr << "ERROR: Table creation failed: " << errorMessage << endl;
0072         sqlite3_free(errorMessage);
0073         return false;
0074     }
0075 
0076     sprintf(index_query, "CREATE INDEX IF NOT EXISTS `TrixelIndex` ON `%s`(`Trixel`)", db_tbl);
0077 
0078     if (sqlite3_exec(db, index_query, 0, 0, &errorMessage) != SQLITE_OK)
0079     {
0080         cerr << "ERROR: Trixel index creation failed: " << errorMessage << endl;
0081         sqlite3_free(errorMessage);
0082         return false;
0083     }
0084 
0085     return true;
0086 }
0087 
0088 /**
0089  *@short Calculate the final destination RA and Dec of a star with the
0090  *given initial RA, Dec and proper motion rates after 'years' number
0091  *of years
0092  */
0093 void NOMADStarDataWriter::calculatePMCoords(double startRA, double startDec, double dRA, double dDec, double *endRA,
0094                                             double *endDec, float years)
0095 {
0096     // (Translated from Perl)
0097     double theta0 = hour2rad(startRA);
0098     double lat0   = deg2rad(startDec);
0099 
0100     double PMperyear = sqrt(dRA * dRA + dDec * dDec);
0101 
0102     double dir0 = (years > 0.0) ? atan2(dRA, dDec) : atan2(-dRA, -dDec); // If years < 0, we're precessing backwards
0103     double PM   = PMperyear * fabs(years);
0104 
0105     double dst = deg2rad(arcsec2deg(PM / 1000.0)); // Milliarcsecond -> radian
0106 
0107     double phi0 = M_PI / 2.0 - lat0;
0108 
0109     double lat1   = asin(sin(lat0) * cos(dst) + cos(lat0) * sin(dst) * cos(dir0)); // Cosine rule on a sphere
0110     double dtheta = atan2(sin(dir0) * sin(dst) * cos(lat0), cos(dst) - sin(lat0) * sin(lat1));
0111 
0112     *endRA  = rad2hour(theta0 + dtheta);
0113     *endDec = rad2deg(lat1);
0114 }
0115 
0116 /**
0117  *@short Do some calculations and insert the star data into the database
0118  */
0119 bool NOMADStarDataWriter::insertStarData(unsigned int trixel, const DeepStarData *const data)
0120 {
0121     char query[2048];
0122     char *errorMessage = 0;
0123     float mag;
0124     float B, V, RA, Dec, dRA, dDec;
0125 
0126     // Rescale the data from the structure
0127     B    = ((double)data->B) / 1000.0;
0128     V    = ((double)data->V) / 1000.0;
0129     RA   = ((double)data->RA) / 1000000.0;
0130     Dec  = ((double)data->Dec) / 100000.0;
0131     dRA  = ((double)data->dRA) / 1000.0;
0132     dDec = ((double)data->dDec) / 1000.0;
0133 
0134     // Check if the supplied trixel is really the trixel in which the
0135     // star is in according to its RA and Dec. If that's not the case,
0136     // this star is a duplicate and must be ignored
0137     unsigned int originalTrixelID = m_Mesh->index(RA, Dec);
0138     if (trixel != originalTrixelID)
0139         return true; // Ignore this star.
0140 
0141     // Magnitude for sorting.
0142     if (V == 30.0 && B != 30.0)
0143     {
0144         mag = B - 1.6;
0145     }
0146     else
0147     {
0148         mag = V;
0149     }
0150 
0151     // Compute the proper motion
0152     double RA1, Dec1, RA2, Dec2;
0153     double PM; // Magnitude of the proper motion in milliarcseconds per year
0154 
0155     PM = sqrt(dRA * dRA + dDec * dDec);
0156 
0157     calculatePMCoords(RA, Dec, dRA, dDec, &RA1, &Dec1, PM_MILLENIA * -1000.);
0158     calculatePMCoords(RA, Dec, dRA, dDec, &RA2, &Dec2, PM_MILLENIA * 1000.);
0159 
0160     unsigned int TrixelList[900];
0161     int ntrixels = 0;
0162 
0163     double separation = sqrt(hour2deg(RA1 - RA2) * hour2deg(RA1 - RA2) +
0164                              (Dec1 - Dec2) * (Dec1 - Dec2)); // Separation in degrees // ugly.
0165     if (separation > 50.0 / 60.0)                            // 50 arcminutes
0166     {
0167         m_Mesh->intersect(RA1, Dec1, RA2, Dec2);
0168         MeshIterator trixels(m_Mesh);
0169         while (trixels.hasNext())
0170         {
0171             TrixelList[ntrixels] = trixels.next();
0172             ntrixels++;
0173         }
0174     }
0175     else
0176     {
0177         TrixelList[0] = originalTrixelID;
0178         ntrixels      = 1;
0179     }
0180 
0181     if (ntrixels == 0)
0182     {
0183         cerr << "Ntrixels is zero in trixel " << originalTrixelID;
0184         return false;
0185     }
0186 
0187     sprintf(query,
0188             "INSERT INTO `%s` (`Trixel`, `RA`, `Dec`, `dRA`, `dDec`, `B`, `V`, `mag`, `PM`, `Copies`) VALUES (:Trixel, "
0189             ":RA, :Dec, :dRA, :dDec, :B, :V, :mag, :PM, :Copies)",
0190             db_tbl);
0191     sqlite3_stmt *stmt;
0192     sqlite3_prepare_v2(db, query, -1, &stmt, 0);
0193 
0194     if (sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0195     {
0196         cerr << "SQLite INSERT INTO failed! Query was: " << endl << query << endl;
0197         cerr << "Error was: " << errorMessage << endl;
0198         sqlite3_free(errorMessage);
0199         return false;
0200     }
0201 
0202     for (int i = 0; i < ntrixels; ++i)
0203     {
0204         sqlite3_bind_int(stmt, 1, TrixelList[i]);
0205         sqlite3_bind_double(stmt, 2, RA);
0206         sqlite3_bind_double(stmt, 3, Dec);
0207         sqlite3_bind_double(stmt, 4, dRA);
0208         sqlite3_bind_double(stmt, 5, dDec);
0209         sqlite3_bind_double(stmt, 6, B);
0210         sqlite3_bind_double(stmt, 7, V);
0211         sqlite3_bind_double(stmt, 8, mag);
0212         sqlite3_bind_double(stmt, 9, PM);
0213         sqlite3_bind_int(stmt, 10, ((TrixelList[i] == originalTrixelID) ? ntrixels : 0));
0214 
0215         sqlite3_step(stmt);
0216         sqlite3_clear_bindings(stmt);
0217         sqlite3_reset(stmt);
0218     }
0219 
0220     if (sqlite3_exec(db, "END TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0221     {
0222         cerr << "SQLite INSERT INTO failed! Query was: " << endl << query << endl;
0223         cerr << "Error was: " << errorMessage << endl;
0224         sqlite3_free(errorMessage);
0225         return false;
0226     }
0227 
0228     sqlite3_finalize(stmt);
0229 
0230     return true;
0231 }
0232 
0233 bool NOMADStarDataWriter::truncateTable()
0234 {
0235     // Truncate table. TODO: Issue warning etc
0236     char query[60];
0237     char *errorMessage = 0;
0238     sprintf(query, "DELETE FROM `%s`; VACUUM;", db_tbl);
0239     if (sqlite3_exec(db, query, 0, 0, &errorMessage) != SQLITE_OK)
0240     {
0241         cerr << "Truncate table query \"" << query << "\" failed!" << endl;
0242         cerr << "Error was: " << errorMessage << endl;
0243         sqlite3_free(errorMessage);
0244         return false;
0245     }
0246     return true;
0247 }
0248 
0249 /**
0250  *@short Write star data to the database
0251  */
0252 bool NOMADStarDataWriter::writeStarDataToDB()
0253 {
0254     int8_t HTM_Level;
0255     u_int16_t MSpT;
0256     u_int32_t nstars;
0257     u_int32_t offset;
0258     unsigned int trixel;
0259     DeepStarData data;
0260     int16_t mag;
0261 
0262     /*
0263       // TODO: FIX THIS // FIXME
0264     // We must at least check if the HTM level matches
0265     fseek( DataFile, m_IndexOffset - 3, SEEK_SET );
0266     fread( &HTM_Level, 1, 1, DataFile );
0267     fprintf( stdout, "HTMesh Level: %d\n", HTM_Level );
0268     if( HTM_Level != m_Mesh->level() ) {
0269         cerr << "ERROR: HTMesh Level in file (" << HTM_Level << ") and HTM_LEVEL in program (" << m_Mesh->level() << ") differ." << endl
0270              << "Please set the define directive for HTM_LEVEL in the header file correctly and rebuild."
0271              << endl;
0272         return false;
0273     }
0274     */
0275     char *errorMessage = 0;
0276     char query[2048];
0277 
0278     sprintf(query,
0279             "INSERT INTO `%s` (`Trixel`, `RA`, `Dec`, `dRA`, `dDec`, `B`, `V`, `mag`, `PM`, `Copies`) VALUES (:Trixel, "
0280             ":RA, :Dec, :dRA, :dDec, :B, :V, :mag, :PM, :Copies)",
0281             db_tbl);
0282     sqlite3_stmt *stmt;
0283     sqlite3_prepare_v2(db, query, -1, &stmt, 0);
0284 
0285     if (sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0286     {
0287         cerr << "SQLite BEGIN TRANSACTION failed! Query was: " << endl << query << endl;
0288         cerr << "Error was: " << errorMessage << endl;
0289         sqlite3_free(errorMessage);
0290         return false;
0291     }
0292 
0293     for (trixel = 0; trixel < ntrixels; ++trixel)
0294     {
0295         fseek(DataFile, m_IndexOffset + trixel * INDEX_ENTRY_SIZE + 4, SEEK_SET);
0296         fread(&offset, 4, 1, DataFile);
0297         fread(&nstars, 4, 1, DataFile);
0298 
0299         if (DEBUG)
0300             cerr << "Processing trixel " << trixel << " of " << ntrixels << endl;
0301 
0302         /* If offset > 2^31 - 1, do the fseek in two steps */
0303         if (offset > unsigned(pow2(31) - 1))
0304         {
0305             fseek(DataFile, unsigned(pow2(31) - 1), SEEK_SET);
0306             fseek(DataFile, offset - pow2(31) + 1, SEEK_CUR);
0307         }
0308         else
0309             fseek(DataFile, offset, SEEK_SET);
0310 
0311         for (int i = 0; i < nstars; ++i)
0312         {
0313             if (DEBUG)
0314                 cerr << "Processing star " << i << " of " << nstars << " in trixel " << trixel << endl;
0315             fread(&data, sizeof(DeepStarData), 1, DataFile);
0316             if (byteswap)
0317                 bswap_stardata(&data);
0318 
0319             /** CODE FROM INSERTSTARDATA PASTED HERE FOR SPEED */
0320             {
0321                 float mag;
0322                 float B, V, RA, Dec, dRA, dDec;
0323 
0324                 // Rescale the data from the structure
0325                 B    = ((double)data.B) / 1000.0;
0326                 V    = ((double)data.V) / 1000.0;
0327                 RA   = ((double)data.RA) / 1000000.0;
0328                 Dec  = ((double)data.Dec) / 100000.0;
0329                 dRA  = ((double)data.dRA) / 1000.0;
0330                 dDec = ((double)data.dDec) / 1000.0;
0331 
0332                 // Check if the supplied trixel is really the trixel in which the
0333                 // star is in according to its RA and Dec. If that's not the case,
0334                 // this star is a duplicate and must be ignored
0335                 unsigned int originalTrixelID = m_Mesh->index(hour2deg(RA), Dec);
0336                 if (trixel != originalTrixelID)
0337                 {
0338                     cout << "Trixel = " << trixel << ", but this is the original Trixel ID: " << originalTrixelID
0339                          << ". Skipping" << endl;
0340                     cout << "Skipped star has (RA, Dec) = " << RA << Dec << "; (dRA, dDec) = " << dRA << dDec
0341                          << "; and (B, V) = " << B << V << "." << endl;
0342                     cout << "This suspected duplicate is star " << i << "in trixel " << trixel;
0343                     continue;
0344                 }
0345 
0346                 // Magnitude for sorting.
0347                 if (V == 30.0 && B != 30.0)
0348                 {
0349                     mag = B - 1.6;
0350                 }
0351                 else
0352                 {
0353                     mag = V;
0354                 }
0355 
0356                 // Compute the proper motion
0357                 double RA1, Dec1, RA2, Dec2, RA1deg, RA2deg;
0358                 double PM; // Magnitude of the proper motion in milliarcseconds per year
0359 
0360                 PM = sqrt(dRA * dRA + dDec * dDec);
0361 
0362                 calculatePMCoords(RA, Dec, dRA, dDec, &RA1, &Dec1, PM_MILLENIA * -1000.);
0363                 calculatePMCoords(RA, Dec, dRA, dDec, &RA2, &Dec2, PM_MILLENIA * 1000.);
0364                 RA1deg = hour2deg(RA1);
0365                 RA2deg = hour2deg(RA2);
0366 
0367                 unsigned int TrixelList[60];
0368                 int nt = 0;
0369 
0370                 double separationsqr = (RA1deg - RA2deg) * (RA1deg - RA2deg) +
0371                                        (Dec1 - Dec2) * (Dec1 - Dec2); // Separation in degrees // ugly.
0372                 if (separationsqr >
0373                     0.69) // 50 arcminutes converted to degrees, squared and rounded below = 0.69. (This has nothing to do with sex positions.)
0374                 {
0375                     m_Mesh->intersect(RA1deg, Dec1, RA2deg, Dec2);
0376                     MeshIterator trixels(m_Mesh);
0377                     while (trixels.hasNext())
0378                     {
0379                         TrixelList[nt] = trixels.next();
0380                         nt++;
0381                     }
0382                 }
0383                 else
0384                 {
0385                     TrixelList[0] = originalTrixelID;
0386                     nt            = 1;
0387                 }
0388 
0389                 if (nt == 0)
0390                 {
0391                     cerr << "# of trixels is zero in trixel " << originalTrixelID;
0392                     return false;
0393                 }
0394 
0395                 for (int i = 0; i < nt; ++i)
0396                 {
0397                     sqlite3_bind_int(stmt, 1, TrixelList[i]);
0398                     sqlite3_bind_double(stmt, 2, RA);
0399                     sqlite3_bind_double(stmt, 3, Dec);
0400                     sqlite3_bind_double(stmt, 4, dRA);
0401                     sqlite3_bind_double(stmt, 5, dDec);
0402                     sqlite3_bind_double(stmt, 6, B);
0403                     sqlite3_bind_double(stmt, 7, V);
0404                     sqlite3_bind_double(stmt, 8, mag);
0405                     sqlite3_bind_double(stmt, 9, PM);
0406                     sqlite3_bind_int(stmt, 10, ((TrixelList[i] == originalTrixelID) ? ntrixels : 0));
0407 
0408                     sqlite3_step(stmt);
0409                     sqlite3_clear_bindings(stmt);
0410                     sqlite3_reset(stmt);
0411                 }
0412             }
0413         }
0414         if (trixel % 100 == 0 && trixel != 0)
0415         {
0416             if (sqlite3_exec(db, "END TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0417             {
0418                 cerr << "SQLite END TRANSACTION failed! Query was: " << endl << query << endl;
0419                 cerr << "Error was: " << errorMessage << endl;
0420                 sqlite3_free(errorMessage);
0421                 return false;
0422             }
0423             sqlite3_finalize(stmt);
0424             sqlite3_prepare_v2(db, query, -1, &stmt, 0);
0425 
0426             if (sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0427             {
0428                 cerr << "SQLite BEGIN TRANSACTION failed! Query was: " << endl << query << endl;
0429                 cerr << "Error was: " << errorMessage << endl;
0430                 sqlite3_free(errorMessage);
0431                 return false;
0432             }
0433 
0434             cout << "Finished trixel " << trixel << endl;
0435         }
0436     }
0437 
0438     if (sqlite3_exec(db, "END TRANSACTION", 0, 0, &errorMessage) != SQLITE_OK)
0439     {
0440         cerr << "SQLite INSERT INTO failed! Query was: " << endl << query << endl;
0441         cerr << "Error was: " << errorMessage << endl;
0442         sqlite3_free(errorMessage);
0443         return false;
0444     }
0445 
0446     sqlite3_finalize(stmt);
0447 
0448     return true;
0449 }
0450 
0451 bool NOMADStarDataWriter::readFileHeader()
0452 {
0453     int i;
0454     int16_t endian_id;
0455     char ASCII_text[125];
0456     u_int8_t version_no;
0457     u_int16_t nfields;
0458 
0459     if (!DataFile)
0460         return false;
0461 
0462     fread(ASCII_text, 124, 1, DataFile);
0463     ASCII_text[124] = '\0';
0464     printf("%s", ASCII_text);
0465 
0466     fread(&endian_id, 2, 1, DataFile);
0467     if (endian_id != 0x4B53)
0468     {
0469         fprintf(stdout, "Byteswapping required\n");
0470         byteswap = 1;
0471     }
0472     else
0473     {
0474         fprintf(stdout, "Byteswapping not required\n");
0475         byteswap = 0;
0476     }
0477 
0478     fread(&version_no, 1, 1, DataFile);
0479     fprintf(stdout, "Version number: %d\n", version_no);
0480 
0481     fread(&nfields, 2, 1, DataFile);
0482 
0483     // Just to read those many bytes
0484     // TODO: Don't waste time and memory. fseek.
0485     dataElement de;
0486     for (i = 0; i < nfields; ++i)
0487         fread(&de, sizeof(struct dataElement), 1, DataFile);
0488 
0489     fread(&ntrixels, 4, 1, DataFile);
0490     if (byteswap)
0491         ntrixels = bswap_32(ntrixels);
0492     fprintf(stdout, "Number of trixels reported = %d\n", ntrixels);
0493 
0494     m_IndexOffset = ftell(DataFile);
0495 
0496     m_HeaderRead = true;
0497 
0498     return true;
0499 }
0500 
0501 bool NOMADStarDataWriter::write()
0502 {
0503     if (!readFileHeader())
0504         return false;
0505     if (!createTable())
0506         return false;
0507     truncateTable();
0508     if (!writeStarDataToDB())
0509         return false;
0510 }
0511 
0512 int main(int argc, char *argv[])
0513 {
0514     sqlite3 *db;
0515     FILE *f;
0516     char db_tbl[20];
0517     char db_name[20];
0518     int rc;
0519 
0520     if (argc <= 3)
0521     {
0522         fprintf(stderr, "USAGE: %s <NOMAD bin file> <SQLite DB File Name> <Table Name>\n", argv[0]);
0523         return 1;
0524     }
0525 
0526     strcpy(db_name, argv[2]);
0527     strcpy(db_tbl, argv[3]);
0528 
0529     f = fopen(argv[1], "r");
0530 
0531     if (f == NULL)
0532     {
0533         fprintf(stderr, "ERROR: Could not open file %s for binary read.\n", argv[1]);
0534         return 1;
0535     }
0536 
0537     /* Open the Database */
0538     if (sqlite3_open(db_name, &db))
0539     {
0540         fprintf(stderr, "ERROR: Failed to open database: %s\n", sqlite3_errmsg(db));
0541         sqlite3_close(db);
0542         return 1;
0543     }
0544 
0545     NOMADStarDataWriter writer(f, HTM_LEVEL, db, db_tbl);
0546 
0547     writer.write();
0548 
0549     fclose(f);
0550     sqlite3_close(db);
0551     return 0;
0552 }