From 02fa9e41da4c336763f781ecb70e25b569e69fab Mon Sep 17 00:00:00 2001 From: Stephan de Wit Date: Tue, 11 Nov 2025 14:08:53 +0100 Subject: [PATCH] network time: status: refactor to MVC (#9361) --- plist | 6 +- .../OPNsense/Ntpd/Api/ServiceController.php | 118 +++++++ .../OPNsense/Ntpd/StatusController.php | 43 +++ .../mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml | 3 +- .../app/models/OPNsense/Ntpd/Menu/Menu.xml | 2 +- .../mvc/app/views/OPNsense/Ntpd/status.volt | 157 ++++++++++ src/opnsense/scripts/ntpd/ntpd_status.php | 168 ++++++++++ .../service/conf/actions.d/actions_ntpd.conf | 5 + src/opnsense/www/js/opnsense_bootgrid.js | 3 +- src/www/status_ntpd.php | 287 ------------------ 10 files changed, 501 insertions(+), 291 deletions(-) create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php create mode 100644 src/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt create mode 100755 src/opnsense/scripts/ntpd/ntpd_status.php create mode 100644 src/opnsense/service/conf/actions.d/actions_ntpd.conf delete mode 100644 src/www/status_ntpd.php diff --git a/plist b/plist index 03f5ed2674..ca25da2e9c 100644 --- a/plist +++ b/plist @@ -423,6 +423,8 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml +/usr/local/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ClientOverwritesController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ExportController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/InstancesController.php @@ -974,6 +976,7 @@ /usr/local/opnsense/mvc/app/views/OPNsense/Kea/leases6.volt /usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt /usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt +/usr/local/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/cso.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/export.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/instances.volt @@ -1269,6 +1272,7 @@ /usr/local/opnsense/scripts/netflow/lib/aggregates/source.py /usr/local/opnsense/scripts/netflow/lib/flowparser.py /usr/local/opnsense/scripts/netflow/lib/parse.py +/usr/local/opnsense/scripts/ntpd/ntpd_status.php /usr/local/opnsense/scripts/openssh/ssh_query.py /usr/local/opnsense/scripts/openvpn/client_connect.php /usr/local/opnsense/scripts/openvpn/client_disconnect.sh @@ -1397,6 +1401,7 @@ /usr/local/opnsense/service/conf/actions.d/actions_kea.conf /usr/local/opnsense/service/conf/actions.d/actions_monit.conf /usr/local/opnsense/service/conf/actions.d/actions_netflow.conf +/usr/local/opnsense/service/conf/actions.d/actions_ntpd.conf /usr/local/opnsense/service/conf/actions.d/actions_openssh.conf /usr/local/opnsense/service/conf/actions.d/actions_openvpn.conf /usr/local/opnsense/service/conf/actions.d/actions_shaper.conf @@ -2505,7 +2510,6 @@ /usr/local/www/services_ntpd_pps.php /usr/local/www/services_opendns.php /usr/local/www/services_router_advertisements.php -/usr/local/www/status_ntpd.php /usr/local/www/status_wireless.php /usr/local/www/system_advanced_admin.php /usr/local/www/system_advanced_firewall.php diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php new file mode 100644 index 0000000000..221c10b6ef --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php @@ -0,0 +1,118 @@ + gettext('The remote NTP server\'s address or reference (IP or hostname)'), + 'refid' => gettext('Reference ID of the server that the remote peer is synchronized to. This may be another server or a hardware device.'), + 'stratum' => gettext('Distance from the top of the time hierarchy (16 = unsynchronized, 1 = hardware device that provides true time)'), + 'type' => gettext('Connection type'), + 'when' => gettext('How many seconds ago the last NTP packet was received from this peer'), + 'poll' => gettext('Poll interval (in seconds) between requests to this peer. Typically increases automatically'), + 'reach' => gettext('An octal value showing an 8-bit shift register of the last 8 reachability checks'), + 'delay' => gettext('Round-trip delay (in milliseconds) to the server. Lower is better'), + 'offset' => gettext('The time difference between your system clock and the server clock (in milliseconds)'), + 'jitter' => gettext('The variation in offset over time (in milliseconds). Lower means a more stable connection') + ]; + } + + private function symbolMetadata() + { + return [ + 'status' => [ + '*' => [ + 'descr' => gettext('The current system peer your machine is syncing time from'), + 'status' => gettext('Active Peer') + ], + '+' => [ + 'descr' => gettext('Candidates that could be used if the primary fails'), + 'status' => gettext('Candidate') + ], + 'o' => [ + 'descr' => gettext('Peer synchronized to a Pulse Per Second signal'), + 'status' => gettext('PPS Peer') + ], + '#' => [ + 'descr' => gettext('A source that is selected as a "backup" in the pool'), + 'status' => gettext('Selected') + ], + '.' => [ + 'descr' => gettext('Peer was considered, but rejected by the intersection algorithm'), + 'status' => gettext('Excess Peer') + ], + 'x' => [ + 'descr' => gettext('The peer is deemed to be delivering incorrect time, this peer is ignored'), + 'status' => gettext('False Ticker') + ], + '-' => [ + 'descr' => gettext('Peer responded, but statistically out of sync with the main cluster'), + 'status' => gettext('Outlier') + ], + ' ' => [ + 'descr' => gettext('Not currently considered'), + 'status' => gettext('Not Considered') + ] + ], + 'connection_type' => [ + 'u' => gettext('Unicast'), + 'b' => gettext('Broadcast'), + 'l' => gettext('Local'), + 'm' => gettext('Multicast'), + 's' => gettext('Symmetric Active, this machine and the peer can provide time to each other'), + 'p' => gettext('This peer was discovered via an NTP pool association'), + ] + ]; + } + + public function metaAction() + { + return [ + 'key' => $this->keyMetadata(), + 'symbols' => $this->symbolMetadata() + ]; + } + + public function gpsAction() + { + return json_decode((new Backend())->configdRun('ntpd status'), true)['gps']; + } + + public function statusAction() + { + $status = json_decode((new Backend())->configdRun('ntpd status'), true); + return $this->searchRecordsetBase($status['ntpq_servers']); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php b/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php new file mode 100644 index 0000000000..a60c88ffbe --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php @@ -0,0 +1,43 @@ +view->pick('OPNsense/Ntpd/status'); + } +} diff --git a/src/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml index 63a09f055a..aec1921ca2 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml @@ -14,7 +14,8 @@ Status: NTP - status_ntpd.php* + /ui/ntpd/status/* + /api/ntpd/service/status diff --git a/src/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml index 4e47826e1a..221b8a11bc 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml @@ -4,7 +4,7 @@ - + diff --git a/src/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt b/src/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt new file mode 100644 index 0000000000..a041d191d4 --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt @@ -0,0 +1,157 @@ +{# + # Copyright (c) 2025 Deciso B.V. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without modification, + # are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # 2. Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + # POSSIBILITY OF SUCH DAMAGE. + #} + + + + + +
+ + + + + + + + + + + + + + + + +
{{ lang._('Status') }}{{ lang._('Server') }}{{ lang._('Ref ID') }}{{ lang._('Stratum') }}{{ lang._('Type') }}{{ lang._('When') }}{{ lang._('Poll') }}{{ lang._('Reach') }}{{ lang._('Delay') }}{{ lang._('Offset') }}{{ lang._('Jitter') }}
+ +
+
+

GPS Information

+
+
+
+
diff --git a/src/opnsense/scripts/ntpd/ntpd_status.php b/src/opnsense/scripts/ntpd/ntpd_status.php new file mode 100755 index 0000000000..78cf0eb253 --- /dev/null +++ b/src/opnsense/scripts/ntpd/ntpd_status.php @@ -0,0 +1,168 @@ +#!/usr/local/bin/php + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +require_once('script/load_phalcon.php'); + +use OPNsense\Core\Config; + +$result = []; + +exec("/usr/local/sbin/ntpq -pnw", $ntpq_output); +$ntpq_servers = []; +$server = []; +foreach (array_slice($ntpq_output, 3) as $line) { + if (empty($server['status'])) { + $server['status'] = substr($line, 0, 1); + } + $line = substr($line, 1); + $peerinfo = preg_split('/\s+/', $line); + if (empty($server['server'])) { + $server['server'] = $peerinfo[0]; + } + if (empty($peerinfo[1])) { + $server = []; + continue; + } + $server['refid'] = $peerinfo[1]; + $server['stratum'] = $peerinfo[2]; + $server['type'] = $peerinfo[3]; + $server['when'] = $peerinfo[4]; + $server['poll'] = $peerinfo[5]; + $server['reach'] = $peerinfo[6]; + $server['delay'] = $peerinfo[7]; + $server['offset'] = $peerinfo[8]; + $server['jitter'] = $peerinfo[9]; + + if ($server['type'] === 'p') { + $server['server'] = gettext('DNS Pool'); + } + + $ntpq_servers[] = $server; + $server = []; +} + +$result['ntpq_servers'] = $ntpq_servers; +$result['gps'] = []; + +function nmeaGeoParts(?string $val, ?string $dir): ?array { + if ($val === null || $dir === null) return null; + if (!preg_match('/^\d+(\.\d+)?$/', $val)) return null; + + [$int, $frac] = array_pad(explode('.', $val, 2), 2, '0'); + $degStr = substr($int, 0, max(strlen($int) - 2, 0)); + $minStr = substr($int, -2) . '.' . $frac; + + $deg = ($degStr === '' ? 0 : (int)$degStr); + $min = (float)$minStr; + $dir = strtoupper($dir); + $sign = ($dir === 'S' || $dir === 'W') ? -1 : 1; + + $dec = $sign * ($deg + $min / 60.0); + + return ['dec' => $dec, 'deg' => $deg, 'min' => $min, 'dir' => $dir]; +} + +exec("/usr/local/sbin/ntpq -c clockvar 2>/dev/null", $ntpq_clockvar_output); +foreach ($ntpq_clockvar_output as $line) { + if (strncmp($line, "timecode=", 9) !== 0) continue; + if (!preg_match('/"([^"]+)"/', $line, $m)) continue; + + $vars = explode(',', $m[1]); + $type = $vars[0] ?? ''; + $gps = ['sentence' => $type]; + + switch ($type) { + case '$GPRMC': { + $gps['ok'] = (($vars[2] ?? '') === 'A'); + + $lat = nmeaGeoParts($vars[3] ?? null, $vars[4] ?? null); + $lon = nmeaGeoParts($vars[5] ?? null, $vars[6] ?? null); + break; + } + case '$GPGGA': { + $gps['ok'] = $vars[6] ?? null; + $gps['alt'] = $vars[9] ?? null; + $gps['alt_unit'] = $vars[10] ?? null; + $gps['sat'] = $vars[7] ?? null; + + $lat = nmeaGeoParts($vars[2] ?? null, $vars[3] ?? null); + $lon = nmeaGeoParts($vars[4] ?? null, $vars[5] ?? null); + break; + } + case '$GPGLL': { + $gps['ok'] = (($vars[6] ?? '') === 'A'); + + $lat = nmeaGeoParts($vars[1] ?? null, $vars[2] ?? null); + $lon = nmeaGeoParts($vars[3] ?? null, $vars[4] ?? null); + break; + } + default: + $lat = $lon = null; + } + + // Merge parsed lat/lon parts if present + if ($lat) { + $gps += [ + 'lat' => $lat['dec'], + 'lat_deg' => $lat['deg'], + 'lat_min' => $lat['min'], + 'lat_dir' => $lat['dir'], + ]; + } + if ($lon) { + $gps += [ + 'lon' => $lon['dec'], + 'lon_deg' => $lon['deg'], + 'lon_min' => $lon['min'], + 'lon_dir' => $lon['dir'], + ]; + } + + $gps = array_filter($gps, static fn($v) => $v !== null && $v !== ''); + $result['gps'] = array_replace($result['gps'], $gps); +} + +$cfg = Config::getInstance()->object(); + +if (!empty($cfg->ntpd->gps->type) && (string)$cfg->ntpd->gps->type == 'SureGPS' && isset($result['gps']['ok'])) { + // GSV message is only enabled by init commands in services_ntpd_gps.php for SureGPS board + $gpsport = fopen("/dev/gps0", "r+"); + while ($gpsport) { + $buffer = fgets($gpsport); + if (substr($buffer, 0, 6) == '$GPGSV') { + $gpgsv = explode(',',$buffer); + $result['gps']['gps_satview'] = $gpgsv[3]; + break; + } + } +} + +echo json_encode($result); diff --git a/src/opnsense/service/conf/actions.d/actions_ntpd.conf b/src/opnsense/service/conf/actions.d/actions_ntpd.conf new file mode 100644 index 0000000000..a74254cce3 --- /dev/null +++ b/src/opnsense/service/conf/actions.d/actions_ntpd.conf @@ -0,0 +1,5 @@ +[status] +command:/usr/local/opnsense/scripts/ntpd/ntpd_status.php +parameters: +type:script_output +message:Show ntpd status diff --git a/src/opnsense/www/js/opnsense_bootgrid.js b/src/opnsense/www/js/opnsense_bootgrid.js index aa6a5082e0..207725148b 100644 --- a/src/opnsense/www/js/opnsense_bootgrid.js +++ b/src/opnsense/www/js/opnsense_bootgrid.js @@ -378,7 +378,8 @@ class UIBootgrid { let def = cell.getColumn().getDefinition(); let column = { id: def.field, - visible: def.visible + visible: def.visible, + title: def.title }; onRendered(() => { diff --git a/src/www/status_ntpd.php b/src/www/status_ntpd.php deleted file mode 100644 index 008b816d15..0000000000 --- a/src/www/status_ntpd.php +++ /dev/null @@ -1,287 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -require_once("guiconfig.inc"); -require_once("interfaces.inc"); - - exec("/usr/local/sbin/ntpq -pnw | /usr/bin/tail +3", $ntpq_output); - $ntpq_servers = array(); - $server = array(); - foreach ($ntpq_output as $line) { - $status = gettext('Unknown'); - switch (substr($line, 0, 1)) { - case ' ': - $status = gettext('Unreach/Pending'); - break; - case '*': - $status = gettext('Active Peer'); - break; - case '+': - $status = gettext('Candidate'); - break; - case 'o': - $status = gettext('PPS Peer'); - break; - case '#': - $status = gettext('Selected'); - break; - case '.': - $status = gettext('Excess Peer'); - break; - case 'x': - $status = gettext('False Ticker'); - break; - case '-': - $status = gettext('Outlier'); - break; - } - if (empty($server['status'])) { - $server['status'] = $status; - } - $line = substr($line, 1); - $peerinfo = preg_split('/\s+/', $line); - if (empty($server['server'])) { - $server['server'] = $peerinfo[0]; - } - if (empty($peerinfo[1])) { - continue; - } - $server['refid'] = $peerinfo[1]; - $server['stratum'] = $peerinfo[2]; - $server['type'] = $peerinfo[3]; - $server['when'] = $peerinfo[4]; - $server['poll'] = $peerinfo[5]; - $server['reach'] = $peerinfo[6]; - $server['delay'] = $peerinfo[7]; - $server['offset'] = $peerinfo[8]; - $server['jitter'] = $peerinfo[9]; - $ntpq_servers[] = $server; - $server = array(); - } - - exec("/usr/local/sbin/ntpq -c clockvar", $ntpq_clockvar_output); - foreach ($ntpq_clockvar_output as $line) { - if (substr($line, 0, 9) == "timecode=") { - $tmp = explode('"', $line); - $tmp = $tmp[1]; - $gps_vars = explode(',', $tmp); - if (substr($tmp, 0, 6) == '$GPRMC') { - if (is_numeric($gps_vars[3]) && is_numeric($gps_vars[5])) { - list ($gps_lat_deg, $gps_lat_min) = explode('.', $gps_vars[3]); - $gps_lat_min = substr($gps_lat_deg, -2) .".". $gps_lat_min; - $gps_lat_deg = substr($gps_lat_deg, 0, strlen($gps_lat_deg) - 2); - $gps_lat_min /= 60.0; - $gps_lat = $gps_lat_deg + $gps_lat_min; - $gps_lat_dir = $gps_vars[4]; - $gps_lat = $gps_lat * ($gps_lat_dir == 'N' ? 1 : -1); - - list ($gps_lon_deg, $gps_lon_min) = explode('.', $gps_vars[5]); - $gps_lon_min = substr($gps_lon_deg, -2) .".". $gps_lon_min; - $gps_lon_deg = substr($gps_lon_deg, 0, strlen($gps_lon_deg) - 2); - $gps_lon_min /= 60.0; - $gps_lon = $gps_lon_deg + $gps_lon_min; - $gps_lon_dir = $gps_vars[6]; - $gps_lon = $gps_lon * ($gps_lon_dir == 'E' ? 1 : -1); - } - - $gps_ok = $gps_vars[2] == 'A'; - } elseif (substr($tmp, 0, 6) == '$GPGGA') { - if (is_numeric($gps_vars[2]) && is_numeric($gps_vars[4])) { - list ($gps_lat_deg, $gps_lat_min) = explode('.', $gps_vars[2]); - $gps_lat_min = substr($gps_lat_deg, -2) .".". $gps_lat_min; - $gps_lat_deg = substr($gps_lat_deg, 0, strlen($gps_lat_deg) - 2); - $gps_lat_min /= 60.0; - $gps_lat = $gps_lat_deg + $gps_lat_min; - $gps_lat_dir = $gps_vars[3]; - $gps_lat = $gps_lat * ($gps_lat_dir == 'N' ? 1 : -1); - - list ($gps_lon_deg, $gps_lon_min) = explode('.', $gps_vars[4]); - $gps_lon_min = substr($gps_lon_deg, -2) .".". $gps_lon_min; - $gps_lon_deg = substr($gps_lon_deg, 0, strlen($gps_lon_deg) - 2); - $gps_lon_min /= 60.0; - $gps_lon = $gps_lon_deg + $gps_lon_min; - $gps_lon_dir = $gps_vars[5]; - $gps_lon = $gps_lon * ($gps_lon_dir == 'E' ? 1 : -1); - - } - - $gps_ok = $gps_vars[6]; - $gps_alt = $gps_vars[9]; - $gps_alt_unit = $gps_vars[10]; - $gps_sat = $gps_vars[7]; - } elseif (substr($tmp, 0, 6) == '$GPGLL') { - if (is_numeric($gps_vars[1]) && is_numeric($gps_vars[3])) { - list ($gps_lat_deg, $gps_lat_min) = explode('.', $gps_vars[1]); - $gps_lat_min = substr($gps_lat_deg, -2) .".". $gps_lat_min; - $gps_lat_deg = substr($gps_lat_deg, 0, strlen($gps_lat_deg) - 2); - $gps_lat_min /= 60.0; - $gps_lat = $gps_lat_deg + $gps_lat_min; - $gps_lat_dir = $gps_vars[2]; - $gps_lat = $gps_lat * ($gps_lat_dir == 'N' ? 1 : -1); - - list ($gps_lon_deg, $gps_lon_min) = explode('.', $gps_vars[3]); - $gps_lon_min = substr($gps_lon_deg, -2) .".". $gps_lon_min; - $gps_lon_deg = substr($gps_lon_deg, 0, strlen($gps_lon_deg) - 2); - $gps_lon_min /= 60.0; - $gps_lon = $gps_lon_deg + $gps_lon_min; - $gps_lon_dir = $gps_vars[4]; - $gps_lon = $gps_lon * ($gps_lon_dir == 'E' ? 1 : -1); - - } - - $gps_ok = $gps_vars[6] == 'A'; - } - } - } - -if (isset($config['ntpd']['gps']['type']) && ($config['ntpd']['gps']['type'] == 'SureGPS') && isset($gps_ok)) { - //GSV message is only enabled by init commands in services_ntpd_gps.php for SureGPS board - $gpsport = fopen("/dev/gps0", "r+"); - while ($gpsport) { - $buffer = fgets($gpsport); - if (substr($buffer, 0, 6) == '$GPGSV') { - $gpgsv = explode(',',$buffer); - $gps_satview = $gpgsv[3]; - break; - } - } -} - -$service_hook = 'ntpd'; -include("head.inc"); -?> - - - -
-
-
-
-
-
-

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - - - - ' . gettext("Clock Altitude") . ''; $gps_goo_lnk++;}?> - ' . gettext("Satellites") . ''; $gps_goo_lnk++;}?> - - - - - - - - - - - - - - - ' . $gps_alt . ' ' . $gps_alt_unit . '';}?> - - - - - - - - -
(° ) (° ) - -
Google Maps Link
- -
-
-
-
-
-
-