mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-08-01 13:32:33 -04:00
431 lines
10 KiB
C++
431 lines
10 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
// Copyright (C) 2025 Jon Beniston, M7RCE <jon@beniston.com> //
|
||
|
// //
|
||
|
// This program 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 as version 3 of the License, or //
|
||
|
// (at your option) any later version. //
|
||
|
// //
|
||
|
// This program is distributed in the hope that it will be useful, //
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||
|
// GNU General Public License V3 for more details. //
|
||
|
// //
|
||
|
// You should have received a copy of the GNU General Public License //
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "aurora.h"
|
||
|
|
||
|
#include <QDebug>
|
||
|
#include <QUrl>
|
||
|
#include <QNetworkReply>
|
||
|
#include <QNetworkDiskCache>
|
||
|
|
||
|
// Green -> Yellow -> Red
|
||
|
const unsigned char Aurora::m_colorMap[] = {
|
||
|
0, 255, 0,
|
||
|
1, 255, 0,
|
||
|
3, 255, 0,
|
||
|
5, 255, 0,
|
||
|
7, 255, 0,
|
||
|
9, 255, 0,
|
||
|
11, 255, 0,
|
||
|
13, 255, 0,
|
||
|
15, 255, 0,
|
||
|
17, 255, 0,
|
||
|
19, 255, 0,
|
||
|
21, 255, 0,
|
||
|
23, 255, 0,
|
||
|
25, 255, 0,
|
||
|
27, 255, 0,
|
||
|
29, 255, 0,
|
||
|
31, 255, 0,
|
||
|
33, 255, 0,
|
||
|
35, 255, 0,
|
||
|
37, 255, 0,
|
||
|
39, 255, 0,
|
||
|
41, 255, 0,
|
||
|
43, 255, 0,
|
||
|
45, 255, 0,
|
||
|
47, 255, 0,
|
||
|
49, 255, 0,
|
||
|
51, 255, 0,
|
||
|
53, 255, 0,
|
||
|
55, 255, 0,
|
||
|
57, 255, 0,
|
||
|
59, 255, 0,
|
||
|
61, 255, 0,
|
||
|
63, 255, 0,
|
||
|
65, 255, 0,
|
||
|
67, 255, 0,
|
||
|
69, 255, 0,
|
||
|
71, 255, 0,
|
||
|
73, 255, 0,
|
||
|
75, 255, 0,
|
||
|
77, 255, 0,
|
||
|
79, 255, 0,
|
||
|
81, 255, 0,
|
||
|
83, 255, 0,
|
||
|
85, 255, 0,
|
||
|
87, 255, 0,
|
||
|
89, 255, 0,
|
||
|
91, 255, 0,
|
||
|
93, 255, 0,
|
||
|
95, 255, 0,
|
||
|
97, 255, 0,
|
||
|
99, 255, 0,
|
||
|
101, 255, 0,
|
||
|
103, 255, 0,
|
||
|
105, 255, 0,
|
||
|
107, 255, 0,
|
||
|
109, 255, 0,
|
||
|
111, 255, 0,
|
||
|
113, 255, 0,
|
||
|
115, 255, 0,
|
||
|
117, 255, 0,
|
||
|
119, 255, 0,
|
||
|
121, 255, 0,
|
||
|
123, 255, 0,
|
||
|
125, 255, 0,
|
||
|
127, 255, 0,
|
||
|
129, 255, 0,
|
||
|
131, 255, 0,
|
||
|
133, 255, 0,
|
||
|
135, 255, 0,
|
||
|
137, 255, 0,
|
||
|
139, 255, 0,
|
||
|
141, 255, 0,
|
||
|
143, 255, 0,
|
||
|
145, 255, 0,
|
||
|
147, 255, 0,
|
||
|
149, 255, 0,
|
||
|
151, 255, 0,
|
||
|
153, 255, 0,
|
||
|
155, 255, 0,
|
||
|
157, 255, 0,
|
||
|
159, 255, 0,
|
||
|
161, 255, 0,
|
||
|
163, 255, 0,
|
||
|
165, 255, 0,
|
||
|
167, 255, 0,
|
||
|
169, 255, 0,
|
||
|
171, 255, 0,
|
||
|
173, 255, 0,
|
||
|
175, 255, 0,
|
||
|
177, 255, 0,
|
||
|
179, 255, 0,
|
||
|
181, 255, 0,
|
||
|
183, 255, 0,
|
||
|
185, 255, 0,
|
||
|
187, 255, 0,
|
||
|
189, 255, 0,
|
||
|
191, 255, 0,
|
||
|
193, 255, 0,
|
||
|
195, 255, 0,
|
||
|
197, 255, 0,
|
||
|
199, 255, 0,
|
||
|
201, 255, 0,
|
||
|
203, 255, 0,
|
||
|
205, 255, 0,
|
||
|
207, 255, 0,
|
||
|
209, 255, 0,
|
||
|
211, 255, 0,
|
||
|
213, 255, 0,
|
||
|
215, 255, 0,
|
||
|
217, 255, 0,
|
||
|
219, 255, 0,
|
||
|
221, 255, 0,
|
||
|
223, 255, 0,
|
||
|
225, 255, 0,
|
||
|
227, 255, 0,
|
||
|
229, 255, 0,
|
||
|
231, 255, 0,
|
||
|
233, 255, 0,
|
||
|
235, 255, 0,
|
||
|
237, 255, 0,
|
||
|
239, 255, 0,
|
||
|
241, 255, 0,
|
||
|
243, 255, 0,
|
||
|
245, 255, 0,
|
||
|
247, 255, 0,
|
||
|
249, 255, 0,
|
||
|
251, 255, 0,
|
||
|
253, 255, 0,
|
||
|
255, 255, 0,
|
||
|
255, 253, 0,
|
||
|
255, 251, 0,
|
||
|
255, 249, 0,
|
||
|
255, 247, 0,
|
||
|
255, 245, 0,
|
||
|
255, 243, 0,
|
||
|
255, 241, 0,
|
||
|
255, 239, 0,
|
||
|
255, 237, 0,
|
||
|
255, 235, 0,
|
||
|
255, 233, 0,
|
||
|
255, 231, 0,
|
||
|
255, 229, 0,
|
||
|
255, 227, 0,
|
||
|
255, 225, 0,
|
||
|
255, 223, 0,
|
||
|
255, 221, 0,
|
||
|
255, 219, 0,
|
||
|
255, 217, 0,
|
||
|
255, 215, 0,
|
||
|
255, 213, 0,
|
||
|
255, 211, 0,
|
||
|
255, 209, 0,
|
||
|
255, 207, 0,
|
||
|
255, 205, 0,
|
||
|
255, 203, 0,
|
||
|
255, 201, 0,
|
||
|
255, 199, 0,
|
||
|
255, 197, 0,
|
||
|
255, 195, 0,
|
||
|
255, 193, 0,
|
||
|
255, 191, 0,
|
||
|
255, 189, 0,
|
||
|
255, 187, 0,
|
||
|
255, 185, 0,
|
||
|
255, 183, 0,
|
||
|
255, 181, 0,
|
||
|
255, 179, 0,
|
||
|
255, 177, 0,
|
||
|
255, 175, 0,
|
||
|
255, 173, 0,
|
||
|
255, 171, 0,
|
||
|
255, 169, 0,
|
||
|
255, 167, 0,
|
||
|
255, 165, 0,
|
||
|
255, 163, 0,
|
||
|
255, 161, 0,
|
||
|
255, 159, 0,
|
||
|
255, 157, 0,
|
||
|
255, 155, 0,
|
||
|
255, 153, 0,
|
||
|
255, 151, 0,
|
||
|
255, 149, 0,
|
||
|
255, 147, 0,
|
||
|
255, 145, 0,
|
||
|
255, 143, 0,
|
||
|
255, 141, 0,
|
||
|
255, 139, 0,
|
||
|
255, 137, 0,
|
||
|
255, 135, 0,
|
||
|
255, 133, 0,
|
||
|
255, 131, 0,
|
||
|
255, 129, 0,
|
||
|
255, 127, 0,
|
||
|
255, 125, 0,
|
||
|
255, 123, 0,
|
||
|
255, 121, 0,
|
||
|
255, 119, 0,
|
||
|
255, 117, 0,
|
||
|
255, 115, 0,
|
||
|
255, 113, 0,
|
||
|
255, 111, 0,
|
||
|
255, 109, 0,
|
||
|
255, 107, 0,
|
||
|
255, 105, 0,
|
||
|
255, 103, 0,
|
||
|
255, 101, 0,
|
||
|
255, 99, 0,
|
||
|
255, 97, 0,
|
||
|
255, 95, 0,
|
||
|
255, 93, 0,
|
||
|
255, 91, 0,
|
||
|
255, 89, 0,
|
||
|
255, 87, 0,
|
||
|
255, 85, 0,
|
||
|
255, 83, 0,
|
||
|
255, 81, 0,
|
||
|
255, 79, 0,
|
||
|
255, 77, 0,
|
||
|
255, 75, 0,
|
||
|
255, 73, 0,
|
||
|
255, 71, 0,
|
||
|
255, 69, 0,
|
||
|
255, 67, 0,
|
||
|
255, 65, 0,
|
||
|
255, 63, 0,
|
||
|
255, 61, 0,
|
||
|
255, 59, 0,
|
||
|
255, 57, 0,
|
||
|
255, 55, 0,
|
||
|
255, 53, 0,
|
||
|
255, 51, 0,
|
||
|
255, 49, 0,
|
||
|
255, 47, 0,
|
||
|
255, 45, 0,
|
||
|
255, 43, 0,
|
||
|
255, 41, 0,
|
||
|
255, 39, 0,
|
||
|
255, 37, 0,
|
||
|
255, 35, 0,
|
||
|
255, 33, 0,
|
||
|
255, 31, 0,
|
||
|
255, 29, 0,
|
||
|
255, 27, 0,
|
||
|
255, 25, 0,
|
||
|
255, 23, 0,
|
||
|
255, 21, 0,
|
||
|
255, 19, 0,
|
||
|
255, 17, 0,
|
||
|
255, 15, 0,
|
||
|
255, 13, 0,
|
||
|
255, 11, 0,
|
||
|
255, 9, 0,
|
||
|
255, 7, 0,
|
||
|
255, 5, 0,
|
||
|
255, 3, 0,
|
||
|
255, 1, 0,
|
||
|
};
|
||
|
|
||
|
Aurora::Aurora()
|
||
|
{
|
||
|
connect(&m_dataTimer, &QTimer::timeout, this, &Aurora::getData);
|
||
|
m_networkManager = new QNetworkAccessManager();
|
||
|
connect(m_networkManager, &QNetworkAccessManager::finished, this, &Aurora::handleReply);
|
||
|
|
||
|
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||
|
QDir writeableDir(locations[0]);
|
||
|
if (!writeableDir.mkpath(QStringLiteral("cache") + QDir::separator() + QStringLiteral("aurora"))) {
|
||
|
qDebug() << "Failed to create cache/aurora";
|
||
|
}
|
||
|
|
||
|
m_cache = new QNetworkDiskCache();
|
||
|
m_cache->setCacheDirectory(locations[0] + QDir::separator() + QStringLiteral("cache") + QDir::separator() + QStringLiteral("giro"));
|
||
|
m_cache->setMaximumCacheSize(100000000);
|
||
|
m_networkManager->setCache(m_cache);
|
||
|
}
|
||
|
|
||
|
Aurora::~Aurora()
|
||
|
{
|
||
|
disconnect(&m_dataTimer, &QTimer::timeout, this, &Aurora::getData);
|
||
|
disconnect(m_networkManager, &QNetworkAccessManager::finished, this, &Aurora::handleReply);
|
||
|
delete m_networkManager;
|
||
|
}
|
||
|
|
||
|
Aurora* Aurora::create(const QString& service)
|
||
|
{
|
||
|
if (service == "noaa.gov")
|
||
|
{
|
||
|
return new Aurora();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::create: Unsupported service: " << service;
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Aurora::getDataPeriodically(int periodInMins)
|
||
|
{
|
||
|
if (periodInMins > 0)
|
||
|
{
|
||
|
m_dataTimer.setInterval(periodInMins*60*1000);
|
||
|
m_dataTimer.start();
|
||
|
getData();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_dataTimer.stop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Aurora::getData()
|
||
|
{
|
||
|
QUrl url(QString("https://services.swpc.noaa.gov/json/ovation_aurora_latest.json"));
|
||
|
m_networkManager->get(QNetworkRequest(url));
|
||
|
}
|
||
|
|
||
|
void Aurora::handleReply(QNetworkReply* reply)
|
||
|
{
|
||
|
if (reply)
|
||
|
{
|
||
|
if (!reply->error())
|
||
|
{
|
||
|
QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
|
||
|
|
||
|
QString fileName = reply->url().fileName();
|
||
|
if (fileName == "ovation_aurora_latest.json")
|
||
|
{
|
||
|
handleJSON(document);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleReply: unexpected filename: " << fileName;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleReply: error: " << reply->error();
|
||
|
}
|
||
|
reply->deleteLater();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleReply: reply is null";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Aurora::handleJSON(QJsonDocument& document)
|
||
|
{
|
||
|
if (document.isObject())
|
||
|
{
|
||
|
QJsonObject obj = document.object();
|
||
|
|
||
|
if (obj.contains(QStringLiteral("coordinates")))
|
||
|
{
|
||
|
QJsonArray array = obj.value(QStringLiteral("coordinates")).toArray();
|
||
|
|
||
|
// Longitude: [0, 359]
|
||
|
// Latitude: [-90, 90] including 0
|
||
|
QImage image(360, 181, QImage::Format_ARGB32);
|
||
|
image.fill(qRgba(0, 0, 0, 0));
|
||
|
|
||
|
for (auto valRef : array)
|
||
|
{
|
||
|
if (valRef.isArray())
|
||
|
{
|
||
|
QJsonArray coords = valRef.toArray();
|
||
|
|
||
|
if (coords.size() == 3)
|
||
|
{
|
||
|
int longitude = coords[0].toInt();
|
||
|
int latitude = coords[1].toInt();
|
||
|
int probabilty = coords[2].toInt();
|
||
|
const int min = 5; // Don't display anything for <5% probabilty
|
||
|
const int alphaMax = 180; // Always slightly transparent
|
||
|
|
||
|
if ((probabilty > min) && (std::abs(latitude) > 5)) // Ignore data around equator, as can incorrectly predict aurora
|
||
|
{
|
||
|
// Scale from 5%-100% to 256 entry colormap
|
||
|
int index = (int) ((probabilty - min) / (100.0f - min) * 255.0f);
|
||
|
// Make lowest probababilties a bit more transparent
|
||
|
int alpha = index < 25 ? (int)((index / 25.0f) * alphaMax) : alphaMax;
|
||
|
//qDebug() << probabilty << index << alpha;
|
||
|
image.setPixel((longitude + 180) % 360, 180 - (latitude + 90), qRgba(m_colorMap[index*3], m_colorMap[index*3+1], m_colorMap[index*3+2], alpha));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleJSON: Expected coordinates array to be of length 3";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
emit dataUpdated(image);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleJSON: No coordinates";
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
qDebug() << "Aurora::handleJSON: Expected an object";
|
||
|
}
|
||
|
}
|