601 lines
20 KiB
PHP
601 lines
20 KiB
PHP
<?php
|
|
|
|
class Station extends Model
|
|
{
|
|
public function __construct($id)
|
|
{
|
|
parent::__construct($id);
|
|
}
|
|
|
|
/**
|
|
* Returnes likly ham radio callsign
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLiklyHamRadioCallsign()
|
|
{
|
|
if ($this->sourceId == 1 && $this->stationTypeId == 1) {
|
|
$pos = strpos($this->name, '-');
|
|
if ($pos !== false) {
|
|
$callsign = substr($this->name, 0, $pos);
|
|
} else {
|
|
$callsign = $this->name;
|
|
}
|
|
|
|
if (strlen($callsign) >= 3 && preg_match('~[0-9]~', substr($callsign, 1, strlen($callsign)-1))) {
|
|
// At least 3 letters and a digit somewhere in the middle
|
|
return $callsign;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the latest used comment for this station
|
|
*
|
|
* @param int $packetType (1 = comment)
|
|
* @return array
|
|
*/
|
|
public function getLatestComment($packetType = 1)
|
|
{
|
|
static $result = null;
|
|
|
|
// Check cache to prevent multiple queries and return if it's not empty
|
|
if ($result != null) return $result;
|
|
|
|
if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24))) {
|
|
// Latest packet is not that old, go on
|
|
|
|
$sql = 'select timestamp, comment from packet where station_id = ? and timestamp > ? and packet_type_id = ? and comment is not null order by timestamp desc limit 1';
|
|
$stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24), $packetType]);
|
|
|
|
$result = $stmt->fetch(PDO::FETCH_OBJ);
|
|
|
|
// Trim off leading garbage for any improperly stored packets
|
|
if (isset($result->comment)) $result->comment = ltrim($result->comment, '`');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the latest beacon comment for this station
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getLatestBeacon()
|
|
{
|
|
static $result = null;
|
|
|
|
// Check cache to prevent multiple queries and return if it's not empty
|
|
if ($result != null) return $result;
|
|
|
|
if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24)))
|
|
{
|
|
// Query the beacon receiver id
|
|
$sql = "SELECT id FROM receiver WHERE name = 'BEACON' LIMIT 1";
|
|
$stmt = PDOConnection::getInstance()->prepareAndExec($sql);
|
|
$beaconReceiverId = $stmt->fetchColumn() ?: 0;
|
|
|
|
// Latest packet is not that old, go on
|
|
if ($beaconReceiverId > 0)
|
|
{
|
|
$sql = 'select id as packet_id, timestamp, comment from packet where station_id = ? and timestamp > ? and receiver_id = ? and comment is not null order by timestamp desc limit 1';
|
|
$stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24), $beaconReceiverId]);
|
|
$result = $stmt->fetch(PDO::FETCH_OBJ);
|
|
|
|
if ($result == false) return null;
|
|
|
|
// Trim off leading garbage for any improperly stored packets
|
|
if (isset($result->comment)) $result->comment = ltrim($result->comment, '`');
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the station type name
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getStationTypeName()
|
|
{
|
|
switch ($this->stationTypeId)
|
|
{
|
|
case 1:
|
|
return 'Station';
|
|
case 2:
|
|
return 'Object';
|
|
case 3:
|
|
return 'Item';
|
|
}
|
|
return 'Unknown';
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns OGN sender address
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getOgnSenderAddress()
|
|
{
|
|
if (isset($this->latestOgnSenderAddress) && $this->latestOgnSenderAddress != '') {
|
|
return $this->latestOgnSenderAddress;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns OGN device
|
|
*
|
|
* @return Model
|
|
*/
|
|
public function getOgnDevice()
|
|
{
|
|
static $cache = array();
|
|
$key = $this->id;
|
|
if (!isset($cache[$key])) {
|
|
if (isset($this->latestOgnSenderAddress) && $this->latestOgnSenderAddress != '') {
|
|
$cache[$key] = ModelRepository::getInstance()->getObjectFromSql('select * from ogn_device where device_id = ?', [$this->latestOgnSenderAddress]);
|
|
} else {
|
|
$cache[$key] = null;
|
|
}
|
|
}
|
|
return $cache[$key];
|
|
}
|
|
|
|
/**
|
|
* Returns OGN device DB aircraft type name
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getOgnDdbAircraftTypeName()
|
|
{
|
|
$ognDevice = $this->getOgnDevice();
|
|
if ($ognDevice) {
|
|
switch ($ognDevice->ddbAircraftType) {
|
|
case 1:
|
|
return 'Glider/Motoglider';
|
|
case 2:
|
|
return 'Plane';
|
|
case 3:
|
|
return 'Ultralight';
|
|
case 4:
|
|
return 'Helicopter';
|
|
case 5:
|
|
return 'Drone/UAV';
|
|
case 6:
|
|
return 'Other';
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns OGN aircraft type name
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getOgnAircraftTypeName()
|
|
{
|
|
if (isset($this->latestOgnAircraftTypeId) && $this->latestOgnAircraftTypeId != '') {
|
|
switch ($this->latestOgnAircraftTypeId) {
|
|
case 1:
|
|
return 'Glider';
|
|
case 2:
|
|
return 'Tow Plane';
|
|
case 3:
|
|
return 'Helicopter';
|
|
case 4:
|
|
return 'Parachute';
|
|
case 5:
|
|
return 'Drop Plane';
|
|
case 6:
|
|
return 'Hang Glider';
|
|
case 7:
|
|
return 'Para Glider';
|
|
case 8:
|
|
return 'Powered Aircraft';
|
|
case 9:
|
|
return 'Jet Aircraft';
|
|
case 10:
|
|
return 'UFO';
|
|
case 11:
|
|
return 'Balloon';
|
|
case 12:
|
|
return 'Airship';
|
|
case 13:
|
|
return 'UAV';
|
|
case 14:
|
|
return '';
|
|
case 15:
|
|
return 'Static Object';
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns source description
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSourceDescription()
|
|
{
|
|
if (isset($this->sourceId) && $this->sourceId != '') {
|
|
if ($this->sourceId == 1) {
|
|
return '<a target="_blank" rel="nofollow" href="http://www.aprs-is.net/">APRS-IS</a>';
|
|
} elseif ($this->sourceId == 2) {
|
|
return '<a target="_blank" rel="nofollow" href="http://wxqa.com/">CWOP (Citizen Weather Observer Program)</a>';
|
|
} elseif ($this->sourceId == 3) {
|
|
return '<a target="_blank" rel="nofollow" href="http://www.cbaprs.de">CBAPRS (Citizen Band APRS)</a>';
|
|
} elseif ($this->sourceId == 5) {
|
|
return '<a target="_blank" rel="nofollow" href="http://wiki.glidernet.org/">OGN (Open Glider Network)</a>';
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get distance between specified lat/lng and the station latest position (confirmed position if exists)
|
|
*
|
|
* @param float $lat
|
|
* @param float $lng
|
|
* @return int;
|
|
*/
|
|
public function getDistance($lat, $lng)
|
|
{
|
|
static $cache = array();
|
|
$key = $this->id . ':' . $lat . ':' . $lng;
|
|
if (!isset($cache[$key])) {
|
|
|
|
$latestLatitude = null;
|
|
$latestLongitude = null;
|
|
if ($this->latestConfirmedLongitude !== null && $this->latestConfirmedLatitude !== null) {
|
|
$latestLatitude = $this->latestConfirmedLatitude;
|
|
$latestLongitude = $this->latestConfirmedLongitude;
|
|
} else if ($this->latestLongitude !== null && $this->latestLatitude !== null) {
|
|
$latestLatitude = $this->latestLatitude;
|
|
$latestLongitude = $this->latestLongitude;
|
|
}
|
|
|
|
if ($lat !== null && $lng !== null && $latestLatitude !== null && $latestLongitude !== null) {
|
|
$theta = $lng - $latestLongitude;
|
|
$dist = sin(deg2rad($lat)) * sin(deg2rad($latestLatitude)) + cos(deg2rad($lat)) * cos(deg2rad($latestLatitude)) * cos(deg2rad($theta));
|
|
$dist = acos($dist);
|
|
$dist = rad2deg($dist);
|
|
$miles = $dist * 60 * 1.1515;
|
|
$cache[$key] = round($miles * 1609.344, 0);
|
|
} else {
|
|
$cache[$key] = null;
|
|
}
|
|
}
|
|
return $cache[$key];
|
|
}
|
|
|
|
/**
|
|
* Get the latest maindenhead locator (grid aquare) using latest position (confirmed position if exists)
|
|
*
|
|
* @param float $precision - Number of coordinate pairs to return; minimum 1 ("FN"), maximum 5 ("FN13WB43JL"), default 5.
|
|
*
|
|
* @return string;
|
|
*/
|
|
public function getMaidenheadLocator($precision = 5)
|
|
{
|
|
$latestLatitude = null;
|
|
$latestLongitude = null;
|
|
if ($this->latestConfirmedLongitude !== null && $this->latestConfirmedLatitude !== null) {
|
|
$latestLatitude = $this->latestConfirmedLatitude;
|
|
$latestLongitude = $this->latestConfirmedLongitude;
|
|
} else if ($this->latestLongitude !== null && $this->latestLatitude !== null) {
|
|
$latestLatitude = $this->latestLatitude;
|
|
$latestLongitude = $this->latestLongitude;
|
|
}
|
|
|
|
if ($latestLatitude !== null && $latestLongitude !== null) {
|
|
if ($latestLongitude >= 180 || $latestLongitude <= -180 || $latestLatitude >= 90 || $latestLatitude <= -90) {
|
|
return "Invalid";
|
|
}
|
|
|
|
return getMaidenheadFromCoordinates($latestLatitude, $latestLongitude, $precision);
|
|
} else {
|
|
return 'Unknown';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returnes icon http path
|
|
*
|
|
* @param int $scaleWidth
|
|
* @param int $scaleHeight
|
|
* @return string
|
|
*/
|
|
public function getIconFilePath($scaleWidth = null, $scaleHeight = null)
|
|
{
|
|
if (strlen($this->latestConfirmedSymbol ?? '') >= 1 && strlen($this->latestConfirmedSymbolTable ?? '') >= 1) {
|
|
$symbolAsciiValue = ord(substr($this->latestConfirmedSymbol, 0, 1));
|
|
$symbolTableAsciiValue = ord(substr($this->latestConfirmedSymbolTable, 0, 1));
|
|
} else {
|
|
// Default values
|
|
$symbolAsciiValue = 125;
|
|
$symbolTableAsciiValue = 47;
|
|
}
|
|
|
|
$scaleStrValue = '';
|
|
if ($scaleWidth !== null && $scaleHeight !== null) {
|
|
$scaleStrValue = '-scale' . $scaleWidth . 'x' . $scaleHeight;
|
|
}
|
|
|
|
return '/symbols/symbol-' . $symbolAsciiValue . '-' . $symbolTableAsciiValue . $scaleStrValue . '.svg';
|
|
}
|
|
|
|
/**
|
|
* Get array of the latest used symbols for this station
|
|
*
|
|
* @param int $scaleWidth
|
|
* @param int $scaleHeight
|
|
* @return array
|
|
*/
|
|
public function getLatestIconFilePaths($scaleWidth = 24, $scaleHeight = 24)
|
|
{
|
|
$result = Array();
|
|
if ($this->latestConfirmedPacketTimestamp > (time() - (60*60*24))) {
|
|
// Latest packet is not that old, go on
|
|
|
|
$scaleStrValue = '';
|
|
if ($scaleWidth !== null && $scaleHeight !== null) {
|
|
$scaleStrValue = '-scale' . $scaleWidth . 'x' . $scaleHeight;
|
|
}
|
|
|
|
$sql = 'select symbol, symbol_table from packet where station_id = ? and timestamp > ? group by symbol, symbol_table';
|
|
$stmt = PDOConnection::getInstance()->prepareAndExec($sql, [$this->id, $this->latestConfirmedPacketTimestamp - (60*60*24)]);
|
|
|
|
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
foreach ($records as $record) {
|
|
|
|
if (strlen($record['symbol'] ?? '') >= 1 && strlen($record['symbol_table'] ?? '') >= 1) {
|
|
$key = $record['symbol'] . ':' . $record['symbol_table'];
|
|
|
|
$symbolAsciiValue = ord(substr($record['symbol'], 0, 1));
|
|
$symbolTableAsciiValue = ord(substr($record['symbol_table'], 0, 1));
|
|
$result[$key] = '/symbols/symbol-' . $symbolAsciiValue . '-' . $symbolTableAsciiValue . $scaleStrValue . '.svg';
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_values($result);
|
|
}
|
|
|
|
/**
|
|
* Get packet frequency in number of seconds for the latest 10 packets
|
|
*
|
|
* @param string $date
|
|
* @param int &$numberOfPackets
|
|
* @return int
|
|
*/
|
|
public function getPacketFrequency($date, &$numberOfPackets)
|
|
{
|
|
$pdo = PDOConnection::getInstance();
|
|
if ($this->latestPacketTimestamp !== null) {
|
|
$timestamp = $this->latestPacketTimestamp;
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
// Find start timestamp
|
|
$sql = 'select timestamp ts from packet where station_id = ? and map_id in (1,2,5,7,9) and timestamp < ? order by timestamp desc limit 1';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id, $timestamp - 1]);
|
|
|
|
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!empty($record) && $record['ts'] != null && $record['ts'] < $timestamp) {
|
|
$startTimestamp = $timestamp - (($timestamp - $record['ts'])*10);
|
|
if ($timestamp - $startTimestamp < 600) {
|
|
$startTimestamp = $timestamp - 600;
|
|
}
|
|
} else {
|
|
if ($date === null) {
|
|
// Try to find frequency for the date before
|
|
$date = time();
|
|
return $this->getPacketFrequency($date, $numberOfPackets);
|
|
} else {
|
|
// Give up
|
|
return null;
|
|
}
|
|
}
|
|
|
|
$sql = 'select (max(timestamp) - min(timestamp)) / count(*) freq, count(*) c from packet where station_id = ? and map_id in (1,2,5,7,9) and timestamp >= ?';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id, $startTimestamp]);
|
|
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!empty($record) && $record['freq'] > 0) {
|
|
$numberOfPackets = $record['c'];
|
|
return $record['freq'];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get total packets recorded for the station
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getTotalPackets()
|
|
{
|
|
$pdo = PDOConnection::getInstance();
|
|
|
|
$sql = 'select count(*) c from packet where station_id = ?';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id]);
|
|
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!empty($record) && $record['c'] > 0) {
|
|
return $record['c'];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get total packets recorded for the station
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getTotalPositions()
|
|
{
|
|
$pdo = PDOConnection::getInstance();
|
|
|
|
$sql = 'select count(*) c from packet_path where sending_station_id = ?';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id]);
|
|
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!empty($record) && $record['c'] > 0) {
|
|
return $record['c'];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets the current value of the view (hit) counter for the station
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getViews()
|
|
{
|
|
$pdo = PDOConnection::getInstance();
|
|
|
|
$sql = 'SELECT view_count FROM station_views WHERE station_id = ?';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id]);
|
|
$this->viewCount = intval($stmt->fetchColumn());
|
|
return $this->viewCount;
|
|
}
|
|
|
|
|
|
/**
|
|
* Increments the view (hit) counter table record (inserting if it doesn't exist) for the station by one and returns the current views
|
|
*
|
|
* @return int
|
|
*/
|
|
public function incrementViews()
|
|
{
|
|
$pdo = PDOWriterConnection::getInstance();
|
|
|
|
$sql = 'INSERT INTO station_views (station_id, view_count) VALUES (?, 1) ON CONFLICT (station_id) DO UPDATE SET view_count = (SELECT view_count + 1 FROM station_views WHERE station_id = ?) RETURNING view_count;';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id, $this->id]);
|
|
$this->viewCount = intval($stmt->fetchColumn());
|
|
return $this->viewCount;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns symbol description
|
|
* @param boolean $includeUndefinedOverlay
|
|
* @return string
|
|
*/
|
|
public function getLatestSymbolDescription($includeUndefinedOverlay = true)
|
|
{
|
|
$symbol = null;
|
|
$symbolTable = null;
|
|
|
|
if (strlen($this->latestConfirmedSymbol ?? '') >= 1 && strlen($this->latestConfirmedSymbolTable ?? '') >= 1) {
|
|
$symbol = $this->latestConfirmedSymbol;
|
|
$symbolTable = $this->latestConfirmedSymbolTable;
|
|
}
|
|
|
|
return getSymbolDescription($symbolTable, $symbol, $includeUndefinedOverlay);
|
|
}
|
|
|
|
/**
|
|
* Get the list of #APRSThursday checkin dates for the station
|
|
*
|
|
* @param int $limit (Limits number )
|
|
* @return array (false on no records found)
|
|
*/
|
|
public function getAPRSThursdayCheckins($limit = 1)
|
|
{
|
|
$pdo = PDOConnection::getInstance();
|
|
|
|
$sql = 'SELECT * FROM aprs_thursday WHERE station_id = ? ORDER BY net_date DESC LIMIT ?';
|
|
$stmt = $pdo->prepareAndExec($sql, [$this->id, $limit]);
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the list of #APRSThursday checkin dates for the station and related SSIDs
|
|
*
|
|
* @param int $limit (Limits number )
|
|
* @return array (false on no records found)
|
|
*/
|
|
public function getRelatedAPRSThursdayCheckins($limit = 52)
|
|
{
|
|
// Fetch relates stations
|
|
$call = $this->getLiklyHamRadioCallsign();
|
|
$stations = [];
|
|
$relatedStations = StationRepository::getInstance()->getRelatedObjectListByStationId($this->id, 15);
|
|
foreach($relatedStations AS $station)
|
|
{
|
|
$stations[] = $station->id;
|
|
}
|
|
$station_list = implode(',', $stations);
|
|
$pdo = PDOConnection::getInstance();
|
|
|
|
$sql = "SELECT * FROM aprs_thursday WHERE station_id IN(SELECT id FROM station WHERE name ILIKE ?) ORDER BY net_date DESC LIMIT ?";
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->bindValue(1, "$call%", PDO::PARAM_STR);
|
|
$stmt->bindValue(2, $limit);
|
|
$stmt->execute();
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get all of the DMR ID's for the station
|
|
*
|
|
* @param int $limit (Limits number )
|
|
* @return array (false on no records found)
|
|
*/
|
|
public function getDMRIDs()
|
|
{
|
|
$dmrIdList = DMRDatabaseRepository::getInstance()->getObjectListByCallsign($this->getLiklyHamRadioCallsign());
|
|
return $dmrIdList;
|
|
}
|
|
|
|
/**
|
|
* Get all of the FCC File for the station
|
|
*
|
|
* @return FCCDatabase (or empty, intitialized object if none found)
|
|
*/
|
|
public function getFCCFile()
|
|
{
|
|
$fccFile = FCCDatabaseRepository::getInstance()->getObjectByCallsign($this->getLiklyHamRadioCallsign());
|
|
return $fccFile;
|
|
}
|
|
|
|
/**
|
|
* Get all of the Canadian License File for the station
|
|
*
|
|
* @return CADatabase (or empty, intitialized object if none found)
|
|
*/
|
|
public function getCAFile()
|
|
{
|
|
$caFile = CADatabaseRepository::getInstance()->getObjectByCallsign($this->getLiklyHamRadioCallsign());
|
|
return $caFile;
|
|
}
|
|
|
|
/**
|
|
* Check if the station has had invalid packets within the past week.
|
|
*
|
|
* @return int (number of invalid packets, or 0 if none found)
|
|
*/
|
|
public function getInvalidPackets()
|
|
{
|
|
$invalid = PacketRepository::getInstance()->getNumberOfInvalidPacketsByStationId($this->id);
|
|
return $invalid;
|
|
}
|
|
}
|