series;
series.append(new QLineSeries());
QLineSeries *s = series.first();
QPen pen(getSeriesColor(0), 2, Qt::SolidLine);
s->setPen(pen);
qreal prevAz = polarSeries->at(0).x();
qreal prevEl = polarSeries->at(0).y();
for (int i = 1; i < polarSeries->count(); i++)
{
qreal az = polarSeries->at(i).x();
qreal el = polarSeries->at(i).y();
if ((prevAz > 270.0) && (az <= 90.0))
{
double elMid = interpolate(prevAz, prevEl, az+360.0, el, 360.0);
s->append(360.0, elMid);
series.append(new QLineSeries());
s = series.last();
s->setPen(pen);
s->append(0.0, elMid);
s->append(az, el);
}
else if ((prevAz <= 90.0) && (az > 270.0))
{
double elMid = interpolate(prevAz, prevEl, az-360.0, el, 0.0);
s->append(0.0, elMid);
series.append(new QLineSeries());
s = series.last();
s->setPen(pen);
s->append(360.0, elMid);
s->append(az, el);
}
else
s->append(polarSeries->at(i));
prevAz = az;
prevEl = el;
}
for (int i = 0; i < series.length(); i++)
{
m_azElPolarChart->addSeries(series[i]);
series[i]->attachAxis(angularAxis);
series[i]->attachAxis(radialAxis);
}
// Create series with single point, so we can plot time of rising
if (riseTime.isValid())
{
QLineSeries *riseSeries = new QLineSeries();
riseSeries->append(polarSeries->at(riseIdx));
riseSeries->setPointLabelsFormat(QString("Rise %1").arg(riseTime.time().toString("hh:mm")));
riseSeries->setPointLabelsVisible(true);
riseSeries->setPointLabelsClipping(false);
m_azElPolarChart->addSeries(riseSeries);
riseSeries->attachAxis(angularAxis);
riseSeries->attachAxis(radialAxis);
}
// Create series with single point, so we can plot time of setting
if (setTime.isValid())
{
QLineSeries *setSeries = new QLineSeries();
setSeries->append(polarSeries->at(setIdx));
setSeries->setPointLabelsFormat(QString("Set %1").arg(setTime.time().toString("hh:mm")));
setSeries->setPointLabelsVisible(true);
setSeries->setPointLabelsClipping(false);
m_azElPolarChart->addSeries(setSeries);
setSeries->attachAxis(angularAxis);
setSeries->attachAxis(radialAxis);
}
if (maxElevation < 0)
m_azElPolarChart->setTitle("Not visible from this latitude");
else
m_azElPolarChart->setTitle("");
ui->chart->setChart(m_azElPolarChart);
delete polarSeries;
delete oldChart;
}
// Find target on the Map
void StarTrackerGUI::on_viewOnMap_clicked()
{
QString target = m_settings.m_target == "Sun" || m_settings.m_target == "Moon" ? m_settings.m_target : "Star";
FeatureWebAPIUtils::mapFind(target);
}
void StarTrackerGUI::updateChartSubSelect()
{
if (ui->chartSelect->currentIndex() == 2)
{
ui->chartSubSelect->setItemText(6, QString("%1 MHz %2%3 Equatorial")
.arg((int)std::round(m_settings.m_frequency/1e6))
.arg((int)std::round(m_settings.m_beamwidth))
.arg(QChar(0xb0)));
ui->chartSubSelect->setItemText(7, QString("%1 MHz %2%3 Galactic")
.arg((int)std::round(m_settings.m_frequency/1e6))
.arg((int)std::round(m_settings.m_beamwidth))
.arg(QChar(0xb0)));
}
}
void StarTrackerGUI::on_chartSelect_currentIndexChanged(int index)
{
bool oldState = ui->chartSubSelect->blockSignals(true);
ui->chartSubSelect->clear();
if (index == 0)
{
ui->chartSubSelect->addItem("Az/El vs time");
ui->chartSubSelect->addItem("Polar");
}
else if (index == 2)
{
ui->chartSubSelect->addItem(QString("150 MHz 5%1 Equatorial").arg(QChar(0xb0)));
ui->chartSubSelect->addItem(QString("150 MHz 5%1 Galactic").arg(QChar(0xb0)));
ui->chartSubSelect->addItem("408 MHz 51' Equatorial");
ui->chartSubSelect->addItem("408 MHz 51' Galactic");
ui->chartSubSelect->addItem("1420 MHz 35' Equatorial");
ui->chartSubSelect->addItem("1420 MHz 35' Galactic");
ui->chartSubSelect->addItem("Custom Equatorial");
ui->chartSubSelect->addItem("Custom Galactic");
ui->chartSubSelect->setCurrentIndex(2);
updateChartSubSelect();
}
ui->chartSubSelect->blockSignals(oldState);
plotChart();
}
void StarTrackerGUI::on_chartSubSelect_currentIndexChanged(int index)
{
(void) index;
plotChart();
}
double StarTrackerGUI::convertSolarFluxUnits(double sfu)
{
switch (m_settings.m_solarFluxUnits)
{
case StarTrackerSettings::SFU:
return sfu;
case StarTrackerSettings::JANSKY:
return Units::solarFluxUnitsToJansky(sfu);
case StarTrackerSettings::WATTS_M_HZ:
return Units::solarFluxUnitsToWattsPerMetrePerHertz(sfu);
}
return 0.0;
}
QString StarTrackerGUI::solarFluxUnit()
{
switch (m_settings.m_solarFluxUnits)
{
case StarTrackerSettings::SFU:
return "sfu";
case StarTrackerSettings::JANSKY:
return "Jy";
case StarTrackerSettings::WATTS_M_HZ:
return "Wm^-2Hz^-1";
}
return "";
}
void StarTrackerGUI::displaySolarFlux()
{
if (((m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800) && (m_solarFlux == 0.0))
|| ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) && !m_solarFluxesValid))
ui->solarFlux->setText("");
else
{
double solarFlux;
if (m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800)
{
solarFlux = m_solarFlux;
ui->solarFlux->setToolTip(QString("Solar flux density at 2800 MHz"));
}
else if (m_settings.m_solarFluxData == StarTrackerSettings::TARGET_FREQ)
{
double freqMhz = m_settings.m_frequency/1000000.0;
const int fluxes = sizeof(m_solarFluxFrequencies)/sizeof(*m_solarFluxFrequencies);
int i;
for (i = 0; i < fluxes; i++)
{
if (freqMhz < m_solarFluxFrequencies[i])
break;
}
if (i == 0)
{
solarFlux = extrapolate(m_solarFluxFrequencies[0], m_solarFluxes[0],
m_solarFluxFrequencies[1], m_solarFluxes[1],
freqMhz
);
}
else if (i == fluxes)
{
solarFlux = extrapolate(m_solarFluxFrequencies[fluxes-2], m_solarFluxes[fluxes-2],
m_solarFluxFrequencies[fluxes-1], m_solarFluxes[fluxes-1],
freqMhz
);
}
else
{
solarFlux = interpolate(m_solarFluxFrequencies[i-1], m_solarFluxes[i-1],
m_solarFluxFrequencies[i], m_solarFluxes[i],
freqMhz
);
}
ui->solarFlux->setToolTip(QString("Solar flux density interpolated to %1 MHz").arg(freqMhz));
}
else
{
int idx = m_settings.m_solarFluxData-StarTrackerSettings::L_245;
solarFlux = m_solarFluxes[idx];
ui->solarFlux->setToolTip(QString("Solar flux density at %1 MHz").arg(m_solarFluxFrequencies[idx]));
}
ui->solarFlux->setText(QString("%1 %2").arg(convertSolarFluxUnits(solarFlux)).arg(solarFluxUnit()));
ui->solarFlux->setCursorPosition(0);
}
}
bool StarTrackerGUI::readSolarFlux()
{
QFile file(getSolarFluxFilename());
QDateTime lastModified = file.fileTime(QFileDevice::FileModificationTime);
if (QDateTime::currentDateTime().secsTo(lastModified) >= -(60*60*24))
{
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray bytes = file.readLine();
QString string(bytes);
// HHMMSS 245 410 610 1415 2695 4995 8800 15400 Mhz
// 000000 000019 000027 000037 000056 000073 000116 000202 000514 sfu
QRegExp re("([0-9]{2})([0-9]{2})([0-9]{2}) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)");
if (re.indexIn(string) != -1)
{
for (int i = 0; i < 8; i++)
m_solarFluxes[i] = re.capturedTexts()[i+4].toInt();
m_solarFluxesValid = true;
displaySolarFlux();
plotChart();
return true;
}
}
}
else
qDebug() << "StarTrackerGUI::readSolarFlux: Solar flux data is more than 1 day old";
return false;
}
void StarTrackerGUI::networkManagerFinished(QNetworkReply *reply)
{
ui->solarFlux->setText(""); // Don't show obsolete data
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "StarTrackerGUI::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
QRegExp re("\\Observed Flux Density\\<\\/th\\>\\ | ([0-9]+(\\.[0-9]+)?)\\<\\/td\\>");
if (re.indexIn(answer) != -1)
{
m_solarFlux = re.capturedTexts()[1].toDouble();
displaySolarFlux();
}
else
qDebug() << "StarTrackerGUI::networkManagerFinished - No Solar flux found: " << answer;
}
reply->deleteLater();
}
QString StarTrackerGUI::getSolarFluxFilename()
{
return HttpDownloadManager::downloadDir() + "/solar_flux.srd";
}
void StarTrackerGUI::updateSolarFlux(bool all)
{
qDebug() << "StarTrackerGUI: Updating Solar flux data";
if ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) || all)
{
QDate today = QDateTime::currentDateTimeUtc().date();
QString solarFluxFile = getSolarFluxFilename();
if (m_dlm.confirmDownload(solarFluxFile, nullptr, 1))
{
QString urlString = QString("https://www.sws.bom.gov.au/Category/World Data Centre/Data Display and Download/Solar Radio/station/learmonth/SRD/%1/L%2.SRD")
.arg(today.year()).arg(today.toString("yyMMdd"));
m_dlm.download(QUrl(urlString), solarFluxFile, this);
}
}
if ((m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800) || all)
{
m_networkRequest.setUrl(QUrl("https://www.spaceweather.gc.ca/forecast-prevision/solar-solaire/solarflux/sx-4-en.php"));
m_networkManager->get(m_networkRequest);
}
}
void StarTrackerGUI::autoUpdateSolarFlux()
{
updateSolarFlux(false);
}
void StarTrackerGUI::on_downloadSolarFlux_clicked()
{
updateSolarFlux(true);
}
void StarTrackerGUI::on_darkTheme_clicked(bool checked)
{
m_settings.m_chartsDarkTheme = checked;
m_solarFluxChart.setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
m_chart.setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
plotChart();
applySettings();
}
void StarTrackerGUI::downloadFinished(const QString& filename, bool success)
{
(void) filename;
if (success)
readSolarFlux();
}
|