diff --git a/release/files_common b/release/files_common index 4784b369e7..cf985f682d 100644 --- a/release/files_common +++ b/release/files_common @@ -153,6 +153,7 @@ selfdrive/controls/lib/lateral_mpc_lib/* selfdrive/controls/lib/longitudinal_mpc_lib/* system/__init__.py +system/*.py system/hardware/__init__.py system/hardware/base.h diff --git a/release/files_tici b/release/files_tici index 1e5adab498..5d65ef458a 100644 --- a/release/files_tici +++ b/release/files_tici @@ -4,8 +4,6 @@ third_party/snpe/aarch64-ubuntu-gcc7.5/* third_party/mapbox-gl-native-qt/include/* third_party/acados/larch64/** -system/timezoned.py - system/camerad/cameras/camera_qcom2.cc system/camerad/cameras/camera_qcom2.h system/camerad/cameras/camera_util.cc diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index 36d69b03bb..cb6dd8883f 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -49,7 +49,7 @@ procs = [ NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad), PythonProcess("logmessaged", "system.logmessaged", always_run), PythonProcess("micd", "system.micd", iscar), - PythonProcess("timezoned", "system.timezoned", always_run, enabled=not PC), + PythonProcess("timed", "system.timed", always_run, enabled=not PC), PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(not PC or WEBCAM)), NativeProcess("encoderd", "system/loggerd", ["./encoderd"], only_onroad), diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 0161259356..335da73232 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -53,7 +53,7 @@ PROCS = { "selfdrive.tombstoned": 0, "./logcatd": 0, "system.micd": 6.0, - "system.timezoned": 0, + "system.timed": 0, "selfdrive.boardd.pandad": 0, "selfdrive.statsd": 0.4, "selfdrive.navd.navd": 0.4, diff --git a/system/timed.py b/system/timed.py new file mode 100755 index 0000000000..21fb47b680 --- /dev/null +++ b/system/timed.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +import datetime +import os +import subprocess +import time +from typing import NoReturn + +from timezonefinder import TimezoneFinder + +import cereal.messaging as messaging +from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog +from openpilot.system.hardware import AGNOS + + +def set_timezone(timezone): + valid_timezones = subprocess.check_output('timedatectl list-timezones', shell=True, encoding='utf8').strip().split('\n') + if timezone not in valid_timezones: + cloudlog.error(f"Timezone not supported {timezone}") + return + + cloudlog.debug(f"Setting timezone to {timezone}") + try: + if AGNOS: + tzpath = os.path.join("/usr/share/zoneinfo/", timezone) + subprocess.check_call(f'sudo su -c "ln -snf {tzpath} /data/etc/tmptime && \ + mv /data/etc/tmptime /data/etc/localtime"', shell=True) + subprocess.check_call(f'sudo su -c "echo \"{timezone}\" > /data/etc/timezone"', shell=True) + else: + subprocess.check_call(f'sudo timedatectl set-timezone {timezone}', shell=True) + except subprocess.CalledProcessError: + cloudlog.exception(f"Error setting timezone to {timezone}") + + +def set_time(new_time): + diff = datetime.datetime.now() - new_time + if diff < datetime.timedelta(seconds=10): + cloudlog.debug(f"Time diff too small: {diff}") + return + + cloudlog.debug(f"Setting time to {new_time}") + try: + subprocess.run(f"TZ=UTC date -s '{new_time}'", shell=True, check=True) + except subprocess.CalledProcessError: + cloudlog.exception("timed.failed_setting_time") + + +def main() -> NoReturn: + """ + timed has two responsibilities: + - getting the current time + - getting the current timezone + + GPS directly gives time, and timezone is looked up from GPS position. + AGNOS will also use NTP to update the time. + """ + + params = Params() + tf = TimezoneFinder() + + # Restore timezone from param + tz = params.get("Timezone", encoding='utf8') + tf = TimezoneFinder() + if tz is not None: + cloudlog.debug("Restoring timezone from param") + set_timezone(tz) + + sm = messaging.SubMaster(['liveLocationKalman']) + while True: + sm.update(1000) + + llk = sm['liveLocationKalman'] + if not llk.gpsOK or (time.monotonic() - sm.logMonoTime['liveLocationKalman']/1e9) > 0.2: + continue + + # set time + # TODO: account for unixTimesatmpMillis being a (usually short) time in the past + gps_time = datetime.datetime.fromtimestamp(llk.unixTimestampMillis / 1000.) + set_time(gps_time) + + # set timezone + pos = llk.positionGeodetic.value + if len(pos) == 3: + gps_timezone = tf.timezone_at(lat=pos[0], lng=pos[1]) + if gps_timezone is None: + cloudlog.critical(f"No timezone found based on {pos=}") + else: + set_timezone(gps_timezone) + params.put_nonblocking("Timezone", gps_timezone) + + time.sleep(10) + +if __name__ == "__main__": + main() diff --git a/system/timezoned.py b/system/timezoned.py deleted file mode 100755 index 2cfc0076e9..0000000000 --- a/system/timezoned.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -import json -import os -import time -import subprocess -from typing import NoReturn - -from timezonefinder import TimezoneFinder - -from openpilot.common.params import Params -from openpilot.system.hardware import AGNOS -from openpilot.common.swaglog import cloudlog -from openpilot.system.version import get_version - -REQUEST_HEADERS = {'User-Agent': "openpilot-" + get_version()} - - -def set_timezone(valid_timezones, timezone): - if timezone not in valid_timezones: - cloudlog.error(f"Timezone not supported {timezone}") - return - - cloudlog.info(f"Setting timezone to {timezone}") - try: - if AGNOS: - tzpath = os.path.join("/usr/share/zoneinfo/", timezone) - subprocess.check_call(f'sudo su -c "ln -snf {tzpath} /data/etc/tmptime && \ - mv /data/etc/tmptime /data/etc/localtime"', shell=True) - subprocess.check_call(f'sudo su -c "echo \"{timezone}\" > /data/etc/timezone"', shell=True) - else: - subprocess.check_call(f'sudo timedatectl set-timezone {timezone}', shell=True) - except subprocess.CalledProcessError: - cloudlog.exception(f"Error setting timezone to {timezone}") - - -def main() -> NoReturn: - params = Params() - tf = TimezoneFinder() - - # Get allowed timezones - valid_timezones = subprocess.check_output('timedatectl list-timezones', shell=True, encoding='utf8').strip().split('\n') - - timezone = params.get("Timezone", encoding='utf8') - if timezone is not None: - cloudlog.debug("Setting timezone based on param") - set_timezone(valid_timezones, timezone) - - while True: - time.sleep(60) - - location = params.get("LastGPSPosition", encoding='utf8') - - # Find timezone by reverse geocoding the last known gps location - if location is not None: - cloudlog.debug("Setting timezone based on GPS location") - try: - location = json.loads(location) - except Exception: - cloudlog.exception("Error parsing location") - continue - - timezone = tf.timezone_at(lng=location['longitude'], lat=location['latitude']) - if timezone is None: - cloudlog.error(f"No timezone found based on location, {location}") - continue - set_timezone(valid_timezones, timezone) - - -if __name__ == "__main__": - main()