Knio / pynmea2

Python library for parsing the NMEA 0183 protocol (GPS)
MIT License
635 stars 223 forks source link

Checksum checking + signalId and systemId parsing (NMEA 4.10+ GSV GRS) #115

Closed fohrloop closed 2 years ago

fohrloop commented 4 years ago

The checksum checking was previously always on, even though the default value for check in parse() was False

coveralls commented 4 years ago

Coverage Status

Coverage decreased (-0.4%) to 97.696% when pulling 17d2c39a48c20535ad7153bf3112acf283d33b45 on np-8:master into 4527cf47d45624764727311327d7863d30a86766 on Knio:master.

fohrloop commented 4 years ago

Added GSV signal_id processing to fix https://github.com/Knio/pynmea2/issues/108

The idea is the add custom __init__ method to GSV class which allows for custom name_to_idx and fields for the GSV instances. Also, there is custom __getattr__ for the GSV class, which basically just makes the GSV instances to use self.name_to_idx and self.fields instead of the t.name_to_idx or t.fields (t=type).

Some examples below: (adds signal_id and removes the erroneus sv_prn_num_#).

Same pattern could be used also for other messages which may be variable length, and which may contain additional identifiers added by GNSS receiver manufacturers.

Example 1

In [1]: pynmea2.parse("$GPGSV,3,3,09,25,,,40,1*6E")
Out[1]: <GSV(num_messages='3', msg_num='3', num_sv_in_view='09', sv_prn_num_1='25', elevation_deg_1='', azimuth_1='', snr_1='40', sv_prn_num_2='1')>

In [2]: pynmea2.parse("$GPGSV,3,3,09,25,,,40,1*6E", GSV_signal_id=True)
Out[2]: <GSV(num_messages='3', msg_num='3', num_sv_in_view='09', sv_prn_num_1='25', elevation_deg_1='', azimuth_1='', snr_1='40', signal_id='1')>

Example 2

In [1]: pynmea2.parse("$GPGSV,3,3,11,26,49,301,08,29,58,056,37,31,50,235,22,1*55")
Out[1]: <GSV(num_messages='3', msg_num='3', num_sv_in_view='11', sv_prn_num_1='26', elevation_deg_1='49', azimuth_1='301', snr_1='08', sv_prn_num_2='29', elevation_deg_2='58', azimuth_2='056', snr_2='37', sv_prn_num_3='31', elevation_deg_3='50', azimuth_3='235', snr_3='22', sv_prn_num_4='1')>

In [2]: pynmea2.parse("$GPGSV,3,3,11,26,49,301,08,29,58,056,37,31,50,235,22,1*55", GSV_signal_id=True)
Out[2]: <GSV(num_messages='3', msg_num='3', num_sv_in_view='11', sv_prn_num_1='26', elevation_deg_1='49', azimuth_1='301', snr_1='08', sv_prn_num_2='29', elevation_deg_2='58', azimuth_2='056', snr_2='37', sv_prn_num_3='31', elevation_deg_3='50', azimuth_3='235', snr_3='22', signal_id='1')>

Example 3

In [1]: pynmea2.parse('$GAGSV,1,1,00,2*76\r\n')
Out[1]: <GSV(num_messages='1', msg_num='1', num_sv_in_view='00', sv_prn_num_1='2')>

In [2]: pynmea2.parse('$GAGSV,1,1,00,2*76\r\n', GSV_signal_id=True)
Out[2]: <GSV(num_messages='1', msg_num='1', num_sv_in_view='00', signal_id='2')>
fohrloop commented 4 years ago

Added support for reading the systemId and signalId from GRS NMEA 4.10+ messages.

Example

In [1]: data = "$GNGRS,121519.00,1,0.2,-1.4,-1.9,3.0,-0.2,1.2,-0.8,-1.2,,,,,3,7*70"

# This is the default behaviour; the system_id and signal_id are in the data property
In [2]: msg = pynmea2.parse(data, GRS_ids=False)

In [3]: msg
Out[3]: <GRS(timestamp=datetime.time(12, 15, 19), residuals_mode=1, sv_res_01=0.2, sv_res_02=-1.4, sv_res_03=-1.9, sv_res_04=3.0, sv_res_05=-0.2, sv_res_06=1.2, sv_res_07=-0.8, sv_res_08=-1.2, sv_res_09=None, sv_res_10=None, sv_res_11=None, sv_res_12=None) data=['3', '7']>

In [4]: msg = pynmea2.parse(data, GRS_ids=True)

In [5]: msg
Out[5]: <GRS(timestamp=datetime.time(12, 15, 19), residuals_mode=1, sv_res_01=0.2, sv_res_02=-1.4, sv_res_03=-1.9, sv_res_04=3.0, sv_res_05=-0.2, sv_res_06=1.2, sv_res_07=-0.8, sv_res_08=-1.2, sv_res_09=None, sv_res_10=None, sv_res_11=None, sv_res_12=None, system_id='3', signal_id='7')>
xOneca commented 4 years ago

If signal_id and system_id are standard, why not just add them to the field list of the messages that need them? If the receiver doesn't add them to the sentence (old hardware), the new fields will just remain empty.

xOneca commented 4 years ago

Sorry. Didn't see that GSV can have less satellites but still send signal_id and system_id.

fohrloop commented 4 years ago

No problem. I see that the original implementation uses a metaclass and the different NMEA Sentences are implemented as classes that are constructed using the metaclass. Now, solving the problem of variable length NMEA strings (with variable amount of commas) could probably be also solved using some extra arguments when creating the NMEA Sentence classes, by modifying the metaclass code, but I did not figure out an easy way to do that.

You're right that actually GRS systemId and signalId could be read by just adding the two entries to the GRS.fields.

The GSV messages are tricky since at least uBlox outputs sometimes variable amount of commas in the message. Maybe this could be also solved by introducing "fields" that are read from end of the string but still it will probably need user given argument to parse().