Add Sky Map feature plugin.

pull/1986/head
srcejon 2024-02-14 13:20:33 +00:00
rodzic 03232b094c
commit 2708a81623
51 zmienionych plików z 6438 dodań i 18 usunięć

Wyświetl plik

@ -141,6 +141,7 @@ option(ENABLE_FEATURE_RIGCTLSERVER "Enable feature rigctlserver plugin" ON)
option(ENABLE_FEATURE_PERTESTER "Enable feature pertester plugin" ON)
option(ENABLE_FEATURE_GS232CONTROLLER "Enable feature gs232controller plugin" ON)
option(ENABLE_FEATURE_REMOTECONTROL "Enable feature remote control plugin" ON)
option(ENABLE_FEATURE_SKYMAP "Enable feature sky map plugin" ON)
# on windows always build external libraries
if(WIN32)

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 357 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 8.7 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.4 MiB

Wyświetl plik

@ -12,6 +12,11 @@ else()
message(STATUS "Not building map (ENABLE_FEATURE_MAP=${ENABLE_FEATURE_MAP} Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND})")
endif()
# WebEngine on Qt5, WebEngineCore on Qt6
if(ENABLE_FEATURE_SKYMAP AND Qt${QT_DEFAULT_MAJOR_VERSION}WebEngine_FOUND OR Qt${QT_DEFAULT_MAJOR_VERSION}WebEngineCore_FOUND)
add_subdirectory(skymap)
endif()
if (ENABLE_FEATURE_VORLOCALIZER AND Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND)
add_subdirectory(vorlocalizer)
else()

Wyświetl plik

