Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of gnss by
gnss.cpp
- Committer:
- fahim.alavi@u-blox.com
- Date:
- 2018-05-21
- Revision:
- 9:bfa9081e1d14
- Parent:
- 8:cff83b9f5093
- Child:
- 11:a04f7eeece23
File content as of revision 9:bfa9081e1d14:
/* mbed Microcontroller Library * Copyright (c) 2017 u-blox * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file gnss.cpp * This file defines a class that communicates with a u-blox GNSS chip. */ #include "mbed.h" #include "ctype.h" #include "gnss.h" GnssParser::GnssParser(void) { // Create the enable pin but set everything to disabled _gnssEnable = NULL; #ifdef TARGET_UBLOX_C030 _gnssEnable = new DigitalInOut(GNSSEN, PIN_OUTPUT, PushPullNoPull, 0); #else _gnssEnable = new DigitalInOut(GNSSEN, PIN_OUTPUT, PullNone, 1); #endif } GnssParser::~GnssParser(void) { if (_gnssEnable != NULL) { *_gnssEnable = 0; delete _gnssEnable; } } void GnssParser::powerOff(void) { // Set the GNSS into backup mode using the command RMX-LPREQ struct { unsigned long dur; unsigned long flags; } msg = {0/*endless*/,0/*backup*/}; sendUbx(0x02, 0x41, &msg, sizeof(msg)); } void GnssParser::_powerOn(void) { if (_gnssEnable != NULL) { *_gnssEnable = 1; } wait_ms (1); } int GnssParser::_getMessage(Pipe<char>* pipe, char* buf, int len) { int unkn = 0; int sz = pipe->size(); int fr = pipe->free(); if (len > sz) len = sz; while (len > 0) { // NMEA protocol pipe->set(unkn); int nmea = _parseNmea(pipe,len); if ((nmea != NOT_FOUND) && (unkn > 0)) return UNKNOWN | pipe->get(buf,unkn); if (nmea == WAIT && fr) return WAIT; if (nmea > 0) return NMEA | pipe->get(buf,nmea); // UBX protocol pipe->set(unkn); int ubx = _parseUbx(pipe,len); if ((ubx != NOT_FOUND) && (unkn > 0)) return UNKNOWN | pipe->get(buf,unkn); if (ubx == WAIT && fr) return WAIT; if (ubx > 0) return UBX | pipe->get(buf,ubx); // UNKNOWN unkn ++; len--; } if (unkn > 0) return UNKNOWN | pipe->get(buf,unkn); return WAIT; } int GnssParser::_parseNmea(Pipe<char>* pipe, int len) { int o = 0; int c = 0; char ch; if (++o > len) return WAIT; if ('$' != pipe->next()) return NOT_FOUND; // This needs to be extended by crc checking for (;;) { if (++o > len) return WAIT; ch = pipe->next(); if ('*' == ch) break; // crc delimiter if (!isprint(ch)) return NOT_FOUND; c ^= ch; } if (++o > len) return WAIT; ch = _toHex[(c >> 4) & 0xF]; // high nibble if (ch != pipe->next()) return NOT_FOUND; if (++o > len) return WAIT; ch = _toHex[(c >> 0) & 0xF]; // low nibble if (ch != pipe->next()) return NOT_FOUND; if (++o > len) return WAIT; if ('\r' != pipe->next()) return NOT_FOUND; if (++o > len) return WAIT; if ('\n' != pipe->next()) return NOT_FOUND; return o; } int GnssParser::_parseUbx(Pipe<char>* pipe, int l) { int o = 0; if (++o > l) return WAIT; if ('\xB5' != pipe->next()) return NOT_FOUND; if (++o > l) return WAIT; if ('\x62' != pipe->next()) return NOT_FOUND; o += 4; if (o > l) return WAIT; int i,j,ca,cb; i = pipe->next(); ca = i; cb = ca; // cls i = pipe->next(); ca += i; cb += ca; // id i = pipe->next(); ca += i; cb += ca; // len_lsb j = pipe->next(); ca += j; cb += ca; // len_msb j = i + (j << 8); while (j--) { if (++o > l) return WAIT; i = pipe->next(); ca += i; cb += ca; } ca &= 0xFF; cb &= 0xFF; if (++o > l) return WAIT; if (ca != pipe->next()) return NOT_FOUND; if (++o > l) return WAIT; if (cb != pipe->next()) return NOT_FOUND; return o; } int GnssParser::send(const char* buf, int len) { return _send(buf, len); } int GnssParser::sendNmea(const char* buf, int len) { char head[1] = { '$' }; char tail[5] = { '*', 0x00/*crc_high*/, 0x00/*crc_low*/, '\r', '\n' }; int i; int crc = 0; for (i = 0; i < len; i ++) crc ^= *buf++; i = _send(head, sizeof(head)); i += _send(buf, len); tail[1] = _toHex[(crc > 4) & 0xF0]; tail[2] = _toHex[(crc > 0) & 0x0F]; i += _send(tail, sizeof(tail)); return i; } int GnssParser::sendUbx(unsigned char cls, unsigned char id, const void* buf /*= NULL*/, int len /*= 0*/) { char head[6] = { 0xB5, 0x62, cls, id, (char) len, (char) (len >> 8)}; char crc[2]; int i; int ca = 0; int cb = 0; for (i = 2; i < 6; i ++) { ca += head[i]; cb += ca; } for (i = 0; i < len; i ++) { ca += ((char*)buf)[i]; cb += ca; } i = _send(head, sizeof(head)); i += _send(buf, len); crc[0] = ca & 0xFF; crc[1] = cb & 0xFF; i += _send(crc, sizeof(crc)); return i; } const char* GnssParser::findNmeaItemPos(int ix, const char* start, const char* end) { // Find the start for (; (start < end) && (ix > 0); start ++) { if (*start == ',') ix --; } // Found and check bounds if ((ix == 0) && (start < end) && (*start != ',') && (*start != '*') && (*start != '\r') && (*start != '\n')) return start; else return NULL; } bool GnssParser::getNmeaItem(int ix, char* buf, int len, double& val) { char* end = &buf[len]; const char* pos = findNmeaItemPos(ix, buf, end); // Find the start if (!pos) return false; val = strtod(pos, &end); // Restore the last character return (end > pos); } bool GnssParser::getNmeaItem(int ix, char* buf, int len, int& val, int base /*=10*/) { char* end = &buf[len]; const char* pos = findNmeaItemPos(ix, buf, end); // Find the start if (!pos) return false; val = (int)strtol(pos, &end, base); return (end > pos); } bool GnssParser::getNmeaItem(int ix, char* buf, int len, char& val) { const char* end = &buf[len]; const char* pos = findNmeaItemPos(ix, buf, end); // Find the start if (!pos) return false; // Skip leading spaces while ((pos < end) && isspace(*pos)) pos++; // Check bound if ((pos < end) && (*pos != ',') && (*pos != '*') && (*pos != '\r') && (*pos != '\n')) { val = *pos; return true; } return false; } bool GnssParser::getNmeaAngle(int ix, char* buf, int len, double& val) { char ch; if (getNmeaItem(ix,buf,len,val) && getNmeaItem(ix+1,buf,len,ch) && ((ch == 'S') || (ch == 'N') || (ch == 'E') || (ch == 'W'))) { val *= 0.01; int i = (int)val; val = (val - i) / 0.6 + i; if (ch == 'S' || ch == 'W') val = -val; return true; } return false; } int GnssParser::enable_ubx() { unsigned char ubx_cfg_prt[]={0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,0x00, 0x00}; int conf = RETRY; int length = 0; while(conf) { length = sendUbx(0x06, 0x00, ubx_cfg_prt, sizeof(ubx_cfg_prt)); if(length >= (int)(sizeof(ubx_cfg_prt) + UBX_FRAME_SIZE)) { wait(5); break; } else { conf = conf - 1; } } if(conf == 0) { return 1; } unsigned char enable_ubx_nav_pvt[]={0x01, 0x07, 0x01}; conf = RETRY; while(conf) { length = sendUbx(0x06, 0x01, enable_ubx_nav_pvt, sizeof(enable_ubx_nav_pvt)); if(length >= (int)(sizeof(enable_ubx_nav_pvt) + UBX_FRAME_SIZE)) { wait(5); break; } else { conf = conf - 1; } } if(conf == 0) { return 1; } unsigned char ubx_cfg_cfg[] = { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; conf = RETRY; while(conf) { length = sendUbx(0x06, 0x09, ubx_cfg_cfg, sizeof(ubx_cfg_cfg)); if(length >= (int)(sizeof(ubx_cfg_cfg) + UBX_FRAME_SIZE)) { wait(5); break; } else { conf = conf - 1; } } if(conf == 0) { return 1; } return 0; } eUBX_MESSAGE GnssParser::get_ubx_message(char *buff) { eUBX_MESSAGE return_value = UNKNOWN_UBX; if(buff[SYNC_CHAR_INDEX_1] == 0xB5 && buff[SYNC_CHAR_INDEX_2] == 0x62) { switch (buff[MSG_CLASS_INDEX]) { case NAV: { switch (buff[MSG_ID_INDEX]) { case 0x07: { return_value = UBX_NAV_PVT; } break; case 0x09: { return_value = UBX_NAV_ODO; } break; default: { return_value = UNKNOWN_UBX; } break; } } break; case ACK: { switch (buff[MSG_ID_INDEX]) { case 0x00: { return_value = UBX_ACK_NAK; } break; case 0x01: { return_value = UBX_ACK_ACK; } break; default: { return_value = UNKNOWN_UBX; } break; } } break; case LOG: { switch (buff[MSG_ID_INDEX]) { case 0x11: { return_value = UBX_LOG_BATCH; } break; default: { return_value = UNKNOWN_UBX; } break; } } break; default: { return_value = UNKNOWN_UBX; } break; } } return return_value; } tUBX_ACK_ACK GnssParser::decode_ubx_cfg_ack_nak_msg(char *buf) { tUBX_ACK_ACK return_decoded_msg; uint8_t index = UBX_PAYLOAD_INDEX; return_decoded_msg.msg_class = buf[index++]; return_decoded_msg.msg_id = buf[index]; return return_decoded_msg; } tUBX_NAV_ODO GnssParser::decode_ubx_nav_odo_msg(char *buf) { tUBX_NAV_ODO return_decoded_msg; uint8_t index = UBX_PAYLOAD_INDEX; return_decoded_msg.version = buf[index++]; index +=3; // 3 bytes are reserved return_decoded_msg.itow = buf[index++]; return_decoded_msg.itow |= (buf[index++] << 8); return_decoded_msg.itow |= (buf[index++] << 16); return_decoded_msg.itow |= (buf[index++] << 24); return_decoded_msg.distance = buf[index++]; return_decoded_msg.distance |= (buf[index++] << 8); return_decoded_msg.distance |= (buf[index++] << 16); return_decoded_msg.distance |= (buf[index++] << 24); return_decoded_msg.totalDistance = buf[index++]; return_decoded_msg.totalDistance |= (buf[index++] << 8); return_decoded_msg.totalDistance |= (buf[index++] << 16); return_decoded_msg.totalDistance |= (buf[index++] << 24); return_decoded_msg.distanceSTD = buf[index++]; return_decoded_msg.distanceSTD |= (buf[index++] << 8); return_decoded_msg.distanceSTD |= (buf[index++] << 16); return_decoded_msg.distanceSTD |= (buf[index++] << 24); return return_decoded_msg; } tUBX_NAV_PVT GnssParser::decode_ubx_nav_pvt_msg(char *buf) { tUBX_NAV_PVT return_decoded_msg; uint8_t index = UBX_PAYLOAD_INDEX; return_decoded_msg.itow = buf[index++]; return_decoded_msg.itow |= (buf[index++] << 8); return_decoded_msg.itow |= (buf[index++] << 16); return_decoded_msg.itow |= (buf[index++] << 24); return_decoded_msg.year = buf[index++]; return_decoded_msg.year |= (buf[index++] << 8); return_decoded_msg.month = buf[index++]; return_decoded_msg.day = buf[index++]; // Go to lon index = UBX_PAYLOAD_INDEX + 24; return_decoded_msg.lon = buf[index++]; return_decoded_msg.lon |= (buf[index++] << 8); return_decoded_msg.lon |= (buf[index++] << 16); return_decoded_msg.lon |= (buf[index++] << 24); return_decoded_msg.lat = buf[index++]; return_decoded_msg.lat |= (buf[index++] << 8); return_decoded_msg.lat |= (buf[index++] << 16); return_decoded_msg.lat |= (buf[index++] << 24); return_decoded_msg.height = buf[index++]; return_decoded_msg.height |= (buf[index++] << 8); return_decoded_msg.height |= (buf[index++] << 16); return_decoded_msg.height |= (buf[index++] << 24); return return_decoded_msg; } tUBX_LOG_BATCH GnssParser::decode_ubx_log_batch_msg(char *buf) { tUBX_LOG_BATCH return_decoded_msg; uint8_t index = UBX_PAYLOAD_INDEX; // move index itow index = UBX_PAYLOAD_INDEX + 4; return_decoded_msg.itow = buf[index++]; return_decoded_msg.itow |= (buf[index++] << 8); return_decoded_msg.itow |= (buf[index++] << 16); return_decoded_msg.itow |= (buf[index++] << 24); // move index lon index = UBX_PAYLOAD_INDEX + 24; return_decoded_msg.lon = buf[index++]; return_decoded_msg.lon |= (buf[index++] << 8); return_decoded_msg.lon |= (buf[index++] << 16); return_decoded_msg.lon |= (buf[index++] << 24); return_decoded_msg.lat = buf[index++]; return_decoded_msg.lat |= (buf[index++] << 8); return_decoded_msg.lat |= (buf[index++] << 16); return_decoded_msg.lat |= (buf[index++] << 24); return_decoded_msg.height = buf[index++]; return_decoded_msg.height |= (buf[index++] << 8); return_decoded_msg.height |= (buf[index++] << 16); return_decoded_msg.height |= (buf[index++] << 24); // move index to distance index = UBX_PAYLOAD_INDEX + 84; return_decoded_msg.distance = buf[index++]; return_decoded_msg.distance |= (buf[index++] << 8); return_decoded_msg.distance |= (buf[index++] << 16); return_decoded_msg.distance |= (buf[index++] << 24); return_decoded_msg.totalDistance = buf[index++]; return_decoded_msg.totalDistance |= (buf[index++] << 8); return_decoded_msg.totalDistance |= (buf[index++] << 16); return_decoded_msg.totalDistance |= (buf[index++] << 24); return_decoded_msg.distanceSTD = buf[index++]; return_decoded_msg.distanceSTD |= (buf[index++] << 8); return_decoded_msg.distanceSTD |= (buf[index++] << 16); return_decoded_msg.distanceSTD |= (buf[index++] << 24); return return_decoded_msg; } int GnssParser::ubx_request_batched_data(bool sendMonFirst) { unsigned char ubx_log_retrieve_batch[]={0x00, 0x00, 0x00, 0x00}; ubx_log_retrieve_batch[1] = (sendMonFirst == true) ? 0x01 : 0x00; int conf = RETRY; while(conf) { int length = sendUbx(0x21, 0x10, ubx_log_retrieve_batch, sizeof(ubx_log_retrieve_batch)); if(length >= (int)(sizeof(ubx_log_retrieve_batch) + UBX_FRAME_SIZE)) { wait(5); break; } else { conf = conf - 1; } } if(conf == 0) { return 1; } return 0; } const char GnssParser::_toHex[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; // ---------------------------------------------------------------- // Serial Implementation // ---------------------------------------------------------------- GnssSerial::GnssSerial(PinName tx /*= GNSSTXD */, PinName rx /*= GNSSRXD */, int baudrate /*= GNSSBAUD */, int rxSize /*= 256 */, int txSize /*= 128 */) : SerialPipe(tx, rx, baudrate, rxSize, txSize) { baud(baudrate); } GnssSerial::~GnssSerial(void) { powerOff(); } bool GnssSerial::init(PinName pn) { Timer timer; int size; // Unused (kept only for compatibility with the I2C version) (void)pn; // Power up and enable the module _powerOn(); // Send a byte to wakup the device again putc(0xFF); // Wait until we get some bytes size = _pipeRx.size(); timer.start(); while ((timer.read_ms() < 1000) && (size == _pipeRx.size())) { /* Nothing, just wait */ } timer.stop(); enable_ubx(); return (size != _pipeRx.size()); } int GnssSerial::getMessage(char* buf, int len) { return _getMessage(&_pipeRx, buf, len); } int GnssSerial::_send(const void* buf, int len) { return put((const char*)buf, len, true/*=blocking*/); } // ---------------------------------------------------------------- // I2C Implementation // ---------------------------------------------------------------- GnssI2C::GnssI2C(PinName sda /*= NC */, PinName scl /*= NC */, unsigned char i2cAdr /*= (66<<1) */, int rxSize /*= 256 */) : I2C(sda,scl), _pipe(rxSize), _i2cAdr(i2cAdr) { frequency(100000); } GnssI2C::~GnssI2C(void) { powerOff(); } bool GnssI2C::init(PinName pn) { // Power up and enable the module _powerOn(); if (pn != NC) { DigitalOut pin(pn, 0); ::wait_us(1); pin = 1; ::wait_ms(100); } return !I2C::write(_i2cAdr,®STREAM,sizeof(REGSTREAM)); } int GnssI2C::getMessage(char* buf, int len) { // Fill the pipe int sz = _pipe.free(); if (sz) sz = _get(buf, sz); if (sz) _pipe.put(buf, sz); // Now parse it return _getMessage(&_pipe, buf, len); } int GnssI2C::send(const char* buf, int len) { int sent = 0; if (len) { if (!I2C::write(_i2cAdr,®STREAM,sizeof(REGSTREAM),true)) sent = send(buf, len); stop(); } return sent; } int GnssI2C::sendNmea(const char* buf, int len) { int sent = 0; if (!I2C::write(_i2cAdr,®STREAM,sizeof(REGSTREAM),true)) sent = GnssParser::sendNmea(buf, len); stop(); return sent; } int GnssI2C::sendUbx(unsigned char cls, unsigned char id, const void* buf, int len) { int sent = 0; if (!I2C::write(_i2cAdr,®STREAM,sizeof(REGSTREAM),true)) sent = GnssParser::sendUbx(cls, id, buf, len); I2C::stop(); return sent; } int GnssI2C::_get(char* buf, int len) { int read = 0; unsigned char sz[2] = {0,0}; if (!I2C::write(_i2cAdr,®LEN,sizeof(REGLEN),true) && !I2C::read(_i2cAdr,(char*)sz,sizeof(sz))) { int size = 256 * (int)sz[0] + sz[1]; if (size > len) size = len; if (size > 0) { if (!I2C::write(_i2cAdr,®STREAM,sizeof(REGSTREAM),true) && !I2C::read(_i2cAdr,buf,size)) { read = size; } } } return read; } int GnssI2C::_send(const void* buf, int len) { return !I2C::write(_i2cAdr,(const char*)buf,len,true) ? len : 0; } const char GnssI2C::REGLEN = 0xFD; const char GnssI2C::REGSTREAM = 0xFF; // End Of File