diff --git a/kstars/ekos/observatory/observatory.cpp b/kstars/ekos/observatory/observatory.cpp index caff8a16d..d59449e0e 100644 --- a/kstars/ekos/observatory/observatory.cpp +++ b/kstars/ekos/observatory/observatory.cpp @@ -1,881 +1,905 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "kstarsdata.h" #include "observatory.h" #include "ekos_observatory_debug.h" namespace Ekos { Observatory::Observatory() { setupUi(this); // status control mObservatoryModel = new ObservatoryModel(); setObseratoryStatusControl(mObservatoryModel->statusControl()); // update UI for status control connect(useDomeCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useShutterCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useWeatherCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(mObservatoryModel, &Ekos::ObservatoryModel::newStatus, this, &Ekos::Observatory::observatoryStatusChanged); // ready button deactivated // connect(statusReadyButton, &QPushButton::clicked, mObservatoryModel, &Ekos::ObservatoryModel::makeReady); statusReadyButton->setEnabled(false); setDomeModel(new ObservatoryDomeModel()); setWeatherModel(new ObservatoryWeatherModel()); } void Observatory::setObseratoryStatusControl(ObservatoryStatusControl control) { if (mObservatoryModel != nullptr) { useDomeCB->setChecked(control.useDome); useShutterCB->setChecked(control.useShutter); useWeatherCB->setChecked(control.useWeather); } } void Observatory::setDomeModel(ObservatoryDomeModel *model) { mObservatoryModel->setDomeModel(model); if (model != nullptr) { connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); connect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); connect(model, &Ekos::ObservatoryDomeModel::newParkStatus, this, &Ekos::Observatory::setDomeParkStatus); connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); connect(model, &Ekos::ObservatoryDomeModel::azimuthPositionChanged, this, &Ekos::Observatory::domeAzimuthChanged); connect(model, &Ekos::ObservatoryDomeModel::newAutoSyncStatus, this, &Ekos::Observatory::showAutoSync); // motion controls connect(motionMoveAbsButton, &QCheckBox::clicked, [this]() { mObservatoryModel->getDomeModel()->setAzimuthPosition(absoluteMotionSB->value()); }); connect(motionMoveRelButton, &QCheckBox::clicked, [this]() { mObservatoryModel->getDomeModel()->setRelativePosition(relativeMotionSB->value()); }); // abort button connect(motionAbortButton, &QPushButton::clicked, model, &ObservatoryDomeModel::abort); // weather controls connect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); connect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherAlertSettingsChanged(); }); } else { shutdownDome(); disconnect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); disconnect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); disconnect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); disconnect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); } } void Observatory::initDome() { domeBox->setEnabled(true); if (getDomeModel() != nullptr) { connect(getDomeModel(), &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText); // dome motion buttons connect(motionCWButton, &QPushButton::clicked, [ = ](bool checked) { getDomeModel()->moveDome(true, checked); }); connect(motionCCWButton, &QPushButton::clicked, [ = ](bool checked) { getDomeModel()->moveDome(false, checked); }); if (getDomeModel()->canPark()) { connect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); connect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); domePark->setEnabled(true); domeUnpark->setEnabled(true); } else { domePark->setEnabled(false); domeUnpark->setEnabled(false); } if (getDomeModel()->isRolloffRoof()) { SlavingBox->setVisible(false); domeAzimuthPosition->setText(i18nc("Not Applicable", "N/A")); enableMotionControl(true); } else { // initialize the dome motion controls domeAzimuthChanged(getDomeModel()->azimuthPosition()); // slaving showAutoSync(getDomeModel()->isAutoSync()); connect(slavingEnableButton, &QPushButton::clicked, this, [this]() { enableAutoSync(true); }); connect(slavingDisableButton, &QPushButton::clicked, this, [this]() { enableAutoSync(false); }); } // shutter handling if (getDomeModel()->hasShutter()) { shutterBox->setVisible(true); shutterBox->setEnabled(true); connect(shutterOpen, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::openShutter); connect(shutterClosed, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::closeShutter); shutterClosed->setEnabled(true); shutterOpen->setEnabled(true); setShutterStatus(getDomeModel()->shutterStatus()); useShutterCB->setVisible(true); } else { shutterBox->setVisible(false); weatherWarningShutterCB->setVisible(false); weatherAlertShutterCB->setVisible(false); useShutterCB->setVisible(false); } // abort button should always be available motionAbortButton->setEnabled(true); statusDefinitionBox->setVisible(true); statusDefinitionBox->setEnabled(true); // update the dome parking status setDomeParkStatus(getDomeModel()->parkStatus()); } } void Observatory::shutdownDome() { domeBox->setEnabled(false); shutterBox->setEnabled(false); shutterBox->setVisible(false); domePark->setEnabled(false); domeUnpark->setEnabled(false); shutterClosed->setEnabled(false); shutterOpen->setEnabled(false); disconnect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); disconnect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); } void Observatory::setDomeStatus(ISD::Dome::Status status) { qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome status to " << status; switch (status) { case ISD::Dome::DOME_ERROR: appendLogText(i18n("%1 error. See INDI log for details.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); motionCWButton->setChecked(false); motionCCWButton->setChecked(false); break; case ISD::Dome::DOME_IDLE: motionCWButton->setChecked(false); motionCWButton->setEnabled(true); motionCCWButton->setChecked(false); motionCCWButton->setEnabled(true); appendLogText(i18n("%1 is idle.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); break; case ISD::Dome::DOME_MOVING_CW: motionCWButton->setChecked(true); motionCWButton->setEnabled(false); motionCCWButton->setChecked(false); motionCCWButton->setEnabled(true); if (getDomeModel()->isRolloffRoof()) { domeAzimuthPosition->setText(i18n("Opening")); toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park")); appendLogText(i18n("Rolloff roof opening...")); } else { appendLogText(i18n("Dome is moving clockwise...")); } break; case ISD::Dome::DOME_MOVING_CCW: motionCWButton->setChecked(false); motionCWButton->setEnabled(true); motionCCWButton->setChecked(true); motionCCWButton->setEnabled(false); if (getDomeModel()->isRolloffRoof()) { domeAzimuthPosition->setText(i18n("Closing")); toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark")); appendLogText(i18n("Rolloff roof is closing...")); } else { appendLogText(i18n("Dome is moving counter clockwise...")); } break; case ISD::Dome::DOME_PARKED: setDomeParkStatus(ISD::PARK_PARKED); appendLogText(i18n("%1 is parked.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); break; case ISD::Dome::DOME_PARKING: toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark")); motionCWButton->setEnabled(true); if (getDomeModel()->isRolloffRoof()) domeAzimuthPosition->setText(i18n("Closing")); else enableMotionControl(false); motionCWButton->setChecked(false); motionCCWButton->setChecked(true); appendLogText(i18n("%1 is parking...", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); break; case ISD::Dome::DOME_UNPARKING: toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park")); motionCCWButton->setEnabled(true); if (getDomeModel()->isRolloffRoof()) domeAzimuthPosition->setText(i18n("Opening")); else enableMotionControl(false); motionCWButton->setChecked(true); motionCCWButton->setChecked(false); appendLogText(i18n("%1 is unparking...", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); break; case ISD::Dome::DOME_TRACKING: enableMotionControl(true); motionCWButton->setEnabled(true); motionCCWButton->setChecked(true); appendLogText(i18n("%1 is tracking.", getDomeModel()->isRolloffRoof() ? i18n("Rolloff roof") : i18n("Dome"))); break; } } void Observatory::setDomeParkStatus(ISD::ParkStatus status) { qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome park status to " << status; switch (status) { case ISD::PARK_UNPARKED: activateButton(domePark, i18n("Park")); buttonPressed(domeUnpark, i18n("Unparked")); motionCWButton->setChecked(false); motionCWButton->setEnabled(true); motionCCWButton->setChecked(false); if (getDomeModel()->isRolloffRoof()) domeAzimuthPosition->setText(i18n("Open")); else enableMotionControl(true); break; case ISD::PARK_PARKED: buttonPressed(domePark, i18n("Parked")); activateButton(domeUnpark, i18n("Unpark")); motionCWButton->setChecked(false); motionCCWButton->setChecked(false); motionCCWButton->setEnabled(false); if (getDomeModel()->isRolloffRoof()) domeAzimuthPosition->setText(i18n("Closed")); else enableMotionControl(false); break; default: break; } } void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status) { qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting shutter status to " << status; switch (status) { case ISD::Dome::SHUTTER_OPEN: buttonPressed(shutterOpen, i18n("Opened")); activateButton(shutterClosed, i18n("Close")); appendLogText(i18n("Shutter is open.")); break; case ISD::Dome::SHUTTER_OPENING: toggleButtons(shutterOpen, i18n("Opening"), shutterClosed, i18n("Close")); appendLogText(i18n("Shutter is opening...")); break; case ISD::Dome::SHUTTER_CLOSED: buttonPressed(shutterClosed, i18n("Closed")); activateButton(shutterOpen, i18n("Open")); appendLogText(i18n("Shutter is closed.")); break; case ISD::Dome::SHUTTER_CLOSING: toggleButtons(shutterClosed, i18n("Closing"), shutterOpen, i18n("Open")); appendLogText(i18n("Shutter is closing...")); break; default: break; } } void Observatory::enableWeather(bool enable) { weatherBox->setEnabled(enable); clearGraphHistory->setVisible(enable); clearGraphHistory->setEnabled(enable); + autoscaleValuesCB->setVisible(enable); sensorGraphs->setVisible(enable); } void Observatory::clearSensorDataHistory() { std::map*>::iterator it; for (it=sensorGraphData.begin(); it != sensorGraphData.end(); ++it) { QVector* graphDataVector = it->second; if (graphDataVector->size() > 0) { // we keep only the last one QCPGraphData last = graphDataVector->last(); graphDataVector->clear(); QDateTime when = QDateTime(); when.setTime_t(static_cast(last.key)); updateSensorGraph(it->first, when, last.value); } } // force an update to the current graph if (selectedSensorID != "") selectedSensorChanged(selectedSensorID); } void Observatory::setWeatherModel(ObservatoryWeatherModel *model) { mObservatoryModel->setWeatherModel(model); // disable the weather UI enableWeather(false); if (model != nullptr) connect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); else shutdownWeather(); // make invisible, since not implemented yet weatherWarningSchedulerCB->setVisible(false); weatherAlertSchedulerCB->setVisible(false); } void Observatory::enableMotionControl(bool enabled) { MotionBox->setEnabled(enabled); // absolute motion controls if (getDomeModel()->canAbsoluteMove()) { motionMoveAbsButton->setEnabled(enabled); absoluteMotionSB->setEnabled(enabled); } else { motionMoveAbsButton->setEnabled(false); absoluteMotionSB->setEnabled(false); } // relative motion controls if (getDomeModel()->canRelativeMove()) { motionMoveRelButton->setEnabled(enabled); relativeMotionSB->setEnabled(enabled); motionCWButton->setEnabled(enabled); motionCCWButton->setEnabled(enabled); } else { motionMoveRelButton->setEnabled(false); relativeMotionSB->setEnabled(false); motionCWButton->setEnabled(false); motionCCWButton->setEnabled(false); } // special case for rolloff roofs if (getDomeModel()->isRolloffRoof()) { motionCWButton->setText(i18n("Open")); motionCCWButton->setText(i18n("Close")); motionCWButton->setEnabled(enabled); motionCCWButton->setEnabled(enabled); motionMoveAbsButton->setVisible(false); motionMoveRelButton->setVisible(false); absoluteMotionSB->setVisible(false); relativeMotionSB->setVisible(false); } } void Observatory::enableAutoSync(bool enabled) { if (getDomeModel() == nullptr) showAutoSync(false); else { getDomeModel()->setAutoSync(enabled); showAutoSync(enabled); } } void Observatory::showAutoSync(bool enabled) { slavingEnableButton->setChecked(enabled); slavingDisableButton->setChecked(! enabled); } void Observatory::initWeather() { // initialize the weather sensor data group box sensorDataBoxLayout = new QGridLayout(); sensorData->setLayout(sensorDataBoxLayout); enableWeather(true); initSensorGraphs(); connect(weatherWarningBox, &QGroupBox::clicked, getWeatherModel(), &ObservatoryWeatherModel::setWarningActionsActive); connect(weatherAlertBox, &QGroupBox::clicked, getWeatherModel(), &ObservatoryWeatherModel::setAlertActionsActive); connect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); connect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); connect(clearGraphHistory, &QPushButton::clicked, this, &Observatory::clearSensorDataHistory); + connect(autoscaleValuesCB, &QCheckBox::clicked, [this](bool checked) + { + getWeatherModel()->setAutoScaleValues(checked); + this->refreshSensorGraph(); + }); connect(&weatherStatusTimer, &QTimer::timeout, [this]() { weatherWarningStatusLabel->setText(getWeatherModel()->getWarningActionsStatus()); weatherAlertStatusLabel->setText(getWeatherModel()->getAlertActionsStatus()); }); weatherBox->setEnabled(true); + autoscaleValuesCB->setChecked(getWeatherModel()->autoScaleValues()); weatherActionsBox->setVisible(true); weatherActionsBox->setEnabled(true); weatherWarningBox->setChecked(getWeatherModel()->getWarningActionsActive()); weatherAlertBox->setChecked(getWeatherModel()->getAlertActionsActive()); setWeatherStatus(getWeatherModel()->status()); setWarningActions(getWeatherModel()->getWarningActions()); setAlertActions(getWeatherModel()->getAlertActions()); weatherStatusTimer.start(1000); if (getWeatherModel()->refresh() == false) appendLogText(i18n("Refreshing weather data failed.")); + // avoid double init + disconnect(getWeatherModel(), &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); } void Observatory::shutdownWeather() { weatherStatusTimer.stop(); setWeatherStatus(ISD::Weather::WEATHER_IDLE); enableWeather(false); } void Observatory::updateSensorGraph(QString label, QDateTime now, double value) { // we assume that labels are unique and use the full label as identifier QString id = label; // lazy instantiation of the sensor data storage if (sensorGraphData[id] == nullptr) { sensorGraphData[id] = new QVector(); sensorRanges[id] = value > 0 ? 1 : (value < 0 ? -1 : 0); } // store the data sensorGraphData[id]->append(QCPGraphData(static_cast(now.toTime_t()), value)); // add data for the graphs we display if (selectedSensorID == id) { // display first point in scattered style if (sensorGraphData[id]->size() == 1) sensorGraphs->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black, 0), QBrush(Qt::green), 5)); else sensorGraphs->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone)); // display data point sensorGraphs->graph()->addData(sensorGraphData[id]->last().key, sensorGraphData[id]->last().value); - sensorGraphs->rescaleAxes(); - // ensure that the 0-line is visible + + // determine where the x axis is relatively to the value ranges if ((sensorRanges[id] > 0 && value < 0) || (sensorRanges[id] < 0 && value > 0)) sensorRanges[id] = 0; - // ensure visibility of the 0-line on the y-axis - if (sensorRanges[id] > 0) - sensorGraphs->yAxis->setRangeLower(0); - else if (sensorRanges[id] < 0) - sensorGraphs->yAxis->setRangeUpper(0); - sensorGraphs->replot(); + refreshSensorGraph(); } } void Observatory::updateSensorData(std::vector weatherData) { std::vector::iterator it; QDateTime now = KStarsData::Instance()->lt(); for (it=weatherData.begin(); it != weatherData.end(); ++it) { QString const id = it->label; if (sensorDataWidgets[id] == nullptr) { QPushButton* labelWidget = new QPushButton(it->label); labelWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); labelWidget->setCheckable(true); labelWidget->setStyleSheet("QPushButton:checked\n{\nbackground-color: maroon;\nborder: 1px outset;\nfont-weight:bold;\n}"); // we need the object name since the label may contain '&' for keyboard shortcuts labelWidget->setObjectName(it->label); QLineEdit* valueWidget = new QLineEdit(QString().setNum(it->value, 'f', 2)); // fix width to enable stretching of the graph valueWidget->setMinimumWidth(96); valueWidget->setMaximumWidth(96); valueWidget->setReadOnly(true); valueWidget->setAlignment(Qt::AlignRight); sensorDataWidgets[id] = new QPair(labelWidget, valueWidget); sensorDataBoxLayout->addWidget(labelWidget, sensorDataBoxLayout->rowCount(), 0); sensorDataBoxLayout->addWidget(valueWidget, sensorDataBoxLayout->rowCount()-1, 1); // initial graph selection if (selectedSensorID == "" && id.indexOf('(') > 0 && id.indexOf('(') < id.indexOf(')')) { selectedSensorID = id; labelWidget->setChecked(true); } sensorDataNamesGroup->addButton(labelWidget); } else { sensorDataWidgets[id]->first->setText(QString(it->label)); sensorDataWidgets[id]->second->setText(QString().setNum(it->value, 'f', 2)); } // store sensor data unit if necessary updateSensorGraph(it->label, now, it->value); } } void Observatory::mouseOverLine(QMouseEvent *event) { double key = sensorGraphs->xAxis->pixelToCoord(event->localPos().x()); QCPGraph *graph = qobject_cast(sensorGraphs->plottableAt(event->pos(), false)); if (graph) { int index = sensorGraphs->graph(0)->findBegin(key); double value = sensorGraphs->graph(0)->dataMainValue(index); QDateTime when = QDateTime::fromTime_t(sensorGraphs->graph(0)->dataMainKey(index)); QToolTip::showText( event->globalPos(), i18n("%1 = %2 @ %3", selectedSensorID, value, when.toString("hh:mm"))); } else { QToolTip::hideText(); } } +void Observatory::refreshSensorGraph() +{ + + sensorGraphs->rescaleAxes(); + + // restrict the y-Axis to the values range + if (getWeatherModel()->autoScaleValues() == false) + { + if (sensorRanges[selectedSensorID] > 0) + sensorGraphs->yAxis->setRangeLower(0); + else if (sensorRanges[selectedSensorID] < 0) + sensorGraphs->yAxis->setRangeUpper(0); + } + + sensorGraphs->replot(); +} + void Observatory::selectedSensorChanged(QString id) { QVector *data = sensorGraphData[id]; if (data != nullptr) { // copy the graph data to the graph container QCPGraphDataContainer *container = new QCPGraphDataContainer(); for (QVector::iterator it = data->begin(); it != data->end(); ++it) container->add(QCPGraphData(it->key, it->value)); sensorGraphs->graph()->setData(QSharedPointer(container)); - sensorGraphs->rescaleAxes(); - sensorGraphs->replot(); selectedSensorID = id; + refreshSensorGraph(); } } void Observatory::setWeatherStatus(ISD::Weather::Status status) { std::string label; switch (status) { case ISD::Weather::WEATHER_OK: label = "security-high"; appendLogText(i18n("Weather is OK")); break; case ISD::Weather::WEATHER_WARNING: label = "security-medium"; appendLogText(i18n("Weather Warning")); break; case ISD::Weather::WEATHER_ALERT: label = "security-low"; appendLogText(i18n("Weather Alert")); break; default: label = ""; break; } weatherStatusLabel->setPixmap(QIcon::fromTheme(label.c_str()).pixmap(QSize(28, 28))); std::vector weatherData = getWeatherModel()->getWeatherData(); // update weather sensor data updateSensorData(weatherData); } void Observatory::initSensorGraphs() { // set some pens, brushes and backgrounds: sensorGraphs->xAxis->setBasePen(QPen(Qt::white, 1)); sensorGraphs->yAxis->setBasePen(QPen(Qt::white, 1)); sensorGraphs->xAxis->setTickPen(QPen(Qt::white, 1)); sensorGraphs->yAxis->setTickPen(QPen(Qt::white, 1)); sensorGraphs->xAxis->setSubTickPen(QPen(Qt::white, 1)); sensorGraphs->yAxis->setSubTickPen(QPen(Qt::white, 1)); sensorGraphs->xAxis->setTickLabelColor(Qt::white); sensorGraphs->yAxis->setTickLabelColor(Qt::white); sensorGraphs->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); sensorGraphs->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); sensorGraphs->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); sensorGraphs->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); sensorGraphs->xAxis->grid()->setSubGridVisible(true); sensorGraphs->yAxis->grid()->setSubGridVisible(true); sensorGraphs->xAxis->grid()->setZeroLinePen(Qt::NoPen); sensorGraphs->yAxis->grid()->setZeroLinePen(Qt::NoPen); sensorGraphs->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow); sensorGraphs->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow); QLinearGradient plotGradient; plotGradient.setStart(0, 0); plotGradient.setFinalStop(0, 350); plotGradient.setColorAt(0, QColor(80, 80, 80)); plotGradient.setColorAt(1, QColor(50, 50, 50)); sensorGraphs->setBackground(plotGradient); QLinearGradient axisRectGradient; axisRectGradient.setStart(0, 0); axisRectGradient.setFinalStop(0, 350); axisRectGradient.setColorAt(0, QColor(80, 80, 80)); axisRectGradient.setColorAt(1, QColor(30, 30, 30)); sensorGraphs->axisRect()->setBackground(axisRectGradient); QSharedPointer dateTicker(new QCPAxisTickerDateTime); dateTicker->setDateTimeFormat("hh:mm"); dateTicker->setTickCount(2); sensorGraphs->xAxis->setTicker(dateTicker); // allow dragging in all directions sensorGraphs->setInteraction(QCP::iRangeDrag, true); sensorGraphs->setInteraction(QCP::iRangeZoom); // create the universal graph QCPGraph *graph = sensorGraphs->addGraph(); graph->setPen(QPen(Qt::darkGreen, 2)); graph->setBrush(QColor(10, 100, 50, 70)); // ensure that the 0-line is visible sensorGraphs->yAxis->setRangeLower(0); sensorDataNamesGroup = new QButtonGroup(); // enable changing the displayed sensor connect(sensorDataNamesGroup, static_cast(&QButtonGroup::buttonClicked), [this](QAbstractButton *button) { selectedSensorChanged(button->objectName()); }); // show current temperature below the mouse connect(sensorGraphs, &QCustomPlot::mouseMove, this, &Ekos::Observatory::mouseOverLine); } void Observatory::weatherWarningSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherWarningDomeCB->isChecked(); actions.closeShutter = weatherWarningShutterCB->isChecked(); + // Fixme: not implemented yet + actions.stopScheduler = false; actions.delay = static_cast(weatherWarningDelaySB->value()); getWeatherModel()->setWarningActions(actions); } void Observatory::weatherAlertSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherAlertDomeCB->isChecked(); actions.closeShutter = weatherAlertShutterCB->isChecked(); + // Fixme: not implemented yet + actions.stopScheduler = false; actions.delay = static_cast(weatherAlertDelaySB->value()); getWeatherModel()->setAlertActions(actions); } void Observatory::observatoryStatusChanged(bool ready) { // statusReadyButton->setEnabled(!ready); statusReadyButton->setChecked(ready); emit newStatus(ready); } void Observatory::domeAzimuthChanged(double position) { domeAzimuthPosition->setText(QString::number(position, 'f', 2)); } void Observatory::setWarningActions(WeatherActions actions) { if (getDomeModel() != nullptr) weatherWarningDomeCB->setChecked(actions.parkDome); else weatherWarningDomeCB->setChecked(actions.parkDome); if (getDomeModel() != nullptr && getDomeModel()->hasShutter()) weatherWarningShutterCB->setChecked(actions.closeShutter); else weatherWarningShutterCB->setChecked(actions.closeShutter); weatherWarningDelaySB->setValue(static_cast(actions.delay)); } void Observatory::setAlertActions(WeatherActions actions) { if (getDomeModel() != nullptr) weatherAlertDomeCB->setChecked(actions.parkDome); else weatherAlertDomeCB->setChecked(false); if (getDomeModel() != nullptr && getDomeModel()->hasShutter()) weatherAlertShutterCB->setChecked(actions.closeShutter); else weatherAlertShutterCB->setChecked(false); weatherAlertDelaySB->setValue(static_cast(actions.delay)); } void Observatory::toggleButtons(QPushButton *buttonPressed, QString titlePressed, QPushButton *buttonCounterpart, QString titleCounterpart) { buttonPressed->setEnabled(false); buttonPressed->setText(titlePressed); buttonCounterpart->setEnabled(true); buttonCounterpart->setChecked(false); buttonCounterpart->setCheckable(false); buttonCounterpart->setText(titleCounterpart); } void Observatory::activateButton(QPushButton *button, QString title) { button->setEnabled(true); button->setCheckable(false); button->setText(title); } void Observatory::buttonPressed(QPushButton *button, QString title) { button->setEnabled(false); button->setCheckable(true); button->setChecked(true); button->setText(title); } void Observatory::statusControlSettingsChanged() { ObservatoryStatusControl control; control.useDome = useDomeCB->isChecked(); control.useShutter = useShutterCB->isChecked(); control.useWeather = useWeatherCB->isChecked(); mObservatoryModel->setStatusControl(control); } void Observatory::appendLogText(const QString &text) { m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text)); qCInfo(KSTARS_EKOS_OBSERVATORY) << text; emit newLog(text); } void Observatory::clearLog() { m_LogText.clear(); emit newLog(QString()); } } diff --git a/kstars/ekos/observatory/observatory.h b/kstars/ekos/observatory/observatory.h index b87e461f3..8008c2272 100644 --- a/kstars/ekos/observatory/observatory.h +++ b/kstars/ekos/observatory/observatory.h @@ -1,143 +1,144 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ui_observatory.h" #include "observatorymodel.h" #include "observatorydomemodel.h" #include "observatoryweathermodel.h" #include #include #include namespace Ekos { class Observatory : public QWidget, public Ui::Observatory { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Observatory") Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) public: Observatory(); ObservatoryDomeModel *getDomeModel() { return mObservatoryModel->getDomeModel(); } ObservatoryWeatherModel *getWeatherModel() { return mObservatoryModel->getWeatherModel(); } // Logging QStringList logText() { return m_LogText; } QString getLogText() { return m_LogText.join("\n"); } void clearLog(); signals: Q_SCRIPTABLE void newLog(const QString &text); /** * @brief Signal a new observatory status */ Q_SCRIPTABLE void newStatus(bool isReady); private: ObservatoryModel *mObservatoryModel = nullptr; void setDomeModel(ObservatoryDomeModel *model); void setWeatherModel(ObservatoryWeatherModel *model); // motion control void enableMotionControl(bool enabled); // slaving control void enableAutoSync(bool enabled); void showAutoSync(bool enabled); // Logging QStringList m_LogText; void appendLogText(const QString &); // timer for refreshing the observatory status QTimer weatherStatusTimer; // reacting on weather changes void setWarningActions(WeatherActions actions); void setAlertActions(WeatherActions actions); // button handling void toggleButtons(QPushButton *buttonPressed, QString titlePressed, QPushButton *buttonCounterpart, QString titleCounterpart); void activateButton(QPushButton *button, QString title); void buttonPressed(QPushButton *button, QString title); // weather sensor data QGridLayout* sensorDataBoxLayout; // map id -> (label, widget) std::map*> sensorDataWidgets = {}; // map id -> graph key x value vector std::map*> sensorGraphData = {}; // map id -> range (+1: values only > 0, 0: values > 0 and < 0; -1: values < 0) std::map sensorRanges = {}; // selected sensor for graph display QString selectedSensorID = ""; // button group for sensor names to ensure, that only one button is pressed QButtonGroup *sensorDataNamesGroup; void initSensorGraphs(); void updateSensorData(std::vector weatherData); void updateSensorGraph(QString label, QDateTime now, double value); private slots: // observatory status handling void setObseratoryStatusControl(ObservatoryStatusControl control); void statusControlSettingsChanged(); void initWeather(); void enableWeather(bool enable); void clearSensorDataHistory(); void shutdownWeather(); void setWeatherStatus(ISD::Weather::Status status); // sensor data graphs void mouseOverLine(QMouseEvent *event); + void refreshSensorGraph(); // reacting on weather changes void weatherWarningSettingsChanged(); void weatherAlertSettingsChanged(); // reacting on sensor selection change void selectedSensorChanged(QString id); // reacting on observatory status changes void observatoryStatusChanged(bool ready); void domeAzimuthChanged(double position); void initDome(); void shutdownDome(); void setDomeStatus(ISD::Dome::Status status); void setDomeParkStatus(ISD::ParkStatus status); void setShutterStatus(ISD::Dome::ShutterStatus status); }; } diff --git a/kstars/ekos/observatory/observatory.ui b/kstars/ekos/observatory/observatory.ui index 8e3fca6a5..d0d0d8478 100644 --- a/kstars/ekos/observatory/observatory.ui +++ b/kstars/ekos/observatory/observatory.ui @@ -1,1167 +1,1193 @@ Observatory 0 0 800 600 Observatory Qt::Horizontal - + 3 false 1 0 Dome 3 3 3 3 160 0 320 16777215 24 75 true 0 Qt::AlignCenter Position 3 3 3 3 false Motion false Absolute position the dome should move. 999.990000000000009 false 96 36 96 36 Move the dome to the given absolute position. Move (abs) false Relative position the dome should move. -999.990000000000009 999.990000000000009 false 96 36 96 36 Move the dome for the given degrees and direction. QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Move (rel) false 96 36 96 36 Rotate clockwise QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } &CW true false 96 36 96 36 Rotate counter clockwise QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } CCW true 3 3 3 3 Slaving Qt::Horizontal 40 20 96 36 96 36 <html><head/><body><p>Enable slaving, dome motion <span style=" font-weight:600;">follows telescope motion</span></p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Enable true 96 36 96 36 <html><head/><body><p>Disable slaving, dome <span style=" font-weight:600;">does not follow telescope motion</span>.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Disable true 3 3 3 3 false 96 36 96 36 <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Park 32 16 false false 96 36 96 36 <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } UnPark false false Qt::Horizontal 40 20 false 96 36 96 36 Abort dome motion QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Abort false 1 0 Shutter 3 3 3 3 3 Qt::Horizontal 40 20 false 96 36 72 36 <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Close 32 16 false false 96 36 72 36 <html><head/><body><p>Open the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Open false false false 1 0 Observatory Status 3 3 3 3 3 <html><head/><body><p>If selected, the dome needs to be unparked for the observatory status being &quot;READY&quot;.</p></body></html> Dome <html><head/><body><p>If selected, the shutter needs to be open for the observatory status being &quot;READY&quot;.</p></body></html> Shutter <html><head/><body><p>If selected, the weather needs to be OK for the observatory status being &quot;READY&quot;.</p></body></html> Weather false 96 36 72 36 <html><head/><body><p>Observatory status. Select the observatory elements that are relevant for the status:</p> <ul> <li><b>Dome</b>: unparked &rarr; ready</li> <li><b>Shutter</b>: open &rarr; ready</li> <li><b>Weather</b>: OK &rarr; ready</li> </ul> </body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Ready 32 16 true false false 1 0 Weather 3 3 3 3 3 - - - - - 0 - 0 - - - - - - - - - - - - 140 - 0 - - - - <html><head/><body><p>Current data of the weather sensors. Click on the sensor name to display its data over time.</p></body></html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + false Actions - + 3 3 3 3 3 - - + + - War&ning + Ale&rt true - + 3 - 0 + 3 3 0 - + 3 - - + + - Park Dome + Close Shutter - - + + - Close Shutter + Park Dome - + Stop Scheduler - + - + <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText - + Qt::Horizontal 40 20 - + Delay (sec): - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 - + - Ale&rt + War&ning true - + 3 - 3 + 0 3 0 - + 3 - - - - Close Shutter - - - - - - - Park Dome - - - - - - - Stop Scheduler - - - - + - + <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText - + Qt::Horizontal 40 20 - + Delay (sec): - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 + + + + Park Dome + + + + + + + Stop Scheduler + + + + + + + Close Shutter + + + - - + + + + + 0 + 0 + + + + + + + + + Qt::Horizontal - - QSizePolicy::Fixed - - 10 + 40 20 - + 0 0 28 28 28 28 - - - - false - + + - 24 - 24 - - - - - 24 - 24 + 140 + 0 - <html><head/><body><p>Clear sensor data history</p></body></html> - - - - - - - - - - 24 - 24 - + <html><head/><body><p>Current data of the weather sensors. Click on the sensor name to display its data over time.</p></body></html> + + + + + 9 + + + 3 + + + 9 + + + 3 + + + + + <html><head/><body><p>Scale the value axis to the current value range.</p></body></html> + + + auto scale values + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 24 + 24 + + + + + 24 + 24 + + + + <html><head/><body><p>Clear sensor data history</p></body></html> + + + + + + + .. + + + + 24 + 24 + + + + + + + Qt::Vertical 20 3 QCustomPlot QWidget
auxiliary/qcustomplot.h
1
diff --git a/kstars/ekos/observatory/observatoryweathermodel.cpp b/kstars/ekos/observatory/observatoryweathermodel.cpp index 9df8da90b..95598ad05 100644 --- a/kstars/ekos/observatory/observatoryweathermodel.cpp +++ b/kstars/ekos/observatory/observatoryweathermodel.cpp @@ -1,228 +1,235 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "observatoryweathermodel.h" #include "Options.h" #include namespace Ekos { void ObservatoryWeatherModel::initModel(Weather *weather) { weatherInterface = weather; // ensure that we start the timers if required weatherChanged(status()); connect(weatherInterface, &Weather::ready, this, &ObservatoryWeatherModel::updateWeatherStatus); connect(weatherInterface, &Weather::newStatus, this, &ObservatoryWeatherModel::weatherChanged); connect(weatherInterface, &Weather::newWeatherData, this, &ObservatoryWeatherModel::updateWeatherData); connect(weatherInterface, &Weather::disconnected, this, &ObservatoryWeatherModel::disconnected); // read the default values warningActionsActive = Options::warningActionsActive(); warningActions.parkDome = Options::weatherWarningCloseDome(); warningActions.closeShutter = Options::weatherWarningCloseShutter(); warningActions.delay = Options::weatherWarningDelay(); alertActionsActive = Options::alertActionsActive(); alertActions.parkDome = Options::weatherAlertCloseDome(); alertActions.closeShutter = Options::weatherAlertCloseShutter(); alertActions.delay = Options::weatherAlertDelay(); + m_autoScaleValues = Options::weatherAutoScaleValues(); // not implemented yet warningActions.stopScheduler = false; alertActions.stopScheduler = false; warningTimer.setInterval(static_cast(warningActions.delay * 1000)); warningTimer.setSingleShot(true); alertTimer.setInterval(static_cast(alertActions.delay * 1000)); alertTimer.setSingleShot(true); connect(&warningTimer, &QTimer::timeout, [this]() { execute(warningActions); }); connect(&alertTimer, &QTimer::timeout, [this]() { execute(alertActions); }); if (weatherInterface->status() != ISD::Weather::WEATHER_IDLE) emit ready(); } ISD::Weather::Status ObservatoryWeatherModel::status() { if (weatherInterface == nullptr) return ISD::Weather::WEATHER_IDLE; return weatherInterface->status(); } bool ObservatoryWeatherModel::refresh() { return weatherInterface->refresh(); } void ObservatoryWeatherModel::setWarningActionsActive(bool active) { warningActionsActive = active; Options::setWarningActionsActive(active); // stop warning actions if deactivated if (!active && warningTimer.isActive()) warningTimer.stop(); // start warning timer if activated else if (weatherInterface->status() == ISD::Weather::WEATHER_WARNING) startWarningTimer(); } void ObservatoryWeatherModel::startWarningTimer() { if (warningActionsActive && (warningActions.parkDome || warningActions.closeShutter || warningActions.stopScheduler)) { if (!warningTimer.isActive()) warningTimer.start(); } else if (warningTimer.isActive()) warningTimer.stop(); } void ObservatoryWeatherModel::setAlertActionsActive(bool active) { alertActionsActive = active; Options::setAlertActionsActive(active); // stop alert actions if deactivated if (!active && alertTimer.isActive()) alertTimer.stop(); // start alert timer if activated else if (weatherInterface->status() == ISD::Weather::WEATHER_ALERT) startAlertTimer(); } +void ObservatoryWeatherModel::setAutoScaleValues(bool value) +{ + m_autoScaleValues = value; + Options::setWeatherAutoScaleValues(value); +} + void ObservatoryWeatherModel::startAlertTimer() { if (alertActionsActive && (alertActions.parkDome || alertActions.closeShutter || alertActions.stopScheduler)) { if (!alertTimer.isActive()) alertTimer.start(); } else if (alertTimer.isActive()) alertTimer.stop(); } void ObservatoryWeatherModel::setWarningActions(WeatherActions actions) { warningActions = actions; Options::setWeatherWarningCloseDome(actions.parkDome); Options::setWeatherWarningCloseShutter(actions.closeShutter); Options::setWeatherWarningDelay(actions.delay); if (!warningTimer.isActive()) warningTimer.setInterval(static_cast(actions.delay * 1000)); if (weatherInterface->status() == ISD::Weather::WEATHER_WARNING) startWarningTimer(); } QString ObservatoryWeatherModel::getWarningActionsStatus() { if (warningTimer.isActive()) { int remaining = warningTimer.remainingTime() / 1000; return i18np("%1 second remaining", "%1 seconds remaining", remaining); } return i18n("Status: inactive"); } void ObservatoryWeatherModel::setAlertActions(WeatherActions actions) { alertActions = actions; Options::setWeatherAlertCloseDome(actions.parkDome); Options::setWeatherAlertCloseShutter(actions.closeShutter); Options::setWeatherAlertDelay(actions.delay); if (!alertTimer.isActive()) alertTimer.setInterval(static_cast(actions.delay * 1000)); if (weatherInterface->status() == ISD::Weather::WEATHER_ALERT) startAlertTimer(); } QString ObservatoryWeatherModel::getAlertActionsStatus() { if (alertTimer.isActive()) { int remaining = alertTimer.remainingTime() / 1000; return i18np("%1 second remaining", "%1 seconds remaining", remaining); } return i18n("Status: inactive"); } void ObservatoryWeatherModel::updateWeatherStatus() { weatherChanged(status()); emit ready(); } void ObservatoryWeatherModel::weatherChanged(ISD::Weather::Status status) { switch (status) { case ISD::Weather::WEATHER_OK: warningTimer.stop(); alertTimer.stop(); break; case ISD::Weather::WEATHER_WARNING: alertTimer.stop(); startWarningTimer(); break; case ISD::Weather::WEATHER_ALERT: warningTimer.stop(); startAlertTimer(); break; default: break; } emit newStatus(status); } void ObservatoryWeatherModel::updateWeatherData(std::vector entries) { // add or update all received values for (std::vector::iterator entry = entries.begin(); entry != entries.end(); ++entry) { // update if already existing unsigned long pos = findWeatherData(entry->name); if (pos < m_WeatherData.size()) m_WeatherData[pos].value = entry->value; // new weather sensor? else if (entry->name.startsWith("WEATHER_")) m_WeatherData.push_back({QString(entry->name), QString(entry->label), entry->value}); } // update UI emit newStatus(status()); } unsigned long ObservatoryWeatherModel::findWeatherData(const QString name) { unsigned long i; for (i = 0; i < m_WeatherData.size(); i++) { if (m_WeatherData[i].name.compare(name) == 0) return i; } // none found return i; } } // Ekos diff --git a/kstars/ekos/observatory/observatoryweathermodel.h b/kstars/ekos/observatory/observatoryweathermodel.h index 9586cb2e1..54941e911 100644 --- a/kstars/ekos/observatory/observatoryweathermodel.h +++ b/kstars/ekos/observatory/observatoryweathermodel.h @@ -1,112 +1,117 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ekos/auxiliary/weather.h" #include namespace Ekos { struct WeatherActions { bool parkDome, closeShutter, stopScheduler; uint delay; }; class ObservatoryWeatherModel : public QObject { Q_OBJECT public: ObservatoryWeatherModel() = default; void initModel(Weather *weather); ISD::Weather::Status status(); bool refresh(); /** * @brief Actions to be taken when a weather warning occurs */ WeatherActions getWarningActions() { return warningActions; } QString getWarningActionsStatus(); void setWarningActions(WeatherActions actions); bool getWarningActionsActive() { return warningActionsActive; } /** * @brief Actions to be taken when a weather alert occurs */ WeatherActions getAlertActions() { return alertActions; } QString getAlertActionsStatus(); void setAlertActions(WeatherActions actions); bool getAlertActionsActive() { return alertActionsActive; } /** * @brief Retrieve the currently known weather sensor values */ std::vector getWeatherData() { return m_WeatherData; } + /** + * @brief Flag whether the X axis should be visible in the sensor graph + */ + bool autoScaleValues() {return m_autoScaleValues;} + void setAutoScaleValues(bool show); public slots: /** * @brief Activate or deactivate the weather warning actions */ void setWarningActionsActive(bool active); /** * @brief Activate or deactivate the weather alert actions */ void setAlertActionsActive(bool active); - private: +private: Weather *weatherInterface; QTimer warningTimer, alertTimer; struct WeatherActions warningActions, alertActions; - bool warningActionsActive, alertActionsActive; + bool warningActionsActive, alertActionsActive, m_autoScaleValues; void startAlertTimer(); void startWarningTimer(); // hold all sensor data received from the weather station std::vector m_WeatherData; // update the stored values void updateWeatherData(std::vector entries); unsigned long findWeatherData(QString name); private slots: void weatherChanged(ISD::Weather::Status status); void updateWeatherStatus(); signals: void newStatus(ISD::Weather::Status status); void ready(); void disconnected(); /** * @brief signal that actions need to be taken due to weather conditions */ void execute(WeatherActions actions); }; } diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg index 58ed29e6a..840817bd4 100644 --- a/kstars/kstars.kcfg +++ b/kstars/kstars.kcfg @@ -1,2485 +1,2489 @@ ksutils.h The screen coordinates of the Time InfoBox. QPoint(0,0) The screen coordinates of the Focus InfoBox. QPoint(600,0) The screen coordinates of the Geographic Location InfoBox. QPoint(0,600) If true, the Time InfoBox will show only its top line of data. true If true, the Focus InfoBox will show only its top line of data. true If true, the Geographic Location InfoBox will show only its top line of data. true Toggles display of all three InfoBoxes. true Toggles display of the Time InfoBox. true Toggles display of the Focus InfoBox. true Toggles display of the Geographic Location InfoBox. true Is the Time InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 0 0 3 Is the Focus InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 1 0 3 Is the Geographic Location InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 2 0 3 Toggle display of the status bar. true Toggle display of the Horizontal coordinates of the mouse cursor in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the current epoch in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the standard epoch in the status bar. false true 1024 768 true Black Body List of the filenames of custom object catalogs. List of integers toggling display of each custom object catalog (any nonzero value indicates the objects in that catalog will be displayed). List of names for which custom catalogs are to be displayed. Names of objects entered into the find dialog are resolved using online services and stored in the database. This option also toggles the display of such resolved objects on the sky map. true 800 600 true true false Toggle display of crosshairs centered at telescope's pointed position in the KStars sky map. true Toggle display of INDI messages in the KStars statusbar. true false Show INDI messages as desktop notifications instead of dialogs. false true false false The default location of saved FITS files KSUtils::getDefaultPath("fitsDir") INDI server will attempt to bind with ports starting from this port 7624 INDI server will attempt to bind with ports ending with this port 9000 List of the aliases for filter wheel slots. PATH to indiserver binary KSUtils::getDefaultPath("indiServer") false PATH to indi drivers directory KSUtils::getDefaultPath("indiDriversDir") false 320 240 false false false false false false false false false false false The City name of the current geographic location. Greenwich The Province name of the current geographic location. This is the name of the state for locations in the U. S. The Country name of the current geographic location. United Kingdom The longitude of the current geographic location, in decimal degrees. 0.0 The latitude of the current geographic location, in decimal degrees. 51.468 -10.0 0.0 Two-letter code that determines the dates on which daylight savings time begins and ends (you can view the rules by pressing the "Explain DST Rules" button in the Geographic Location window). -- If true, focus changes will cause the sky to visibly spin to the new position. Otherwise, the display will "snap" instantly to the new position. true If true, clicking on the skymap will select the closest object and highlights it. false Type of cursor when exploring the sky map. 1 The names of the currently selected field-of-view indicators. The list of defined FOV indicator names is listed in the "Settings|FOV Symbols" menu. Telrad If true, trails attached to solar system bodies will fade into the background sky color. true The right ascension of the initial focus position of the sky map, in decimal hours. This value is volatile; it is reset whenever the program shuts down. 180.0 The declination of the initial focus position of the sky map, in decimal degrees. This value is volatile; it is reset whenever the program shuts down. 45.0 The name of the object that should be centered and tracked on startup. If no object should be centered, set to "nothing". This value is volatile; it is reset whenever the program shuts down. nothing True if the skymap should track on its initial position on startup. This value is volatile; it is reset whenever the program shuts down. false Toggle whether KStars should hide some objects while the display is moving, for smoother motion. true Toggle whether constellation boundaries are hidden while the display is in motion. true Toggle whether constellation lines are hidden while the display is in motion. false Choose sky culture. 11 Toggle whether constellation names are hidden while the display is in motion. false Toggle whether the coordinate grids are hidden while the display is in motion. true Toggle whether the Milky Way contour is hidden while the display is in motion. true Toggle whether IC objects are hidden while the display is in motion. true Toggle whether Messier objects are hidden while the display is in motion. true Toggle whether NGC objects are hidden while the display is in motion. true Toggle whether extra objects are hidden while the display is in motion. true Toggle whether solar system objects are hidden while the display is in motion. false Toggle whether faint stars are hidden while the display is in motion. true Toggle whether name labels are hidden while the display is in motion. true Toggle whether asteroids are drawn in the sky map. true Toggle whether asteroid name labels are drawn in the sky map. false true Toggle whether comets are drawn in the sky map. true Toggle whether comet comas are drawn in the sky map. true Toggle whether comet name labels are drawn in the sky map. false Toggle whether supernovae are drawn in the sky map. false Toggle whether supernova name labels are drawn in the sky map. false Set magnitude limit for supernovae to be shown on the skymap. 16 Toggle supernova alerts. true Set magnitude limit for supernovae to be alerted. 13 Toggle whether constellation boundaries are drawn in the sky map. false Toggle whether constellation boundary containing the central focus point is highlighted in the sky map. false Toggle whether constellation lines are drawn in the sky map. false Toggle whether constellation art drawn in the sky map. false Toggle whether constellation name labels are drawn in the sky map. false Toggle whether deep-sky objects are drawn in the sky map. true Toggle whether the ecliptic line is drawn in the sky map. false Toggle whether the equator line is drawn in the sky map. false Coordinate grids will automatically change according to active coordinate system. true Toggle whether the equatorial coordinate grid is drawn in the sky map. false Toggle whether the horizontal coordinate grid is drawn in the sky map. false Toggle whether the local meridian line is drawn in the sky map. false Toggle whether the region below the horizon is opaque. true Toggle whether the horizon line is drawn in the sky map. true Toggle whether flags are drawn in the sky map. true Toggle whether IC objects are drawn in the sky map. false Toggle whether NGC objects are drawn in the sky map. true Toggle whether Messier objects are drawn in the sky map. true Toggle whether Messier objects are rendered as images in the sky map. true Toggle whether extra objects are drawn in the sky map. true Toggle whether the Milky Way contour is drawn in the sky map. true Toggle whether the Milky Way contour is filled. When this option is false, the Milky Way is shown as an outline. true Meta-option to control whether all major planets (and the Sun and Moon) are drawn in the sky map. true Toggle whether major planets (and the Sun and Moon) are rendered as images in the sky map. true Toggle whether major planets (and the Sun and Moon) are labeled in the sky map. true Toggle whether the Sun is drawn in the sky map. true Toggle whether the Moon is drawn in the sky map. true Toggle whether Mercury is drawn in the sky map. true Toggle whether Venus is drawn in the sky map. true Toggle whether Mars is drawn in the sky map. true Toggle whether Jupiter is drawn in the sky map. true Toggle whether Saturn is drawn in the sky map. true Toggle whether Uranus is drawn in the sky map. true Toggle whether Neptune is drawn in the sky map. true Toggle whether Pluto is drawn in the sky map. true Toggle whether stars are drawn in the sky map. true Toggle whether star magnitude (brightness) labels are shown in the sky map. false Toggle whether star name labels are shown in the sky map. true Toggle whether deep-sky object magnitude (brightness) labels are shown in the sky map. false Toggle whether deep-sky object name labels are shown in the sky map. false The timescale above which slewing mode is forced on at all times. 60 The background fill mode for the on-screen information boxes: 0="no BG"; 1="semi-transparent BG"; 2="opaque BG" 1 Algorithm for the mapping projection. 0 Use official IAU abbreviations for constellation names. false Use Latin constellation names. false Use localized constellation names (if localized names are not available, default to Latin names). true Display the sky with horizontal coordinates (when false, equatorial coordinates will be used). true Toggle whether a centered object automatically gets a name label attached. true Toggle whether a centered solar system object automatically gets a trail attached, as long as it remains centered. true Toggle whether the object under the mouse cursor gets a transient name label. true Toggle whether object positions are corrected for the effects of atmospheric refraction (only applies when horizontal coordinates are used). true Toggle whether corrections due to bending of light around the sun are taken into account false Toggle whether the sky is rendered using antialiasing. Lines and shapes are smoother with antialiasing, but rendering the screen will take more time. true The zoom level, measured in pixels per radian. 250. 250. 5000000. When zooming in or out, change zoom speed factor by this multiplier. 0.2 0.01 1.0 The faint magnitude limit for drawing asteroids. 15.0 The maximum magnitude (visibility) to filter the asteroid data download from JPL. 12.000 Controls the relative number of asteroid name labels drawn in the map. 4.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed in. 16.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed out. 5.0 When enabled, objects whose magnitudes are unknown, or not available to KStars, are drawn irrespective of the faint limits set. true Sets the density of stars in the field of view 5 The faint magnitude limit for drawing stars, when the map is in motion (only applicable if faint stars are set to be hidden while the map is in motion). 5.0 The relative density for drawing star name and magnitude labels. 2.0 The relative density for drawing deep-sky object name and magnitude labels. 5.0 If true, long names (common names) for deep-sky objects are shown in the labels. false The maximum solar distance for drawing comets. 3.0 Use experimental OpenGL backend (deprecated). false The state of the clock (running or not) true Objects in the observing list will be highlighted with a symbol in the map. true Objects in the observing list will be highlighted with a colored name label in the map. false The observing list will prefer DSS imagery while downloading imagery. true The observing list will prefer SDSS imagery while downloading imagery. false Check this if you use a large Dobsonian telescope. Sorting by percentage current altitude is an easy way of determining what objects are well-placed for observation. However, when using a large Dobsonian telescope, objects close to the zenith are hard to observe. Since tracking there corresponds to a rotation in azimuth, it is both counterintuitive and requires the observer to frequently move the ladder. The region around the zenith where this is particularly frustrating is called the Dobsonian hole. This checkbox makes the observing list consider objects present in the hole as unfit for observation. false This specifies the angular radius of the Dobsonian hole, i.e. the region where a large Dobsonian telescope cannot be pointed easily. 15.00 40.00 The name of the color scheme moonless-night.colors The method for rendering stars: 0="realistic colors"; 1="solid red"; 2="solid black"; 3="solid white"; 4="solid real colors" 0 4 The color saturation level of stars (only applicable when using "realistic colors" mode). 6 10 The color for the angular-distance measurement ruler. #FFF The background color of the on-screen information boxes. #000 The text color for the on-screen information boxes, when activated by a mouse click. #F00 The normal text color of the on-screen information boxes. #FFF The color for the constellation boundary lines. #222 The color for the constellation boundary lines. #222 The color for the constellation figure lines. #555 The color for the constellation names. #AA7 The color for the cardinal compass point labels. #002 The color for the ecliptic line. #663 The color for the equator line. #FFF The color for the equatorial coordinate grid lines. #456 The color for the horizontal coordinate grid lines. #5A3 The color for objects which have extra URL links available. #A00 The color for the horizon line and opaque ground. #5A3 The color for the local meridian line. #0059b3 The color for Messier object symbols. #0F0 The color for NGC object symbols. #066 The color for IC object symbols. #439 The color for the Milky Way contour. #123 The color for star name labels. #7AA The color for deep-sky object name labels. #7AA The color for solar system object labels. #439 The color for solar system object trails. #963 The color for the sky background. #002 The color for the artificial horizon region. #C82828 The color for telescope target symbols. #8B8 Color of visible satellites. #00FF00 Color of invisible satellites. #FF0000 Color of satellites labels. #640000 Color of supernova #FFA500 The color for user-added object labels. #439 The color for RA Guide Error bar in Ekos guide module. #00FF00 The color for DEC Guide Error bar in Ekos guide module. #00A5FF The color for solver FOV box in Ekos alignment module. #FFFF00 false Xplanet binary path KSUtils::getDefaultPath("XplanetPath") Option to use a FIFO file instead of saving to the hard disk true How long to wait for XPlanet before giving up in milliseconds 1000 How long to pause between frames in the XPlanet Animation 100 Width of xplanet window 640 Height of xplanet window 480 If true, display a label in the upper right corner. false Show local time. true Show GMT instead of local time. false Specify the text of the first line of the label. By default, it says something like "Looking at Earth". Any instances of %t will be replaced by the target name, and any instances of %o will be replaced by the origin name. Specify the point size. 12 Set the color for the label. #F00 Specify the format for the date/time label. This format string is passed to strftime(3). The default is "%c %Z", which shows the date, time, and time zone in the locale’s appropriate date and time representation. %c %Z false true false false Draw a glare around the sun with a radius of the specified value larger than the Sun. The default value is 28. 28 Place the observer above a random latitude and longitude false Place the observer above the specified longitude and latitude true Render the target body as seen from above the specified latitude (in degrees). The default value is 0. 0 Place the observer above the specified longitude (in degrees). Longitude is positive going east, negative going west (for the earth and moon), so for example Los Angeles is at -118 or 242. The default value is 0. 0 The default is no projection. Multiple bodies will not be shown if this option is specified, although shadows will still be drawn. 0 Use a file as the background image, with the planet to be superimposed upon it. This option is only meaningful with the -projection option. A color may also be supplied. false Use a file as the background image. false The path of the background image. Use a color as the background. true The color of the background. #000 A star of the specified magnitude will have a pixel brightness of 1. The default value is 10. Stars will be drawn more brightly if this number is larger. 10 If checked, use an arc file to be plotted against the background stars. false Specify an arc file to be plotted against the background stars. If checked, use a config file. false Use the specified configuration file. If checked, use kstars's FOV. false If checked, use the specified marker file. false Specify a file containing user-defined marker data to display against the background stars. If checked, write coordinates of the bounding box for each marker in a file. false Write coordinates of the bounding box for each marker to this file. If checked, use star map file to draw the background stars. false Star map file path This option is only used when creating JPEG images. The quality can range from 0 to 100. The default value is 80. 80 Toggle whether satellite tracks are drawn in the sky map. false Toggle whether satellite tracks are drawn in the sky map. false If selected, satellites will be draw like stars, otherwise, draw satellites as small colored square. false Toggle whether satellite labels are drawn in the sky map. false List of selected satellites. Checking this option causes recomputation of current equatorial coordinates from catalog coordinates (i.e. application of precession, nutation and aberration corrections) for every redraw of the map. This makes processing slower when there are many stars to handle, but is more likely to be bug free. There are known bugs in the rendering of stars when this recomputation is avoided. false The default size for DSS images downloaded from the Internet. 15.0 To include parts of the star field, we add some extra padding around DSS images of deep-sky objects. This option configures the total (both sides) padding added to either dimension of the field. 10.0 Checking this option causes KStars to generate verbose debug information for diagnostic purposes. This may cause slowdown of KStars. false Checking this option causes KStars to generate regular debug information. true Checking this option causes KStars to stop generating ANY debug information. false Checking this option causes KStars log debug messages to the default output used by the platform (e.g. Standard Error). true Checking this option causes KStars log debug messages to a log file as specified. false Log FITS Data activity. false Log INDI devices activity. false Log Ekos Capture Module activity. false Log Ekos Focus Module activity. false Log Ekos Guide Module activity. false Log Ekos Alignment Module activity. false Log Ekos Mount Module activity. false Log Ekos Observatory Module activity. false true Display all captured FITS images in a single tab instead of multiple tabs per image. true Display all captured FITS images in a single FITS Viewer window. By default each camera create its own FITS Viewer instance false Display all opened FITS images in a single FITS Viewer window. true Bring the FITSViewer window to the foreground when receiving a new image. true false !KSUtils::isHardwareLimited() false !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() KSUtils::isHardwareLimited() 4 false false 40.0 0 600 600 true false true true true false Simulators false true false true 1 Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop. 0 Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop. 90.0 false If the target hour angle exceeds this value, Ekos will command a meridian flip and if successful it will resume guiding and capture operations. false true false false false 3:00 AM false 1 0 If guide deviation exceeds this limit, the exposure will be automatically aborted and only resumed when the deviation is within this limit. 2 If HFR deviation exceeds this limit, the autofocus routine will be automatically started. 0.5 false false false Sets the time interval before forced autofocus attempts during a capture sequence. 60 false If set, Ekos will capture a few flat images to determine the optimal exposure time to achieve the desired ADU value. 0 Maximum difference between measured and target ADU values to deem the value as acceptable. 1000 0 0 0 0 0.1 0 false false 2.5 true false 1 30 true !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() true 0 KSUtils::getDefaultPath("fitsDir") 60 false false false Step size of the absolute focuser. The step size TICKS should be adjusted so that when the focuser moves TICKS steps, the difference in HFR is more than 0.1 pixels. Lower the value when you are close to optimal focus. 100 Wait for this many seconds after moving the focuser before capturing the next image during AutoFocus. 0 Wait for this many seconds after resuming guide. 0 The tolerance specifies the percentage difference between the current focusing position and the minimum obtained during the focusing run. Adjustment of this value is necessary to prevent the focusing algorithm from oscillating back and forth. 1 Set the maximum travel distance of an absolute focuser. 10000 Specifies gain value of CCD when performing focusing if supported by camera. 0 Set box size to select a focus star. 64 Set horizontal binning of CCD camera while in focus mode. 1 Set vertical binning of CCD camera while in focus mode. 1 true false During full field focusing, stars which are inside this percentage of the frame are filtered out of HFR calculation (default 0%). Detection algorithms may also have an inherent filter. 0.0 During full field focusing, stars which are outside this percentage of the frame are filtered out of HFR calculation (default 100%). Detection algorithms may also have an inherent filter. 100.0 false true false 0 150 0 0 1 Specifies exposure value of CCD in seconds when performing plate solving. 1 Set binning index of CCD camera while in alignment mode. Default values 0-3 corresponding to 1x1 to 4x4 binning. 4 is max binning. 4 Use rotator when performing load and slew. false Threshold between measured and FITS position angles in arcminutes to consider the load and slew operation successful. 30 Solver type (0 ASTAP, 1 astrometry.net). 1 0 0 1 true false false 30 0 false 1500 false true true true true 1 true 2 true true true 30 false Path to astrometry.net solver location. KSUtils::getDefaultPath("AstrometrySolverBinary") false Path to astrometry.net wcsinfo location. KSUtils::getDefaultPath("AstrometryWCSInfo") false Path to astrometry.net file location. KSUtils::getDefaultPath("AstrometryConfFile") true true Folder in which the desired python executable or link to be used for astrometry.net resides. /usr/local/opt/python/libexec/bin Key to access astrometry.net online web services. You must register with astrometry.net to obtain a key. iczikaqstszeptgs http://nova.astrometry.net true 180 -1 true 1.0 0 0 localhost 4400 localhost 5656 0 1000 2 false 1 false false 3 60 10 true false false false 2 1 0 1 45 10 500 false false false 2 true true true true true true 133.33 133.33 0 0 0 0 5000 5000 100 100 0.5 2 true true false false Log Ekos Scheduler Module activity. false Sort scheduler jobs by priority and altitude. true true false false false false true false 2 true 5 30 3 0 0 0 0 0 0 0 0 1 0 false 7624 8624 300 1000 None false false false Toggle whether the HIPS sources are drawn in the sky map. false true true false false true 600 true true true 30 true true true + + + true + KSUtils::getDefaultPath("ASTAP") false 0 true 30 false 0.005 true