@ -0,0 +1,92 @@
project(skymap)
set(skymap_SOURCES
skymap.cpp
skymapsettings.cpp
skymapplugin.cpp
skymapwebapiadapter.cpp
webserver.cpp
)
set(skymap_HEADERS
skymap.h
skymapsettings.h
skymapplugin.h
skymapreport.h
skymapwebapiadapter.h
webserver.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${Qt${QT_DEFAULT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}
)
if(NOT SERVER_MODE)
set(skymap_SOURCES
${skymap_SOURCES}
skymapgui.cpp
skymapgui.ui
skymapsettingsdialog.cpp
skymapsettingsdialog.ui
websocketserver.cpp
skymap.qrc
webinterface.cpp
wtml.cpp
webview.cpp
icons.qrc
)
set(skymap_HEADERS
${skymap_HEADERS}
skymapgui.h
skymapsettingsdialog.h
skymapmodel.h
skymapitem.h
websocketserver.h
skymaptileserver.h
webinterface.h
wtml.h
webview.h
)
set(TARGET_NAME featureskymap)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
if(Qt${QT_DEFAULT_MAJOR_VERSION}WebEngineCore_FOUND)
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location Qt::WebEngineCore Qt::WebEngineWidgets)
elseif(Qt${QT_DEFAULT_MAJOR_VERSION}WebEngine_FOUND)
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location Qt::WebEngine Qt::WebEngineCore Qt::WebEngineWidgets)
else()
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location)
endif()
else()
set(TARGET_NAME featureskymapsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${skymap_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
if(WIN32)
# Run deployqt for QtQuick etc
include(DeployQt)
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/skymap)
endif()
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

Wyświetl plik

@ -0,0 +1,166 @@
<!doctype html>
<html lang="en" style="height:100%;min-height:100%;">
<head>
<title>SDRangel Aladin</title>
<style>
.aladin-fov {
display: none;
visibility: hidden
}
.aladin-location {
display: none;
visibility: hidden
}
</style>
</head>
<body style="margin:0;padding:0;height:100%;">
<div id="aladin-lite-div" style="width:100%;height:100%;"></div>
<script type="text/javascript" src="https://aladin.cds.unistra.fr/AladinLite/api/v3/latest/aladin.js" charset="utf-8"></script>
<script type="text/javascript">
let aladin;
A.init.then(() => {
// Create Aladin
aladin = A.aladin('#aladin-lite-div', {
survey: "P/DSS2/color",
fov: 60,
showProjectionControl: false,
showFrame: false,
fullScreen: true, // fullScreen in terms of the div element, not the entire monitor
showFullscreenControl: false,
showGotoControl: false,
showZoomControl: false,
showSimbadPointerControl: true,
showContextMenu: true
});
// Load some catalogs, but don't show them - displayLabel doesn't seem to work for these ProgressiveCat only Catalog
let hips = A.catalogHiPS('https://axel.u-strasbg.fr/HiPSCatService/Simbad', { onClick: 'showTable', name: 'Simbad' });
hips.isShowing = false;
aladin.addCatalog(hips);
hips = A.catalogHiPS('http://axel.u-strasbg.fr/HiPSCatService/I/345/gaia2', { onClick: 'showTable', color: 'orange', name: 'Gaia' });
hips.isShowing = false;
aladin.addCatalog(hips);
// Report ra/dec/time
setInterval(function () { reportView(); }, 100);
reportReady();
});
// Stop browser popup menu from appearing over the top of the Aladin menu
document.addEventListener('contextmenu', event => event.preventDefault());
</script>
<script type="text/javascript">
// Use WebSockets for handling commands from SDRangel Plugin
// and sending events back to it
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
var antennaHpbw = 5.0;
var antennaOverlay;
var antennaCircle;
socket.onmessage = function (event) {
try {
const command = JSON.parse(event.data);
if (command.command == "setView") {
aladin.gotoRaDec(command.ra * 15.0, command.dec); // Convert RA from hours to degrees, J2000
} else if (command.command == "setPosition") {
// Not supported
} else if (command.command == "projection") {
aladin.setProjection(command.projection);
} else if (command.command == "setDateTime") {
// Not supported
} else if (command.command == "track") {
aladin.gotoObject(command.name);
} else if (command.command == "setBackground") {
aladin.setImageSurvey(command.background);
} else if (command.command == "setProjection") {
aladin.setProjection(command.projection);
} else if (command.command == "showNames") {
for (var i = 0; i < aladin.view.catalogs.length; i++) {
aladin.view.catalogs[i].displayLabel = command.show;
}
aladin.view.requestRedraw();
} else if (command.command == "showConstellations") {
// Not supported
} else if (command.command == "showReticle") {
aladin.showReticle(command.show);
} else if (command.command == "showGrid") {
if (command.show) {
aladin.showCooGrid();
} else {
aladin.hideCooGrid();
}
} else if (command.command == "showAntennaFoV") {
if (command.show) {
showAntennaFoV();
} else {
hideAntennaFoV();
}
} else if (command.command == "setAntennaFoV") {
antennaHpbw = command.hpbw;
if (antennaCircle) {
antennaCircle.setRadius(antennaHpbw);
}
} else {
console.log(`Unknown command ${command.command}`);
}
} catch (e) {
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
}
};
function createAntennaFoV() {
antennaOverlay = A.graphicOverlay({ color: '#ffffff', lineWidth: 1 });
aladin.addOverlay(antennaOverlay);
const [ra, dec] = aladin.getRaDec();
antennaCircle = A.circle(ra, dec, antennaHpbw);
antennaOverlay.add(antennaCircle);
}
function showAntennaFoV() {
antennaCircle.show();
}
function hideAntennaFoV() {
antennaCircle.hide();
}
function reportReady() {
console.log("reportReady " + socket.readyState);
createAntennaFoV();
if (socket.readyState === 1) {
socket.send(JSON.stringify({
event: "ready"
}));
} else {
setTimeout(reportReady, 100);
}
}
function reportView() {
if (socket.readyState === 1) {
const [ra, dec] = aladin.getRaDec();
const [fovX, fovY] = aladin.getFov();
socket.send(JSON.stringify({
event: "view",
ra: ra / 15.0, // Convert RA from degrees to hours
dec: dec,
fov: fovX
}));
antennaCircle.setCenter([ra, dec]);
}
}
</script>
</body>
</html>

Wyświetl plik

@ -0,0 +1,181 @@
<!doctype html>
<html lang="en" style="height:100%;min-height:100%;">
<head>
<title>SDRangel ESASky</title>
</head>
<body style="margin:0;padding:0;height:100%;">
<script type="text/javascript">
var esaskyFrame;
var antennaHpbw = 5.0;
var showAntenna = false;
// API reference: https://www.cosmos.esa.int/web/esdc/esasky-javascript-api
function on_ready() {
esaskyFrame = document.getElementsByClassName("iframe-container")[0].children[0];
var cmd = { event: 'setModuleVisibility', content: { modules: { jwst_planning_button: false } } };
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
window.addEventListener("message", handleMessage);
setTimeout(function () { reportReady(); }, 500); // Add a delay, otherwise subsequent commands to set grid seem to get lost
// Can't access contentDocument due to same origin policy
//console.log(esaskyFrame.contentDocument);
//console.log("containers: " + esaskyFrame.contentDocument.getElementsByName("aladin-container"));
// Report ra/dec/time
// We don't use events, as they are too slow (view_changed is only sent after all data has been calculated)
setInterval(function () { reportView(); }, 100);
//setTimeout(function () { requestEvents(); }, 1000);
}
/*function requestEvents() {
var cmd1 = { event: 'registerEventListener', content: {} };
esaskyFrame.contentWindow.postMessage(cmd1, 'https://sky.esa.int');
var cmd2 = { event: 'registerFoVChangedListener', content: {} };
esaskyFrame.contentWindow.postMessage(cmd2, 'https://sky.esa.int');
}*/
// Use WebSockets for handling commands from SDRangel Plugin
// and sending events back to it
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
socket.onmessage = function (event) {
try {
const command = JSON.parse(event.data);
if (command.command == "setView") {
var cmd = { event: 'goToRaDec', content: { ra: command.ra * 15.0, dec: command.dec } }; // Convert RA hours to decimal degrees
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
} else if (command.command == "setPosition") {
// Not supported
} else if (command.command == "setDateTime") {
// Not supported
} else if (command.command == "track") {
var cmd = { event: 'goToTargetName', content: { targetName: command.name } };
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
} else if (command.command == "setBackground") {
// Leave to its own GUI
} else if (command.command == "showConstellations") {
// Not supported
} else if (command.command == "showReticle") {
// Not supported
} else if (command.command == "showGrid") {
var cmd = { event: 'showCoordinateGrid', content: { show: command.show } };
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
} else if (command.command == "showAntennaFoV") {
showAntenna = command.show;
if (!showAntenna) {
hideAntennaFoV();
}
} else if (command.command == "setAntennaFoV") {
antennaHpbw = command.hpbw;
} else {
console.log(`Unknown command ${command.command}`);
}
} catch (e) {
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
}
};
function reportReady() {
console.log("reportReady " + socket.readyState);
if (socket.readyState === 1) {
socket.send(JSON.stringify({
event: "ready"
}));
} else {
setTimeout(reportReady, 100);
}
}
function handleMessage(e) {
//console.log(e);
var data = e.data;
if (data.origin == "esasky") {
var ra, dec, fov;
if (data.hasOwnProperty('event')) {
if (data.event.action == "view_changed") {
ra = data.event.values["ra"];
dec = data.event.values["dec"];
fov = data.event.values["fov"];
}
} else {
ra = data.values["ra"];
dec = data.values["dec"];
fov = data.values["fov"];
}
//console.log("handleMessage esasky: " + ra + " " + dec + " " + fov);
if (socket.readyState === 1) {
socket.send(JSON.stringify({
event: "view",
ra: ra / 15.0, // convert decimal degrees to hours
dec: dec,
fov: fov
}));
}
if (showAntenna) {
showAntennaFoV(ra, dec);
}
}
}
function reportView() {
if (socket.readyState === 1) {
var cmd = { event: 'getCenter', content: { cooFrame: 'J2000' } };
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
}
}
function hideAntennaFoV() {
var cmd = { event: 'deleteFootprintsOverlay', content: { 'overlayName': 'Antenna FoV' } };
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
}
function showAntennaFoV(ra, dec) {
var cmd =
{
event: 'overlayFootprints',
content: {
'overlaySet':
{
'type': 'FootprintListOverlay',
'overlayName': 'Antenna FoV',
'cooframe': 'J2000',
'color': 'white',
'lineWidth': 5,
'skyObjectList': [
{
'name': 'Antenna FoV',
'id': 1,
'stcs': 'CIRCLE J2000 ' + ra + ' ' + dec + ' ' + (antennaHpbw / 2.0),
'ra_deg': ra,
'dec_deg': dec
}]
}
}
};
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
}
</script>
<div class="iframe-container" style="width: 100%; height: 100%">
<iframe alt="" border="0" bordercolor="#000000" frameborder="0" height="" hspace="0" id="_ESAsky" longdesc="" name="_ESASky" onload="on_ready();" scrolling="auto" src="https://sky.esa.int?hide_welcome=true&amp;&amp;hide_banner_info=true" title="" vspace="0" width="100%" class="" style="height: 100%;">
Frames not supported.
</iframe>
</div>
</body >
</html >

Wyświetl plik

@ -0,0 +1,828 @@
<!doctype html>
<html lang="en" style="height:100%;min-height:100%;">
<head>
<title>SDRangel WWT</title>
<script src="https://web.wwtassets.org/engine/7/wwtsdk.js"></script>
<style>
body {
overflow: hidden; /* Hide scrollbars if popup window near edge */
}
.popup {
visibility: hidden;
position: absolute;
z-index: 10;
background-color: #fff;
border-radius: 25px;
padding: 15px;
font-family: Arial, sans-serif;
font-size: 0.875em;
}
.popup .popuptext {
}
.popup .close {
float: right;
}
</style>
</head>
<body style="margin:0;padding:0;height:100%;">
<div class="popup" id="popupDiv" onmousedown="dragDown()">
<button aria-hidden="true" class="close" type="button" onclick="hidePlace()">x</button>
<span class="popuptext" id="myPopup"></span>
</div>
<div id="wwtcanvas" style="width: 100%; height: 100%; background-color: #000"></div>
<script type="text/javascript">
var script_interface, wwt;
var circle, antennaFoV;
var antennaHpbw = 5.0;
var wwtSettings = {
constellationBoundaries: "false",
constellationFigures: "true",
constellationLabels: "true",
constellationPictures: "false",
constellationSelection: "false",
ecliptic: "false",
eclipticOverviewText: "false",
eclipticGrid: "false",
eclipticGridText: "false",
altAzGrid: "false",
altAzGridText: "false",
equatorialGrid: "false",
equatorialGridText: "false",
galacticGrid: "false",
galacticGridText: "false",
precessionChart: "false",
iss: "false",
solarSystemCosmos: "false",
solarSystemLighting: "true",
solarSystemMilkyWay: "true",
solarSystemMinorOrbits: "false",
solarSystemMinorPlanets: "false",
solarSystemMultiRes: "true",
solarSystemOrbits: "true",
solarSystemOverlays: "false",
solarSystemPlanets: "true",
solarSystemStars: "true",
};
var showNames = true;
var showGrid = false;
var showConstellations = false;
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
var popup = document.getElementById("popupDiv");
function dragDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
popup.style.top = (popup.offsetTop - pos2) + "px";
popup.style.left = (popup.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
function init_wwt() {
const builder = new wwtlib.WWTControlBuilder("wwtcanvas");
builder.startRenderLoop(true);
script_interface = builder.create();
script_interface.add_ready(on_ready);
}
// API reference: https://docs.worldwidetelescope.org/webgl-reference/latest/apiref/engine/classes/WWTControl-1.html
function on_ready() {
wwt = wwtlib.WWTControl.singleton;
reportReady();
// Report ra/dec/time
setInterval(function () { reportView(); }, 100);
// Load search data
// This sets the wwt.searchData variable to what's in https://web.wwtassets.org/data/searchdata_v2.min.js
var scr = document.createElement('script');
scr.setAttribute("src", wwtlib.URLHelpers.singleton.coreStaticUrl('data/searchdata_v2.min.js'));
document.getElementsByTagName("head")[0].appendChild(scr);
setTimeout(initSearch, 333);
}
window.addEventListener("load", init_wwt);
// Use WebSockets for handling commands from WorldwideTelescope SDRangel Plugin
// and sending events back to it
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
socket.onmessage = function (event) {
try {
const command = JSON.parse(event.data);
if (command.command == "setView") {
wwt.gotoRADecZoom(command.ra, command.dec, wwt.renderContext.viewCamera.zoom, true); // Requires RA in hours, J2000
} else if (command.command == "setPosition") {
script_interface.settings.set_locationLat(command.latitude);
script_interface.settings.set_locationLng(command.longitude);
script_interface.settings.set_locationAltitude(command.altitude);
} else if (command.command == "setProjection") {
if (command.projection == "Solar system") {
wwt.setBackgroundImageByName("Solar System");
}
// In other cases, we just use setBackground which will follow
} else if (command.command == "setDateTime") {
// Set current date and time of viewer
//console.log("setDateTime " + command.dateTime);
//const dt = new Date(command.dateTime);
const dt = Date.parse(command.dateTime);
wwtlib.SpaceTimeController.set_now(dt);
} else if (command.command == "track") {
//console.log("Finding " + command.name);
const place = search(command.name);
if (place) {
//console.log(place);
wwt.gotoTarget(place, false, true, true);
} else {
console.log("Can't find " + command.name);
}
} else if (command.command == "setBackground") {
if (command.background != "") {
wwt.setBackgroundImageByName(command.background);
}
} else if (command.command == "showNames") {
showNames = command.show;
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
script_interface.settings.set_showEclipticOverviewText(showNames && (wwtSettings.eclipticOverviewText === "true"))
} else if (command.command == "showConstellations") {
showConstellations = command.show;
const ok = script_interface.settings.set_showConstellations(showConstellations);
script_interface.settings.set_showConstellationBoundries(showConstellations && (wwtSettings.constellationBoundaries === "true"));
script_interface.settings.set_showConstellationFigures(showConstellations && (wwtSettings.constellationFigures === "true"));
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
script_interface.settings.set_showConstellationPictures(showConstellations && (wwtSettings.constellationPictures === "true"));
script_interface.settings.set_showConstellationSelection(showConstellations && (wwtSettings.constellationSelection === "true"));
} else if (command.command == "showReticle") {
script_interface.settings.set_showCrosshairs(command.show);
} else if (command.command == "showGrid") {
showGrid = command.show;
script_interface.settings.set_showEclipticGrid(showGrid && (wwtSettings.eclipticGrid === "true"));
script_interface.settings.set_showEclipticGridText(showGrid && (wwtSettings.eclipticGridText === "true"));
script_interface.settings.set_showAltAzGrid(showGrid && (wwtSettings.altAzGrid === "true"));
script_interface.settings.set_showAltAzGridText(showGrid && (wwtSettings.altAzGridText === "true"));
script_interface.settings.set_showGrid(showGrid && (wwtSettings.equatorialGrid === "true"));
script_interface.settings.set_showEquatorialGridText(showGrid && (wwtSettings.equatorialGridText === "true"));
script_interface.settings.set_showGalacticGrid(showGrid && (wwtSettings.galacticGrid === "true"));
script_interface.settings.set_showGalacticGridText(showGrid && (wwtSettings.galacticGridText === "true"));
} else if (command.command == "showAntennaFoV") {
if (command.show) {
showAntennaFoV();
} else {
hideAntennaFoV();
}
} else if (command.command == "setAntennaFoV") {
antennaHpbw = command.hpbw;
if (antennaFoV) {
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
}
} else if (command.command == "setWWTSettings") {
Object.assign(wwtSettings, command);
script_interface.settings.set_showConstellationBoundries(showConstellations && (wwtSettings.constellationBoundaries === "true"));
script_interface.settings.set_showConstellationFigures(showConstellations && (wwtSettings.constellationFigures === "true"));
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
script_interface.settings.set_showConstellationPictures(showConstellations && (wwtSettings.constellationPictures === "true"));
script_interface.settings.set_showConstellationSelection(showConstellations && (wwtSettings.constellationSelection === "true"));
script_interface.settings.set_showEcliptic(wwtSettings.ecliptic === "true")
script_interface.settings.set_showEclipticOverviewText(showNames && (wwtSettings.eclipticOverviewText === "true"))
script_interface.settings.set_showEclipticGrid(showGrid && (wwtSettings.eclipticGrid === "true"));
script_interface.settings.set_showEclipticGridText(showGrid && (wwtSettings.eclipticGridText === "true"));
script_interface.settings.set_showAltAzGrid(showGrid && (wwtSettings.altAzGrid === "true"));
script_interface.settings.set_showAltAzGridText(showGrid && (wwtSettings.altAzGridText === "true"));
script_interface.settings.set_showGrid(showGrid && (wwtSettings.equatorialGrid === "true"));
script_interface.settings.set_showEquatorialGridText(showGrid && (wwtSettings.equatorialGridText === "true"));
script_interface.settings.set_showGalacticGrid(showGrid && (wwtSettings.galacticGrid === "true"));
script_interface.settings.set_showGalacticGridText(showGrid && (wwtSettings.galacticGridText === "true"));
script_interface.settings.set_showPrecessionChart(showGrid && (wwtSettings.precessionChart === "true"));
// Solar System view
// Need to zoom a long way out to see Cosmos
script_interface.settings.set_solarSystemCosmos(wwtSettings.solarSystemCosmos === "true");
script_interface.settings.set_solarSystemLighting(wwtSettings.solarSystemLighting === "true");
script_interface.settings.set_solarSystemMilkyWay(wwtSettings.solarSystemMilkyWay === "true");
script_interface.settings.set_solarSystemMinorOrbits(wwtSettings.solarSystemMinorOrbits === "true");
script_interface.settings.set_solarSystemMinorPlanets(wwtSettings.solarSystemMinorPlanets === "true");
script_interface.settings.set_solarSystemMultiRes(wwtSettings.solarSystemMultiRes === "true");
script_interface.settings.set_solarSystemOrbits(wwtSettings.solarSystemOrbits === "true");
script_interface.settings.set_solarSystemOverlays(wwtSettings.solarSystemOverlays === "true");
script_interface.settings.set_solarSystemPlanets(wwtSettings.solarSystemPlanets === "true");
script_interface.settings.set_solarSystemStars(wwtSettings.solarSystemStars === "true");
script_interface.settings.set_showISSModel(wwtSettings.iss === "true");
// These only supported in Windows client, not WebGL client
//script_interface.settings.set_showClouds(wwtSettings.clouds === "true");
//script_interface.settings.set_showElevationModel(wwtSettings.elevationModel === "true");
//script_interface.settings.set_showEarthSky(wwtSettings.earthSky === "true");
//script_interface.settings.set_earthCutawayView(wwtSettings.earthCutawayView === "true");
//script_interface.settings.get_showFieldOfView(wwtSettings.fieldOfView === "true")
//script_interface.settings.set_solarSystemCMB(wwtSettings.solarSystemCMB === "true");
} else {
console.log(`Unknown command ${command.command}`);
}
} catch (e) {
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
}
};
// Workaround for https://github.com/WorldWideTelescope/wwt-webgl-engine/issues/297
// Although circles not drawn when abs(dec)=90
function radiusAdjust(radius, dec) {
if (Math.abs(dec) >= 89.9999999) {
return radius * 0.769 / Math.cos(89.9999999 * Math.PI / 180.0);
} else {
return radius * 0.769 / Math.cos(dec * Math.PI / 180.0);
}
}
function search(name) {
const firstChar = name.charAt(0).toLowerCase();
const searchData = wwt.searchDataIndexed[firstChar];
const nameLower = name.toLowerCase();
if (searchData) {
for (var i = 0; i < searchData.length; i++) {
var names = searchData[i].get_names();
for (var j = 0; j < names.length; j++) {
if (names[j].toLowerCase() == nameLower) {
return searchData[i];
}
}
}
} else {
console.log("No search index: " + name);
}
}
function reportReady() {
if (socket.readyState === 1) {
socket.send(JSON.stringify({
event: "ready"
}));
} else {
setTimeout(reportReady, 100);
}
}
function reportView() {
if (socket.readyState === 1) {
socket.send(JSON.stringify({
event: "view",
ra: script_interface.getRA(), // RA in hours
dec: script_interface.getDec(),
fov: wwt.renderContext.get_fovAngle(),
dateTime: wwtlib.SpaceTimeController.get_now().toISOString(),
latitude: script_interface.settings.get_locationLat(),
longitude: script_interface.settings.get_locationLng(),
}));
}
if (antennaFoV) {
showAntennaFoV();
}
}
// 'click' event doesn't work for right button, so use mousedown
// Based on code from www-web-client FinderScope.js (MIT license)
var div = document.getElementById('wwtcanvas');
div.addEventListener('mousedown', function (event) {
if (event.button == 2) {
var rect = div.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
const obj = wwt.getCoordinatesForScreenPoint(x, y);
var scope = wwtlib.Coordinates.raDecTo3d(obj.x, obj.y);
// findConstellationForPoint can return error if x < 0, but not always
// Also this isn't in the original code, but seems to work better
var ob = obj.x;
if (ob < 0) {
ob = ob + 24;
}
var constellation = wwtlib.Constellations.containment.findConstellationForPoint(ob, obj.y);
var closestDist, closestPlace;
var constellationPlaces, ssPlaces;
for (var i = 0; i < wwt.searchData.Constellations.length; i++) {
var item = wwt.searchData.Constellations[i];
if (item.name === constellation) {
constellationPlaces = item.places;
} else if (item.name === 'SolarSystem') {
ssPlaces = item.places;
}
}
var searchPlaces = ssPlaces.concat(constellationPlaces);
for (var i = 0; i < searchPlaces.length; i++) {
var place = searchPlaces[i];
try {
var placeDist = wwtlib.Vector3d.subtractVectors(place.get_location3d(), scope);
if ((i === 0) || closestDist.length() > placeDist.length()) {
closestPlace = place;
closestDist = placeDist;
}
} catch (er) {
if (place && place.get_name() != 'Earth') {
console.log(er);
}
}
}
getAstroDetails(closestPlace);
//console.log("Closest place " + JSON.stringify(closestPlace));
showPlace(event.clientX, event.clientY, closestPlace);
}
});
function hideAntennaFoV() {
if (antennaFoV) {
script_interface.removeAnnotation(antennaFoV);
antennaFoV = undefined;
}
}
function showAntennaFoV() {
if (!antennaFoV) {
antennaFoV = new wwtlib.Circle();
antennaFoV.set_id('antennaFoV');
antennaFoV.setCenter(script_interface.getRA() * 15, script_interface.getDec());
antennaFoV.set_skyRelative(false);
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
antennaFoV.set_lineWidth(3);
script_interface.addAnnotation(antennaFoV);
} else {
antennaFoV.setCenter(script_interface.getRA() * 15, script_interface.getDec());
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
}
}
function hidePlace() {
var popupDiv = document.getElementById("popupDiv");
popupDiv.style.visibility = "hidden";
// Remove annotation
if (circle) {
script_interface.removeAnnotation(circle);
}
}
function showPlace(x, y, place) {
// Set text to display for the place
var str = "<p>";
if (place.get_names()) {
str += "Names: " + place.get_names().join(', ');
} else {
str += "Name: " + place.get_name();
}
str += "<p>";
str += "Classification: " + getClassificationText(place.get_classification());
if (!place.isSurvey && (typeof place.get_constellation() !== "undefined")) {
if (place.get_constellation() != "SolarSystem") {
str += " in " + wwtlib.Constellations.fullNames[place.get_constellation()];
}
}
str += "<p>";
str += "<img src=" + place.get_thumbnailUrl() + ">";
str += "<p>";
str += "<table>";
if (!place.isSurvey) {
str += "<tr><td>RA<td>" + formatHms(place.get_RA(), true, false, false, 1);
str += "<tr><td>Dec<td>" + formatHms(place.get_dec(), false, true, true);
str += "<tr><td>Az<td>" + formatHms(place.altAz.get_az(), false, false, true);
str += "<tr><td>Alt<td>" + formatHms(place.altAz.get_alt(), false, false, true);
}
if (place.get_magnitude() != 0) {
str += "<tr><td>Magnitude<td>" + place.get_magnitude().toFixed(2);
}
if (place.get_distance() != 0) {
str += "<tr><td>Distance<td>" + wwtlib.UiTools.formatDistance(place.get_distance());
}
if (!place.riseSet.bNeverRises) {
str += "<tr><td>Rise<td>" + formatDecimalHours(place.riseSet.rise, false, false, true);
}
if (!place.riseSet.bNeverSets) {
str += "<tr><td>Set<td>" + formatDecimalHours(place.riseSet.set, false, false, true);
}
str += "</table>";
// Add links to research databases
const name = placeNameForQueryString(place);
str += "<p>";
str += "<a href=\"//wikipedia.org/wiki/Special:Search?search=" + name + "\" target=\"_blank\">Wiki</a>";
str += " <a href=\"//ui.adsabs.harvard.edu/search/q=object:%22" + name + "%22\" target=\"_blank\">ADS</a>";
str += " <a href=\"//simbad.u-strasbg.fr/simbad/sim-id?Ident=" + name + "\" target=\"_blank\">SIMBAD</a>";
str += " <a href=\"//ned.ipac.caltech.edu/cgi-bin/nph-imgdata?objname=" + name + "\" target=\"_blank\">NED</a>";
str += " <a href=\"//cdsportal.u-strasbg.fr/gadgets/ifr?url=http://cdsportal.unistra.fr/widgets/SED_plotter.xml&SED_plot_object=" + name + "&SED_plot_radius=5\" target=\"_blank\">VizieR</a>";
var popup = document.getElementById("myPopup");
popup.innerHTML = str;
// Get location of place on screen, as may not be exactly where clicked
const pos = wwt.getScreenPointForCoordinates(place.get_RA(), place.get_dec());
// Position and show
var popupDiv = document.getElementById("popupDiv");
popupDiv.style.visibility = "visible";
popupDiv.style.left = pos.x + "px";
popupDiv.style.top = pos.y + "px";
// Remove old annotation
if (circle) {
script_interface.removeAnnotation(circle);
}
// Create circle annotation
circle = new wwtlib.Circle();
circle.set_id('focused');
circle.setCenter(place.get_RA() * 15, place.get_dec());
circle.set_skyRelative(false);
circle.set_radius(radiusAdjust(.22, place.get_dec()));
circle.set_lineWidth(3);
script_interface.addAnnotation(circle);
}
function placeNameForQueryString(item) {
// Various "research" menus like to put an item's name into URL query
// strings. Compute the proper (well, good-enough) representation. When
// the name has multiple semicolon-separated identifiers, usually the
// first one is a description of a particular image, and the subsequent
// ones (if any) are names for the object.
var name_segments = item.get_name().split(';');
var name_to_use = (name_segments.length > 1) ? name_segments[1] : name_segments[0];
name_to_use = encodeURIComponent(name_to_use);
return name_to_use.replace(/%20/g, '+');
}
// The following code based on code from www-web-client Util.js (MIT license)
function formatHms(angle, isHmsFormat, signed, spaced, extraPrecision) {
var sign = '';
if (angle < 0) {
sign = '-';
angle = -angle;
} else if (signed) {
sign = '+';
}
var seps = [':', ':', ''];
if (isHmsFormat) {
seps = ['h', 'm', 's'];
} else if (spaced) {
seps = [' : ', ' : ', ''];
}
var values = ['??', '??', '??'];
if (!isNaN(angle)) {
var hourlike = Math.floor(angle);
var remainder = (angle - hourlike) * 60;
var minutes = Math.floor(remainder);
var seconds = (remainder - minutes) * 60;
if (isNaN(extraPrecision)) {
extraPrecision = 0;
}
var secondsStr = seconds.toFixed(extraPrecision);
if (secondsStr.startsWith('60')) {
seconds = 0;
secondsStr = seconds.toFixed(extraPrecision);
minutes += 1;
if (minutes == 60) {
minutes = 0;
hourlike += 1;
}
}
values[0] = hourlike.toFixed(0);
if (hourlike < 10) {
values[0] = '0' + values[0];
}
values[1] = minutes.toFixed(0);
if (minutes < 10) {
values[1] = '0' + values[1];
}
values[2] = secondsStr;
if (seconds < 10) {
values[2] = '0' + values[2];
}
}
return sign.concat(values[0], seps[0], values[1], seps[1], values[2], seps[2]);
};
function formatDecimalHours(dayFraction, spaced) {
var ts = new Date(new Date().toUTCString()).valueOf() - new Date().valueOf();
var hr = ts / (1000 * 60 * 60);
var day = (dayFraction - hr) + 0.0083333334;
while (day > 24) {
day -= 24;
}
while (day < 0) {
day += 24;
}
var hours = day.toFixed(0);
var minutes = ((day * 60) - (hours * 60)).toFixed(0);
var join = spaced ? ' : ' : ':';
//var seconds = ((day * 3600) - (((hours * 3600) + ((double)minutes * 60.0)));
return ([int2(hours), int2(minutes)]).join(join);
}
function int2(dec) {
var sign = dec < 0 ? '-' : '';
var int = Math.floor(Math.abs(dec));
var pad = int < 10 ? '0' : '';
return sign + pad + int;
}
function getClassificationText(clsid) {
if (clsid && !isNaN(parseInt(clsid))) {
var str;
for (const [k, v] of Object.entries(wwtlib.Classification)) {
if (v === clsid) {
str = k;
}
}
var out = str.replace(/^\s*/, ""); // strip leading spaces
out = out.replace(/^[a-z]|[^\s][A-Z]/g, function (str, offset) {
if (offset == 0) {
return (str.toUpperCase());
} else {
return (str.substr(0, 1) + " " + str.substr(1).toUpperCase());
}
});
return (out);
} else {
return null;
}
};
function getAstroDetails(place) {
var coords = wwtlib.Coordinates.fromRaDec(place.get_RA(), place.get_dec());
var stc = wwtlib.SpaceTimeController;
var altAz = wwtlib.Coordinates.equitorialToHorizon(coords, stc.get_location(), stc.get_now());
place.altAz = altAz;
var classificationText = getClassificationText(place.get_classification());
var riseSet;
if (classificationText == 'Solar System') {
var jNow = stc.get_jNow() + .5;
var p1 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow - 1);
var p2 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow);
var p3 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow + 1);
var type = 0;
switch (place.get_name()) {
case "Sun":
type = 1;
break;
case "Moon":
type = 2;
break;
default:
type = 0;
break;
}
riseSet = wwtlib.AstroCalc.getRiseTrinsitSet(
jNow,
stc.get_location().get_lat(),
-stc.get_location().get_lng(),
p1.RA, p1.dec,
p2.RA, p2.dec,
p3.RA, p3.dec,
type
);
} else {
riseSet = wwtlib.AstroCalc.getRiseTrinsitSet(
stc.get_jNow() + .5,
stc.get_location().get_lat(),
-stc.get_location().get_lng(),
place.get_RA(), place.get_dec(),
place.get_RA(), place.get_dec(),
place.get_RA(), place.get_dec(),
0
);
}
if (!riseSet.bValid && !riseSet.bNeverRises) {
riseSet.bNeverSets = true;
}
place.riseSet = riseSet;
}
// The following code based on code from www-web-client SearchUtil.js (MIT license)
// searchData gets inserted here
var wwt = {};
var searchIndex = {};
var imageset_id = 100;
function initSearch() {
// The special `wwt.searchData` global is assigned in the JS file that
// the main webclient app loads asynchronously (see `app.js`).
if (!wwt.searchData) {
setTimeout(initSearch, 333);
} else {
// searchDataIndexed is used in `factories/SearchUtil.js`.
wwt.searchDataIndexed = [];
data = wwt.searchData;
var start = new Date();
for (var i = 0; i < data.Constellations.length; i++) {
var item = data.Constellations[i];
//constellations[i] = item.name;
for (var j = 0; j < item.places.length; j++) {
var place = item.places[j];
var fgi = place.fgi,
imgSet;
if (fgi) {
imageset_id++;
var band_pass = (fgi.bp !== undefined) ? fgi.bp : wwtlib.BandPass.visible;
var projection = (fgi.pr !== undefined) ? fgi.pr : wwtlib.ProjectionType.tan;
var base_tile_level = (fgi.bl !== undefined) ? fgi.bl : 0;
var file_type = (fgi.ft !== undefined) ? fgi.ft : ".png";
var tile_levels = (fgi.lv !== undefined) ? fgi.lv : 4;
var bottoms_up = (fgi.bu !== undefined) ? fgi.bu : false;
var quad_tree_map = (fgi.q !== undefined) ? fgi.q : "";
var offset_x = (fgi.oX !== undefined) ? fgi.oX : 0;
var offset_y = (fgi.oY !== undefined) ? fgi.oY : 0;
var default_set = (fgi.ds !== undefined) ? fgi.ds : false; // "StockSet" in XML
var rotation = (fgi.r !== undefined) ? fgi.r : 0;
var width_factor = (fgi.wf !== undefined) ? fgi.wf : 2;
imgSet = wwtlib.Imageset.create(
fgi.n, // name
fgi.u, // url
wwtlib.ImageSetType.sky, // data_set_type -- never changes (for now?)
band_pass,
projection,
imageset_id, // imageset id
base_tile_level,
tile_levels,
null, // tile_size
fgi.bd, // baseTileDegrees
file_type,
bottoms_up,
quad_tree_map,
fgi.cX, // centerX
fgi.cY, // centerY
rotation,
true, // sparse
fgi.tu, // thumbnailUrl,
default_set,
false, // elevationModel
width_factor,
offset_x,
offset_y,
fgi.ct, // creditsText
fgi.cu, // creditsUrl
'', // demUrl
'', // altUrl
0, // meanRadius
null // referenceFrame
);
rewritePlaceUrls(imgSet);
}
var classification = (place.c !== undefined) ? place.c : wwtlib.Classification.unidentified;
var zoom_factor = (place.z !== undefined) ? place.z : -1;
//console.log("Creating place " + place.n + " of type " + classification + " in " + item.name);
var pl = wwtlib.Place.create(
place.n, // name
place.d, // dec
place.r, // ra
classification,
item.name, // constellation
wwtlib.ImageSetType.sky, // type -- never changes (for now?)
zoom_factor,
);
if (imgSet) {
pl.set_studyImageset(imgSet);
}
if (item.name === 'SolarSystem') {
for (const [k, member] of Object.entries(pl)) {
if (wwtlib.ss.canCast(member, wwtlib.CameraParameters)) {
member.target = wwtlib.SolarSystemObjects.undefined;
}
}
}
pl.guid = i + "." + j;
rewritePlaceUrls(pl);
item.places[j] = pl;
indexPlaceNames(pl);
}
}
var end = new Date();
//console.log('parsed places in ' + (end.valueOf() - start.valueOf()) + 'ms', data);
}
};
function addPlace(s, place) {
var firstChar = s.charAt(0).toLowerCase();
if (firstChar === "'")
firstChar = s.charAt(1).toLowerCase();
if (searchIndex[firstChar]) {
if (searchIndex[firstChar][searchIndex[firstChar].length - 1] !== place) {
searchIndex[firstChar].push(place);
wwt.searchDataIndexed = searchIndex;
}
} else {
try {
wwt.searchDataIndexed[firstChar] = searchIndex[firstChar] = [place];
} catch (er) {
console.error(er);
}
}
}
function indexPlaceNames(pl) {
var names = pl.get_names();
for (var n = 0; n < names.length; n++) {
var name = names[n];
if (name.indexOf(' ') !== -1) {
var words = name.split(' ');
for (var w = 0; w < words.length; w++) {
var word = words[w];
addPlace(word, pl);
}
} else if (name.charAt(0)) {
addPlace(name, pl);
}
}
};
function rewritePlaceUrls(item) {
if (item.get_thumbnailUrl) {
var u = item.get_thumbnailUrl();
if (u)
item.thumb = wwtlib.URLHelpers.singleton.rewrite(u, wwtlib.URLRewriteMode.asIfAbsolute);
}
if (item.get_url) {
var u = item.get_url();
if (u)
item.url = wwtlib.URLHelpers.singleton.rewrite(u, wwtlib.URLRewriteMode.asIfAbsolute);
}
}
</script>
</body>
</html>

