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 'APRS-IS'; } elseif ($this->sourceId == 2) { return 'CWOP (Citizen Weather Observer Program)'; } elseif ($this->sourceId == 3) { return 'CBAPRS (Citizen Band APRS)'; } elseif ($this->sourceId == 5) { return 'OGN (Open Glider Network)'; } } 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; } }