From 08e2aef5a74579e85c5a8ba2dce71dead8a855ad Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 26 Feb 2022 17:37:40 +0200 Subject: [PATCH] geostationary satellites, footprint, client side pass calculation, updated rotator control, node-red info --- app/routes_jsapi.py | 6 +- app/static/js/polar_svg.js | 138 ++++--- app/static/js/rotator.js | 55 ++- app/static/js/satellite_tracker.js | 459 ++++++++++++++++------ app/templates/index.html | 12 +- images/node_red_basic.png | Bin 0 -> 22186 bytes nodered_flow_examples/basic_template.json | 162 ++++++++ 7 files changed, 638 insertions(+), 194 deletions(-) create mode 100644 images/node_red_basic.png create mode 100644 nodered_flow_examples/basic_template.json diff --git a/app/routes_jsapi.py b/app/routes_jsapi.py index 2a0198b..77e7f3f 100644 --- a/app/routes_jsapi.py +++ b/app/routes_jsapi.py @@ -32,14 +32,10 @@ def satellite_data(collection_id): for sat in satCollection: satellite = sat.Satellite satObject = {} + satObject['dbid'] = satellite.satellite_id satObject['title'] = satellite.satellite_tle0 satObject['tle1'] = satellite.satellite_tle1 satObject['tle2'] = satellite.satellite_tle2 - - next_pass = get_next_pass(satellite.satellite_tle0, satellite.satellite_tle1, satellite.satellite_tle2, float(config.config_data['qth_latitude']), float(config.config_data['qth_longitude']), 0) - satObject['next_pass'] = next_pass - - data['satellites'].append(satObject) data['success'] = True diff --git a/app/static/js/polar_svg.js b/app/static/js/polar_svg.js index 183d673..1eb5d6d 100644 --- a/app/static/js/polar_svg.js +++ b/app/static/js/polar_svg.js @@ -1,5 +1,5 @@ -function calcPolarPlotSVG(timeframe, groundstation, tleLine1, tleLine2) { +function calcPolarPlotSVG(timeframe, groundstation, tleLine1, tleLine2, geo, rotatorValues) { 'use strict'; const pi = Math.PI; @@ -13,7 +13,7 @@ function calcPolarPlotSVG(timeframe, groundstation, tleLine1, tleLine2) { height: 0 }; - var polarGetXY = function(az, el) { + var polarGetXY = function (az, el) { var ret = new Object(); ret.x = (90 - el) * Math.sin(az * deg2rad); ret.y = (el - 90) * Math.cos(az * deg2rad); @@ -35,79 +35,113 @@ function calcPolarPlotSVG(timeframe, groundstation, tleLine1, tleLine2) { var positionAndVelocity = satellite.propagate(satrec, t.toDate()); var gmst = satellite.gstime(t.toDate()); - var positionEci = positionAndVelocity.position; - var positionEcf = satellite.eciToEcf(positionEci, gmst); + var positionEci = positionAndVelocity.position; + var positionEcf = satellite.eciToEcf(positionEci, gmst); - var lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf); + var lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf); - return {'azimuth': lookAngles.azimuth * rad2deg, - 'elevation': lookAngles.elevation * rad2deg}; + return { + 'azimuth': lookAngles.azimuth * rad2deg, + 'elevation': lookAngles.elevation * rad2deg + }; } + if (geo == false) { + // Draw the orbit pass on the polar az/el plot + var g = ''; - // Draw the orbit pass on the polar az/el plot - var g = ''; - - for (var t = timeframe.start; t < timeframe.end; t = t.add(20, 's')) { - var sky_position = polarGetAzEl(t); - var coord = polarGetXY(sky_position.azimuth, sky_position.elevation); - - if (g == '') { - g += 'M'; - } else { - g += ' L'; + for (var t = timeframe.start; t < timeframe.end; t = t.add(20, 's')) { + var sky_position = polarGetAzEl(t); + var coord = polarGetXY(sky_position.azimuth, sky_position.elevation); + + if (g == '') { + g += 'M'; + } else { + g += ' L'; + } + g += coord.x + ' ' + coord.y; } - g += coord.x + ' ' + coord.y; - } + + polarOrbit.setAttribute('d', g); + + // Draw observation start + var point_start = document.createElementNS(svg_namespace, 'circle'); + point_start.setAttributeNS(null, 'fill', 'blue'); + point_start.setAttributeNS(null, 'stroke', 'black'); + point_start.setAttributeNS(null, 'stroke-width', '1'); + + var sky_position_rise = polarGetAzEl(timeframe.start); + var coord_rise = polarGetXY(sky_position_rise.azimuth, sky_position_rise.elevation); + + point_start.setAttribute('cx', coord_rise.x); + point_start.setAttribute('cy', coord_rise.y); + point_start.setAttribute('r', 2); - polarOrbit.setAttribute('d', g); + // Draw oberservation end + var point_end = document.createElementNS(svg_namespace, 'circle'); + point_end.setAttributeNS(null, 'fill', 'blue'); + point_end.setAttributeNS(null, 'stroke', 'black'); + point_end.setAttributeNS(null, 'stroke-width', '1'); + + var sky_position_set = polarGetAzEl(timeframe.end); + var coord_set = polarGetXY(sky_position_set.azimuth, sky_position_set.elevation); + + point_end.setAttribute('cx', coord_set.x); + point_end.setAttribute('cy', coord_set.y); + point_end.setAttribute('r', 2); + + } - // Draw observation start - var point_start = document.createElementNS(svg_namespace, 'circle'); - point_start.setAttributeNS(null, 'fill', 'blue'); - point_start.setAttributeNS(null, 'stroke', 'black'); - point_start.setAttributeNS(null, 'stroke-width', '1'); + // draw rotator + if (rotatorValues.az >= 0 && rotatorValues.el >= 0) + { + + var point_rotator = document.createElementNS(svg_namespace, 'circle'); + var coord_rotator = polarGetXY(rotatorValues.az, rotatorValues.el); + point_rotator.setAttribute('cx', coord_rotator.x); + point_rotator.setAttribute('cy', coord_rotator.y); + point_rotator.setAttribute('r', 9); + //point_end.setAttributeNS(null, 'fill', 'blue'); + point_rotator.setAttributeNS(null, 'stroke', 'purple'); + point_rotator.setAttributeNS(null, 'stroke-width', '1'); + point_rotator.setAttributeNS(null, 'fill-opacity', '0.1'); + + /* + var point_crosshair = document.createElementNS(svg_namespace, 'path'); + point_crosshair.setAttributeNS(null, 'stroke', 1) + point_crosshair.setAttributeNS(null, 'stroke-width', 1) + var pathdata = "M" + coord_rotator.x + " " + coord_rotator.y + " L" + (coord_rotator.x + 10) + " " + coord_rotator.y + console.log(pathdata) + point_crosshair.setAttribute('d', "M" + coord_rotator.x + " " + coord_rotator.y + " L" + (coord_rotator.x + 10) + " " + coord_rotator.y) + */ + + } - var sky_position_rise = polarGetAzEl(timeframe.start); - var coord_rise = polarGetXY(sky_position_rise.azimuth, sky_position_rise.elevation); // sat visible now ? var sky_position_current = polarGetAzEl(new dayjs(new Date())); - if (sky_position_current.elevation > 0 ) - { + if (sky_position_current.elevation > 0) { var point_current = document.createElementNS(svg_namespace, 'circle'); point_current.setAttributeNS(null, 'fill', 'green'); point_current.setAttributeNS(null, 'stroke', 'black'); point_current.setAttributeNS(null, 'stroke-width', '1'); - + var coord_set = polarGetXY(sky_position_current.azimuth, sky_position_current.elevation); - + point_current.setAttribute('cx', coord_set.x); point_current.setAttribute('cy', coord_set.y); point_current.setAttribute('r', 7); } - point_start.setAttribute('cx', coord_rise.x); - point_start.setAttribute('cy', coord_rise.y); - point_start.setAttribute('r', 2); - - // Draw oberservation end - var point_end = document.createElementNS(svg_namespace, 'circle'); - point_end.setAttributeNS(null, 'fill', 'blue'); - point_end.setAttributeNS(null, 'stroke', 'black'); - point_end.setAttributeNS(null, 'stroke-width', '1'); - - var sky_position_set = polarGetAzEl(timeframe.end); - var coord_set = polarGetXY(sky_position_set.azimuth, sky_position_set.elevation); - - point_end.setAttribute('cx', coord_set.x); - point_end.setAttribute('cy', coord_set.y); - point_end.setAttribute('r', 2); + var drawObjects = [polarOrbit, point_start, point_end]; + if (point_current != null) + drawObjects.push(point_current) + if (point_rotator != null) + { + drawObjects.push(point_rotator) + } - if ( point_current == null) - return [polarOrbit, point_start, point_end]; - else - return [polarOrbit, point_start, point_end, point_current]; + return drawObjects } diff --git a/app/static/js/rotator.js b/app/static/js/rotator.js index 9790c06..8aa310d 100644 --- a/app/static/js/rotator.js +++ b/app/static/js/rotator.js @@ -1,15 +1,50 @@ -function rotatorConnectionOpen(event) { - console.log("Rotator Connected"); +var rotatorConnected = false; +var azActual = -1 +var elActual = -1 + +function connectRotator() +{ + if ( rotatorConnected == true ) + { + return; } - function rotatorConnectionClosed(event) { - console.log("Rotator Disconnected"); + try { + + rotator_header.innerHTML = "Rotator Control Connecting"; + + connectWS("ws://192.168.0.250:1880/ws/rotator", rotatorConnectionOpen, rotatorConnectionClosed, rotatorConnectionMessageReceived); } + catch (err) { + console.log("Couldn't connect to rotator websocket") + } + +} + +function rotatorConnectionOpen(event) { + console.log("Rotator Connected"); + + rotator_header.innerHTML = "Rotator Control Connected"; + + rotatorConnected = true; + $("#rotatorEnabled").attr("disabled", false); +} + +function rotatorConnectionClosed(event) { + rotator_header.innerHTML = "Rotator Control Disconnected"; + + $("#rotatorEnabled").attr("disabled", true); + + console.log("Rotator Disconnected"); + rotatorConnected = false; +} - function rotatorConnectionMessageReceived(event) { - console.log(JSON.parse(event.data)) +function rotatorConnectionMessageReceived(event) { + console.log(JSON.parse(event.data)) - rotatorData = JSON.parse(event.data) - rot_actual_el.innerHTML = rotatorData.el; - rot_actual_az.innerHTML = rotatorData.az; - } \ No newline at end of file + rotatorData = JSON.parse(event.data) + azActual = rotatorData.az; + elActual = rotatorData.el; + rot_actual_el.innerHTML = rotatorData.el; + rot_actual_az.innerHTML = rotatorData.az; +} \ No newline at end of file diff --git a/app/static/js/satellite_tracker.js b/app/static/js/satellite_tracker.js index 3579f12..6520f62 100644 --- a/app/static/js/satellite_tracker.js +++ b/app/static/js/satellite_tracker.js @@ -27,7 +27,6 @@ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { var table = document.getElementById("satellitelist"); - // search for satellite object in satellite list via title function getSatelliteByTitle(arr, value) { for (var i = 0, iLen = arr.length; i < iLen; i++) { @@ -41,12 +40,86 @@ function getSatelliteByTitle(arr, value) { function calcSatellitePositions(satrec, dateobject) { var gmst = satellite.gstime(dateobject); var positionAndVelocity = satellite.propagate(satrec, dateobject); + var positionEci = positionAndVelocity.position; var velocityEci = positionAndVelocity.velocity; var positionGd = satellite.eciToGeodetic(positionEci, gmst); var positionEcf = satellite.eciToEcf(positionEci, gmst); var lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf); - return [positionGd, lookAngles] + var positionEcf = satellite.eciToEcf(positionEci, gmst); + //var velocityEcf = satellite.eciToEcf(velocityEci, gmst); + var observerEcf = satellite.geodeticToEcf(observerGd); + var observerEci = satellite.ecfToEci(observerEcf, gmst); + + var dopplerFactorEci = satellite.dopplerFactor(observerEci, positionEci, velocityEci); + + return [positionGd, lookAngles, dopplerFactorEci] +} + +function calcNextPass(satrec) { + + if (isGeostationary(satrec)) { + return { 'aos': null, 'los': null, 'maxel_dt': null, 'max_el': 0, 'start_az': 0, 'path': [] } + } + + var start = new dayjs(new Date()) + var end = start.add(2, 'd') + + console.log(start) + console.log(end) + var prevLat = 0; + var prevLon = 0; + + var aos_dt = null + var los_dt = null + var maxel_dt = null + var max_el = -1 + var start_az = -1 + var prev_el = -1 + + var path = [] + + for (var t = start; t < end; t = t.add(20, 's')) { + var position = calcSatellitePositions(satrec, t.toDate()) + + //console.log(position) + var elevation = satellite.radiansToDegrees(position[1].elevation) + var azimuth = satellite.radiansToDegrees(position[1].azimuth) + + //console.log(elevation) + + if (elevation > 0) { + // pass started + if (prev_el < 0) { + console.log("sat rise") + max_el = elevation + start_az = azimuth + aos_dt = t + } + + // determine highest point + if (elevation > max_el) { + max_el = elevation + maxel_dt = t + } + + newLat = satellite.radiansToDegrees(position[0].latitude); + newLon = satellite.radiansToDegrees(position[0].longitude) + + path.push([newLat, newLon]) + } + else { + // have we reached end of the pass + if (prev_el > 0) { + los_dt = t; + break; + } + } + + prev_el = elevation; + } + + return { 'aos': aos_dt, 'los': los_dt, 'maxel_dt': maxel_dt, 'max_el': max_el, 'start_az': start_az, 'path': path } } // update satellite data from server (called when next pass is over) @@ -58,97 +131,201 @@ function updateData(jsondata) { jsondata.satellites.forEach(element => { for (var i = 0, iLen = satellites.length; i < iLen; i++) { if (satellites[i].title == element.title) { - var nextpass = [] - for (var x = 0; x < element.next_pass.passdata.length; x++) { - nextpass.push([element.next_pass.passdata[x].latitude, element.next_pass.passdata[x].longitude]) - } - satellites[i].next_pass_data = nextpass - satellites[i].all_data = element + var satrec = satellite.twoline2satrec(element.tle1, element.tle2); + satellites[i].next_pass_data = calcNextPass(satrec) break; } } }) }; +/* +Arccosine implementation. + +Returns a value between zero and two pi. +Borrowed from gsat 0.9 by Xavier Crehueras, EB3CZS. +Optimized by Alexandru Csete. +*/ +function arccos(x, y) { + if (x && y) { + if (y > 0.0) + return Math.acos(x / y); + else if (y < 0.0) + return pi + Math.acos(x / y); + } + + return 0.0; +} + +/* +Range circle calculations. + +Borrowed from gsat 0.9.0 by Xavier Crehueras, EB3CZS +who borrowed from John Magliacane, KD2BD. +Optimized by Alexandru Csete and William J Beksi. +*/ +function generateFootprint(satrec) { + pos = calcSatellitePositions(satrec, new Date()) + + var xkmper = 6.378135e3; + var pi = 3.1415926535898; /* Pi */ + var pio2 = 1.5707963267949; /* Pi/2 */ + + // not sure if this is 100% correct, but it looks right + var footprint = 12756.33 * Math.acos(xkmper / (xkmper + pos[0].height)) + + var beta = (0.5 * footprint) / xkmper; + var ssplat = pos[0].latitude + var ssplon = pos[0].longitude + + var points = []; + + var lat = 0; + var lon = 0; + + for (azi = 0; azi < 360; azi += 1) { + azimuth = satellite.degreesToRadians(azi); + lat = Math.asin(Math.sin(ssplat) * Math.cos(beta) + Math.cos(azimuth) * Math.sin(beta) + * Math.cos(ssplat)); + num = Math.cos(beta) - (Math.sin(ssplat) * Math.sin(lat)); + dem = Math.cos(ssplat) * Math.cos(lat); + + if (azi == 0 && (beta > pio2 - ssplat)) + lon = ssplon + pi; + + else if (azi == 180 && (beta > pio2 + ssplat)) + lon = ssplon + pi; + + else if (Math.abs(num / dem) > 1.0) + lon = ssplon; + + else { + if ((180 - azi) >= 0) + lon = ssplon - arccos(num, dem); + else + lon = ssplon + arccos(num, dem); + } + + points.push([satellite.radiansToDegrees(lat), satellite.radiansToDegrees(lon)]); + } + + + return points; +} + +function dopplerDiff(dopplerFactor, frequency) { + dop100mhz = Math.round(100 * dopplerFactor * 1000000) + dop = dop100mhz - 100000000 + return dop +} function updateSelectedSatellite() { if (selectedSatellite == null) return + layerGroup.clearLayers(); + satelliteData = selectedSatellite; var satdata = calcSatellitePositions(satelliteData.satrec, new Date()) + var positionGd = satdata[0] var lookAngles = satdata[1] + var doppler = satdata[2] + + dopp = dopplerDiff(doppler, 100) // satellite properties prop_title.innerHTML = satelliteData.title prop_az.innerHTML = satellite.radiansToDegrees(lookAngles.azimuth).toFixed(2) prop_el.innerHTML = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) - prop_range.innerHTML = lookAngles.rangeSat.toFixed(0) - - var satrise = new dayjs(satelliteData.all_data.next_pass.satrise) - - var difference = satrise.toDate().getTime() - (new Date().getTime()) - var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); - - if (hours >= 0 && minutes >= 0) { - prop_countdown.innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); + if (satelliteData.prevRange == null) { + prop_range.innerHTML = lookAngles.rangeSat.toFixed(0) + " (?)" } else { - prop_countdown.innerHTML = 'Now'; + if (satelliteData.prevRange > lookAngles.rangeSat) { + prop_range.innerHTML = lookAngles.rangeSat.toFixed(0) + " (-)" + prop_dopup.innerHTML = dop + "Hz" + } + else { + prop_range.innerHTML = lookAngles.rangeSat.toFixed(0) + " (+)" + prop_dopup.innerHTML = (-1 * dop) + "Hz" + } } + selectedSatellite.prevRange = lookAngles.rangeSat - // draw next pass - layerGroup.clearLayers(); - var polyline = L.polyline(satelliteData.next_pass_data).arrowheads({ size: '20px', frequency: 'endonly' }); - polyline.addTo(layerGroup); + // draw footprint + footprintpoints = generateFootprint(satelliteData.satrec); + var footprint = L.polygon(footprintpoints); + footprint.addTo(layerGroup); - // draw orbit - orbit = [] - var start = new dayjs(new Date()) - var end = new dayjs(new Date(satelliteData.all_data.next_pass.satrise)) + if (isGeostationary(satelliteData.satrec) == false) { + var satrise = satelliteData.next_pass_data.aos; - var prevLat = 0; - var prevLon = 0; - - for (var t = start; t < end; t = t.add(20, 's')) { - var position = calcSatellitePositions(satelliteData.satrec, t.toDate()) + var difference = satrise.toDate().getTime() - (new Date().getTime()) - newLat = satellite.radiansToDegrees(position[0].latitude); - newLon = satellite.radiansToDegrees(position[0].longitude) + var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); - if (Math.abs(newLat - prevLat) > 10) { - var polyline = L.polyline(orbit, { color: 'black' }); - polyline.addTo(layerGroup); - orbit = [] + if (hours >= 0 && minutes >= 0) { + prop_countdown.innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); } - - if (Math.abs(newLon - prevLon) > 10) { - var polyline = L.polyline(orbit, { color: 'black' }); - polyline.addTo(layerGroup); - orbit = [] + else { + prop_countdown.innerHTML = 'Now'; } - orbit.push([newLat, newLon]) + // draw next pass + var polyline = L.polyline(satelliteData.next_pass_data.path).arrowheads({ size: '20px', frequency: 'endonly' }); + polyline.addTo(layerGroup); + + // draw orbit + orbit = [] + var start = new dayjs(new Date()) + var end = satelliteData.next_pass_data.aos + + var prevLat = 0; + var prevLon = 0; + + for (var t = start; t < end; t = t.add(20, 's')) { + var position = calcSatellitePositions(satelliteData.satrec, t.toDate()) + + newLat = satellite.radiansToDegrees(position[0].latitude); + newLon = satellite.radiansToDegrees(position[0].longitude) + + if (Math.abs(newLat - prevLat) > 10) { + var polyline = L.polyline(orbit, { color: 'black' }); + polyline.addTo(layerGroup); + orbit = [] + } + + if (Math.abs(newLon - prevLon) > 10) { + var polyline = L.polyline(orbit, { color: 'black' }); + polyline.addTo(layerGroup); + orbit = [] + } + + orbit.push([newLat, newLon]) - prevLat = satellite.radiansToDegrees(position[0].latitude) - prevLon = satellite.radiansToDegrees(position[0].longitude) + prevLat = satellite.radiansToDegrees(position[0].latitude) + prevLon = satellite.radiansToDegrees(position[0].longitude) + } + var polyline = L.polyline(orbit, { color: 'black' }); + polyline.addTo(layerGroup); + } + else { + prop_countdown.innerHTML = "N/A" } - var polyline = L.polyline(orbit, { color: 'black' }); - polyline.addTo(layerGroup); // draw polar graph var tleLine1 = satelliteData.tle1 var tleLine2 = satelliteData.tle2 var timeframe = { - start: new dayjs(new Date(satelliteData.all_data.next_pass.satrise)), - end: new dayjs(new Date(satelliteData.all_data.next_pass.satset)) + start: new dayjs(new Date(satelliteData.next_pass_data.aos)), + end: new dayjs(new Date(satelliteData.next_pass_data.los)) }; @@ -159,10 +336,44 @@ function updateSelectedSatellite() { }; + // send rotator position + if ($("#rotatorEnabled").is(':checked')) { + + var az = satellite.radiansToDegrees(lookAngles.azimuth).toFixed(2) + var el = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) + + // only send if elevation > 0 + // TODO: improve this to handle with 0 - 180 elevation rotator (ie. flip) + if (el > 0) { + rot_requested_az.innerHTML = az; + rot_requested_el.innerHTML = el; + + if (rotatorConnected) + { + var rotatorData = { 'az' : satellite.radiansToDegrees(lookAngles.azimuth), 'el' : satellite.radiansToDegrees(lookAngles.elevation)} + sendWS(rotatorData); + } + } + else + { + rot_requested_az.innerHTML = satelliteData.next_pass_data.start_az.toFixed(2) + rot_requested_el.innerHTML = 0; + + if (rotatorConnected) + { + var rotatorData = { 'az' : satelliteData.next_pass_data.start_az, 'el' : 0} + sendWS(rotatorData); + } + + } + } + + + // update polar graph var polarPlotSVG = calcPolarPlotSVG(timeframe, groundstation, tleLine1, - tleLine2); + tleLine2, isGeostationary(satelliteData.satrec), { 'az' : azActual, 'el' : elActual }); var based = ' \ \ @@ -186,19 +397,6 @@ function updateSelectedSatellite() { $('svg#polar').html(based); $('svg#polar').append(polarPlotSVG); - // send rotator position - if ($("#rotatorEnabled").is(':checked')) { - var az = satellite.radiansToDegrees(lookAngles.azimuth).toFixed(2) - var el = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) - - // only send if elevation > 0 - // TODO: improve this to handle with 0 - 180 elevation rotator (ie. flip) - if (el > 0) { - rot_requested_az.innerHTML = az; - rot_requested_el.innerHTML = el; - sendWS(lookAngles) - } - } } function selectSatellite(marker) { @@ -219,6 +417,14 @@ function selectSatellite(marker) { } } +function isGeostationary(satrec) { + + if (satrec.no < 0.005) + return true; + else + return false; +} + function initMap(jsondata) { console.log(jsondata); layerGroup.clearLayers(); @@ -265,12 +471,16 @@ function initMap(jsondata) { var satrec = satellite.twoline2satrec(element.tle1, element.tle2); + console.log(element.title) + console.log("Geostationary Check"); + console.log(satrec.no) + console.log(isGeostationary(satrec)); + var satdata = calcSatellitePositions(satrec, new Date()) var positionGd = satdata[0] var lookAngles = satdata[1] - var lat = satellite.radiansToDegrees(positionGd.latitude); var lon = satellite.radiansToDegrees(positionGd.longitude); @@ -282,23 +492,7 @@ function initMap(jsondata) { satObject.marker.setLatLng([lat, lon]).update() satObject.satrec = satrec - - var nextpass = [] - - // just skip it if error - if (element.next_pass == null) { - return; - } - - for (var x = 0; x < element.next_pass.passdata.length; x++) { - nextpass.push([element.next_pass.passdata[x].latitude, element.next_pass.passdata[x].longitude]) - } - - satObject.next_pass_data = nextpass - - - //satObject.orbit_data = orbit - satObject.all_data = element + satObject.next_pass_data = calcNextPass(satrec) var newRow = table.insertRow(); var satname = newRow.insertCell(); @@ -316,25 +510,37 @@ function initMap(jsondata) { satel.innerHTML = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) satrange.innerHTML = lookAngles.rangeSat.toFixed(0) - var satrise = new dayjs(element.next_pass.satrise) - var satset = new dayjs(element.next_pass.satset) - var satmax = new dayjs(element.next_pass.satmax) + if (isGeostationary(satrec) == false) { + var satrise = satdata.aos + var satset = satdata.los + var satmax = satdata.maxel_dt + + if (satrise != null) nextpassaos.innerHTML = satrise.utc().format('YYYY/MM/DD HH:mm') + if (satmax != null) nextpassmax.innerHTML = satmax.utc().format('YYYY/MM/DD HH:mm') + if (satset != null) nextpasslos.innerHTML = satset.utc().format('YYYY/MM/DD HH:mm') - nextpassaos.innerHTML = satrise.utc().format('YYYY/MM/DD HH:mm') - nextpassmax.innerHTML = satmax.utc().format('YYYY/MM/DD HH:mm') - nextpasslos.innerHTML = satset.utc().format('YYYY/MM/DD HH:mm') - maxel.innerHTML = element.next_pass.maxel.toFixed(2) + maxel.innerHTML = satdata.max_el - var difference = satrise.toDate().getTime() - (new Date().getTime()) + if (satrise != null) { + var difference = satrise.toDate().getTime() - (new Date().getTime()) - var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); + var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); - if (hours >= 0 && minutes >= 0) { - countdown.innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); + if (hours >= 0 && minutes >= 0) { + countdown.innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); + } + else { + countdown.innerHTML = 'Now'; + } + } } else { - countdown.innerHTML = 'Now'; + nextpassaos.innerHTML = "N/A" + nextpassmax.innerHTML = "N/A" + nextpasslos.innerHTML = "N/A" + countdown.innerHTML = "N/A" + maxel.innerHTML = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) } satObject.row = newRow @@ -350,8 +556,6 @@ function initMap(jsondata) { updateLoop(); } - - function updateLoop() { sortTable(5, table); @@ -391,27 +595,41 @@ function updateLoop() { table.rows[x].cells[2].innerHTML = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) table.rows[x].cells[3].innerHTML = lookAngles.rangeSat.toFixed(2) - var satrise = new dayjs(element.all_data.next_pass.satrise) - var satset = new dayjs(element.all_data.next_pass.satset) - var satmax = new dayjs(element.all_data.next_pass.satmax) - - if (satset < new dayjs()) { - console.log("need update") - needUpdate = 1; + if (isGeostationary(element.satrec) == false) { + var satrise = element.next_pass_data.aos + var satset = element.next_pass_data.los + var satmax = element.next_pass_data.maxel_dt + + if (satset != null) { + if (satset < new dayjs()) { + console.log("need update") + needUpdate = 1; + } + } + + if (satrise != null && satset != null && satmax != null) { + var difference = satrise.toDate().getTime() - (new Date().getTime()) + var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); + + + if (hours >= 0 && minutes >= 0) { + table.rows[x].cells[4].innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); + } + else { + table.rows[x].cells[4].innerHTML = 'Now'; + } + + table.rows[x].cells[5].innerHTML = satrise.utc().format('YYYY/MM/DD HH:mm') + table.rows[x].cells[6].innerHTML = satmax.utc().format('YYYY/MM/DD HH:mm') + table.rows[x].cells[7].innerHTML = satset.utc().format('YYYY/MM/DD HH:mm') + } + + table.rows[x].cells[8].innerHTML = element.next_pass_data.max_el.toFixed(2) + } + else { + table.rows[x].cells[8].innerHTML = satellite.radiansToDegrees(lookAngles.elevation).toFixed(2) } - - var difference = satrise.toDate().getTime() - (new Date().getTime()) - - var hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - var minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); - - //countdown.innerHTML = hours + ":" + minutes; - - table.rows[x].cells[4].innerHTML = hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0'); - table.rows[x].cells[5].innerHTML = satrise.utc().format('YYYY/MM/DD HH:mm') - table.rows[x].cells[6].innerHTML = satmax.utc().format('YYYY/MM/DD HH:mm') - table.rows[x].cells[7].innerHTML = satset.utc().format('YYYY/MM/DD HH:mm') - table.rows[x].cells[8].innerHTML = element.all_data.next_pass.maxel.toFixed(2) } } @@ -459,11 +677,6 @@ function updateSatelliteList() { $(document).ready(function () { getSatelliteList(); - try { - connectWS("ws://localhost:1880/ws/rotator", rotatorConnectionOpen, rotatorConnectionClosed, rotatorConnectionMessageReceived); - } - catch (err) { - console.log("Couldn't connect to rotator websocket") - } + connectRotator(); }); diff --git a/app/templates/index.html b/app/templates/index.html index 36d2bbe..2253923 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -78,6 +78,12 @@
+ + Doppler@100Mhz + +
+ +
-
- Rotator Control +
diff --git a/images/node_red_basic.png b/images/node_red_basic.png new file mode 100644 index 0000000000000000000000000000000000000000..581b2558a5df46adade31909b766066d301fa776 GIT binary patch literal 22186 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�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 literal 0 HcmV?d00001 diff --git a/nodered_flow_examples/basic_template.json b/nodered_flow_examples/basic_template.json new file mode 100644 index 0000000..9da7664 --- /dev/null +++ b/nodered_flow_examples/basic_template.json @@ -0,0 +1,162 @@ +[ + { + "id": "0d2779ba14e375b5", + "type": "tab", + "label": "Satellite Server Control", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "6b8438d5e9a11a22", + "type": "websocket in", + "z": "0d2779ba14e375b5", + "name": "", + "server": "70c045e4549401f7", + "client": "", + "x": 320, + "y": 260, + "wires": [ + [ + "44b44287fc260a5b" + ] + ] + }, + { + "id": "0d0420f25d82e593", + "type": "websocket out", + "z": "0d2779ba14e375b5", + "name": "", + "server": "70c045e4549401f7", + "client": "", + "x": 860, + "y": 440, + "wires": [] + }, + { + "id": "aa57fdb47adb550d", + "type": "function", + "z": "0d2779ba14e375b5", + "name": "Update From Rotator", + "func": "// update this function with code to read from your rotator\nvar az_value = 179;\nvar el_value = 50\n\nvar newAzimuth = { payload: { 'az' : az_value, 'el' : el_value } }\n\nreturn newAzimuth;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 620, + "y": 440, + "wires": [ + [ + "0d0420f25d82e593" + ] + ] + }, + { + "id": "8c724718ab717d61", + "type": "inject", + "z": "0d2779ba14e375b5", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "2", + "crontab": "", + "once": true, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "date", + "x": 370, + "y": 440, + "wires": [ + [ + "aa57fdb47adb550d" + ] + ] + }, + { + "id": "666ef6429cf7ae1f", + "type": "function", + "z": "0d2779ba14e375b5", + "name": "Seperate Request", + "func": "// This function outputs the requested azimuth and elevation\n// from the satellite server. You need to add nodes after this\n// which will control your rotator\n\nvar az = msg.payload.az\nvar el = msg.payload.el\n\nvar newAzimuth = { payload: az }\nvar newElevation = { payload: el }\n\nreturn [newAzimuth, newElevation]", + "outputs": 2, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 670, + "y": 260, + "wires": [ + [ + "ce162e92462be6d4" + ], + [ + "33cbdf3e4e339bc4" + ] + ] + }, + { + "id": "44b44287fc260a5b", + "type": "json", + "z": "0d2779ba14e375b5", + "name": "", + "property": "payload", + "action": "", + "pretty": false, + "x": 490, + "y": 260, + "wires": [ + [ + "666ef6429cf7ae1f" + ] + ] + }, + { + "id": "ce162e92462be6d4", + "type": "debug", + "z": "0d2779ba14e375b5", + "name": "Set Azimuth", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 240, + "wires": [] + }, + { + "id": "33cbdf3e4e339bc4", + "type": "debug", + "z": "0d2779ba14e375b5", + "name": "Set Elevation", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 870, + "y": 280, + "wires": [] + }, + { + "id": "70c045e4549401f7", + "type": "websocket-listener", + "path": "/ws/rotator", + "wholemsg": "false" + } +] \ No newline at end of file