1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-01 21:54:55 -04:00

Remote TCP updates:

Add support for public list of SDRangel servers that can be displayed on Map.
Add FLAC and zlib IQ compression.
Add IQ squelch for compression.
Add remote device/antenna position and direction reporting.
Add text messaging.
This commit is contained in:
srcejon
2024-09-22 10:44:30 +01:00
parent 7d1beb9eff
commit 8bf1a2f803
41 changed files with 7181 additions and 800 deletions
+225 -58
View File
@@ -295,6 +295,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
connect(&m_kiwiSDRList, &KiwiSDRList::dataUpdated, this, &MapGUI::kiwiSDRUpdated);
connect(&m_spyServerList, &SpyServerList::dataUpdated, this, &MapGUI::spyServerUpdated);
connect(&m_sdrangelServerList, &SDRangelServerList::dataUpdated, this, &MapGUI::sdrangelServerUpdated);
#ifdef QT_WEBENGINE_FOUND
QWebEngineSettings *settings = ui->web->settings();
@@ -309,6 +310,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
connect(profile, &QWebEngineProfile::downloadRequested, this, &MapGUI::downloadRequested);
#endif
qDebug() << "Get station position";
// Get station position
float stationLatitude = MainCore::instance()->getSettings().getLatitude();
float stationLongitude = MainCore::instance()->getSettings().getLongitude();
@@ -320,6 +322,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
m_polygonMapFilter.setPosition(stationPosition);
m_polylineMapFilter.setPosition(stationPosition);
qDebug() << "Centre map";
// Centre map at My Position
QQuickItem *item = ui->map->rootObject();
QObject *object = item->findChild<QObject*>("map");
@@ -331,6 +334,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
object->setProperty("center", QVariant::fromValue(coords));
}
qDebug() << "Creating antenna";
// Create antenna at My Position
m_antennaMapItem.setName(new QString("Station"));
m_antennaMapItem.setLatitude(stationLatitude);
@@ -358,7 +362,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
setBeacons(beacons);
}
addIBPBeacons();
addNAT();
addRadioTimeTransmitters();
addRadar();
addIonosonde();
@@ -371,6 +375,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
addVLF();
addKiwiSDR();
addSpyServer();
addSDRangelServer();
displaySettings();
applySettings(true);
@@ -490,6 +495,44 @@ void MapGUI::addIBPBeacons()
}
}
// https://www.icao.int/EURNAT/EUR%20and%20NAT%20Documents/NAT%20Documents/NAT%20Documents/NAT%20Doc%20003/NAT%20Doc003%20-%20HF%20Guidance%20v3.0.0_2015.pdf
// Coords aren't precise
const QList<RadioTimeTransmitter> MapGUI::m_natTransmitters = {
{"Bodo", 0, 67.26742f, 14.34990, 0},
{"Gander", 0, 48.993056f, -54.674444f, 0},
{"Iceland", 0, 64.08516f, -21.84531f, 0},
{"New York", 0, 40.881111f, -72.647778f, 0},
{"Santa Maria", 0, 36.995556f, -25.170556, 0},
{"Shanwick", 0, 52.75f, -8.933333f, 0},
};
// North Atlantic HF ATC ground stations
void MapGUI::addNAT()
{
for (int i = 0; i < m_natTransmitters.size(); i++)
{
SWGSDRangel::SWGMapItem natMapItem;
// Need to suffix frequency, as there are multiple becaons with same callsign at different locations
QString name = QString("%1").arg(m_natTransmitters[i].m_callsign);
natMapItem.setName(new QString(name));
natMapItem.setLatitude(m_natTransmitters[i].m_latitude);
natMapItem.setLongitude(m_natTransmitters[i].m_longitude);
natMapItem.setAltitude(0.0);
natMapItem.setImage(new QString("antenna.png"));
natMapItem.setImageRotation(0);
QString text = QString("NAT ATC Transmitter\nCallsign: %1")
.arg(m_natTransmitters[i].m_callsign);
natMapItem.setText(new QString(text));
natMapItem.setModel(new QString("antenna.glb"));
natMapItem.setFixedPosition(true);
natMapItem.setOrientation(0);
natMapItem.setLabel(new QString(name));
natMapItem.setLabelAltitudeOffset(4.5);
natMapItem.setAltitudeReference(1);
update(m_map, &natMapItem, "NAT ATC Transmitters");
}
}
void MapGUI::addVLF()
{
for (int i = 0; i < VLFTransmitters::m_transmitters.size(); i++)
@@ -699,6 +742,75 @@ void MapGUI::spyServerUpdated(const QList<SpyServerList::SpyServer>& sdrs)
}
}
void MapGUI::addSDRangelServer()
{
m_sdrangelServerList.getDataPeriodically();
}
void MapGUI::sdrangelServerUpdated(const QList<SDRangelServerList::SDRangelServer>& sdrs)
{
for (const auto& sdr : sdrs)
{
SWGSDRangel::SWGMapItem sdrangelServerMapItem;
QString address = QString("%1:%2").arg(sdr.m_address).arg(sdr.m_port);
sdrangelServerMapItem.setName(new QString(address));
sdrangelServerMapItem.setLatitude(sdr.m_latitude);
sdrangelServerMapItem.setLongitude(sdr.m_longitude);
sdrangelServerMapItem.setAltitude(sdr.m_altitude);
sdrangelServerMapItem.setImage(new QString("antennaangel.png"));
sdrangelServerMapItem.setImageRotation(0);
QStringList antenna;
if (!sdr.m_antenna.isEmpty()) {
antenna.append(sdr.m_antenna);
}
if (sdr.m_isotropic) {
antenna.append("Isotropic");
} else {
antenna.append(QString("Az: %1%3 El: %2%3").arg(sdr.m_azimuth).arg(sdr.m_elevation).arg(QChar(0x00b0)));
}
QString text = QString("SDRangel\n\nStation: %1\nDevice: %2\nAntenna: %3\nFrequency: %4 - %5\nRemote control: %6\nUsers: %7/%8")
.arg(sdr.m_stationName)
.arg(sdr.m_device)
.arg(antenna.join(" - "))
.arg(formatFrequency(sdr.m_minFrequency))
.arg(formatFrequency(sdr.m_maxFrequency))
.arg(sdr.m_remoteControl ? "Yes" : "No")
.arg(sdr.m_clients)
.arg(sdr.m_maxClients)
;
if (sdr.m_timeLimit > 0) {
text.append(QString("\nTime limit: %1 mins").arg(sdr.m_timeLimit));
}
QString url = QString("sdrangel-server://%1").arg(address);
QString link = QString("<a href=%1 onclick=\"return parent.infoboxLink('%1')\">%2</a>").arg(url).arg(address);
text.append(QString("\nURL: %1").arg(link));
sdrangelServerMapItem.setText(new QString(text));
sdrangelServerMapItem.setModel(new QString("antenna.glb"));
sdrangelServerMapItem.setFixedPosition(true);
sdrangelServerMapItem.setOrientation(0);
QStringList bands;
if (sdr.m_minFrequency < 30000000) {
bands.append("HF");
}
if ((sdr.m_minFrequency < 300000000) && (sdr.m_maxFrequency > 30000000)) {
bands.append("VHF");
}
if ((sdr.m_minFrequency < 3000000000) && (sdr.m_maxFrequency > 300000000)) {
bands.append("UHF");
}
if (sdr.m_maxFrequency > 3000000000) {
bands.append("SHF");
}
QString label = QString("SDRangel %1").arg(bands.join(" "));
sdrangelServerMapItem.setLabel(new QString(label));
sdrangelServerMapItem.setLabelAltitudeOffset(4.5);
sdrangelServerMapItem.setAltitudeReference(1);
update(m_map, &sdrangelServerMapItem, "SDRangel");
}
}
// Ionosonde stations
void MapGUI::addIonosonde()
{
@@ -1582,8 +1694,13 @@ void MapGUI::applyMap2DSettings(bool reloadMap)
if (!m_settings.m_osmURL.isEmpty()) {
parameters["osm.mapping.custom.host"] = m_settings.m_osmURL; // E.g: "http://a.tile.openstreetmap.fr/hot/"
}
#ifdef __EMSCRIPTEN__
// Default is http://maps-redirect.qt.io/osm/5.8/ and Emscripten needs https
parameters["osm.mapping.providersrepository.address"] = QString("https://sdrangel.beniston.com/sdrangel/maps/");
#else
// Use our repo, so we can append API key
parameters["osm.mapping.providersrepository.address"] = QString("http://127.0.0.1:%1/").arg(m_osmPort);
#endif
// Use application specific cache, as other apps may not use API key so will have different images
QString cachePath = osmCachePath();
parameters["osm.mapping.cache.directory"] = cachePath;
@@ -1685,6 +1802,9 @@ void MapGUI::displayToolbar()
bool narrow = this->screen()->availableGeometry().width() < 400;
ui->layersMenu->setVisible(narrow);
bool overlayButtons = !narrow && ((m_settings.m_mapProvider == "osm") || m_settings.m_map3DEnabled);
#ifdef __EMSCRIPTEN__
overlayButtons = false;
#endif
ui->displayRain->setVisible(overlayButtons);
ui->displayClouds->setVisible(overlayButtons);
ui->displaySeaMarks->setVisible(overlayButtons);
@@ -2600,12 +2720,16 @@ void MapGUI::linkClicked(const QString& url)
QString spyServerURL = url.mid(21);
openSpyServer(spyServerURL);
}
else if (url.startsWith("sdrangel-server://"))
{
QString sdrangelServerURL = url.mid(18);
openSDRangelServer(sdrangelServerURL);
}
}
// Open a KiwiSDR RX device
void MapGUI::openKiwiSDR(const QString& url)
bool MapGUI::openKiwiSDRInput()
{
// Create DeviceSet
// Create DeviceSet
MainCore *mainCore = MainCore::instance();
unsigned int deviceSetIndex = mainCore->getDeviceSets().size();
MainCore::MsgAddDeviceSet *msg = MainCore::MsgAddDeviceSet::create(0);
@@ -2634,39 +2758,43 @@ void MapGUI::openKiwiSDR(const QString& url)
if (!found)
{
qCritical() << "MapGUI::openKiwiSDR: Failed to find KiwiSDR";
return;
return false;
}
// Wait until device is created - is there a better way?
DeviceSet *deviceSet = nullptr;
do
{
QTime dieTime = QTime::currentTime().addMSecs(100);
while (QTime::currentTime() < dieTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (mainCore->getDeviceSets().size() > deviceSetIndex)
{
deviceSet = mainCore->getDeviceSets()[deviceSetIndex];
}
}
while (!deviceSet);
// Move to same workspace
//getWorkspaceIndex();
// Set address setting
QStringList deviceSettingsKeys = {"serverAddress"};
SWGSDRangel::SWGDeviceSettings response;
response.init();
SWGSDRangel::SWGKiwiSDRSettings *deviceSettings = response.getKiwiSdrSettings();
deviceSettings->setServerAddress(new QString(url));
QString errorMessage;
deviceSet->m_deviceAPI->getSampleSource()->webapiSettingsPutPatch(false, deviceSettingsKeys, response, errorMessage);
return true;
}
// Open a RemoteTCPInput device to use for SpyServer
void MapGUI::openSpyServer(const QString& url)
// Open a KiwiSDR RX device
void MapGUI::openKiwiSDR(const QString& url)
{
m_remoteDeviceAddress = url;
connect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::kiwiSDRDeviceSetAdded);
if (!openKiwiSDRInput()) {
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::kiwiSDRDeviceSetAdded);
}
}
void MapGUI::kiwiSDRDeviceSetAdded(int index, DeviceAPI *device)
{
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::kiwiSDRDeviceSetAdded);
// FIXME: Doesn't work if we do it immediately. Settings overwritten?
QTimer::singleShot(200, [=] {
// Set address setting
QStringList deviceSettingsKeys = {"serverAddress"};
SWGSDRangel::SWGDeviceSettings response;
response.init();
SWGSDRangel::SWGKiwiSDRSettings *deviceSettings = response.getKiwiSdrSettings();
deviceSettings->setServerAddress(new QString(m_remoteDeviceAddress));
QString errorMessage;
device->getSampleSource()->webapiSettingsPutPatch(false, deviceSettingsKeys, response, errorMessage);
});
}
bool MapGUI::openRemoteTCPInput()
{
// Create DeviceSet
MainCore *mainCore = MainCore::instance();
@@ -2696,39 +2824,79 @@ void MapGUI::openSpyServer(const QString& url)
}
if (!found)
{
qCritical() << "MapGUI::openSpyServer: Failed to find RemoteTCPInput";
return;
qCritical() << "MapGUI::openRemoteTCPInput: Failed to find RemoteTCPInput";
return false;
}
// Wait until device is created - is there a better way?
DeviceSet *deviceSet = nullptr;
do
{
QTime dieTime = QTime::currentTime().addMSecs(100);
while (QTime::currentTime() < dieTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (mainCore->getDeviceSets().size() > deviceSetIndex)
{
deviceSet = mainCore->getDeviceSets()[deviceSetIndex];
}
}
while (!deviceSet);
// Move to same workspace
//getWorkspaceIndex();
// Set address/port setting
return true;
}
// Open a RemoteTCPInput device to use for SpyServer
void MapGUI::openSpyServer(const QString& url)
{
QStringList address = url.split(":");
QStringList deviceSettingsKeys = {"dataAddress", "dataPort", "protocol"};
SWGSDRangel::SWGDeviceSettings response;
response.init();
SWGSDRangel::SWGRemoteTCPInputSettings *deviceSettings = response.getRemoteTcpInputSettings();
deviceSettings->setDataAddress(new QString(address[0]));
deviceSettings->setDataPort(address[1].toInt());
deviceSettings->setProtocol(new QString("Spy Server"));
QString errorMessage;
deviceSet->m_deviceAPI->getSampleSource()->webapiSettingsPutPatch(false, deviceSettingsKeys, response, errorMessage);
m_remoteDeviceAddress = address[0];
m_remoteDevicePort = address[1].toInt();
connect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::spyServerDeviceSetAdded);
if (!openRemoteTCPInput()) {
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::spyServerDeviceSetAdded);
}
}
void MapGUI::spyServerDeviceSetAdded(int index, DeviceAPI *device)
{
qDebug() << "**************** MapGUI::spyServerDeviceSetAdded";
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::spyServerDeviceSetAdded);
// FIXME: Doesn't work if we do it immediately. Settings overwritten?
QTimer::singleShot(200, [=] {
// Set address/port setting
QStringList deviceSettingsKeys = {"dataAddress", "dataPort", "protocol", "overrideRemoteSettings"};
SWGSDRangel::SWGDeviceSettings response;
response.init();
SWGSDRangel::SWGRemoteTCPInputSettings *deviceSettings = response.getRemoteTcpInputSettings();
deviceSettings->setDataAddress(new QString(m_remoteDeviceAddress));
deviceSettings->setDataPort(m_remoteDevicePort);
deviceSettings->setProtocol(new QString("Spy Server"));
deviceSettings->setOverrideRemoteSettings(false);
QString errorMessage;
device->getSampleSource()->webapiSettingsPutPatch(false, deviceSettingsKeys, response, errorMessage);
});
}
// Open a RemoteTCPInput device to use for SDRangel
void MapGUI::openSDRangelServer(const QString& url)
{
QStringList address = url.split(":");
m_remoteDeviceAddress = address[0];
m_remoteDevicePort = address[1].toInt();
connect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::sdrangelServerDeviceSetAdded);
if (!openRemoteTCPInput()) {
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::sdrangelServerDeviceSetAdded);
}
}
void MapGUI::sdrangelServerDeviceSetAdded(int index, DeviceAPI *device)
{
disconnect(MainCore::instance(), &MainCore::deviceSetAdded, this, &MapGUI::sdrangelServerDeviceSetAdded);
// FIXME: Doesn't work if we do it immediately. Settings overwritten?
QTimer::singleShot(200, [=] {
// Set address/port setting
QStringList deviceSettingsKeys = {"dataAddress", "dataPort", "protocol", "overrideRemoteSettings"};
SWGSDRangel::SWGDeviceSettings response;
response.init();
SWGSDRangel::SWGRemoteTCPInputSettings *deviceSettings = response.getRemoteTcpInputSettings();
deviceSettings->setDataAddress(new QString(m_remoteDeviceAddress));
deviceSettings->setDataPort(m_remoteDevicePort);
deviceSettings->setProtocol(new QString("SDRangel"));
deviceSettings->setOverrideRemoteSettings(false);
QString errorMessage;
device->getSampleSource()->webapiSettingsPutPatch(false, deviceSettingsKeys, response, errorMessage);
});
}
#ifdef QT_WEBENGINE_FOUND
@@ -2836,4 +3004,3 @@ void MapGUI::makeUIConnections()
QObject::connect(ui->ibpBeacons, &QToolButton::clicked, this, &MapGUI::on_ibpBeacons_clicked);
QObject::connect(ui->radiotime, &QToolButton::clicked, this, &MapGUI::on_radiotime_clicked);
}