Library Architecture

EasyNMEA is divided into three levels (from outer to inner):

  1. API Level : This level contains all public API, i.e. the classes in the include directory.

  2. Implementation Level: This level contains all the internal classes which provide functionality to the library.

  3. Serial Interface Level: This level contains the classes for interacting with the serial port (through Asio).

@startuml
skinparam linetype ortho

component [API Level] as api_level
component [Implementation Level] as impl_level
component [Serial Interface Level] as serial_level

api_level <.. impl_level
impl_level <.. serial_level

@enduml

API Level

The API level comprises all the EasyNMEA public classes and structures, and acts as entry point for the library’s functionalities. It consists of a main class EasyNmea, which provides application with access to the functionalities, and all the supporting classes and structures for return types and input and output parameters. Those companion classes and structures are ReturnCode, GPGGAData, and NMEA0183DataKindMask. For the actual functionality implementation, EasyNmea relies on the internal class EasyNmeaImpl.

@startuml
skinparam linetype ortho
hide empty members

class EasyNmea
class EasyNmeaImpl
enum ReturnCode
class NMEA0183Data
class GPGGAData
class Bitmask<typename E>
class NMEA0183DataKind
class NMEA0183DataKindMask<NMEA0183DataKind>

EasyNmea : EasyNmea() noexcept
EasyNmea : virtual ReturnCode open(const char* serial_port, long baudrate) noexcept
EasyNmea : virtual bool is_open() noexcept
EasyNmea : virtual ReturnCode close() noexcept
EasyNmea : virtual ReturnCode take_next(GPGGAData& gpgga) noexcept
EasyNmea : virtual ReturnCode wait_for_data(NMEA0183DataKindMask data_mask, std::chrono::milliseconds timeout) noexcept

ReturnCode : RETURN_CODE_OK
ReturnCode : RETURN_CODE_NO_DATA
ReturnCode : RETURN_CODE_TIMEOUT
ReturnCode : RETURN_CODE_BAD_PARAMETER
ReturnCode : RETURN_CODE_ILLEGAL_OPERATION
ReturnCode : RETURN_CODE_UNSUPPORTED
ReturnCode : RETURN_CODE_ERROR

NMEA0183Data : NMEA0183DataKind kind

GPGGAData : float timestamp
GPGGAData : float latitude
GPGGAData : float longitude
GPGGAData : uint16_t fix
GPGGAData : uint16_t satellites_on_view
GPGGAData : float horizontal_precision
GPGGAData : float altitude
GPGGAData : float height_of_geoid
GPGGAData : float dgps_last_update
GPGGAData : uint16_t dgps_reference_station_id

Bitmask : void set(const E& value)
Bitmask : void clear(const E& value)
Bitmask : bool is_set(const E& value)
Bitmask : Bitmask none()
Bitmask : Bitmask all()
Bitmask : bool is_none()

NMEA0183DataKind : INVALID
NMEA0183DataKind : GPGGA

EasyNmea <.. ReturnCode : <<uses>>
EasyNmea <.. GPGGAData : <<uses>>
EasyNmea <.. NMEA0183DataKindMask : <<uses>>
EasyNmea <|.. EasyNmeaImpl

NMEA0183Data <.. NMEA0183DataKind : <<uses>>

NMEA0183Data <|-- GPGGAData

Bitmask <.. NMEA0183DataKindMask : <<bind>>

NMEA0183DataKindMask <.. NMEA0183DataKind : <<uses>>

@enduml

Implementation Level

The implementation level comprises two main components:

  1. The EasyNmeaImpl class, which provides with implementation for the EasyNmea public API, i.e opening and closing the serial port, waiting until data of one or more NMEA 0183 types has been received, checking whether the serial port connection is opened, and taking the next unread sample of a given NMEA 0183 type. The EasyNmeaImpl holds a FixedSizeQueue of ten elements for each supported NMEA 0183 type. This way, keeping outdated samples, as well as dynamic allocation of data samples, is avoided. The managing of the serial port is enabled through the SerialInterface class.

  2. The EasyNmeaCoder class, which provides APIs for decode NMEA 0183 sentences (and to encode them in the future).

@startuml
hide empty members

EasyNmeaImpl : EasyNmeaImpl() noexcept
EasyNmeaImpl : virtual ReturnCode open(const char* serial_port, long baudrate) noexcept
EasyNmeaImpl : virtual bool is_open() noexcept
EasyNmeaImpl : virtual ReturnCode close() noexcept
EasyNmeaImpl : virtual ReturnCode take_next(GPGGAData& gpgga) noexcept
EasyNmeaImpl : virtual ReturnCode wait_for_data(NMEA0183DataKindMask data_mask, std::chrono::milliseconds timeout) noexcept

class FixedSizeQueue<typename T, int max_size, typename Container = std::deque<T>>

class SerialInterface<asio::serial_port>

class EasyNmeaCoder {
    + static std::shared_ptr<NMEA0183Data> decode(const std::string& sentence) noexcept
    ---
    # static std::shared_ptr<GPGGAData> decode_gpgga_(const std::string gpgga_sentence) noexcept
    # static NMEA0183DataKind data_kind_(const std::string& sentence) noexcept
    # static bool validate_checksum_(const std::string& sentence) noexcept
    # static std::vector<std::string> split_(const std::string& sentence, char separator) noexcept
    # static float to_degrees_(const std::string& nmea_angle) noexcept
}

class NMEA0183Data {
    NMEA0183DataKind kind
}

class GPGGAData {
    float timestamp
    float latitude
    float longitude
    uint16_t fix
    uint16_t satellites_on_view
    float horizontal_precision
    float altitude
    float height_of_geoid
    float dgps_last_update
    uint16_t dgps_reference_station_id
}

class NMEA0183DataKind {
    INVALID
    GPGGA
}

NMEA0183Data <|-- GPGGAData
NMEA0183Data <.. NMEA0183DataKind : <<uses>>
EasyNmeaCoder <.. NMEA0183Data : <<uses>>
EasyNmeaCoder <.. GPGGAData : <<uses>>

EasyNmeaImpl o-- "n" FixedSizeQueue
EasyNmeaImpl o-- "1" SerialInterface

EasyNmeaImpl <.. EasyNmeaCoder : <<uses>>

@enduml

Serial Interface Level

The serial interface level is comprised of the SerialInterface class, which provides member functions to open and close a serial port, as well as for reading data from it. SerialInterface is a template class with a template parameter SerialPort that defines the serial port implementation, which defaults to :class: asio::serial_port.

@startuml
hide empty members

class SerialInterface<class SerialPort = asio::serial_port>

SerialInterface : virtual SerialInterface() noexcept
SerialInterface : virtual bool open(std::string port, uint64_t baudrate) noexcept
SerialInterface : virtual bool is_open() noexcept
SerialInterface : virtual bool close() noexcept
SerialInterface : virtual bool read_line(std::string& result) noexcept
SerialInterface : virtual std::size_t read_char(char& c, asio::error_code& ec) noexcept

class SerialPort

SerialInterface o-- "1" SerialPort
@enduml