1 /*
2 SPDX-FileCopyrightText: 2005 Thomas Kabelmann <thomas.kabelmann@gmx.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "starcomponent.h"
8
9 #include "binfilehelper.h"
10 #include "deepstarcomponent.h"
11 #include "highpmstarlist.h"
12 #ifndef KSTARS_LITE
13 #include "kstars.h"
14 #endif
15 #include "kstarsdata.h"
16 #include "kstarssplash.h"
17 #include "Options.h"
18 #include "skylabeler.h"
19 #include "skymap.h"
20 #include "skymesh.h"
21 #ifndef KSTARS_LITE
22 #include "skyqpainter.h"
23 #endif
24 #include "htmesh/MeshIterator.h"
25 #include "projections/projector.h"
26
27 #include "kstars_debug.h"
28
29 #include <qplatformdefs.h>
30
31 #ifdef _WIN32
32 #include <windows.h>
33 #endif
34
35 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
36 #include <sys/endian.h>
37 #define bswap_16(x) bswap16(x)
38 #define bswap_32(x) bswap32(x)
39 #else
40 #include "byteorder.h"
41 #endif
42
43 StarComponent *StarComponent::pinstance = nullptr;
44
StarComponent(SkyComposite * parent)45 StarComponent::StarComponent(SkyComposite *parent)
46 : ListComponent(parent), m_reindexNum(J2000)
47 {
48 m_skyMesh = SkyMesh::Instance();
49 m_StarBlockFactory = StarBlockFactory::Instance();
50
51 m_starIndex.reset(new StarIndex());
52 for (int i = 0; i < m_skyMesh->size(); i++)
53 m_starIndex->append(new StarList());
54 m_highPMStars.append(new HighPMStarList(840.0));
55 m_highPMStars.append(new HighPMStarList(304.0));
56 m_reindexInterval = StarObject::reindexInterval(304.0);
57
58 for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
59 m_labelList[i] = new LabelList;
60
61 // Actually load data
62 emitProgressText(i18n("Loading stars"));
63
64 loadStaticData();
65 // Load any deep star catalogs that are available
66 loadDeepStarCatalogs();
67
68 // The following works but can cause crashes sometimes
69 //QtConcurrent::run(this, &StarComponent::loadDeepStarCatalogs);
70
71 //In KStars Lite star images are initialized in SkyMapLite
72 #ifndef KSTARS_LITE
73 SkyQPainter::initStarImages();
74 #endif
75 }
76
~StarComponent()77 StarComponent::~StarComponent()
78 {
79 qDeleteAll(*m_starIndex);
80 m_starIndex->clear();
81 qDeleteAll(m_DeepStarComponents);
82 m_DeepStarComponents.clear();
83 qDeleteAll(m_highPMStars);
84 m_highPMStars.clear();
85 for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
86 delete m_labelList[i];
87 }
88
Create(SkyComposite * parent)89 StarComponent *StarComponent::Create(SkyComposite *parent)
90 {
91 delete pinstance;
92 pinstance = new StarComponent(parent);
93 return pinstance;
94 }
95
selected()96 bool StarComponent::selected()
97 {
98 return Options::showStars();
99 }
100
addDeepStarCatalogIfExists(const QString & fileName,float trigMag,bool staticstars)101 bool StarComponent::addDeepStarCatalogIfExists(const QString &fileName, float trigMag, bool staticstars)
102 {
103 if (BinFileHelper::testFileExists(fileName))
104 {
105 m_DeepStarComponents.append(new DeepStarComponent(parent(), fileName, trigMag, staticstars));
106 return true;
107 }
108 return false;
109 }
110
loadDeepStarCatalogs()111 int StarComponent::loadDeepStarCatalogs()
112 {
113 // Look for the basic unnamed star catalog to mag 8.0
114 if (!addDeepStarCatalogIfExists("unnamedstars.dat", -5.0, true))
115 return 0;
116
117 // Look for the Tycho-2 add-on with 2.5 million stars to mag 12.5
118 if (!addDeepStarCatalogIfExists("tycho2.dat", 8.0) && !addDeepStarCatalogIfExists("deepstars.dat", 8.0))
119 return 1;
120
121 // Look for the USNO NOMAD 1e8 star catalog add-on with stars to mag 16
122 if (!addDeepStarCatalogIfExists("USNO-NOMAD-1e8.dat", 11.0))
123 return 2;
124
125 return 3;
126 }
127
128 //This function is empty for a reason; we override the normal
129 //update function in favor of JiT updates for stars.
update(KSNumbers *)130 void StarComponent::update(KSNumbers *)
131 {
132 }
133
134 // We use the update hook to re-index all the stars when the date has changed by
135 // more than 150 years.
136
reindex(KSNumbers * num)137 bool StarComponent::reindex(KSNumbers *num)
138 {
139 if (!num)
140 return false;
141
142 // for large time steps we re-index all points
143 if (fabs(num->julianCenturies() - m_reindexNum.julianCenturies()) > m_reindexInterval)
144 {
145 reindexAll(num);
146 return true;
147 }
148
149 bool highPM = true;
150
151 // otherwise we just re-index fast movers as needed
152 for (auto &star : m_highPMStars)
153 highPM &= !(star->reindex(num, m_starIndex.get()));
154
155 return !(highPM);
156 }
157
reindexAll(KSNumbers * num)158 void StarComponent::reindexAll(KSNumbers *num)
159 {
160 #if 0
161 if (0 && !m_reindexSplash)
162 {
163 m_reindexSplash = new KStarsSplash(i18n("Please wait while re-indexing stars..."));
164 QObject::connect(KStarsData::Instance(), SIGNAL(progressText(QString)), m_reindexSplash,
165 SLOT(setMessage(QString)));
166
167 m_reindexSplash->show();
168 m_reindexSplash->raise();
169 return;
170 }
171 #endif
172
173 qCInfo(KSTARS) << "Re-indexing Stars to year" << 2000.0 + num->julianCenturies() * 100.0;
174
175 m_reindexNum = KSNumbers(*num);
176 m_skyMesh->setKSNumbers(num);
177
178 // clear out the old index
179 for (auto &item : *m_starIndex)
180 {
181 item->clear();
182 }
183
184 // re-populate it from the objectList
185 for (auto &object : m_ObjectList)
186 {
187 StarObject *star = dynamic_cast<StarObject *>(object);
188 Trixel trixel = m_skyMesh->indexStar(star);
189
190 m_starIndex->at(trixel)->append(star);
191 }
192
193 // Let everyone else know we have re-indexed to num
194 for (auto &star : m_highPMStars)
195 {
196 star->setIndexTime(num);
197 }
198
199 //delete m_reindexSplash;
200 //m_reindexSplash = 0;
201 }
202
faintMagnitude() const203 float StarComponent::faintMagnitude() const
204 {
205 float faintmag = m_FaintMagnitude;
206
207 for (auto &component : m_DeepStarComponents)
208 {
209 if (faintmag < component->faintMagnitude())
210 faintmag = component->faintMagnitude();
211 }
212 return faintmag;
213 }
214
zoomMagnitudeLimit()215 float StarComponent::zoomMagnitudeLimit()
216 {
217 //adjust maglimit for ZoomLevel
218 double lgmin = log10(MINZOOM);
219 double lgz = log10(Options::zoomFactor());
220
221 // Old formula:
222 // float maglim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + Options::magLimitDrawStarZoomOut();
223
224 /*
225 Explanation for the following formula:
226 --------------------------------------
227 Estimates from a sample of 125000 stars shows that, magnitude
228 limit vs. number of stars follows the formula:
229 nStars = 10^(.45 * maglim + .95)
230 (A better formula is available here: https://www.aa.quae.nl/en/antwoorden/magnituden.html
231 which we do not implement for simplicity)
232 We want to keep the star density on screen a constant. This is directly proportional to the number of stars
233 and directly proportional to the area on screen. The area is in turn inversely proportional to the square
234 of the zoom factor ( zoomFactor / MINZOOM ). This means that (taking logarithms):
235 0.45 * maglim + 0.95 - 2 * log( ZoomFactor ) - log( Star Density ) - log( Some proportionality constant )
236 hence the formula. We've gathered together all the constants and set it to 3.5, so as to set the minimum
237 possible value of maglim to 3.5
238 */
239
240 // float maglim = 4.444 * ( lgz - lgmin ) + 2.222 * log10( Options::starDensity() ) + 3.5;
241
242 // Reducing the slope w.r.t zoom factor to avoid the extremely fast increase in star density with zoom
243 // that 4.444 gives us (although that is what the derivation gives us)
244
245 return 3.5 + 3.7 * (lgz - lgmin) + 2.222 * log10(static_cast<float>(Options::starDensity()));
246 }
247
draw(SkyPainter * skyp)248 void StarComponent::draw(SkyPainter *skyp)
249 {
250 #ifndef KSTARS_LITE
251 if (!selected())
252 return;
253
254 SkyMap *map = SkyMap::Instance();
255 const Projector *proj = map->projector();
256 KStarsData *data = KStarsData::Instance();
257 UpdateID updateID = data->updateID();
258
259 bool checkSlewing = (map->isSlewing() && Options::hideOnSlew());
260 m_hideLabels = checkSlewing || !(Options::showStarMagnitudes() || Options::showStarNames());
261
262 //shortcuts to inform whether to draw different objects
263 bool hideFaintStars = checkSlewing && Options::hideStars();
264 double hideStarsMag = Options::magLimitHideStar();
265 reindex(data->updateNum());
266
267 double lgmin = log10(MINZOOM);
268 double lgmax = log10(MAXZOOM);
269 double lgz = log10(Options::zoomFactor());
270
271 double maglim;
272 m_zoomMagLimit = maglim = zoomMagnitudeLimit();
273
274 double labelMagLim = Options::starLabelDensity() / 5.0;
275 labelMagLim += (12.0 - labelMagLim) * (lgz - lgmin) / (lgmax - lgmin);
276 if (labelMagLim > 8.0)
277 labelMagLim = 8.0;
278
279 //Calculate sizeMagLim
280 // Old formula:
281 // float sizeMagLim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + 5.8;
282
283 // Using the maglim to compute the sizes of stars reduces
284 // discernability between brighter and fainter stars at high zoom
285 // levels. To fix that, we use an "arbitrary" constant in place of
286 // the variable star density.
287 // Not using this formula now.
288 // float sizeMagLim = 4.444 * ( lgz - lgmin ) + 5.0;
289
290 float sizeMagLim = zoomMagnitudeLimit();
291 if (sizeMagLim > faintMagnitude() * (1 - 1.5 / 16))
292 sizeMagLim = faintMagnitude() * (1 - 1.5 / 16);
293 skyp->setSizeMagLimit(sizeMagLim);
294
295 //Loop for drawing star images
296
297 MeshIterator region(m_skyMesh, DRAW_BUF);
298 magLim = maglim;
299
300 // If we are hiding faint stars, then maglim is really the brighter of hideStarsMag and maglim
301 if (hideFaintStars && maglim > hideStarsMag)
302 maglim = hideStarsMag;
303
304 m_StarBlockFactory->drawID = m_skyMesh->drawID();
305
306 int nTrixels = 0;
307
308 while (region.hasNext())
309 {
310 ++nTrixels;
311 Trixel currentRegion = region.next();
312 StarList *starList = m_starIndex->at(currentRegion);
313
314 for (auto &star : *starList)
315 {
316 if (!star)
317 continue;
318
319 float mag = star->mag();
320
321 // break loop if maglim is reached
322 if (mag > maglim)
323 break;
324
325 if (star->updateID != updateID)
326 star->JITupdate();
327
328 bool drawn = skyp->drawPointSource(star, mag, star->spchar());
329
330 //FIXME_SKYPAINTER: find a better way to do this.
331 if (drawn && !(m_hideLabels || mag > labelMagLim))
332 addLabel(proj->toScreen(star), star);
333 }
334 }
335
336 // Draw focusStar if not null
337 if (focusStar)
338 {
339 if (focusStar->updateID != updateID)
340 focusStar->JITupdate();
341 float mag = focusStar->mag();
342 skyp->drawPointSource(focusStar, mag, focusStar->spchar());
343 }
344
345 // Now draw each of our DeepStarComponents
346 for (auto &component : m_DeepStarComponents)
347 {
348 component->draw(skyp);
349 }
350 #else
351 Q_UNUSED(skyp)
352 #endif
353 }
354
addLabel(const QPointF & p,StarObject * star)355 void StarComponent::addLabel(const QPointF &p, StarObject *star)
356 {
357 int idx = int(star->mag() * 10.0);
358 if (idx < 0)
359 idx = 0;
360 if (idx > MAX_LINENUMBER_MAG)
361 idx = MAX_LINENUMBER_MAG;
362 m_labelList[idx]->append(SkyLabel(p, star));
363 }
364
drawLabels()365 void StarComponent::drawLabels()
366 {
367 if (m_hideLabels)
368 return;
369
370 SkyLabeler *labeler = SkyLabeler::Instance();
371 labeler->setPen(QColor(KStarsData::Instance()->colorScheme()->colorNamed("SNameColor")));
372
373 int max = int(m_zoomMagLimit * 10.0);
374 if (max < 0)
375 max = 0;
376 if (max > MAX_LINENUMBER_MAG)
377 max = MAX_LINENUMBER_MAG;
378
379 for (int i = 0; i <= max; i++)
380 {
381 LabelList *list = m_labelList[i];
382
383 for (const auto &item : *list)
384 {
385 labeler->drawNameLabel(item.obj, item.o);
386 }
387 list->clear();
388 }
389 }
390
loadStaticData()391 bool StarComponent::loadStaticData()
392 {
393 // We break from Qt / KDE API and use traditional file handling here, to obtain speed.
394 // We also avoid C++ constructors for the same reason.
395 FILE *dataFile, *nameFile;
396 bool swapBytes = false, named = false, gnamed = false;
397 BinFileHelper dataReader, nameReader;
398 QString name, gname, visibleName;
399 StarObject *star;
400
401 if (starsLoaded)
402 return true;
403
404 // prepare to index stars to this date
405 m_skyMesh->setKSNumbers(&m_reindexNum);
406
407 /* Open the data files */
408 // TODO: Maybe we don't want to hardcode the filename?
409 if ((dataFile = dataReader.openFile("namedstars.dat")) == nullptr)
410 {
411 qCWarning(KSTARS) << "Could not open data file namedstars.dat";
412 return false;
413 }
414
415 if (!(nameFile = nameReader.openFile("starnames.dat")))
416 {
417 qCWarning(KSTARS) << "Could not open data file starnames.dat";
418 return false;
419 }
420
421 if (!dataReader.readHeader())
422 {
423 qCWarning(KSTARS) << "Error reading namedstars.dat header : " << dataReader.getErrorNumber() << " : "
424 << dataReader.getError();
425 return false;
426 }
427
428 if (!nameReader.readHeader())
429 {
430 qCWarning(KSTARS) << "Error reading starnames.dat header : " << nameReader.getErrorNumber() << " : "
431 << nameReader.getError();
432 return false;
433 }
434 //KDE_fseek(nameFile, nameReader.getDataOffset(), SEEK_SET);
435 QT_FSEEK(nameFile, nameReader.getDataOffset(), SEEK_SET);
436 swapBytes = dataReader.getByteSwap();
437
438 long int nstars = 0;
439
440 //KDE_fseek(dataFile, dataReader.getDataOffset(), SEEK_SET);
441 QT_FSEEK(dataFile, dataReader.getDataOffset(), SEEK_SET);
442
443 qint16 faintmag;
444 quint8 htm_level;
445 quint16 t_MSpT;
446 int ret = 0;
447
448 // Faint Magnitude
449 ret = fread(&faintmag, 2, 1, dataFile);
450 if (swapBytes)
451 faintmag = bswap_16(faintmag);
452
453 // HTM Level
454 ret = fread(&htm_level, 1, 1, dataFile);
455
456 // Unused
457 {
458 int rc = fread(&t_MSpT, 2, 1, dataFile);
459 Q_UNUSED(rc)
460 }
461
462 if (faintmag / 100.0 > m_FaintMagnitude)
463 m_FaintMagnitude = faintmag / 100.0;
464
465 if (htm_level != m_skyMesh->level())
466 qCWarning(KSTARS)
467 << "HTM Level in shallow star data file and HTM Level in m_skyMesh do not match. EXPECT TROUBLE"
468 ;
469 for (int i = 0; i < m_skyMesh->size(); ++i)
470 {
471 Trixel trixel = i; // = ( ( i >= 256 ) ? ( i - 256 ) : ( i + 256 ) );
472 for (unsigned long j = 0; j < static_cast<unsigned long>(dataReader.getRecordCount(i)); ++j)
473 {
474 if (1 != fread(&stardata, sizeof(StarData), 1, dataFile))
475 {
476 qCCritical(KSTARS) << "FILE FORMAT ERROR: Could not read StarData structure for star #" << j << " under trixel #"
477 << trixel;
478 continue;
479 }
480
481 /* Swap Bytes when required */
482 if (swapBytes)
483 byteSwap(&stardata);
484
485 named = false;
486 gnamed = false;
487
488 /* Named Star - Read the nameFile */
489 if (stardata.flags & 0x01)
490 {
491 visibleName = "";
492 if (1 != fread(&starname, sizeof(starName), 1, nameFile))
493 qCCritical(KSTARS) << "ERROR: fread() call on nameFile failed in trixel " << trixel << " star " << j;
494
495 name = QByteArray(starname.longName, 32);
496 named = !name.isEmpty();
497
498 gname = QByteArray(starname.bayerName, 8);
499 gnamed = !gname.isEmpty();
500
501 if (gnamed && starname.bayerName[0] != '.')
502 visibleName = gname;
503
504 if (named)
505 {
506 // HEV: look up star name in internationalization filesource
507 name = i18nc("star name", name.toLocal8Bit().data());
508 }
509 else
510 {
511 name = i18n("star");
512 }
513 }
514 else
515 qCCritical(KSTARS) << "ERROR: Named star file contains unnamed stars! Expect trouble.";
516
517 /* Create the new StarObject */
518 star = new StarObject;
519 star->init(&stardata);
520 //if( star->getHDIndex() != 0 && name == i18n("star"))
521 if (stardata.HD)
522 {
523 m_HDHash.insert(stardata.HD, star);
524 if (named == false)
525 {
526 name = QString("HD %1").arg(stardata.HD);
527 named = true;
528 }
529 }
530
531 star->setNames(name, visibleName);
532 //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
533 ++nstars;
534
535 if (gnamed)
536 m_genName.insert(gname, star);
537
538 //if ( ! name.isEmpty() && name != i18n("star"))
539 if (named)
540 {
541 objectNames(SkyObject::STAR).append(name);
542 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(name, star));
543 }
544
545 if (!visibleName.isEmpty() && gname != name)
546 {
547 QString gName = star->gname(false);
548 objectNames(SkyObject::STAR).append(gName);
549 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(gName, star));
550 }
551
552 appendListObject(star);
553
554 m_starIndex->at(trixel)->append(star);
555 double pm = star->pmMagnitude();
556
557 for (auto &list : m_highPMStars)
558 {
559 if (list->append(trixel, star, pm))
560 break;
561 }
562 }
563 }
564
565 dataReader.closeFile();
566 nameReader.closeFile();
567
568 starsLoaded = true;
569 return true;
570 }
571
appendListObject(SkyObject * object)572 void StarComponent::appendListObject(SkyObject *object)
573 {
574 m_ObjectList.append(object);
575 m_ObjectHash.insert(object->name().toLower(), object);
576 m_ObjectHash.insert(object->longname().toLower(), object);
577 m_ObjectHash.insert(object->name2().toLower(), object);
578 m_ObjectHash.insert(object->name2().toLower(), object);
579 m_ObjectHash.insert((dynamic_cast<StarObject *>(object))->gname(false).toLower(), object);
580 }
581
findStarByGenetiveName(const QString name)582 SkyObject *StarComponent::findStarByGenetiveName(const QString name)
583 {
584 if (name.startsWith(QLatin1String("HD")))
585 {
586 QStringList fields = name.split(' ', QString::SkipEmptyParts);
587 bool Ok = false;
588 unsigned int HDNum = fields[1].toInt(&Ok);
589 if (Ok)
590 return findByHDIndex(HDNum);
591 }
592 return m_genName.value(name);
593 }
594
objectsInArea(QList<SkyObject * > & list,const SkyRegion & region)595 void StarComponent::objectsInArea(QList<SkyObject *> &list, const SkyRegion ®ion)
596 {
597 for (SkyRegion::const_iterator it = region.constBegin(); it != region.constEnd(); ++it)
598 {
599 Trixel trixel = it.key();
600 StarList *starlist = m_starIndex->at(trixel);
601 for (int i = 0; starlist && i < starlist->size(); i++)
602 if (starlist->at(i) && starlist->at(i)->name() != QString("star"))
603 list.push_back(starlist->at(i));
604 }
605 }
606
findByHDIndex(int HDnum)607 StarObject *StarComponent::findByHDIndex(int HDnum)
608 {
609 KStarsData *data = KStarsData::Instance();
610 StarObject *o = nullptr;
611 BinFileHelper hdidxReader;
612
613 // First check the hash to see if we have a corresponding StarObject already
614 if ((o = m_HDHash.value(HDnum, nullptr)))
615 return o;
616 // If we don't have the StarObject here, try it in the DeepStarComponents' hashes
617 if (m_DeepStarComponents.size() >= 1)
618 if ((o = m_DeepStarComponents.at(0)->findByHDIndex(HDnum)))
619 return o;
620 if (m_DeepStarComponents.size() >= 2)
621 {
622 qint32 offset = 0;
623 int ret = 0;
624 FILE *hdidxFile = hdidxReader.openFile("Henry-Draper.idx");
625 FILE *dataFile = nullptr;
626
627 if (!hdidxFile)
628 return nullptr;
629 //KDE_fseek( hdidxFile, (HDnum - 1) * 4, SEEK_SET );
630 QT_FSEEK(hdidxFile, (HDnum - 1) * 4, SEEK_SET);
631 // TODO: Offsets need to be byteswapped if this is a big endian machine.
632 // This means that the Henry Draper Index needs a endianness indicator.
633 ret = fread(&offset, 4, 1, hdidxFile);
634 if (offset <= 0)
635 return nullptr;
636 dataFile = m_DeepStarComponents.at(1)->getStarReader()->getFileHandle();
637 //KDE_fseek( dataFile, offset, SEEK_SET );
638 QT_FSEEK(dataFile, offset, SEEK_SET);
639 {
640 int rc = fread(&stardata, sizeof(StarData), 1, dataFile);
641 Q_UNUSED(rc)
642 }
643 if (m_DeepStarComponents.at(1)->getStarReader()->getByteSwap())
644 {
645 byteSwap(&stardata);
646 }
647 m_starObject.init(&stardata);
648 m_starObject.EquatorialToHorizontal(data->lst(), data->geo()->lat());
649 m_starObject.JITupdate();
650 focusStar = m_starObject.clone();
651 m_HDHash.insert(HDnum, focusStar);
652 hdidxReader.closeFile();
653 return focusStar;
654 }
655
656 return nullptr;
657 }
658
659 // This uses the main star index for looking up nearby stars but then
660 // filters out objects with the generic name "star". We could easily
661 // build an index for just the named stars which would make this go
662 // much faster still. -jbb
663 //
objectNearest(SkyPoint * p,double & maxrad)664 SkyObject *StarComponent::objectNearest(SkyPoint *p, double &maxrad)
665 {
666 m_zoomMagLimit = zoomMagnitudeLimit();
667
668 SkyObject *oBest = nullptr;
669
670 MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
671
672 while (region.hasNext())
673 {
674 Trixel currentRegion = region.next();
675 StarList *starList = m_starIndex->at(currentRegion);
676
677 for (auto &star : *starList)
678 {
679 if (!star)
680 continue;
681 if (star->mag() > m_zoomMagLimit)
682 continue;
683
684 double r = star->angularDistanceTo(p).Degrees();
685
686 if (r < maxrad)
687 {
688 oBest = star;
689 maxrad = r;
690 }
691 }
692 }
693
694 // Check up with our Deep Star Components too!
695 double rTry, rBest;
696 SkyObject *oTry;
697 // JM 2016-03-30: Multiply rBest by a factor of 0.5 so that named stars are preferred to unnamed stars searched below
698 rBest = maxrad * 0.5;
699 rTry = maxrad;
700 for (auto &component : m_DeepStarComponents)
701 {
702 oTry = component->objectNearest(p, rTry);
703 if (rTry < rBest)
704 {
705 rBest = rTry;
706 oBest = oTry;
707 }
708 }
709 maxrad = rBest;
710
711 return oBest;
712 }
713
starsInAperture(QList<StarObject * > & list,const SkyPoint & center,float radius,float maglim)714 void StarComponent::starsInAperture(QList<StarObject *> &list, const SkyPoint ¢er, float radius, float maglim)
715 {
716 // Ensure that we have deprecessed the (RA, Dec) to (RA0, Dec0)
717 Q_ASSERT(center.ra0().Degrees() >= 0.0);
718 Q_ASSERT(center.dec0().Degrees() <= 90.0);
719
720 m_skyMesh->intersect(center.ra0().Degrees(), center.dec0().Degrees(), radius, static_cast<BufNum>(OBJ_NEAREST_BUF));
721
722 MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
723
724 if (maglim < -28)
725 maglim = m_FaintMagnitude;
726
727 while (region.hasNext())
728 {
729 Trixel currentRegion = region.next();
730 StarList *starList = m_starIndex->at(currentRegion);
731
732 for (auto &star : *starList)
733 {
734 if (!star)
735 continue;
736 if (star->mag() > m_FaintMagnitude)
737 continue;
738 if (star->angularDistanceTo(¢er).Degrees() <= radius)
739 list.append(star);
740 }
741 }
742
743 // Add stars from the DeepStarComponents as well
744 for (auto &component : m_DeepStarComponents)
745 {
746 component->starsInAperture(list, center, radius, maglim);
747 }
748 }
749
byteSwap(StarData * stardata)750 void StarComponent::byteSwap(StarData *stardata)
751 {
752 stardata->RA = bswap_32(stardata->RA);
753 stardata->Dec = bswap_32(stardata->Dec);
754 stardata->dRA = bswap_32(stardata->dRA);
755 stardata->dDec = bswap_32(stardata->dDec);
756 stardata->parallax = bswap_32(stardata->parallax);
757 stardata->HD = bswap_32(stardata->HD);
758 stardata->mag = bswap_16(stardata->mag);
759 stardata->bv_index = bswap_16(stardata->bv_index);
760 }
761 /*
762 void StarComponent::printDebugInfo() {
763
764 int nTrixels = 0;
765 int nBlocks = 0;
766 long int nStars = 0;
767 float faintMag = -5.0;
768
769 MeshIterator trixels( m_skyMesh, DRAW_BUF );
770 Trixel trixel;
771
772 while( trixels.hasNext() ) {
773 trixel = trixels.next();
774 nTrixels++;
775 for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
776 nBlocks++;
777 StarBlock *block = m_starBlockList[ trixel ]->block( i );
778 for(int j = 0; j < block->getStarCount(); ++j) {
779 nStars++;
780 }
781 if( block->getFaintMag() > faintMag ) {
782 faintMag = block->getFaintMag();
783 }
784 }
785 }
786
787 printf( "========== UNNAMED STAR MEMORY ALLOCATION INFORMATION ==========\n" );
788 printf( "Number of visible trixels = %8d\n", nTrixels );
789 printf( "Number of visible StarBlocks = %8d\n", nBlocks );
790 printf( "Number of StarBlocks allocated via SBF = %8d\n", m_StarBlockFactory.getBlockCount() );
791 printf( "Number of unnamed stars in memory = %8ld\n", nStars );
792 printf( "Magnitude of the faintest star in memory = %8.2f\n", faintMag );
793 printf( "Target magnitude limit = %8.2f\n", magLim );
794 printf( "Size of each StarBlock = %8d bytes\n", sizeof( StarBlock ) );
795 printf( "Size of each StarObject = %8d bytes\n", sizeof( StarObject ) );
796 printf( "Memory use due to visible unnamed stars = %8.2f MB\n", ( sizeof( StarObject ) * nStars / 1048576.0 ) );
797 printf( "Memory use due to visible StarBlocks = %8d bytes\n", sizeof( StarBlock ) * nBlocks );
798 printf( "Memory use due to StarBlocks in SBF = %8d bytes\n", sizeof( StarBlock ) * m_StarBlockFactory.getBlockCount() );
799 printf( "=============== STAR DRAW LOOP TIMING INFORMATION ==============\n" );
800 printf( "Time taken for drawing named stars = %8ld ms\n", t_drawNamed );
801 printf( "Time taken for dynamic load of data = %8ld ms\n", t_dynamicLoad );
802 printf( "Time taken for updating LRU cache = %8ld ms\n", t_updateCache );
803 printf( "Time taken for drawing unnamed stars = %8ld ms\n", t_drawUnnamed );
804 printf( "================================================================\n" );
805 }
806
807 bool StarComponent::verifySBLIntegrity() {
808
809 float faintMag = -5.0;
810 bool integrity = true;
811 for(Trixel trixel = 0; trixel < m_skyMesh->size(); ++trixel) {
812 for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
813 StarBlock *block = m_starBlockList[ trixel ]->block( i );
814 if( i == 0 )
815 faintMag = block->getBrightMag();
816 // NOTE: Assumes 2 decimal places in magnitude field. TODO: Change if it ever does change
817 if( block->getBrightMag() != faintMag && ( block->getBrightMag() - faintMag ) > 0.016) {
818 qDebug() << "Trixel " << trixel << ": ERROR: faintMag of prev block = " << faintMag
819 << ", brightMag of block #" << i << " = " << block->getBrightMag();
820 integrity = false;
821 }
822 if( i > 1 && ( !block->prev ) )
823 qDebug() << "Trixel " << trixel << ": ERROR: Block" << i << "is unlinked in LRU Cache";
824 if( block->prev && block->prev->parent == m_starBlockList[ trixel ]
825 && block->prev != m_starBlockList[ trixel ]->block( i - 1 ) ) {
826 qDebug() << "Trixel " << trixel << ": ERROR: SBF LRU Cache linked list seems to be broken at before block " << i;
827 integrity = false;
828 }
829 faintMag = block->getFaintMag();
830 }
831 }
832 return integrity;
833 }
834 */
835