diff --git a/station/recorder.py b/station/recorder.py index 853e958..6de8317 100644 --- a/station/recorder.py +++ b/station/recorder.py @@ -6,6 +6,7 @@ from simplecom import simplecom from pathlib import Path import config import time +import datetime # A recursive function to remove the folder def del_folder(path): @@ -47,8 +48,12 @@ class recorder(threading.Thread): time.sleep(50) + realStart = datetime.datetime.utcnow().timestamp() + os.system(f"satdump record {baseband} --source {self.job['receiver']['params']['radio']} --samplerate {fs} --frequency {self.job['transmitter']['centerFrequency']} --gain {self.job['receiver']['params']['gain']} --baseband_format s8 --timeout {recordTime}") + realEnd = datetime.datetime.utcnow().timestamp() + print(f"Recorder for job {self.job['target']['name']} stoped") puller.setRecorded(self.job["id"]) @@ -63,9 +68,21 @@ class recorder(threading.Thread): adir = f"artefacts/{self.job['id']}" os.makedirs(adir) + replacements = { + "{baseband}": str(baseband) + ".s8", + "{fs}": str(fs), + "{artefactDir}": str(adir), + "{freq}": str(self.job['transmitter']['centerFrequency']), + "{targetNum}": ''.join(x for x in self.job['target']['name'] if x.isdigit()), + "{target}": self.job['target']['name'], + "{start}": str(realStart), + "{end}": str(realEnd) + } + for pipe in self.job["proccessPipe"]: #ok now replace - pipe = pipe.replace("{baseband}", str(baseband) + ".s8").replace("{fs}", str(fs)).replace("{artefactDir}", str(adir)).replace("{freq}", str(self.job['transmitter']['centerFrequency'])) + for k, v in replacements.items(): + pipe = pipe.replace(k, v) os.system(pipe) diff --git a/web/API/stations.php b/web/API/stations.php index 8e2401b..7d7825d 100644 --- a/web/API/stations.php +++ b/web/API/stations.php @@ -39,17 +39,6 @@ return $res; } - function keys($params) { - $stations = new \wsos\database\core\table(\DAL\station::class); - - $res = []; - foreach ($stations->getAll()->values as $station) { - $res[] = ["name" => $station->name->get(), "key" => $station->apiKey->get()]; - } - - return $res; - } - function add($params) { $stations = new \wsos\database\core\table(\DAL\station::class); @@ -66,5 +55,41 @@ $myStation->commit(); + return ["id" => $myStation->id->get()]; + } + + function update($params) { + $stations = new \wsos\database\core\table(\DAL\station::class); + + $myStation = new \DAL\station(); + $myStation->id->set($params["id"]); + $myStation->fetch(); + + $myStation->name->set($params["name"]); + $myStation->description->set($params["description"]); + $myStation->locator->set([ + "gps" => [ + "lat" => floatval($params["lat"]), + "lon" => floatval($params["lon"]), + "alt" => floatval($params["alt"]) + ] + ]); + + $myStation->commit(); + + return ["id" => $myStation->id->get()]; + } + + function apiRegenerate($params) { + $stations = new \wsos\database\core\table(\DAL\station::class); + + $myStation = new \DAL\station(); + $myStation->id->set($params["id"]); + $myStation->fetch(); + + $myStation->apiKey->regenerate(); + + $myStation->commit(); + return ["id" => $myStation->id->get()]; } \ No newline at end of file diff --git a/web/CONTROLLERS/targets.php b/web/CONTROLLERS/targets.php new file mode 100644 index 0000000..68edaa4 --- /dev/null +++ b/web/CONTROLLERS/targets.php @@ -0,0 +1,36 @@ +<?php + $container = new \wsos\structs\container(); + + $templates = $container->get("templateLoader"); + $context = $container->get("context"); + $auth = $container->get("auth"); + + // to show this page user must be logined + $auth->requireLogin(); + + $context["targets"] = new \wsos\structs\vector(); + + $targets = (new \wsos\database\core\table(\DAL\target::class))->getAll(); + + foreach ($targets->values as $target) { + + $last = (new \wsos\database\core\table(\DAL\observation::class))->query("transmitter.target.id = ?", [$target->id->get()], "DESC end", 1); + $last = $last->len() > 0 ? "ago " . $last->values[0]->end->strDelta() : "never"; + + $observations = (new \wsos\database\core\table(\DAL\observation::class))->count("transmitter.target.id = ?", [$target->id->get()]); + + $context["targets"]->append([ + "name" => $target->name->get(), + "orbit" => $target->orbit->get(), + "type" => $target->type->get()->name->get(), + "last" => $last, + "observations" => $observations + ]); + } + + $context["targets"] = $context["targets"]->values; + + + $templates->load("targets.html"); + $templates->render($context); + $templates->show(); \ No newline at end of file diff --git a/web/DAL/target.php b/web/DAL/target.php index 232cf2e..f60e5cc 100644 --- a/web/DAL/target.php +++ b/web/DAL/target.php @@ -5,12 +5,23 @@ public \wsos\database\types\text $name; // noaa19, jonHAM, ... , ... public \wsos\database\types\reference $type; // sat, groundStation, ... public \wsos\database\types\text $description; + public \wsos\database\types\enum $orbit; public \wsos\database\types\json $locator; // TLE, GPS or URL locator if avaible - function __construct($id = null, $name = "", $type = null, $description = "", $locator = []) { + function __construct($id = null, $name = "", $type = null, $description = "", $orbit = "", $locator = []) { parent::__construct($id); $this->name = new \wsos\database\types\text($name); $this->type = new \wsos\database\types\reference($type, \DAL\targetType::class); + + $this->orbit = new \wsos\database\types\enum($orbit, [ + "leo", + "meo", + "geo", + "sso", + "gto", + "none", + "other" + ], "other"); $this->description = new \wsos\database\types\text($description); $this->locator = new \wsos\database\types\json($locator); diff --git a/web/VIEWS/blocks/target-item.html b/web/VIEWS/blocks/target-item.html new file mode 100644 index 0000000..7fa10f6 --- /dev/null +++ b/web/VIEWS/blocks/target-item.html @@ -0,0 +1,7 @@ +<tr onclick="location.href = '/target/{% BIND item.id %}'"> + <td>{% BIND item.name %}</td> + <td>{% BIND item.type %}</td> + <td>{% BIND item.orbit %}</td> + <td>{% BIND item.last %}</td> + <td>{% BIND item.observations %}</td> +</tr> \ No newline at end of file diff --git a/web/VIEWS/observation.html b/web/VIEWS/observation.html index 7cd7946..f9c3378 100644 --- a/web/VIEWS/observation.html +++ b/web/VIEWS/observation.html @@ -214,6 +214,13 @@ return parts[parts.length - 1].toLowerCase(); } + const getMeta = (url, cb) => { + const img = new Image(); + img.onload = () => cb(null, img); + img.onerror = (err) => cb(err); + img.src = url; + }; + function art(el) { var url = el.getAttribute("value"); var name = el.innerHTML; @@ -221,7 +228,15 @@ document.getElementById("artefact-title").innerHTML = name; if (extension(name) == "png" || extension(name) == "jpg" ) { - document.getElementById("artefact-body") .innerHTML = "<img src='" + url + "'>"; + getMeta(url, (err, img) => { + var ratio = img.naturalWidth / img.naturalHeight; + + if (ratio < 1) { // height is bigger + document.getElementById("artefact-body") .innerHTML = "<img src='" + url + "' style='max-width: 500px; margin: auto;'>"; + } else { // width is bigger + document.getElementById("artefact-body") .innerHTML = "<img src='" + url + "'>"; + } + }); } else if (extension(name) == "json" || extension(name) == "txt" ) { var request = new XMLHttpRequest(); request.open('GET', url, true); diff --git a/web/VIEWS/station.html b/web/VIEWS/station.html index fe7722f..71cfaed 100644 --- a/web/VIEWS/station.html +++ b/web/VIEWS/station.html @@ -141,6 +141,60 @@ </div> </div> + <div class="col-md-6 col-lg-12"> + <div class="card"> + <div class="card-status-start bg-danger"></div> + <div class="card-body"> + <h3 class="card-title">Station basic info</h3> + <div class="row g-3"> + <div class="col-md"> + <div class="form-label">Name</div> + <input type="text" class="form-control" value="{% BIND station.name %}" id="station-name"> + </div> + </div> + <div class="row g-3 mt-2"> + <div class="col-md"> + <div class="form-label">Description</div> + <textarea class="form-control" id="station-description">{% BIND station.description %}</textarea> + </div> + </div> + <h3 class="card-title mt-4">Station location</h3> + <div class="row g-3"> + <div class="col-md"> + <div class="form-label">Latitude</div> + <input type="number" class="form-control" id="station-lat" value="{% BIND station.lat %}"> + </div> + <div class="col-md"> + <div class="form-label">Longitude</div> + <input type="number" class="form-control" id="station-lon" value="{% BIND station.lon %}"> + </div> + <div class="col-md"> + <div class="form-label">Altitude [m]</div> + <input type="number" class="form-control" id="station-alt" value="{% BIND station.alt %}"> + </div> + </div> + + <h3 class="card-title mt-4">API Key</h3> + <p class="card-subtitle">API key is used for access to YAGS server from others programs like yags-station.</p> + <div> + <a class="btn" onclick="apiRegenerate('{% BIND station.id %}')"> + Regenerate API Key + </a> + </div> + </div> + <div class="card-footer bg-transparent mt-auto"> + <div class="btn-list justify-content-end"> + <a href="#" class="btn btn-ghost-danger" onclick="deleteStation('{% BIND station.id %}')"> + Delete station + </a> + <a class="btn btn-primary" onclick="updateStation('{% BIND station.id %}')"> + Update station + </a> + </div> + </div> + </div> + </div> + </div> @@ -148,6 +202,7 @@ <!-- Tabler Core --> <script src="/dist/js/tabler.min.js?1668287865" defer=""></script> + <script src="/static/js/station.js" defer=""></script> </div> </div> </div> diff --git a/web/VIEWS/targets.html b/web/VIEWS/targets.html new file mode 100644 index 0000000..29f70d9 --- /dev/null +++ b/web/VIEWS/targets.html @@ -0,0 +1,127 @@ +<!doctype html> +<html lang="en"> + {% INCLUDE layout/head.html %} + <body> + <div class="page"> + {% BINDINCLUDE layout/header.html logined %} + + <div class="page-header d-print-none mt-4"> + <div class="container-xl"> + <div class="row g-2 align-items-center"> + <div class="col"> + <!-- Page pre-title --> + <div class="page-pretitle"> + stations + </div> + <h2 class="page-title"> + Targets + </h2> + </div> + <div class="col-auto ms-auto d-print-none"> + <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal"> + Add target + </button> + </div> + </div> + + <div class="col mt-4" style="display: none;" id="created-alert"> + <div class="alert alert-success" role="alert">Successly created observation plan <span id="created-id"></span> [<a href="/observations">refresh</a>]?</div> + </div> + </div> + </div> + + <div class="page-body"> + <div class="container-xl"> + <div class="row row-deck row-cards"> + + + <div class="col-12"> + <div class="card"> + <div class="card-header"> + <h3 class="card-title">Sats, Stations, HABs, ...</h3> + </div> + <div class="table-responsive"> + <table class="table card-table table-vcenter text-nowrap datatable table-hover"> + <thead> + <tr> + <th>Name</th> + <th>Type</th> + <th>Orbit</th> + <th>Last observation</th> + <th>Observations count</th> + </tr> + </thead> + <tbody> + {% FOREACH targets RENDER blocks/target-item.html %} + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="modal" id="exampleModal" tabindex="-1"> + <div class="modal-dialog modal-lg" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title">New target</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + <div class="row"> + <div class="col-lg-7"> + <div class="mb-3"> + <label class="form-label">Target transmitter</label> + <select class="form-select" id="plan-transmitter"> + {% FOREACH transmitters USE '<option value="(\ BIND item.id \)"> + (\ BIND item.target.name \) - + (\ BIND item.modulation.name \) - + (\ BIND item.dataType.name \) @ + (\ BIND item.centerFrequency \)Hz + </option>' %} + </select> + </div> + </div> + <div class="col-lg-5"> + <div class="mb-3"> + <label class="form-label">Station receiver</label> + <select class="form-select" id="plan-receiver"> + {% FOREACH receivers USE '<option value="(\ BIND item.id \)">(\ BIND item.station.name \) @ (\ BIND item.centerFrequency \)Hz</option>' %} + </select> + </div> + </div> + </div> + </div> + <div class="modal-body"> + <div class="row"> + <div class="col-lg-6"> + <div class="mb-3"> + <label class="form-label">Start UTC time</label> + <input type="datetime-local" id="plan-start" class="form-control"> + </div> + </div> + <div class="col-lg-6"> + <div class="mb-3"> + <label class="form-label">End UTC time</label> + <input type="datetime-local" id="plan-end" class="form-control"> + </div> + </div> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn me-auto" data-bs-dismiss="modal">Close</button> + <button type="button" onclick="plan()" class="btn btn-primary" data-bs-dismiss="modal">Plan!</button> + </div> + </div> + </div> + </div> + + <!-- Tabler Core --> + <script src="/dist/js/tabler.min.js?1668287865" defer=""></script> + + <script src="/static/js/observations.js"></script> + </div> + </body> +</html> \ No newline at end of file diff --git a/web/index.php b/web/index.php index 90e8f1a..124267a 100644 --- a/web/index.php +++ b/web/index.php @@ -15,9 +15,9 @@ $sites = [ "sites" => [ "observations" => ["controller" => __DIR__ . "/CONTROLLERS/observations.php", "name" => "Observations", "icon" => "/static/icons/telescope.svg", "menu" => true], + "targets" => ["controller" => __DIR__ . "/CONTROLLERS/targets.php", "name" => "Targets", "icon" => "/static/icons/focus-2.svg", "menu" => true], /* "stations" => ["controller" => __DIR__ . "/CONTROLLERS/stations.php", "name" => "Stations", "icon" => "/static/icons/radio.svg", "menu" => true], - "targets" => ["controller" => __DIR__ . "/CONTROLLERS/targets.php", "name" => "Targets", "icon" => "/static/icons/focus-2.svg", "menu" => true], "modulations" => ["controller" => __DIR__ . "/CONTROLLERS/modulations.php", "name" => "Modulations", "icon" => "/static/icons/wave-sine.svg", "menu" => true], "datatypes" => ["controller" => __DIR__ . "/CONTROLLERS/datatypes.php", "name" => "Data Types", "icon" => "/static/icons/file-analytics.svg", "menu" => true], */ diff --git a/web/seeds.php b/web/seeds.php index b28382b..641ca40 100644 --- a/web/seeds.php +++ b/web/seeds.php @@ -8,9 +8,9 @@ $admin->admin->set(true); $admin->commit(); /* commit changes to DB */ - $satType = new \DAL\targetType(); - $satType->name->set("sat"); - $satType->commit(); + $leoWSatTape = new \DAL\targetType(); + $leoWSatTape->name->set("Weather Satellite"); + $leoWSatTape->commit(); $avhrrType = new \DAL\dataType(); $avhrrType->name->set("AVHRR"); @@ -108,7 +108,7 @@ $aptPipe = new \DAL\processPipe(); $aptPipe->name->set("NOAA APT"); $aptPipe->pipe->set([ - "satdump noaa_apt baseband {baseband} {artefactDir} --samplerate {fs} --baseband_format s8", + "satdump noaa_apt baseband {baseband} {artefactDir} --samplerate {fs} --satellite_number {targetNum} --start_timestamp {start} --autocrop_wedges --baseband_format s8", "cp {baseband} {artefactDir}/{freq}_{fs}.s8" ]); @@ -129,7 +129,8 @@ */ $noaa19 = new \DAL\target(); $noaa19->name->set("NOAA 19"); - $noaa19->type->set($satType); + $noaa19->type->set($leoWSatTape); + $noaa19->orbit->set("leo"); $noaa19->description->set("NOAA 19 is the fifth in a series of five Polar-orbiting Operational Environmental Satellites (POES) with advanced microwave sounding instruments that provide imaging and sounding capabilities."); $noaa19->locator->set([ "tle" => [ @@ -173,7 +174,8 @@ */ $noaa18 = new \DAL\target(); $noaa18->name->set("NOAA 18"); - $noaa18->type->set($satType); + $noaa18->type->set($leoWSatTape); + $noaa18->orbit->set("leo"); $noaa18->description->set("NOAA 18, known before launch as NOAA-N, is a weather forecasting satellite run by NOAA. NOAA-N (18) was launched into a sun-synchronous orbit at an altitude of 854 km above the Earth, with an orbital period of 102 minutes. It hosts the AMSU-A, MHS, AVHRR, Space Environment Monitor SEM/2 instrument and High Resolution Infrared Radiation Sounder (HIRS) instruments, as well as the SBUV/2 ozone-monitoring instrument."); $noaa18->locator->set([ "tle" => [ @@ -217,7 +219,8 @@ */ $noaa15 = new \DAL\target(); $noaa15->name->set("NOAA 15"); - $noaa15->type->set($satType); + $noaa15->type->set($leoWSatTape); + $noaa15->orbit->set("leo"); $noaa15->description->set(""); $noaa15->locator->set([ "tle" => [ @@ -258,7 +261,8 @@ $meteor23 = new \DAL\target(); $meteor23->name->set("METEOR M2-3"); - $meteor23->type->set($satType); + $meteor23->type->set($leoWSatTape); + $meteor23->orbit->set("leo"); $meteor23->description->set(""); $meteor23->locator->set([ "tle" => [ diff --git a/web/static/js/dashboard.js b/web/static/js/dashboard.js index 701813d..a42db38 100644 --- a/web/static/js/dashboard.js +++ b/web/static/js/dashboard.js @@ -15,9 +15,9 @@ function addStation() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 200) { - alert(JSON.parse(this.responseText).id); - } + if (this.readyState == 4 && this.status == 200) { + window.location.href = "/station/" + JSON.parse(this.responseText).id; + } }; xhttp.open("POST", "/api/station/add", true); diff --git a/web/static/js/station.js b/web/static/js/station.js new file mode 100644 index 0000000..306935d --- /dev/null +++ b/web/static/js/station.js @@ -0,0 +1,58 @@ +function updateStation(id) { + var station = new FormData(); + + var name = document.getElementById("station-name").value; + var lat = document.getElementById("station-lat").value; + var lon = document.getElementById("station-lon").value; + var alt = document.getElementById("station-alt").value; + var description = document.getElementById("station-description").value; + + station.append('name', name); + station.append('lat', lat); + station.append('lon', lon); + station.append('alt', alt); + station.append('description', description); + station.append('id', id); + + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + location.reload(); + } + }; + + xhttp.open("POST", "/api/station/update", true); + xhttp.send(station); +} + +function deleteStation(id) { + var station = new FormData(); + + station.append('id', id); + + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + location.reload(); + } + }; + + xhttp.open("POST", "/api/station/delete", true); + xhttp.send(station); +} + +function apiRegenerate(id) { + var station = new FormData(); + + station.append('id', id); + + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + location.reload(); + } + }; + + xhttp.open("POST", "/api/station/apiRegenerate", true); + xhttp.send(station); +} \ No newline at end of file