diff --git a/selfdrive/sensord/rawgps/modemdiag.py b/selfdrive/sensord/rawgps/modemdiag.py index 21e39f4f37..cc2bc5b261 100644 --- a/selfdrive/sensord/rawgps/modemdiag.py +++ b/selfdrive/sensord/rawgps/modemdiag.py @@ -1,5 +1,6 @@ import os import time +import select from serial import Serial from crcmod import mkCrcFun from struct import pack, unpack_from, calcsize @@ -7,10 +8,11 @@ from struct import pack, unpack_from, calcsize class ModemDiag: def __init__(self): self.serial = self.open_serial() + self.pend = b'' def open_serial(self): def op(): - return Serial("/dev/ttyUSB0", baudrate=115200, rtscts=True, dsrdtr=True) + return Serial("/dev/ttyUSB0", baudrate=115200, rtscts=True, dsrdtr=True, timeout=0) try: serial = op() except Exception: @@ -22,6 +24,8 @@ class ModemDiag: os.system("sudo chmod 666 /dev/ttyUSB0") serial = op() serial.flush() + serial.reset_input_buffer() + serial.reset_output_buffer() return serial ccitt_crc16 = mkCrcFun(0x11021, initCrc=0, xorOut=0xffff) @@ -45,13 +49,15 @@ class ModemDiag: return payload[:-2] def recv(self): - raw_payload = [] - while 1: - char_read = self.serial.read() - raw_payload.append(char_read) - if char_read.endswith(self.TRAILER_CHAR): - break + # self.serial.read_until makes tons of syscalls! + raw_payload = [self.pend] + while self.TRAILER_CHAR not in raw_payload[-1]: + select.select([self.serial.fd], [], []) + raw = self.serial.read(0x10000) + raw_payload.append(raw) raw_payload = b''.join(raw_payload) + raw_payload, self.pend = raw_payload.split(self.TRAILER_CHAR, 1) + raw_payload += self.TRAILER_CHAR unframed_message = self.hdlc_decapsulate(raw_payload) return unframed_message[0], unframed_message[1:] diff --git a/selfdrive/sensord/rawgps/rawgpsd.py b/selfdrive/sensord/rawgps/rawgpsd.py index cdaac253e7..e0861c0d9a 100755 --- a/selfdrive/sensord/rawgps/rawgpsd.py +++ b/selfdrive/sensord/rawgps/rawgpsd.py @@ -6,18 +6,19 @@ import itertools import math import time from typing import NoReturn -from struct import unpack_from, calcsize +from struct import unpack_from, calcsize, pack import cereal.messaging as messaging from cereal import log from selfdrive.swaglog import cloudlog -from selfdrive.sensord.rawgps.modemdiag import ModemDiag, DIAG_LOG_F, setup_logs +from selfdrive.sensord.rawgps.modemdiag import ModemDiag, DIAG_LOG_F, setup_logs, send_recv from selfdrive.sensord.rawgps.structs import dict_unpacker from selfdrive.sensord.rawgps.structs import gps_measurement_report, gps_measurement_report_sv from selfdrive.sensord.rawgps.structs import glonass_measurement_report, glonass_measurement_report_sv +from selfdrive.sensord.rawgps.structs import oemdre_measurement_report, oemdre_measurement_report_sv from selfdrive.sensord.rawgps.structs import LOG_GNSS_GPS_MEASUREMENT_REPORT, LOG_GNSS_GLONASS_MEASUREMENT_REPORT -from selfdrive.sensord.rawgps.structs import position_report, LOG_GNSS_POSITION_REPORT +from selfdrive.sensord.rawgps.structs import position_report, LOG_GNSS_POSITION_REPORT, LOG_GNSS_OEMDRE_MEASUREMENT_REPORT DEBUG = int(os.getenv("DEBUG", "0"))==1 @@ -71,9 +72,13 @@ def main() -> NoReturn: unpack_glonass_meas, size_glonass_meas = dict_unpacker(glonass_measurement_report, True) unpack_glonass_meas_sv, size_glonass_meas_sv = dict_unpacker(glonass_measurement_report_sv, True) + unpack_oemdre_meas, size_oemdre_meas = dict_unpacker(oemdre_measurement_report, True) + unpack_oemdre_meas_sv, size_oemdre_meas_sv = dict_unpacker(oemdre_measurement_report_sv, True) + log_types = [ LOG_GNSS_GPS_MEASUREMENT_REPORT, LOG_GNSS_GLONASS_MEASUREMENT_REPORT, + LOG_GNSS_OEMDRE_MEASUREMENT_REPORT, ] pub_types = ['qcomGnss'] if int(os.getenv("PUBLISH_EXTERNAL", "0")) == 1: @@ -81,11 +86,18 @@ def main() -> NoReturn: log_types.append(LOG_GNSS_POSITION_REPORT) pub_types.append("gpsLocationExternal") - # disable DPO power savings for more accuracy - os.system("mmcli -m 0 --command='AT+QGPSCFG=\"dpoenable\",0'") - os.system("mmcli -m 0 --location-enable-gps-raw --location-enable-gps-nmea") + # connect to modem diag = ModemDiag() + # NV enable OEMDRE + # TODO: it has to reboot for this to take effect + DIAG_NV_READ_F = 38 + DIAG_NV_WRITE_F = 39 + NV_GNSS_OEM_FEATURE_MASK = 7165 + + opcode, payload = send_recv(diag, DIAG_NV_WRITE_F, pack(' NoReturn: try_setup_logs(diag, log_types) cloudlog.warning("rawgpsd: setup logs done") + # disable DPO power savings for more accuracy + os.system("mmcli -m 0 --command='AT+QGPSCFG=\"dpoenable\",0'") + os.system("mmcli -m 0 --location-enable-gps-raw --location-enable-gps-nmea") + + # enable OEMDRE mode + DIAG_SUBSYS_CMD_F = 75 + DIAG_SUBSYS_GPS = 13 + CGPS_DIAG_PDAPI_CMD = 0x64 + CGPS_OEM_CONTROL = 202 + GPSDIAG_OEMFEATURE_DRE = 1 + GPSDIAG_OEM_DRE_ON = 1 + + # gpsdiag_OemControlReqType + opcode, payload = send_recv(diag, DIAG_SUBSYS_CMD_F, pack(' NoReturn: if log_type not in log_types: continue if DEBUG: - print("%.2f: got log: %x len %d" % (time.time(), log_type, len(log_payload))) - if log_type == LOG_GNSS_POSITION_REPORT: + print("%.4f: got log: %x len %d" % (time.time(), log_type, len(log_payload))) + if log_type == LOG_GNSS_OEMDRE_MEASUREMENT_REPORT: + msg = messaging.new_message('qcomGnss') + + gnss = msg.qcomGnss + gnss.logTs = log_time + gnss.init('drMeasurementReport') + report = gnss.drMeasurementReport + + dat = unpack_oemdre_meas(log_payload) + for k,v in dat.items(): + if k in ["gpsTimeBias", "gpsClockTimeUncertainty"]: + k += "Ms" + if k == "version": + assert v == 2 + elif k == "svCount" or k.startswith("cdmaClockInfo["): + # TODO: should we save cdmaClockInfo? + pass + elif k == "systemRtcValid": + setattr(report, k, bool(v)) + else: + setattr(report, k, v) + + report.init('sv', dat['svCount']) + sats = log_payload[size_oemdre_meas:] + for i in range(dat['svCount']): + sat = unpack_oemdre_meas_sv(sats[size_oemdre_meas_sv*i:size_oemdre_meas_sv*(i+1)]) + sv = report.sv[i] + sv.init('measurementStatus') + for k,v in sat.items(): + if k in ["unkn", "measurementStatus2"]: + pass + elif k == "multipathEstimateValid": + sv.measurementStatus.multipathEstimateIsValid = bool(v) + elif k == "directionValid": + sv.measurementStatus.directionIsValid = bool(v) + elif k == "goodParity": + setattr(sv, k, bool(v)) + elif k == "measurementStatus": + for kk,vv in measurementStatusFields.items(): + setattr(sv.measurementStatus, kk, bool(v & (1<