Wyświetl plik

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/skymap/">
<file>icons/constellation.png</file>
<file>icons/reticle.png</file>
</qresource>
</RCC>

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.8 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.4 KiB

Wyświetl plik

@ -0,0 +1,141 @@
<h1>Sky Map Feature Plugin</h1>
<h2>Introduction</h2>
The Sky Map Feature provides visualization of the sky in multiple wavelengths (radio, IR, optical, UV, X-Ray, gamma).
Multiple Sky Maps are supported, including the [World Wide Telescope](https://www.worldwidetelescope.org/), [ESASky](https://www.esa.int/About_Us/ESAC/Explore_the_cosmos_with_ESASky) and [Aladin Lite](https://aladin.cds.unistra.fr/).
The Rotator Controller and Star Tracker plugins can track the position viewed in the Sky Map. The Sky Map can also be set to track coordinates from the Star Tracker, Satellite Tracker, Rotator Controller or Map.
![Sky Map feature WWT](../../../doc/img/SkyMap_WWT.png)
With the ESASky map, a host of astronomical data is available from JWST, Hubble, Gaia, Herschel and others, including images, spectra and publications:
![Sky Map feature ESASky](../../../doc/img/SkyMap_ESASky.png)
<h2>Interface</h2>
![Sky Map feature plugin GUI](../../../doc/img/SkyMap_settings.png)
<h3>1: Find</h3>
Enter an astronomical object name and press enter to centre the sky map on that object.
You can also enter J2000 RA and Dec coordinates, in either HMS/DMS or decimal:
* 12 05 12.23 +17 06 21.0
* 12 05 12 17 06 21
* 12:05:12.23 -17:06:21.20
* 12h05m12.23s +17d06m21.10s
* 107.1324 -34.233
Note that the supported object names depend on the chosen map (2).
<h3>2: Map Type</h3>
Allows you to select the sky map:
* WWT - World Wide Telescope
* ESASky
* Aladin
Each map provides different features, image and data sets.
<h3>3: Background Image Set</h3>
For WWT and Aladin, this sets the background image set the sky map will display. For ESASky, this must be set within the ESASky GUI (using the Manage Skies button), as this can use multiple image sets.
Image sets are available for a variety of wavelengths (radio, IR, optical, UV, X-Ray, gamma) from a number of different surveys.
<h3>4: Projection</h3>
For World Wide Telescope:
* Sky - Views the sky from a position on Earth,
* Solar System - Views the Solar System, with the ability to show orbits,
* Planet - Views one of the planets or major moons.
For Aladin:
* SIN - [orthographic](https://en.wikipedia.org/wiki/Orthographic_map_projection),
* AIT - [Hammer-Aitoff](https://en.wikipedia.org/wiki/Hammer_projection),
* MOL - [Mollweide](https://en.wikipedia.org/wiki/Mollweide_projection),
* MER - [Mercator](https://en.wikipedia.org/wiki/Mercator_projection),
* ARC - zenithal/azimuthal equidistant,
* TAN - [gnomonic](https://en.wikipedia.org/wiki/Gnomonic_projection),
* HPX - [HEALPix](https://en.wikipedia.org/wiki/HEALPix).
This option is not available for ESASky, which is currently fixed to orthographic.
See [List of map projections](https://en.wikipedia.org/wiki/List_of_map_projections).
<h3>5: Display Names</h3>
For WWT, when checked, displays names of constellations and Ecliptic text (when the constellations and Ecliptic are visible).
<h3>6: Display Constellations</h3>
For WWT, this option enables the display of constellations. How the constellations are drawn can be customised in the Display Settings dialog (12).
<h3>7: Display gird</h3>
When checked, displays a coordinate grid.
For WWT, Ecliptic, Alt/Az, Equatorial and Galactic grids can be displayed. These can be selected in the Display Settings dialog (12).
For ESASky and Aladin, the grid will be Equatorial or Galactic, depending on the coordinate mode selected.
<h3>8: Display reticle</h3>
When checked, displays a reticle (cross hair) at the centre of the view. Coordinates for the reticle are displayed in the status bar in the bottom of the window.
<h3>9: Display antenna field-of-view</h3>
When checked, displays the antenna field-of-view. The antenna beamwidth can be set in the Display Settings dialog (12) or from a Star Tracker source (11).
<h3>10: Track</h3>
When checked, the centre of view will track the coordinates received from the selected Source plugin (11).
<h3>11: Source</h3>
Select a Star Tracker, Rotator Controller, Satellite Tracker or Map plugin to read viewing coordinates from.
* When a Star Tracker is selected, target RA/Dec, observation position, antenna beamwith and date/time will be read from the selected plugin.
* For other plugin types, Alt/El will be read and other parameters are taken from the the Display Settings dialog (12).
<h3>12: Display settings</h3>
When clicked, opens the Display Settings dialog. The Display Settings dialog allows the user to set:
* Observation location (latitude and longitude in degreees).
* Antenna beamwidth in degrees.
* Settings for WWT, such as how the constellations are drawn, what grids are displayed and how the Solar System view appears.
<h2>Sky Map Controls</h2>
* Click and drag using the left mouse button to rotate through right ascension or declination.
* Use the mouse wheel to zoom in and out.
* In WWT, right click to display information about the nearest astronomical object. The object will be circled and a popup information window will appear with links to relevant astronomical databases (ADS, SIMBAD, NED, VizieR).
The popup window can be moved by left clicking and dragging it.
* In ESASky, right click to display a popup window, with links to various astronomical databases (SIMBAD, NED, VizieR) for the corresponding coordinates.
* In Aladin, right click to display a popup menu. Use the "What is this?" menu to display a window with a link to the CDS astronomical database.
ESASky and Aladin are able to overlay catalog data:
* In ESASky, zoom in to the area of interest, then press the "Explore catalog data for this region" button. A window will appear showing available catalogs. Left click and boxes will be overlaid on the map for each catalog entry. The data is also displayed in tabular form. You can left click on a box to view its data in the table.
* In Aladin, zoom in to the areae of interest, then press the "Manage layers" button. In the popup window, select one or more of the available catalogs, or press Add catalogue to add a new one. Boxes will be overlaid on the map for each catalog entry. Left click on a box to display the data for it in tabular form.
<h2>Attribution</h2>
Constellation icons created by Freepik - https://www.flaticon.com
<h2>API</h2>
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to centre the sky map on an object from the command line:
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/actions" -d '{"featureType": "SkyMap", "SkyMapActions": { "find": "polaris" }}'
And to centre the sky map at a particular RA and dec (Not for WWT):
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/actions" -d '{"featureType": "SkyMap", "SkyMapActions": { "find": "18 36 56 +38 47 01" }}'

Wyświetl plik

@ -0,0 +1,510 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2021-2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
// //
// 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 <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QTimer>
#include "SWGFeatureSettings.h"
#include "SWGFeatureReport.h"
#include "SWGFeatureActions.h"
#include "SWGDeviceState.h"
#include "dsp/dspengine.h"
#include "device/deviceset.h"
#include "channel/channelapi.h"
#include "feature/featureset.h"
#include "settings/serializable.h"
#include "maincore.h"
#include "skymap.h"
MESSAGE_CLASS_DEFINITION(SkyMap::MsgConfigureSkyMap, Message)
MESSAGE_CLASS_DEFINITION(SkyMap::MsgFind, Message)
MESSAGE_CLASS_DEFINITION(SkyMap::MsgSetDateTime, Message)
MESSAGE_CLASS_DEFINITION(SkyMap::MsgReportViewDetails, Message)
const char* const SkyMap::m_featureIdURI = "sdrangel.feature.skymap";
const char* const SkyMap::m_featureId = "SkyMap";
SkyMap::SkyMap(WebAPIAdapterInterface *webAPIAdapterInterface) :
Feature(m_featureIdURI, webAPIAdapterInterface),
m_multiplier(0.0)
{
qDebug("SkyMap::SkyMap: webAPIAdapterInterface: %p", webAPIAdapterInterface);
setObjectName(m_featureId);
m_state = StIdle;
m_errorMessage = "SkyMap error";
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SkyMap::networkManagerFinished
);
}
SkyMap::~SkyMap()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SkyMap::networkManagerFinished
);
delete m_networkManager;
}
bool SkyMap::handleMessage(const Message& cmd)
{
if (MsgConfigureSkyMap::match(cmd))
{
MsgConfigureSkyMap& cfg = (MsgConfigureSkyMap&) cmd;
qDebug() << "SkyMap::handleMessage: MsgConfigureSkyMap";
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else if (MsgReportViewDetails::match(cmd))
{
MsgReportViewDetails& report = (MsgReportViewDetails&) cmd;
m_viewDetails = report.getViewDetails();
return true;
}
else
{
return false;
}
}
QByteArray SkyMap::serialize() const
{
return m_settings.serialize();
}
bool SkyMap::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return false;
}
}
void SkyMap::applySettings(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "SkyMap::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
if (settingsKeys.contains("useReverseAPI"))
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||
settingsKeys.contains("reverseAPIAddress") ||
settingsKeys.contains("reverseAPIPort") ||
settingsKeys.contains("reverseAPIFeatureSetIndex") ||
settingsKeys.contains("m_reverseAPIFeatureIndex");
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
int SkyMap::webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) run;
(void) errorMessage;
getFeatureStateStr(*response.getState());
return 202;
}
int SkyMap::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
response.getSkyMapSettings()->init();
webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SkyMap::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
SkyMapSettings settings = m_settings;
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(settings, featureSettingsKeys, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureSkyMap *msgToGUI = MsgConfigureSkyMap::create(settings, featureSettingsKeys, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatFeatureSettings(response, settings);
return 200;
}
int SkyMap::webapiReportGet(
SWGSDRangel::SWGFeatureReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSkyMapReport(new SWGSDRangel::SWGSkyMapReport());
response.getSkyMapReport()->init();
webapiFormatFeatureReport(response);
return 200;
}
int SkyMap::webapiActionsPost(
const QStringList& featureActionsKeys,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage)
{
SWGSDRangel::SWGSkyMapActions *swgSkyMapActions = query.getSkyMapActions();
if (swgSkyMapActions)
{
if (featureActionsKeys.contains("find"))
{
QString id = *swgSkyMapActions->getFind();
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgFind::create(id));
}
}
/*if (featureActionsKeys.contains("setDateTime"))
{
QString dateTimeString = *swgSkyMapActions->getSetDateTime();
QDateTime dateTime = QDateTime::fromString(dateTimeString, Qt::ISODateWithMs);
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgSetDateTime::create(dateTime));
}
}*/
return 202;
}
else
{
errorMessage = "Missing SkyMapActions in query";
return 400;
}
return 400;
}
void SkyMap::webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SkyMapSettings& settings)
{
response.getSkyMapSettings()->setDisplayNames(settings.m_displayNames ? 1 : 0);
response.getSkyMapSettings()->setDisplayConstellations(settings.m_displayConstellations ? 1 : 0);
response.getSkyMapSettings()->setDisplayReticle(settings.m_displayReticle ? 1 : 0);
response.getSkyMapSettings()->setDisplayGrid(settings.m_displayGrid ? 1 : 0);
response.getSkyMapSettings()->setDisplayAntennaFoV(settings.m_displayAntennaFoV ? 1 : 0);
response.getSkyMapSettings()->setMap(new QString(settings.m_map));
response.getSkyMapSettings()->setBackground(new QString(settings.m_background));
response.getSkyMapSettings()->setProjection(new QString(settings.m_projection));
response.getSkyMapSettings()->setSource(new QString(settings.m_source));
response.getSkyMapSettings()->setTrack(settings.m_track ? 1 : 0);
response.getSkyMapSettings()->setLatitude(settings.m_latitude);
response.getSkyMapSettings()->setLongitude(settings.m_longitude);
response.getSkyMapSettings()->setAltitude(settings.m_altitude);
response.getSkyMapSettings()->setHpbw(settings.m_hpbw);
response.getSkyMapSettings()->setUseMyPosition(settings.m_useMyPosition);
if (response.getSkyMapSettings()->getTitle()) {
*response.getSkyMapSettings()->getTitle() = settings.m_title;
} else {
response.getSkyMapSettings()->setTitle(new QString(settings.m_title));
}
response.getSkyMapSettings()->setRgbColor(settings.m_rgbColor);
response.getSkyMapSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getSkyMapSettings()->getReverseApiAddress()) {
*response.getSkyMapSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getSkyMapSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getSkyMapSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getSkyMapSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
response.getSkyMapSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
if (settings.m_rollupState)
{
if (response.getSkyMapSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getSkyMapSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getSkyMapSettings()->setRollupState(swgRollupState);
}
}
}
void SkyMap::webapiUpdateFeatureSettings(
SkyMapSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response)
{
if (featureSettingsKeys.contains("displayNames")) {
settings.m_displayNames = response.getSkyMapSettings()->getDisplayNames();
}
if (featureSettingsKeys.contains("displayConstellations")) {
settings.m_displayConstellations = response.getSkyMapSettings()->getDisplayConstellations();
}
if (featureSettingsKeys.contains("displayReticle")) {
settings.m_displayReticle = response.getSkyMapSettings()->getDisplayReticle();
}
if (featureSettingsKeys.contains("displayGrid")) {
settings.m_displayGrid = response.getSkyMapSettings()->getDisplayGrid();
}
if (featureSettingsKeys.contains("displayAntennaFoV")) {
settings.m_displayAntennaFoV = response.getSkyMapSettings()->getDisplayAntennaFoV();
}
if (featureSettingsKeys.contains("map")) {
settings.m_map = *response.getSkyMapSettings()->getMap();
}
if (featureSettingsKeys.contains("background")) {
settings.m_background = *response.getSkyMapSettings()->getBackground();
}
if (featureSettingsKeys.contains("projection")) {
settings.m_projection = *response.getSkyMapSettings()->getProjection();
}
if (featureSettingsKeys.contains("source")) {
settings.m_source = *response.getSkyMapSettings()->getSource();
}
if (featureSettingsKeys.contains("track")) {
settings.m_track = response.getSkyMapSettings()->getTrack();
}
if (featureSettingsKeys.contains("latitude")) {
settings.m_latitude = response.getSkyMapSettings()->getLatitude();
}
if (featureSettingsKeys.contains("longitude")) {
settings.m_longitude = response.getSkyMapSettings()->getLongitude();
}
if (featureSettingsKeys.contains("altitude")) {
settings.m_altitude = response.getSkyMapSettings()->getAltitude();
}
if (featureSettingsKeys.contains("hpbw")) {
settings.m_hpbw = response.getSkyMapSettings()->getHpbw();
}
if (featureSettingsKeys.contains("useMyPosition")) {
settings.m_useMyPosition = response.getSkyMapSettings()->getUseMyPosition();
}
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getSkyMapSettings()->getTitle();
}
if (featureSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getSkyMapSettings()->getRgbColor();
}
if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getSkyMapSettings()->getUseReverseApi() != 0;
}
if (featureSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getSkyMapSettings()->getReverseApiAddress();
}
if (featureSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getSkyMapSettings()->getReverseApiPort();
}
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
settings.m_reverseAPIFeatureSetIndex = response.getSkyMapSettings()->getReverseApiFeatureSetIndex();
}
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
settings.m_reverseAPIFeatureIndex = response.getSkyMapSettings()->getReverseApiFeatureIndex();
}
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getSkyMapSettings()->getRollupState());
}
}
void SkyMap::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SkyMapSettings& settings, bool force)
{
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
swgFeatureSettings->setFeatureType(new QString("SkyMap"));
swgFeatureSettings->setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
SWGSDRangel::SWGSkyMapSettings *swgSkyMapSettings = swgFeatureSettings->getSkyMapSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (featureSettingsKeys.contains("displayNames") || force) {
swgSkyMapSettings->setDisplayNames(settings.m_displayNames);
}
if (featureSettingsKeys.contains("displayConstellations") || force) {
swgSkyMapSettings->setDisplayConstellations(settings.m_displayConstellations);
}
if (featureSettingsKeys.contains("displayReticle") || force) {
swgSkyMapSettings->setDisplayReticle(settings.m_displayReticle);
}
if (featureSettingsKeys.contains("displayGrid") || force) {
swgSkyMapSettings->setDisplayGrid(settings.m_displayGrid);
}
if (featureSettingsKeys.contains("displayAntennaFoV") || force) {
swgSkyMapSettings->setDisplayAntennaFoV(settings.m_displayAntennaFoV);
}
if (featureSettingsKeys.contains("map") || force) {
swgSkyMapSettings->setMap(new QString(settings.m_map));
}
if (featureSettingsKeys.contains("background") || force) {
swgSkyMapSettings->setBackground(new QString(settings.m_background));
}
if (featureSettingsKeys.contains("projection") || force) {
swgSkyMapSettings->setProjection(new QString(settings.m_projection));
}
if (featureSettingsKeys.contains("source") || force) {
swgSkyMapSettings->setSource(new QString(settings.m_source));
}
if (featureSettingsKeys.contains("track") || force) {
swgSkyMapSettings->setTrack(settings.m_track);
}
if (featureSettingsKeys.contains("latitude") || force) {
swgSkyMapSettings->setLatitude(settings.m_latitude);
}
if (featureSettingsKeys.contains("longitude") || force) {
swgSkyMapSettings->setLongitude(settings.m_longitude);
}
if (featureSettingsKeys.contains("altitude") || force) {
swgSkyMapSettings->setAltitude(settings.m_altitude);
}
if (featureSettingsKeys.contains("hpbw") || force) {
swgSkyMapSettings->setHpbw(settings.m_hpbw);
}
if (featureSettingsKeys.contains("useMyPosition") || force) {
swgSkyMapSettings->setTrack(settings.m_useMyPosition);
}
if (featureSettingsKeys.contains("title") || force) {
swgSkyMapSettings->setTitle(new QString(settings.m_title));
}
if (featureSettingsKeys.contains("rgbColor") || force) {
swgSkyMapSettings->setRgbColor(settings.m_rgbColor);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIFeatureSetIndex)
.arg(settings.m_reverseAPIFeatureIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgFeatureSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgFeatureSettings;
}
void SkyMap::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response)
{
QString skymapDateTime = getSkyMapDateTime().toString(Qt::ISODateWithMs);
if (response.getSkyMapReport()->getDateTime()) {
//*response.getSkyMapReport()->getDateTime() = skymapDateTime;
*response.getSkyMapReport()->getDateTime() = m_viewDetails.m_dateTime.toString(Qt::ISODateWithMs);
} else {
//response.getSkyMapReport()->setDateTime(new QString(skymapDateTime));
response.getSkyMapReport()->setDateTime(new QString(m_viewDetails.m_dateTime.toString(Qt::ISODateWithMs)));
}
response.getSkyMapReport()->setRa(m_viewDetails.m_ra);
response.getSkyMapReport()->setDec(m_viewDetails.m_dec);
response.getSkyMapReport()->setAzimuth(m_viewDetails.m_azimuth);
response.getSkyMapReport()->setElevation(m_viewDetails.m_elevation);
response.getSkyMapReport()->setFov(m_viewDetails.m_fov);
response.getSkyMapReport()->setLatitude(m_viewDetails.m_latitude);
response.getSkyMapReport()->setLongitude(m_viewDetails.m_longitude);
}
void SkyMap::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "SkyMap::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("SkyMap::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void SkyMap::setSkyMapDateTime(QDateTime skymapDateTime, QDateTime systemDateTime, double multiplier)
{
QMutexLocker mutexLocker(&m_dateTimeMutex);
m_skymapDateTime = skymapDateTime;
m_systemDateTime = systemDateTime;
m_multiplier = multiplier;
}
QDateTime SkyMap::getSkyMapDateTime()
{
QMutexLocker mutexLocker(&m_dateTimeMutex);
if (m_multiplier == 0.0)
{
return m_skymapDateTime;
}
else
{
// It's not possible to synchronously get the time from Cesium
// so we calculate it based on the system clock difference from
// when changes were made to the clock GUI elements
// Should be accurate enough for satellite tracker
qint64 diffMsecs = m_systemDateTime.msecsTo(QDateTime::currentDateTime());
return m_skymapDateTime.addMSecs(diffMsecs * m_multiplier);
}
}

