CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_Airspeed/AP_Airspeed_NMEA.cpp
Views: 1798
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program. If not, see <http://www.gnu.org/licenses/>.
14
*/
15
/*
16
* NMEA Sensor driver for VHW ans MTW messages over Serial
17
* https://gpsd.gitlab.io/gpsd/NMEA.html#_vhw_water_speed_and_heading
18
* https://gpsd.gitlab.io/gpsd/NMEA.html#_mtw_mean_temperature_of_water
19
*/
20
21
#include "AP_Airspeed_NMEA.h"
22
23
#if AP_AIRSPEED_NMEA_ENABLED
24
25
#include <AP_Vehicle/AP_Vehicle_Type.h>
26
#if APM_BUILD_TYPE(APM_BUILD_Rover) || APM_BUILD_TYPE(APM_BUILD_ArduSub)
27
28
#include "AP_Airspeed.h"
29
#include <AP_SerialManager/AP_SerialManager.h>
30
31
#define TIMEOUT_MS 2000
32
33
bool AP_Airspeed_NMEA::init()
34
{
35
const AP_SerialManager& serial_manager = AP::serialmanager();
36
37
_uart = serial_manager.find_serial(AP_SerialManager::SerialProtocol_AirSpeed, 0);
38
if (_uart == nullptr) {
39
return false;
40
}
41
42
set_bus_id(AP_HAL::Device::make_bus_id(AP_HAL::Device::BUS_TYPE_SERIAL,0,0,0));
43
44
_uart->begin(serial_manager.find_baudrate(AP_SerialManager::SerialProtocol_AirSpeed, 0));
45
46
// make sure this sensor cannot be used in the EKF
47
set_use(0);
48
49
// must set use zero offset to pass offset check for health
50
set_use_zero_offset();
51
52
return true;
53
}
54
55
// read the from the sensor
56
bool AP_Airspeed_NMEA::get_airspeed(float &airspeed)
57
{
58
if (_uart == nullptr) {
59
return false;
60
}
61
62
uint32_t now = AP_HAL::millis();
63
64
// read any available lines from the sensor
65
float sum = 0.0f;
66
uint16_t count = 0;
67
int16_t nbytes = _uart->available();
68
while (nbytes-- > 0) {
69
int16_t c = _uart->read();
70
if (c==-1) {
71
return false;
72
}
73
if (decode(char(c))) {
74
_last_update_ms = now;
75
if (_sentence_type == TYPE_VHW) {
76
sum += _speed;
77
count++;
78
} else {
79
_temp_sum += _temp;
80
_temp_count++;
81
}
82
}
83
}
84
85
if (count == 0) {
86
// Cant return false because updates are too slow, return previous reading
87
// Could return false after some timeout, however testing shows that the DST800 just stops sending the message at zero speed
88
airspeed = _last_speed;
89
} else {
90
// return average of all measurements
91
airspeed = sum / count;
92
_last_speed = airspeed;
93
}
94
95
return (now - _last_update_ms) < TIMEOUT_MS;
96
}
97
98
// return the current temperature in degrees C
99
// the main update is done in the get_pressure function
100
// this just reports the value
101
bool AP_Airspeed_NMEA::get_temperature(float &temperature)
102
{
103
if (_uart == nullptr) {
104
return false;
105
}
106
107
if (_temp_count == 0) {
108
temperature = _last_temp;
109
} else {
110
// return average of all measurements
111
temperature = _temp_sum / _temp_count;
112
_last_temp = temperature;
113
_temp_count = 0;
114
_temp_sum = 0;
115
}
116
117
return true;
118
}
119
120
121
// add a single character to the buffer and attempt to decode
122
// returns true if a complete sentence was successfully decoded
123
bool AP_Airspeed_NMEA::decode(char c)
124
{
125
switch (c) {
126
case ',':
127
// end of a term, add to checksum
128
_checksum ^= c;
129
FALLTHROUGH;
130
case '\r':
131
case '\n':
132
case '*':
133
{
134
if (!_sentence_done && _sentence_valid) {
135
// null terminate and decode latest term
136
_term[_term_offset] = 0;
137
bool valid_sentence = decode_latest_term();
138
139
// move onto next term
140
_term_number++;
141
_term_offset = 0;
142
_term_is_checksum = (c == '*');
143
return valid_sentence;
144
}
145
return false;
146
}
147
148
case '$': // sentence begin
149
_term_number = 0;
150
_term_offset = 0;
151
_checksum = 0;
152
_term_is_checksum = false;
153
_sentence_done = false;
154
_sentence_valid = true;
155
return false;
156
}
157
158
// ordinary characters are added to term
159
if (_term_offset < sizeof(_term) - 1) {
160
_term[_term_offset++] = c;
161
}
162
if (!_term_is_checksum) {
163
_checksum ^= c;
164
}
165
166
return false;
167
}
168
169
// decode the most recently consumed term
170
// returns true if new sentence has just passed checksum test and is validated
171
bool AP_Airspeed_NMEA::decode_latest_term()
172
{
173
// handle the last term in a message
174
if (_term_is_checksum) {
175
_sentence_done = true;
176
uint8_t nibble_high = 0;
177
uint8_t nibble_low = 0;
178
if (!hex_to_uint8(_term[0], nibble_high) || !hex_to_uint8(_term[1], nibble_low)) {
179
return false;
180
}
181
const uint8_t checksum = (nibble_high << 4u) | nibble_low;
182
return checksum == _checksum;
183
}
184
185
// the first term determines the sentence type
186
if (_term_number == 0) {
187
// the first two letters of the NMEA term are the talker ID.
188
// we accept any two characters here.
189
// actually expecting YX for MTW and VW for VHW
190
if (_term[0] < 'A' || _term[0] > 'Z' ||
191
_term[1] < 'A' || _term[1] > 'Z') {
192
return false;
193
}
194
const char *term_type = &_term[2];
195
if (strcmp(term_type, "MTW") == 0) {
196
_sentence_type = TPYE_MTW;
197
} else if (strcmp(term_type, "VHW") == 0) {
198
_sentence_type = TYPE_VHW;
199
} else {
200
_sentence_valid = false;
201
}
202
return false;
203
}
204
205
if (_sentence_type == TPYE_MTW) {
206
// parse MTW messages
207
if (_term_number == 1) {
208
_temp = strtof(_term, NULL);
209
}
210
} else if (_sentence_type == TYPE_VHW) {
211
// parse VHW messages
212
if (_term_number == 7) {
213
_speed = strtof(_term, NULL) * KM_PER_HOUR_TO_M_PER_SEC;
214
}
215
}
216
217
return false;
218
}
219
220
#endif // APM_BUILD_TYPE(APM_BUILD_Rover) || APM_BUILD_TYPE(APM_BUILD_ArduSub)
221
222
#endif // AP_AIRSPEED_NMEA_ENABLED
223
224