isImperialUnitUser(); } /** * Convert km to miles (or kmh to mph) * * @param {float} value * @return float */ function convertKilometerToMile($value) { return $value * 0.621371192; } /** * Convert meter to feet * * @param {float} value * @return float */ function convertMeterToFeet($value) { return $value * 3.2808399; } /** * Convert meter per second to miles per second * * @param {float} value * @return float */ function convertMpsToMph($value) { return $value * 2.23693629; } /** * Convert meter to yard * * @param {float} value * @return float */ function convertMeterToYard($value) { return $value * 1.0936133; } /** * Convert mm to inches * * @param {float} value * @return float */ function convertMmToInch($value) { return $value * 0.0393700787; } /** * Convert celcius to fahrenheit * * @param {float} value * @return float */ function convertCelciusToFahrenheit($value) { return $value * (9/5) + 32; } /** * Calculate wind chill and return celcius temperature * * @param {float} temp Temperature in degrees celcius * @param {float} speed Wind speed in m/s * @return float */ function calculateMetricWindChill($temp, $speed) { // Convert windspeed m/s to Km/h $speed *= 3.6; if ($temp > 10) return null; if ($speed <= 4.8) return $temp; return 13.12 + (0.6215 * $temp) + ((0.3965 * $temp - 11.37) * pow($speed, 0.16)); } /** * Calculate wind chill and return fahrenheit temperature * * @param {float} temp Temperature in degrees celcius * @param {float} speed Wind speed in m/s * @return float */ function calculateImperialWindChill($temp, $speed) { $value = calculateMetricWindChill($temp, $speed); if ($value) { $value = convertCelciusToFahrenheit($value); } return $value; } /** * Convert hPa/mbar to mmhg * * @param {float} value * @return float */ function convertMbarToMmhg($value) { return $value * 0.75006375541921; } /** * Convert hPa/mbar to inches hg * * @param {float} value * @return float */ function convertMbarToInchHg($value) { return $value * 0.0295301; } /** * Convert a value based on its unit * * @param {float} value * @param {string} unit * @return float */ function universalDataUnitConvert($value, $unit) { if (!isImperialUnitUser()) return array('value' => $value, 'unit' => $unit); $unit = trim($unit); if ($unit == 'C') return array('value' => round(convertCelciusToFahrenheit($value), 2), 'unit' => 'F'); return array('value' => $value, 'unit' => $unit); } /** * Returnes true if value is float * * @param mixed $value * @return boolean */ function isFloat($value) { if (is_numeric($value)) { // PHP automagically tries to coerce $value to a number return is_float($value + 0); } return false; } /** * Returnes true if value is int * * @param mixed $value * @return boolean */ function isInt($value) { return (ctype_digit(strval($value))); } /** * Replace first occurence * * @param string $search * @param string $replace * @param string $string * @return string */ function str_replace_first($search, $replace, $string) { $pos = strpos($string, $search); if ($pos !== false) { return substr_replace($string, $replace, $pos, strlen($search)); } else { return $string; } } /** * Gets the APRS data source filter from an HTTP request * * @param array $defaultValues (Default data source values to use if none set) * @param string $method (INPUT_GET or INPUT_POST) * @param varname $string (Key to look for, default 'source') * @return array (Data sources) */ function getAPRSFilterSourceFromRequest($defaultValues = [SOURCE_APRS,SOURCE_CWOP,SOURCE_CBAPRS,SOURCE_OGN], $method = INPUT_GET, $varname = 'source') { $source = array_filter(array_unique(filter_input($method, $varname, FILTER_VALIDATE_INT, ['flags' => FILTER_REQUIRE_ARRAY | FILTER_NULL_ON_FAILURE | FILTER_FORCE_ARRAY, 'options' => ['min_range'=> SOURCE_APRS, 'max_range'=> SOURCE_OGN]]) ?: $defaultValues)); if (sizeof($source) == 0) $source = $defaultValues; return $source; } /* * Returnes symbol description * * @param string $symbolTable * @param string $symbol * @param boolean $includeUndefinedOverlay * @return string */ function getSymbolDescription($symbolTable, $symbol, $includeUndefinedOverlay) { if ($symbolTable == '/') { switch ($symbol) { case '!': return 'Police, Sheriff'; case '"': return 'No Symbol'; case '#': return 'Digipeater'; case '$': return 'Phone'; case '%': return 'DX Cluster'; case '&': return 'HF Gateway'; case '\'': return 'Small Aircraft'; case '(': return 'Mobile Satellite Station'; case ')': return 'Wheelchair (handicapped)'; case '*': return 'Snowmobile'; case '+': return 'Red Cross'; case ',': return 'Scout (Boy Scout)'; case '-': return 'House (VHF)'; case '.': return 'X'; case '/': return 'Red Dot'; case '0': return 'Circle (0)'; case '1': return 'Circle (1)'; case '2': return 'Circle (2)'; case '3': return 'Circle (3)'; case '4': return 'Circle (4)'; case '5': return 'Circle (5)'; case '6': return 'Circle (6)'; case '7': return 'Circle (7)'; case '8': return 'Circle (8)'; case '9': return 'Circle (9)'; case ':': return 'Fire'; case ';': return 'Campground or Portable operation'; case '<': return 'Motorcycle'; case '=': return 'Railroad Engine'; case '>': return 'Car'; case '?': return 'File server'; case '@': return 'Hurricane prediction'; case 'A': return 'Aid Station'; case 'B': return 'BBS or PBBS'; case 'C': return 'Canoe'; case 'D': return 'No Symbol'; // was originally undefined case 'E': return 'Event (Special live event)'; // Eyeball case 'F': return 'Farm Vehicle (tractor)'; case 'G': return 'Grid Square (6 digit)'; case 'H': return 'Hotel (blue bed symbol)'; case 'I': return 'TCP/IP network station'; case 'J': return 'No Symbol'; case 'K': return 'School'; case 'L': return 'Logged-ON user (or PC User)'; case 'M': return 'MacAPRS'; case 'N': return 'NTS Station'; case 'O': return 'Balloon'; case 'P': return 'Police'; case 'Q': return 'TBD'; case 'R': return 'Recreational Vehicle'; case 'S': return 'Shuttle'; case 'T': return 'SSTV'; case 'U': return 'Bus'; case 'V': return 'ATV'; case 'W': return 'National Weather Service'; case 'X': return 'Helicopter'; case 'Y': return 'Yacht (sail)'; case 'Z': return 'WinAPRS'; case '[': return 'Human/Person'; case '\\': return 'Triangle (DF station)'; case ']': return 'Mail/PostOffice'; case '^': return 'Large Aircraft'; case '_': return 'Weather Station'; case '`': return 'Dish Antenna'; case 'a': return 'Ambulance'; case 'b': return 'Bike'; case 'c': return 'Incident Command Post'; case 'd': return 'Fire Station'; case 'e': return 'Horse (equestrian)'; case 'f': return 'Fire Truck'; case 'g': return 'Glider'; case 'h': return 'Hospital'; case 'i': return 'IOTA (islands on the air)'; case 'j': return 'Jeep'; case 'k': return 'Truck'; case 'l': return 'Laptop'; case 'm': return 'Mic-E Repeater'; case 'n': return 'Node (black bulls-eye)'; case 'o': return 'EOC'; case 'p': return 'Rover (puppy, or dog)'; case 'q': return 'Grid square'; case 'r': return 'Antenna'; case 's': return 'Power Boat'; case 't': return 'Truck Stop'; case 'u': return 'Truck (18 wheeler)'; case 'v': return 'Van'; case 'w': return 'Water Station'; case 'x': return 'xAPRS (Unix)'; case 'y': return 'Yagi @ QTH'; case 'z': return 'Shelter'; case '{': return 'No Symbol'; case '|': return 'TNC Stream Switch'; case '}': return 'No Symbol'; case '~': return 'TNC Stream Switch'; } } else { switch ($symbol) { case '!': switch ($symbolTable) { case "\\": return 'Emergency!'; case 'E': return 'ELT or EPIRB'; case 'V': return 'Volcanic Eruption or Lava'; default: if ($includeUndefinedOverlay) { return 'Emergency (with overlay ' . $symbolTable . ')'; } else { return null; } } case '"': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '#': switch ($symbolTable) { case '\\': return 'Digipeater'; case 'A': return 'Digipeater (An Alt-Freq input digi)'; case 'R': return 'Digipeater (RELAY only)'; case 'W': return 'Digipeater (RELAY and WIDE)'; case 'T': return 'Digipeater (PacComm RELAY,WIDE and TRACE)'; case 'N': return 'Digipeater (WIDEn-N and relay,wide,trace)'; case 'I': return 'Digipeater (digipeater is also an IGate)'; case 'L': return 'Digipeater (LIMITED New n-N Paradigm digi)'; case 'S': return 'Digipeater (New n-N Paradigm digi)'; case 'P': return 'Digipeater (PacComm, New n-N Paradigm)'; case 'U': return 'Digipeater (UI-DIGI firmware)'; case 'D': return 'Digipeater (DIGI_NED)'; case '1': return 'Digipeater (Fill-in digi, 1-hop limit)'; case 'X': return 'Digipeater (Experimental)'; default: if ($includeUndefinedOverlay) { return 'Digipeater (with overlay ' . $symbolTable . ')'; } else { return null; } } case '$': switch ($symbolTable) { case '\\': return 'Bank or ATM'; case 'U': return 'ATM (US dollars)'; case 'L': return 'ATM (Brittish Pound)'; case 'Y': return 'ATM (Japanese Yen)'; default: if ($includeUndefinedOverlay) { return 'Bank or ATM (with overlay ' . $symbolTable . ')'; } else { return null; } } case '%': switch ($symbolTable) { case '\\': return 'Power/Energy'; case 'C': return 'Power/Energy (Coal)'; case 'E': return 'Power/Energy (Emergency)'; case 'G': return 'Power/Energy (Geothermal)'; case 'H': return 'Power/Energy (Hydroelectric)'; case 'N': return 'Power/Energy (Nuclear)'; case 'P': return 'Power/Energy (Portable)'; case 'S': return 'Power/Energy (Solar)'; case 'T': return 'Power/Energy (Turbine)'; case 'W': return 'Power/Energy (Wind)'; default: if ($includeUndefinedOverlay) { return 'Power/Energy'; } else { return null; } } case '&': switch ($symbolTable) { case '\\': return 'Gateway'; case 'I': return 'Igate (Generic)'; case 'R': return 'Receive only IGate'; case 'P': return 'PSKmail node'; case 'T': return 'TX Igate (with path set to 1 hop)'; case 'W': return 'WIRES-X'; case '2': return 'TX igate (with path set to 2 hops)'; case 'D': return 'D-STAR (D-PRS IGate)'; default: if ($includeUndefinedOverlay) { return 'Gateway (with overlay ' . $symbolTable . ')'; } else { return null; } } case '\'': switch ($symbolTable) { case '\\': return 'Incident site (Airplane Crash Site)'; case 'A': return 'Incident site (Automobile crash site)'; case 'H': return 'Incident site (Hazardous incident)'; case 'M': return 'Incident site (Multi-Vehicle crash site)'; case 'P': return 'Incident site (Pileup)'; case 'T': return 'Incident site (Truck wreck)'; default: if ($includeUndefinedOverlay) { return 'Incident site'; } else { return null; } } case '(': switch ($symbolTable) { case "\\": return 'Cloudy'; default: if ($includeUndefinedOverlay) { return 'Cloudy'; } else { return null; } } case ')': switch ($symbolTable) { case "\\": return 'Firenet MEO (Modis Earth Observatory)'; default: if ($includeUndefinedOverlay) { return 'Firenet MEO (Modis Earth Observatory)'; } else { return null; } } case '*': switch ($symbolTable) { case "\\": return 'Snow'; default: if ($includeUndefinedOverlay) { return 'Snow'; } else { return null; } } case '+': switch ($symbolTable) { case "\\": return 'Church'; default: if ($includeUndefinedOverlay) { return 'Church'; } else { return null; } } case ',': switch ($symbolTable) { case "\\": return 'Scout (Girl Scout)'; default: if ($includeUndefinedOverlay) { return 'Scout (Girl Scout)'; } else { return null; } } case '-': switch ($symbolTable) { case '\\': return 'House (HF)'; case '5': return 'House (50 Hz mains power)'; case '6': return 'House (60 Hz mains power)'; case 'B': return 'House (Backup Battery Power)'; case 'C': return 'House (Ham club)'; case 'E': return 'House (Emergency power)'; case 'G': return 'House (Geothermal)'; case 'H': return 'House (Hydro powered)'; case 'O': return 'House (Operator Present)'; case 'S': return 'House (Solar Powered)'; case 'W': return 'House (Wind powered)'; default: if ($includeUndefinedOverlay) { return 'House (with overlay ' . $symbolTable . ')'; } else { return null; } } case '.': switch ($symbolTable) { case "\\": return 'Unknown Position'; default: if ($includeUndefinedOverlay) { return 'Unknown Position'; } else { return null; } } case '/': switch ($symbolTable) { case "\\": return 'Waypoint Destination'; default: if ($includeUndefinedOverlay) { return 'Waypoint Destination'; } else { return null; } } case '0': switch ($symbolTable) { case '\\': return 'Circle'; case 'A': return 'Allstar Node'; case 'E': return 'Echolink Node'; case 'I': return 'IRLP repeater'; case 'S': return 'Staging Area'; case 'V': return 'Echolink and IRLP (VOIP)'; case 'W': return 'WIRES (Yaesu VOIP)'; default: if ($includeUndefinedOverlay) { return 'Circle (with overlay ' . $symbolTable . ')'; } else { return null; } } case '1': case '2': case '3': case '4': case '5': case '6': case '7': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '8': switch ($symbolTable) { case '\\': return 'No Symbol'; case '8': return '802.11 network node'; case 'G': return '802.11G'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '9': switch ($symbolTable) { case "\\": return 'Gas Station'; default: if ($includeUndefinedOverlay) { return 'Gas Station'; } else { return null; } } case ':': switch ($symbolTable) { case "\\": return 'Hail (weather condition)'; default: if ($includeUndefinedOverlay) { return 'Hail (weather condition)'; } else { return null; } } case ';': switch ($symbolTable) { case '\\': return 'Portable, Park or Picnic'; case 'F': return 'Field Day'; case 'I': return 'Islands on the air'; case 'S': return 'Summits on the air'; case 'W': return 'WOTA'; default: if ($includeUndefinedOverlay) { return 'Portable'; } else { return null; } } case '<': switch ($symbolTable) { case "\\": return 'Advisory (single gale flag)'; default: if ($includeUndefinedOverlay) { return 'Advisory (single gale flag)'; } else { return null; } } case '=': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '>': switch ($symbolTable) { case '\\': return 'Car'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': return 'Numbered Car (' . $symbolTable . ')'; case 'E': return 'Car (Electric)'; case 'H': return 'Car (Hybrid)'; case 'S': return 'Car (Solar powered)'; case 'V': return 'Car (GM Volt)'; default: if ($includeUndefinedOverlay) { return 'Car (with overlay ' . $symbolTable . ')'; } else { return null; } } case '?': switch ($symbolTable) { case "\\": return 'Info Kiosk'; default: if ($includeUndefinedOverlay) { return 'Info Kiosk'; } else { return null; } } case '@': switch ($symbolTable) { case "\\": return 'Hurricane/Tropical storm'; default: if ($includeUndefinedOverlay) { return 'Hurricane/Tropical storm (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'A': switch ($symbolTable) { case '\\': return 'Box'; case '9': return 'Mobile DTMF user'; case '7': return 'HT DTMF user'; case 'H': return 'House DTMF user'; case 'E': return 'Echolink DTMF report'; case 'I': return 'IRLP DTMF report'; case 'R': return 'RFID report'; case 'A': return 'AllStar DTMF report'; case 'D': return 'D-Star report'; case 'X': return 'OLPC Laptop XO'; default: if ($includeUndefinedOverlay) { return 'Box (' . $symbolTable . ')'; } else { return null; } } case 'B': switch ($symbolTable) { case "\\": return 'Blowing snow'; default: if ($includeUndefinedOverlay) { return 'Blowing snow'; } else { return null; } } case 'C': switch ($symbolTable) { case "\\": return 'Coast Guard'; default: if ($includeUndefinedOverlay) { return 'Coast Guard'; } else { return null; } } case 'D': switch ($symbolTable) { case '\\': return 'Depot'; // drizzle rain moved to ' ovlyD case 'A': return 'Airport'; case 'F': return 'Ferry Landing'; case 'H': return 'Heloport'; case 'R': return 'Rail Depot'; case 'B': return 'Bus Depot'; case 'L': return 'LIght Rail or Subway'; case 'S': return 'Seaport Depot'; default: if ($includeUndefinedOverlay) { return 'Depot (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'E': switch ($symbolTable) { case '\\': return 'Smoke'; case 'H': return 'Haze'; case 'S': return 'Smoke'; case 'B': return 'Blowing Snow'; // was \B case 'D': return 'Blowing Dust or Sand'; // was \b case 'F': return 'Fog'; // was \{ default: if ($includeUndefinedOverlay) { return 'Smoke (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'F': switch ($symbolTable) { case "\\": return 'Freezing rain'; default: if ($includeUndefinedOverlay) { return 'Freezing rain'; } else { return null; } } case 'G': switch ($symbolTable) { case "\\": return 'Snow Shower'; default: if ($includeUndefinedOverlay) { return 'Snow Shower'; } else { return null; } } case 'H': switch ($symbolTable) { case '\\': return 'Haze'; case 'R': return 'Radiation detector'; case 'W': return 'Hazardous Waste'; case 'X': return 'Skull&Crossbones'; default: if ($includeUndefinedOverlay) { return 'Hazard (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'I': switch ($symbolTable) { case "\\": return 'Rain Shower'; default: if ($includeUndefinedOverlay) { return 'Rain Shower'; } else { return null; } } case 'J': switch ($symbolTable) { case "\\": return 'Lightning'; default: if ($includeUndefinedOverlay) { return 'Lightning'; } else { return null; } } case 'K': switch ($symbolTable) { case "\\": return 'Kenwood HT'; default: if ($includeUndefinedOverlay) { return 'Kenwood HT'; } else { return null; } } case 'L': switch ($symbolTable) { case "\\": return 'Lighthouse'; default: if ($includeUndefinedOverlay) { return 'Lighthouse (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'M': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case 'N': switch ($symbolTable) { case "\\": return 'Navigation Buoy'; default: if ($includeUndefinedOverlay) { return 'Navigation Buoy (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'O': switch ($symbolTable) { case '\\': return 'Rocket (amateur)'; case 'B': return 'Blimp (non-rigid airship)'; case 'M': return 'Manned Balloon'; case 'T': return 'Teathered Balloon'; case 'C': return 'Constant Pressure Balloon - Long duration'; case 'R': return 'Rocket bearing Balloon (Rockoon)'; default: if ($includeUndefinedOverlay) { return 'Balloon (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'P': switch ($symbolTable) { case "\\": return 'Parking'; default: if ($includeUndefinedOverlay) { return 'Parking (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'Q': switch ($symbolTable) { case "\\": return 'Quake'; default: if ($includeUndefinedOverlay) { return 'Quake'; } else { return null; } } case 'R': switch ($symbolTable) { case '\\': return 'Restaurant (generic)'; case '7': return 'Restaurant (7/11)'; case 'K': return 'Restaurant (KFC)'; case 'M': return 'Restaurant (McDonalds)'; case 'T': return 'Restaurant (Taco Bell)'; default: if ($includeUndefinedOverlay) { return 'Restaurant (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'S': switch ($symbolTable) { case "\\": return 'Satellite/Pacsat'; default: if ($includeUndefinedOverlay) { return 'Satellite/Pacsat (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'T': switch ($symbolTable) { case "\\": return 'Thunderstorm'; default: if ($includeUndefinedOverlay) { return 'Thunderstorm (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'U': switch ($symbolTable) { case "\\": return 'Sunny'; default: if ($includeUndefinedOverlay) { return 'Sunny (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'V': switch ($symbolTable) { case "\\": return 'VORTAC Navigation Aid'; default: if ($includeUndefinedOverlay) { return 'VORTAC Navigation Aid'; } else { return null; } } case 'W': switch ($symbolTable) { case "\\": return 'National Weather Service'; default: if ($includeUndefinedOverlay) { return 'National Weather Service (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'X': switch ($symbolTable) { case "\\": return 'Pharmacy'; default: if ($includeUndefinedOverlay) { return 'Pharmacy'; } else { return null; } } case 'Y': switch ($symbolTable) { case '\\': return 'Radio/APRS Device'; case 'A': return 'Alinco'; case 'B': return 'Byonics'; case 'I': return 'Icom'; case 'K': return 'Kenwood'; case 'Y': return 'Yaesu/Standard'; default: if ($includeUndefinedOverlay) { return 'Radio/APRS Device'; // or "No Symbol"? } else { return null; } } case 'Z': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '[': switch ($symbolTable) { case '\\': return 'Wall Cloud (or pedestal cloud)'; case 'B': return 'Baby on board (stroller, pram etc)'; case 'S': return 'Skier'; case 'R': return 'Runner'; case 'H': return 'Hiker'; default: if ($includeUndefinedOverlay) { return 'Human/Person (with overlay ' . $symbolTable . ')'; } else { return null; } } case '\\': switch ($symbolTable) { case '\\': return 'No Symbol'; case 'A': return 'Avmap G5'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case ']': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '^': switch ($symbolTable) { case '\\': return 'Aircraft'; // top-view originally intended to point in direction of flight case 'A': return 'Aircraft (A = Autonomous)'; case 'D': return 'Aircraft (D = Drone)'; case 'E': return 'Aircraft (E = Electric aircraft)'; case 'G': return 'Aircraft (G = Glider aircraft)'; case 'H': return 'Aircraft (H = Hovercraft)'; case 'J': return 'Aircraft (J = JET)'; case 'L': return 'Paraglider'; // Not official definition case 'M': return 'Aircraft (M = Missle)'; case 'P': return 'Aircraft (P = Propeller)'; case 'R': return 'Aircraft (R = Remotely Piloted)'; case 'S': return 'Aircraft (S = Solar Powered)'; case 'V': return 'Aircraft (V = Vertical takeoff)'; case 'X': return 'Aircraft (X = Experimental)'; default: if ($includeUndefinedOverlay) { return 'Aircraft (with overlay ' . $symbolTable . ')'; } else { return null; } } case '_': switch ($symbolTable) { case "\\": return 'Weather Station'; default: if ($includeUndefinedOverlay) { return 'Weather Station (with overlay ' . $symbolTable . ')'; } else { return null; } } case '`': switch ($symbolTable) { case "\\": return 'Rain'; default: if ($includeUndefinedOverlay) { return 'Rain'; } else { return null; } } case 'a': switch ($symbolTable) { case '\\': return 'Red Diamond'; case 'A': return 'ARES'; case 'D': return 'D-STAR (previously Dutch ARES)'; case 'G': return 'RSGB Radio Society of Great Brittan'; case 'R': return 'RACES'; case 'S': return 'SATERN Salvation Army'; case 'W': return 'WinLink'; case 'Y': return 'C4FM Yaesu repeaters'; default: if ($includeUndefinedOverlay) { return 'Red Diamond (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'b': switch ($symbolTable) { case "\\": return 'Dust blowing'; default: if ($includeUndefinedOverlay) { return 'Dust blowing'; } else { return null; } } case 'c': switch ($symbolTable) { case '\\': return 'Civil Defense'; case 'D': return 'Decontamination'; case 'R': return 'RACES'; case 'S': return 'SATERN mobile canteen'; default: if ($includeUndefinedOverlay) { return 'Civil Defense (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'd': switch ($symbolTable) { case "\\": return 'DX Spot'; default: if ($includeUndefinedOverlay) { return 'DX Spot'; } else { return null; } } case 'e': switch ($symbolTable) { case "\\": return 'Sleet'; default: if ($includeUndefinedOverlay) { return 'Sleet'; } else { return null; } } case 'f': switch ($symbolTable) { case "\\": return 'Funnel Cloud'; default: if ($includeUndefinedOverlay) { return 'Funnel Cloud'; } else { return null; } } case 'g': switch ($symbolTable) { case "\\": return 'Gale warning flags'; default: if ($includeUndefinedOverlay) { return 'Gale warning flags (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'h': switch ($symbolTable) { case '\\': return 'Store/Ham Store'; case 'F': return 'Hamfest'; case 'H': return 'Home Dept etc..'; default: if ($includeUndefinedOverlay) { return 'Other store (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'i': switch ($symbolTable) { case "\\": return 'Point of interest'; default: if ($includeUndefinedOverlay) { return 'Point of interest (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'j': switch ($symbolTable) { case "\\": return 'Work Zone'; default: if ($includeUndefinedOverlay) { return 'Work Zone'; } else { return null; } } case 'k': switch ($symbolTable) { case '\\': return 'Special Vehicle (SUV)'; case '4': return 'Special Vehicle (4x4)'; case 'A': return 'Special Vehicle (ATV)'; default: if ($includeUndefinedOverlay) { return 'Special Vehicle (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'l': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case 'm': switch ($symbolTable) { case "\\": return 'Milepost'; default: if ($includeUndefinedOverlay) { return 'Milepost'; } else { return null; } } case 'n': switch ($symbolTable) { case "\\": return 'Red Triangle'; default: if ($includeUndefinedOverlay) { return 'Red Triangle (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'o': switch ($symbolTable) { case "\\": return 'Small Circle'; default: if ($includeUndefinedOverlay) { return 'Small Circle (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'p': switch ($symbolTable) { case "\\": return 'Partly Cloudy'; default: if ($includeUndefinedOverlay) { return 'Partly Cloudy'; } else { return null; } } case 'q': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case 'r': switch ($symbolTable) { case "\\": return 'Restrooms'; default: if ($includeUndefinedOverlay) { return 'Restrooms'; } else { return null; } } case 's': switch ($symbolTable) { case '\\': return 'Ship/Boat'; case '6': return 'Ship/Boat (Shipwreck "deep6")'; case 'B': return 'Ship/Boat (Pleasure Boat)'; case 'C': return 'Ship/Boat (Cargo)'; case 'D': return 'Ship/Boat (Diving)'; case 'E': return 'Ship/Boat (Emergency or Medical transport)'; case 'F': return 'Ship/Boat (Fishing)'; case 'H': return 'Ship/Boat (High-speed Craft)'; case 'J': return 'Ship/Boat (Jet Ski)'; case 'L': return 'Ship/Boat (Law enforcement)'; case 'M': return 'Ship/Boat (Miltary)'; case 'O': return 'Ship/Boat (Oil Rig)'; case 'P': return 'Ship/Boat (Pilot Boat)'; case 'Q': return 'Ship/Boat (Torpedo)'; case 'S': return 'Ship/Boat (Search and Rescue)'; case 'T': return 'Ship/Boat (Tug)'; case 'U': return 'Ship/Boat (Underwater ops or submarine)'; case 'W': return 'Ship/Boat (Wing-in-Ground effect or Hovercraft)'; case 'X': return 'Ship/Boat (Passenger, Ferry)'; case 'Y': return 'Ship/Boat (Sailing, large ship)'; default: if ($includeUndefinedOverlay) { return 'Ship/Boat (with overlay ' . $symbolTable . ')'; } else { return null; } } case 't': switch ($symbolTable) { case "\\": return 'Tornado'; default: if ($includeUndefinedOverlay) { return 'Tornado'; } else { return null; } } case 'u': switch ($symbolTable) { case '\\': return 'Truck'; case 'B': return 'Truck (Buldozer/construction/Backhoe)'; case 'G': return 'Truck (Gas)'; case 'P': return 'Truck (Plow or SnowPlow)'; case 'T': return 'Truck (Tanker)'; case 'C': return 'Truck (Chlorine Tanker)'; case 'H': return 'Truck (Hazardous)'; default: if ($includeUndefinedOverlay) { return 'Truck (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'v': switch ($symbolTable) { case "\\": return 'Van'; default: if ($includeUndefinedOverlay) { return 'Van (with overlay ' . $symbolTable . ')'; } else { return null; } } case 'w': switch ($symbolTable) { case "\\": return 'Flooding'; default: if ($includeUndefinedOverlay) { return 'Flooding'; } else { return null; } } case 'x': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case 'y': switch ($symbolTable) { case "\\": return 'Skywarn'; default: if ($includeUndefinedOverlay) { return 'Skywarn'; } else { return null; } } case 'z': switch ($symbolTable) { case '\\': return 'Shelter'; case 'C': return 'Clinic'; case 'G': return 'Government building'; case 'M': return 'Morgue'; case 'T': return 'Triage'; default: if ($includeUndefinedOverlay) { return 'Building (with overlay ' . $symbolTable . ')'; } else { return null; } } case '{': switch ($symbolTable) { case "\\": return 'Fog'; default: if ($includeUndefinedOverlay) { return 'Fog'; } else { return null; } } case '|': switch ($symbolTable) { case "\\": return 'TNC Stream Switch'; default: if ($includeUndefinedOverlay) { return 'TNC Stream Switch'; } else { return null; } } case '}': switch ($symbolTable) { case "\\": return 'No Symbol'; default: if ($includeUndefinedOverlay) { return 'No Symbol'; } else { return null; } } case '~': switch ($symbolTable) { case "\\": return 'TNC Stream Switch'; default: if ($includeUndefinedOverlay) { return 'TNC Stream Switch'; } else { return null; } } } } } /** * Returnes true if we may show data older than 24h * * @return boolean */ function isAllowedToShowOlderData() { $isAllowedToShowOlderData = false; $config = getConfig('application_server'); if (isset($config) && is_array($config)) { if (isset($config['allow_time_travel'])) { if ($config['allow_time_travel'] == '1') { $isAllowedToShowOlderData = true; } } } return $isAllowedToShowOlderData; } /** * Returnes valid view path * * @param {string} $request * @return string */ function getView($request) { if (is_null($request)) return null; $parts = explode("/", trim($request, '/')); if (count($parts) >= 2) { $page = array_pop($parts); $dir = array_pop($parts); if ($page && ($dir == 'views' || $dir == 'station' || $dir == 'account' || $dir == 'events')) { $path = ROOT . '/public/' . $dir . '/' . $page; if (file_exists($path)) return $path; } } return null; } /** * Helper function to return an assoc array containing the bootstrap config * * @param {string} $key * @return string * */ function getBootstrapConfig() { global $bootstrap_config; return $bootstrap_config['bootstrap']; } /** * Helper function to return an assoc array containing website related values from config * * @param {string} $key * @return string * */ function getWebsiteConfig($key) { return getConfig('website', $key); } /** * Helper function to return an assoc array containing backend database related values from config * * @param {string} $key * @return string * */ function getBackendDatabaseConfig($key) { return getConfig('backend_database', $key); } /** * Helper function to return an assoc array containing database related values from config * * @param {string} $key * @return string * */ function getDatabaseConfig($key) { return getConfig('database', $key); } /** * Helper function to return an assoc array containing websocket related values from config * * @param {string} $key * @return string * */ function getWebsocketConfig($key) { return getConfig('application_server', $key); } /** * Helper function to return an assoc array containing SMTP related values from config * * @param {string} $key * @return string * */ function getEmailConfig($key) { return getConfig('email', $key); } /** * Returns an associative array containing related key values from the specified section in the configuration. * * @param string $section The section of the configuration to retrieve values from. * @param string|null $key The specific key to retrieve. If null, the entire section is returned. * @return array|null Returns the value associated with the key, the entire section, or null if not found. */ function getConfig(string $section, ?string $key = null): mixed { global $config; if (isset($config[$section])) { if ($key === null) { return $config[$section]; } if (isset($config[$section][$key])) { return $config[$section][$key]; } } return null; // Explicitly return null if the section or key is not found } /** * Echo a language phrase from $id to the browser * * @param {string} $id - The phrase key * @param {int} $itemCount - Number of items the phrase refers to (for pluralization) * @return none */ function _L($id, $itemCount = 1) { global $language; if (isset($language['en-us']["$id"])) { if ($itemCount == 1) echo $language['en-us'][$id]['s'] ?? 'literal missing'; else echo $language['en-us'][$id]['p'] ?? 'literal missing'; } else echo 'literal missing'; } /** * Returns a language phrase from $id * * @param {string} $id - The phrase key * @param {int} $itemCount - Number of items the phrase refers to (for pluralization) * @return {string} - The phrase from the language */ function _LANG($id, $itemCount = 1) { global $language; if (isset($language['en-us']["$id"])) { if ($itemCount == 1) return $language['en-us'][$id]['s']; else return $language['en-us'][$id]['p']; } else return 'literal missing'; } /** * Convert coordinate to pixel position in heatmap image * * @param {float} $lat * @param {int} $zoom * @param {int} $imageTileSize * @return int */ function getLatPixelCoordinate($lat, $zoom, $imageTileSize) { $pixelGlobeSize = $imageTileSize * pow(2, $zoom); $yPixelsToRadiansRatio = $pixelGlobeSize / (2 * M_PI); $halfPixelGlobeSize = $pixelGlobeSize / 2; $pixelGlobeCenterY = $halfPixelGlobeSize; $degreesToRadiansRatio = 180 / M_PI; $siny = sin($lat * M_PI / 180); # Truncating to 0.9999 effectively limits latitude to 89.189. This is # about a third of a tile past the edge of the world tile. if ($siny < -0.9999) { $siny = -0.9999; } if ($siny > 0.9999) { $siny = 0.9999; } $latY = round($pixelGlobeCenterY + 0.5 * log((1 + $siny) / (1 - $siny)) * -$yPixelsToRadiansRatio); return $latY; } /** * Convert coordinate to pixel position in heatmap image * * @param {float} $lng * @param {int} $zoom * @param {int} $imageTileSize * @return int */ function getLngPixelCoordinate($lng, $zoom, $imageTileSize) { $scale = 1 << $zoom; $lngX = floor($imageTileSize * (0.5 + $lng / 360) * $scale); return $lngX; } /** * Convert pixel position in heatmap image to coordinate * * @param {float} $latPixelCoord * @param {int} $zoom * @param {int} $imageTileSize * @return float */ function getLatFromLatPixelCoordinate($latPixelCoord, $zoom, $imageTileSize) { $pixelGlobeSize = $imageTileSize * pow(2, $zoom); $yPixelsToRadiansRatio = $pixelGlobeSize / (2 * M_PI); $halfPixelGlobeSize = $pixelGlobeSize / 2; $pixelGlobeCenterY = $halfPixelGlobeSize; $degreesToRadiansRatio = 180 / M_PI; $lat = (2 * atan(exp(($latPixelCoord - $pixelGlobeCenterY) / -$yPixelsToRadiansRatio)) - M_PI / 2) * $degreesToRadiansRatio; return $lat; } /** * Convert pixel position in heatmap image to coordinate * * @param {float} $lngPixelCoord * @param {int} $zoom * @param {int} $imageTileSize * @return float */ function getLngFromLngPixelCoordinate($lngPixelCoord, $zoom, $imageTileSize) { $scale = 1 << $zoom; $lng = ((($lngPixelCoord / $scale) / $imageTileSize) - 0.5) * 360; return $lng; } /** * Get the closest timezone from coordinates * Original credit to ben@jp * https://stackoverflow.com/questions/3126878/get-php-timezone-name-from-latitude-and-longitude * * @param {float} $latitude * @param {float} $longitude * @param {string} $countryCode (optional) * @return string */ function getNearestTimezone($latitude, $longitude, $countryCode = '') { if (is_null($latitude) || is_null($longitude)) return null; $countryCode = strtoupper(trim($countryCode)); $timezone_ids = ($countryCode) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $countryCode) : DateTimeZone::listIdentifiers(); if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) { $time_zone = ''; $tz_distance = 0; $timezone_obj = null; //only one identifier? if (count($timezone_ids) == 1) { $time_zone = $timezone_ids[0]; } else { foreach($timezone_ids as $timezone_id) { $timezone = new DateTimeZone($timezone_id); $location = $timezone->getLocation(); $tz_lat = $location['latitude']; $tz_long = $location['longitude']; $theta = $longitude - $tz_long; $distance = (sin(deg2rad($latitude)) * sin(deg2rad($tz_lat))) + (cos(deg2rad($latitude)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta))); $distance = acos($distance); $distance = abs(rad2deg($distance)); if (!$time_zone || $tz_distance > $distance) { $time_zone = $timezone_id; $tz_distance = $distance; $timezone_obj = $timezone; } } } return $timezone_obj; } return null; } /** * Get the maindenhead grid square from latitiude and longitude * * @param {float} $latitude * @param {float} $longitude * @param {int} $precision (optional, min/default = 3, max = 5) * @return string */ define('ORD_A', ord('A')); define('ORD_0', ord('0')); function getMaidenheadFromCoordinates($latitude, $longitude, $precision = 3) { if ($precision < 3 || $precision > 5) $precision = 3; $lat = $latitude + 90; $lon = $longitude + 180; // Pair 1 $gs = chr(ORD_A + floor($lon / 20)) . chr(ORD_A + floor($lat / 10)); $lon = fmod($lon, 20); if ($lon < 0) $lon += 20; $lat = fmod($lat, 10); if ($lat < 0) $lat += 10; // Pair 2 $gs .= chr(ORD_0 + floor($lon / 2)) . chr(ORD_0 + floor($lat / 1)); $lon = fmod($lon, 2); if ($lon < 0) $lon += 2; $lat = fmod($lat, 1); if ($lat < 0) $lat += 1; // Pair 3 $gs .= chr(ORD_A + floor($lon * 12)) . chr(ORD_A + floor($lat * 24)); $lon = fmod($lon, (1/12)); if ($lon < 0) $lon += (1/12); $lat = fmod($lat, (1/24)); if ($lat < 0) $lat += (1/24); // Pair 4 if ($precision > 3) { $gs .= chr(ORD_0 + floor($lon * 120)) . chr(ORD_0 + floor($lat * 240)); $lon = fmod($lon, (1/120)); if ($lon < 0) $lon += (1/120); $lat = fmod($lat, (1/240)); if ($lat < 0) $lat += (1/240); } // Pair 5 if ($precision > 4) { $gs .= chr(ORD_A + floor($lon * 120 * 24)) . chr(ORD_A + floor($lat * 240 * 24)); $lon = fmod($lon, (1/120/24)); if ($lon < 0) $lon += (1/120/24); $lat = fmod($lat, (1/240/24)); if ($lat < 0) $lat += (1/240/24); } return $gs; } // Imported from repository... Object fetch is slower, so we will not use it here function getTelemetryValue($telemetryPacket, $valNumber, $rootTelemetryPacket) { $val = $telemetryPacket["val{$valNumber}"]; $eqns = $rootTelemetryPacket->getEqnsValue($valNumber); $a = $eqns[0]; $b = $eqns[1]; $c = $eqns[2]; if ($a == null && $b == null) { // User has sent us a faulty eqns, just return raw value return $val; } else { $result = 0; if ($a != null) { $result += $a * $val * $val; } if ($b != null) { $result += $b * $val; } if ($c != null) { $result += $c; } return $result; } } /** * Converts degrees to cardinal directions * * @param {int} $degrees * @return string */ function convertDegreesToCardinalDirection($degrees) { if (!isInt($degrees) || $degrees > 360) return ''; return ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'][round($degrees / 22.5)]; } /** * Parses a Mic-E Destination Address * * @param {char} $c * @return array * */ function getInfoForDestinationAddressChar($c) { //0 - 0, 1 - 1 custom, 2 - 1 std $asciiCode = ord($c); $ret = ['latDigit'=> 0, 'message'=> 0, 'isNorth'=> false, 'longOffset'=> 0, 'isWest'=> false]; if ($c >= '0' && $c <= '9') { $ret['latDigit'] = $asciiCode - ord('0'); } else if ($c >= 'A' && $c <= 'J') { $ret['latDigit'] = $asciiCode - ord('A'); $ret['message'] = 1; $ret['isNorth'] = null; $ret['longOffset'] = null; $ret['isWest'] = null; } else if ($c == 'K') { $ret['latDigit'] = 0; $ret['message'] = 1; $ret['isNorth'] = null; $ret['longOffset'] = null; $ret['isWest'] = null; } else if ($c == 'L') { $ret['latDigit'] = 0; $ret['message'] = 0; } else if ($c >= 'P' && $c <= 'Y') { $ret['latDigit'] = $asciiCode - ord('P'); $ret['message'] = 2; $ret['isNorth'] = true; $ret['longOffset'] = 100; $ret['isWest'] = true; } else if ($c == 'Z') { $ret['latDigit'] = 0; $ret['message'] = 2; $ret['isNorth'] = true; $ret['longOffset'] = 100; $ret['isWest'] = true; } return $ret; } const MicEStates = [ 'M0'=> 'off duty', 'M1'=> 'en route', 'M2'=> 'in service', 'M3'=> 'returning', 'M4'=> 'commited', 'M5'=> 'special', 'M6'=> 'priority', 'C0'=> 'custom-0', 'C1'=> 'custom-1', 'C2'=> 'custom-2', 'C3'=> 'custom-3', 'C4'=> 'custom-4', 'C5'=> 'custom-5', 'C6'=> 'custom-6', 'EMERGENCY'=> 'emergency', 'UNKNOWN'=> 'unknown' ]; /** * Gets the Mic-E state (i.e. In Service) from the Mic-E data * Note Message: array, 3 elements, values: 0 - 0, 1 - 1 custom, 2 - 1 std * @param {string} $message * @return {string} */ function getStateFromMicE($message) { $state = null; if (sizeof($message) != 3) return null; // Message array should contain 3 elements if ($message[0] == 0 && $message[1] == 0 && $message[2] == 0) { $state = MicEStates['EMERGENCY']; } else { $isStd = $message[0] == 2 || $message[1] == 2 || $message[2] == 2; $isCustom = $message[0] == 1 || $message[1] == 1 || $message[2] == 1; if ($isStd && $isCustom) { $state = MicEStates['UNKNOWN']; } else { $message[0] = $message[0] == 2 ? 1 : $message[0]; $message[1] = $message[1] == 2 ? 1 : $message[1]; $message[2] = $message[2] == 2 ? 1 : $message[2]; if ($message == [1, 1, 1]) $state = $isStd ? MicEStates['M0'] : MicEStates['C0']; else if ($message == [1, 1, 0]) $state = $isStd ? MicEStates['M1'] : MicEStates['C1']; else if ($message == [1, 0, 1]) $state = $isStd ? MicEStates['M2'] : MicEStates['C2']; else if ($message == [1, 0, 0]) $state = $isStd ? MicEStates['M3'] : MicEStates['C3']; else if ($message == [0, 1, 1]) $state = $isStd ? MicEStates['M4'] : MicEStates['C4']; else if ($message == [0, 1, 0]) $state = $isStd ? MicEStates['M5'] : MicEStates['C5']; else if ($message == [0, 0, 1]) $state = $isStd ? MicEStates['M6'] : MicEStates['C6']; else $state = MicEStates['UNKNOWN']; } } return $state; } /** * Generates a random numerical code $length digits long * * @param {int} $length (Length of the random code to generate, default 6) * @return {string} (Generated code) */ function generateSecureCode($length = 6) { // Generate a {$length} digit random code $code = []; for ($x = 0; $x < $length; ++$x) { $code[] = strval(random_int(0, 9)); } return implode('', $code); } /** * Generates a random binary token $length digits long * * @param {int} $length (Length of the random token to generate, default 20) * @return {string} (Generated code) */ function generateSecureToken($length = 20) { return bin2hex(random_bytes($length)); } /** * Generates a base URL to the site taking into account http vs https and domain name * * @return {string} (Full URL to site including http/https) */ function getWebsiteURL() { return (getWebsiteConfig('cookie_secure') == 1 ? "https" : "http") . "://" . getWebsiteConfig('cookie_domain'); } /** * Outputs to the client a fully rendered navigation menu for the station modal * * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getStationMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'station.php' => ['title' => 'Station Details', 'text' => 'Station'], 'statistics.php' => ['title' => 'Station Statistics', 'text' => 'Statistics'], 'trail.php' => ['title' => 'Trail Chart', 'text' => 'Trail Chart'], 'weather.php' => ['title' => 'Weather Conditions, History, and Graphs', 'text' => 'Weather'], 'telemetry.php' => ['title' => 'Current and Historical Telemetry and Graphs', 'text' => 'Telemetry'], 'raw.php' => ['title' => 'Raw Packets', 'text' => 'Raw Packets'], 'live.php' => ['title' => 'Live Packet Feed', 'text' => 'Live Feed'], 'messages.php' => ['title' => 'Station Messages', 'text' => 'Messages'], 'bulletins.php' => ['title' => 'Station Bulletins', 'text' => 'Bulletins'] ]; return renderMenu('station-menu', '/station/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered navigation menu for the account management modal * * @param {string} $selectedIndexL (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getAccountMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'map_settings.php' => ['title' => 'Map Settings', 'text' => 'Map Settings'], 'site_settings.php' => ['title' => 'Site Settings', 'text' => 'Site Settings'], 'my_stations.php' => ['title' => 'My Stations', 'text' => 'My Stations'], 'favorites.php' => ['title' => 'Favorite Stations', 'text' => 'Favorite Stations'], 'aprs_thursday.php' => ['title' => 'My APRSThursday Profile', 'text' => 'APRSThursday Profile'], 'mapshots.php' => ['title' => 'Map Snapshots (Mapshots)', 'text' => 'Map Snapshots'], 'my_api.php' => ['title' => 'Personal API', 'text' => 'Personal API'], 'account.php' => ['title' => 'My Account', 'text' => 'My Account'] ]; return renderMenu('account-menu', '/account/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered general navigation menu for the account management modal * * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getGeneralMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'about.php' => ['title' => 'More information about this APRS website', 'text' => 'About'], 'faq.php' => ['title' => 'Frequently Asked Questions about APRS', 'text' => 'FAQ'], 'site_statistics.php' => ['title' => 'Site and Database Statistics', 'text' => 'Statistics'], 'changelog.php' => ['title' => 'APRS Site News and Updates', 'text' => 'What\'s New?'], 'server_health.php' => ['title' => 'Check the Health of systems supporting this APRS map.', 'text' => 'Server Status'], 'data_availability.php' => ['title' => 'Check the availability of the data avaiable to the map.', 'text' => 'Data Availability'] ]; if (getWebsiteConfig('aprs_is_status_url')) $pages['aprs_status.php'] = ['title' => 'APRS Server Status and Metrics', 'text' => 'APRS Server Status']; if (getWebsiteConfig('cwop_is_status_url')) $pages['cwop_status.php'] = ['title' => 'CWOP Server Status and Metrics', 'text' => 'CWOP Server Status']; if (getWebsiteConfig('ogn_is_status_url')) $pages['ogn_status.php'] = ['title' => 'OGN Server Status and Metrics', 'text' => 'OGN Server Status']; if (getWebsiteConfig('cbaprs_is_status_url')) $pages['cbaprs_status.php'] = ['title' => 'CBAPRS Server Status and Metrics', 'text' => 'CBAPRS Server Status']; return renderMenu('about-menu', '/views/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered search window navigation menu * * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getSearchMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'search.php' => ['title' => 'Search for a station/object/airplane, etc.', 'text' => 'Station Search'], 'search_grid.php' => ['title' => 'Maidenhead Locator Grid Search', 'text' => 'Maidenhead Grid Search'], 'search_location.php' => ['title' => 'Search for any place (address, business, park) or location', 'text' => 'Location Search'], 'search_license.php' => ['title' => 'Search the FCC and Canadian License Databases', 'text' => 'License Search'] ]; return renderMenu('search-menu', '/views/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered legal (ToS) window navigation menu * * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getLegalMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'contact.php' => ['title' => 'Contact us with questions or to report a problem', 'text' => 'Contact Us'], 'terms_of_service.php' => ['title' => 'Terms of Service Agreement', 'text' => 'Terms of Service'], 'privacy_policy.php' => ['title' => 'Our Commitment to your Privacy', 'text' => 'Privacy Policy'] ]; return renderMenu('legal-menu', '/views/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered #APRSThursday window navigation menu * * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function getAPRSThursdayMenu($selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; $pages = [ 'aprs_thursday.php' => ['title' => 'All about the #APRSThursday Weekly Net', 'text' => 'About #APRSThursday'], 'aprs_thursday_checkins.php' => ['title' => 'View check-ins for the most recent nets.', 'text' => 'Recent Check-Ins'], 'aprs_thursday_statistics.php' => ['title' => 'Historical statistics since the beginning of #APRSThursday.', 'text' => 'Statistics'], 'aprs_thursday_leaderboard.php' => ['title' => 'View the participation leaderboard.', 'text' => 'Leaderboard'] ]; return renderMenu('APRSThursday-menu', '/events/', $pages, $selectedIndex, $urlParams, $extraHTML); } /** * Outputs to the client a fully rendered navigation menu * * @param {string} $selectId (ID of the select tag for the menu) * @param {string} $baseURL (Root path of the items in the menu) * @param {array} $pages (Array of pages to use as menu items) * @param {string} $selectedIndex (Base name of the file being displayed i.e. station.php) * @param {array} $urlParams (Array of parameters to pass in each generated URL) * @param {string} $extraHTML (Extra menu HTML to include at the end of the menu) * @return {string} HTML Menu */ function renderMenu($selectId, $baseURL, $pages, $selectedIndex, $urlParams = [], $extraHTML = null) { global $tdSession; // Mobile Menu $menu = '
Menu: '; // Normal Menu $menu .= '' . $extraHTML . '
'; return $menu; } /** * Outputs to the client a fully rendered navigation menu * * @param {string} $colorName (CSS Color Name) * @param {string} $alpha (Alpha transparency value 0-.99) * @return {string} Complete rgbA CSS tag */ function cssColorToRgbATag($colorName, $alpha) { $cssColors = [ 'black' => '0,0,0', 'silver' => '192, 192, 192', 'gray' => '128, 128, 128', 'white' => '255, 255, 255', 'maroon' => '128, 0, 0', 'red' => '255, 0, 0', 'purple' => '128, 0, 128', 'fuchsia' => '255, 0, 255', 'green' => '0, 128, 0', 'lime' => '0, 255, 0', 'olive' => '128, 128, 0', 'yellow' => '255, 255, 0', 'navy' => '0, 0, 128', 'blue' => '0, 0, 255', 'teal' => '0, 128, 128', 'aqua' => '0, 255, 255' ]; // Return black if no color is specififed if (is_null($colorName) || $colorName == '') return 'rgba(' . $cssColors["black"] . ", $alpha)"; return 'rgba(' . $cssColors["$colorName"] . ", $alpha)"; } /** * Wrapper function to return a station object from various URI critera * * @return {Station} Station object (uninitialized if no station found) */ function getStationFromURI() { if (isset($_GET['c'])) { $name = filter_input(INPUT_GET, 'c', FILTER_DEFAULT); return StationRepository::getInstance()->getObjectByName(strtoupper(trim($name))); } // New ID format with sourceId as the first digit else if (isset($_GET['s'])) { $station_id = filter_input(INPUT_GET, 's', FILTER_VALIDATE_INT, ['options' => ['flags' => FILTER_NULL_ON_FAILURE]]); if (!is_null($station_id)) { $sid = strval($station_id); if (strlen($sid) >= 2) { $source_id = $sid[0]; $station_id = substr($sid, 1); return StationRepository::getInstance()->getObjectById($station_id, $source_id); } } return new Station(0); } // Legacy ID format else { $station_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, ['options' => ['flags' => FILTER_NULL_ON_FAILURE]]); return StationRepository::getInstance()->getObjectById($station_id); } return new Station(0); }