Wyświetl plik

@ -0,0 +1,221 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
// //
// 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_FEATURE_SKYMAP_H_
#define INCLUDE_FEATURE_SKYMAP_H_
#include <QHash>
#include <QNetworkRequest>
#include <QDateTime>
#include <QRecursiveMutex>
#include "feature/feature.h"
#include "util/message.h"
#include "skymapsettings.h"
class WebAPIAdapterInterface;
class QNetworkAccessManager;
class QNetworkReply;
namespace SWGSDRangel {
class SWGDeviceState;
}
class SkyMap : public Feature
{
Q_OBJECT
public:
class MsgConfigureSkyMap : public Message {
MESSAGE_CLASS_DECLARATION
public:
const SkyMapSettings& getSettings() const { return m_settings; }
const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
bool getForce() const { return m_force; }
static MsgConfigureSkyMap* create(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force) {
return new MsgConfigureSkyMap(settings, settingsKeys, force);
}
private:
SkyMapSettings m_settings;
QList<QString> m_settingsKeys;
bool m_force;
MsgConfigureSkyMap(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force) :
Message(),
m_settings(settings),
m_settingsKeys(settingsKeys),
m_force(force)
{ }
};
class MsgFind : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getTarget() const { return m_target; }
static MsgFind* create(const QString& target) {
return new MsgFind(target);
}
private:
QString m_target;
MsgFind(const QString& target) :
Message(),
m_target(target)
{}
};
class MsgSetDateTime : public Message {
MESSAGE_CLASS_DECLARATION
public:
QDateTime getDateTime() const { return m_dateTime; }
static MsgSetDateTime* create(const QDateTime& dateTime) {
return new MsgSetDateTime(dateTime);
}
private:
QDateTime m_dateTime;
MsgSetDateTime(const QDateTime& dateTime) :
Message(),
m_dateTime(dateTime)
{}
};
struct ViewDetails {
double m_ra;
double m_dec;
float m_azimuth;
float m_elevation;
float m_fov;
float m_latitude;
float m_longitude;
QDateTime m_dateTime;
ViewDetails() :
m_ra(0.0),
m_dec(0.0),
m_azimuth(0.0f),
m_elevation(0.0f),
m_fov(0.0f),
m_latitude(0.0f),
m_longitude(0.0f)
{
}
};
class MsgReportViewDetails : public Message {
MESSAGE_CLASS_DECLARATION
public:
const ViewDetails& getViewDetails() { return m_viewDetails; }
static MsgReportViewDetails* create(const ViewDetails& viewDetails) {
return new MsgReportViewDetails(viewDetails);
}
private:
ViewDetails m_viewDetails;
MsgReportViewDetails(const ViewDetails& viewDetails) :
Message(),
m_viewDetails(viewDetails)
{}
};
SkyMap(WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~SkyMap();
virtual void destroy() { delete this; }
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) const { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGFeatureReport& response,
QString& errorMessage);
virtual int webapiActionsPost(
const QStringList& featureActionsKeys,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage);
static void webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SkyMapSettings& settings);
static void webapiUpdateFeatureSettings(
SkyMapSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response);
void setSkyMapDateTime(QDateTime skymapDateTime, QDateTime systemDateTime, double multiplier);
QDateTime getSkyMapDateTime();
static const char* const m_featureIdURI;
static const char* const m_featureId;
private:
SkyMapSettings m_settings;
ViewDetails m_viewDetails;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void applySettings(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force = false);
void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response);
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SkyMapSettings& settings, bool force);
QDateTime m_skymapDateTime;
QDateTime m_systemDateTime;
double m_multiplier;
QRecursiveMutex m_dateTimeMutex;
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_FEATURE_SKYMAP_H_

Wyświetl plik

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/skymap/">
<file>html/wwt.html</file>
<file>html/esasky.html</file>
<file>html/aladin.html</file>
</qresource>
</RCC>

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,153 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SKYMAPGUI_H_
#define INCLUDE_FEATURE_SKYMAPGUI_H_
#include <QTimer>
#include <QJsonObject>
#include <QWebEngineFullScreenRequest>
#include <QWebEnginePage>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QWebEngineLoadingInfo>
#endif
#include <QGeoCoordinate>
#include <math.h>
#include <limits>
#include "feature/featuregui.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "maincore.h"
#include "skymapsettings.h"
#include "webinterface.h"
#include "webserver.h"
#include "wtml.h"
class PluginAPI;
class FeatureUISet;
class SkyMap;
namespace Ui {
class SkyMapGUI;
}
class SkyMapGUI;
class SkyMapGUI : public FeatureGUI {
Q_OBJECT
public:
static SkyMapGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index);
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }
SkyMap *getSkyMap() { return m_skymap; }
private:
Ui::SkyMapGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
SkyMapSettings m_settings;
QList<QString> m_settingsKeys;
RollupState m_rollupState;
bool m_doApplySettings;
QList<MainCore::AvailableChannelOrFeature> m_availableChannelOrFeatures;
QObject *m_source;
SkyMap* m_skymap;
MessageQueue m_inputMessageQueue;
quint16 m_skymapTileServerPort;
WebServer *m_webServer;
quint16 m_webPort;
WTML m_wtml;
WebInterface *m_webInterface;
double m_ra; //!< Target from source plugin
double m_dec;
QDateTime m_dateTime; //!< Date time from source plugin
explicit SkyMapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~SkyMapGUI();
void blockApplySettings(bool block);
void applySetting(const QString& settingsKey);
void applySettings(const QStringList& settingsKeys, bool force = false);
void applyAllSettings();
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void setDateTime(QDateTime dateTime);
QDateTime getDateTime() const;
void setPosition(float latitude, float longitude, float altitude);
QGeoCoordinate getPosition() const;
void initSkyMap();
QString backgroundID(const QString& name);
void updateBackgrounds();
void updateToolbar();
void updateProjection();
void find(const QString& text);
void sendToRotator(const QString& name, double az, double alt);
void updateSourceList();
void registerPipe(QObject *object);
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_tabs_tabCloseRequested(int index);
void on_find_returnPressed();
void on_displaySettings_clicked();
void on_displayNames_clicked(bool checked);
void on_displayConstellations_clicked(bool checked);
void on_displayReticle_clicked(bool checked);
void on_displayGrid_clicked(bool checked);
void on_displayAntennaFoV_clicked(bool checked);
void on_map_currentIndexChanged(int index);
void on_background_currentIndexChanged(int index);
void on_projection_currentIndexChanged(int index);
void on_source_currentIndexChanged(int index);
void on_track_clicked(bool checked);
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
void renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void loadingChanged(const QWebEngineLoadingInfo &loadingInfo);
#endif
void preferenceChanged(int elementType);
void receivedEvent(const QJsonObject &obj);
void wtmlUpdated(const QList<WTML::ImageSet>& dataSets);
void handleFeatureAdded(int featureSetIndex, Feature *feature);
void handleFeatureRemoved(int featureSetIndex, Feature *oldFeature);
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
void handleChannelRemoved(int deviceSetIndex, ChannelAPI *channel);
void handlePipeMessageQueue(MessageQueue* messageQueue);
};
#endif // INCLUDE_FEATURE_SKYMAPGUI_H_

Wyświetl plik

@ -0,0 +1,425 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SkyMapGUI</class>
<widget class="RollupContents" name="SkyMapGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>881</width>
<height>293</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>SkyMap</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>871</width>
<height>41</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QLabel" name="findLabel">
<property name="text">
<string>Find</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="find">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Enter name of object to find</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="map">
<property name="toolTip">
<string>Sky map</string>
</property>
<item>
<property name="text">
<string>WWT</string>
</property>
</item>
<item>
<property name="text">
<string>ESASky</string>
</property>
</item>
<item>
<property name="text">
<string>Aladin</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="background">
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Select background image set</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="projection">
<property name="toolTip">
<string>Projection</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayNames">
<property name="toolTip">
<string>Display names</string>
</property>
<property name="text">
<string>^</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/info.png</normaloff>:/info.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayConstellations">
<property name="toolTip">
<string>Display constellations</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/skymap/icons/constellation.png</normaloff>:/skymap/icons/constellation.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayGrid">
<property name="toolTip">
<string>Display grid</string>
</property>
<property name="text">
<string>^</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/gridrect.png</normaloff>:/gridrect.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayReticle">
<property name="toolTip">
<string>Display reticle</string>
</property>
<property name="text">
<string>^</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/skymap/icons/reticle.png</normaloff>:/skymap/icons/reticle.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayAntennaFoV">
<property name="toolTip">
<string>Display antenna field-of-view</string>
</property>
<property name="text">
<string>^</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/antenna.png</normaloff>:/antenna.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="track">
<property name="toolTip">
<string>Track source</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/gridpolar.png</normaloff>:/gridpolar.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="source">
<property name="toolTip">
<string>Source feature to get target from</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="displaySettings">
<property name="toolTip">
<string>Show settings dialog</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="skymapContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>591</width>
<height>211</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>SkyMap</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabs">
<property name="tabsClosable">
<bool>true</bool>
</property>
<property name="tabBarAutoHide">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab0">
<attribute name="title">
<string>Map</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="WebView" name="web" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QWebEngineView</class>
<extends>QWidget</extends>
<header location="global">QtWebEngineWidgets/QWebEngineView</header>
<container>1</container>
</customwidget>
<customwidget>
<class>WebView</class>
<extends>QWebEngineView</extends>
<header>webview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>find</tabstop>
<tabstop>background</tabstop>
<tabstop>displayNames</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

