854 lines
23 KiB
PHP
854 lines
23 KiB
PHP
<?php
|
|
class Packet extends Model
|
|
{
|
|
public function __construct($id)
|
|
{
|
|
parent::__construct($id);
|
|
}
|
|
|
|
/**
|
|
* Returnes icon http path
|
|
*
|
|
* @param int $scaleWidth
|
|
* @param int $scaleHeight
|
|
* @return string
|
|
*/
|
|
public function getIconFilePath($scaleWidth = null, $scaleHeight = null)
|
|
{
|
|
if (strlen($this->symbol) >= 1 && strlen($this->symbolTable) >= 1) {
|
|
$symbolAsciiValue = ord(substr($this->symbol, 0, 1));
|
|
$symbolTableAsciiValue = ord(substr($this->symbolTable, 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 PGH range in meters
|
|
*
|
|
* @return float
|
|
*/
|
|
public function getPHGRange()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
$p = $this->getPhgPower();
|
|
$h = $this->getPhgHaat(false);
|
|
$g = $this->getPhgGain();
|
|
|
|
$gain = pow(10, ($g/10)); //converts from DB to decimal
|
|
$range = sqrt(2 * $h * sqrt(($p / 10) * ($gain / 2)));
|
|
return $range / 0.000621371192; // convert to m and return
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH description
|
|
*
|
|
* @return String
|
|
*/
|
|
public function getPHGDescription()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
$power = $this->getPhgPower();
|
|
$haat = $this->getPhgHaat();
|
|
$gain = $this->getPhgGain();
|
|
$direction = $this->getPhgDirection();
|
|
$range = $this->getPHGRange();
|
|
|
|
$description = '';
|
|
if ($power !== null) {
|
|
$description .= 'Power ' . $power . ' W';
|
|
}
|
|
|
|
if ($haat !== null) {
|
|
|
|
if (strlen($description) > 0) {
|
|
$description .= ', ';
|
|
}
|
|
|
|
if (isImperialUnitUser()) {
|
|
$description .= 'Height ' . round(convertMeterToFeet($haat), 0) . ' ft';
|
|
} else {
|
|
$description .= 'Height ' . $haat . ' m';
|
|
}
|
|
}
|
|
|
|
if ($gain !== null && $direction !== null) {
|
|
if (strlen($description) > 0) {
|
|
$description .= ', ';
|
|
}
|
|
$description .= 'Gain ' . $gain . ' dB ' . $direction;
|
|
}
|
|
|
|
return $description;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH string
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getPhg()
|
|
{
|
|
if ($this->phg != null) {
|
|
if ($this->phg == 0) {
|
|
return null; // 0000 is considered not to be used (power == 0!)
|
|
} else if ($this->phg < 10) {
|
|
return '000' + strval($this->phg);
|
|
} else if ($this->phg < 100) {
|
|
return '00' + strval($this->phg);
|
|
} else if ($this->phg < 1000) {
|
|
return '0' + strval($this->phg);
|
|
} else {
|
|
return strval($this->phg);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH power
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getPhgPower()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
return pow(intval(substr($this->getPhg(), 0, 1)), 2);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH hight (above averange terrain)
|
|
*
|
|
* @param boolean $inMeters
|
|
* @return int
|
|
*/
|
|
public function getPhgHaat($inMeters = true)
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
$value = intval(substr($this->getPhg(), 1, 1));
|
|
|
|
$haat = 0;
|
|
if ($value != 0) {
|
|
$haat = 10 * pow(2, $value);
|
|
}
|
|
|
|
if ($inMeters) {
|
|
return intval(round($haat * 0.3048));
|
|
} else {
|
|
return $haat;
|
|
}
|
|
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH Gain
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getPhgGain()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
return intval(substr($this->getPhg(), 2, 1));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH Direction
|
|
*
|
|
* @return String
|
|
*/
|
|
public function getPhgDirection()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
|
|
switch (substr($this->getPhg(), 3, 1)) {
|
|
case 0:
|
|
return 'omni';
|
|
break;
|
|
case 1:
|
|
return 'North East';
|
|
break;
|
|
case 2:
|
|
return 'East';
|
|
break;
|
|
case 3:
|
|
return 'South East';
|
|
break;
|
|
case 4:
|
|
return 'South';
|
|
break;
|
|
case 5:
|
|
return 'South West';
|
|
break;
|
|
case 6:
|
|
return 'West';
|
|
break;
|
|
case 7:
|
|
return 'North West';
|
|
break;
|
|
case 8:
|
|
return 'North';
|
|
break;
|
|
case 9:
|
|
return null;
|
|
break;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get PGH Direction Degree
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getPhgDirectionDegree()
|
|
{
|
|
if ($this->getPhg() != null) {
|
|
|
|
switch (substr($this->getPhg(), 3, 1)) {
|
|
case 0:
|
|
return null;
|
|
break;
|
|
case 1:
|
|
return 45;
|
|
break;
|
|
case 2:
|
|
return 90;
|
|
break;
|
|
case 3:
|
|
return 135;
|
|
break;
|
|
case 4:
|
|
return 180;
|
|
break;
|
|
case 5:
|
|
return 225;
|
|
break;
|
|
case 6:
|
|
return 270;
|
|
break;
|
|
case 7:
|
|
return 315;
|
|
break;
|
|
case 8:
|
|
return 360;
|
|
break;
|
|
case 9:
|
|
return null;
|
|
break;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get RNG
|
|
*
|
|
* @return float
|
|
*/
|
|
public function getRng()
|
|
{
|
|
if ($this->rng != null) {
|
|
return $this->rng;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get packet type description
|
|
*
|
|
* @return Station
|
|
*/
|
|
public function getPacketTypeName()
|
|
{
|
|
switch ($this->packetTypeId) {
|
|
case 1:
|
|
return 'Position';
|
|
break;
|
|
case 2:
|
|
return 'Direction';
|
|
break;
|
|
case 3:
|
|
return 'Weather';
|
|
break;
|
|
case 4:
|
|
return 'Object';
|
|
break;
|
|
case 5:
|
|
return 'Item';
|
|
break;
|
|
case 6:
|
|
return 'Telemetry';
|
|
break;
|
|
case 7:
|
|
return 'Message';
|
|
break;
|
|
case 8:
|
|
return 'Query';
|
|
break;
|
|
case 9:
|
|
return 'Response';
|
|
break;
|
|
case 10:
|
|
return 'Status';
|
|
break;
|
|
case 11:
|
|
return 'Other';
|
|
break;
|
|
case 12:
|
|
return 'Unknown';
|
|
break;
|
|
case 13:
|
|
return 'Invalid';
|
|
break;
|
|
case 14:
|
|
return 'Capability';
|
|
break;
|
|
|
|
default:
|
|
return 'Unknown';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get releted packet weather
|
|
* @return PacketWeather
|
|
*/
|
|
public function getPacketWeather() {
|
|
return PacketWeatherRepository::getInstance()->getObjectByPacketId($this->id, $this->timestamp);
|
|
}
|
|
|
|
/**
|
|
* Get releted packet telemetry
|
|
* @return PacketTelemetry
|
|
*/
|
|
public function getPacketTelemetry() {
|
|
return PacketTelemetryRepository::getInstance()->getObjectByPacketId($this->id, $this->timestamp);
|
|
}
|
|
|
|
/**
|
|
* Returns OGN part of packet
|
|
*
|
|
* @return PacketOgn
|
|
*/
|
|
public function getPacketOgn()
|
|
{
|
|
static $cache = array();
|
|
$key = $this->id;
|
|
if (!isset($cache[$key])) {
|
|
if ($this->sourceId == 5) {
|
|
$cache[$key] = PacketOgnRepository::getInstance()->getObjectByPacketId($this->id, $this->timestamp);
|
|
} else {
|
|
$cache[$key] = new PacketOgn(null);
|
|
}
|
|
}
|
|
return $cache[$key];
|
|
}
|
|
|
|
/**
|
|
* Get Station
|
|
* @return Station
|
|
*/
|
|
public function getStationObject() {
|
|
return StationRepository::getInstance()->getObjectById($this->stationId);
|
|
}
|
|
|
|
/**
|
|
* Get Sender
|
|
* @return Sender
|
|
*/
|
|
public function getSenderObject() {
|
|
return SenderRepository::getInstance()->getObjectById($this->senderId);
|
|
}
|
|
|
|
/**
|
|
* Get Receiver
|
|
* @return Receiver
|
|
*/
|
|
public function getReceiverObject() {
|
|
return ReceiverRepository::getInstance()->getObjectById($this->receiverId);
|
|
}
|
|
|
|
/*
|
|
const qAC = 'received from the client directly via a verified connection';
|
|
const qAU = 'received from the client directly via a UDP connection';
|
|
const qAO = 'received via a client-only port and the FROMCALL does not match the login';
|
|
const qAS = 'received from another server or generated by this server';
|
|
const qAR = 'received directly (via a verified connection) from an IGate using the ,I construct';
|
|
*/
|
|
|
|
/**
|
|
* Get packet path quality based on digipeater hops used
|
|
* @return int $quality (0-4, 0 = unknown, 2 = 3 hops, 3 = 2 hops, 4+ = 1 hop)
|
|
*/
|
|
public function getPathInfo()
|
|
{
|
|
if ($this->rawPath == '') return null;
|
|
$path = explode(',', $this->rawPath);
|
|
|
|
// A valid path cannot have less than 3 elements
|
|
if (sizeof($path) < 3) return null;
|
|
|
|
// To call
|
|
$to_call = array_shift($path);
|
|
|
|
// Final destination
|
|
$last_hop = array_pop($path);
|
|
$gateway = array_pop($path);
|
|
|
|
// Iterate remaining path elements
|
|
$info = ['quality' => 4, 'hops' => 1, 'type' => 'Unknown', 'igate' => false];
|
|
foreach ($path AS $p)
|
|
{
|
|
// Digi hop?
|
|
if (strpos($p, 'WIDE') === false && strpos($p, 'RELAY') === false && strpos($p, 'TRACE') === false && strpos($p, 'TCPIP') === false && strpos($p, 'TCPXX') === false && strpos($p, 'DMR*') === false && $p != $last_hop)
|
|
{
|
|
if ($this->source_id != SOURCE_CWOP)
|
|
{
|
|
$info['quality']--;
|
|
}
|
|
$info['type'] = "RF via Digi/iGate ($last_hop)";
|
|
$info['hops']++;
|
|
}
|
|
else if (strpos($p, 'TCPIP') !== false)
|
|
{
|
|
$info['igate'] = true;
|
|
}
|
|
else if (strpos($p, 'DMR') !== false)
|
|
{
|
|
$info['type'] = "RF via DMR ($last_hop)";
|
|
}
|
|
}
|
|
|
|
if (strpos($gateway, 'qAC') !== false) $info['type'] = 'APRS-IS Direct, Verified Login';
|
|
else if (strpos($gateway, 'qAU') !== false) $info['type'] = 'APRS-IS IS Direct via UDP';
|
|
else if (strpos($gateway, 'qAO') !== false) $info['type'] = 'APRS-IS Direct via Client Port';
|
|
else if (strpos($gateway, 'qAS') !== false) $info['type'] = 'Forwarded via APRS-IS';
|
|
else if (strpos($gateway, 'qAR') !== false && $info['type'] == 'Unknown') $info['type'] = 'RF Direct';
|
|
else if (strpos($gateway, 'qAX') !== false) $info['type'] = 'APRS-IS Direct, Unverified Login';
|
|
|
|
return $info;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get packet equipment type name based on to_call
|
|
*
|
|
* @return String
|
|
*/
|
|
public function getEquipmentTypeName()
|
|
{
|
|
$path = explode(',', $this->rawPath ?? '');
|
|
if (sizeof($path) > 1)
|
|
{
|
|
$path = explode('-', $path[0]);
|
|
$pdo = PDOConnection::getInstance();
|
|
$sql =
|
|
'SELECT e.*, c.description, c.display_name
|
|
FROM equipment e
|
|
LEFT JOIN equipment_class c ON (c.id = e.class_id)
|
|
WHERE e.to_call = ? OR e.to_call = ? OR e.to_call = ?
|
|
ORDER BY e.to_call DESC LIMIT 1';
|
|
$stmt = $pdo->prepareAndExec($sql, [$path[0], substr($path[0], 0, 5).'*', substr($path[0], 0, 4) . '**']);
|
|
$record1 = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
// Extended Description
|
|
if (!empty($record1) && is_array($record1) && !is_null($record1['model']))
|
|
{
|
|
$sql =
|
|
'SELECT description AS extended_description
|
|
FROM to_calls
|
|
WHERE callsign = ? OR callsign = ? OR callsign = ?
|
|
ORDER BY callsign DESC LIMIT 1';
|
|
$stmt = $pdo->prepareAndExec($sql, [$path[0], substr($path[0], 0, 5).'*', substr($path[0], 0, 4) . '**']);
|
|
$record2 = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
return is_array($record2) ? array_merge($record1, $record2) : $record1;
|
|
}
|
|
else if (is_array($record1)) return $record1;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public $MicEData = array();
|
|
|
|
// Ref: http://www.aprs.org/aprs12/mic-e-types.txt
|
|
const radioTypes =
|
|
[
|
|
'knownPrefixes' => ['>', ']', '`', '\''],
|
|
'radios' => [
|
|
[
|
|
'prefix' => '>',
|
|
'suffix' => 'v',
|
|
'name' => 'Kenwood TH-D7A Mobile'
|
|
],
|
|
[
|
|
'prefix' => ']',
|
|
'suffix' => '',
|
|
'name' => 'Kenwood TM-D700 Mobile'
|
|
],
|
|
[
|
|
'prefix' => ']',
|
|
'suffix' => '=',
|
|
'name' => 'Kenwood TM-D710 Mobile'
|
|
],
|
|
[
|
|
'prefix' => '>',
|
|
'suffix' => '=',
|
|
'name' => 'Kenwood TH-D72 Handheld'
|
|
],
|
|
[
|
|
'prefix' => '>',
|
|
'suffix' => '^',
|
|
'name' => 'Kenwood TH-D74 Handheld'
|
|
],
|
|
[
|
|
'prefix' => '>',
|
|
'suffix' => '&',
|
|
'name' => 'Kenwood TH-D75 Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_ ',
|
|
'name' => 'SQ8L VP-Tracker'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_ ',
|
|
'name' => 'Yaesu VX-8 Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_#',
|
|
'name' => 'Yaesu VX-8G Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_$',
|
|
'name' => 'Yaesu FT1D Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_(',
|
|
'name' => 'Yaesu FT2D Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_0',
|
|
'name' => 'Yaesu FT3D Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_3',
|
|
'name' => 'Yaesu FT5D Handheld'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_)',
|
|
'name' => 'Yaesu FTM-100D Mobile'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_"',
|
|
'name' => 'Yaesu FTM-350 Mobile'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_2',
|
|
'name' => 'Yaesu FTM-200DR Mobile'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_1',
|
|
'name' => 'Yaesu FTM-300DR Mobile'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_%',
|
|
'name' => 'Yaesu FTM-400DR Mobile'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '_4',
|
|
'name' => 'Yaesu FTM-500DR Mobile'
|
|
],
|
|
[
|
|
'prefix' => '\'',
|
|
'suffix' => '|3',
|
|
'name' => 'Byonics TinyTrack3'
|
|
],
|
|
[
|
|
'prefix' => '\'',
|
|
'suffix' => '|4',
|
|
'name' => 'Byonics TinyTrack4'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '(5',
|
|
'name' => 'Anytone D578UV'
|
|
],
|
|
[
|
|
'prefix' => '`',
|
|
'suffix' => '(8',
|
|
'name' => 'Anytone D878UV'
|
|
]
|
|
]
|
|
];
|
|
|
|
|
|
/**
|
|
* Parse MIC-E Packet Data from Raw
|
|
*
|
|
* @return boolean (true if successful, false if no MIC-E data present MICE)
|
|
*/
|
|
public function getMicEData()
|
|
{
|
|
$this->MicEData = [];
|
|
|
|
// Break the path from the body
|
|
$parts = explode(':', $this->raw ?? '', 2);
|
|
if (sizeof($parts) == 2)
|
|
{
|
|
$mice_raw_path = $parts[0];
|
|
$body = $parts[1];
|
|
|
|
// Mic-E?
|
|
$micEPrefix = $body[0];
|
|
if (($micEPrefix == '`' || $micEPrefix == '\'') && strlen($body) >= 9)
|
|
{
|
|
$symbol = $body[8] . $body[7];
|
|
|
|
// Parse the MIC-E data... This is very order specific as the comment is updated with each pass
|
|
$pathParts = explode(',', $this->rawPath, 2);
|
|
if (sizeof($pathParts) > 1)
|
|
{
|
|
// Parse MicE Desitnation Address
|
|
if ($this->_getMicEDataFromToCall())
|
|
{
|
|
$this->_getLongitudeFromMicE($body);
|
|
$this->_getCommentFromMicE($body);
|
|
$this->_getRadioTypeFromMicE();
|
|
$this->_getAltitudeFromMicE();
|
|
return $this->MicEData;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse comment from MIC-E message body
|
|
*
|
|
* @param {string} $body (Message Body)
|
|
* @return boolean
|
|
* */
|
|
private function _getCommentFromMicE($body)
|
|
{
|
|
$this->MicEData['comment'] = substr($body, 9);
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse longitude from Mic-E
|
|
* @param {string} $body (Message Body)
|
|
* @return {float}
|
|
*/
|
|
private function _getLongitudeFromMicE($body)
|
|
{
|
|
$longitudeString = substr($body, 1, 3);
|
|
|
|
//degrees
|
|
$d = $this->MicEData['longitudeOffset'] + (ord($longitudeString[0]) - 28);
|
|
if ($d >= 180 && $d <= 189)
|
|
$d = $d - 80;
|
|
else if ($d >= 190 && $d <= 199)
|
|
$d = $d - 190;
|
|
|
|
//minutes
|
|
$m = ord($longitudeString[1]) - 28;
|
|
if ($m >= 60)
|
|
$m = $m - 60;
|
|
|
|
//hundredths of minutes
|
|
$h = ord($longitudeString[2]) - 28;
|
|
|
|
$minutes = $m + ($h / 100);
|
|
|
|
$this->MicEData['longitude'] = ($this->MicEData['isWest'] ? -1 : 1) * ($d + ($minutes / 60));
|
|
return $this->MicEData['comment'] = substr($body, 9);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads Data from a Mic-E Destination Address
|
|
*
|
|
* @return boolean
|
|
* */
|
|
private function _getMicEDataFromToCall()
|
|
{
|
|
$pathParts = explode(',', $this->rawPath, 2);
|
|
if (sizeof($pathParts) <= 1 || (sizeof($pathParts) > 1 && strlen($pathParts[0]) != 6))
|
|
return false;
|
|
|
|
$this->toCall = $pathParts[0];
|
|
|
|
$latitudeDigits = [];
|
|
$message = [];
|
|
$isNorth = false;
|
|
$longOffset = 0;
|
|
$isWest = false;
|
|
|
|
for ($i = 1; $i <= 6; $i++)
|
|
{
|
|
$values = getInfoForDestinationAddressChar($this->toCall[$i - 1]);
|
|
|
|
array_push($latitudeDigits, $values['latDigit']);
|
|
|
|
if ($i >= 1 && $i <= 3)
|
|
{
|
|
array_push($message, $values['message']);
|
|
}
|
|
|
|
if ($i == 4)
|
|
$isNorth = $values['isNorth'];
|
|
|
|
if ($i == 5)
|
|
$longOffset = $values['longOffset'];
|
|
|
|
if ($i == 6)
|
|
$isWest = $values['isWest'];
|
|
}
|
|
|
|
$degrees = 10 * $latitudeDigits[0] + $latitudeDigits[1];
|
|
$minutes = 10 * $latitudeDigits[2] + $latitudeDigits[3] + ((10 * $latitudeDigits[4] + $latitudeDigits[5]) / 100);
|
|
$latitude = ($isNorth ? 1 : -1) * $degrees + ($minutes / 60);
|
|
|
|
$this->MicEData =
|
|
[
|
|
'latitude' => $latitude,
|
|
'status' => getStateFromMicE($message),
|
|
'longitudeOffset' => $longOffset,
|
|
'isWest' => $isWest
|
|
];
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse altitude from Mic-E
|
|
* @return {string}
|
|
*/
|
|
private function _getAltitudeFromMicE()
|
|
{
|
|
if (empty($this->MicEData['comment'])) return;
|
|
|
|
$altitude = null;
|
|
|
|
// Alt is in the format XXX} where XXX = base91 encoded altitude in meters
|
|
if (preg_match("/(...)}/", substr($this->MicEData['comment'], 0, 5), $msgString) && sizeof($msgString) && $msgString[1])
|
|
{
|
|
$value = 0;
|
|
for ($i = 0; $i < strlen($msgString[1]); $i++) {
|
|
$value = $value * 91 + ord($msgString[1][$i]) - 33;
|
|
}
|
|
$this->MicEData['altitude'] = $value - 10000;
|
|
}
|
|
if (isset($this->MicEData['altitude']) && $this->MicEData['altitude'] != null)
|
|
{
|
|
if ($this->altitude == null) $this->altitude = $this->MicEData['altitude']; // Use Mic-E if no other altitude info is available
|
|
$this->MicEData['comment'] = substr($this->MicEData['comment'], strpos($this->MicEData['comment'], '}') + 1); //Remove altitude info from comment
|
|
}
|
|
return $this->MicEData['comment'];
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse raw comment and set radio type and return packet message as a string
|
|
* @return {string}
|
|
*/
|
|
private function _getRadioTypeFromMicE()
|
|
{
|
|
$this->MicEData['equipment'] = null;
|
|
|
|
if (array_search(substr($this->MicEData['comment'], 0, 1), self::radioTypes['knownPrefixes']) !== false)
|
|
{
|
|
foreach(self::radioTypes['radios'] as $radio)
|
|
{
|
|
if (substr($this->MicEData['comment'], 0, 1) == $radio['prefix'] && substr($this->MicEData['comment'], 0 - strlen($radio['suffix'])) == $radio['suffix'])
|
|
{
|
|
$this->MicEData['equipment'] = $radio['name']; //Set radio name
|
|
$this->MicEData['comment'] = substr($this->MicEData['comment'], strlen($radio['prefix']), strlen($this->MicEData['comment']) - strlen($radio['suffix']) - 1); //Remove radio designators from comment
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->MicEData['equipment'];
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns wind direction in cardinal format (i.e. N, S, E, W)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCardinalCourse()
|
|
{
|
|
return $this->course != '' ? convertDegreesToCardinalDirection($this->course) : '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the capabilities data for a station capability packet
|
|
* i.e. VK4ZZ-4>APU25N,TCPIP*,qAC,T2PERTH:<IGATE,MSG_CNT=0,LOC_CNT=0
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getCapabilites()
|
|
{
|
|
if ($this->packetTypeId != 14 || is_null($this->raw) || empty($this->raw)) return null;
|
|
|
|
// Parse the capability data
|
|
$parts = explode('<', $this->raw, 2);
|
|
$capabilities = explode(',', $parts[1]);
|
|
$type = array_shift($capabilities);
|
|
|
|
$capabilityData = [];
|
|
foreach($capabilities AS $entry)
|
|
{
|
|
$data = explode('=', $entry);
|
|
$capabilityData[$data[0]] = $data[1];
|
|
}
|
|
|
|
return
|
|
[
|
|
'type' => $type,
|
|
'data' => $capabilityData
|
|
];
|
|
}
|
|
|
|
}
|