123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- /*
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- //
- // Emlid Reach Binary (ERB) GPS driver for ArduPilot.
- // ERB protocol: http://files.emlid.com/ERB.pdf
- #include "AP_GPS.h"
- #include "AP_GPS_ERB.h"
- #define ERB_DEBUGGING 0
- #define STAT_FIX_VALID 0x01
- extern const AP_HAL::HAL& hal;
- #if ERB_DEBUGGING
- # define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
- #else
- # define Debug(fmt, args ...)
- #endif
- // Process bytes available from the stream
- //
- // The stream is assumed to contain only messages we recognise. If it
- // contains other messages, and those messages contain the preamble
- // bytes, it is possible for this code to fail to synchronise to the
- // stream immediately. Without buffering the entire message and
- // re-processing it from the top, this is unavoidable. The parser
- // attempts to avoid this when possible.
- //
- bool
- AP_GPS_ERB::read(void)
- {
- uint8_t data;
- int16_t numc;
- bool parsed = false;
- numc = port->available();
- for (int16_t i = 0; i < numc; i++) { // Process bytes received
- // read the next byte
- data = port->read();
- reset:
- switch(_step) {
- // Message preamble detection
- //
- case 1:
- if (PREAMBLE2 == data) {
- _step++;
- break;
- }
- _step = 0;
- Debug("reset %u", __LINE__);
- FALLTHROUGH;
- case 0:
- if(PREAMBLE1 == data)
- _step++;
- break;
- // Message header processing
- //
- case 2:
- _step++;
- _msg_id = data;
- _ck_b = _ck_a = data; // reset the checksum accumulators
- break;
- case 3:
- _step++;
- _ck_b += (_ck_a += data); // checksum byte
- _payload_length = data; // payload length low byte
- break;
- case 4:
- _step++;
- _ck_b += (_ck_a += data); // checksum byte
- _payload_length += (uint16_t)(data<<8);
- _payload_counter = 0; // prepare to receive payload
- break;
- // Receive message data
- //
- case 5:
- _ck_b += (_ck_a += data); // checksum byte
- if (_payload_counter < sizeof(_buffer)) {
- _buffer[_payload_counter] = data;
- }
- if (++_payload_counter == _payload_length)
- _step++;
- break;
- // Checksum and message processing
- //
- case 6:
- _step++;
- if (_ck_a != data) {
- Debug("bad cka %x should be %x", data, _ck_a);
- _step = 0;
- goto reset;
- }
- break;
- case 7:
- _step = 0;
- if (_ck_b != data) {
- Debug("bad ckb %x should be %x", data, _ck_b);
- break; // bad checksum
- }
- if (_parse_gps()) {
- parsed = true;
- }
- break;
- }
- }
- return parsed;
- }
- bool
- AP_GPS_ERB::_parse_gps(void)
- {
- switch (_msg_id) {
- case MSG_VER:
- Debug("Version of ERB protocol %u.%u.%u",
- _buffer.ver.ver_high,
- _buffer.ver.ver_medium,
- _buffer.ver.ver_low);
- break;
- case MSG_POS:
- Debug("Message POS");
- _last_pos_time = _buffer.pos.time;
- state.location.lng = (int32_t)(_buffer.pos.longitude * (double)1e7);
- state.location.lat = (int32_t)(_buffer.pos.latitude * (double)1e7);
- state.location.alt = (int32_t)(_buffer.pos.altitude_msl * 100);
- state.status = next_fix;
- _new_position = true;
- state.horizontal_accuracy = _buffer.pos.horizontal_accuracy * 1.0e-3f;
- state.vertical_accuracy = _buffer.pos.vertical_accuracy * 1.0e-3f;
- state.have_horizontal_accuracy = true;
- state.have_vertical_accuracy = true;
- break;
- case MSG_STAT:
- Debug("Message STAT fix_status=%u fix_type=%u",
- _buffer.stat.fix_status,
- _buffer.stat.fix_type);
- if (_buffer.stat.fix_status & STAT_FIX_VALID) {
- if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_FIX) {
- next_fix = AP_GPS::GPS_OK_FIX_3D_RTK_FIXED;
- } else if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_FLOAT) {
- next_fix = AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT;
- } else if (_buffer.stat.fix_type == AP_GPS_ERB::FIX_SINGLE) {
- next_fix = AP_GPS::GPS_OK_FIX_3D;
- } else {
- next_fix = AP_GPS::NO_FIX;
- state.status = AP_GPS::NO_FIX;
- }
- } else {
- next_fix = AP_GPS::NO_FIX;
- state.status = AP_GPS::NO_FIX;
- }
- state.num_sats = _buffer.stat.satellites;
- if (next_fix >= AP_GPS::GPS_OK_FIX_3D) {
- // use the uart receive time to make packet timestamps more accurate
- set_uart_timestamp(_payload_length + sizeof(erb_header) + 2);
- state.last_gps_time_ms = AP_HAL::millis();
- state.time_week_ms = _buffer.stat.time;
- state.time_week = _buffer.stat.week;
- }
- break;
- case MSG_DOPS:
- Debug("Message DOPS");
- state.hdop = _buffer.dops.hDOP;
- state.vdop = _buffer.dops.vDOP;
- break;
- case MSG_VEL:
- Debug("Message VEL");
- _last_vel_time = _buffer.vel.time;
- state.ground_speed = _buffer.vel.speed_2d * 0.01f; // m/s
- // Heading 2D deg * 100000 rescaled to deg * 100
- state.ground_course = wrap_360(_buffer.vel.heading_2d * 1.0e-5f);
- state.have_vertical_velocity = true;
- state.velocity.x = _buffer.vel.vel_north * 0.01f;
- state.velocity.y = _buffer.vel.vel_east * 0.01f;
- state.velocity.z = _buffer.vel.vel_down * 0.01f;
- state.have_speed_accuracy = true;
- state.speed_accuracy = _buffer.vel.speed_accuracy * 0.01f;
- _new_speed = true;
- break;
- case MSG_RTK:
- Debug("Message RTK");
- state.rtk_baseline_coords_type = RTK_BASELINE_COORDINATE_SYSTEM_NED;
- state.rtk_num_sats = _buffer.rtk.base_num_sats;
- if (_buffer.rtk.age_cs == 0xFFFF) {
- state.rtk_age_ms = 0xFFFFFFFF;
- } else {
- state.rtk_age_ms = _buffer.rtk.age_cs * 10;
- }
- state.rtk_baseline_x_mm = _buffer.rtk.baseline_N_mm;
- state.rtk_baseline_y_mm = _buffer.rtk.baseline_E_mm;
- state.rtk_baseline_z_mm = _buffer.rtk.baseline_D_mm;
- state.rtk_accuracy = _buffer.rtk.ar_ratio;
- state.rtk_week_number = _buffer.rtk.base_week_number;
- state.rtk_time_week_ms = _buffer.rtk.base_time_week_ms;
- break;
- default:
- Debug("Unexpected message 0x%02x", (unsigned)_msg_id);
- return false;
- }
- // we only return true when we get new position and speed data
- // this ensures we don't use stale data
- if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {
- _new_speed = _new_position = false;
- _fix_count++;
- return true;
- }
- return false;
- }
- /*
- detect a ERB GPS. Adds one byte, and returns true if the stream
- matches a ERB
- */
- bool
- AP_GPS_ERB::_detect(struct ERB_detect_state &state, uint8_t data)
- {
- reset:
- switch (state.step) {
- case 1:
- if (PREAMBLE2 == data) {
- state.step++;
- break;
- }
- state.step = 0;
- FALLTHROUGH;
- case 0:
- if (PREAMBLE1 == data)
- state.step++;
- break;
- case 2:
- state.step++;
- state.ck_b = state.ck_a = data;
- break;
- case 3:
- state.step++;
- state.ck_b += (state.ck_a += data);
- state.payload_length = data;
- break;
- case 4:
- state.step++;
- state.ck_b += (state.ck_a += data);
- state.payload_counter = 0;
- break;
- case 5:
- state.ck_b += (state.ck_a += data);
- if (++state.payload_counter == state.payload_length)
- state.step++;
- break;
- case 6:
- state.step++;
- if (state.ck_a != data) {
- state.step = 0;
- goto reset;
- }
- break;
- case 7:
- state.step = 0;
- if (state.ck_b == data) {
- return true;
- } else {
- goto reset;
- }
- }
- return false;
- }
|