Wyświetl plik

@ -0,0 +1,84 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2019 Davide Gerhard <rainbow@irh.it> //
// Copyright (C) 2020-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.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 <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "skymapgui.h"
#endif
#include "skymap.h"
#include "skymapplugin.h"
#include "skymapwebapiadapter.h"
const PluginDescriptor SkyMapPlugin::m_pluginDescriptor = {
SkyMap::m_featureId,
QStringLiteral("Sky Map"),
QStringLiteral("7.18.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
SkyMapPlugin::SkyMapPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& SkyMapPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void SkyMapPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerFeature(SkyMap::m_featureIdURI, SkyMap::m_featureId, this);
}
#ifdef SERVER_MODE
FeatureGUI* SkyMapPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
(void) featureUISet;
(void) feature;
return nullptr;
}
#else
FeatureGUI* SkyMapPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return SkyMapGUI::create(m_pluginAPI, featureUISet, feature);
}
#endif
Feature* SkyMapPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
{
return new SkyMap(webAPIAdapterInterface);
}
FeatureWebAPIAdapter* SkyMapPlugin::createFeatureWebAPIAdapter() const
{
return new SkyMapWebAPIAdapter();
}

Wyświetl plik

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2015 John Greb <hexameron@spam.no> //
// Copyright (C) 2020-2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SKYMAPPLUGIN_H
#define INCLUDE_FEATURE_SKYMAPPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FeatureGUI;
class WebAPIAdapterInterface;
class SkyMapPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.feature.skymap")
public:
explicit SkyMapPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FEATURE_SKYMAPPLUGIN_H

Wyświetl plik

