sdrangel/plugins/feature/map/beacon.h

239 wiersze
9.6 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);
}
// 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))
{
int id = 0;
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 && frequency && 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