[Sat Sked] Can now predict sat skeds

This commit is contained in:
Andreas Kristiansen
2024-12-09 09:39:23 +01:00
parent 7222d9d909
commit 486527ed15
4 changed files with 287 additions and 19 deletions

View File

@@ -234,12 +234,32 @@ class Satellite extends CI_Controller {
$this->load->view('interface_assets/footer', $footerData);
}
public function searchpasses() {
public function searchPasses() {
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
try {
$result = $this->get_tle_for_predict();
$this->calcpass($result);
$tle = $this->get_tle_for_predict();
$yourgrid = $this->security->xss_clean($this->input->post('yourgrid'));
$altitude = $this->security->xss_clean($this->input->post('altitude'));
$date = $this->security->xss_clean($this->input->post('date'));
$minelevation = $this->security->xss_clean($this->input->post('minelevation'));
$timezone = $this->security->xss_clean($this->input->post('timezone'));
$data = $this->calcPass($tle, $yourgrid, $altitude, $date, $minelevation, $timezone);
$this->load->view('satellite/passtable', $data);
}
catch (Exception $e) {
header("Content-type: application/json");
echo json_encode(['ok' => 'Error', 'message' => $e->getMessage() . $e->getCode()]);
}
}
public function searchSkedPasses() {
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
try {
$tle = $this->get_tle_for_predict();
$this->calcSkedPass($tle);
}
catch (Exception $e) {
header("Content-type: application/json");
@@ -255,7 +275,7 @@ class Satellite extends CI_Controller {
return $this->satellite_model->get_tle($sat);
}
function calcpass($sat_tle) {
function calcPass($sat_tle, $yourgrid, $altitude, $date, $minelevation, $timezone) {
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
require_once "./src/predict/Predict.php";
@@ -267,9 +287,9 @@ class Satellite extends CI_Controller {
// The observer or groundstation is called QTH in ham radio terms
$predict = new Predict();
$qth = new Predict_QTH();
$qth->alt = $this->security->xss_clean($this->input->post('altitude')); // Altitude in meters
$qth->alt = $altitude; // Altitude in meters
$strQRA = $this->security->xss_clean($this->input->post('yourgrid'));
$strQRA = $yourgrid;
if ((strlen($strQRA) % 2 == 0) && (strlen($strQRA) <= 10)) { // Check if QRA is EVEN (the % 2 does that) and smaller/equal 8
$strQRA = strtoupper($strQRA);
@@ -285,7 +305,7 @@ class Satellite extends CI_Controller {
if(!$this->load->is_loaded('Qra')) {
$this->load->library('Qra');
}
$homecoordinates = $this->qra->qra2latlong($this->security->xss_clean($this->input->post('yourgrid')));
$homecoordinates = $this->qra->qra2latlong($yourgrid);
$qth->lat = $homecoordinates[0];
$qth->lon = $homecoordinates[1];
@@ -295,11 +315,11 @@ class Satellite extends CI_Controller {
$tle = new Predict_TLE($sat_tle->satellite, $temp[0], $temp[1]); // Instantiate it
$sat = new Predict_Sat($tle); // Load up the satellite data
$now = $this->get_daynum_from_date($this->security->xss_clean($this->input->post('date'))); // get the current time as Julian Date (daynum)
$now = $this->get_daynum_from_date($date); // get the current time as Julian Date (daynum)
// You can modify some preferences in Predict(), the defaults are below
//
$predict->minEle = intval($this->security->xss_clean($this->input->post('minelevation'))); // Minimum elevation for a pass
$predict->minEle = intval($minelevation); // Minimum elevation for a pass
$predict->timeRes = 1; // Pass details: time resolution in seconds
$predict->numEntries = 20; // Pass details: number of entries per pass
// $predict->threshold = -6; // Twilight threshold (sun must be at this lat or lower)
@@ -308,8 +328,6 @@ class Satellite extends CI_Controller {
$results = $predict->get_passes($sat, $qth, $now, 1);
$filtered = $predict->filterVisiblePasses($results);
$zone = $this->security->xss_clean($this->input->post('timezone'));
// Get Date format
if ($this->session->userdata('user_date_format')) {
// If Logged in and session exists
@@ -322,9 +340,73 @@ class Satellite extends CI_Controller {
$format = $custom_date_format . ' H:i:s';
$data['filtered'] = $filtered;
$data['zone'] = $zone;
$data['zone'] = $timezone;
$data['format'] = $format;
$this->load->view('satellite/passtable', $data);
return $data;
}
function calcSkedPass($sat_tle) {
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$tle = $this->get_tle_for_predict();
$yourgrid = $this->security->xss_clean($this->input->post('yourgrid'));
$altitude = $this->security->xss_clean($this->input->post('altitude'));
$date = $this->security->xss_clean($this->input->post('date'));
$minelevation = $this->security->xss_clean($this->input->post('minelevation'));
$timezone = $this->security->xss_clean($this->input->post('timezone'));
$homePass = $this->calcPass($tle, $yourgrid, $altitude, $date, $minelevation, $timezone);
$skedgrid = $this->security->xss_clean($this->input->post('skedgrid'));
$minskedelevation = $this->security->xss_clean($this->input->post('minskedelevation'));
$skedPass = $this->calcPass($tle, $skedgrid, 0, $date, $minskedelevation, $timezone);
// Get Date format
if ($this->session->userdata('user_date_format')) {
// If Logged in and session exists
$custom_date_format = $this->session->userdata('user_date_format');
} else {
// Get Default date format from /config/wavelog.php
$custom_date_format = $this->config->item('qso_date_format');
}
$data['format'] = $custom_date_format . ' H:i:s';
$data['overlaps'] = $this->findOverlaps($homePass, $skedPass);
$data['zone'] = $timezone;
$data['yourgrid'] = $yourgrid;
$data['skedgrid'] = $skedgrid;
$this->load->view('satellite/skedtable', $data);
}
function findOverlaps($homePass, $skedPass) {
$overlaps = []; // Store overlapping passes
foreach ($homePass['filtered'] as $pass1) {
foreach ($skedPass['filtered'] as $pass2) {
if ($this->checkOverlap($pass1, $pass2)) {
$overlaps[] = [
'grid1' => $pass1,
'grid2' => $pass2
];
}
}
}
return $overlaps;
}
function checkOverlap($pass1, $pass2) {
// Calculate the overlap condition
$start = max($pass1->visible_aos, $pass2->visible_aos); // Latest start time
$end = min($pass1->visible_los, $pass2->visible_los); // Earliest end time
return $start <= $end; // True if intervals overlap
}
public static function get_daynum_from_date($date) {

View File

@@ -4,8 +4,11 @@
<div class="card-body">
<form class="d-flex align-items-center">
<div class="row">
<?php if ($satellites) { ?>
<h4>Your station</h4>
<div class="mb-3 w-auto">
<label class="my-1 me-sm-2 w-auto" id="satslabel" for="satslist"><?= __("Min. Satellite Elevation"); ?></label>
<label class="my-1 me-sm-2 w-auto" id="minelevation" for="minelevation"><?= __("Min. Satellite Elevation"); ?></label>
<input class="my-1 me-sm-2 w-auto form-control" id="minelevation" type="number" min="0" max="90" name="minelevation" value="0" />
</div>
<div class="mb-3 w-auto">
@@ -477,7 +480,7 @@
<label class="my-1 me-sm-2 w-auto" for="mintime"><?= __("Min. time"); ?></label>
<select class="my-1 me-sm-2 w-auto form-select" id="mintime" name="mintime">
<?php for ($i = 0; $i <= 24; $i += 1): ?>
<option value="<?= $i ?>" <?= $i === 8 ? 'selected' : '' ?>><?= $i ?>:00</option>
<option value="<?= $i ?>" <?= $i === 00 ? 'selected' : '' ?>><?= $i ?>:00</option>
<?php endfor; ?>
</select>
</div>
@@ -485,7 +488,7 @@
<label class="my-1 me-sm-2 w-auto" for="maxtime"><?= __("Max. time"); ?></label>
<select class="my-1 me-sm-2 w-auto form-select" id="maxtime" name="maxtime">
<?php for ($i = 0; $i <= 24; $i += 1): ?>
<option value="<?= $i ?>" <?= $i === 22 ? 'selected' : '' ?>><?= $i ?>:00</option>
<option value="<?= $i ?>" <?= $i === 24 ? 'selected' : '' ?>><?= $i ?>:00</option>
<?php endfor; ?>
</select>
</div>
@@ -497,9 +500,41 @@
} ?>
</select>
</div>
<div id="addskedpartner" style="display:none" class="row">
<h4>Sked partner</h2>
<div class="mb-3 w-auto">
<label class="my-1 me-sm-2 w-auto" id="minskedelevationlabel" for="minskedelevation"><?= __("Min. Satellite Elevation"); ?></label>
<input class="my-1 me-sm-2 w-auto form-control" id="minskedelevation" type="number" min="0" max="90" name="minskedelevation" value="0" />
</div>
<div class="mb-3 w-auto">
<label class="my-1 me-sm-2 w-auto" for="minskedazimuth"><?= __("Min. Azimuth"); ?></label>
<select class="my-1 me-sm-2 w-auto form-select" id="minskedazimuth" name="minskedazimuth">
<?php for ($i = 0; $i <= 350; $i += 10): ?>
<option value="<?= $i ?>" <?= $i === 0 ? 'selected' : '' ?>><?= $i ?> &deg;</option>
<?php endfor; ?>
</select>
</div>
<div class="mb-3 w-auto">
<label class="my-1 me-sm-2 w-auto" for="maxskedazimuth"><?= __("Max. Azimuth"); ?></label>
<select class="my-1 me-sm-2 w-auto form-select" id="maxskedazimuth" name="maxskedazimuth">
<?php for ($i = 10; $i <= 360; $i += 10): ?>
<option value="<?= $i ?>" <?= $i === 360 ? 'selected' : '' ?>><?= $i ?> &deg;</option>
<?php endfor; ?>
</select>
</div>
<div class="mb-3 w-auto">
<label class="my-1 me-sm-2 w-auto" for="skedgrid"><?= __("Gridsquare"); ?></label>
<input class="my-1 me-sm-2 w-auto form-control" id="skedgrid" type="text" name="skedgrid" value=""/>
</div>
</div>
</form>
</div>
<button id="plot" type="button" name="searchpass" class="btn-sm btn btn-primary me-1 ld-ext-right ld-ext-right-plot" onclick="searchpasses()"><?= __("Load predictions"); ?><div class="ld ld-ring ld-spin"></div></button>
<button id="plot" type="button" name="searchpass" class="btn-sm btn btn-primary me-1 ld-ext-right ld-ext-right-plot" onclick="searchpasses()"><i class="fas fa-search"></i> <?= __("Load predictions"); ?><div class="ld ld-ring ld-spin"></div></button>
<button id="addsked" type="button" name="addsked" class="btn-sm btn btn-success me-1 ld-ext-right ld-ext-right-plot" onclick="addskedpartner()"><i class="fa fa-plus"></i> <?= __("Add sked partner"); ?><div class="ld ld-ring ld-spin"></div></button>
<?php } else { ?>
<?= __("No TLE information detected. Please update TLE's.")?>
<?php } ?>
</div>
<div id="resultpasses">

View File

@@ -0,0 +1,102 @@
<?php
if (!empty($overlaps)) {
echo '<table style="width:100%" class="table-sm table table-bordered table-hover table-striped table-condensed text-center">';
echo '<thead>
<tr>
<th>' . __("Grid") . '</th>
<th>' . __("Satellite") . '</th>
<th>' . __("AOS Time") . '</th>
<th>' . __("Duration") . '</th>
<th>' . __("AOS Azimuth") . '</th>
<th>' . __("Max Elevation") . '</th>
<th>' . __("LOS Time") . '</th>
<th>' . __("LOS Azimuth") . '</th>
</tr>
</thead>
<tbody>';
foreach ($overlaps as $overlap) {
echo '<tr>';
echo '<td>' . $yourgrid . '</td>';
echo '<td>' . $overlap['grid1']->satname . '</td>';
echo '<td>' . Predict_Time::daynum2readable($overlap['grid1']->visible_aos, $zone, $format) . '</td>';
echo '<td>' . returntimediff(Predict_Time::daynum2readable($overlap['grid1']->visible_aos, $zone, $format), Predict_Time::daynum2readable($overlap['grid1']->visible_los, $zone, $format), $format) . '</td>';
$aos_az = round($overlap['grid1']->visible_aos_az);
echo '<td>' . $aos_az . ' ° (' . azDegreesToDirection($overlap['grid1']->visible_aos_az) . ')<span style="margin-left: 10px; display: inline-block; transform: rotate('.(-45+$aos_az).'deg);"><i class="fas fa-location-arrow fa-xs"></i></span></td>';
$max_el = round($overlap['grid1']->max_el);
echo '<td>' . $max_el . ' °<span style="margin-left: 10px; display: inline-block; transform: rotate(-'.$max_el.'deg);"><i class="fas fa-arrow-right fa-xs"></i></span></td>';
echo '<td>' . Predict_Time::daynum2readable($overlap['grid1']->visible_los, $zone, $format) . '</td>';
$los_az = round($overlap['grid1']->visible_los_az);
echo '<td>' . $los_az . ' ° (' . azDegreesToDirection($overlap['grid1']->visible_los_az) . ')<span style="margin-left: 10px; display: inline-block; transform: rotate('.(-45+$los_az).'deg);"><i class="fas fa-location-arrow fa-xs"></i></span></td>';
echo '</tr>';
echo '<tr>';
echo '<td>' . $skedgrid . '</td>';
echo '<td>' . $overlap['grid2']->satname . '</td>';
echo '<td>' . Predict_Time::daynum2readable($overlap['grid2']->visible_aos, $zone, $format) . '</td>';
echo '<td>' . returntimediff(Predict_Time::daynum2readable($overlap['grid2']->visible_aos, $zone, $format), Predict_Time::daynum2readable($overlap['grid2']->visible_los, $zone, $format), $format) . '</td>';
$aos_az = round($overlap['grid2']->visible_aos_az);
echo '<td>' . $aos_az . ' ° (' . azDegreesToDirection($overlap['grid2']->visible_aos_az) . ')<span style="margin-left: 10px; display: inline-block; transform: rotate('.(-45+$aos_az).'deg);"><i class="fas fa-location-arrow fa-xs"></i></span></td>';
$max_el = round($overlap['grid2']->max_el);
echo '<td>' . $max_el . ' °<span style="margin-left: 10px; display: inline-block; transform: rotate(-'.$max_el.'deg);"><i class="fas fa-arrow-right fa-xs"></i></span></td>';
echo '<td>' . Predict_Time::daynum2readable($overlap['grid2']->visible_los, $zone, $format) . '</td>';
$los_az = round($overlap['grid2']->visible_los_az);
echo '<td>' . $los_az . ' ° (' . azDegreesToDirection($overlap['grid2']->visible_los_az) . ')<span style="margin-left: 10px; display: inline-block; transform: rotate('.(-45+$los_az).'deg);"><i class="fas fa-location-arrow fa-xs"></i></span></td>';
echo '</tr>';
echo "<tr><td colspan='8'>---</td></tr>"; // Separator row
}
echo "</tbody>";
echo "</table>";
} else {
echo "<p>No overlapping passes found.</p>";
}
echo '<table style="width:100%" class="table-sm table table-bordered table-hover table-striped table-condensed text-center">';
echo '<thead>
<tr>
<th>' . __("Satellite") . '</th>
<th>' . __("Date") . '</th>
<th>' . __("Sked AOS Time") . '</th>
<th>' . __("Sked LOS Time") . '</th>
<th>' . __("Duration") . '</th>
</tr>
</thead>
<tbody>';
foreach ($overlaps as $overlap) {
// Example data (replace these with real calculations or data from the arrays)
$satellite = $overlap['grid1']->satname; // Replace with your satellite name
$skedDate = Predict_Time::daynum2readable($overlap['grid1']->visible_aos, $zone, $format); // Example sked date
$skedAOS = $overlap['grid1']->visible_aos < $overlap['grid2']->visible_aos ? $overlap['grid2']->visible_aos : $overlap['grid1']->visible_aos;
$skedLOS = $overlap['grid1']->visible_los < $overlap['grid2']->visible_los ? $overlap['grid1']->visible_los : $overlap['grid2']->visible_los;
echo '<tr>';
echo "<td>". $satellite . "</td>";
echo "<td></td>";
echo "<td>" . Predict_Time::daynum2readable($skedAOS, $zone, $format) . "</td>";
echo "<td>" . Predict_Time::daynum2readable($skedLOS, $zone, $format) . "</td>";
echo "<td>" . returntimediff(Predict_Time::daynum2readable($skedAOS, $zone, $format), Predict_Time::daynum2readable($skedLOS, $zone, $format), $format) . "</td>";
echo "</div>";
}
function returntimediff($start, $end, $format) {
$datetime1 = DateTime::createFromFormat($format, $end);
$datetime2 = DateTime::createFromFormat($format, $start);
$interval = $datetime1->diff($datetime2);
$diff = sprintf('%02d', (($interval->h*60)+$interval->i)).':'.sprintf('%02d', $interval->s).' '.__("min");
return $diff;
}
function azDegreesToDirection($az = 0) {
$i = floor($az / 22.5);
$m = (22.5 * (2 * $i + 1)) / 2;
$i = ($az >= $m) ? $i + 1 : $i;
return trim(substr('N NNENE ENEE ESESE SSES SSWSW WSWW WNWNW NNWN ', $i * 3, 3));
}

View File

@@ -1,6 +1,20 @@
function searchpasses() {
$.ajax({
url: base_url + 'index.php/satellite/searchpasses',
if ($('#addskedpartner').is(':hidden')) {
loadPasses();
} else {
let skedgrid = $("#skedgrid").val();
if (skedgrid == '') {
return;
}
loadSkedPasses();
}
return;
}
function loadPasses() {
$.ajax({
url: base_url + 'index.php/satellite/searchPasses',
type: 'post',
data: {'sat': $("#satlist").val(),
'yourgrid': $("#yourgrid").val(),
@@ -21,3 +35,38 @@ function searchpasses() {
}
});
}
function loadSkedPasses() {
$.ajax({
url: base_url + 'index.php/satellite/searchSkedPasses',
type: 'post',
data: {'sat': $("#satlist").val(),
'yourgrid': $("#yourgrid").val(),
'minelevation': $("#minelevation").val(),
'minazimuth': $("#minazimuth").val(),
'maxazimuth': $("#maxazimuth").val(),
'altitude': $("#altitude").val(),
'timezone': $("#timezone").val(),
'date': $("#date").val(),
'mintime': $("#mintime").val(),
'maxtime': $("#maxtime").val(),
'skedgrid': $("#skedgrid").val(),
'minskedelevation': $("#minskedelevation").val(),
},
success: function (html) {
$("#resultpasses").html(html);
},
error: function(e) {
modalloading=false;
}
});
}
function addskedpartner() {
if ($('#addskedpartner').is(':hidden')) {
$('#addskedpartner').show();
} else {
$('#addskedpartner').hide();
}
}