@ -0,0 +1,361 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2017, 2019-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 CRD716 <crd716@gmail.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 <QColor>
#include <QDebug>
#include "util/simpleserializer.h"
#include "util/httpdownloadmanager.h"
#include "settings/serializable.h"
#include "skymapsettings.h"
const QStringList SkyMapSettings::m_pipeTypes = {
QStringLiteral("StarTracker"),
QStringLiteral("SatelliteTracker"),
QStringLiteral("GS232Controller"),
QStringLiteral("Map")
};
const QStringList SkyMapSettings::m_pipeURIs = {
QStringLiteral("sdrangel.feature.startracker"),
QStringLiteral("sdrangel.feature.satellitetracker"),
QStringLiteral("sdrangel.feature.gs232controller"),
QStringLiteral("sdrangel.feature.map")
};
SkyMapSettings::SkyMapSettings() :
m_rollupState(nullptr)
{
resetToDefaults();
}
SkyMapSettings::~SkyMapSettings()
{
}
void SkyMapSettings::resetToDefaults()
{
m_map = "WWT";
m_displayNames = true;
m_displayConstellations = true;
m_displayReticle = true;
m_displayGrid = false;
m_displayAntennaFoV = false;
m_projection = "";
m_source = "";
m_track = false;
m_hpbw = 10.0f;
m_latitude = 0.0f;
m_longitude = 0.0f;
m_altitude = 0.0f;
m_useMyPosition = false;
m_wwtSettings = QHash<QString, QVariant>({
{"constellationBoundaries", false},
{"constellationFigures", true},
{"constellationLabels", true},
{"constellationPictures", false},
{"constellationSelection", false},
{"ecliptic", false},
{"eclipticOverviewText", false},
{"eclipticGrid", false},
{"eclipticGridText", false},
{"altAzGrid", false},
{"altAzGridText", false},
{"galacticGrid", false},
{"galacticGridText", false},
{"elevationModel", false},
{"earthSky", false},
{"horizon", false},
{"iss", false},
{"precessionChart", false},
{"skyGrids", false},
{"skyNode", false},
{"skyOverlays", false},
{"solarSystemCosmos", false},
{"solarSystemLighting", true},
{"solarSystemMilkyWay", true},
{"solarSystemMinorOrbits", false},
{"solarSystemMinorPlanets", false},
{"solarSystemMultiRes", true},
{"solarSystemOrbits", true},
{"solarSystemOverlays", false},
{"solarSystemPlanets", true},
{"solarSystemStars", true},
});
m_title = "Sky Map";
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIFeatureSetIndex = 0;
m_reverseAPIFeatureIndex = 0;
m_workspaceIndex = 0;
}
QByteArray SkyMapSettings::serialize() const
{
SimpleSerializer s(1);
s.writeString(2, m_map);
s.writeBool(1, m_displayNames);
s.writeBool(15, m_displayConstellations);
s.writeBool(17, m_displayReticle);
s.writeBool(18, m_displayGrid);
s.writeBool(21, m_displayAntennaFoV);
s.writeString(3, m_projection);
s.writeString(4, m_source);
s.writeBool(20, m_track);
s.writeFloat(22, m_hpbw);
s.writeFloat(23, m_latitude);
s.writeFloat(24, m_longitude);
s.writeFloat(25, m_altitude);
s.writeBool(26, m_useMyPosition);
s.writeHash(27, m_wwtSettings);
s.writeString(8, m_title);
s.writeU32(9, m_rgbColor);
s.writeBool(10, m_useReverseAPI);
s.writeString(11, m_reverseAPIAddress);
s.writeU32(12, m_reverseAPIPort);
s.writeU32(13, m_reverseAPIFeatureSetIndex);
s.writeU32(14, m_reverseAPIFeatureIndex);
if (m_rollupState) {
s.writeBlob(19, m_rollupState->serialize());
}
s.writeS32(33, m_workspaceIndex);
s.writeBlob(34, m_geometryBytes);
return s.final();
}
bool SkyMapSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
QByteArray blob;
QString string;
d.readString(2, &m_map, "WWT");
d.readBool(1, &m_displayNames, true);
d.readBool(15, &m_displayConstellations, true);
d.readBool(17, &m_displayReticle, true);
d.readBool(18, &m_displayGrid, true);
d.readBool(21, &m_displayAntennaFoV, true);
d.readString(3, &m_projection, "");
d.readString(4, &m_source, "");
d.readBool(20, &m_track, false);
d.readFloat(22, &m_hpbw, 10.0f);
d.readFloat(23, &m_latitude, 0.0f);
d.readFloat(24, &m_longitude, 0.0f);
d.readFloat(25, &m_altitude, 0.0f);
d.readBool(26, &m_useMyPosition, false);
d.readHash(27, &m_wwtSettings);
d.readString(8, &m_title, "Sky Map");
d.readU32(9, &m_rgbColor, QColor(225, 25, 99).rgba());
d.readBool(10, &m_useReverseAPI, false);
d.readString(11, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(12, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(13, &utmp, 0);
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
d.readU32(14, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
if (m_rollupState)
{
d.readBlob(19, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(33, &m_workspaceIndex, 0);
d.readBlob(34, &m_geometryBytes);
return true;
}
else
{
resetToDefaults();
return false;
}
}
void SkyMapSettings::applySettings(const QStringList& settingsKeys, const SkyMapSettings& settings)
{
if (settingsKeys.contains("map")) {
m_map = settings.m_map;
}
if (settingsKeys.contains("displayNames")) {
m_displayNames = settings.m_displayNames;
}
if (settingsKeys.contains("displayConstellations")) {
m_displayConstellations = settings.m_displayConstellations;
}
if (settingsKeys.contains("displayReticle")) {
m_displayReticle = settings.m_displayReticle;
}
if (settingsKeys.contains("displayGrid")) {
m_displayGrid = settings.m_displayGrid;
}
if (settingsKeys.contains("displayAntennaFoV")) {
m_displayAntennaFoV = settings.m_displayAntennaFoV;
}
if (settingsKeys.contains("background")) {
m_background = settings.m_background;
}
if (settingsKeys.contains("projection")) {
m_projection = settings.m_projection;
}
if (settingsKeys.contains("source")) {
m_source = settings.m_source;
}
if (settingsKeys.contains("track")) {
m_track = settings.m_track;
}
if (settingsKeys.contains("hpbw")) {
m_hpbw = settings.m_hpbw;
}
if (settingsKeys.contains("latitude")) {
m_latitude = settings.m_latitude;
}
if (settingsKeys.contains("longitude")) {
m_longitude = settings.m_longitude;
}
if (settingsKeys.contains("altitude")) {
m_altitude = settings.m_altitude;
}
if (settingsKeys.contains("useMyPosition")) {
m_useMyPosition = settings.m_useMyPosition;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
if (settingsKeys.contains("reverseAPIAddress")) {
m_reverseAPIAddress = settings.m_reverseAPIAddress;
}
if (settingsKeys.contains("reverseAPIPort")) {
m_reverseAPIPort = settings.m_reverseAPIPort;
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex")) {
m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex")) {
m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
}
QString SkyMapSettings::getDebugString(const QStringList& settingsKeys, bool force) const
{
std::ostringstream ostr;
if (settingsKeys.contains("map") || force) {
ostr << " m_map: " << m_map.toStdString();
}
if (settingsKeys.contains("displayNames") || force) {
ostr << " m_displayNames: " << m_displayNames;
}
if (settingsKeys.contains("displayConstellations") || force) {
ostr << " m_displayConstellations: " << m_displayConstellations;
}
if (settingsKeys.contains("displayReticle") || force) {
ostr << " m_displayReticle: " << m_displayReticle;
}
if (settingsKeys.contains("displayAntennaFoV") || force) {
ostr << " m_displayAntennaFoV: " << m_displayAntennaFoV;
}
if (settingsKeys.contains("background") || force) {
ostr << " m_background: " << m_background.toStdString();
}
if (settingsKeys.contains("projection") || force) {
ostr << " m_projection: " << m_projection.toStdString();
}
if (settingsKeys.contains("source") || force) {
ostr << " m_source: " << m_source.toStdString();
}
if (settingsKeys.contains("track") || force) {
ostr << " m_track: " << m_track;
}
if (settingsKeys.contains("hpbw") || force) {
ostr << " m_hpbw: " << m_hpbw;
}
if (settingsKeys.contains("latitude") || force) {
ostr << " m_latitude: " << m_latitude;
}
if (settingsKeys.contains("longitude") || force) {
ostr << " m_longitude: " << m_longitude;
}
if (settingsKeys.contains("altitude") || force) {
ostr << " m_altitude: " << m_altitude;
}
if (settingsKeys.contains("useMyPosition") || force) {
ostr << " m_useMyPosition: " << m_useMyPosition;
}
if (settingsKeys.contains("title") || force) {
ostr << " m_title: " << m_title.toStdString();
}
if (settingsKeys.contains("useReverseAPI") || force) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}
if (settingsKeys.contains("reverseAPIAddress") || force) {
ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString();
}
if (settingsKeys.contains("reverseAPIPort") || force) {
ostr << " m_reverseAPIPort: " << m_reverseAPIPort;
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) {
ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}
return QString(ostr.str().c_str());
}

Wyświetl plik

@ -0,0 +1,75 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2020-2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SKYMAPSETTINGS_H_
#define INCLUDE_FEATURE_SKYMAPSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <QHash>
class Serializable;
struct SkyMapSettings
{
QString m_map; //!< "WWT", "ESASky" or "Aladin"
bool m_displayNames;
bool m_displayConstellations;
bool m_displayReticle;
bool m_displayGrid;
bool m_displayAntennaFoV;
QString m_background;
QString m_projection;
QString m_source; //!< Plugin to get target coords from
bool m_track; //!< Track source
float m_hpbw; //!< Antenna HPBW
float m_latitude; //!< Antenna position
float m_longitude;
float m_altitude;
bool m_useMyPosition;
QHash<QString, QVariant> m_wwtSettings; //!< WWT specific settings
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIFeatureSetIndex;
uint16_t m_reverseAPIFeatureIndex;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
SkyMapSettings();
~SkyMapSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void applySettings(const QStringList& settingsKeys, const SkyMapSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
static const QStringList m_pipeTypes;
static const QStringList m_pipeURIs;
};
#endif // INCLUDE_FEATURE_SKYMAPSETTINGS_H_

Wyświetl plik

@ -0,0 +1,176 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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 <QDebug>
#include <QToolButton>
#include "skymapsettingsdialog.h"
SkyMapSettingsDialog::SkyMapSettingsDialog(SkyMapSettings *settings, QWidget* parent) :
QDialog(parent),
m_settings(settings),
ui(new Ui::SkyMapSettingsDialog)
{
ui->setupUi(this);
ui->hpbw->setValue(m_settings->m_hpbw);
ui->latitude->setText(QString::number(m_settings->m_latitude, 'f', 6));
ui->longitude->setText(QString::number(m_settings->m_longitude, 'f', 6));
ui->altitude->setValue(m_settings->m_altitude);
ui->useMyPosition->setChecked(m_settings->m_useMyPosition);
// WWT
ui->constellationBoundaries->setChecked(m_settings->m_wwtSettings.value("constellationBoundaries").toBool());
ui->constellationFigures->setChecked(m_settings->m_wwtSettings.value("constellationFigures").toBool());
ui->constellationLabels->setChecked(m_settings->m_wwtSettings.value("constellationLabels").toBool());
ui->constellationPictures->setChecked(m_settings->m_wwtSettings.value("constellationPictures").toBool());
ui->constellationSelection->setChecked(m_settings->m_wwtSettings.value("constellationSelection").toBool());
ui->ecliptic->setChecked(m_settings->m_wwtSettings.value("ecliptic").toBool());
ui->eclipticOverviewText->setChecked(m_settings->m_wwtSettings.value("eclipticOverviewText").toBool());
ui->eclipticGrid->setChecked(m_settings->m_wwtSettings.value("eclipticGrid").toBool());
ui->eclipticGridText->setChecked(m_settings->m_wwtSettings.value("eclipticGridText").toBool());
ui->altAzGrid->setChecked(m_settings->m_wwtSettings.value("altAzGrid").toBool());
ui->altAzGridText->setChecked(m_settings->m_wwtSettings.value("altAzGridText").toBool());
ui->equatorialGrid->setChecked(m_settings->m_wwtSettings.value("equatorialGrid").toBool());
ui->equatorialGridText->setChecked(m_settings->m_wwtSettings.value("equatorialGridText").toBool());
ui->galacticGrid->setChecked(m_settings->m_wwtSettings.value("galacticGrid").toBool());
ui->galacticGridText->setChecked(m_settings->m_wwtSettings.value("galacticGridText").toBool());
ui->precessionChart->setChecked(m_settings->m_wwtSettings.value("precessionChart").toBool());
ui->solarSystemCosmos->setChecked(m_settings->m_wwtSettings.value("solarSystemCosmos").toBool());
ui->solarSystemLighting->setChecked(m_settings->m_wwtSettings.value("solarSystemLighting").toBool());
ui->solarSystemMilkyWay->setChecked(m_settings->m_wwtSettings.value("solarSystemMilkyWay").toBool());
ui->solarSystemMinorOrbits->setChecked(m_settings->m_wwtSettings.value("solarSystemMinorOrbits").toBool());
ui->solarSystemMinorPlanets->setChecked(m_settings->m_wwtSettings.value("solarSystemMinorPlanets").toBool());
ui->solarSystemMultiRes->setChecked(m_settings->m_wwtSettings.value("solarSystemMultiRes").toBool());
ui->solarSystemOrbits->setChecked(m_settings->m_wwtSettings.value("solarSystemOrbits").toBool());
//ui->solarSystemOverlays->setChecked(m_settings->m_wwtSettings.value("solarSystemOverlays").toBool());
ui->solarSystemPlanets->setChecked(m_settings->m_wwtSettings.value("solarSystemPlanets").toBool());
ui->solarSystemStars->setChecked(m_settings->m_wwtSettings.value("solarSystemStars").toBool());
ui->iss->setChecked(m_settings->m_wwtSettings.value("iss").toBool());
}
SkyMapSettingsDialog::~SkyMapSettingsDialog()
{
delete ui;
}
void SkyMapSettingsDialog::accept()
{
QDialog::accept();
if (m_settings->m_hpbw != ui->hpbw->value())
{
m_settings->m_hpbw = ui->hpbw->value();
m_settingsKeysChanged.append("hpbw");
}
if (m_settings->m_latitude != ui->latitude->text().toFloat())
{
m_settings->m_latitude = ui->latitude->text().toFloat();
m_settingsKeysChanged.append("latitude");
}
if (m_settings->m_longitude != ui->longitude->text().toFloat())
{
m_settings->m_longitude = ui->longitude->text().toFloat();
m_settingsKeysChanged.append("longitude");
}
if (m_settings->m_altitude != ui->altitude->value())
{
m_settings->m_altitude = ui->altitude->value();
m_settingsKeysChanged.append("altitude");
}
if (m_settings->m_useMyPosition != ui->useMyPosition->isChecked())
{
m_settings->m_useMyPosition = ui->useMyPosition->isChecked();
m_settingsKeysChanged.append("useMyPosition");
}
m_settings->m_wwtSettings.insert("constellationBoundaries", ui->constellationBoundaries->isChecked());
m_settings->m_wwtSettings.insert("constellationFigures", ui->constellationFigures->isChecked());
m_settings->m_wwtSettings.insert("constellationLabels", ui->constellationLabels->isChecked());
m_settings->m_wwtSettings.insert("constellationPictures", ui->constellationPictures->isChecked());
m_settings->m_wwtSettings.insert("constellationSelection", ui->constellationSelection->isChecked());
m_settings->m_wwtSettings.insert("ecliptic", ui->ecliptic->isChecked());
m_settings->m_wwtSettings.insert("eclipticOverviewText", ui->eclipticOverviewText->isChecked());
m_settings->m_wwtSettings.insert("eclipticGrid", ui->eclipticGrid->isChecked());
m_settings->m_wwtSettings.insert("eclipticGridText", ui->eclipticGridText->isChecked());
m_settings->m_wwtSettings.insert("altAzGrid", ui->altAzGrid->isChecked());
m_settings->m_wwtSettings.insert("altAzGridText", ui->altAzGridText->isChecked());
m_settings->m_wwtSettings.insert("equatorialGrid", ui->equatorialGrid->isChecked());
m_settings->m_wwtSettings.insert("equatorialGridText", ui->equatorialGridText->isChecked());
m_settings->m_wwtSettings.insert("galacticGrid", ui->galacticGrid->isChecked());
m_settings->m_wwtSettings.insert("galacticGridText", ui->galacticGridText->isChecked());
m_settings->m_wwtSettings.insert("precessionChart", ui->precessionChart->isChecked());
m_settings->m_wwtSettings.insert("solarSystemCosmos", ui->solarSystemCosmos->isChecked());
m_settings->m_wwtSettings.insert("solarSystemLighting", ui->solarSystemLighting->isChecked());
m_settings->m_wwtSettings.insert("solarSystemMilkyWay", ui->solarSystemMilkyWay->isChecked());
m_settings->m_wwtSettings.insert("solarSystemMinorOrbits", ui->solarSystemMinorOrbits->isChecked());
m_settings->m_wwtSettings.insert("solarSystemMinorPlanets", ui->solarSystemMinorPlanets->isChecked());
m_settings->m_wwtSettings.insert("solarSystemMultiRes", ui->solarSystemMultiRes->isChecked());
m_settings->m_wwtSettings.insert("solarSystemOrbits", ui->solarSystemOrbits->isChecked());
//m_settings->m_wwtSettings.insert("solarSystemOverlays", ui->solarSystemOverlays->isChecked());
m_settings->m_wwtSettings.insert("solarSystemPlanets", ui->solarSystemPlanets->isChecked());
m_settings->m_wwtSettings.insert("solarSystemStars", ui->solarSystemStars->isChecked());
m_settings->m_wwtSettings.insert("iss", ui->iss->isChecked());
m_settingsKeysChanged.append("wwtSettings"); // Being lazy here
}
void SkyMapSettingsDialog::on_constellationBoundaries_toggled(bool checked)
{
ui->constellationSelection->setEnabled(checked);
}
void SkyMapSettingsDialog::on_ecliptic_toggled(bool checked)
{
ui->eclipticOverviewText->setEnabled(checked);
}
void SkyMapSettingsDialog::on_eclipticGrid_toggled(bool checked)
{
ui->eclipticGridText->setEnabled(checked);
}
void SkyMapSettingsDialog::on_altAzGrid_toggled(bool checked)
{
ui->altAzGridText->setEnabled(checked);
}
void SkyMapSettingsDialog::on_equatorialGrid_toggled(bool checked)
{
ui->equatorialGridText->setEnabled(checked);
}
void SkyMapSettingsDialog::on_galacticGrid_toggled(bool checked)
{
ui->galacticGridText->setEnabled(checked);
}
void SkyMapSettingsDialog::on_useMyPosition_toggled(bool checked)
{
ui->latitude->setEnabled(!checked);
ui->latitudeLabel->setEnabled(!checked);
ui->longitude->setEnabled(!checked);
ui->longitudeLabel->setEnabled(!checked);
ui->altitude->setEnabled(!checked);
ui->altitudeLabel->setEnabled(!checked);
}

Wyświetl plik

@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SKYMAPSETTINGSDIALOG_H
#define INCLUDE_FEATURE_SKYMAPSETTINGSDIALOG_H
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QMessageBox>
#include "ui_skymapsettingsdialog.h"
#include "skymapsettings.h"
class SkyMapSettingsDialog : public QDialog {
Q_OBJECT
public:
explicit SkyMapSettingsDialog(SkyMapSettings *settings, QWidget* parent = 0);
~SkyMapSettingsDialog();
public:
QStringList m_settingsKeysChanged; // List of setting keys that have been changed
private:
SkyMapSettings *m_settings;
private slots:
void accept();
void on_constellationBoundaries_toggled(bool checked);
void on_ecliptic_toggled(bool checked);
void on_eclipticGrid_toggled(bool checked);
void on_altAzGrid_toggled(bool checked);
void on_equatorialGrid_toggled(bool checked);
void on_galacticGrid_toggled(bool checked);
void on_useMyPosition_toggled(bool checked);
private:
Ui::SkyMapSettingsDialog* ui;
};
#endif // INCLUDE_FEATURE_SKYMAPSETTINGSDIALOG_H

Wyświetl plik

@ -0,0 +1,480 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SkyMapSettingsDialog</class>
<widget class="QDialog" name="SkyMapSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>441</width>
<height>671</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>SkyMap Display Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Settings</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="useMyPosition">
<property name="text">
<string>Use observation location from Preferences &gt; My Position</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="latitudeLabel">
<property name="text">
<string>Latitude (°)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude (°)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text">
<string>Altitude (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="altitude">
<property name="toolTip">
<string>Antenna/observation altitude in metres</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="hpbwLabel">
<property name="text">
<string>HPBW (°)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="hpbw">
<property name="toolTip">
<string>Antenna half-power beamwidth in degrees</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="latitude">
<property name="toolTip">
<string>Latitude of antenna/observation location in degrees. North positive</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="longitude">
<property name="toolTip">
<string>Longitude of antenna/observation location in degrees. East positive</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="skymapsTab">
<attribute name="title">
<string>WWT</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="gridGroupBox">
<property name="title">
<string>Grids</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="ecliptic">
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Ecliptic</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="eclipticGrid">
<property name="text">
<string>Ecliptic Grid</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="eclipticOverviewText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Ecliptic Overview Text</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="eclipticGridText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Ecliptic Grid Text</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="altAzGrid">
<property name="text">
<string>Alt Az Grid</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="altAzGridText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Alt Az Grid Text</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="equatorialGrid">
<property name="text">
<string>Equatorial Grid</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="equatorialGridText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Equatorial Grid Text</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="galacticGrid">
<property name="text">
<string>Galactic Grid</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="galacticGridText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Galactic Grid Text</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="precessionChart">
<property name="text">
<string>Precession Chart</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="23" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="constellationGroupBox">
<property name="title">
<string>Constellations</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="1">
<widget class="QCheckBox" name="constellationFigures">
<property name="toolTip">
<string>Display constellation stick figures</string>
</property>
<property name="text">
<string>Figures</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="constellationBoundaries">
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display boundaries between constellations</string>
</property>
<property name="text">
<string>Boundaries</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="constellationSelection">
<property name="toolTip">
<string>Displays boundary only for constellation at the center of view</string>
</property>
<property name="text">
<string>Centre boundary only</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="constellationLabels">
<property name="toolTip">
<string>Display constellation names</string>
</property>
<property name="text">
<string>Labels</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="constellationPictures">
<property name="toolTip">
<string>Display constellation pictures</string>
</property>
<property name="text">
<string>Pictures</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="19" column="0">
<widget class="QCheckBox" name="iss">
<property name="toolTip">
<string>Display ISS model</string>
</property>
<property name="text">
<string>ISS</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="solarSystemGroupBox">
<property name="toolTip">
<string>Settings that apply only to the Solar System view</string>
</property>
<property name="title">
<string>Solar System View</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QCheckBox" name="solarSystemCosmos">
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Displays cosmos (when zoomed far out)</string>
</property>
<property name="text">
<string>Cosmos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="solarSystemMilkyWay">
<property name="toolTip">
<string>Displays Milky Way</string>
</property>
<property name="text">
<string>Milky Way</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="solarSystemStars">
<property name="toolTip">
<string>Displays stars</string>
</property>
<property name="text">
<string>Stars</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="solarSystemPlanets">
<property name="toolTip">
<string>Displays planets</string>
</property>
<property name="text">
<string>Planets</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="solarSystemMinorPlanets">
<property name="toolTip">
<string>Displays asteroids</string>
</property>
<property name="text">
<string>Asteroids</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="solarSystemOrbits">
<property name="toolTip">
<string>Displays planetary orbits</string>
</property>
<property name="text">
<string>Planetary Orbits</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="solarSystemMinorOrbits">
<property name="toolTip">
<string>Displays orbits of moons and minor satellites</string>
</property>
<property name="text">
<string>Moon and Satellite Orbits</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="solarSystemLighting">
<property name="toolTip">
<string>Applies lighting and shadows to planets</string>
</property>
<property name="text">
<string>Lighting and Shadows</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="solarSystemMultiRes">
<property name="toolTip">
<string>Uses higher resolution planetary imagery when zooming in</string>
</property>
<property name="text">
<string>Mulit Res Planets</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SkyMapSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SkyMapSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Wyświetl plik

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2021 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 "SWGFeatureSettings.h"
#include "skymap.h"
#include "skymapwebapiadapter.h"
SkyMapWebAPIAdapter::SkyMapWebAPIAdapter()
{}
SkyMapWebAPIAdapter::~SkyMapWebAPIAdapter()
{}
int SkyMapWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
response.getSkyMapSettings()->init();
SkyMap::webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SkyMapWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
SkyMap::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
return 200;
}

Wyświetl plik

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2021 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SKYMAP_WEBAPIADAPTER_H
#define INCLUDE_SKYMAP_WEBAPIADAPTER_H
#include "feature/featurewebapiadapter.h"
#include "skymapsettings.h"
/**
* Standalone API adapter only for the settings
*/
class SkyMapWebAPIAdapter : public FeatureWebAPIAdapter {
public:
SkyMapWebAPIAdapter();
virtual ~SkyMapWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
private:
SkyMapSettings m_settings;
};
#endif // INCLUDE_SKYMAP_WEBAPIADAPTER_H

Wyświetl plik

@ -0,0 +1,160 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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 <QJsonObject>
#include <QHashIterator>
#include "webinterface.h"
WebInterface::WebInterface(QObject *parent) :
WebSocketServer(parent)
{
}
// Set the current camera view to a given coordinates
void WebInterface::setView(double ra, double dec, float zoom)
{
QJsonObject obj {
{"command", "setView"},
{"ra", ra},
{"dec", dec},
{"zoom", zoom}
};
send(obj);
}
// Set observation position
void WebInterface::setPosition(const QGeoCoordinate& position)
{
QJsonObject obj {
{"command", "setPosition"},
{"latitude", position.latitude()},
{"longitude", position.longitude()},
{"altitude", position.altitude()}
};
send(obj);
}
// Set date and time
void WebInterface::setDateTime(QDateTime dateTime)
{
QJsonObject obj {
{"command", "setDateTime"},
{"dateTime", dateTime.toUTC().toString(Qt::ISODateWithMs)}
};
send(obj);
}
// Set the camera to track the item with the specified name
void WebInterface::track(const QString& name)
{
QJsonObject obj {
{"command", "track"},
{"name", name}
};
send(obj);
}
void WebInterface::setBackground(const QString &background)
{
QJsonObject obj {
{"command", "setBackground"},
{"background", background},
};
send(obj);
}
void WebInterface::setProjection(const QString &projection)
{
QJsonObject obj {
{"command", "setProjection"},
{"projection", projection},
};
send(obj);
}
void WebInterface::showConstellations(bool show)
{
QJsonObject obj {
{"command", "showConstellations"},
{"show", show}
};
send(obj);
}
void WebInterface::showReticle(bool show)
{
QJsonObject obj {
{"command", "showReticle"},
{"show", show}
};
send(obj);
}
void WebInterface::showGrid(bool show)
{
QJsonObject obj {
{"command", "showGrid"},
{"show", show}
};
send(obj);
}
void WebInterface::showNames(bool show)
{
QJsonObject obj {
{"command", "showNames"},
{"show", show}
};
send(obj);
}
void WebInterface::showAntennaFoV(bool show)
{
QJsonObject obj {
{"command", "showAntennaFoV"},
{"show", show}
};
send(obj);
}
void WebInterface::setAntennaFoV(float hpbw)
{
QJsonObject obj {
{"command", "setAntennaFoV"},
{"hpbw", hpbw}
};
send(obj);
}
void WebInterface::setWWTSettings(const QHash<QString, QVariant>& settings)
{
QJsonObject obj {
{"command", "setWWTSettings"},
};
QHashIterator<QString, QVariant> itr(settings);
while (itr.hasNext())
{
itr.next();
QString key = itr.key();
QVariant value = itr.value();
obj.insert(key, value.toString());
}
send(obj);
}

Wyświetl plik

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_WEBINTERFACE_H_
#define INCLUDE_FEATURE_WEBINTERFACE_H_
#include <QGeoCoordinate>
#include "websocketserver.h"
// Interface between C++ code and Web page via Web Socket using JSON
class WebInterface : public WebSocketServer
{
public:
WebInterface(QObject *parent = nullptr);
void setView(double ra, double dec, float zoom=1.0f);
void setPosition(const QGeoCoordinate& position);
void setDateTime(QDateTime dateTime);
void track(const QString &name);
void setBackground(const QString& name);
void setProjection(const QString& name);
void showConstellations(bool show);
void showReticle(bool show);
void showGrid(bool show);
void showAntennaFoV(bool show);
void showNames(bool show);
void setAntennaFoV(float hpbw);
void setWWTSettings(const QHash<QString, QVariant>& settings);
};
#endif // INCLUDE_FEATURE_WEBINTERFACE_H_

Wyświetl plik

@ -0,0 +1,222 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2024 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 <QResource>
#include <QFile>
#include <QRegularExpression>
#include <QDebug>
#include "webserver.h"
// port - port to listen on / is listening on. Use 0 for any free port.
WebServer::WebServer(quint16 &port, QObject* parent) :
QTcpServer(parent),
m_defaultMimeType("application/octet-stream")
{
listen(QHostAddress::Any, port);
port = serverPort();
qDebug() << "WebServer on port " << port;
m_mimeTypes.insert(".html", new MimeType("text/html; charset=\"utf-8\"", false));
m_mimeTypes.insert(".png", new MimeType("image/png"));
m_mimeTypes.insert(".glb", new MimeType("model/gltf-binary"));
m_mimeTypes.insert(".glbe", new MimeType("model/gltf-binary"));
m_mimeTypes.insert(".js", new MimeType("text/javascript"));
m_mimeTypes.insert(".css", new MimeType("text/css"));
m_mimeTypes.insert(".json", new MimeType("application/json"));
m_mimeTypes.insert(".geojson", new MimeType("application/geo+json"));
}
void WebServer::incomingConnection(qintptr socket)
{
QTcpSocket* s = new QTcpSocket(this);
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
s->setSocketDescriptor(socket);
//addPendingConnection(socket);
}
// Don't include leading or trailing / in from
void WebServer::addPathSubstitution(const QString &from, const QString &to)
{
qDebug() << "Mapping " << from << " to " << to;
m_pathSubstitutions.insert(from, to);
}
void WebServer::addSubstitution(QString path, QString from, QString to)
{
Substitution *s = new Substitution(from, to);
if (m_substitutions.contains(path))
{
QList<Substitution *> *list = m_substitutions.value(path);
QMutableListIterator<Substitution *> i(*list);
while (i.hasNext()) {
Substitution *sub = i.next();
if (sub->m_from == from) {
i.remove();
delete sub;
}
}
list->append(s);
}
else
{
QList<Substitution *> *list = new QList<Substitution *>();
list->append(s);
m_substitutions.insert(path, list);
}
}
QString WebServer::substitute(QString path, QString html)
{
QList<Substitution *> *list = m_substitutions.value(path);
for (const auto s : *list) {
html = html.replace(s->m_from, s->m_to);
}
return html;
}
void WebServer::addFile(const QString &path, const QByteArray &data)
{
m_files.insert(path, data);
}
void WebServer::sendFile(QTcpSocket* socket, const QByteArray &data, MimeType *mimeType, const QString &path)
{
QString header = QString("HTTP/1.0 200 Ok\r\nContent-Type: %1\r\nAccess-Control-Allow-Origin \"*\"\r\n\r\n").arg(mimeType->m_type);
if (mimeType->m_binary)
{
// Send file as binary
QByteArray headerUtf8 = header.toUtf8();
socket->write(headerUtf8);
socket->write(data);
}
else
{
// Send file as text
QString html = QString(data);
// Make any substitutions in the content of the file
if (m_substitutions.contains(path)) {
html = substitute(path, html);
}
QTextStream os(socket);
os.setAutoDetectUnicode(true);
os << header << html;
}
}
void WebServer::readClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
if (socket->canReadLine())
{
QString line = socket->readLine();
//qDebug() << "WebServer HTTP Request: " << line;
QStringList tokens = QString(line).split(QRegularExpression("[ \r\n][ \r\n]*"));
if (tokens[0] == "GET")
{
// Get file type from extension
QString path = tokens[1];
MimeType *mimeType = &m_defaultMimeType;
int extensionIdx = path.lastIndexOf(".");
if (extensionIdx != -1) {
QString extension = path.mid(extensionIdx);
if (m_mimeTypes.contains(extension)) {
mimeType = m_mimeTypes[extension];
}
}
// Try skymapping path
QStringList dirs = path.split('/');
if ((dirs.length() >= 2) && m_pathSubstitutions.contains(dirs[1]))
{
dirs[1] = m_pathSubstitutions.value(dirs[1]);
dirs.removeFirst();
QString newPath = dirs.join('/');
//qDebug() << "SkyMapping " << path << " to " << newPath;
path = newPath;
}
// See if we can find the file in our resources
QResource res(path);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
if (res.isValid() && (res.uncompressedSize() > 0))
{
QByteArray data = res.uncompressedData();
sendFile(socket, data, mimeType, path);
}
#else
if (res.isValid() && (res.size() > 0))
{
QByteArray data = QByteArray::fromRawData((const char *)res.data(), res.size());
if (res.isCompressed()) {
data = qUncompress(data);
}
sendFile(socket, data, mimeType, path);
}
#endif
else if (m_files.contains(path))
{
// Path is a file held in memory
sendFile(socket, m_files.value(path).data(), mimeType, path);
}
else
{
// See if we can find a file on disk
QFile file(path);
if (file.open(QIODevice::ReadOnly))
{
QByteArray data = file.readAll();
if (path.endsWith(".glbe")) {
for (int i = 0; i < data.size(); i++) {
data[i] = data[i] ^ 0x55;
}
}
sendFile(socket, data, mimeType, path);
}
else
{
qDebug() << "WebServer " << path << " not found";
// File not found
QTextStream os(socket);
os.setAutoDetectUnicode(true);
os << "HTTP/1.0 404 Not Found\r\n"
"Content-Type: text/html; charset=\"utf-8\"\r\n"
"\r\n"
"<html>\n"
"<body>\n"
"<h1>404 Not Found</h1>\n"
"</body>\n"
"</html>\n";
}
}
socket->close();
if (socket->state() == QTcpSocket::UnconnectedState) {
delete socket;
}
}
}
}
void WebServer::discardClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
socket->deleteLater();
}

Wyświetl plik

@ -0,0 +1,80 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_WEB_SERVER_H_
#define INCLUDE_WEB_SERVER_H_
#include <QTcpServer>
#include <QTcpSocket>
// WebServer for making simple dynamic html pages and serving binaries from
// resources or local disk
class WebServer : public QTcpServer
{
Q_OBJECT
struct Substitution {
QString m_from;
QString m_to;
Substitution(const QString& from, const QString& to) :
m_from(from),
m_to(to)
{
}
};
struct MimeType {
QString m_type;
bool m_binary;
MimeType(const QString& type, bool binary=true) :
m_type(type),
m_binary(binary)
{
}
};
private:
// Hash of a list of paths to substitute
QHash<QString, QString> m_pathSubstitutions;
// Hash of path to a list of substitutions to make in the file
QHash<QString, QList<Substitution *>*> m_substitutions;
// Hash of files held in memory
QHash<QString, QByteArray> m_files;
// Hash of filename extension to MIME type information
QHash<QString, MimeType *> m_mimeTypes;
MimeType m_defaultMimeType;
public:
WebServer(quint16 &port, QObject* parent = 0);
void incomingConnection(qintptr socket) override;
void addPathSubstitution(const QString &from, const QString &to);
void addSubstitution(QString path, QString from, QString to);
void addFile(const QString &path, const QByteArray &data);
QString substitute(QString path, QString html);
void sendFile(QTcpSocket* socket, const QByteArray &data, MimeType *mimeType, const QString &path);
private slots:
void readClient();
void discardClient();
};
#endif

Wyświetl plik

@ -0,0 +1,101 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 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 <QJsonDocument>
#include "websocketserver.h"
WebSocketServer::WebSocketServer(QObject *parent) :
QObject(parent),
m_socket("", QWebSocketServer::NonSecureMode, this),
m_client(nullptr)
{
connect(&m_socket, &QWebSocketServer::newConnection, this, &WebSocketServer::onNewConnection);
int port = 0;
if (!m_socket.listen(QHostAddress::Any, port)) {
qCritical() << "WebSocketServer - Unable to listen on port " << port;
}
}
quint16 WebSocketServer::serverPort()
{
return m_socket.serverPort();
}
void WebSocketServer::onNewConnection()
{
QWebSocket *socket = m_socket.nextPendingConnection();
connect(socket, &QWebSocket::textMessageReceived, this, &WebSocketServer::processTextMessage);
connect(socket, &QWebSocket::binaryMessageReceived, this, &WebSocketServer::processBinaryMessage);
connect(socket, &QWebSocket::disconnected, this, &WebSocketServer::socketDisconnected);
m_client = socket;
emit connected();
}
void WebSocketServer::processTextMessage(QString message)
{
//QWebSocket *client = qobject_cast<QWebSocket *>(sender());
//qDebug() << "WebSocketServer::processTextMessage - Received text " << message;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
if (!doc.isNull() && doc.isObject()) {
emit received(doc.object());
} else {
qDebug() << "WebSocketServer::processTextMessage: " << error.errorString();
}
}
void WebSocketServer::processBinaryMessage(QByteArray message)
{
//QWebSocket *client = qobject_cast<QWebSocket *>(sender());
// Shouldn't receive any binary messages for now
qDebug() << "WebSocketServer::processBinaryMessage - Received binary " << message;
}
void WebSocketServer::socketDisconnected()
{
QWebSocket *client = qobject_cast<QWebSocket *>(sender());
if (client) {
client->deleteLater();
m_client = nullptr;
}
}
bool WebSocketServer::isConnected()
{
return m_client != nullptr;
}
void WebSocketServer::send(const QJsonObject &obj)
{
if (m_client)
{
QJsonDocument doc(obj);
QByteArray bytes = doc.toJson();
qint64 bytesSent = m_client->sendTextMessage(bytes);
m_client->flush(); // Try to reduce latency
if (bytesSent != bytes.size()) {
qDebug() << "WebSocketServer::update - Sent only " << bytesSent << " bytes out of " << bytes.size();
}
//qDebug() << obj;
}
}

Wyświetl plik

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2021-2022 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_WEBSOCKERSERVER_H_
#define INCLUDE_FEATURE_WEBSOCKERSERVER_H_
#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <QJsonObject>
class WebSocketServer : public QObject
{
Q_OBJECT
private:
QWebSocketServer m_socket;
QWebSocket *m_client;
public:
WebSocketServer(QObject *parent = nullptr);
quint16 serverPort();
bool isConnected();
void send(const QJsonObject &obj);
signals:
void connected();
void received(const QJsonObject &obj);
public slots:
void onNewConnection();
void processTextMessage(QString message);
void processBinaryMessage(QByteArray message);
void socketDisconnected();
};
#endif // INCLUDE_FEATURE_MAPWEBSOCKERSERVER_H_

Wyświetl plik

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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 "webview.h"
#include <QTabWidget>
#include <QWebEngineView>
#include <QHBoxLayout>
WebView::WebView(QWidget *parent) :
QWebEngineView(parent)
{
}
QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type)
{
QWebEngineView *view = new QWebEngineView();
connect(view, &QWebEngineView::titleChanged, this, &WebView::on_titleChanged);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(view);
int tab = m_tabs->addTab(view, "Web");
m_tabs->setCurrentIndex(tab);
return view;
}
void WebView::on_titleChanged(const QString& title)
{
QWebEngineView *view = qobject_cast<QWebEngineView *>(sender());
for (int i = 0; i < m_tabs->count(); i++)
{
if (m_tabs->widget(i) == view) {
m_tabs->setTabText(i, title);
}
}
}

Wyświetl plik

@ -0,0 +1,46 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_WEBVIEW_H_
#define INCLUDE_FEATURE_WEBVIEW_H_
#include <QWebEngineView>
#include "websocketserver.h"
class QTabWidget;
class WebView : public QWebEngineView
{
Q_OBJECT
public:
WebView(QWidget *parent = nullptr);
QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
void setTabs(QTabWidget *tabs) { m_tabs = tabs; }
private slots:
void on_titleChanged(const QString& title);
private:
QTabWidget *m_tabs;
};
#endif // INCLUDE_FEATURE_WEBVIEW_H_

Wyświetl plik

@ -0,0 +1,105 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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 "wtml.h"
#include <QUrl>
#include <QNetworkReply>
#include <QXmlStreamReader>
#include <QDebug>
WTML::WTML()
{
m_networkManager = new QNetworkAccessManager();
QObject::connect(m_networkManager, &QNetworkAccessManager::finished, this, &WTML::handleReply);
}
WTML::~WTML()
{
QObject::disconnect(m_networkManager, &QNetworkAccessManager::finished, this, &WTML::handleReply);
delete m_networkManager;
}
void WTML::getData()
{
// https://worldwidetelescope.org/wwtweb/catalog.aspx?X=ImageSets6
// https://worldwidetelescope.org/wwtweb/catalog.aspx?W=exploreroot6
QUrl url(QString("https://worldwidetelescope.org/wwtweb/catalog.aspx?X=ImageSets6"));
m_networkManager->get(QNetworkRequest(url));
}
void WTML::handleReply(QNetworkReply* reply)
{
if (!reply->error())
{
QXmlStreamReader xmlReader(reply->readAll());
QList<ImageSet> dataSets;
while (!xmlReader.atEnd() && !xmlReader.hasError())
{
while (xmlReader.readNextStartElement())
{
if (xmlReader.name() == QLatin1String("Folder"))
{
while(xmlReader.readNextStartElement())
{
if (xmlReader.name() == QLatin1String("ImageSet"))
{
QString name = xmlReader.attributes().value("Name").toString();
QString dataSetType = xmlReader.attributes().value("DataSetType").toString();
if (!name.isEmpty() && !dataSetType.isEmpty())
{
ImageSet imageSet;
imageSet.m_name = name;
imageSet.m_dataSetType = dataSetType;
dataSets.append(imageSet);
//qDebug() << "Adding ImageSet " << name << dataSetType;
}
// Credits, Thumbnail etc
while(xmlReader.readNextStartElement())
{
xmlReader.skipCurrentElement();
}
}
else
{
xmlReader.skipCurrentElement();
}
}
}
else
{
xmlReader.skipCurrentElement();
}
}
}
// Ignore "Premature end of document." here even if ok
if (!xmlReader.atEnd() && xmlReader.hasError()) {
qDebug() << "WTML::handleReply: Error parsing XML: " << xmlReader.errorString();
}
emit dataUpdated(dataSets);
}
else
{
qDebug() << "WTML::handleReply: error: " << reply->error();
}
reply->deleteLater();
}

Wyświetl plik

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_WTML_H
#define INCLUDE_WTML_H
#include <QtCore>
class QNetworkAccessManager;
class QNetworkReply;
// World Wide Telescope WTML files containing imageset catalogs
class WTML : public QObject
{
Q_OBJECT
public:
struct ImageSet {
QString m_name;
QString m_dataSetType;
};
WTML();
~WTML();
void getData();
public slots:
void handleReply(QNetworkReply* reply);
signals:
void dataUpdated(const QList<ImageSet>& dataSets); // Emitted when new data are available.
private:
QNetworkAccessManager *m_networkManager;
};
#endif /* INCLUDE_WTML_H */

Wyświetl plik

@ -9,6 +9,7 @@ The Star Tracker feature plugin is for use in radio astronomy and EME (Earth-Moo
* The overhead position of the Sun, Moon and selected star can be displayed on the Map Feature.
* It can display local Sidereal time, solar flux density and sky temperature.
* It can plot the line of sight through the Milky Way.
* It can send the target to the Sky Map plugin, to display associated imagery in a variety of wavelengths. It can also use the Sky Map to set the target.
* The plugin can communicate with Stellarium, allowing Stellarium to control SDRangel as though it was a telescope and for the direction the antenna is pointing to be displayed in Stellarium.
<h2>Settings</h2>
@ -66,7 +67,7 @@ Specifies the longitude in decimal degrees (East positive) of the observation po
<h3>8: Time</h3>
Select the date and time at which the position of the target should be calculated. Select either Now, for the current time, or Custom to manually enter a date and time.
Select the local date and time at which the position of the target should be calculated. Select either Now, for the current time, or Custom to manually enter a date and time.
<h3>9: LST - Local Sidereal Time</h3>
@ -100,6 +101,7 @@ To allow Stellarium to set the RA and Dec, select Custom RA/Dec, and ensure the
| S8 | HI | IAU primary calibration region (l=207,b=-15) | Tb=72 peak |
| S9 | HI | IAU secondary calibration region (l=356,b=-4) | Tb=85 peak |
| SatelliteTracker | | Gets target Az/El from Satellite Tracker | |
| SkyMap | | Gets target RA/Dec from Sky Map Tracker | |
References:
@ -240,6 +242,12 @@ The Star Tracker feature can send the overhead position of the Sun, Moon and tar
When using the Find feature in the Map GUI, you can search for "Sun", "Moon" or "Star".
<h2>Sky Map</h2>
The Star Tracker feature will send the target RA/Dec, observation point (antenna location) and antenna beamwidth to the Sky Map.
If the Star Tracker is set as the Source plugin in the Sky Map, pressing the Track button in the Sky Map will result in the Sky Map tracking the target
selected in the Star Tracker.
<h2>Stellarium Interface</h2>
In Star Tracker:
@ -288,15 +296,15 @@ Icons are by Freepik from Flaticon https://www.flaticon.com/
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the target to the Moon at the current time:
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Moon", "dateTime": "" }}'
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Moon", "dateTime": "" }}'
Or to a custom RA and declination on a given date and time:
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Custom", "ra": "03h32m59.35s", "dec": "54d34m45.05s", "dateTime": "1921-04-15T10:17:05" }}'
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Custom", "ra": "03h32m59.35s", "dec": "54d34m45.05s", "dateTime": "1921-04-15T10:17:05" }}'
To start tracking:
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/run"
<h2>Developer Notes</h2>

Wyświetl plik

@ -936,7 +936,7 @@ void StarTracker::scanAvailableFeatures()
{
Feature *feature = featureSet->getFeatureAt(fei);
if (feature->getURI() == "sdrangel.feature.satellitetracker")
if ((feature->getURI() == "sdrangel.feature.satellitetracker") || (feature->getURI() == "sdrangel.feature.skymap"))
{
StarTrackerSettings::AvailableFeature satelliteTracker =
StarTrackerSettings::AvailableFeature{featureSet->getIndex(), fei, feature->getIdentifier()};

Wyświetl plik

@ -232,15 +232,15 @@ bool StarTrackerGUI::handleMessage(const Message& message)
void StarTrackerGUI::updateSatelliteTrackerList(const QList<StarTrackerSettings::AvailableFeature>& satelliteTrackers)
{
// Update list of satellite trackers
// Update list of plugins we can get target from
ui->target->blockSignals(true);
// Remove Satellite Trackers no longer available
// Remove targets no longer available
for (int i = 0; i < ui->target->count(); )
{
QString text = ui->target->itemText(i);
bool found = false;
if (text.contains("SatelliteTracker"))
if (text.contains("SatelliteTracker") || text.contains("SkyMap"))
{
for (const auto& satelliteTracker : satelliteTrackers)
{
@ -262,7 +262,7 @@ void StarTrackerGUI::updateSatelliteTrackerList(const QList<StarTrackerSettings:
}
}
// Add new Satellite Trackers
// Add new targets
for (const auto& satelliteTracker : satelliteTrackers)
{
QString name = satelliteTracker.getName();
@ -271,7 +271,7 @@ void StarTrackerGUI::updateSatelliteTrackerList(const QList<StarTrackerSettings:
}
}
// Satellite Tracker feature can be created after this plugin, so select it
// Features can be created after this plugin, so select it
// if the chosen tracker appears
int index = ui->target->findText(m_settings.m_target);
if (index >= 0) {

Wyświetl plik

@ -141,10 +141,10 @@
<string>Antenna half power (-3dB) beamwidth (degrees)</string>
</property>
<property name="decimals">
<number>1</number>
<number>2</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>

Wyświetl plik

@ -52,7 +52,7 @@ struct StarTrackerSettings
double m_latitude;
double m_longitude;
QString m_target; // Sun, Moon, Custom
QString m_dateTime; // Date/time for observation, or "" for now
QString m_dateTime; // Local date/time for observation, or "" for now
QString m_refraction; // Refraction correction. "None", "Saemundsson" or "Positional Astronomy Library"
double m_pressure; // Air pressure in millibars
double m_temperature; // Air temperature in C

Wyświetl plik

@ -30,6 +30,7 @@
#include "SWGTargetAzimuthElevation.h"
#include "SWGMapItem.h"
#include "SWGStarTrackerTarget.h"
#include "SWGSkyMapTarget.h"
#include "webapi/webapiadapterinterface.h"
#include "webapi/webapiutils.h"
@ -384,7 +385,7 @@ void StarTrackerWorker::updateRaDec(RADec rd, QDateTime dt, bool lbTarget)
// Send to Stellarium
writeStellariumTarget(rdJ2000.ra, rdJ2000.dec);
// Send to GUI
if (m_settings.m_target == "Sun" || m_settings.m_target == "Moon" || (m_settings.m_target == "Custom Az/El") || lbTarget || m_settings.m_target.contains("SatelliteTracker"))
if (m_settings.m_target == "Sun" || m_settings.m_target == "Moon" || (m_settings.m_target == "Custom Az/El") || lbTarget || m_settings.m_target.contains("SatelliteTracker") || m_settings.m_target.contains("SkyMap"))
{
if (getMessageQueueToGUI())
{
@ -517,6 +518,26 @@ void StarTrackerWorker::update()
else
qDebug() << "StarTrackerWorker::update - Failed to parse feature name " << m_settings.m_target;
}
if (m_settings.m_target.contains("SkyMap"))
{
// Get RA/Dec from Sky Map
double ra, dec;
unsigned int featureSetIndex,featureIndex;
if (MainCore::getFeatureIndexFromId(m_settings.m_target, featureSetIndex, featureIndex))
{
if (ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "ra", ra)
&& ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "dec", dec))
{
m_settings.m_ra = QString::number(ra, 'f', 10);
m_settings.m_dec = QString::number(dec, 'f', 10);
}
else
qDebug() << "StarTrackerWorker::update - Failed to target from feature " << m_settings.m_target;
}
else
qDebug() << "StarTrackerWorker::update - Failed to parse feature name " << m_settings.m_target;
}
if (m_settings.m_target == "Sun")
{
@ -654,6 +675,33 @@ void StarTrackerWorker::update()
}
}
// Send RA/Dec, position, beamwidth and date to Sky Map
QList<ObjectPipe*> skyMapPipes;
MainCore::instance()->getMessagePipes().getMessagePipes(m_starTracker, "skymap.target", skyMapPipes);
for (const auto& pipe : skyMapPipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
SWGSDRangel::SWGSkyMapTarget *swgTarget = new SWGSDRangel::SWGSkyMapTarget();
if (m_settings.m_jnow)
{
double jd = Astronomy::julianDate(dt);
RADec rdJ2000 = Astronomy::precess(rd, jd, Astronomy::jd_j2000());
swgTarget->setRa(rdJ2000.ra);
swgTarget->setDec(rdJ2000.dec);
}
else
{
swgTarget->setRa(rd.ra);
swgTarget->setDec(rd.dec);
}
swgTarget->setLatitude(m_settings.m_latitude);
swgTarget->setLongitude(m_settings.m_longitude);
swgTarget->setAltitude(m_settings.m_heightAboveSeaLevel);
swgTarget->setDateTime(new QString(dt.toString(Qt::ISODateWithMs)));
swgTarget->setHpbw(m_settings.m_beamwidth);
messageQueue->push(MainCore::MsgSkyMapTarget::create(m_starTracker, swgTarget));
}
// Send to Map
if (m_settings.m_drawSunOnMap || m_settings.m_drawMoonOnMap || m_settings.m_drawStarOnMap)
{

Wyświetl plik

@ -71,6 +71,7 @@ MESSAGE_CLASS_DEFINITION(MainCore::MsgTargetAzimuthElevation, Message)
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerTarget, Message)
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerDisplaySettings, Message)
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerDisplayLoSSettings, Message)
MESSAGE_CLASS_DEFINITION(MainCore::MsgSkyMapTarget, Message)
MainCore::MainCore()
{
@ -435,6 +436,100 @@ void MainCore::updateWakeLock()
}
#endif
QList<MainCore::AvailableChannelOrFeature> MainCore::getAvailableChannels(const QStringList& uris)
{
QList<AvailableChannelOrFeature> list;
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
for (const auto deviceSet : m_deviceSets)
{
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
{
ChannelAPI* channel = deviceSet->getChannelAt(chi);
if ((uris.size() == 0) || uris.contains(channel->getURI()))
{
QChar type = getDeviceSetTypeId(deviceSet);
int streamIdx = type == 'M' ? channel->getStreamIndex() : -1;
AvailableChannelOrFeature item {
type,
deviceSet->getIndex(),
chi,
streamIdx,
channel->getIdentifier(),
channel
};
list.append(item);
}
}
}
return list;
}
QList<MainCore::AvailableChannelOrFeature> MainCore::getAvailableFeatures(const QStringList& uris)
{
QList<AvailableChannelOrFeature> list;
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
for (const auto& featureSet : featureSets)
{
for (int fei = 0; fei < featureSet->getNumberOfFeatures(); fei++)
{
Feature *feature = featureSet->getFeatureAt(fei);
if ((uris.size() == 0) || uris.contains(feature->getURI()))
{
AvailableChannelOrFeature item {
'F',
featureSet->getIndex(),
fei,
-1,
feature->getIdentifier(),
feature
};
list.append(item);
}
}
}
return list;
}
QList<MainCore::AvailableChannelOrFeature> MainCore::getAvailableChannelsAndFeatures(const QStringList& uris)
{
QList<AvailableChannelOrFeature> list;
list.append(getAvailableChannels(uris));
list.append(getAvailableFeatures(uris));
return list;
}
QObject *MainCore::getAvailableChannelOrFeatureById(const QString& id, const QList<AvailableChannelOrFeature>& list)
{
for (const auto& item : list)
{
if (item.getId() == id) {
return item.m_object;
}
}
return nullptr;
}
QObject *MainCore::getAvailableChannelOrFeatureByLongId(const QString& longId, const QList<AvailableChannelOrFeature>& list)
{
for (const auto& item : list)
{
if (item.getLongId() == longId) {
return item.m_object;
}
}
return nullptr;
}
QChar MainCore::getDeviceSetTypeId(const DeviceSet* deviceSet)
{
if (deviceSet->m_deviceMIMOEngine) {
@ -461,9 +556,11 @@ QString MainCore::getChannelId(const ChannelAPI* channel)
DeviceSet* deviceSet = deviceSets[channel->getDeviceSetIndex()];
QString deviceSetId = getDeviceSetId(deviceSet);
int index = channel->getIndexInDeviceSet();
// FIXME: if (deviceSet->m_deviceMIMOEngine) {
// we should append stream index. E.g. "M0:0.0" However, only ChannelGUI seems to know what it is
return QString("%1:%2").arg(deviceSetId).arg(index);
if (deviceSet->m_deviceMIMOEngine) {
return QString("%1:%2.%3").arg(deviceSetId).arg(index).arg(channel->getStreamIndex());
} else {
return QString("%1:%2").arg(deviceSetId).arg(index);
}
}
QStringList MainCore::getDeviceSetIds(bool rx, bool tx, bool mimo)

Wyświetl plik

@ -59,6 +59,7 @@ namespace SWGSDRangel
class SWGStarTrackerTarget;
class SWGStarTrackerDisplaySettings;
class SWGStarTrackerDisplayLoSSettings;
class SWGSkyMapTarget;
}
class SDRBASE_API MainCore : public QObject
@ -842,7 +843,28 @@ public:
{ }
};
class SDRBASE_API MsgSkyMapTarget : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QObject *getPipeSource() const { return m_pipeSource; }
SWGSDRangel::SWGSkyMapTarget *getSWGSkyMapTarget() const { return m_swgSkyMapTarget; }
static MsgSkyMapTarget* create(const QObject *pipeSource, SWGSDRangel::SWGSkyMapTarget *swgSkyMapTarget)
{
return new MsgSkyMapTarget(pipeSource, swgSkyMapTarget);
}
private:
const QObject *m_pipeSource;
SWGSDRangel::SWGSkyMapTarget *m_swgSkyMapTarget;
MsgSkyMapTarget(const QObject *pipeSource, SWGSDRangel::SWGSkyMapTarget *swgSkyMapTarget) :
Message(),
m_pipeSource(pipeSource),
m_swgSkyMapTarget(swgSkyMapTarget)
{ }
};
MainCore();
~MainCore();
@ -886,6 +908,40 @@ public:
// Position
const QGeoPositionInfo& getPosition() const;
struct AvailableChannelOrFeature
{
QChar m_kind; //!< 'R' or 'T' for channel, 'M' for MIMO channel, 'F' for feature as from MainCore::getDeviceSetTypeId
int m_superIndex; //!< Device Set index or Feature Set index
int m_index; //!< Channel or Feature index
int m_streamIndex; //!< For MIMO channels only
QString m_type; //!< Plugin type (E.g. NFMDemod)
QObject *m_object; //!< Pointer to the object (ChannelAPI or Feature object)
AvailableChannelOrFeature() = default;
AvailableChannelOrFeature(const AvailableChannelOrFeature&) = default;
AvailableChannelOrFeature& operator=(const AvailableChannelOrFeature&) = default;
bool operator==(const AvailableChannelOrFeature& a) const {
return (m_kind == a.m_kind) && (m_superIndex == a.m_superIndex) && (m_index == a.m_index) && (m_type == a.m_type);
}
QString getId() const { // Eg: "R3:4"
QString id = QString("%1%2:%3").arg(m_kind).arg(m_superIndex).arg(m_index);
if (m_kind == "M") {
id.append(QString(".%1").arg(m_streamIndex));
}
return id;
}
QString getLongId() const { // Eg: "F0:1 StarTracker"
return QString("%1 %2").arg(getId()).arg(m_type);
}
};
// Use QList so ordered numerically
QList<AvailableChannelOrFeature> getAvailableChannels(const QStringList& uris); // Get hash of available channels with given URIs or all if empty list. Hash hey is Id
QList<AvailableChannelOrFeature> getAvailableFeatures(const QStringList& uris); // Get hash of available features with given URIs or all if empty list. Hash key is Id
QList<AvailableChannelOrFeature> getAvailableChannelsAndFeatures(const QStringList& uris); // Get hash of available channels and features with given URIs or all if empty list. Hash key is Id
static QObject *getAvailableChannelOrFeatureById(const QString& id, const QList<AvailableChannelOrFeature>& list);
static QObject *getAvailableChannelOrFeatureByLongId(const QString& longId, const QList<AvailableChannelOrFeature>& list);
// Ids
QChar getDeviceSetTypeId(const DeviceSet* deviceSet); //!< Get Type Id (E.g. 'R', 'T' or 'M') for the given device set
QString getDeviceSetId(const DeviceSet* deviceSet); //!< Get Id (E.g. "R2") for the given device set

Wyświetl plik

@ -339,6 +339,43 @@ public:
return false;
}
// Try to convert a string to Right Ascension (RA) and Declination. Returns false if not recognised format.
// This supports HMS/DMS or decimal.
// E.g.:
// 12 05 12.23 +17 06 21.0
// 12:05:12.23 -17:06:21.0
// 12h05m12.23s +17d06m21.0s
// 107.1324 -34.233
static bool stringToRADec(const QString& string, float& ra, float& dec)
{
QRegExp dms("([0-9]+)[ :h]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s? *,? *([+-]?[0-9]+)[ :d]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s?");
if (dms.exactMatch(string))
{
int raHours = dms.capturedTexts()[1].toInt();
int raMins = dms.capturedTexts()[2].toInt();
float raSecs = dms.capturedTexts()[3].toFloat();
ra = raHours + raMins / 60.0f + raSecs / (60.0f * 60.0f);
qDebug() << ra << raHours << raMins << raSecs;
int decDegs = dms.capturedTexts()[5].toInt();
int decMins = dms.capturedTexts()[6].toInt();
float decSecs = dms.capturedTexts()[7].toFloat();
bool neg = decDegs < 0;
dec = abs(decDegs) + decMins / 60.0f + decSecs / (60.0f * 60.0f);
if (neg) {
dec = -dec;
}
return true;
}
QRegExp decimal("([0-9]+(\\.[0-9]+)?) *,? *([+-]?[0-9]+(\\.[0-9]+)?)");
if (decimal.exactMatch(string))
{
ra = decimal.capturedTexts()[1].toFloat();
dec = decimal.capturedTexts()[3].toFloat();
return true;
}
return false;
}
static float solarFluxUnitsToJansky(float sfu)
{
return sfu * 10000.0f;

Wyświetl plik

@ -5273,6 +5273,11 @@ bool WebAPIRequestMapper::getFeatureSettings(
featureSettings->setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings());
featureSettings->getSimplePttSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "SkyMapSettings")
{
featureSettings->setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
featureSettings->getSkyMapSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "StarTrackerSettings")
{
featureSettings->setStarTrackerSettings(new SWGSDRangel::SWGStarTrackerSettings());
@ -5366,6 +5371,11 @@ bool WebAPIRequestMapper::getFeatureActions(
featureActions->setSimplePttActions(new SWGSDRangel::SWGSimplePTTActions());
featureActions->getSimplePttActions()->fromJsonObject(actionsJsonObject);
}
else if (featureActionsKey == "SkyMapActions")
{
featureActions->setSkyMapActions(new SWGSDRangel::SWGSkyMapActions());
featureActions->getSkyMapActions()->fromJsonObject(actionsJsonObject);
}
else if (featureActionsKey == "StarTrackerActions")
{
featureActions->setStarTrackerActions(new SWGSDRangel::SWGStarTrackerActions());
@ -5376,7 +5386,7 @@ bool WebAPIRequestMapper::getFeatureActions(
featureActions->setVorLocalizerActions(new SWGSDRangel::SWGVORLocalizerActions());
featureActions->getVorLocalizerActions()->fromJsonObject(actionsJsonObject);
}
else if (featureActionsKey == "DemodAnalyzerActions")
else if (featureActionsKey == "DemodAnalyzerActions")
{
featureActions->setDemodAnalyzerActions(new SWGSDRangel::SWGDemodAnalyzerActions());
featureActions->getDemodAnalyzerActions()->fromJsonObject(actionsJsonObject);
@ -5623,6 +5633,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
featureSettings.setPerTesterSettings(nullptr);
featureSettings.setSatelliteTrackerSettings(nullptr);
featureSettings.setSimplePttSettings(nullptr);
featureSettings.setSkyMapSettings(nullptr);
featureSettings.setStarTrackerSettings(nullptr);
featureSettings.setRadiosondeSettings(nullptr);
featureSettings.setRigCtlServerSettings(nullptr);
@ -5639,6 +5650,7 @@ void WebAPIRequestMapper::resetFeatureReport(SWGSDRangel::SWGFeatureReport& feat
featureReport.setMapReport(nullptr);
featureReport.setSatelliteTrackerReport(nullptr);
featureReport.setSimplePttReport(nullptr);
featureReport.setSkyMapReport(nullptr);
featureReport.setStarTrackerReport(nullptr);
featureReport.setVorLocalizerReport(nullptr);
}
@ -5654,6 +5666,7 @@ void WebAPIRequestMapper::resetFeatureActions(SWGSDRangel::SWGFeatureActions& fe
featureActions.setRigCtlServerActions(nullptr);
featureActions.setSatelliteTrackerActions(nullptr);
featureActions.setSimplePttActions(nullptr);
featureActions.setSkyMapActions(nullptr);
featureActions.setStarTrackerActions(nullptr);
featureActions.setVorLocalizerActions(nullptr);
}

Wyświetl plik

@ -320,6 +320,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToSettingsKey = {
{"RigCtlServer", "RigCtlServerSettings"},
{"SatelliteTracker", "SatelliteTrackerSettings"},
{"SimplePTT", "SimplePTTSettings"},
{"SkyMap", "SkyMapSettings"},
{"StarTracker", "StarTrackerSettings"},
{"VORLocalizer", "VORLocalizerSettings"}
};
@ -334,6 +335,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToActionsKey = {
{"RigCtlServer", "RigCtlServerActions"},
{"SatelliteTracker", "SatelliteTrackerActions"},
{"SimplePTT", "SimplePTTActions"},
{"SkyMap", "SkyMapActions"},
{"StarTracker", "StarTrackerActions"},
{"VORLocalizer", "VORLocalizerActions"},
{"DemodAnalyzer", "DemodAnalyzerActions"}
@ -355,6 +357,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureURIToSettingsKey = {
{"sdrangel.feature.rigctlserver", "RigCtlServerSettings"},
{"sdrangel.feature.satellitetracker", "SatelliteTrackerSettings"},
{"sdrangel.feature.simpleptt", "SimplePTTSettings"},
{"sdrangel.feature.skymap", "SkyMapSettings"},
{"sdrangel.feature.startracker", "StarTrackerSettings"},
{"sdrangel.feature.vorlocalizer", "VORLocalizerSettings"}
};

Wyświetl plik

@ -417,3 +417,8 @@ void FeatureGUI::setDisplayedame(const QString& name)
m_displayedName = name;
m_indexLabel->setToolTip(tr("%1").arg(m_displayedName));
}
void FeatureGUI::setStatusText(const QString& text)
{
m_statusLabel->setText(text);
}

Wyświetl plik

@ -68,6 +68,7 @@ public:
void setIndex(int index);
int getIndex() const { return m_featureIndex; }
void setDisplayedame(const QString& name);
void setStatusText(const QString& text);
protected:
void closeEvent(QCloseEvent *event) override;