Quectel.Gps¶
- class Quectel.Gps(gpsport='/dev/ttyUSB1', portwrite='/dev/ttyUSB3', baud=115200)¶
GPS fix acquisition using a Quectel/Telit modem’s GNSS engine.
Parses NMEA sentences (
GPGGA,GPRMC) from the modem’s dedicated GPS serial port (e.g./dev/ttyUSB1) and, if no satellite fix is obtained withinMAX_GSM_TIMEOUT, falls back to GSM cell-tower geolocation through the Google Geolocation API (AT+QENG="servingcell"on Quectel,AT#SERVINFOon Telit). Optionally injects Qualcomm xTRA assistance data viaxOneProcess()to shorten time-to-first-fix.- Variables:
port (
str) – Serial device path for GPS NMEA output.portwrite (
str) – Serial device path for modem AT commands.module_name (
str | None) – Detected vendor ("Quectel"or"Telit").gpsserial (
serial.Serial | None) – Active GPS NMEA serial connection.modelserial (
serial.Serial | None) – Active AT-command serial connection used for AT+QGPS / AT$GPSP / AT+QENG queries.baud (
int) – UART baud rate for both ports.URL (
str) – Base URL for Qualcomm xTRA file download.filename (
str) – Local filename of the xTRA binary.one_xtra_time (
str) – UTC timestamp string injected viaAT+QGPSXTRATIME.GOOGLELOCATION (
str) – Endpoint of the Google Geolocation v1 API.TIMEOUT (
int) – Generic per-operation timeout in milliseconds.MAX_GSM_TIMEOUT (
int) – Window in ms after which GSM fallback fires (2 minutes by default).ENABLE_MODULE_TIMEOUT (
int) – Window in ms after which the GNSS engine is re-enabled if no fix has been obtained (30 minutes).
Example
>>> from drivers.Quectel.Quectel import Gps >>> gps = Gps() >>> gps.initialize(xone=0) >>> gps.getSensorData() {'status': True, 'data': {'GPRMC': {...}}}
Initialise the GPS object with fallback serial-port paths.
Does not open any port –
initialize()/enable_module()perform the actual device discovery and port binding. The GSM fallback timer is seeded from the constructor so the cell-tower fallback only kicks in afterMAX_GSM_TIMEOUT.- Parameters:
- Returns:
Constructor.
- Return type:
None
- Raises:
None – No serial I/O happens here.
Example
>>> from drivers.Quectel.Quectel import Gps >>> gps = Gps("/dev/ttyUSB1", "/dev/ttyUSB3", 115200)
Note
No AT command issued; pairs with
Modem.__init__()which opens the AT port for the connectivity side.- checkFileUpdated() None | bool¶
Verify that the xTRA file injection actually completed.
Calls
checkXtraData()and compares the returned validity duration against the locally stored injection timestampone_xtra_time. Logging only – the caller is expected to treat the boolean as a soft indicator.- Parameters:
None.
- Returns:
Truewhen the modem reports a non-empty validity field,Falsewhen an exception is caught, orNoneif the field was empty.- Return type:
None | bool
- Raises:
None – All exceptions are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize(xone=1) >>> g.checkFileUpdated() True
Note
AT command issued indirectly through
checkXtraData():AT+QGPSXTRADATA?.
- checkXOneData() bool¶
Check whether xTRA data is current; refresh it if expired.
Re-opens
modelserial, enables xTRA mode withAT+QGPSXTRA=1, queries the validity viacheckXtraData()and branches:" 0"– expired; callsupdateXOne()to redownload and inject." 10080"– still valid (one week); no-op.
- Parameters:
None.
- Returns:
Trueonce the check/update cycle has finished.- Return type:
- Raises:
None – All exceptions are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize(xone=1) >>> g.checkXOneData() True
Note
AT commands:
AT+QGPSXTRA=1(enable xTRA),AT+QGPSXTRADATA?(validity query, viacheckXtraData()).
- checkXtraData() list[str]¶
Query the modem for the current xTRA validity status.
Sends
AT+QGPSXTRADATA?and reads the response viareadAT(). The reply has the form+QGPSXTRADATA: <duration>,<date>– the method splits at the first comma and again at the colon to surface the validity duration (in minutes) on index1of the returned list.- Parameters:
None.
- Returns:
The colon-split prefix of the response, where index
0is"+QGPSXTRADATA"and index1is the validity duration (a leading-space-padded integer such as" 10080").- Return type:
- Raises:
None – Errors are logged inside
readAT().
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize() >>> g.checkXtraData() ['+QGPSXTRADATA', ' 10080']
Note
AT command:
AT+QGPSXTRADATA?– Query Validity of xTRA Data.
- decode(coord: str) str¶
Convert an NMEA coordinate string to a degrees/minutes representation.
Splits the
DDDMM.MMMM(orDDMM.MMMM) NMEA token at the decimal point, treats the last two digits of the integer portion as whole minutes and everything before that as the degree count, then re-assembles a human-readable string. Consumed byparseGPS()before the final conversion to signed decimal degrees.- Parameters:
coord (
str) – NMEA coordinate token, e.g."2302.290121"for 23 degrees 02.290121 minutes.- Returns:
Coordinate rendered as
"DD deg MM.MMMM min".- Return type:
- Raises:
IndexError – If
coorddoes not contain a decimal point.
Example
>>> from drivers.Quectel.Quectel import Gps >>> Gps().decode("2302.290121") '23 deg 02.290121 min'
Note
Not an AT command – pure NMEA-0183 coordinate formatting helper.
- enable_module() None¶
Power on the GNSS engine via vendor-specific AT commands.
Seeds the GNSS re-enable timer, runs
identify_module()to bind the correct ports, opensgpsserial, and probes for already-streaming NMEA. If sentences are flowing the method returns early; otherwise it dispatches by vendor:Quectel: opens the AT port and writes
AT+QGPS=1.Telit: switches to command mode with
+++, queriesAT$GPSP?, and ifGPSP: 0(off) sendsAT$GPSP=1andAT$GPSNMUN=2,1,1,1,1,1,1to start unsolicited NMEA output.
- Parameters:
None.
- Returns:
All status reporting is logged through
context_logger.- Return type:
None
- Raises:
None – Each
serial.Serialinteraction is wrapped in its own
:raises try`/
exceptand :py:class:`logged.:Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.enable_module()
Note
AT commands:
AT+QGPS=1(Quectel),AT$GPSP=1/AT$GPSNMUN(Telit).
- getGSMLocation(parse_data: dict) None¶
Approximate a location from serving-cell info via Google Geolocation.
Re-opens
modelserial, queries the modem for serving-cell parameters (AT+QENG="servingcell"on Quectel,AT#SERVINFOon Telit), assembles an LTE{cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode, signalStrength}payload and POSTs it to the Google Geolocation v1 endpoint stored inGOOGLELOCATION. On HTTP 200 the resulting latitude/longitude are written underparse_data["GPRMC"].- Parameters:
parse_data (
dict) – Mutable dictionary updated withlat/lonkeys nested inside"GPRMC".- Returns:
Side effects only.
- Return type:
None
- Raises:
None – All serial, JSON and
requestserrors are caught and
:raises logged via
context_logger.:Example
>>> from drivers.Quectel.Quectel import Gps >>> bucket = {} >>> Gps().getGSMLocation(bucket) >>> bucket["GPRMC"] {'lat': 23.038, 'lon': 72.453}
Note
AT commands:
AT+QENG="servingcell"(Quectel) andAT#SERVINFO(Telit). Network request: Google Geolocation v1 (POST /geolocation/v1/geolocate).
- getSensorData() dict | None¶
Acquire a GPS fix and return a unified location dictionary.
Reads NMEA lines from
gpsserialand feeds each one toparseGPS()until either a valid GPRMC fix is obtained or the per-callTIMEOUTwatchdog fires. If the GSM-fallback windowMAX_GSM_TIMEOUThas elapsed without a fix the method falls back togetGSMLocation()and clears the_gsmFlag. If still nothing is parsed andENABLE_MODULE_TIMEOUThas elapsed,enable_module()is re-invoked to restart the GNSS engine.- Parameters:
None.
- Returns:
{"status": bool, "data": dict}wherestatusisTrueonly when an NMEA-derived fix was obtained, anddatacontains the parsedGPRMCand/orGPGGAsub-dictionaries (or just the GSM-fallbackGPRMC.lat/lonkeys).- Return type:
dict | None
- Raises:
None – All errors are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize() >>> g.getSensorData() {'status': True, 'data': {'GPRMC': {...}, 'GPGGA': {...}}}
Note
AT commands indirectly involved:
AT+QGPS=1/AT$GPSP=1for engine power,AT+QENG="servingcell"/AT#SERVINFOfor GSM fallback. The companion moduledrivers.Quectel.xOnecan be used to prime xTRA data and shorten time-to-first-fix observed inside this loop.
- identify_module() None | Literal['Quectel'] | Literal['Telit']¶
Auto-detect the modem vendor and bind both GPS and AT serial ports.
Polls
DeviceDetector.scan_gsm_devices()up to three times. On a successful Quectel/Telit match the discoveredgpsandmodemnamed ports are assigned toportandportwriterespectively; missing entries are filled with safe defaults (/dev/ttyUSB1//dev/ttyUSB2).- Parameters:
None.
- Returns:
Detected vendor tag or
Noneif no module was found after three retries.- Return type:
None | Literal[“Quectel”] | Literal[“Telit”]
- Raises:
None – All scan errors are swallowed and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.identify_module() 'Quectel'
Note
Same descriptor-based detection as
Modem.identify_module()but additionally maps thegpsport; no AT command issued.
- initialize(xone: int = 0, xpath: str = 'http://xtrapath6.izatcloud.net/', xname: str = 'xtra3grc.bin') None¶
Enable the GNSS engine and (optionally) inject Qualcomm xTRA data.
Stores
xpathandxnameon the instance, callsenable_module()to power the GNSS engine on, and – whenxoneis set to1– triggerscheckXOneData()to refresh the xTRA assistance file.- Parameters:
- Returns:
None.
- Raises:
serial.SerialException – Propagated from
enable_module()when the GPS port cannot be opened.
Example
>>> from drivers.Quectel.Quectel import Gps >>> gps = Gps() >>> gps.initialize(xone=1)
Note
Wraps the Quectel
AT+QGPS=1/AT+QGPSXTRA=1sequence and the TelitAT$GPSP=1/AT$GPSNMUNsequence. The xTRA workflow mirrors the standalone script indrivers.Quectel.xOne.
- millis()¶
Return the current wall-clock time in milliseconds.
- Parameters:
None.
- Returns:
Integer millisecond timestamp derived from
time.time().- Return type:
- Raises:
None. –
Example
>>> from drivers.Quectel.Quectel import Gps >>> Gps().millis() 1721650000123
Note
Internal helper, mirrors
Modem.millis(). Not an AT command.
- parseGPS(data: str, parse_data: dict) bool¶
Parse an NMEA sentence and merge the result into
parse_data.Attempts to parse the line with
pynmea2.parse()and, when the identifier isGPGGA, stores the satellite count underparse_data["GPGGA"]["num_sats"]. If pynmea2 fails or the sentence isGPRMC, falls back to manual comma-split parsing to extract UTC time, latitude/longitude (degrees + decimal minutes), speed, course over ground, date and magnetic variation. Latitude / longitude are converted to signed decimal degrees (negative for South / West) and rounded to 8 decimal places.- Parameters:
- Returns:
Trueif a valid GPGGA or GPRMC sentence was parsed and written toparse_data;Falseotherwise (invalid line, missing fix flag"V", or parse exception).- Return type:
- Raises:
None – Both the pynmea2 path and the manual fallback are wrapped
:raises in
try/except`and :py:class:`logged.:Example
>>> from drivers.Quectel.Quectel import Gps >>> out = {} >>> Gps().parseGPS("$GPGGA,...", out) True
Note
Not an AT command – pure NMEA-0183 sentence handling.
- readAT() str¶
Read the echo line plus the response line from
modelserial.Many Quectel AT helpers in this class fire a write-then-read pair;
readATconsumes the echoed command line (logged at INFO) and returns the next line, which is the actual response. Used heavily by the xTRA helpers (xOneProcess(),updateXOne(),checkXtraData(),checkXOneData()).- Parameters:
None.
- Returns:
The response (second) line decoded as UTF-8.
- Return type:
- Raises:
serial.SerialException – If the underlying port has been closed or yanked.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize() >>> g.readAT() 'OK\r\n'
Note
Not an AT command itself – pure serial-read helper. The equivalent in the standalone
drivers.Quectel.xOnescript is an inlineport.readline()pair.
- updateXOne() bool¶
Run the full xTRA assistance-data update workflow.
Thin wrapper around
xOneProcess()that guaranteesmodelserialis open before the work begins and closed in thefinallyclause regardless of outcome.- Parameters:
None.
- Returns:
Trueonce the update procedure has finished (independent of the inner success flag, which is logged).- Return type:
- Raises:
None – Internal exceptions are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize() >>> g.updateXOne() True
Note
AT commands issued indirectly via
xOneProcess():AT+QGPSEND,AT+QGPSXTRA,AT+QFUPL,AT+QGPSXTRATIME,AT+QGPSXTRADATA.
- xOneProcess() bool¶
Download and inject Qualcomm xTRA GPS assistance data into the modem.
End-to-end xTRA workflow:
AT+QGPSEND– stop the running GNSS session.AT+QGPSXTRA=1– enable xTRA mode.AT+QFDEL="RAM:<file>"– delete any stale copy.AT+QFUPL="RAM:<file>",<size>,60– upload the binary.AT+QGPSXTRATIME=0,"<utc>",1,1,3500– set the xTRA time.AT+QGPSXTRADATA="RAM:<file>"– inject the data.AT+QGPSXTRADATA?+AT– verify and idle.
- Parameters:
None.
- Returns:
Trueif every step completed without raising,Falseif any AT call or HTTP fetch raised an exception.- Return type:
- Raises:
None – All exceptions are caught and logged.
Example
>>> from drivers.Quectel.Quectel import Gps >>> g = Gps() >>> g.initialize(xone=0) >>> g.xOneProcess() True
Note
Mirrors the standalone procedure in
drivers.Quectel.xOne. The order of AT commands matches the QuectelAN_GPSXtraapplication note. PRE-REQUISITES: remove GPS from main loop config and removeSGsignal-strength mapping from config before running.
- ENABLE_MODULE_TIMEOUT = 1800000¶
- GOOGLELOCATION = 'https://www.googleapis.com/geolocation/v1/geolocate?key=AIzaSyB29IwvmHfSf7Sa94IviBKp4UbWhwdn_DQ'¶
- MAX_GSM_TIMEOUT = 120000¶
- TIMEOUT = 5000¶
- URL = ''¶
- _counter = 0¶
- _enableTimer = None¶
- _gsmFlag = True¶
- _gsmTimer = 0¶
- _timeout_ = 5¶
- baud = 115200¶
- filename = ''¶
- gpsserial = None¶
- modemserial = None¶
- one_xtra_time = ''¶
- timeout = 15¶
- xone = 0¶