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 within MAX_GSM_TIMEOUT, falls back to GSM cell-tower geolocation through the Google Geolocation API (AT+QENG="servingcell" on Quectel, AT#SERVINFO on Telit). Optionally injects Qualcomm xTRA assistance data via xOneProcess() 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 via AT+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 after MAX_GSM_TIMEOUT.

Parameters:
  • gpsport (str) – Default GPS NMEA serial port. Defaults to /dev/ttyUSB1.

  • portwrite (str) – Default modem AT-command serial port. Defaults to /dev/ttyUSB3.

  • baud (int) – UART baud rate for both ports. Defaults to 115200.

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 timestamp one_xtra_time. Logging only – the caller is expected to treat the boolean as a soft indicator.

Parameters:

None.

Returns:

True when the modem reports a non-empty validity field, False when an exception is caught, or None if 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 with AT+QGPSXTRA=1, queries the validity via checkXtraData() and branches:

  • " 0" – expired; calls updateXOne() to redownload and inject.

  • " 10080" – still valid (one week); no-op.

Parameters:

None.

Returns:

True once the check/update cycle has finished.

Return type:

bool

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, via checkXtraData()).

checkXtraData() list[str]

Query the modem for the current xTRA validity status.

Sends AT+QGPSXTRADATA? and reads the response via readAT(). 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 index 1 of the returned list.

Parameters:

None.

Returns:

The colon-split prefix of the response, where index 0 is "+QGPSXTRADATA" and index 1 is the validity duration (a leading-space-padded integer such as " 10080").

Return type:

list[str]

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 (or DDMM.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 by parseGPS() 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:

str

Raises:

IndexError – If coord does 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, opens gpsserial, 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 +++, queries AT$GPSP?, and if GPSP: 0 (off) sends AT$GPSP=1 and AT$GPSNMUN=2,1,1,1,1,1,1 to start unsolicited NMEA output.

Parameters:

None.

Returns:

All status reporting is logged through context_logger.

Return type:

None

Raises:

None – Each serial.Serial interaction is wrapped in its own

:raises try`/except and :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#SERVINFO on Telit), assembles an LTE {cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode, signalStrength} payload and POSTs it to the Google Geolocation v1 endpoint stored in GOOGLELOCATION. On HTTP 200 the resulting latitude/longitude are written under parse_data["GPRMC"].

Parameters:

parse_data (dict) – Mutable dictionary updated with lat / lon keys nested inside "GPRMC".

Returns:

Side effects only.

Return type:

None

Raises:

None – All serial, JSON and requests errors 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) and AT#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 gpsserial and feeds each one to parseGPS() until either a valid GPRMC fix is obtained or the per-call TIMEOUT watchdog fires. If the GSM-fallback window MAX_GSM_TIMEOUT has elapsed without a fix the method falls back to getGSMLocation() and clears the _gsmFlag. If still nothing is parsed and ENABLE_MODULE_TIMEOUT has elapsed, enable_module() is re-invoked to restart the GNSS engine.

Parameters:

None.

Returns:

{"status": bool, "data": dict} where status is True only when an NMEA-derived fix was obtained, and data contains the parsed GPRMC and/or GPGGA sub-dictionaries (or just the GSM-fallback GPRMC.lat/lon keys).

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=1 for engine power, AT+QENG="servingcell" / AT#SERVINFO for GSM fallback. The companion module drivers.Quectel.xOne can 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 discovered gps and modem named ports are assigned to port and portwrite respectively; missing entries are filled with safe defaults (/dev/ttyUSB1 / /dev/ttyUSB2).

Parameters:

None.

Returns:

Detected vendor tag or None if 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 the gps port; 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 xpath and xname on the instance, calls enable_module() to power the GNSS engine on, and – when xone is set to 1 – triggers checkXOneData() to refresh the xTRA assistance file.

Parameters:
  • xone (int) – Set to 1 to download and inject Qualcomm xTRA GPS data, 0 to skip. Defaults to 0.

  • xpath (str) – Base URL for the xTRA data file. Defaults to the Qualcomm izatcloud endpoint.

  • xname (str) – Filename of the xTRA binary to download. Defaults to "xtra3grc.bin".

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=1 sequence and the Telit AT$GPSP=1 / AT$GPSNMUN sequence. The xTRA workflow mirrors the standalone script in drivers.Quectel.xOne.

millis()

Return the current wall-clock time in milliseconds.

Parameters:

None.

Returns:

Integer millisecond timestamp derived from time.time().

Return type:

int

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 is GPGGA, stores the satellite count under parse_data["GPGGA"]["num_sats"]. If pynmea2 fails or the sentence is GPRMC, 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:
  • data (str) – Raw NMEA sentence string including $ prefix and \r\n terminator.

  • parse_data (dict) – Mutable dictionary updated in-place with a "GPGGA" and/or "GPRMC" sub-dictionary.

Returns:

True if a valid GPGGA or GPRMC sentence was parsed and written to parse_data; False otherwise (invalid line, missing fix flag "V", or parse exception).

Return type:

bool

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; readAT consumes 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:

str

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.xOne script is an inline port.readline() pair.

updateXOne() bool

Run the full xTRA assistance-data update workflow.

Thin wrapper around xOneProcess() that guarantees modelserial is open before the work begins and closed in the finally clause regardless of outcome.

Parameters:

None.

Returns:

True once the update procedure has finished (independent of the inner success flag, which is logged).

Return type:

bool

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:

  1. HTTP-GET the xTRA binary from URL + filename.

  2. AT+QGPSEND – stop the running GNSS session.

  3. AT+QGPSXTRA=1 – enable xTRA mode.

  4. AT+QFDEL="RAM:<file>" – delete any stale copy.

  5. AT+QFUPL="RAM:<file>",<size>,60 – upload the binary.

  6. AT+QGPSXTRATIME=0,"<utc>",1,1,3500 – set the xTRA time.

  7. AT+QGPSXTRADATA="RAM:<file>" – inject the data.

  8. AT+QGPSXTRADATA? + AT – verify and idle.

Parameters:

None.

Returns:

True if every step completed without raising, False if any AT call or HTTP fetch raised an exception.

Return type:

bool

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 Quectel AN_GPSXtra application note. PRE-REQUISITES: remove GPS from main loop config and remove SG signal-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