GPS receiver is a device that receives information from GPS satellites and then calculates the device's geographical position, velocity and precise time. The device usually includes a receiver, an IMU (depends on the model), an interface to a wheel encoder, and a fusion engine that combines information from those sensors. The Default GPS receiver used in Apollo is Novatel cards. The instruction demonstrates how to add and use a new GPS Receiver.
Please follow the steps below to add a new GPS Receiver.
- Implement the new data parser for the new GPS receiver, by inheriting class
Parser
- Add new interfaces in
Parser
class for the new GPS receiver - In
config.proto
, add the new data format for the new GPS receiver - In function
create_parser
from file data_parser.cpp, add the new parser instance for the new GPS receiver
Let's look at how to add the GPS Receiver using the above mentioned steps for Reciever: u-blox
.
Let us implement the new data parser for the new GPS receiver, by inheriting class Parser
:
class UbloxParser : public Parser {
public:
UbloxParser();
virtual MessageType get_message(MessagePtr& message_ptr);
private:
bool verify_checksum();
Parser::MessageType prepare_message(MessagePtr& message_ptr);
// The handle_xxx functions return whether a message is ready.
bool handle_esf_raw(const ublox::EsfRaw* raw, size_t data_size);
bool handle_esf_ins(const ublox::EsfIns* ins);
bool handle_hnr_pvt(const ublox::HnrPvt* pvt);
bool handle_nav_att(const ublox::NavAtt *att);
bool handle_nav_pvt(const ublox::NavPvt* pvt);
bool handle_nav_cov(const ublox::NavCov *cov);
bool handle_rxm_rawx(const ublox::RxmRawx *raw);
double _gps_seconds_base = -1.0;
double _gyro_scale = 0.0;
double _accel_scale = 0.0;
float _imu_measurement_span = 0.0;
int _imu_frame_mapping = 5;
double _imu_measurement_time_previous = -1.0;
std::vector<uint8_t> _buffer;
size_t _total_length = 0;
::apollo::drivers::gnss::Gnss _gnss;
::apollo::drivers::gnss::Imu _imu;
::apollo::drivers::gnss::Ins _ins;
};
Let us now add the new interfaces in the Parser class for the new GPS receiver:
Add the function create_ublox
in Parser
class:
class Parser {
public:
// Return a pointer to a NovAtel parser. The caller should take ownership.
static Parser* create_novatel();
// Return a pointer to a u-blox parser. The caller should take ownership.
static Parser* create_ublox();
virtual ~Parser() {}
// Updates the parser with new data. The caller must keep the data valid until get_message()
// returns NONE.
void update(const uint8_t* data, size_t length) {
_data = data;
_data_end = data + length;
}
void update(const std::string& data) {
update(reinterpret_cast<const uint8_t*>(data.data()), data.size());
}
enum class MessageType {
NONE,
GNSS,
GNSS_RANGE,
IMU,
INS,
WHEEL,
EPHEMERIDES,
OBSERVATION,
GPGGA,
};
// Gets a parsed protobuf message. The caller must consume the message before calling another
// get_message() or update();
virtual MessageType get_message(MessagePtr& message_ptr) = 0;
protected:
Parser() {}
// Point to the beginning and end of data. Do not take ownership.
const uint8_t* _data = nullptr;
const uint8_t* _data_end = nullptr;
private:
DISABLE_COPY_AND_ASSIGN(Parser);
};
Parser* Parser::create_ublox() {
return new UbloxParser();
}
In config.proto, let us add the new data format definition for the new GPS receiver:
Add UBLOX_TEXT
and UBLOX_BINARY
in the config file: modules/drivers/gnss/proto/config.proto
message Stream {
enum Format {
UNKNOWN = 0;
NMEA = 1;
RTCM_V2 = 2;
RTCM_V3 = 3;
NOVATEL_TEXT = 10;
NOVATEL_BINARY = 11;
UBLOX_TEXT = 20;
UBLOX_BINARY = 21;
}
... ...
In function create_parser
from file data_parser.cpp, let us add the new parser instance for the new GPS receiver.
We will do so by adding code to process config::Stream::UBLOX_BINARY
as below:
Parser* create_parser(config::Stream::Format format, bool is_base_station = false) {
switch (format) {
case config::Stream::NOVATEL_BINARY:
return Parser::create_novatel();
case config::Stream::UBLOX_BINARY:
return Parser::create_ubloxl();
default:
return nullptr;
}
}