sparkfun / Qwiic_Ublox_Gps_Py

https://qwiic-ublox-gps-py.readthedocs.io/en/latest/index.html
Other
70 stars 41 forks source link

Getting satellites from library is broken #20

Closed camielverdult closed 1 year ago

camielverdult commented 3 years ago
Traceback (most recent call last):
  File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.6/threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "gps.py", line 61, in GGA_parse_thread
    print(f"GEO: Capturing NMEA for 2 seconds... ({self.gps.satellites()} satellites)")
  File "/usr/local/lib/python3.6/dist-packages/ublox_gps/ublox_gps.py", line 253, in satellites
    s_payload = self.scale_NAV_SAT(payload)
  File "/usr/local/lib/python3.6/dist-packages/ublox_gps/ublox_gps.py", line 703, in scale_NAV_SAT
    pr_res = nav_payload.prRes
AttributeError: 'SAT' object has no attribute 'prRes'

This is on a Sparkfun GPS-RTK2 ZED-F9P with the following code:

            try: 
                print(f"GEO: Capturing NMEA for 2 seconds... ({self.gps.satellites()} satellites)")
                start = time.time()
                while (time.time() - start < NMEA_CAPTURE_TIME):
                    try:
                        data.append(self.gps.stream_nmea())
                    except (ValueError, IOError) as err:
                        print(err)
            finally:
                self.port.close()
camielverdult commented 3 years ago

Communication is over serial, set up as following:

UBLOX_PORT = "/dev/ttyACM0"
# Setup port to communicate with GPS
self.port = serial.Serial(port=UBLOX_PORT, baudrate=38400)

# Wait a second to let the port initialize
time.sleep(1)

self.gps = UbloxGps(self.port)
supercrazysam commented 2 years ago

Any update on this? this is still broken, no explaination on what is prRes either.

grwells commented 1 year ago

Would love to have someone more knowledgeable weigh in on this (@edspark) ... but after running into the same problem this is what I have pieced together.

I think the library is attempting to read the wrong field from the UBX packets. A call to satellites() (defined ublox_gps.py line 241) requests the SAT message with:

self.send_message(sp.NAV_CLS, self.nav_ms.get('SAT'))

received values are then parsed with a call to receive_from(...) and it seems that the returned payload is scaled three lines below into an integer:

s_payload = self.scale_NAV_SAT(payload)

which is executing the very simple:

pr_res = nav_payload.prRes
nav_payload = nav_payload._replace(prRes= pr_res * 0.1)

return nav_payload

Since the SAT message is where prRes is supposed to be I took a look at its structure. What I saw did and didn't make sense.

    core.Message(0x35, 'SAT', [
        core.Field('iTOW', 'U4'),
        core.Field('version', 'U1'),
        core.Field('numSvs', 'U1'),
        core.PadByte(repeat=1),
        core.RepeatedBlock('RB', [
            core.Field('gnssId', 'U1'),
            core.Field('svId', 'U1'),
            core.Field('cno', 'U1'),
            core.Field('elev', 'I1'),
            core.Field('azim', 'I2'),
            core.Field('prRes', 'I2'),
            core.BitField('flags', 'X4', [
                core.Flag('qualityInd', 0, 3),
                core.Flag('svUsed', 3, 4),
                core.Flag('health', 4, 6),
                core.Flag('diffCorr', 6, 7),
                core.Flag('smoothed', 7, 8),
                core.Flag('orbitSource', 8, 11),
                core.Flag('ephAvail', 11, 12),
                core.Flag('almAvail', 12, 13),
                core.Flag('anoAvail', 13, 14),
                core.Flag('aopAvail', 14, 15),
                core.Flag('sbasCorrUsed', 16, 17),
                core.Flag('rtcmCorrUsed', 17, 18),
                core.Flag('slasCorrUsed', 18, 19),
                core.Flag('prCorrUsed', 20, 21),
                core.Flag('crCorrUsed', 21, 22),
                core.Flag('doCorrUsed', 22, 23),
            ]),
        ]),
    ]),

According to that defined structure, it makes sense that an error is thrown. prRes is contained in a repeating block which represents each connected satellite I believe. However... prRes doesn't seem to have any correlation to the number of connected satellites as far as I can tell. On the other hand, numSvs, which is part of the base structure (non-repeating block) sounds kinda like "number of servers" so I thought "hmm, maybe that could be helpful?".

A google search yielded this ROS2 message field snippet for ublox SAT messages:

uint8 numSvs # Number of satellites

Ah! Maybe this is what we should be looking at instead? After modifying a cloned version of the source and running on my machine it seems to work... had some minor errors thrown (checksum not matching) that once disabled produced at least realistic looking data. Hoping somebody else can help shed light on whether this is the right direction to go or not. Or at least use what I found to hack together their own solution.

edspark commented 1 year ago

Hey hello! There was a great pull request by @DanNsk that added a few things that were not available in the original release. One of them is the use of "Repeated Blocks". I don't need to go into the details however, satellites is working now because Repeated Blocks are working. This is, I believe, the source of the original error you were seeing @camielverdult.

To get the satellite numbers in the way that you detailed in your original issue, as @grwells pointed out, you do want to use numSvs to get the number of satellites:

sats = gps.satellites()
print("Number of Satellites: ", sats.numSvs)

This is in the latest pip release 1.1.5.

Happy Spacing