-
-
Save alastair-dm/263209b54d01209be28828e555fa6628 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
# | |
# load_agps_data | |
# | |
# proof of concept loading of agps data to quectel modem via AT commands | |
from datetime import datetime, timezone | |
import requests | |
import serial | |
at_port = '/dev/ttyUSB2' # ttyUSB2 is the command port on mobian - think they renamed via udev on PmOS | |
agps_url = 'http://xtrapath1.izatcloud.net/xtra.bin' | |
agps_file = 'RAM:xtra.bin' | |
def sendCmd(cmdstring): | |
print(cmdstring) | |
ser.write(f"{cmdstring}\r\n".encode('ascii')) | |
res = [] | |
while True: | |
line = ser.readline() | |
if (line == b''): | |
break | |
else: | |
res.append(line) | |
print(res) | |
return res | |
def setTime(): | |
now = datetime.utcnow().replace(tzinfo=timezone.utc).strftime('%Y/%m/%d,%H:%M:%S') | |
return sendCmd(f"AT+QGPSXTRATIME=0,\"{now}\"") | |
# is ModemManager running? Won't work if it's already got the port | |
print('open serial port') | |
ser = serial.Serial(at_port, timeout=1) | |
print('Check modem will talk to us') | |
res = sendCmd('AT') | |
# FIXME - expect response OK - really ought to check this and other responses | |
## is GPS turned on? If so the later stuff won't work so we'll have to turn it off | |
print('Is GPS turned on?') | |
res = sendCmd('AT+QGPS?') | |
print('Turn off GPS') | |
res = sendCmd('AT+QGPSEND') # this ought to be sent only if the gps was on... | |
print('Is AGPS enabled?') | |
res = sendCmd('AT+QGPSXTRA?') | |
print('Precautionary enabling of AGPS') | |
res = sendCmd('AT+QGPSXTRA=1') | |
print('Is valid AGPS data already loaded?') | |
res = sendCmd('AT+QGPSXTRADATA?') | |
# FIXME: check the returned stuff | |
# actually this check seems unnecessary as the returned value is alway the same | |
# so doesn't help (and doesn't match the docs) | |
# fetch http://xtrapath[1-3].izatcloud.net/xtra(2).bin and upload | |
r = requests.get(agps_url) | |
#FIXME: check r.status_code before doing anything with the data | |
print('download status: ', r.status_code) | |
# precautionary delete of the target file - upload will fail if file name in use | |
print('delete previous AGPS data file') | |
res = sendCmd(f"AT+QFDEL=\"{agps_file}\"") | |
print('upload new AGPS data') | |
res = sendCmd(f"AT+QFUPL=\"{agps_file}\",{len(r.content)}") | |
# FIXME: check res == CONNECT and don't send if it's not | |
ser.write(r.content) | |
res = [] | |
while True: | |
line = ser.readline() | |
if (line == b''): | |
break | |
else: | |
res.append(line) | |
print(res) | |
#FIXME: calculate agps file checksum and check that it matches the response | |
# we'll assume here that local system time is kept accurate either by NTP or | |
# phone network time, so we don't need to fetch SNTP time | |
print('set current UTC time using local system time') | |
res = setTime() | |
print('set AGPS data to file we uploaded') | |
res = sendCmd(f"AT+QGPSXTRADATA=\"{agps_file}\"") | |
print('what does it day about data validity now?') | |
res = sendCmd('AT+QGPSXTRADATA?') | |
print('NOTE: it\'s given us the same response as before, despite having new data uploaded') | |
print('enable gps') | |
res = sendCmd('AT+QGPS=1') | |
print('close serial port') | |
if ser.is_open: | |
ser.close() |
Also if AGPS is NOT enabled (which it probably won't be to begin with) you'll need to restart the modem after running the script as the setting is only activated on restart. I powered down the phone and removed the battery just to be certain, but but there are probably easier ways.
This helped to get a fix within a few minutes. I suggest to combine the shell script into the .py script and to add
"mmcli -m 0 --location-enable-gps-nmea" at the end.
That would make it specific to distros using systemd and ModemManager. If you want to use it as the basis for a better script then feel free, but the better fix would be to get it fixed in the upstream ModemManager, oFono or whatever.
- agps_url should be 'http://xtrapath1.izatcloud.net/xtra2.bin' that changed the date for me to '2001/07/05,06:00:00'
- line 82 should be changed to sendCmd('AT+QGPSXTRA=0') otherwise the module unnecessarily drains the battery
- Any reasoning behind that choice of URL, or just trial and error? I picked the first from Quectel_EC25EC21_GNSS_AT_Commands_Manual_V1.1.pdf but apparently there are others that aren't listed there. Qualcomm customers seem to have been having problems on and off since the gps epoch rollover last October, and there are hints it may depend on firmware version: https://developer.qualcomm.com/comment/17055 More research needed on this, but it might explain why some people have been having more difficulty than others.
- It's proof of concept - I wanted it spewing NMEA data so it was easy to see if it was working. It's far from a proper tool to be using for a variety of reasons. If you're worried about power then you need something like the gpsd hook to turn it on and off on demand anyway. ModemManager needs support adding so we don't need a separate tool, and to allow gps to be turned on when there's no SIM present since the hardware supports that. I'm not clear on how oFono handles this sort of thing.
- Simply trial and error.
- Yes – I know. It was just a little surprising for me to see that the battery was discharged within a day of testing, instead of five days ...
I think there still is a lot to do. The device even sometimes changes state to unavailable for no apparent reason.
Nevertheless nice thing to play with :)
I get an error in line 34:
Traceback (most recent call last): File "/home/diederick/load_agps_data.py", line 34, in <module> ser = serial.Serial(at_port, timeout=1) AttributeError: module 'serial' has no attribute 'Serial'
I get an error in line 34:
Traceback (most recent call last): File "/home/diederick/load_agps_data.py", line 34, in <module> ser = serial.Serial(at_port, timeout=1) AttributeError: module 'serial' has no attribute 'Serial'
I had the same issue because I installed "serial" with pip. You need to install "pyserial" (after uninstalling serial)
Tested on Mobian - I think PmOS remaps the tty names in udev rules, and other distros may too. Again depending on distro you may need to stop whatever service is already using the port before running it, and re-enable after. On Mobian this is ModemManager:
#!/bin/sh
#
# stop ModemManager, run python script to upload AGPS data then restart ModemManager
sudo systemctl disable ModemManager.service
sudo systemctl stop ModemManager.service
/home/mobian/load_agps_data.py
sudo systemctl enable ModemManager.service
sudo systemctl start ModemManager.service
Prerequisites - python3 and its requests and serial libraries:
sudo apt install python3 python3-requests python3-serial