mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-27 02:09:14 -05:00
d381568437
Add support for taken and predicted ground tracks. Support multiple beacons with same callsign at different locations. Use separate QML for Qt 5.14, as 5.12 doesn't support autoFadeIn, needed to view satellites at min zoom.
248 lines
9.9 KiB
C++
248 lines
9.9 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
|
// //
|
|
// 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/>. //
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef INCLUDE_BEACON_H
|
|
#define INCLUDE_BEACON_H
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <QString>
|
|
#include <QList>
|
|
|
|
#include "util/units.h"
|
|
#include "util/maidenhead.h"
|
|
#include "../../channelrx/demodadsb/csv.h"
|
|
|
|
#define IARU_BEACONS_URL "https://iaru-r1-c5-beacons.org/wp-content/uploads/beacons.csv"
|
|
|
|
struct Beacon {
|
|
|
|
QString m_callsign;
|
|
quint64 m_frequency; // In Hz
|
|
QString m_locator;
|
|
float m_latitude;
|
|
float m_longitude;
|
|
float m_altitude; // In metres above sea-level
|
|
QString m_power; // In Watts - sometimes a string with extra infos
|
|
QString m_polarization; // H or V
|
|
QString m_pattern; // Omni or 30deg etc
|
|
QString m_key; // F1A, F1B
|
|
QString m_mgm; // Machine mode
|
|
|
|
QString getText()
|
|
{
|
|
QStringList list;
|
|
list.append("Beacon");
|
|
list.append(QString("Callsign: %1").arg(m_callsign));
|
|
list.append(QString("Frequency: %1").arg(getFrequencyText()));
|
|
if (!m_power.isEmpty())
|
|
list.append(QString("Power: %1 Watts ERP").arg(m_power));
|
|
if (!m_polarization.isEmpty())
|
|
list.append(QString("Polarization: %1").arg(m_polarization));
|
|
if (!m_pattern.isEmpty())
|
|
list.append(QString("Pattern: %1").arg(m_pattern));
|
|
if (!m_key.isEmpty())
|
|
list.append(QString("Key: %1").arg(m_key));
|
|
if (!m_mgm.isEmpty())
|
|
list.append(QString("MGM: %1").arg(m_mgm));
|
|
list.append(QString("Locator: %1").arg(m_locator));
|
|
return list.join("\n");
|
|
}
|
|
|
|
QString getFrequencyText()
|
|
{
|
|
if (m_frequency > 1000000000)
|
|
return QString("%1 GHz").arg(m_frequency/1000000000.0, 0, ',', 6);
|
|
else if (m_frequency > 1000000)
|
|
return QString("%1 MHz").arg(m_frequency/1000000.0, 0, ',', 3);
|
|
else
|
|
return QString("%1 kHz").arg(m_frequency/1000.0, 0, ',', 3);
|
|
}
|
|
|
|
QString getFrequencyShortText()
|
|
{
|
|
if (m_frequency > 1000000000)
|
|
return QString("%1G").arg(m_frequency/1000000000.0, 0, ',', 1);
|
|
else if (m_frequency > 1000000)
|
|
return QString("%1M").arg(std::floor(m_frequency/1000000.0), 0, ',', 0);
|
|
else
|
|
return QString("%1k").arg(std::floor(m_frequency/1000.0), 0, ',', 0);
|
|
}
|
|
|
|
// Uses ; rather than ,
|
|
static QList<Beacon *> *readIARUCSV(const QString &filename)
|
|
{
|
|
int cnt = 0;
|
|
QList<Beacon *> *beacons = nullptr;
|
|
|
|
// Column numbers used for the data as of 2021/1/20
|
|
int callsignCol = 0;
|
|
int qrgCol = 1;
|
|
int locatorCol = 2;
|
|
int heightCol = 5;
|
|
int patternCol = 7;
|
|
int polarizationCol = 9;
|
|
int powerCol = 10;
|
|
int keyCol = 11;
|
|
int mgmCol = 12;
|
|
|
|
FILE *file;
|
|
QByteArray utfFilename = filename.toUtf8();
|
|
if ((file = fopen(utfFilename.constData(), "r")) != nullptr)
|
|
{
|
|
char row[2048];
|
|
|
|
if (fgets(row, sizeof(row), file))
|
|
{
|
|
beacons = new QList<Beacon *>();
|
|
|
|
// Read header
|
|
int idx = 0;
|
|
char *p = strtok(row, ";");
|
|
while (p != nullptr)
|
|
{
|
|
if (!strcmp(p, "Callsign"))
|
|
callsignCol = idx;
|
|
else if (!strcmp(p, "QRG"))
|
|
qrgCol = idx;
|
|
else if (!strcmp(p, "Locator"))
|
|
locatorCol = idx;
|
|
else if (!strcmp(p, "Hight ASL") || !strcmp(p, "Height ASL"))
|
|
heightCol = idx;
|
|
else if (!strcmp(p, "Pattern"))
|
|
patternCol = idx;
|
|
else if (!strcmp(p, "H/V"))
|
|
polarizationCol = idx;
|
|
else if (!strcmp(p, "Power"))
|
|
powerCol = idx;
|
|
else if (!strcmp(p, "Keying"))
|
|
keyCol = idx;
|
|
else if (!strcmp(p, "MGM"))
|
|
mgmCol = idx;
|
|
p = strtok(nullptr, ",");
|
|
idx++;
|
|
}
|
|
// Read data
|
|
while (fgets(row, sizeof(row), file))
|
|
{
|
|
char *callsign = nullptr;
|
|
size_t callsignLen = 0;
|
|
char *frequencyString = nullptr;
|
|
quint64 frequency;
|
|
char *locator = nullptr;
|
|
int height = 0;
|
|
char *heightString = nullptr;
|
|
char *pattern = nullptr;
|
|
char *polarization = nullptr;
|
|
char *power = nullptr;
|
|
char *key = nullptr;
|
|
char *mgm = nullptr;
|
|
|
|
char *q = row;
|
|
idx = 0;
|
|
while ((p = csvNext(&q, ';')) != nullptr)
|
|
{
|
|
// Read strings, stripping quotes
|
|
if ((idx == callsignCol) && (p[0] == '\"'))
|
|
{
|
|
callsign = p+1;
|
|
callsignLen = strlen(callsign)-1;
|
|
callsign[callsignLen] = '\0';
|
|
}
|
|
else if ((idx == qrgCol) && (p[0] == '\"'))
|
|
{
|
|
frequencyString = p+1;
|
|
frequencyString[strlen(frequencyString)-1] = '\0';
|
|
frequency = QString(frequencyString).toLongLong();
|
|
}
|
|
else if ((idx == locatorCol) && (p[0] == '\"'))
|
|
{
|
|
locator = p+1;
|
|
locator[strlen(locator)-1] = '\0';
|
|
}
|
|
else if ((idx == heightCol) && (p[0] == '\"'))
|
|
{
|
|
heightString = p+1;
|
|
heightString[strlen(heightString)-1] = '\0';
|
|
height = atoi(heightString);
|
|
}
|
|
else if ((idx == patternCol) && (p[0] == '\"'))
|
|
{
|
|
pattern = p+1;
|
|
pattern[strlen(pattern)-1] = '\0';
|
|
}
|
|
else if ((idx == polarizationCol) && (p[0] == '\"'))
|
|
{
|
|
polarization = p+1;
|
|
polarization[strlen(polarization)-1] = '\0';
|
|
}
|
|
else if ((idx == powerCol) && (p[0] == '\"'))
|
|
{
|
|
power = p+1;
|
|
power[strlen(power)-1] = '\0';
|
|
}
|
|
else if ((idx == keyCol) && (p[0] == '\"'))
|
|
{
|
|
key = p+1;
|
|
key[strlen(key)-1] = '\0';
|
|
}
|
|
else if ((idx == mgmCol) && (p[0] == '\"'))
|
|
{
|
|
mgm = p+1;
|
|
mgm[strlen(mgm)-1] = '\0';
|
|
}
|
|
idx++;
|
|
}
|
|
float latitude, longitude;
|
|
if (callsign && frequencyString && locator && Maidenhead::fromMaidenhead(locator, latitude, longitude))
|
|
{
|
|
Beacon *beacon = new Beacon();
|
|
beacon->m_callsign = callsign;
|
|
beacon->m_frequency = frequency * 1000; // kHz to Hz
|
|
beacon->m_locator = locator;
|
|
beacon->m_latitude = latitude;
|
|
beacon->m_longitude = longitude;
|
|
beacon->m_altitude = height;
|
|
if (!QString("omni").compare(pattern, Qt::CaseInsensitive))
|
|
beacon->m_pattern = "Omni"; // Eliminate usage of mixed case
|
|
else
|
|
beacon->m_pattern = pattern;
|
|
beacon->m_polarization = polarization;
|
|
beacon->m_power = power;
|
|
beacon->m_key = key;
|
|
beacon->m_mgm = mgm;
|
|
|
|
beacons->append(beacon);
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
fclose(file);
|
|
}
|
|
else
|
|
qDebug() << "Beacon::readIARUCSV: Failed to open " << filename;
|
|
|
|
qDebug() << "Beacon::readIARUCSV: Read " << cnt << " beacons";
|
|
|
|
return beacons;
|
|
}
|
|
|
|
};
|
|
|
|
#endif // INCLUDE_BEACON_H
|