Package: marulc
marulcMaritimeUnpack-Lookup-ConvertA library for parsing and unparsing (future feature) of maritime message formats. Currently supports:- NMEA0183
- NMEA2000Main features:- Parsing NMEA0183 sentences to python dictionaries
- Parsing and decoding NMEA2000 binary messages to python dictionaries
- Support for NMEA2000 messages wrapped in NMEA0183 sentences (``--PGN``-sentences)
- Support for multi-packet NMEA2000 messages (fast-type messages)Since everything is parsed and decoded into regular python dictionaries, serialization to JSON format is very simple.Definitions for parsing and decodingFor NMEA0183, definitions have been extracted from the class-based hierarchy of pynmea2 and copmiled into a JSON definition. It can be foundhere. The script for extracting these definitions from the pynmea2 source code is available in thescripts-folder.For NMEA2000, definitions are identical to what is being used in theCANBOATproject. The definitions can be foundhere.InstallationFrom pypi:pip install marulcExample usageSingle NMEA0183 sentence using standard sentence libraryfrommarulcimportunpack_nmea0183_messagemsg_as_dict=unpack_nmea0183_message("$GNGGA,122203.19,5741.1549,N,01153.1748,E,4,37,0.5,4.03,M,35.78,M,,*72")Single NMEA0183 sentence wrapping a N2K message using custom formatterfrommarulcimportNMEA0183Parserfrommarulc.custom_parsers.PCDINimportPCDINFormatterparser=NMEA0183Parser([PCDINFormatter()])msg_as_dict=parser.unpack("$PCDIN,01F201,001935D5,38,0000000B0C477CBC0C0000FFFFFFFFFFFF30007F000000000000*26")Parse from iteratorfrommarulcimportNMEA0183Parser,parse_from_iteratorexample_data=["$YDGLL,5741.1612,N,01153.1447,E,110759.00,A,A*6B","$YDRMC,110759.00,A,5741.1612,N,01153.1447,E,0.0,300.0,010170,,E,A,C*72","$YDRPM,E,0,0.0,,A*64","$YDRPM,E,1,0.0,,A*65","$YDROT,-0.6,A*10","$YDHDG,0.0,0.0,E,,*3F","$YDHDM,0.0,M*3F","$YDRSA,-0.1,A,,V*48","$YDVTG,328.0,T,328.0,M,0.0,N,0.0,K,A*29"]parser=NMEA0183Parser()forunpacked_msginparse_from_iterator(parser,example_data,quiet=True):print(unpacked_msg)NMEA2000 framesfrommarulcimportNMEA2000Parserparser=NMEA2000Parser()# Unpack a single frame message# Note: This will only work for single-frame N2K messages. For multi-frame messages, the unpack# method will raise a `MultiPacketInProcessError` and expect further frames to be providedmsg_as_dict=parser.unpack("09F10D0A FF 00 00 00 FF 7F FF FF")# For unpacking multi-frame messages, its usually better to use a `parse_from_iterator` setup, such as:frommarulcimportparse_from_iteratormulti_frame_message=["09F201B7 C0 1A 01 FF FF FF FF B0","09F201B7 C1 81 3C 05 00 00 B0 BA","09F201B7 C2 1C 00 FF FF FF FF FF","09F201B7 C3 00 00 00 00 7F 7F FF",]forfull_messageinparse_from_iterator(parser,multi_frame_message,quiet=True):print(full_message)Filter for specific messagesfrommarulcimportNMEA0183Parser,parse_from_iteratorfrommarulc.utilsimportfilter_on_talker_formatterexample_data=["$YDGLL,5741.1612,N,01153.1447,E,110759.00,A,A*6B","$YDRMC,110759.00,A,5741.1612,N,01153.1447,E,0.0,300.0,010170,,E,A,C*72","$YDRPM,E,0,0.0,,A*64","$YDRPM,E,1,0.0,,A*65","$YDROT,-0.6,A*10","$YDHDG,0.0,0.0,E,,*3F","$YDHDM,0.0,M*3F","$YDRSA,-0.1,A,,V*48","$YDVTG,328.0,T,328.0,M,0.0,N,0.0,K,A*29"]parser=NMEA0183Parser()iterator_all=parse_from_iterator(parser,example_data,quiet=True)rpm_sentences=list(filter(filter_on_talker_formatter("..RPM"),iterator_all))assertlen(rpm_sentences)==2Extract specific value from specific messagesfrommarulcimportNMEA2000Parser,parse_from_iteratorfrommarulc.utilsimportfilter_on_pgn,deep_getexample_data=["08FF12C9 4A 9A 00 17 DB 00 00 00","08FF13C9 4A 9A 00 00 FF FF FF FF","08FF14C9 4A 9A 00 00 00 00 00 FF","09F200C9 00 57 30 FF FF 01 FF FF","09F205C9 00 FC FF FF FF FF 00 FF","09F10DE5 00 F8 FF 7F F9 FE FF FF","09F11365 DA AB 4B FE FF FF FF FF","08FF12B7 4A 9A 01 17 DB 00 00 00","08FF13B7 4A 9A 01 00 FF FF FF FF","08FF14B7 4A 9A 01 00 00 00 00 FF","09F200B7 01 DA 2F FF FF 01 FF FF","09F205B7 01 FC FF FF FF FF 00 FF",]parser=NMEA2000Parser()iterator_all=parse_from_iterator(parser,example_data,quiet=True)speeds=[]forfiltered_unpacked_msginfilter(filter_on_pgn(127488),iterator_all):speed=deep_get(filtered_unpacked_msg,"Fields","speed")speeds.append(speed)assertlen(speeds)==2Extraction using JSON pointersRequires thejsonpointerpackage (pip install jsonpointer)fromjsonpointerimportresolve_pointerfrommarulcimportNMEA2000Parser,parse_from_iteratorfrommarulc.utilsimportfilter_on_pgn,deep_getexample_data=["08FF12C9 4A 9A 00 17 DB 00 00 00","08FF13C9 4A 9A 00 00 FF FF FF FF","08FF14C9 4A 9A 00 00 00 00 00 FF","09F200C9 00 57 30 FF FF 01 FF FF","09F205C9 00 FC FF FF FF FF 00 FF","09F10DE5 00 F8 FF 7F F9 FE FF FF","09F11365 DA AB 4B FE FF FF FF FF","08FF12B7 4A 9A 01 17 DB 00 00 00","08FF13B7 4A 9A 01 00 FF FF FF FF","08FF14B7 4A 9A 01 00 00 00 00 FF","09F200B7 01 DA 2F FF FF 01 FF FF","09F205B7 01 FC FF FF FF FF 00 FF",]parser=NMEA2000Parser()iterator_all=parse_from_iterator(parser,example_data,quiet=True)speeds=[]forfiltered_unpacked_msginfilter(filter_on_pgn(127488),iterator_all):speed=resolve_pointer(filtered_unpacked_msg,"/Fields/speed")speeds.append(speed)assertlen(speeds)==2Development setupCreate a virtual environment:python3 -m venv venv
source venv/bin/activateInstall the development requirements:pip install -r requirements.txtRun the formatter and linter:black marulc tests
pylint marulcRun the tests:pytest --codeblocks