File indexing completed on 2024-04-28 03:43:41

0001 /*
0002     SPDX-FileCopyrightText: 2022 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "framingassistant.h"
0008 #include "ui_framingassistant.h"
0009 #include "mosaictilesmanager.h"
0010 #include "kstars.h"
0011 #include "Options.h"
0012 #include "scheduler.h"
0013 #include "skymap.h"
0014 #include "ekos/manager.h"
0015 #include "projections/projector.h"
0016 
0017 #include "ekos_scheduler_debug.h"
0018 
0019 namespace Ekos
0020 {
0021 
0022 FramingAssistant* FramingAssistant::Instance()
0023 {
0024     if (_FramingAssistant == nullptr)
0025     {
0026         _FramingAssistant = new FramingAssistant;
0027     }
0028 
0029     return _FramingAssistant;
0030 }
0031 
0032 FramingAssistant::FramingAssistant(): QDialog(KStars::Instance()), ui(new Ui::FramingAssistant())
0033 {
0034     ui->setupUi(this);
0035 
0036     // Initial optics information is taken from Ekos options
0037     ui->focalLenSpin->setValue(Options::telescopeFocalLength());
0038     ui->pixelWSizeSpin->setValue(Options::cameraPixelWidth());
0039     ui->pixelHSizeSpin->setValue(Options::cameraPixelHeight());
0040     ui->cameraWSpin->setValue(Options::cameraWidth());
0041     ui->cameraHSpin->setValue(Options::cameraHeight());
0042     ui->rotationSpin->setValue(Options::cameraRotation());
0043 
0044     // Initial job location is the home path appended with the target name
0045     //ui->jobsDir->setText(QDir::cleanPath(QDir::homePath() + QDir::separator() + targetName.replace(' ', '_')));
0046     //ui->selectJobsDirB->setIcon(QIcon::fromTheme("document-open-folder"));
0047 
0048     // The update timer avoids stacking updates which crash the sky map renderer
0049     updateTimer = new QTimer(this);
0050     updateTimer->setSingleShot(true);
0051     updateTimer->setInterval(1000);
0052     connect(updateTimer, &QTimer::timeout, this, &Ekos::FramingAssistant::constructMosaic);
0053 
0054     // Scope optics information
0055     // - Changing the optics configuration changes the FOV, which changes the target field dimensions
0056     connect(ui->focalLenSpin, &QDoubleSpinBox::editingFinished, this, &Ekos::FramingAssistant::calculateFOV);
0057     connect(ui->cameraWSpin, &QSpinBox::editingFinished, this, &Ekos::FramingAssistant::calculateFOV);
0058     connect(ui->cameraHSpin, &QSpinBox::editingFinished, this, &Ekos::FramingAssistant::calculateFOV);
0059     connect(ui->pixelWSizeSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0060             &Ekos::FramingAssistant::calculateFOV);
0061     connect(ui->pixelHSizeSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0062             &Ekos::FramingAssistant::calculateFOV);
0063     connect(ui->rotationSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0064             &Ekos::FramingAssistant::calculateFOV);
0065 
0066     // Mosaic configuration
0067     // - Changing the target field dimensions changes the grid dimensions
0068     // - Changing the overlap field changes the grid dimensions (more intuitive than changing the field)
0069     // - Changing the grid dimensions changes the target field dimensions
0070     connect(ui->targetHFOVSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0071             &Ekos::FramingAssistant::updateGridFromTargetFOV);
0072     connect(ui->targetWFOVSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0073             &Ekos::FramingAssistant::updateGridFromTargetFOV);
0074     connect(ui->overlapSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
0075             &Ekos::FramingAssistant::updateGridFromTargetFOV);
0076     connect(ui->mosaicWSpin, QOverload<int>::of(&QSpinBox::valueChanged), this,
0077             &Ekos::FramingAssistant::updateTargetFOVFromGrid);
0078     connect(ui->mosaicHSpin, QOverload<int>::of(&QSpinBox::valueChanged), this,
0079             &Ekos::FramingAssistant::updateTargetFOVFromGrid);
0080 
0081     // Lazy update for s-shape
0082     connect(ui->reverseOddRows, &QCheckBox::toggled, this, [&]()
0083     {
0084         renderedHFOV = 0;
0085         updateTimer->start();
0086     });
0087 
0088     // Buttons
0089     connect(ui->resetB, &QPushButton::clicked, this, &Ekos::FramingAssistant::updateTargetFOVFromGrid);
0090     connect(ui->selectJobsDirB, &QPushButton::clicked, this, &Ekos::FramingAssistant::saveJobsDirectory);
0091     connect(ui->fetchB, &QPushButton::clicked, this, &FramingAssistant::fetchINDIInformation);
0092 
0093     // The sky map is a pixmap background, and the mosaic tiles are rendered over it
0094     //m_TilesScene = new MosaicTilesScene(this);
0095     m_SkyPixmapItem = m_TilesScene.addPixmap(targetPix);
0096     m_SkyPixmapItem->setTransformationMode(Qt::TransformationMode::SmoothTransformation);
0097     m_MosaicTilesManager = new MosaicTilesManager(this);
0098     connect(m_MosaicTilesManager, &MosaicTilesManager::newOffset, this, [this](const QPointF & offset)
0099     {
0100         // Find out new center
0101         QPointF cartesianCenter = SkyMap::Instance()->projector()->toScreen(&m_CenterPoint);
0102         QPointF destinationCenter = cartesianCenter + offset;
0103         SkyPoint newCenter = SkyMap::Instance()->projector()->fromScreen(destinationCenter,
0104                              KStarsData::Instance()->lst(),
0105                              KStarsData::Instance()->geo()->lat());
0106         SkyPoint J2000Center = newCenter.catalogueCoord(KStars::Instance()->data()->ut().djd());
0107         setCenter(J2000Center);
0108         updateTimer->start();
0109     });
0110     m_TilesScene.addItem(m_MosaicTilesManager);
0111     //ui->mosaicView->setScene(&m_TilesScene);
0112 
0113     // Always use Equatorial Mode in Mosaic mode
0114     m_RememberAltAzOption = Options::useAltAz();
0115     Options::setUseAltAz(false);
0116     m_RememberShowGround = Options::showGround();
0117     Options::setShowGround(false);
0118 
0119     // Rendering options
0120     //    connect(ui->transparencySlider, QOverload<int>::of(&QSlider::valueChanged), this, [&](int v)
0121     //    {
0122     //        ui->transparencySlider->setToolTip(QString("%1%").arg(v));
0123     //        m_MosaicTilesManager->setPainterAlpha(v);
0124     //        updateTimer->start();
0125     //    });
0126     //    connect(ui->transparencyAuto, &QCheckBox::toggled, this, [&](bool v)
0127     //    {
0128     //        ui->transparencySlider->setEnabled(!v);
0129     //        if (v)
0130     //            updateTimer->start();
0131     //    });
0132 
0133     // Job options
0134     connect(ui->alignEvery, QOverload<int>::of(&QSpinBox::valueChanged), this, &Ekos::FramingAssistant::rewordStepEvery);
0135     connect(ui->focusEvery, QOverload<int>::of(&QSpinBox::valueChanged), this, &Ekos::FramingAssistant::rewordStepEvery);
0136     emit ui->alignEvery->valueChanged(0);
0137     emit ui->focusEvery->valueChanged(0);
0138 
0139     // Center, fetch optics and adjust size
0140     //setCenter(center);
0141     fetchINDIInformation();
0142     adjustSize();
0143 }
0144 
0145 FramingAssistant::~FramingAssistant()
0146 {
0147     delete updateTimer;
0148     Options::setUseAltAz(m_RememberAltAzOption);
0149     Options::setShowGround(m_RememberShowGround);
0150 }
0151 
0152 QString FramingAssistant::getJobsDir() const
0153 {
0154     return ui->jobsDir->text();
0155 }
0156 
0157 bool FramingAssistant::isScopeInfoValid() const
0158 {
0159     if (0 < ui->focalLenSpin->value())
0160         if (0 < ui->cameraWSpin->value() && 0 < ui->cameraWSpin->value())
0161             if (0 < ui->pixelWSizeSpin->value() && 0 < ui->pixelHSizeSpin->value())
0162                 return true;
0163     return false;
0164 }
0165 
0166 double FramingAssistant::getTargetWFOV() const
0167 {
0168     double const xFOV = ui->cameraWFOVSpin->value() * (1 - ui->overlapSpin->value() / 100.0);
0169     return ui->cameraWFOVSpin->value() + xFOV * (ui->mosaicWSpin->value() - 1);
0170 }
0171 
0172 double FramingAssistant::getTargetHFOV() const
0173 {
0174     double const yFOV = ui->cameraHFOVSpin->value() * (1 - ui->overlapSpin->value() / 100.0);
0175     return ui->cameraHFOVSpin->value() + yFOV * (ui->mosaicHSpin->value() - 1);
0176 }
0177 
0178 double FramingAssistant::getTargetMosaicW() const
0179 {
0180     // If FOV is invalid, or target FOV is null, or target FOV is smaller than camera FOV, we get one tile
0181     if (!isScopeInfoValid() || !ui->targetWFOVSpin->value() || ui->targetWFOVSpin->value() <= ui->cameraWFOVSpin->value())
0182         return 1;
0183 
0184     // Else we get one tile, plus as many overlapping camera FOVs in the remnant of the target FOV
0185     double const xFOV = ui->cameraWFOVSpin->value() * (1 - ui->overlapSpin->value() / 100.0);
0186     int const tiles = 1 + ceil((ui->targetWFOVSpin->value() - ui->cameraWFOVSpin->value()) / xFOV);
0187     //Ekos::Manager::Instance()->schedulerModule()->appendLogText(QString("[W] Target FOV %1, camera FOV %2 after overlap %3, %4 tiles.").arg(ui->targetWFOVSpin->value()).arg(ui->cameraWFOVSpin->value()).arg(xFOV).arg(tiles));
0188     return tiles;
0189 }
0190 
0191 double FramingAssistant::getTargetMosaicH() const
0192 {
0193     // If FOV is invalid, or target FOV is null, or target FOV is smaller than camera FOV, we get one tile
0194     if (!isScopeInfoValid() || !ui->targetHFOVSpin->value() || ui->targetHFOVSpin->value() <= ui->cameraHFOVSpin->value())
0195         return 1;
0196 
0197     // Else we get one tile, plus as many overlapping camera FOVs in the remnant of the target FOV
0198     double const yFOV = ui->cameraHFOVSpin->value() * (1 - ui->overlapSpin->value() / 100.0);
0199     int const tiles = 1 + ceil((ui->targetHFOVSpin->value() - ui->cameraHFOVSpin->value()) / yFOV);
0200     //Ekos::Manager::Instance()->schedulerModule()->appendLogText(QString("[H] Target FOV %1, camera FOV %2 after overlap %3, %4 tiles.").arg(ui->targetHFOVSpin->value()).arg(ui->cameraHFOVSpin->value()).arg(yFOV).arg(tiles));
0201     return tiles;
0202 }
0203 
0204 //int FramingAssistant::exec()
0205 //{
0206 //    premosaicZoomFactor = Options::zoomFactor();
0207 
0208 //    int const result = QDialog::exec();
0209 
0210 //    // Revert various options
0211 //    updateTimer->stop();
0212 //    SkyMap *map = SkyMap::Instance();
0213 //    if (map && 0 < premosaicZoomFactor)
0214 //        map->setZoomFactor(premosaicZoomFactor);
0215 
0216 //    return result;
0217 //}
0218 
0219 void FramingAssistant::saveJobsDirectory()
0220 {
0221     QString dir = QFileDialog::getExistingDirectory(KStars::Instance(), i18nc("@title:window", "FITS Save Directory"),
0222                   ui->jobsDir->text());
0223 
0224     if (!dir.isEmpty())
0225         ui->jobsDir->setText(dir);
0226 }
0227 
0228 void FramingAssistant::setCenter(const SkyPoint &value)
0229 {
0230     m_CenterPoint = value;
0231     m_CenterPoint.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
0232 }
0233 
0234 void FramingAssistant::calculateFOV()
0235 {
0236     if (!isScopeInfoValid())
0237         return;
0238 
0239     ui->fovGroup->setEnabled(true);
0240 
0241     ui->targetWFOVSpin->setMinimum(ui->cameraWFOVSpin->value());
0242     ui->targetHFOVSpin->setMinimum(ui->cameraHFOVSpin->value());
0243 
0244     Options::setTelescopeFocalLength(ui->focalLenSpin->value());
0245     Options::setCameraPixelWidth(ui->pixelWSizeSpin->value());
0246     Options::setCameraPixelHeight(ui->pixelHSizeSpin->value());
0247     Options::setCameraWidth(ui->cameraWSpin->value());
0248     Options::setCameraHeight(ui->cameraHSpin->value());
0249 
0250     // Calculate FOV in arcmins
0251     double const fov_x =
0252         206264.8062470963552 * ui->cameraWSpin->value() * ui->pixelWSizeSpin->value() / 60000.0 / ui->focalLenSpin->value();
0253     double const fov_y =
0254         206264.8062470963552 * ui->cameraHSpin->value() * ui->pixelHSizeSpin->value() / 60000.0 / ui->focalLenSpin->value();
0255 
0256     ui->cameraWFOVSpin->setValue(fov_x);
0257     ui->cameraHFOVSpin->setValue(fov_y);
0258 
0259     double const target_fov_w = getTargetWFOV();
0260     double const target_fov_h = getTargetHFOV();
0261 
0262     if (ui->targetWFOVSpin->value() < target_fov_w)
0263     {
0264         bool const sig = ui->targetWFOVSpin->blockSignals(true);
0265         ui->targetWFOVSpin->setValue(target_fov_w);
0266         ui->targetWFOVSpin->blockSignals(sig);
0267     }
0268 
0269     if (ui->targetHFOVSpin->value() < target_fov_h)
0270     {
0271         bool const sig = ui->targetHFOVSpin->blockSignals(true);
0272         ui->targetHFOVSpin->setValue(target_fov_h);
0273         ui->targetHFOVSpin->blockSignals(sig);
0274     }
0275 
0276     updateTimer->start();
0277 }
0278 
0279 void FramingAssistant::updateTargetFOV()
0280 {
0281     KStars *ks  = KStars::Instance();
0282     SkyMap *map = SkyMap::Instance();
0283 
0284     // Render the required FOV
0285     renderedWFOV = ui->targetWFOVSpin->value();// * cos(ui->rotationSpin->value() * dms::DegToRad);
0286     renderedHFOV = ui->targetHFOVSpin->value();// * sin(ui->rotationSpin->value() * dms::DegToRad);
0287 
0288     // Pick thrice the largest FOV to obtain a proper zoom
0289     double const spacing = ui->mosaicWSpin->value() < ui->mosaicHSpin->value() ? ui->mosaicHSpin->value() :
0290                            ui->mosaicWSpin->value();
0291     double const scale = 1.0 + 2.0 / (1.0 + spacing);
0292     double const renderedFOV = scale * (renderedWFOV < renderedHFOV ? renderedHFOV : renderedWFOV);
0293 
0294     // Check the aspect ratio of the sky map, assuming the map zoom considers the width (see KStars::setApproxFOV)
0295     double const aspect_ratio = map->width() / map->height();
0296 
0297     // Set the zoom (in degrees) that gives the expected FOV for the map aspect ratio, and center the target
0298     ks->setApproxFOV(renderedFOV * aspect_ratio / 60.0);
0299     //center.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
0300     map->setClickedObject(nullptr);
0301     map->setClickedPoint(&m_CenterPoint);
0302     map->slotCenter();
0303 
0304     // Wait for the map to stop slewing, so that HiPS renders properly
0305     while(map->isSlewing())
0306         qApp->processEvents();
0307     qApp->processEvents();
0308 
0309     // Compute the horizontal and vertical resolutions, deduce the actual FOV of the map in arcminutes
0310     pixelsPerArcminRA = pixelsPerArcminDE = Options::zoomFactor() * dms::DegToRad / 60.0;
0311 
0312     // Get the sky map image - don't bother subframing, it causes imprecision sometimes
0313     QImage fullSkyChart(QSize(map->width(), map->height()), QImage::Format_RGB32);
0314     map->exportSkyImage(&fullSkyChart, false);
0315     qApp->processEvents();
0316     m_SkyPixmapItem->setPixmap(QPixmap::fromImage(fullSkyChart));
0317 
0318     // Relocate
0319     QRectF sceneRect = m_SkyPixmapItem->boundingRect().translated(-m_SkyPixmapItem->boundingRect().center());
0320     m_TilesScene.setSceneRect(sceneRect);
0321     m_SkyPixmapItem->setOffset(sceneRect.topLeft());
0322     m_SkyPixmapItem->setPos(QPointF());
0323 
0324 }
0325 
0326 void FramingAssistant::resizeEvent(QResizeEvent *)
0327 {
0328     // Adjust scene rect to avoid rounding holes on border
0329     QRectF adjustedSceneRect(m_TilesScene.sceneRect());
0330     adjustedSceneRect.setTop(adjustedSceneRect.top() + 2);
0331     adjustedSceneRect.setLeft(adjustedSceneRect.left() + 2);
0332     adjustedSceneRect.setRight(adjustedSceneRect.right() - 2);
0333     adjustedSceneRect.setBottom(adjustedSceneRect.bottom() - 2);
0334 
0335     //    ui->mosaicView->fitInView(adjustedSceneRect, Qt::KeepAspectRatioByExpanding);
0336     //    ui->mosaicView->centerOn(QPointF());
0337 }
0338 
0339 void FramingAssistant::showEvent(QShowEvent *)
0340 {
0341     resizeEvent(nullptr);
0342 }
0343 
0344 void FramingAssistant::resetFOV()
0345 {
0346     if (!isScopeInfoValid())
0347         return;
0348 
0349     ui->targetWFOVSpin->setValue(getTargetWFOV());
0350     ui->targetHFOVSpin->setValue(getTargetHFOV());
0351 }
0352 
0353 void FramingAssistant::updateTargetFOVFromGrid()
0354 {
0355     if (!isScopeInfoValid())
0356         return;
0357 
0358     double const targetWFOV = getTargetWFOV();
0359     double const targetHFOV = getTargetHFOV();
0360 
0361     if (ui->targetWFOVSpin->value() != targetWFOV)
0362     {
0363         bool const sig = ui->targetWFOVSpin->blockSignals(true);
0364         ui->targetWFOVSpin->setValue(targetWFOV);
0365         ui->targetWFOVSpin->blockSignals(sig);
0366         updateTimer->start();
0367     }
0368 
0369     if (ui->targetHFOVSpin->value() != targetHFOV)
0370     {
0371         bool const sig = ui->targetHFOVSpin->blockSignals(true);
0372         ui->targetHFOVSpin->setValue(targetHFOV);
0373         ui->targetHFOVSpin->blockSignals(sig);
0374         updateTimer->start();
0375     }
0376 }
0377 
0378 void FramingAssistant::updateGridFromTargetFOV()
0379 {
0380     if (!isScopeInfoValid())
0381         return;
0382 
0383     double const expectedW = getTargetMosaicW();
0384     double const expectedH = getTargetMosaicH();
0385 
0386     if (expectedW != ui->mosaicWSpin->value())
0387     {
0388         bool const sig = ui->mosaicWSpin->blockSignals(true);
0389         ui->mosaicWSpin->setValue(expectedW);
0390         ui->mosaicWSpin->blockSignals(sig);
0391     }
0392 
0393     if (expectedH != ui->mosaicHSpin->value())
0394     {
0395         bool const sig = ui->mosaicHSpin->blockSignals(true);
0396         ui->mosaicHSpin->setValue(expectedH);
0397         ui->mosaicHSpin->blockSignals(sig);
0398     }
0399 
0400     // Update unconditionally, as we may be updating the overlap or the target FOV covered by the mosaic
0401     updateTimer->start();
0402 }
0403 
0404 void FramingAssistant::constructMosaic()
0405 {
0406     updateTimer->stop();
0407 
0408     if (!isScopeInfoValid())
0409         return;
0410 
0411     updateTargetFOV();
0412 
0413     if (m_MosaicTilesManager->getPA() != ui->rotationSpin->value())
0414         Options::setCameraRotation(ui->rotationSpin->value());
0415 
0416     qCDebug(KSTARS_EKOS_SCHEDULER) << "Tile FOV in pixels W:" << ui->cameraWFOVSpin->value() * pixelsPerArcminRA << "H:"
0417                                    << ui->cameraHFOVSpin->value() * pixelsPerArcminDE;
0418 
0419     m_MosaicTilesManager->setPos(0, 0);
0420     m_MosaicTilesManager->setSkyCenter(m_CenterPoint);
0421     m_MosaicTilesManager->setGridDimensions(ui->mosaicWSpin->value(), ui->mosaicHSpin->value());
0422     m_MosaicTilesManager->setPositionAngle(ui->rotationSpin->value());
0423     m_MosaicTilesManager->setSingleTileFOV(ui->cameraWFOVSpin->value() * pixelsPerArcminRA,
0424                                            ui->cameraHFOVSpin->value() * pixelsPerArcminDE);
0425     m_MosaicTilesManager->setMosaicFOV(ui->targetWFOVSpin->value() * pixelsPerArcminRA,
0426                                        ui->targetHFOVSpin->value() * pixelsPerArcminDE);
0427     m_MosaicTilesManager->setOverlap(ui->overlapSpin->value() / 100);
0428     m_MosaicTilesManager->setPixelScale(QSizeF(pixelsPerArcminRA * 60.0, pixelsPerArcminDE * 60.0));
0429     m_MosaicTilesManager->updateTiles(m_MosaicTilesManager->mapToItem(m_SkyPixmapItem,
0430                                       m_SkyPixmapItem->boundingRect().center()),
0431                                       ui->reverseOddRows->checkState() == Qt::CheckState::Checked);
0432 
0433     //    ui->jobCountSpin->setValue(m_MosaicTilesManager->getWidth() * m_MosaicTilesManager->getHeight());
0434 
0435     //    if (ui->transparencyAuto->isChecked())
0436     //    {
0437     //        // Tiles should be more transparent when many are overlapped
0438     //        // Overlap < 50%: low transparency, as only two tiles will overlap on a line
0439     //        // 50% < Overlap < 75%: mid transparency, as three tiles will overlap one a line
0440     //        // 75% < Overlap: high transparency, as four tiles will overlap on a line
0441     //        // Slider controlling transparency provides [5%,50%], which is scaled to 0-200 alpha.
0442 
0443     //        if (1 < ui->jobCountSpin->value())
0444     //            ui->transparencySlider->setValue(40 - ui->overlapSpin->value() / 2);
0445     //        else
0446     //            ui->transparencySlider->setValue(40);
0447 
0448     //        ui->transparencySlider->update();
0449     //    }
0450 
0451     resizeEvent(nullptr);
0452     m_MosaicTilesManager->show();
0453 
0454     //ui->mosaicView->update();
0455 }
0456 
0457 QList <FramingAssistant::Job> FramingAssistant::getJobs() const
0458 {
0459     qCDebug(KSTARS_EKOS_SCHEDULER) << "Mosaic Tile W:" << m_MosaicTilesManager->boundingRect().width() << "H:" <<
0460                                    m_MosaicTilesManager->boundingRect().height();
0461 
0462     QList <FramingAssistant::Job> result;
0463 
0464     // We have two items:
0465     // 1. SkyMapItem is the pixmap we fetch from KStars that shows the sky field.
0466     // 2. MosaicItem is the constructed mosaic boxes.
0467     // We already know the center (RA0,DE0) of the SkyMapItem.
0468     // We Map the coordinate of each tile to the SkyMapItem to find out where the tile center is located
0469     // on the SkyMapItem pixmap.
0470     // We calculate the difference between the tile center and the SkyMapItem center and then find the tile coordinates
0471     // in J2000 coords.
0472     for (int i = 0; i < m_MosaicTilesManager->getHeight(); i++)
0473     {
0474         for (int j = 0; j < m_MosaicTilesManager->getWidth(); j++)
0475         {
0476             MosaicTilesManager::OneTile * const tile = m_MosaicTilesManager->getTile(i, j);
0477             qCDebug(KSTARS_EKOS_SCHEDULER) << "Tile #" << i * m_MosaicTilesManager->getWidth() + j << "Center:" << tile->center;
0478 
0479             Job ts;
0480             ts.center.setRA0(tile->skyCenter.ra0().Hours());
0481             ts.center.setDec0(tile->skyCenter.dec0().Degrees());
0482             ts.rotation = -m_MosaicTilesManager->getPA();
0483 
0484             ts.doAlign =
0485                 (0 < ui->alignEvery->value()) &&
0486                 (0 == ((j + i * m_MosaicTilesManager->getHeight()) % ui->alignEvery->value()));
0487 
0488             ts.doFocus =
0489                 (0 < ui->focusEvery->value()) &&
0490                 (0 == ((j + i * m_MosaicTilesManager->getHeight()) % ui->focusEvery->value()));
0491 
0492             qCDebug(KSTARS_EKOS_SCHEDULER) << "Tile RA0:" << tile->skyCenter.ra0().toHMSString() << "DE0:" <<
0493                                            tile->skyCenter.dec0().toDMSString();
0494             result.append(ts);
0495         }
0496     }
0497 
0498     return result;
0499 }
0500 
0501 void FramingAssistant::fetchINDIInformation()
0502 {
0503     QDBusInterface alignInterface("org.kde.kstars",
0504                                   "/KStars/Ekos/Align",
0505                                   "org.kde.kstars.Ekos.Align",
0506                                   QDBusConnection::sessionBus());
0507 
0508     QDBusReply<QList<double>> cameraReply = alignInterface.call("cameraInfo");
0509     if (cameraReply.isValid())
0510     {
0511         QList<double> const values = cameraReply.value();
0512 
0513         m_CameraSize = QSize(values[0], values[1]);
0514         m_PixelSize = QSizeF(values[2], values[3]);
0515     }
0516 
0517     QDBusReply<QList<double>> telescopeReply = alignInterface.call("telescopeInfo");
0518     if (telescopeReply.isValid())
0519     {
0520         QList<double> const values = telescopeReply.value();
0521         m_FocalLength = values[0];
0522     }
0523 
0524     QDBusReply<QList<double>> solutionReply = alignInterface.call("getSolutionResult");
0525     if (solutionReply.isValid())
0526     {
0527         QList<double> const values = solutionReply.value();
0528         if (values[0] > INVALID_VALUE)
0529             m_Rotation = values[0];
0530     }
0531 
0532     calculateFOV();
0533 }
0534 
0535 void FramingAssistant::rewordStepEvery(int v)
0536 {
0537     QSpinBox * sp = dynamic_cast<QSpinBox *>(sender());
0538     if (0 < v)
0539         sp->setSuffix(i18np(" Scheduler job", " Scheduler jobs", v));
0540     else
0541         sp->setSuffix(i18n(" (first only)"));
0542 }
0543 
0544 }