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

0001 /*
0002     SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003                             2022 Toni Schriber
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 /******************************************************************************************************
0009 * In 'rotatorGauge' and 'paGauge' all angles are displayed in viewing direction and positiv CCW.
0010 *******************************************************************************************************/
0011 
0012 #include "rotatorsettings.h"
0013 #include "Options.h"
0014 #include "fov.h"
0015 #include "kstarsdata.h"
0016 #include "ekos/manager.h"
0017 #include "indi/indirotator.h"
0018 #include <indicom.h>
0019 #include <basedevice.h>
0020 #include <cmath>
0021 #include "capture.h"
0022 #include "ekos/align/align.h"
0023 #include "ekos/capture/capturedeviceadaptor.h"
0024 #include "ekos/align/opsalign.h"
0025 #include "ekos/auxiliary/rotatorutils.h"
0026 
0027 #include "ekos_capture_debug.h"
0028 
0029 RotatorSettings::RotatorSettings(QWidget *parent) : QDialog(parent)
0030 {
0031     setupUi(this);
0032 
0033     connect(this, &RotatorSettings::newLog, Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::appendLogText);
0034     connect(RotatorUtils::Instance(), &RotatorUtils::changedPierside, this, &RotatorSettings::updateGaugeZeroPos);
0035     connect(FlipPolicy, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RotatorSettings::setFlipPolicy);
0036     connect(AlignOptions,  &QPushButton::clicked, this, &RotatorSettings::showAlignOptions);
0037 
0038     // -- Parameter -> ui file
0039 
0040     // -- Camera position angle
0041     CameraPA->setKeyboardTracking(false);
0042     connect(CameraPA, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,  [ = ](double PAngle)
0043     {
0044         double RAngle = RotatorUtils::Instance()->calcRotatorAngle(PAngle);
0045         RotatorAngle->setValue(RAngle);
0046         syncFOV(PAngle);
0047         activateRotator(RAngle);
0048         CameraPASlider->setSliderPosition(PAngle * 100); // Prevent rounding to integer
0049     });
0050     connect(CameraPASlider, &QSlider::sliderReleased, this, [this]()
0051     {
0052         CameraPA->setValue(CameraPASlider->sliderPosition() / 100.0); // Set position angle
0053     });
0054     connect(CameraPASlider, &QSlider::valueChanged, this, [this](int PAngle100)
0055     {
0056         double PAngle = PAngle100 / 100;
0057         paGauge->setValue(-(PAngle)); // Preview cameraPA in gauge
0058         syncFOV(PAngle); // Preview FOV
0059     });
0060 
0061     // -- Options
0062     // enforceJobPA -> header file
0063     connect(reverseDirection,  &QCheckBox::toggled, this, [ = ](bool toggled)
0064     {
0065         commitRotatorDirection(toggled);
0066     });
0067 
0068     // Rotator Gauge
0069     rotatorGauge->setFormat("R");   // dummy format
0070     rotatorGauge->setMinimum(-360); // display in viewing direction
0071     rotatorGauge->setMaximum(0);
0072 
0073     // Position Angle Gauge
0074     paGauge->setFormat("P");        // dummy format
0075     paGauge->setMinimum(-181);      // display in viewing direction
0076     paGauge->setMaximum(181);
0077 
0078     // Angle Ruler
0079     paRuler->plotLayout()->clear();
0080     QCPPolarAxisAngular *angularAxis = new QCPPolarAxisAngular(paRuler);
0081     angularAxis->removeRadialAxis(angularAxis->radialAxis());
0082     QColor TransparentBlack(0, 0, 0, 100);
0083     QPen Pen(TransparentBlack, 3);
0084     angularAxis->setBasePen(Pen);
0085     angularAxis->setTickPen(Pen);
0086     angularAxis->setSubTickPen(Pen);
0087     angularAxis->setTickLabels(false);
0088     angularAxis->setTickLength(10, 10);
0089     angularAxis->setSubTickLength(5, 5);
0090     paRuler->plotLayout()->addElement(0, 0, angularAxis);
0091     paRuler->setBackground(Qt::GlobalColor::transparent);  // transparent background part 1
0092     paRuler->setAttribute(Qt::WA_OpaquePaintEvent, false); // transparent background part 2
0093     angularAxis->grid()->setAngularPen(QPen(Qt::GlobalColor::transparent)); // no grid
0094     paRuler->replot();
0095 
0096     // Parameter Interface
0097     CameraPA->setMaximum(180.00);   // uniqueness of angle (-180 = 180)
0098     CameraPA->setMinimum(-179.99);
0099     RotatorAngle->setButtonSymbols(QAbstractSpinBox::NoButtons);
0100     CameraOffset->setValue(Options::pAOffset());
0101     CameraOffset->setButtonSymbols(QAbstractSpinBox::NoButtons);
0102     MountPierside->setCurrentIndex(ISD::Mount::PIER_UNKNOWN);
0103     MountPierside->setDisabled(true);  // only show pierside for information
0104 }
0105 
0106 void RotatorSettings::initRotator(const QString &train, Ekos::CaptureDeviceAdaptor *CaptureDA, ISD::Rotator *device)
0107 {
0108     m_CaptureDA = CaptureDA;
0109     RotatorUtils::Instance()->initRotatorUtils(train);
0110 
0111     m_Rotator = device;
0112     RotatorName->setText(m_Rotator->getDeviceName());
0113     updateFlipPolicy(Options::astrometryFlipRotationAllowed());
0114     // Give getState() a second
0115     QTimer::singleShot(1000, [ = ]
0116     {
0117         if (m_CaptureDA->getRotatorAngleState() == IPS_OK)
0118         {
0119             double RAngle = m_CaptureDA->getRotatorAngle();
0120             updateRotator(RAngle);
0121             updateGaugeZeroPos(RotatorUtils::Instance()->getMountPierside());
0122             qCInfo(KSTARS_EKOS_CAPTURE()) << "Rotator Settings: Initial raw angle is" << RAngle << ".";
0123             emit newLog(i18n("Initial rotator angle %1° is read in successfully.", RAngle));
0124         }
0125         else
0126             qCWarning(KSTARS_EKOS_CAPTURE()) << "Rotator Settings: Reading initial raw angle failed.";
0127     });
0128 }
0129 
0130 void RotatorSettings::updateRotator(double RAngle)
0131 {
0132     RotatorAngle->setValue(RAngle);
0133     double PAngle = RotatorUtils::Instance()->calcCameraAngle(RAngle, false);
0134     CameraPA->blockSignals(true); // Prevent reaction coupling via user input
0135     CameraPA->setValue(PAngle);
0136     CameraPA->blockSignals(false);
0137     CameraPASlider->setSliderPosition(PAngle * 100); // Prevent rounding to integer
0138     updateGauge(RAngle);
0139 }
0140 
0141 void RotatorSettings::updateGauge(double RAngle)
0142 {
0143     rotatorGauge->setValue(-RAngle); // display in viewing direction
0144     CurrentRotatorAngle->setText(QString::number(RAngle, 'f', 2));
0145     paGauge->setValue(-(RotatorUtils::Instance()->calcCameraAngle(RAngle, false)));
0146 }
0147 
0148 void RotatorSettings::updateGaugeZeroPos(ISD::Mount::PierSide Pierside)
0149 {
0150     double RAngle = 0;
0151     if (Pierside == ISD::Mount::PIER_UNKNOWN)
0152         MountPierside->setStyleSheet("QComboBox {border: 1px solid red;}");
0153     else
0154         MountPierside->setStyleSheet("QComboBox {}");
0155     MountPierside->setCurrentIndex(Pierside);
0156     if (Pierside == ISD::Mount::PIER_WEST)
0157         rotatorGauge->setNullPosition(QRoundProgressBar::PositionTop);
0158     else if (Pierside == ISD::Mount::PIER_EAST)
0159         rotatorGauge->setNullPosition(QRoundProgressBar::PositionBottom);
0160     if (Options::astrometryFlipRotationAllowed()) // Preserve rotator raw angle
0161         RAngle = RotatorAngle->value();
0162     else // Preserve camera position angle
0163     {
0164         RAngle = RotatorUtils::Instance()->calcRotatorAngle(CameraPA->value());
0165         activateRotator(RAngle);
0166     }
0167     updateGauge(RAngle);
0168     updateRotator(RAngle);
0169 }
0170 
0171 void RotatorSettings::setFlipPolicy(const int index)
0172 {
0173     Ekos::OpsAlign::FlipPriority Priority = static_cast<Ekos::OpsAlign::FlipPriority>(index);
0174     Ekos::OpsAlign *AlignOptionsModule = Ekos::Manager::Instance()->alignModule()->getAlignOptionsModule();
0175     if (AlignOptionsModule)
0176         AlignOptionsModule->setFlipPolicy(Priority);
0177 }
0178 
0179 void RotatorSettings::updateFlipPolicy(const bool FlipRotationAllowed)
0180 {
0181     int i = -1;
0182     if (FlipRotationAllowed)
0183         i = static_cast<int>(Ekos::OpsAlign::FlipPriority::ROTATOR_ANGLE);
0184     else
0185         i = static_cast<int>(Ekos::OpsAlign::FlipPriority::POSITION_ANGLE);
0186     FlipPolicy->blockSignals(true); // Prevent reaction coupling
0187     FlipPolicy->setCurrentIndex(i);
0188     FlipPolicy->blockSignals(false);
0189 }
0190 
0191 void RotatorSettings::showAlignOptions()
0192 {
0193     KConfigDialog * alignSettings = KConfigDialog::exists("alignsettings");
0194     if (alignSettings)
0195     {
0196         alignSettings->setEnabled(true);
0197         alignSettings->show();
0198     }
0199 }
0200 
0201 void RotatorSettings::activateRotator(double Angle)
0202 {
0203     m_CaptureDA->setRotatorAngle(Angle);
0204 }
0205 
0206 void RotatorSettings::commitRotatorDirection(bool Reverse)
0207 {
0208     m_CaptureDA->reverseRotator(Reverse);
0209 }
0210 
0211 void RotatorSettings::refresh(double PAngle) // Call from setAlignResults() in Module Capture
0212 {
0213     CameraPA->setValue(PAngle);
0214     syncFOV(PAngle);
0215     CameraOffset->setValue(Options::pAOffset());
0216 }
0217 
0218 void RotatorSettings::syncFOV(double PA)
0219 {
0220     for (auto oneFOV : KStarsData::Instance()->getTransientFOVs())
0221     {
0222         // Only change the PA for the sensor FOV
0223         if (oneFOV->objectName() == "sensor_fov")
0224         {
0225             // Make sure that it is always displayed
0226             if (!Options::showSensorFOV())
0227             {
0228                 Options::setShowSensorFOV(true);
0229                 oneFOV->setProperty("visible", true);
0230             }
0231 
0232             // JM 2020-10-15
0233             // While we have the correct Position Angle
0234             // Because Ekos reads frame TOP-BOTTOM instead of the BOTTOM-TOP approach
0235             // used by astrometry, the PA is always 180 degree off. To avoid confusion to the user
0236             // the PA is drawn REVERSED to show the *expected* frame. However, the final PA is
0237             // the "correct" PA as expected by astrometry.
0238             //double drawnPA = PA >= 0 ? (PA - 180) : (PA + 180);
0239             oneFOV->setPA(PA);
0240             break;
0241         }
0242     }
0243 }
0244