mirror of
https://github.com/Lukas0025/YAGS.git
synced 2025-04-09 00:52:11 +01:00
Updated UI
This commit is contained in:
parent
9bdfeb0379
commit
d4750768c2
docker-compose.yml
station
uploads.iniweb
API
ARTEFACTS
CONTROLLERS
DAL
DB
VIEWS
index.phpseeds.phpstatic
@ -5,6 +5,7 @@ services:
|
||||
build: ./web
|
||||
volumes:
|
||||
- ./web:/var/www/html/
|
||||
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
|
||||
ports:
|
||||
- 8000:80
|
||||
|
||||
|
BIN
station/__pycache__/config.cpython-311.pyc
Normal file
BIN
station/__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
BIN
station/__pycache__/puller.cpython-311.pyc
Normal file
BIN
station/__pycache__/puller.cpython-311.pyc
Normal file
Binary file not shown.
8
station/config.py
Normal file
8
station/config.py
Normal file
@ -0,0 +1,8 @@
|
||||
masterUrl = "http://10.0.3.41:8000"
|
||||
pullInterval = 10 # in sec
|
||||
apiKey = "7b105947-65d6-40ba-bb4c-50b95a3ec1c8"
|
||||
station = {
|
||||
"lat": 49.2397383,
|
||||
"lon": 16.5684175,
|
||||
"alt": 0.277 #KM
|
||||
}
|
36
station/main.py
Normal file
36
station/main.py
Normal file
@ -0,0 +1,36 @@
|
||||
import config
|
||||
import puller
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from recorder import recorder
|
||||
|
||||
def onRecorded(info):
|
||||
pass
|
||||
|
||||
while True:
|
||||
try:
|
||||
puller.pull()
|
||||
|
||||
for job in puller.watingJobs:
|
||||
print(f"Job {job['target']['name']} starts at {job['start']}")
|
||||
|
||||
if job["start"] <= datetime.utcnow() + timedelta(seconds=60):
|
||||
if job["end"] <= datetime.utcnow():
|
||||
puller.setFail(job["id"])
|
||||
puller.watingJobs.remove(job)
|
||||
|
||||
# start record
|
||||
puller.setRecording(job["id"])
|
||||
|
||||
curRecorder = recorder(job)
|
||||
curRecorder.start()
|
||||
|
||||
puller.watingJobs.remove(job)
|
||||
|
||||
except:
|
||||
print("[ERROR] main script fail restarting")
|
||||
|
||||
|
||||
|
||||
|
||||
time.sleep(config.pullInterval)
|
70
station/puller.py
Normal file
70
station/puller.py
Normal file
@ -0,0 +1,70 @@
|
||||
import config
|
||||
from datetime import datetime
|
||||
from urllib.request import urlopen
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
|
||||
import pathlib
|
||||
|
||||
watingJobs = []
|
||||
|
||||
def getNewJobs():
|
||||
response = urlopen(config.masterUrl + "/api/observation/record?key=" + config.apiKey)
|
||||
data_json = json.loads(response.read())
|
||||
return data_json
|
||||
|
||||
def apiSend(url, data, files=None):
|
||||
r = requests.post(url=config.masterUrl + url, data=data, files=files)
|
||||
return r.text
|
||||
|
||||
def setFail(observation):
|
||||
apiSend("/api/observation/fail", {"id": observation})
|
||||
|
||||
def setAssigned(observation):
|
||||
apiSend("/api/observation/assigned", {"id": observation})
|
||||
|
||||
def setRecording(observation):
|
||||
apiSend("/api/observation/recording", {"id": observation})
|
||||
|
||||
def setRecorded(observation):
|
||||
apiSend("/api/observation/recorded", {"id": observation})
|
||||
|
||||
def setDecoding(observation):
|
||||
apiSend("/api/observation/decoding", {"id": observation})
|
||||
|
||||
def setSuccess(observation):
|
||||
apiSend("/api/observation/success", {"id": observation})
|
||||
|
||||
def setArtefacts(adir, observation):
|
||||
ufiles = {} # open('file.txt','rb')
|
||||
|
||||
print("Uploading artefacts")
|
||||
|
||||
for path, subdirs, files in os.walk(adir):
|
||||
for name in files:
|
||||
afile = os.path.join(path, name)
|
||||
fileName = str(afile).replace(str(adir), "").replace("/", "\\")
|
||||
print(fileName)
|
||||
ufiles[fileName] = open(afile, 'rb')
|
||||
|
||||
|
||||
apiSend("/api/observation/addArtefacts", {"id": observation}, ufiles)
|
||||
|
||||
|
||||
def parseNewJobs(jobs):
|
||||
for job in jobs:
|
||||
job["start"] = datetime.strptime(job["start"], '%Y-%m-%d %H:%M:%S')
|
||||
job["end"] = datetime.strptime(job["end"], '%Y-%m-%d %H:%M:%S')
|
||||
|
||||
if job["start"] < datetime.utcnow():
|
||||
setFail(job["id"])
|
||||
continue
|
||||
|
||||
setAssigned(job["id"])
|
||||
|
||||
watingJobs.append(job)
|
||||
|
||||
def pull():
|
||||
jobs = getNewJobs()
|
||||
parseNewJobs(jobs)
|
88
station/recorder.py
Normal file
88
station/recorder.py
Normal file
@ -0,0 +1,88 @@
|
||||
import os
|
||||
import puller
|
||||
import threading
|
||||
from rotator import rotator
|
||||
from simplecom import simplecom
|
||||
from pathlib import Path
|
||||
import config
|
||||
import time
|
||||
|
||||
# A recursive function to remove the folder
|
||||
def del_folder(path):
|
||||
for sub in path.iterdir():
|
||||
if sub.is_dir():
|
||||
# Delete directory if it's a subdirectory
|
||||
del_folder(sub)
|
||||
else :
|
||||
# Delete file if it is a file:
|
||||
sub.unlink()
|
||||
|
||||
# This removes the top-level folder:
|
||||
path.rmdir()
|
||||
|
||||
class recorder(threading.Thread):
|
||||
def __init__(self, job):
|
||||
threading.Thread.__init__(self)
|
||||
self.job = job
|
||||
|
||||
def run(self):
|
||||
print(f"Recorder for job {self.job['target']['name']} started")
|
||||
|
||||
recordTime = (self.job["end"] - self.job["start"]).total_seconds()
|
||||
|
||||
#init rotator
|
||||
rotatorDriver = simplecom("/dev/ttyUSB0")
|
||||
rotatorCTR = rotator(rotatorDriver, self.job, config.station)
|
||||
rotatorCTR.start()
|
||||
|
||||
baseband = f"records/{self.job['id']}"
|
||||
|
||||
fs = max(self.job["receiver"]["params"]["fs"])
|
||||
|
||||
# find supported FS
|
||||
for sample in self.job["receiver"]["params"]["fs"]:
|
||||
if (sample > (int(self.job['transmitter']['bandwidth']) * 2)) and (sample < fs):
|
||||
fs = sample
|
||||
|
||||
time.sleep(50)
|
||||
|
||||
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}")
|
||||
|
||||
|
||||
print(f"Recorder for job {self.job['target']['name']} stoped")
|
||||
|
||||
puller.setRecorded(self.job["id"])
|
||||
rotatorCTR.kill()
|
||||
|
||||
if self.job["proccessPipe"] == []:
|
||||
return
|
||||
|
||||
puller.setDecoding(self.job["id"])
|
||||
|
||||
pipe = " && ".join(self.job["proccessPipe"])
|
||||
|
||||
#create artecats dir
|
||||
adir = f"artefacts/{self.job['id']}"
|
||||
os.makedirs(adir)
|
||||
|
||||
#ok now replace
|
||||
pipe = pipe.replace("{baseband}", str(baseband) + ".s8").replace("{fs}", str(fs)).replace("{artefactDir}", str(adir))
|
||||
|
||||
os.system(pipe)
|
||||
|
||||
puller.setSuccess(self.job["id"])
|
||||
|
||||
puller.setArtefacts(adir, self.job["id"])
|
||||
|
||||
# remove basband record
|
||||
os.remove(str(baseband) + ".s8")
|
||||
|
||||
# remove artefacts
|
||||
path = Path(adir)
|
||||
try:
|
||||
del_folder(path)
|
||||
print("Directory removed successfully")
|
||||
except OSError as o:
|
||||
print(f"Error, {o.strerror}: {path}")
|
||||
|
||||
|
49
station/rotator.py
Normal file
49
station/rotator.py
Normal file
@ -0,0 +1,49 @@
|
||||
import threading
|
||||
from pyorbital.orbital import Orbital
|
||||
from datetime import datetime, timedelta
|
||||
import time
|
||||
|
||||
class rotator(threading.Thread):
|
||||
def __init__(self, driver, job, station):
|
||||
threading.Thread.__init__(self)
|
||||
self.driver = driver
|
||||
self.job = job
|
||||
self.station = station
|
||||
self.killed = False
|
||||
|
||||
def run(self):
|
||||
print("[INFO] Starting rotator service")
|
||||
|
||||
self.driver.reset()
|
||||
time.sleep(30)
|
||||
|
||||
#init pyorbytal
|
||||
orb = Orbital(self.job["target"]["name"], line1=self.job["target"]["locator"]["tle"]["line1"], line2=self.job["target"]["locator"]["tle"]["line2"])
|
||||
|
||||
while (True):
|
||||
az, el = orb.get_observer_look(
|
||||
utc_time=datetime.utcnow() + timedelta(seconds=5),
|
||||
lon=self.station["lon"],
|
||||
lat=self.station["lat"],
|
||||
alt=self.station["alt"]
|
||||
)
|
||||
az, el = round(az), round(el)
|
||||
|
||||
print(f"[INFO] rotator az: {az}, el: {el}")
|
||||
|
||||
self.driver.set_azel(az, el)
|
||||
|
||||
if (self.killed):
|
||||
break
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
# home the rotator on end
|
||||
self.driver.reset()
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
|
||||
def kill(self):
|
||||
self.killed = True
|
43
station/simplecom.py
Normal file
43
station/simplecom.py
Normal file
@ -0,0 +1,43 @@
|
||||
import serial
|
||||
import time
|
||||
|
||||
class simplecom(object):
|
||||
def __init__(self, port):
|
||||
self.port = port
|
||||
self.serial = serial.Serial(self.port, 9600, timeout=60)
|
||||
|
||||
def send(self, cmd):
|
||||
try:
|
||||
self.serial.write(cmd.encode("ASCII"))
|
||||
self.serial.flush()
|
||||
except:
|
||||
print("[ERROR] fail to write to serial")
|
||||
|
||||
def reset(self):
|
||||
self.send("RESET\n")
|
||||
|
||||
def set_azel(self, az, el):
|
||||
self.set_az(az)
|
||||
self.set_el(el)
|
||||
|
||||
def set_az(self, az):
|
||||
while (az < 0):
|
||||
az += 360
|
||||
|
||||
az = round(az % 360)
|
||||
|
||||
self.send(f"AZ{az}\n")
|
||||
#readout target
|
||||
self.send(f"TAR\n")
|
||||
|
||||
def set_el(self, el):
|
||||
if (el < 0):
|
||||
el = 0
|
||||
elif (el > 90):
|
||||
el = 90
|
||||
|
||||
el = round(el)
|
||||
|
||||
self.send(f"EL{el}\n")
|
||||
#readout target
|
||||
self.send(f"TAR\n")
|
2
uploads.ini
Normal file
2
uploads.ini
Normal file
@ -0,0 +1,2 @@
|
||||
upload_max_filesize = 10G
|
||||
post_max_size = 10G
|
17
web/API/main.php
Normal file
17
web/API/main.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
$container = new \wsos\structs\container();
|
||||
$api = new \wsos\api\functional\manager("API");
|
||||
|
||||
$templates = $container->get("templateLoader");
|
||||
$context = $container->get("context");
|
||||
$auth = $container->get("auth");
|
||||
$router = $container->get("router");
|
||||
|
||||
// to show this page user must be logined
|
||||
//$auth->requireLogin();
|
||||
|
||||
//register API functions
|
||||
include_once(__DIR__ . "/observations.php");
|
||||
|
||||
//init API
|
||||
$api->serve($router->getArgs());
|
145
web/API/observations.php
Normal file
145
web/API/observations.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace API\observation;
|
||||
|
||||
function plan($params) {
|
||||
|
||||
//first get trasmitter and receiver
|
||||
$receiver = new \DAL\receiver(new \wsos\database\types\uuid($params["receiver"]));
|
||||
$transmitter = new \DAL\transmitter(new \wsos\database\types\uuid($params["transmitter"]));
|
||||
|
||||
$receiver->fetch();
|
||||
$transmitter->fetch();
|
||||
|
||||
$plan = new \DAL\observation();
|
||||
$plan->status ->set("planed");
|
||||
$plan->locator ->set($transmitter->target->get()->locator->get());
|
||||
$plan->transmitter->set($transmitter);
|
||||
$plan->receiver ->set($receiver);
|
||||
$plan->start ->set($params["start"]);
|
||||
$plan->end ->set($params["end"]);
|
||||
|
||||
if ($plan->start >= $plan->end) return ["status" => false];
|
||||
|
||||
$plan->commit();
|
||||
|
||||
return ["status" => true, "id" => $plan->id->get()];
|
||||
}
|
||||
|
||||
function record($params) {
|
||||
//get GS and set last seen
|
||||
$station = new \DAL\station();
|
||||
if (!$station->find("apiKey", $params["key"])) return ["status" => "bad api key"];
|
||||
|
||||
$station->lastSeen->now();
|
||||
$station->commit();
|
||||
|
||||
//get all jobs for ground station
|
||||
$table = new \wsos\database\core\table(\DAL\observation::class);
|
||||
|
||||
$dummyObservation = new \DAL\observation();
|
||||
$dummyObservation->status->set("planed");
|
||||
//$dummyObservation->station->set($params["station"]);
|
||||
|
||||
$planed = $table->query("(status == ?) && (receiver.station.id == ?)", [$dummyObservation->status->value, $station->id->get()]);
|
||||
|
||||
$jobs = new \wsos\structs\vector();
|
||||
foreach ($planed->values as $plan) {
|
||||
$jobs->append([
|
||||
"id" => $plan->id->get(),
|
||||
"target" => [
|
||||
"id" => $plan->transmitter->get()->target->get()->id->get(),
|
||||
"name" => $plan->transmitter->get()->target->get()->name->get(),
|
||||
"locator" => $plan->locator->get()
|
||||
],
|
||||
|
||||
"transmitter" => [
|
||||
"centerFrequency" => $plan->transmitter->get()->centerFrequency->get(),
|
||||
"bandwidth" => $plan->transmitter->get()->bandwidth->get()
|
||||
],
|
||||
|
||||
"receiver" => [
|
||||
"params" => $plan->receiver->get()->params->get()
|
||||
],
|
||||
|
||||
"proccessPipe" => $plan->transmitter->get()->processPipe->get()->pipe->get(),
|
||||
|
||||
"start" => $plan->start->get(),
|
||||
"end" => $plan->end->get()
|
||||
]);
|
||||
}
|
||||
|
||||
return $jobs->values;
|
||||
}
|
||||
|
||||
function fail($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("fail");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function assigned($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("assigned");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function recording($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("recording");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function recorded($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("recorded");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function decoding($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("decoding");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function success($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$obs->status->set("success");
|
||||
$obs->commit();
|
||||
}
|
||||
|
||||
function addArtefacts($params) {
|
||||
$obs = new \DAL\observation();
|
||||
$obs->id->set($params["id"]);
|
||||
$obs->fetch();
|
||||
|
||||
$adir = __DIR__ . "/../ARTEFACTS/" . $params["id"];
|
||||
|
||||
mkdir($adir, 0777, true);
|
||||
|
||||
$artefacts = $obs->artefacts->get();
|
||||
foreach ($_FILES as $file) {
|
||||
move_uploaded_file($file["tmp_name"], $adir . "/" . $file["name"]);
|
||||
$artefacts[] = "/ARTEFACTS/{$params['id']}/{$file['name']}";
|
||||
}
|
||||
|
||||
$obs->artefacts->set($artefacts);
|
||||
$obs->commit();
|
||||
}
|
0
web/ARTEFACTS/.gitkeep
Normal file
0
web/ARTEFACTS/.gitkeep
Normal file
@ -8,26 +8,98 @@
|
||||
// to show this page user must be logined
|
||||
$auth->requireLogin();
|
||||
|
||||
$context["successCount"] = 75;
|
||||
$context["planedCount"] = 15;
|
||||
$context["lastPlaned"] = 2;
|
||||
$context["failCount"] = 5;
|
||||
$context["observationsCount"] = 5;
|
||||
|
||||
// create planed template observations
|
||||
$planed = new \DAL\observation();
|
||||
$planed->status->set("planed");
|
||||
$ob = new \DAL\observation();
|
||||
|
||||
/**
|
||||
* Get the directory size
|
||||
* @param string $directory
|
||||
* @return integer
|
||||
*/
|
||||
function foldersize($path, $extension = null) {
|
||||
$total_size = 0;
|
||||
$files = scandir($path);
|
||||
$cleanPath = rtrim($path, '/'). '/';
|
||||
|
||||
foreach($files as $t) {
|
||||
if ($t <> "." && $t <> "..") {
|
||||
$currentFile = $cleanPath . $t;
|
||||
if (is_dir($currentFile)) {
|
||||
$size = foldersize($currentFile, $extension);
|
||||
$total_size += $size;
|
||||
} else {
|
||||
if (is_null($extension) || (strtolower(pathinfo($currentFile)['extension']) == $extension)) {
|
||||
$size = filesize($currentFile);
|
||||
$total_size += $size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $total_size;
|
||||
}
|
||||
|
||||
$observationsTable = new \wsos\database\core\table(\DAL\observation::class);
|
||||
$planedTable = $observationsTable->query("status=?", [$planed->status->value]);
|
||||
$maxSize = 8000; //8GB
|
||||
|
||||
$context["artefactsSpace"] = $maxSize;
|
||||
|
||||
/**
|
||||
* For carts
|
||||
*/
|
||||
$context["successCount"] = $observationsTable->count("status==?", [$ob->status->getVal("success")]);
|
||||
$context["planedCount"] = $observationsTable->count("status==?", [$ob->status->getVal("planed")]);
|
||||
$context["lastPlaned"] = $observationsTable->count("", []);
|
||||
$context["failCount"] = $observationsTable->count("status==?", [$ob->status->getVal("fail")]);
|
||||
$context["observationsCount"] = $observationsTable->count("", []);
|
||||
|
||||
/**
|
||||
* Get used size
|
||||
*/
|
||||
$context["usedSize"] = round(foldersize(__DIR__ . "/../artefacts/") / 1000000);
|
||||
$context["imagesSize"] = round(foldersize(__DIR__ . "/../artefacts/", "png") / 1000000);
|
||||
$context["basebandSize"] = round(foldersize(__DIR__ . "/../artefacts/", "s8") / 1000000);
|
||||
$context["otherSize"] = $context["usedSize"] - $context["imagesSize"] + $context["basebandSize"];
|
||||
$context["freeSize"] = $context["artefactsSpace"] - $context["usedSize"];
|
||||
|
||||
/**
|
||||
* Get observations
|
||||
*/
|
||||
$observationTable = new \wsos\database\core\table(\DAL\observation::class);
|
||||
$context["observations"] = $observationTable->query("", [], "DESC start", 10)->values;
|
||||
|
||||
/**
|
||||
* Get stattions
|
||||
*/
|
||||
$context["stations"] = [];
|
||||
$stations = (new \wsos\database\core\table(\DAL\station::class))->getAll()->values;
|
||||
foreach ($stations as $station) {
|
||||
$context["stations"][] = [
|
||||
"name" => $station->name->get(),
|
||||
"observations" => $observationTable->count("receiver.station.id == ?", [$station->id->get()]),
|
||||
"lastSeen" => $station->lastSeen->strDelta()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get planed observations
|
||||
*/
|
||||
$planedTable = $observationsTable->query("(status == ?) || (status == ?)", [$ob->status->getVal("assigned"), $ob->status->getVal("planed")]);
|
||||
|
||||
$observationsLocators = new \wsos\structs\vector();
|
||||
foreach($planedTable->values as $obs) {
|
||||
$observationsLocators->append($obs->locator->get());
|
||||
$locator = $obs->locator->get();
|
||||
$locator["start"] = $obs->start->get() . " UTC";
|
||||
$locator["end"] = $obs->end->get() . " UTC";
|
||||
|
||||
$observationsLocators->append($locator);
|
||||
}
|
||||
|
||||
$context["planedLocators"] = json_encode($observationsLocators->values);
|
||||
$context["planedObservations"] = json_encode($observationsLocators->values);
|
||||
|
||||
/**
|
||||
* Render view
|
||||
*/
|
||||
$templates->load("dashboard.html");
|
||||
$templates->render($context);
|
||||
$templates->show();
|
49
web/CONTROLLERS/observation.php
Normal file
49
web/CONTROLLERS/observation.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
$container = new \wsos\structs\container();
|
||||
|
||||
$templates = $container->get("templateLoader");
|
||||
$router = $container->get("router");
|
||||
$context = $container->get("context");
|
||||
$auth = $container->get("auth");
|
||||
|
||||
// to show this page user must be logined
|
||||
$auth->requireLogin();
|
||||
|
||||
//get observation ID
|
||||
$obId = $router->getArgs()[0];
|
||||
|
||||
//$obId = new \DAL\observation();
|
||||
//$obId->find("status", 4);
|
||||
//$obId = $obId->id;
|
||||
|
||||
//get correct observation
|
||||
$context["observation"] = new \DAL\observation(new \wsos\database\types\uuid($obId));
|
||||
$context["observation"]->fetch();
|
||||
|
||||
//generate artefacts
|
||||
$context["artefacts"] = new \wsos\structs\vector();
|
||||
|
||||
//get observations whats from same satellite and in 24h interval
|
||||
$context["observations"] = new \wsos\database\core\table(\DAL\observation::class);
|
||||
$context["observations"] = $context["observations"]->query(
|
||||
"(transmitter.target.id == ?) && (start > ?) && (end < ?)",
|
||||
[
|
||||
$context["observation"]->transmitter->get()->target->get()->id->get(),
|
||||
$context["observation"]->start->value - (60 * 60 * 12),
|
||||
$context["observation"]->end->value + (60 * 60 * 12)
|
||||
],
|
||||
"DESC start"
|
||||
)->values;
|
||||
|
||||
foreach ($context["observation"]->artefacts->get() as $art) {
|
||||
$context["artefacts"]->append([
|
||||
"name" => basename($art),
|
||||
"url" => $art
|
||||
]);
|
||||
}
|
||||
|
||||
$context["artefacts"] = $context["artefacts"]->values;
|
||||
|
||||
$templates->load("observation.html");
|
||||
$templates->render($context);
|
||||
$templates->show();
|
@ -8,7 +8,9 @@
|
||||
// to show this page user must be logined
|
||||
$auth->requireLogin();
|
||||
|
||||
$context["observations"] = new \wsos\database\core\table(\DAL\observation::class);
|
||||
$context["observations"] = (new \wsos\database\core\table(\DAL\observation::class))->query("", [], "DESC start")->values;
|
||||
$context["receivers"] = new \wsos\database\core\table(\DAL\receiver::class);
|
||||
$context["transmitters"] = new \wsos\database\core\table(\DAL\transmitter::class);
|
||||
|
||||
$templates->load("observations.html");
|
||||
$templates->render($context);
|
||||
|
@ -8,6 +8,8 @@
|
||||
public \wsos\database\types\text $record; // path to record
|
||||
public \wsos\database\types\json $artefacts; // JSON array of artefacts
|
||||
public \wsos\database\types\json $locator; // TLE, GPS or URL locator if avaible
|
||||
public \wsos\database\types\timestamp $start; // start datetime
|
||||
public \wsos\database\types\timestamp $end; // end datetimr
|
||||
|
||||
function __construct(
|
||||
$id = null,
|
||||
@ -16,7 +18,9 @@
|
||||
$status = "",
|
||||
$record = "",
|
||||
$artefacts = [],
|
||||
$locator = ["tle" => null, "gps" => null, "url" => null]
|
||||
$locator = ["tle" => null, "gps" => null, "url" => null],
|
||||
$start = "2000-01-01 00:00:00",
|
||||
$end = "2000-01-01 00:10:00"
|
||||
) {
|
||||
parent::__construct($id);
|
||||
$this->transmitter = new \wsos\database\types\reference($transmitter, \DAL\transmitter::class);
|
||||
@ -25,14 +29,18 @@
|
||||
"fail",
|
||||
"success",
|
||||
"recording",
|
||||
"recorded",
|
||||
"decoding",
|
||||
"planed",
|
||||
"assigned",
|
||||
"unknow"
|
||||
], "unknow");
|
||||
|
||||
$this->record = new \wsos\database\types\text($record);
|
||||
$this->artefacts = new \wsos\database\types\json($artefacts);
|
||||
$this->locator = new \wsos\database\types\json($locator);
|
||||
$this->start = new \wsos\database\types\timestamp($start);
|
||||
$this->end = new \wsos\database\types\timestamp($end);
|
||||
}
|
||||
}
|
||||
?>
|
@ -6,6 +6,7 @@
|
||||
public \wsos\database\types\reference $antenna; // YAGI, DISH, ....
|
||||
public \wsos\database\types\integer $centerFrequency; // in Hz
|
||||
public \wsos\database\types\integer $bandwidth; // in Hz
|
||||
public \wsos\database\types\json $params; // params for use
|
||||
public \wsos\database\types\integer $gain; // gain of reciver setup
|
||||
|
||||
function __construct(
|
||||
@ -14,6 +15,7 @@
|
||||
$antenna = null,
|
||||
$centerFrequency = 0,
|
||||
$bandwidth = 0,
|
||||
$params = [],
|
||||
$gain = 0
|
||||
) {
|
||||
parent::__construct($id);
|
||||
@ -22,6 +24,7 @@
|
||||
|
||||
$this->centerFrequency = new \wsos\database\types\integer($centerFrequency);
|
||||
$this->bandwidth = new \wsos\database\types\integer($bandwidth);
|
||||
$this->params = new \wsos\database\types\json($params);
|
||||
$this->gain = new \wsos\database\types\integer($gain);
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,18 @@
|
||||
namespace DAL;
|
||||
|
||||
class station extends \wsos\database\core\row {
|
||||
public \wsos\database\types\text $name; // Satellite, ...
|
||||
public \wsos\database\types\text $name; // Satellite, ...
|
||||
public \wsos\database\types\uuid $apiKey; // access key
|
||||
public \wsos\database\types\timestamp $lastSeen;
|
||||
public \wsos\database\types\text $description;
|
||||
public \wsos\database\types\json $locator;
|
||||
|
||||
function __construct($id = null, $name = "", $description = "", $locator = []) {
|
||||
function __construct($id = null, $name = "", $apiKey = null, $lastSeen = 0, $description = "", $locator = []) {
|
||||
parent::__construct($id);
|
||||
$this->name = new \wsos\database\types\text($name);
|
||||
$this->description = new \wsos\database\types\text($description);
|
||||
|
||||
$this->apiKey = new \wsos\database\types\uuid($apiKey);
|
||||
$this->lastSeen = new \wsos\database\types\timestamp($lastSeen);
|
||||
$this->locator = new \wsos\database\types\json($locator);
|
||||
}
|
||||
}
|
||||
|
0
web/DB/.gitkeep
Normal file
0
web/DB/.gitkeep
Normal file
@ -11,6 +11,8 @@
|
||||
{% IF item.status==recording USE bg-info %}
|
||||
{% IF item.status==decoding USE bg-warning %}
|
||||
{% IF item.status==planed USE bg-primary %}
|
||||
{% IF item.status==assigned USE bg-secondary %}
|
||||
{% IF item.status==recorded USE bg-light %}
|
||||
me-1"></span> {% BIND item.status %}
|
||||
</td>
|
||||
<td>{% BIND item.start %}</td>
|
||||
|
16
web/VIEWS/blocks/observation-item_short.html
Normal file
16
web/VIEWS/blocks/observation-item_short.html
Normal file
@ -0,0 +1,16 @@
|
||||
<tr onclick="location.href = '/observation/{% BIND item.id %}'">
|
||||
<td>{% BIND item.transmitter.modulation.name %}</td>
|
||||
<td>
|
||||
<span class="badge
|
||||
{% IF item.status==fail USE bg-danger %}
|
||||
{% IF item.status==success USE bg-success %}
|
||||
{% IF item.status==recording USE bg-info %}
|
||||
{% IF item.status==decoding USE bg-warning %}
|
||||
{% IF item.status==planed USE bg-primary %}
|
||||
{% IF item.status==assigned USE bg-secondary %}
|
||||
{% IF item.status==recorded USE bg-light %}
|
||||
me-1"></span> {% BIND item.status %}
|
||||
</td>
|
||||
<td>{% BIND item.start %}</td>
|
||||
<td>{% BIND item.end %}</td>
|
||||
</tr>
|
5
web/VIEWS/blocks/station-item.html
Normal file
5
web/VIEWS/blocks/station-item.html
Normal file
@ -0,0 +1,5 @@
|
||||
<tr>
|
||||
<td>{% BIND item.name %}</td>
|
||||
<td>{% BIND item.observations %}</td>
|
||||
<td>{% BIND item.lastSeen %} ago</td>
|
||||
</tr>
|
@ -24,65 +24,32 @@
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="row row-deck row-cards">
|
||||
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">Success</div>
|
||||
<div class="ms-auto lh-1">
|
||||
<span class="text-secondary">Last 7 days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h1 mb-3">{% BIND successCount %}</div>
|
||||
<div class="subheader">Success observations</div>
|
||||
<div class="h3 m-0">{% BIND successCount %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">Fail</div>
|
||||
<div class="ms-auto lh-1">
|
||||
<span class="text-secondary">Last 7 days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h1 mb-3">{% BIND failCount %}</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="subheader">Fail observations</div>
|
||||
<div class="h3 m-0">{% BIND failCount %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">Planed</div>
|
||||
<div class="ms-auto lh-1">
|
||||
<span class="text-secondary">For {% BIND lastPlaned %} days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h1 mb-3">{% BIND planedCount %}</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="subheader">All observations</div>
|
||||
<div class="h3 m-0">{% BIND observationsCount %}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">all</div>
|
||||
<div class="ms-auto lh-1">
|
||||
<span class="text-secondary">Last 7 days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h1 mb-3">{% BIND observationsCount %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
@ -90,32 +57,32 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="mb-3">Using Storage <strong>6854.45 MB </strong>of 8 GB</p>
|
||||
<p class="mb-3">Using Storage <strong>{% BIND usedSize %} MB </strong>of {% BIND artefactsSpace %} MB</p>
|
||||
<div class="progress progress-separated mb-3">
|
||||
<div class="progress-bar bg-primary" role="progressbar" style="width: 44%" aria-label="Regular"></div>
|
||||
<div class="progress-bar bg-info" role="progressbar" style="width: 19%" aria-label="System"></div>
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 9%" aria-label="Shared"></div>
|
||||
<div class="progress-bar bg-primary" role="progressbar" style="width: {% MUL 100 DIV BIND imagesSize BIND artefactsSpace %}%" aria-label="Images"></div>
|
||||
<div class="progress-bar bg-info" role="progressbar" style="width: {% MUL 100 DIV BIND basebandSize BIND artefactsSpace %}%" aria-label="Baseband"></div>
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {% MUL 100 DIV BIND otherSize BIND artefactsSpace %}%" aria-label="Other"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-auto d-flex align-items-center pe-2">
|
||||
<span class="legend me-2 bg-primary"></span>
|
||||
<span>Regular</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">915MB</span>
|
||||
<span>Images</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">{% BIND imagesSize %}MB</span>
|
||||
</div>
|
||||
<div class="col-auto d-flex align-items-center px-2">
|
||||
<span class="legend me-2 bg-info"></span>
|
||||
<span>System</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">415MB</span>
|
||||
<span>Baseband</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">{% BIND basebandSize %}MB</span>
|
||||
</div>
|
||||
<div class="col-auto d-flex align-items-center px-2">
|
||||
<span class="legend me-2 bg-success"></span>
|
||||
<span>Shared</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">201MB</span>
|
||||
<span>Other</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">{% BIND otherSize %}MB</span>
|
||||
</div>
|
||||
<div class="col-auto d-flex align-items-center ps-2">
|
||||
<span class="legend me-2"></span>
|
||||
<span>Free</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">612MB</span>
|
||||
<span class="d-none d-md-inline d-lg-none d-xxl-inline ms-2 text-secondary">{% BIND freeSize %}MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -127,50 +94,35 @@
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0">
|
||||
<div class="card-title">Planed targets map</div>
|
||||
<div class="card-title">Assigned targets map</div>
|
||||
</div>
|
||||
<div class="position-relative">
|
||||
<div id="map" style="height:400px;" class="position-absolute top-0 left-0 px-3 mt-1 w-100">
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/js/map.js"></script>
|
||||
<script>
|
||||
var map = L.map('map').setView([0, 0], 1);
|
||||
var tragetIcon = L.icon({
|
||||
iconUrl: '/static/icons/satellite.svg',
|
||||
iconSize: [20, 20],
|
||||
shadowSize: [0, 0],
|
||||
iconAnchor: [10, 10],
|
||||
shadowAnchor: [10, 0],
|
||||
popupAnchor: [0, 0]
|
||||
});
|
||||
var map = L.map('map', {
|
||||
worldCopyJump: false
|
||||
}).setView([0, 0], 1);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
var planedObs = JSON.parse('{% BIND planedObservations %}');
|
||||
|
||||
var planedLocators = JSON.parse('{% BIND planedLocators %}'.replaceAll("\n", "\\\\n"));
|
||||
var targetsMarkers = [];
|
||||
|
||||
for (var i = 0; i < planedLocators.length; i++) {
|
||||
targetsMarkers.push(L.marker(map.getCenter(), {icon: tragetIcon}).bindPopup(planedLocators[i]["tle"].split("\\n")[0]).addTo(map));
|
||||
}
|
||||
|
||||
setInterval(function () {
|
||||
for (var i = 0; i < planedLocators.length; i++) {
|
||||
var planedLocator = planedLocators[i]["tle"].split("\\n");
|
||||
|
||||
var satrec = satellite.twoline2satrec(planedLocator[1], planedLocator[2]);
|
||||
|
||||
var positionAndVelocity = satellite.propagate(satrec, new Date());
|
||||
var gmst = satellite.gstime(new Date());
|
||||
|
||||
var positionEci = positionAndVelocity.position;
|
||||
var positionGd = satellite.eciToGeodetic(positionEci, gmst);
|
||||
|
||||
targetsMarkers[i].setLatLng([satellite.degreesLat(positionGd.latitude), satellite.degreesLong(positionGd.longitude)]);
|
||||
}
|
||||
}, 1000);
|
||||
for (var i = 0; i < planedObs.length; i++) {
|
||||
mapAddTLEPath(
|
||||
map,
|
||||
planedObs[i]["tle"]["line1"],
|
||||
planedObs[i]["tle"]["line2"],
|
||||
planedObs[i]["start"],
|
||||
planedObs[i]["end"]
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
@ -178,409 +130,52 @@
|
||||
|
||||
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Social Media Traffic</h3>
|
||||
<h3 class="card-title">Stations</h3>
|
||||
</div>
|
||||
<div class="scrollable" style="height: 274px;">
|
||||
<table class="table card-table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Observations</th>
|
||||
<th>Last seen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% FOREACH stations RENDER blocks/station-item.html %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table class="table card-table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
<th colspan="2">Visitors</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Instagram</td>
|
||||
<td>3,550</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 71.0%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Twitter</td>
|
||||
<td>1,798</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 35.96%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Facebook</td>
|
||||
<td>1,245</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 24.9%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TikTok</td>
|
||||
<td>986</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 19.72%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pinterest</td>
|
||||
<td>854</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 17.08%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>VK</td>
|
||||
<td>650</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 13.0%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pinterest</td>
|
||||
<td>420</td>
|
||||
<td class="w-50">
|
||||
<div class="progress progress-xs">
|
||||
<div class="progress-bar bg-primary" style="width: 8.4%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Invoices</h3>
|
||||
<h3 class="card-title">Last observations</h3>
|
||||
</div>
|
||||
<div class="card-body border-bottom py-3">
|
||||
<div class="d-flex">
|
||||
<div class="text-secondary">
|
||||
Show
|
||||
<div class="mx-2 d-inline-block">
|
||||
<input type="text" class="form-control form-control-sm" value="8" size="3" aria-label="Invoices count">
|
||||
</div>
|
||||
entries
|
||||
</div>
|
||||
<div class="ms-auto text-secondary">
|
||||
Search:
|
||||
<div class="ms-2 d-inline-block">
|
||||
<input type="text" class="form-control form-control-sm" aria-label="Search invoice">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table card-table table-vcenter text-nowrap datatable">
|
||||
<div class="table-body">
|
||||
<table class="table card-table table-vcenter text-nowrap datatable table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1"><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select all invoices"></th>
|
||||
<th class="w-1">No. <!-- Download SVG icon from http://tabler-icons.io/i/chevron-up -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-sm icon-thick" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M6 15l6 -6l6 6"></path></svg>
|
||||
</th>
|
||||
<th>Invoice Subject</th>
|
||||
<th>Client</th>
|
||||
<th>VAT No.</th>
|
||||
<th>Created</th>
|
||||
<th>Station</th>
|
||||
<th>Target</th>
|
||||
<th>Modulation</th>
|
||||
<th>Type</th>
|
||||
<th>Frequency</th>
|
||||
<th>Status</th>
|
||||
<th>Price</th>
|
||||
<th></th>
|
||||
<th>Start</th>
|
||||
<th>End</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001401</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Design Works</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-us me-2"></span>
|
||||
Carlson Limited
|
||||
</td>
|
||||
<td>
|
||||
87956621
|
||||
</td>
|
||||
<td>
|
||||
15 Dec 2017
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-success me-1"></span> Paid
|
||||
</td>
|
||||
<td>$887</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001402</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">UX Wireframes</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-gb me-2"></span>
|
||||
Adobe
|
||||
</td>
|
||||
<td>
|
||||
87956421
|
||||
</td>
|
||||
<td>
|
||||
12 Apr 2017
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-warning me-1"></span> Pending
|
||||
</td>
|
||||
<td>$1200</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001403</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">New Dashboard</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-de me-2"></span>
|
||||
Bluewolf
|
||||
</td>
|
||||
<td>
|
||||
87952621
|
||||
</td>
|
||||
<td>
|
||||
23 Oct 2017
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-warning me-1"></span> Pending
|
||||
</td>
|
||||
<td>$534</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001404</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Landing Page</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-br me-2"></span>
|
||||
Salesforce
|
||||
</td>
|
||||
<td>
|
||||
87953421
|
||||
</td>
|
||||
<td>
|
||||
2 Sep 2017
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary me-1"></span> Due in 2 Weeks
|
||||
</td>
|
||||
<td>$1500</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001405</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Marketing Templates</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-pl me-2"></span>
|
||||
Printic
|
||||
</td>
|
||||
<td>
|
||||
87956621
|
||||
</td>
|
||||
<td>
|
||||
29 Jan 2018
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-danger me-1"></span> Paid Today
|
||||
</td>
|
||||
<td>$648</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001406</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Sales Presentation</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-br me-2"></span>
|
||||
Tabdaq
|
||||
</td>
|
||||
<td>
|
||||
87956621
|
||||
</td>
|
||||
<td>
|
||||
4 Feb 2018
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary me-1"></span> Due in 3 Weeks
|
||||
</td>
|
||||
<td>$300</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001407</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Logo & Print</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-us me-2"></span>
|
||||
Apple
|
||||
</td>
|
||||
<td>
|
||||
87956621
|
||||
</td>
|
||||
<td>
|
||||
22 Mar 2018
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-success me-1"></span> Paid Today
|
||||
</td>
|
||||
<td>$2500</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="form-check-input m-0 align-middle" type="checkbox" aria-label="Select invoice"></td>
|
||||
<td><span class="text-secondary">001408</span></td>
|
||||
<td><a href="invoice.html" class="text-reset" tabindex="-1">Icons</a></td>
|
||||
<td>
|
||||
<span class="flag flag-xs flag-country-pl me-2"></span>
|
||||
Tookapic
|
||||
</td>
|
||||
<td>
|
||||
87956621
|
||||
</td>
|
||||
<td>
|
||||
13 May 2018
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-success me-1"></span> Paid Today
|
||||
</td>
|
||||
<td>$940</td>
|
||||
<td class="text-end">
|
||||
<span class="dropdown">
|
||||
<button class="btn dropdown-toggle align-text-top" data-bs-boundary="viewport" data-bs-toggle="dropdown">Actions</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">
|
||||
Action
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
Another action
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% FOREACH observations RENDER blocks/observation-item.html %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer d-flex align-items-center">
|
||||
<p class="m-0 text-secondary">Showing <span>1</span> to <span>8</span> of <span>16</span> entries</p>
|
||||
<ul class="pagination m-0 ms-auto">
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" tabindex="-1" aria-disabled="true">
|
||||
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-left -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M15 6l-6 6l6 6"></path></svg>
|
||||
prev
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item"><a class="page-link" href="#">1</a></li>
|
||||
<li class="page-item active"><a class="page-link" href="#">2</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">4</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">5</a></li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="#">
|
||||
next <!-- Download SVG icon from http://tabler-icons.io/i/chevron-right -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M9 6l6 6l-6 6"></path></svg>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
|
||||
<title>{% BIND pagename %} - RadioMaster</title>
|
||||
<title>{% BIND pagename %} - {% BIND appName %}</title>
|
||||
|
||||
<!-- CSS files tabler -->
|
||||
<link href="./dist/css/tabler.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="./dist/css/tabler-flags.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="./dist/css/tabler-payments.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="./dist/css/tabler-vendors.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="/dist/css/tabler.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="/dist/css/tabler-flags.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="/dist/css/tabler-payments.min.css?1668287865" rel="stylesheet"/>
|
||||
<link href="/dist/css/tabler-vendors.min.css?1668287865" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
@import url('https://rsms.me/inter/inter.css');
|
||||
|
@ -1,8 +1,8 @@
|
||||
<header class="navbar navbar-expand-md navbar-light d-print-none">
|
||||
<div class="container-xl">
|
||||
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
|
||||
<a href=".">
|
||||
<img src="{% BIND logo %}" width="110" height="32" alt="Tabler" class="navbar-brand-image">
|
||||
<a href="/">
|
||||
<img src="/static/icons/antenna.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image"> {% BIND appName %}
|
||||
</a>
|
||||
</h1>
|
||||
<div class="navbar-nav flex-row order-md-last">
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div class="container container-tight py-4">
|
||||
<div class="text-center mb-4">
|
||||
<a href="." class="navbar-brand navbar-brand-autodark">
|
||||
<img src="./static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image">
|
||||
<img src="/static/icons/antenna.svg" width="110" height="32" alt="{% BIND appName %}" class="navbar-brand-image">
|
||||
</a>
|
||||
</div>
|
||||
<div class="card card-md">
|
||||
|
258
web/VIEWS/observation.html
Normal file
258
web/VIEWS/observation.html
Normal file
@ -0,0 +1,258 @@
|
||||
<!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">
|
||||
<div class="page-pretitle">
|
||||
{% BIND observation.id %}
|
||||
</div>
|
||||
<h2 class="page-title">
|
||||
Observation of {% BIND observation.transmitter.target.name %}
|
||||
</h2>
|
||||
</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="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Base info</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="datagrid">
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Target</div>
|
||||
<div class="datagrid-content"><a href="/target/{% BIND observation.transmitter.target.id %}">
|
||||
{% BIND observation.transmitter.target.name %}
|
||||
</a></div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Station</div>
|
||||
<div class="datagrid-content"><a href="/station/{% BIND observation.receiver.station.id %}">
|
||||
{% BIND observation.receiver.station.name %}
|
||||
</a></div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">MODULATION</div>
|
||||
<div class="datagrid-content"><a
|
||||
href="/modulation/{% BIND observation.transmitter.modulation.id %}">
|
||||
{% BIND observation.transmitter.modulation.name %}
|
||||
</a></div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">TYPE</div>
|
||||
<div class="datagrid-content"><a
|
||||
href="/datatype/{% BIND observation.transmitter.dataType.id %}">
|
||||
{% BIND observation.transmitter.dataType.name %}
|
||||
</a></div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Frequency</div>
|
||||
<div class="datagrid-content">{% BIND observation.transmitter.centerFrequency %}Hz</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Bandwidth</div>
|
||||
<div class="datagrid-content">{% BIND observation.transmitter.bandwidth %}Hz</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Status</div>
|
||||
<div class="datagrid-content">
|
||||
<span class="status
|
||||
{% IF observation.status==fail USE status-red %}
|
||||
{% IF observation.status==success USE status-green %}
|
||||
{% IF observation.status==recording USE status-white %}
|
||||
{% IF observation.status==decoding USE status-yelow %}
|
||||
{% IF observation.status==planed USE status-blue %}
|
||||
{% IF observation.status==assigned USE status-grey %}
|
||||
{% IF observation.status==recorded USE status-white %}
|
||||
">
|
||||
{% BIND observation.status %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Start UTC Time</div>
|
||||
<div class="datagrid-content"> -> {% BIND observation.start %}</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">End UTC Time</div>
|
||||
<div class="datagrid-content"> <- {% BIND observation.end %}</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Transmitter antenna</div>
|
||||
<div class="datagrid-content">{% BIND observation.transmitter.antenna.name %}</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Receiver antenna</div>
|
||||
<div class="datagrid-content">{% BIND observation.receiver.antenna.name %}</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Receiver gain</div>
|
||||
<div class="datagrid-content">{% BIND observation.receiver.gain %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Another observation of target</h3>
|
||||
</div>
|
||||
<div class="table-responsive scrollable" style="height: 400px">
|
||||
<table class="table card-table table-vcenter text-nowrap datatable table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Modulation</th>
|
||||
<th>Status</th>
|
||||
<th>Start</th>
|
||||
<th>End</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% FOREACH observations RENDER blocks/observation-item_short.html %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0">
|
||||
<div class="card-title">Target record map</div>
|
||||
</div>
|
||||
<div class="position-relative">
|
||||
<div id="map" style="height:400px;" tabindex="0">
|
||||
<script src="/static/js/map.js"></script>
|
||||
<script>
|
||||
var map = L.map('map').setView([0, 0], 1);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
|
||||
mapAddTLEPath(
|
||||
map,
|
||||
"{% BIND observation.transmitter.target.locator.tle.line1 %}",
|
||||
"{% BIND observation.transmitter.target.locator.tle.line2 %}",
|
||||
"{% BIND observation.start %} UTC",
|
||||
"{% BIND observation.end %} UTC"
|
||||
);
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="row row-cards">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="row g-0">
|
||||
<div class="col-12 col-md-3 border-end">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Artefacts</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group list-group-transparent scrollable" style="height: 35rem">
|
||||
{% FOREACH artefacts USE '<a value="(\ BIND item.url \)" onclick="art(this)" class="list-group-item list-group-item-action d-flex align-items-center artefact">(\ BIND item.name \)</a>' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-9 d-flex flex-column">
|
||||
<div class="card-body">
|
||||
<h2 class="mb-4" id="artefact-title"></h2>
|
||||
<div id="artefact-body" class="row align-items-center scrollable" style="height: 35rem">
|
||||
<div class="empty">
|
||||
<div class="empty-img"><img src="/static/icons/undraw_printing_invoices_5r4r.svg" height="128" alt="">
|
||||
</div>
|
||||
<p class="empty-title">No artefacts found</p>
|
||||
<p class="empty-subtitle text-secondary">
|
||||
Try wait until observation is decoded.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabler Core -->
|
||||
<script src="/dist/js/tabler.min.js?1668287865" defer=""></script>
|
||||
<script>
|
||||
function extension(val) {
|
||||
var parts = val.split(".");
|
||||
return parts[parts.length - 1].toLowerCase();
|
||||
}
|
||||
|
||||
function art(el) {
|
||||
var url = el.getAttribute("value");
|
||||
var name = el.innerHTML;
|
||||
|
||||
document.getElementById("artefact-title").innerHTML = name;
|
||||
|
||||
if (extension(name) == "png" || extension(name) == "jpg" ) {
|
||||
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);
|
||||
request.send();
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
document.getElementById("artefact-body") .innerHTML = "<pre>" + request.responseText + "</pre>";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document.getElementById("artefact-body") .innerHTML = "Binnary file";
|
||||
}
|
||||
|
||||
var items = document.getElementsByClassName("artefact");
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
items[i].classList.remove("active");
|
||||
}
|
||||
|
||||
el.classList.add("active");
|
||||
}
|
||||
|
||||
var allArtefacts = document.getElementsByClassName("artefact");
|
||||
|
||||
if (allArtefacts.length > 0) {
|
||||
art(allArtefacts[0]);
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -17,6 +17,15 @@
|
||||
Observations
|
||||
</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">
|
||||
Plan observation
|
||||
</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>
|
||||
@ -56,8 +65,66 @@
|
||||
</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 observation</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="/dist/js/tabler.min.js?1668287865" defer=""></script>
|
||||
|
||||
<script src="/static/js/observations.js"></script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
$container = new \wsos\structs\container();
|
||||
$db = new \wsos\database\drivers\inAppArray();
|
||||
$db = new \wsos\database\drivers\csv(__DIR__ . "/DB");
|
||||
$auth = new \wsos\auth\basic\manager(DAL\user::class, "userName", "pass", "/login");
|
||||
|
||||
//get current url
|
||||
@ -15,11 +15,15 @@
|
||||
$sites = [
|
||||
"sites" => [
|
||||
"observations" => ["controller" => __DIR__ . "/CONTROLLERS/observations.php", "name" => "Observations", "icon" => "/static/icons/telescope.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],
|
||||
"observation" => ["controller" => __DIR__ . "/CONTROLLERS/observation.php", "name" => "Observation view", "menu" => false]
|
||||
*/
|
||||
"observation" => ["controller" => __DIR__ . "/CONTROLLERS/observation.php", "name" => "Observation view", "menu" => false],
|
||||
"login" => ["controller" => __DIR__ . "/CONTROLLERS/login.php", "name" => "Login", "menu" => false],
|
||||
"api" => ["controller" => __DIR__ . "/API/main.php", "name" => "api", "menu" => false],
|
||||
],
|
||||
|
||||
"controller" => __DIR__ . "/CONTROLLERS/dashboard.php",
|
||||
@ -34,7 +38,8 @@
|
||||
$context = [
|
||||
"url" => $url,
|
||||
"menu_items" => $router->getFlatMenu()->values,
|
||||
"logined" => $auth->getActive()
|
||||
"logined" => $auth->getActive(),
|
||||
"appName" => "YAGS"
|
||||
];
|
||||
|
||||
// register containers
|
||||
@ -42,11 +47,11 @@
|
||||
$container->register("templateLoader", new wsos\templates\loader(__DIR__ . "/VIEWS"));
|
||||
$container->register("context", $context);
|
||||
$container->register("auth", $auth);
|
||||
$container->register("rounter", $router);
|
||||
$container->register("router", $router);
|
||||
|
||||
// seeds DB
|
||||
// do not do this in release!!
|
||||
include "seeds.php";
|
||||
//include "seeds.php";
|
||||
|
||||
$router->route($url);
|
||||
?>
|
242
web/seeds.php
242
web/seeds.php
@ -13,28 +13,105 @@
|
||||
$satType->commit();
|
||||
|
||||
$wetImgType = new \DAL\dataType();
|
||||
$wetImgType->name->set("weather photo");
|
||||
$wetImgType->name->set("AVHRR");
|
||||
$wetImgType->commit();
|
||||
|
||||
/**
|
||||
* Antennas seeds
|
||||
*/
|
||||
|
||||
$yagi = new \DAL\Antenna();
|
||||
$yagi->name->set("YAGI");
|
||||
$yagi->commit();
|
||||
|
||||
$dish = new \DAL\Antenna();
|
||||
$dish->name->set("DISH");
|
||||
$dish->commit();
|
||||
|
||||
$qfh = new \DAL\Antenna();
|
||||
$qfh->name->set("QFH");
|
||||
$qfh->commit();
|
||||
|
||||
|
||||
/**
|
||||
* Test station
|
||||
*/
|
||||
$myStation = new \DAL\station();
|
||||
$myStation->name->set("Medlánky");
|
||||
$myStation->name->set("default");
|
||||
$myStation->commit();
|
||||
|
||||
$myStation137 = new \DAL\receiver();
|
||||
$myStation137->station->set($myStation);
|
||||
$myStation137->params->set([
|
||||
"radio" => "rtlsdr",
|
||||
"gain" => 45,
|
||||
"agc" => false,
|
||||
"fs" => [250000, 1024000, 1536000, 1792000, 1920000, 2048000, 2160000, 2400000, 2560000, 2880000, 3200000]
|
||||
]);
|
||||
$myStation137->antenna->set($yagi);
|
||||
$myStation137->centerFrequency->set(137500000);
|
||||
$myStation137->gain->set(45);
|
||||
$myStation137->commit();
|
||||
|
||||
$myStation2G = new \DAL\receiver();
|
||||
$myStation2G->station->set($myStation);
|
||||
$myStation2G->params->set([
|
||||
"radio" => "hackrf",
|
||||
"amp" => true,
|
||||
"lna_gain" => 45,
|
||||
"vga_gain" => 10,
|
||||
"bias" => true
|
||||
]);
|
||||
$myStation2G->antenna->set($yagi);
|
||||
$myStation2G->centerFrequency->set(1700000000);
|
||||
$myStation2G->gain->set(45);
|
||||
$myStation2G->commit();
|
||||
|
||||
/**
|
||||
* NOAA modulations
|
||||
* Modulations
|
||||
*/
|
||||
|
||||
$fm = new \DAL\modulation();
|
||||
$fm->name->set("FM");
|
||||
$fm->commit();
|
||||
$apt = new \DAL\modulation();
|
||||
$apt->name->set("APT");
|
||||
$apt->commit();
|
||||
|
||||
$bpsk = new \DAL\modulation();
|
||||
$bpsk->name->set("BPSK");
|
||||
$bpsk->commit();
|
||||
|
||||
$hrpt = new \DAL\modulation();
|
||||
$hrpt->name->set("HRPT");
|
||||
$hrpt->commit();
|
||||
|
||||
$dsb = new \DAL\modulation();
|
||||
$dsb->name->set("DSB");
|
||||
$dsb->commit();
|
||||
|
||||
$lrpt = new \DAL\modulation();
|
||||
$lrpt->name->set("LRPT");
|
||||
$lrpt->commit();
|
||||
|
||||
/**
|
||||
* Process pipes
|
||||
*/
|
||||
|
||||
$aptPipe = new \DAL\processPipe();
|
||||
$aptPipe->name->set("NOAA APT");
|
||||
$aptPipe->pipe->set([
|
||||
"satdump noaa_apt baseband {baseband} {artefactDir} --samplerate {fs} --baseband_format s8",
|
||||
"cp {baseband} {artefactDir}"
|
||||
]);
|
||||
|
||||
$aptPipe->commit();
|
||||
|
||||
$lrptPipe = new \DAL\processPipe();
|
||||
$lrptPipe->name->set("METEOR LRPT");
|
||||
$lrptPipe->pipe->set([
|
||||
"satdump meteor_m2-x_lrpt baseband {baseband} {artefactDir} --samplerate {fs} --baseband_format s8",
|
||||
"cp {baseband} {artefactDir}"
|
||||
]);
|
||||
|
||||
$lrptPipe->commit();
|
||||
|
||||
|
||||
/**
|
||||
@ -45,18 +122,41 @@
|
||||
$noaa19->type->set($satType);
|
||||
$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" => "NOAA 19\n1 33591U 09005A 23243.18101660 .00000207 00000-0 13587-3 0 9998\n2 33591 99.0938 290.2850 0014342 35.8617 324.3514 14.12812127750532"
|
||||
"tle" => [
|
||||
"line1" => "1 33591U 09005A 23243.18101660 .00000207 00000-0 13587-3 0 9998",
|
||||
"line2" => "2 33591 99.0938 290.2850 0014342 35.8617 324.3514 14.12812127750532"
|
||||
]
|
||||
]);
|
||||
$noaa19->commit();
|
||||
|
||||
$noaa19APT = new \DAL\transmitter();
|
||||
$noaa19APT->target->set($noaa19);
|
||||
$noaa19APT->dataType->set($wetImgType);
|
||||
$noaa19APT->bandwidth->set(100);
|
||||
$noaa19APT->bandwidth->set(34000);
|
||||
$noaa19APT->centerFrequency->set(137100000);
|
||||
$noaa19APT->modulation->set($fm);
|
||||
$noaa19APT->modulation->set($apt);
|
||||
$noaa19APT->antenna->set($qfh);
|
||||
$noaa19APT->processPipe->set($aptPipe);
|
||||
$noaa19APT->commit();
|
||||
|
||||
$noaa19DSB = new \DAL\transmitter();
|
||||
$noaa19DSB->target->set($noaa19);
|
||||
$noaa19DSB->dataType->set($wetImgType);
|
||||
$noaa19DSB->bandwidth->set(34000);
|
||||
$noaa19DSB->centerFrequency->set(137770000);
|
||||
$noaa19DSB->modulation->set($dsb);
|
||||
$noaa19DSB->antenna->set($qfh);
|
||||
$noaa19DSB->commit();
|
||||
|
||||
$noaa19HRPT = new \DAL\transmitter();
|
||||
$noaa19HRPT->target->set($noaa19);
|
||||
$noaa19HRPT->dataType->set($wetImgType);
|
||||
$noaa19HRPT->bandwidth->set(3000000);
|
||||
$noaa19HRPT->centerFrequency->set(1698000000);
|
||||
$noaa19HRPT->modulation->set($hrpt);
|
||||
$noaa19HRPT->antenna->set($qfh);
|
||||
$noaa19HRPT->commit();
|
||||
|
||||
/**
|
||||
* NOAA 18
|
||||
*/
|
||||
@ -65,32 +165,124 @@
|
||||
$noaa18->type->set($satType);
|
||||
$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" => "NOAA 18\n1 28654U 05018A 23250.34978256 .00000271 00000-0 16921-3 0 9997\n2 28654 98.9045 324.3258 0015006 144.5825 215.6347 14.13000434943172"
|
||||
"tle" => [
|
||||
"line1" => "1 28654U 05018A 23250.34978256 .00000271 00000-0 16921-3 0 9997",
|
||||
"line2" => "2 28654 98.9045 324.3258 0015006 144.5825 215.6347 14.13000434943172"
|
||||
]
|
||||
]);
|
||||
$noaa18->commit();
|
||||
|
||||
$noaa18APT = new \DAL\transmitter();
|
||||
$noaa18APT->target->set($noaa18);
|
||||
$noaa18APT->dataType->set($wetImgType);
|
||||
$noaa18APT->bandwidth->set(100);
|
||||
$noaa18APT->bandwidth->set(34000);
|
||||
$noaa18APT->centerFrequency->set(137912500);
|
||||
$noaa18APT->modulation->set($fm);
|
||||
$noaa18APT->modulation->set($apt);
|
||||
$noaa18APT->antenna->set($qfh);
|
||||
$noaa18APT->processPipe->set($aptPipe);
|
||||
$noaa18APT->commit();
|
||||
|
||||
$noaa18DSB = new \DAL\transmitter();
|
||||
$noaa18DSB->target->set($noaa18);
|
||||
$noaa18DSB->dataType->set($wetImgType);
|
||||
$noaa18DSB->bandwidth->set(34000);
|
||||
$noaa18DSB->centerFrequency->set(137350000);
|
||||
$noaa18DSB->modulation->set($dsb);
|
||||
$noaa18DSB->antenna->set($qfh);
|
||||
$noaa18DSB->commit();
|
||||
|
||||
$noaa18HRPT = new \DAL\transmitter();
|
||||
$noaa18HRPT->target->set($noaa18);
|
||||
$noaa18HRPT->dataType->set($wetImgType);
|
||||
$noaa18HRPT->bandwidth->set(3000000);
|
||||
$noaa18HRPT->centerFrequency->set(1707000000);
|
||||
$noaa18HRPT->modulation->set($hrpt);
|
||||
$noaa18HRPT->antenna->set($qfh);
|
||||
$noaa18HRPT->commit();
|
||||
|
||||
/**
|
||||
* Plan observation
|
||||
* NOAA 15
|
||||
*/
|
||||
$noaa19Plan = new \DAL\observation();
|
||||
$noaa19Plan->status->set("planed");
|
||||
$noaa19Plan->locator->set($noaa19->locator->get());
|
||||
$noaa19Plan->transmitter->set($noaa19APT);
|
||||
$noaa19Plan->receiver->set($myStation137);
|
||||
$noaa19Plan->commit();
|
||||
$noaa15 = new \DAL\target();
|
||||
$noaa15->name->set("NOAA 15");
|
||||
$noaa15->type->set($satType);
|
||||
$noaa15->description->set("");
|
||||
$noaa15->locator->set([
|
||||
"tle" => [
|
||||
"line1" => "1 25338U 98030A 23256.05271705 .00000271 00000-0 13068-3 0 9997",
|
||||
"line2" => "2 25338 98.6015 283.5892 0011307 61.1013 299.1300 14.26387191317785"
|
||||
]
|
||||
]);
|
||||
$noaa15->commit();
|
||||
|
||||
$noaa18Plan = new \DAL\observation();
|
||||
$noaa18Plan->status->set("planed");
|
||||
$noaa18Plan->locator->set($noaa18->locator->get());
|
||||
$noaa18Plan->transmitter->set($noaa18APT);
|
||||
$noaa18Plan->receiver->set($myStation137);
|
||||
$noaa18Plan->commit();
|
||||
$noaa15APT = new \DAL\transmitter();
|
||||
$noaa15APT->target->set($noaa15);
|
||||
$noaa15APT->dataType->set($wetImgType);
|
||||
$noaa15APT->bandwidth->set(34000);
|
||||
$noaa15APT->centerFrequency->set(137500000);
|
||||
$noaa15APT->modulation->set($apt);
|
||||
$noaa15APT->antenna->set($qfh);
|
||||
$noaa15APT->processPipe->set($aptPipe);
|
||||
$noaa15APT->commit();
|
||||
|
||||
$noaa15DSB = new \DAL\transmitter();
|
||||
$noaa15DSB->target->set($noaa15);
|
||||
$noaa15DSB->dataType->set($wetImgType);
|
||||
$noaa15DSB->bandwidth->set(34000);
|
||||
$noaa15DSB->centerFrequency->set(1377700000);
|
||||
$noaa15DSB->modulation->set($dsb);
|
||||
$noaa15DSB->antenna->set($qfh);
|
||||
$noaa15DSB->commit();
|
||||
|
||||
$noaa15HRPT = new \DAL\transmitter();
|
||||
$noaa15HRPT->target->set($noaa15);
|
||||
$noaa15HRPT->dataType->set($wetImgType);
|
||||
$noaa15HRPT->bandwidth->set(3000000);
|
||||
$noaa15HRPT->centerFrequency->set(1702500000);
|
||||
$noaa15HRPT->modulation->set($hrpt);
|
||||
$noaa15HRPT->antenna->set($qfh);
|
||||
$noaa15HRPT->commit();
|
||||
|
||||
$meteor23 = new \DAL\target();
|
||||
$meteor23->name->set("METEOR M2-3");
|
||||
$meteor23->type->set($satType);
|
||||
$meteor23->description->set("");
|
||||
$meteor23->locator->set([
|
||||
"tle" => [
|
||||
"line1" => "1 57166U 23091A 23258.09139909 .00000046 00000-0 39414-4 0 9995",
|
||||
"line2" => "2 57166 98.7568 309.6443 0003662 194.1864 165.9212 14.23842173 11338"
|
||||
]
|
||||
]);
|
||||
$meteor23->commit();
|
||||
|
||||
$meteor23LRPT1 = new \DAL\transmitter();
|
||||
$meteor23LRPT1->target->set($meteor23);
|
||||
$meteor23LRPT1->dataType->set($wetImgType);
|
||||
$meteor23LRPT1->bandwidth->set(120000);
|
||||
$meteor23LRPT1->centerFrequency->set(137900000);
|
||||
$meteor23LRPT1->modulation->set($lrpt);
|
||||
$meteor23LRPT1->antenna->set($qfh);
|
||||
$meteor23LRPT1->processPipe->set($lrptPipe);
|
||||
$meteor23LRPT1->commit();
|
||||
|
||||
$meteor23LRPT2 = new \DAL\transmitter();
|
||||
$meteor23LRPT2->target->set($meteor23);
|
||||
$meteor23LRPT2->dataType->set($wetImgType);
|
||||
$meteor23LRPT2->bandwidth->set(120000);
|
||||
$meteor23LRPT2->centerFrequency->set(137100000);
|
||||
$meteor23LRPT2->modulation->set($lrpt);
|
||||
$meteor23LRPT2->antenna->set($qfh);
|
||||
$meteor23LRPT2->processPipe->set($lrptPipe);
|
||||
$meteor23LRPT2->commit();
|
||||
|
||||
$meteor23HRPT = new \DAL\transmitter();
|
||||
$meteor23HRPT->target->set($meteor23);
|
||||
$meteor23HRPT->dataType->set($wetImgType);
|
||||
$meteor23HRPT->bandwidth->set(3000000);
|
||||
$meteor23HRPT->centerFrequency->set(1700000000);
|
||||
$meteor23HRPT->modulation->set($hrpt);
|
||||
$meteor23HRPT->antenna->set($qfh);
|
||||
$meteor23HRPT->commit();
|
||||
|
||||
|
||||
die();
|
1
web/static/icons/undraw_printing_invoices_5r4r.svg
Normal file
1
web/static/icons/undraw_printing_invoices_5r4r.svg
Normal file
File diff suppressed because one or more lines are too long
After (image error) Size: 42 KiB |
26
web/static/js/map.js
Normal file
26
web/static/js/map.js
Normal file
@ -0,0 +1,26 @@
|
||||
function mapAddTLEPath(map, line1, line2, start, end) {
|
||||
var pointList = [];
|
||||
|
||||
var satrec = satellite.twoline2satrec(line1, line2);
|
||||
|
||||
for (var i = Date.parse(start); i < Date.parse(end); i += 1000) {
|
||||
var positionAndVelocity = satellite.propagate(satrec, new Date(i));
|
||||
var gmst = satellite.gstime(new Date(i));
|
||||
|
||||
var positionEci = positionAndVelocity.position;
|
||||
var positionGd = satellite.eciToGeodetic(positionEci, gmst);
|
||||
|
||||
pointList.push(
|
||||
new L.LatLng(satellite.degreesLat(positionGd.latitude), satellite.degreesLong(positionGd.longitude))
|
||||
);
|
||||
}
|
||||
|
||||
var targetPath = new L.Polyline(pointList, {
|
||||
color: 'red',
|
||||
weight: 3,
|
||||
opacity: 0.5,
|
||||
smoothFactor: 1
|
||||
});
|
||||
|
||||
targetPath.addTo(map);
|
||||
}
|
24
web/static/js/observations.js
Normal file
24
web/static/js/observations.js
Normal file
@ -0,0 +1,24 @@
|
||||
function plan() {
|
||||
var plan = new FormData();
|
||||
|
||||
var transmitter = document.getElementById("plan-transmitter").value;
|
||||
var receiver = document.getElementById("plan-receiver").value;
|
||||
var start = document.getElementById("plan-start").value;
|
||||
var end = document.getElementById("plan-end").value;
|
||||
|
||||
plan.append('transmitter', transmitter);
|
||||
plan.append('receiver', receiver);
|
||||
plan.append('start', start);
|
||||
plan.append('end', end);
|
||||
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
document.getElementById("created-id").innerHTML = JSON.parse(this.responseText).id;
|
||||
document.getElementById("created-alert").style.display = "block";
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", "/api/observation/plan", true);
|
||||
xhttp.send(plan);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user