File indexing completed on 2024-03-24 15:17:44

0001 /*
0002     SPDX-FileCopyrightText: 2008 Akarsh Simha Thomas Kabelmann <akarshsimha@gmail.com, thomas.kabelmann@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "deepstarcomponent.h"
0008 
0009 #include "byteorder.h"
0010 #include "kstarsdata.h"
0011 #include "Options.h"
0012 #ifndef KSTARS_LITE
0013 #include "skymap.h"
0014 #endif
0015 #include "skymesh.h"
0016 #include "skypainter.h"
0017 #include "starblock.h"
0018 #include "starcomponent.h"
0019 #include "htmesh/MeshIterator.h"
0020 #include "projections/projector.h"
0021 
0022 #include <qplatformdefs.h>
0023 #include <QtConcurrent>
0024 #include <QElapsedTimer>
0025 
0026 #include <kstars_debug.h>
0027 
0028 #ifdef _WIN32
0029 #include <windows.h>
0030 #endif
0031 
0032 DeepStarComponent::DeepStarComponent(SkyComposite *parent, QString fileName, float trigMag, bool staticstars)
0033     : ListComponent(parent), m_reindexNum(J2000), triggerMag(trigMag), m_FaintMagnitude(-5.0), staticStars(staticstars),
0034       dataFileName(fileName)
0035 {
0036     fileOpened = false;
0037     openDataFile();
0038     if (staticStars)
0039         loadStaticStars();
0040     qCInfo(KSTARS) << "Loaded DSO catalog file: " << dataFileName;
0041 }
0042 
0043 DeepStarComponent::~DeepStarComponent()
0044 {
0045     if (fileOpened)
0046         starReader.closeFile();
0047     fileOpened = false;
0048 }
0049 
0050 bool DeepStarComponent::loadStaticStars()
0051 {
0052     FILE *dataFile;
0053 
0054     if (!staticStars)
0055         return true;
0056     if (!fileOpened)
0057         return false;
0058 
0059     dataFile = starReader.getFileHandle();
0060     rewind(dataFile);
0061 
0062     if (!starReader.readHeader())
0063     {
0064         qCCritical(KSTARS) << "Error reading header of catalog file " << dataFileName << ": " << starReader.getErrorNumber()
0065                            << ": " << starReader.getError();
0066         return false;
0067     }
0068 
0069     quint8 recordSize = starReader.guessRecordSize();
0070 
0071     if (recordSize != 16 && recordSize != 32)
0072     {
0073         qCCritical(KSTARS) << "Cannot understand catalog file " << dataFileName;
0074         return false;
0075     }
0076 
0077     //KDE_fseek(dataFile, starReader.getDataOffset(), SEEK_SET);
0078     QT_FSEEK(dataFile, starReader.getDataOffset(), SEEK_SET);
0079 
0080     qint16 faintmag;
0081     quint8 htm_level;
0082     quint16 t_MSpT;
0083     int ret = 0;
0084 
0085     ret = fread(&faintmag, 2, 1, dataFile);
0086     if (starReader.getByteSwap())
0087         faintmag = bswap_16(faintmag);
0088     ret = fread(&htm_level, 1, 1, dataFile);
0089     ret = fread(&t_MSpT, 2, 1, dataFile); // Unused
0090     if (starReader.getByteSwap())
0091         faintmag = bswap_16(faintmag);
0092 
0093     // TODO: Read the multiplying factor from the dataFile
0094     m_FaintMagnitude = faintmag / 100.0;
0095 
0096     if (htm_level != m_skyMesh->level())
0097         qCWarning(KSTARS) << "HTM Level in shallow star data file and HTM Level in m_skyMesh do not match. EXPECT TROUBLE!";
0098 
0099     // JM 2012-12-05: Breaking into 2 loops instead of one previously with multiple IF checks for recordSize
0100     // While the CPU branch prediction might not suffer any penalties since the branch prediction after a few times
0101     // should always gets it right. It's better to do it this way to avoid any chances since the compiler might not optimize it.
0102     if (recordSize == 32)
0103     {
0104         for (Trixel i = 0; i < m_skyMesh->size(); ++i)
0105         {
0106             Trixel trixel   = i;
0107             quint64 records = starReader.getRecordCount(i);
0108             std::shared_ptr<StarBlock> SB(new StarBlock(records));
0109 
0110             if (!SB.get())
0111                 qCCritical(KSTARS) << "ERROR: Could not allocate new StarBlock to hold shallow unnamed stars for trixel "
0112                                    << trixel;
0113 
0114             m_starBlockList.at(trixel)->setStaticBlock(SB);
0115 
0116             for (quint64 j = 0; j < records; ++j)
0117             {
0118                 bool fread_success = fread(&stardata, sizeof(StarData), 1, dataFile);
0119 
0120                 if (!fread_success)
0121                 {
0122                     qCCritical(KSTARS) << "ERROR: Could not read StarData structure for star #" << j << " under trixel #"
0123                                        << trixel;
0124                 }
0125 
0126                 /* Swap Bytes when required */
0127                 if (starReader.getByteSwap())
0128                     byteSwap(&stardata);
0129 
0130                 /* Initialize star with data just read. */
0131                 StarObject *star;
0132 #ifdef KSTARS_LITE
0133                 star = &(SB->addStar(stardata)->star);
0134 #else
0135                 star = SB->addStar(stardata);
0136 #endif
0137                 if (star)
0138                 {
0139                     //KStarsData* data = KStarsData::Instance();
0140                     //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
0141                     //if( star->getHDIndex() != 0 )
0142                     if (stardata.HD)
0143                         m_CatalogNumber.insert(stardata.HD, star);
0144                 }
0145                 else
0146                 {
0147                     qCCritical(KSTARS) << "CODE ERROR: More unnamed static stars in trixel " << trixel
0148                                        << " than we allocated space for!";
0149                 }
0150             }
0151         }
0152     }
0153     else
0154     {
0155         for (Trixel i = 0; i < m_skyMesh->size(); ++i)
0156         {
0157             Trixel trixel   = i;
0158             quint64 records = starReader.getRecordCount(i);
0159             std::shared_ptr<StarBlock> SB(new StarBlock(records));
0160 
0161             if (!SB.get())
0162                 qCCritical(KSTARS) << "Could not allocate new StarBlock to hold shallow unnamed stars for trixel "
0163                                    << trixel;
0164 
0165             m_starBlockList.at(trixel)->setStaticBlock(SB);
0166 
0167             for (quint64 j = 0; j < records; ++j)
0168             {
0169                 bool fread_success = false;
0170                 fread_success      = fread(&deepstardata, sizeof(DeepStarData), 1, dataFile);
0171 
0172                 if (!fread_success)
0173                 {
0174                     qCCritical(KSTARS) << "Could not read StarData structure for star #" << j << " under trixel #"
0175                                        << trixel;
0176                 }
0177 
0178                 /* Swap Bytes when required */
0179                 if (starReader.getByteSwap())
0180                     byteSwap(&deepstardata);
0181 
0182                 /* Initialize star with data just read. */
0183                 StarObject *star;
0184 #ifdef KSTARS_LITE
0185                 star = &(SB->addStar(stardata)->star);
0186 #else
0187                 star = SB->addStar(deepstardata);
0188 #endif
0189                 if (star)
0190                 {
0191                     //KStarsData* data = KStarsData::Instance();
0192                     //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
0193                     //if( star->getHDIndex() != 0 )
0194                     if (stardata.HD)
0195                         m_CatalogNumber.insert(stardata.HD, star);
0196                 }
0197                 else
0198                 {
0199                     qCCritical(KSTARS) << "CODE ERROR: More unnamed static stars in trixel " << trixel
0200                                        << " than we allocated space for!";
0201                 }
0202             }
0203         }
0204     }
0205 
0206     return true;
0207 }
0208 
0209 bool DeepStarComponent::selected()
0210 {
0211     return Options::showStars() && fileOpened;
0212 }
0213 
0214 bool openIndexFile()
0215 {
0216     // TODO: Work out the details
0217     /*
0218     if( hdidxReader.openFile( "Henry-Draper.idx" ) )
0219         qDebug() << Q_FUNC_INFO << "Could not open HD Index file. Search by HD numbers for deep stars will not work.";
0220     */
0221     return 0;
0222 }
0223 
0224 //This function is empty for a reason; we override the normal
0225 //update function in favor of JiT updates for stars.
0226 void DeepStarComponent::update(KSNumbers *)
0227 {
0228 }
0229 
0230 // TODO: Optimize draw, if it is worth it.
0231 void DeepStarComponent::draw(SkyPainter *skyp)
0232 {
0233 #ifndef KSTARS_LITE
0234     if (!fileOpened)
0235         return;
0236 
0237 #ifdef PROFILE_SINCOS
0238     long trig_calls_here      = -dms::trig_function_calls;
0239     long trig_redundancy_here = -dms::redundant_trig_function_calls;
0240     long cachingdms_bad_uses  = -CachingDms::cachingdms_bad_uses;
0241     dms::seconds_in_trig      = 0.;
0242 #endif
0243 
0244 #ifdef PROFILE_UPDATECOORDS
0245     StarObject::updateCoordsCpuTime = 0.;
0246     StarObject::starsUpdated        = 0;
0247 #endif
0248     SkyMap *map       = SkyMap::Instance();
0249     KStarsData *data  = KStarsData::Instance();
0250     UpdateID updateID = data->updateID();
0251 
0252     //FIXME_FOV -- maybe not clamp like that...
0253     float radius = map->projector()->fov();
0254     if (radius > 90.0)
0255         radius = 90.0;
0256 
0257     if (m_skyMesh != SkyMesh::Instance() && m_skyMesh->inDraw())
0258     {
0259         printf("Warning: aborting concurrent DeepStarComponent::draw()");
0260     }
0261     bool checkSlewing = (map->isSlewing() && Options::hideOnSlew());
0262 
0263     //shortcuts to inform whether to draw different objects
0264     bool hideFaintStars(checkSlewing && Options::hideStars());
0265     double hideStarsMag = Options::magLimitHideStar();
0266 
0267     //adjust maglimit for ZoomLevel
0268     //    double lgmin = log10(MINZOOM);
0269     //    double lgmax = log10(MAXZOOM);
0270     //    double lgz = log10(Options::zoomFactor());
0271     // TODO: Enable hiding of faint stars
0272 
0273     float maglim = StarComponent::zoomMagnitudeLimit();
0274 
0275     if (maglim < triggerMag)
0276         return;
0277 
0278     m_zoomMagLimit = maglim;
0279 
0280     m_skyMesh->inDraw(true);
0281 
0282     SkyPoint *focus = map->focus();
0283     m_skyMesh->aperture(focus, radius + 1.0, DRAW_BUF); // divide by 2 for testing
0284 
0285     MeshIterator region(m_skyMesh, DRAW_BUF);
0286 
0287     // If we are to hide the fainter stars (eg: while slewing), we set the magnitude limit to hideStarsMag.
0288     if (hideFaintStars && maglim > hideStarsMag)
0289         maglim = hideStarsMag;
0290 
0291     StarBlockFactory *m_StarBlockFactory = StarBlockFactory::Instance();
0292     //    m_StarBlockFactory->drawID = m_skyMesh->drawID();
0293     //    qDebug() << Q_FUNC_INFO << "Mesh size = " << m_skyMesh->size() << "; drawID = " << m_skyMesh->drawID();
0294     QElapsedTimer t;
0295     int nTrixels = 0;
0296 
0297     t_dynamicLoad = 0;
0298     t_updateCache = 0;
0299     t_drawUnnamed = 0;
0300 
0301     visibleStarCount = 0;
0302 
0303     t.start();
0304 
0305     // Mark used blocks in the LRU Cache. Not required for static stars
0306     if (!staticStars)
0307     {
0308         while (region.hasNext())
0309         {
0310             Trixel currentRegion = region.next();
0311             for (int i = 0; i < m_starBlockList.at(currentRegion)->getBlockCount(); ++i)
0312             {
0313                 std::shared_ptr<StarBlock> prevBlock = ((i >= 1) ? m_starBlockList.at(currentRegion)->block(
0314                         i - 1) : std::shared_ptr<StarBlock>());
0315                 std::shared_ptr<StarBlock> block     = m_starBlockList.at(currentRegion)->block(i);
0316 
0317                 if (i == 0 && !m_StarBlockFactory->markFirst(block))
0318                     qCWarning(KSTARS) << "markFirst failed in trixel" << currentRegion;
0319                 if (i > 0 && !m_StarBlockFactory->markNext(prevBlock, block))
0320                     qCWarning(KSTARS) << "markNext failed in trixel" << currentRegion << "while marking block" << i;
0321                 if (i < m_starBlockList.at(currentRegion)->getBlockCount() &&
0322                         m_starBlockList.at(currentRegion)->block(i)->getFaintMag() < maglim)
0323                     break;
0324             }
0325         }
0326         t_updateCache = t.elapsed();
0327         region.reset();
0328     }
0329 
0330     while (region.hasNext())
0331     {
0332         ++nTrixels;
0333         Trixel currentRegion = region.next();
0334 
0335         // NOTE: We are guessing that the last 1.5/16 magnitudes in the catalog are just additions and the star catalog
0336         //       is actually supposed to reach out continuously enough only to mag m_FaintMagnitude * ( 1 - 1.5/16 )
0337         // TODO: Is there a better way? We may have to change the magnitude tolerance if the catalog changes
0338         // Static stars need not execute fillToMag
0339 
0340         // Safety check if the current region is in star block list
0341         if (currentRegion >= m_starBlockList.size())
0342             continue;
0343 
0344         if (!staticStars)
0345         {
0346             m_starBlockList.at(currentRegion)->fillToMag(maglim);
0347         }
0348 
0349         //        if (!staticStars && !m_starBlockList.at(currentRegion)->fillToMag(maglim) &&
0350         //            maglim <= m_FaintMagnitude * (1 - 1.5 / 16))
0351         //        {
0352         //            qCWarning(KSTARS) << "SBL::fillToMag( " << maglim << " ) failed for trixel " << currentRegion;
0353         //        }
0354 
0355         t_dynamicLoad += t.restart();
0356 
0357         //        qDebug() << Q_FUNC_INFO << "Drawing SBL for trixel " << currentRegion << ", SBL has "
0358         //                 <<  m_starBlockList[ currentRegion ]->getBlockCount() << " blocks";
0359 
0360         // REMARK: The following should never carry state, except for const parameters like updateID and maglim
0361         std::function<void(std::shared_ptr<StarBlock>)> mapFunction = [&updateID, &maglim](std::shared_ptr<StarBlock> myBlock)
0362         {
0363             for (StarObject &star : myBlock->contents())
0364             {
0365                 if (star.updateID != updateID)
0366                     star.JITupdate();
0367                 if (star.mag() > maglim)
0368                     break;
0369             }
0370         };
0371 
0372         QtConcurrent::blockingMap(m_starBlockList.at(currentRegion)->contents(), mapFunction);
0373 
0374         for (int i = 0; i < m_starBlockList.at(currentRegion)->getBlockCount(); ++i)
0375         {
0376             std::shared_ptr<StarBlock> block = m_starBlockList.at(currentRegion)->block(i);
0377             //            qDebug() << Q_FUNC_INFO << "---> Drawing stars from block " << i << " of trixel " <<
0378             //                currentRegion << ". SB has " << block->getStarCount() << " stars";
0379             for (int j = 0; j < block->getStarCount(); j++)
0380             {
0381                 StarObject *curStar = block->star(j);
0382 
0383                 //                qDebug() << Q_FUNC_INFO << "We claim that he's from trixel " << currentRegion
0384                 //<< ", and indexStar says he's from " << m_skyMesh->indexStar( curStar );
0385 
0386                 float mag = curStar->mag();
0387 
0388                 if (mag > maglim)
0389                     break;
0390 
0391                 if (skyp->drawPointSource(curStar, mag, curStar->spchar()))
0392                     visibleStarCount++;
0393             }
0394         }
0395 
0396         // DEBUG: Uncomment to identify problems with Star Block Factory / preservation of Magnitude Order in the LRU Cache
0397         //        verifySBLIntegrity();
0398         t_drawUnnamed += t.restart();
0399     }
0400     m_skyMesh->inDraw(false);
0401 #ifdef PROFILE_SINCOS
0402     trig_calls_here += dms::trig_function_calls;
0403     trig_redundancy_here += dms::redundant_trig_function_calls;
0404     cachingdms_bad_uses += CachingDms::cachingdms_bad_uses;
0405     qDebug() << Q_FUNC_INFO << "Spent " << dms::seconds_in_trig << " seconds doing " << trig_calls_here
0406              << " trigonometric function calls amounting to an average of "
0407              << 1000.0 * dms::seconds_in_trig / double(trig_calls_here) << " ms per call";
0408     qDebug() << Q_FUNC_INFO << "Redundancy of trig calls in this draw: "
0409              << double(trig_redundancy_here) / double(trig_calls_here) * 100. << "%";
0410     qDebug() << Q_FUNC_INFO << "CachedDms constructor calls so far: " << CachingDms::cachingdms_constructor_calls;
0411     qDebug() << Q_FUNC_INFO << "Caching has prevented " << CachingDms::cachingdms_delta << " redundant trig function calls";
0412     qDebug() << Q_FUNC_INFO << "Bad cache uses in this draw: " << cachingdms_bad_uses;
0413 #endif
0414 #ifdef PROFILE_UPDATECOORDS
0415     qDebug() << Q_FUNC_INFO << "Spent " << StarObject::updateCoordsCpuTime << " seconds updating " << StarObject::starsUpdated
0416              << " stars' coordinates (StarObject::updateCoords) for an average of "
0417              << double(StarObject::updateCoordsCpuTime) / double(StarObject::starsUpdated) * 1.e6 << " us per star.";
0418 #endif
0419 
0420 #else
0421     Q_UNUSED(skyp)
0422 #endif
0423 }
0424 
0425 bool DeepStarComponent::openDataFile()
0426 {
0427     if (starReader.getFileHandle())
0428         return true;
0429 
0430     starReader.openFile(dataFileName);
0431     fileOpened = false;
0432     if (!starReader.getFileHandle())
0433         qCWarning(KSTARS) << "Failed to open deep star catalog " << dataFileName << ". Disabling it.";
0434     else if (!starReader.readHeader())
0435         qCWarning(KSTARS) << "Header read error for deep star catalog " << dataFileName << "!! Disabling it!";
0436     else
0437     {
0438         qint16 faintmag;
0439         quint8 htm_level;
0440         int ret = 0;
0441 
0442         ret = fread(&faintmag, 2, 1, starReader.getFileHandle());
0443         if (starReader.getByteSwap())
0444             faintmag = bswap_16(faintmag);
0445         if (starReader.guessRecordSize() == 16)
0446             m_FaintMagnitude = faintmag / 1000.0;
0447         else
0448             m_FaintMagnitude = faintmag / 100.0;
0449         ret = fread(&htm_level, 1, 1, starReader.getFileHandle());
0450         qCInfo(KSTARS) << "Processing " << dataFileName << ", HTMesh Level" << htm_level;
0451         m_skyMesh = SkyMesh::Instance(htm_level);
0452         if (!m_skyMesh)
0453         {
0454             if (!(m_skyMesh = SkyMesh::Create(htm_level)))
0455             {
0456                 qCWarning(KSTARS) << "Could not create HTMesh of level " << htm_level << " for catalog " << dataFileName
0457                                   << ". Skipping it.";
0458                 return false;
0459             }
0460         }
0461         ret = fread(&MSpT, 2, 1, starReader.getFileHandle());
0462         if (starReader.getByteSwap())
0463             MSpT = bswap_16(MSpT);
0464         fileOpened = true;
0465         qCInfo(KSTARS) << "  Sky Mesh Size: " << m_skyMesh->size();
0466         for (long int i = 0; i < m_skyMesh->size(); i++)
0467         {
0468             std::shared_ptr<StarBlockList> sbl(new StarBlockList(i, this));
0469 
0470             if (!sbl.get())
0471             {
0472                 qCWarning(KSTARS) << "nullptr starBlockList. Expect trouble!";
0473             }
0474             m_starBlockList.append(sbl);
0475         }
0476         m_zoomMagLimit = 0.06;
0477     }
0478 
0479     return fileOpened;
0480 }
0481 
0482 StarObject *DeepStarComponent::findByHDIndex(int HDnum)
0483 {
0484     // Currently, we only handle HD catalog indexes
0485     return m_CatalogNumber.value(HDnum, nullptr); // TODO: Maybe, make this more general.
0486 }
0487 
0488 // This uses the main star index for looking up nearby stars but then
0489 // filters out objects with the generic name "star".  We could easily
0490 // build an index for just the named stars which would make this go
0491 // much faster still.  -jbb
0492 //
0493 
0494 SkyObject *DeepStarComponent::objectNearest(SkyPoint *p, double &maxrad)
0495 {
0496     StarObject *oBest = nullptr;
0497 
0498 #ifdef KSTARS_LITE
0499     m_zoomMagLimit = StarComponent::zoomMagnitudeLimit();
0500 #endif
0501     if (!fileOpened)
0502         return nullptr;
0503 
0504     m_skyMesh->index(p, maxrad + 1.0, OBJ_NEAREST_BUF);
0505 
0506     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
0507 
0508     while (region.hasNext())
0509     {
0510         Trixel currentRegion = region.next();
0511 
0512         // Safety check if the current region is in star block list
0513         if ((int)currentRegion >= m_starBlockList.size())
0514             continue;
0515 
0516         for (int i = 0; i < m_starBlockList.at(currentRegion)->getBlockCount(); ++i)
0517         {
0518             std::shared_ptr<StarBlock> block = m_starBlockList.at(currentRegion)->block(i);
0519             for (int j = 0; j < block->getStarCount(); ++j)
0520             {
0521 #ifdef KSTARS_LITE
0522                 StarObject *star = &(block->star(j)->star);
0523 #else
0524                 StarObject *star = block->star(j);
0525 #endif
0526                 if (!star)
0527                     continue;
0528                 if (star->mag() > m_zoomMagLimit)
0529                     continue;
0530 
0531                 double r = star->angularDistanceTo(p).Degrees();
0532                 if (r < maxrad)
0533                 {
0534                     oBest  = star;
0535                     maxrad = r;
0536                 }
0537             }
0538         }
0539     }
0540 
0541     // TODO: What if we are looking around a point that's not on
0542     // screen? objectNearest() will need to keep on filling up all
0543     // trixels around the SkyPoint to find the best match in case it
0544     // has not been found. Ideally, this should be implemented in a
0545     // different method and should be called after all other
0546     // candidates (eg: DeepSkyObject::objectNearest()) have been
0547     // called.
0548 
0549     return oBest;
0550 }
0551 
0552 bool DeepStarComponent::starsInAperture(QList<StarObject *> &list, const SkyPoint &center, float radius, float maglim)
0553 {
0554     if (maglim < triggerMag)
0555         return false;
0556 
0557     // For DeepStarComponents, whether we use ra0() and dec0(), or
0558     // ra() and dec(), should not matter, because the stars are
0559     // repeated in all trixels that they will pass through, although
0560     // the factuality of this statement needs to be verified
0561 
0562     // Ensure that we have deprecessed the (RA, Dec) to (RA0, Dec0)
0563     Q_ASSERT(center.ra0().Degrees() >= 0.0);
0564     Q_ASSERT(center.dec0().Degrees() <= 90.0);
0565 
0566     m_skyMesh->intersect(center.ra0().Degrees(), center.dec0().Degrees(), radius, (BufNum)OBJ_NEAREST_BUF);
0567 
0568     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
0569 
0570     if (maglim < -28)
0571         maglim = m_FaintMagnitude;
0572 
0573     while (region.hasNext())
0574     {
0575         Trixel currentRegion = region.next();
0576         // FIXME: Build a better way to iterate over all stars.
0577         // Ideally, StarBlockList should have such a facility.
0578         std::shared_ptr<StarBlockList> sbl = m_starBlockList[currentRegion];
0579         sbl->fillToMag(maglim);
0580         for (int i = 0; i < sbl->getBlockCount(); ++i)
0581         {
0582             std::shared_ptr<StarBlock> block = sbl->block(i);
0583             for (int j = 0; j < block->getStarCount(); ++j)
0584             {
0585 #ifdef KSTARS_LITE
0586                 StarObject *star = &(block->star(j)->star);
0587 #else
0588                 StarObject *star = block->star(j);
0589 #endif
0590                 if (star->mag() > maglim)
0591                     break; // Stars are organized by magnitude, so this should work
0592                 if (star->angularDistanceTo(&center).Degrees() <= radius)
0593                     list.append(star);
0594             }
0595         }
0596     }
0597 
0598     return true;
0599 }
0600 
0601 void DeepStarComponent::byteSwap(DeepStarData *stardata)
0602 {
0603     stardata->RA   = bswap_32(stardata->RA);
0604     stardata->Dec  = bswap_32(stardata->Dec);
0605     stardata->dRA  = bswap_16(stardata->dRA);
0606     stardata->dDec = bswap_16(stardata->dDec);
0607     stardata->B    = bswap_16(stardata->B);
0608     stardata->V    = bswap_16(stardata->V);
0609 }
0610 
0611 void DeepStarComponent::byteSwap(StarData *stardata)
0612 {
0613     stardata->RA       = bswap_32(stardata->RA);
0614     stardata->Dec      = bswap_32(stardata->Dec);
0615     stardata->dRA      = bswap_32(stardata->dRA);
0616     stardata->dDec     = bswap_32(stardata->dDec);
0617     stardata->parallax = bswap_32(stardata->parallax);
0618     stardata->HD       = bswap_32(stardata->HD);
0619     stardata->mag      = bswap_16(stardata->mag);
0620     stardata->bv_index = bswap_16(stardata->bv_index);
0621 }
0622 
0623 bool DeepStarComponent::verifySBLIntegrity()
0624 {
0625     float faintMag = -5.0;
0626     bool integrity = true;
0627 
0628     for (Trixel trixel = 0; trixel < m_skyMesh->size(); ++trixel)
0629     {
0630         for (int i = 0; i < m_starBlockList[trixel]->getBlockCount(); ++i)
0631         {
0632             std::shared_ptr<StarBlock> block = m_starBlockList[trixel]->block(i);
0633 
0634             if (i == 0)
0635                 faintMag = block->getBrightMag();
0636             // NOTE: Assumes 2 decimal places in magnitude field. TODO: Change if it ever does change
0637             if (block->getBrightMag() != faintMag && (block->getBrightMag() - faintMag) > 0.5)
0638             {
0639                 qCWarning(KSTARS) << "Trixel " << trixel << ": ERROR: faintMag of prev block = " << faintMag
0640                                   << ", brightMag of block #" << i << " = " << block->getBrightMag();
0641                 integrity = false;
0642             }
0643             if (i > 1 && (!block->prev))
0644                 qCWarning(KSTARS) << "Trixel " << trixel << ": ERROR: Block" << i << "is unlinked in LRU Cache";
0645             if (block->prev && block->prev->parent == m_starBlockList[trixel].get() &&
0646                     block->prev != m_starBlockList[trixel]->block(i - 1))
0647             {
0648                 qCWarning(KSTARS) << "Trixel " << trixel
0649                                   << ": ERROR: SBF LRU Cache linked list seems to be broken at before block " << i;
0650                 integrity = false;
0651             }
0652             faintMag = block->getFaintMag();
0653         }
0654     }
0655     return integrity;
0656 }