diff --git a/.vscode/settings.json b/.vscode/settings.json index 1321e72..4d8a3d9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,5 @@ "editor.formatOnSave": true, "modulename": "${workspaceFolderBasename}", "distname": "${workspaceFolderBasename}", - "moduleversion": "1.0.37", + "moduleversion": "1.0.38", } \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index dfaba65..ef2ee7f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,23 @@ # pynmeagps Release Notes +### RELEASE 1.0.38 + +ENHANCEMENTS: + +1. Add Locosys proprietary NMEA GET and SET messages: + - $PINVCRES: Clear the NVM data + - $PINVCSTR: Start session + - $PINVMATTIT: ATTIT information + - $PINVMIMU: MEMS RAW-DATA message information + - $PINVMINR: Calibration status + - $PINVMSTR: Session Status + - $PINVMSLOPE: SLOPE information + - $PLSC: Set status/poll version + - $PLSR: Set status response + - $PLSVD: Attitude yaw, pitch, roll + + NB: $PMTKnnn: proprietary command message sets not yet implemented + ### RELEASE 1.0.37 ENHANCEMENTS: diff --git a/pyproject.toml b/pyproject.toml index 0616262..1c13277 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pynmeagps" authors = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }] maintainers = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }] description = "NMEA protocol parser and generator" -version = "1.0.37" +version = "1.0.38" license = { file = "LICENSE" } readme = "README.md" requires-python = ">=3.8" diff --git a/src/pynmeagps/_version.py b/src/pynmeagps/_version.py index c83016e..ecfbb12 100644 --- a/src/pynmeagps/_version.py +++ b/src/pynmeagps/_version.py @@ -8,4 +8,4 @@ :license: BSD 3-Clause """ -__version__ = "1.0.37" +__version__ = "1.0.38" diff --git a/src/pynmeagps/nmeatypes_core.py b/src/pynmeagps/nmeatypes_core.py index 72fa612..0fef665 100644 --- a/src/pynmeagps/nmeatypes_core.py +++ b/src/pynmeagps/nmeatypes_core.py @@ -321,6 +321,20 @@ "FECGPATT": "Attitude yaw, pitch, roll", "FECGPHVE": "Heave", # *************************************************************** + # Locosys Proprietary Messages (GET and SET) + # *************************************************************** + "INVCRES": "Clear the NVM data", + "INVCSTR": "Start session", + "INVMATTIT": "ATTIT information", + "INVMIMU": "MEMS RAW-DATA message information", + "INVMINR": "Calibration status", + "INVMSTR": "Session Status", + "INVMSLOPE": "SLOPE information", + "LSC": "Set status/poll version", + "LSR": "Set status response", + "LSVD": "Attitude yaw, pitch, roll", + # "MTKnnn": "Proprietary command sets - not implemented", + # *************************************************************** # Dummy message for testing only # *************************************************************** "FOO": "Dummy message", diff --git a/src/pynmeagps/nmeatypes_get.py b/src/pynmeagps/nmeatypes_get.py index b92e691..80f0358 100644 --- a/src/pynmeagps/nmeatypes_get.py +++ b/src/pynmeagps/nmeatypes_get.py @@ -1270,6 +1270,51 @@ "heave": DE, "status": CH, # 'A' }, + # *************************************************************** + # Locosys Proprietary Messages GET + # https://www.locosystech.com/Templates/att/LS2303x-UDG_datasheet_v0.6.pdf?lng=en + # *************************************************************** + "LSR": { + "msgType": ST, # e.g. "SLOPE", "MEMS", "ATTIT" + "value": IN, # e.g. 0 + "response": ST, # e.g. "OK" + }, + "LSVD": { + "velE": DE, # cm/s + "velN": DE, # cm/s + "velD": DE, # cm/s + "velEdev": DE, # cm/s deviation + "velNdev": DE, # cm/s deviation + "velDdev": DE, # cm/s deviation + }, + "INVMINR": { + "status": CH, # 0 uninitialized; 1、2 calibrating/initializing; 3 calibration done + }, + "INVMSTR": { + "value": IN, + }, + "INVMSLOPE": { + "slope": DE, # degrees + "altDiff": DE, # meters + "moveDist": DE, # meters + "slopeAccu": DE, # degrees + "altDiffAccu": DE, # meters + "moveDistAccu": DE, # meters + }, + "INVMIMU": { + "timeSecond": DE, # sec timestamp + "accelX": DE, # m/s^2 + "accelY": DE, # m/s^2 + "accelZ": DE, # m/s^2 + "gyroX": DE, # degree/s + "gyroY": DE, # degree/s + "gyroZ": DE, # degree/s + }, + "INVMATTIT": { + "roll": DE, # degree + "pitch": DE, # degree + "yaw": DE, # degree heading + }, # ********************************************* # Dummy message for error testing # ********************************************* diff --git a/src/pynmeagps/nmeatypes_set.py b/src/pynmeagps/nmeatypes_set.py index f978018..7b1648d 100644 --- a/src/pynmeagps/nmeatypes_set.py +++ b/src/pynmeagps/nmeatypes_set.py @@ -107,4 +107,34 @@ "baudRate": IN, "autobauding": IN, }, + # *************************************************************** + # Locosys Proprietary Messages SET + # https://www.locosystech.com/Templates/att/LS2303x-UDG_datasheet_v0.6.pdf?lng=en + # *************************************************************** + "LSC": { + "msgType": ST, # e.g. "SLOPE", "MEMS", "ATTIT", "VER" + "value": IN, # e.g. 0 (not present if msgType == "VER") + }, + "INVCRES": { + "value": IN, # 0 = clear data + }, + "INVCSTR": { + "value": IN, # 14 = start session + }, + "INVMSLOPE": { + "status": IN, # 0 = disable, 1 = enable + }, + # Proprietary Locosys command sets not yet implemented, e.g. + # Perform a Cold start $PMTK103*30 + # Perform a Warm start $PMTK102*31 + # Perform a Hot start $PMTK101*32 + # Perform a Full Cold start $PMTK104*37 + # Disable GLL message $PMTK314,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29 + # Disable GSV message $PMTK314,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29 + # Disable GLL & GSV message $PMTK314,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28 + # Factory default output message $PMTK314,-1*04 + # Navigate with GPS+GALILEO $PMTK353,1,0,1,0,0*2B + # Navigate with GPS+GLONASS+GALILEO $PMTK353,1,1,1,0,0*2A + # Navigate with GPS+BEIDOU $PMTK353,1,0,0,0,1*2B + # Entering Standby Mode1 $PMTK161,0*28 } diff --git a/tests/locosys_get_nmea.log b/tests/locosys_get_nmea.log new file mode 100644 index 0000000..eb9dfaf --- /dev/null +++ b/tests/locosys_get_nmea.log @@ -0,0 +1,11 @@ +$PLSVD,1,0,-16,16,15,13*77 +$PLSR,SLOPE,1,OK*41 +$PLSR,SLOPE,0,OK*40 +$PLSR,MEMS,1,OK*12 +$PLSR,MEMS,0,OK*13 +$PLSR,ATTIT,1,OK*58 +$PLSR,ATTIT,0,OK*59 +$PINVMSTR,0*05 +$PINVMSLOPE,-3.13,-0.05,0.93,54.42,2.60,1.86*3B +$PINVMIMU,1114.106,-0.36990,1.51074,9.81383,0.67139,0.61035,-0.30518*22 +$PINVMATTIT,-20.652,32.265,0.000*3C diff --git a/tests/locosys_set_nmea.log b/tests/locosys_set_nmea.log new file mode 100644 index 0000000..1362e94 --- /dev/null +++ b/tests/locosys_set_nmea.log @@ -0,0 +1,9 @@ +$PINVCRES,0*1A +$PINVCSTR,14*3E +$PLSC,SLOPE,1*78 +$PLSC,SLOPE,0*79 +$PLSC,MEMS,1*2B +$PLSC,MEMS,0*2A +$PLSC,ATTIT,1*61 +$PLSC,ATTIT,0*60 +$PLSC,VER*61 diff --git a/tests/test_stream.py b/tests/test_stream.py index df5273d..fcfcf8c 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -21,6 +21,7 @@ ERR_RAISE, ERR_IGNORE, ERR_LOG, + SET, ) DIRNAME = os.path.dirname(__file__) @@ -444,6 +445,54 @@ def testNMEAKENWOOD(self): # test proprietary Kenwood messages i += 1 self.assertEqual(i, 5) + def testNMEALOCOSYSGET(self): # test proprietary Locosys GET messages + EXPECTED_RESULTS = ( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ) + i = 0 + raw = 0 + with open(os.path.join(DIRNAME, "locosys_get_nmea.log"), "rb") as stream: + nmr = NMEAReader(stream, nmeaonly=False, quitonerror=2) + for raw, parsed in nmr: + if raw is not None: + # print(f'"{parsed}",') + self.assertEqual(str(parsed), EXPECTED_RESULTS[i]) + i += 1 + self.assertEqual(i, 11) + + def testNMEALOCOSYSSET(self): # test proprietary Locosys SET messages + EXPECTED_RESULTS = ( + "", + "", + "", + "", + "", + "", + "", + "", + "", + ) + i = 0 + raw = 0 + with open(os.path.join(DIRNAME, "locosys_set_nmea.log"), "rb") as stream: + nmr = NMEAReader(stream, nmeaonly=False, quitonerror=2, msgmode=SET) + for raw, parsed in nmr: + if raw is not None: + # print(f'"{parsed}",') + self.assertEqual(str(parsed), EXPECTED_RESULTS[i]) + i += 1 + self.assertEqual(i, 9) + def testBADHDR_FAIL(self): # invalid header in data with quitonerror = 2 EXPECTED_ERROR = "Unknown protocol header b'$&'." with self.assertRaises(NMEAParseError) as context: