1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <chrono>
18 #include <log/log.h>
19 #include <utils/SystemClock.h>
20 #include "GnssHwListener.h"
21
22 namespace aidl {
23 namespace android {
24 namespace hardware {
25 namespace gnss {
26 namespace implementation {
27
28 namespace {
testNmeaField(const char * i,const char * end,const char * v,const char sep)29 const char* testNmeaField(const char* i, const char* end,
30 const char* v,
31 const char sep) {
32 while (i < end) {
33 if (*v == 0) {
34 return (*i == sep) ? (i + 1) : nullptr;
35 } else if (*v == *i) {
36 ++v;
37 ++i;
38 } else {
39 return nullptr;
40 }
41 }
42
43 return nullptr;
44 }
45
skipAfter(const char * i,const char * end,const char c)46 const char* skipAfter(const char* i, const char* end, const char c) {
47 for (; i < end; ++i) {
48 if (*i == c) {
49 return i + 1;
50 }
51 }
52 return nullptr;
53 }
54
convertDMMF(const int dmm,const int f,int p10)55 double convertDMMF(const int dmm, const int f, int p10) {
56 const int d = dmm / 100;
57 const int m = dmm % 100;
58 int base10 = 1;
59 for (; p10 > 0; --p10) { base10 *= 10; }
60
61 return double(d) + (m + (f / double(base10))) / 60.0;
62 }
63
sign(char m,char positive)64 double sign(char m, char positive) { return (m == positive) ? 1.0 : -1; }
65
makeElapsedRealtime(const int64_t timestampNs)66 ElapsedRealtime makeElapsedRealtime(const int64_t timestampNs) {
67 return {
68 .flags = ElapsedRealtime::HAS_TIMESTAMP_NS |
69 ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
70 .timestampNs = timestampNs,
71 .timeUncertaintyNs = 1000000.0
72 };
73 }
74
75 } // namespace
76
GnssHwListener(IDataSink & sink)77 GnssHwListener::GnssHwListener(IDataSink& sink): mSink(sink) {
78 mBuffer.reserve(256);
79 mSink.onGnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_ON);
80 }
81
~GnssHwListener()82 GnssHwListener::~GnssHwListener() {
83 mSink.onGnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_OFF);
84 }
85
consume(const char * buf,size_t sz)86 void GnssHwListener::consume(const char* buf, size_t sz) {
87 ALOGD("%s:%s:%d sz=%zu", "GnssHwListener", __func__, __LINE__, sz);
88
89 for (; sz > 0; ++buf, --sz) {
90 consume1(*buf);
91 }
92 }
93
consume1(const char c)94 void GnssHwListener::consume1(const char c) {
95 if (c == '$' || !mBuffer.empty()) {
96 mBuffer.push_back(c);
97 }
98 if (c == '\n') {
99 using namespace std::chrono;
100
101 const int64_t timestampMs = time_point_cast<milliseconds>(
102 system_clock::now()).time_since_epoch().count();
103 const ElapsedRealtime ert = makeElapsedRealtime(
104 ::android::elapsedRealtimeNano());
105
106 if (parse(mBuffer.data() + 1, mBuffer.data() + mBuffer.size() - 2,
107 timestampMs, ert)) {
108 mSink.onGnssNmeaCb(timestampMs, std::string(mBuffer.data(), mBuffer.size()));
109 } else {
110 mBuffer.back() = 0;
111 ALOGW("%s:%d: failed to parse an NMEA message, '%s'",
112 __func__, __LINE__, mBuffer.data());
113 }
114 mBuffer.clear();
115 } else if (mBuffer.size() >= 1024) {
116 ALOGW("%s:%d buffer was too long, dropped", __func__, __LINE__);
117 mBuffer.clear();
118 }
119 }
120
parse(const char * begin,const char * end,const int64_t timestampMs,const ElapsedRealtime & ert)121 bool GnssHwListener::parse(const char* begin, const char* end,
122 const int64_t timestampMs,
123 const ElapsedRealtime& ert) {
124 if (const char* fields = testNmeaField(begin, end, "GPRMC", ',')) {
125 return parseGPRMC(fields, end, timestampMs, ert);
126 } else if (const char* fields = testNmeaField(begin, end, "GPGGA", ',')) {
127 return parseGPGGA(fields, end, timestampMs, ert);
128 } else {
129 return false;
130 }
131 }
132
133 // begin end
134 // $GPRMC,195206,A,1000.0000,N,10000.0000,E,173.8,231.8,010420,004.2,W*47
135 // 1 2 3 4 5 6 7 8 9 10 11 12
136 // 1 195206 Time Stamp
137 // 2 A validity - A-ok, V-invalid
138 // 3 1000.0000 current Latitude
139 // 4 N North/South
140 // 5 10000.0000 current Longitude
141 // 6 E East/West
142 // 7 173.8 Speed in knots
143 // 8 231.8 True course
144 // 9 010420 Date Stamp (13 June 1994)
145 // 10 004.2 Variation
146 // 11 W East/West
147 // 12 *70 checksum
parseGPRMC(const char * begin,const char *,const int64_t timestampMs,const ElapsedRealtime & ert)148 bool GnssHwListener::parseGPRMC(const char* begin, const char*,
149 const int64_t timestampMs,
150 const ElapsedRealtime& ert) {
151 double speedKnots = 0;
152 double course = 0;
153 double variation = 0;
154 int latdmm = 0;
155 int londmm = 0;
156 int latf = 0;
157 int lonf = 0;
158 int latdmmConsumed = 0;
159 int latfConsumed = 0;
160 int londmmConsumed = 0;
161 int lonfConsumed = 0;
162 int hhmmss = -1;
163 int ddmoyy = 0;
164 char validity = 0;
165 char ns = 0; // north/south
166 char ew = 0; // east/west
167 char var_ew = 0;
168
169 if (sscanf(begin, "%06d,%c,%d.%n%d%n,%c,%d.%n%d%n,%c,%lf,%lf,%d,%lf,%c*",
170 &hhmmss, &validity,
171 &latdmm, &latdmmConsumed, &latf, &latfConsumed, &ns,
172 &londmm, &londmmConsumed, &lonf, &lonfConsumed, &ew,
173 &speedKnots, &course,
174 &ddmoyy,
175 &variation, &var_ew) != 13) {
176 return false;
177 }
178 if (validity != 'A') {
179 return false;
180 }
181
182 const double lat = convertDMMF(latdmm, latf, latfConsumed - latdmmConsumed) * sign(ns, 'N');
183 const double lon = convertDMMF(londmm, lonf, lonfConsumed - londmmConsumed) * sign(ew, 'E');
184 const double speed = speedKnots * 0.514444;
185
186 GnssLocation loc;
187 loc.elapsedRealtime = ert;
188
189 loc.latitudeDegrees = lat;
190 loc.longitudeDegrees = lon;
191 loc.speedMetersPerSec = speed;
192 loc.bearingDegrees = course;
193 loc.horizontalAccuracyMeters = 5;
194 loc.speedAccuracyMetersPerSecond = .5;
195 loc.bearingAccuracyDegrees = 30;
196 loc.timestampMillis = timestampMs;
197
198 loc.gnssLocationFlags =
199 GnssLocation::HAS_LAT_LONG |
200 GnssLocation::HAS_SPEED |
201 GnssLocation::HAS_BEARING |
202 GnssLocation::HAS_HORIZONTAL_ACCURACY |
203 GnssLocation::HAS_SPEED_ACCURACY |
204 GnssLocation::HAS_BEARING_ACCURACY;
205
206 if (mAltitude.has_value()) {
207 loc.altitudeMeters = mAltitude.value();
208 loc.verticalAccuracyMeters = .5;
209 loc.gnssLocationFlags |= GnssLocation::HAS_ALTITUDE |
210 GnssLocation::HAS_VERTICAL_ACCURACY;
211 }
212
213 mSink.onGnssLocationCb(loc);
214
215 return true;
216 }
217
218 // $GPGGA,123519,4807.0382,N,12204.9799,W,1,6,,4.2,M,0.,M,,,*47
219 // time of fix 123519 12:35:19 UTC
220 // latitude 4807.0382 48 degrees, 07.0382 minutes
221 // north/south N or S
222 // longitude 12204.9799 122 degrees, 04.9799 minutes
223 // east/west E or W
224 // fix quality 1 standard GPS fix
225 // satellites 1 to 12 number of satellites being tracked
226 // HDOP <dontcare> horizontal dilution
227 // altitude 4.2 altitude above sea-level
228 // altitude units M to indicate meters
229 // diff <dontcare> height of sea-level above ellipsoid
230 // diff units M to indicate meters (should be <dontcare>)
231 // dgps age <dontcare> time in seconds since last DGPS fix
232 // dgps sid <dontcare> DGPS station id
parseGPGGA(const char * begin,const char * end,const int64_t,const ElapsedRealtime &)233 bool GnssHwListener::parseGPGGA(const char* begin, const char* end,
234 const int64_t /*timestampMs*/,
235 const ElapsedRealtime& /*ert*/) {
236 double altitude = 0;
237 int latdmm = 0;
238 int londmm = 0;
239 int latf = 0;
240 int lonf = 0;
241 int latdmmConsumed = 0;
242 int latfConsumed = 0;
243 int londmmConsumed = 0;
244 int lonfConsumed = 0;
245 int hhmmss = 0;
246 int fixQuality = 0;
247 int nSatellites = 0;
248 int consumed = 0;
249 char ns = 0;
250 char ew = 0;
251 char altitudeUnit = 0;
252
253 if (sscanf(begin, "%06d,%d.%n%d%n,%c,%d.%n%d%n,%c,%d,%d,%n",
254 &hhmmss,
255 &latdmm, &latdmmConsumed, &latf, &latfConsumed, &ns,
256 &londmm, &londmmConsumed, &lonf, &lonfConsumed, &ew,
257 &fixQuality,
258 &nSatellites,
259 &consumed) != 9) {
260 return false;
261 }
262
263 begin = skipAfter(begin + consumed, end, ','); // skip HDOP
264 if (!begin) {
265 return false;
266 }
267 if (sscanf(begin, "%lf,%c,", &altitude, &altitudeUnit) != 2) {
268 return false;
269 }
270 if (altitudeUnit != 'M') {
271 return false;
272 }
273
274 mAltitude = altitude;
275
276 std::vector<IGnssCallback::GnssSvInfo> svInfo(nSatellites);
277 for (int i = 0; i < nSatellites; ++i) {
278 auto* info = &svInfo[i];
279
280 info->svid = i + 3;
281 info->constellation = GnssConstellationType::GPS;
282 info->cN0Dbhz = 30;
283 info->basebandCN0DbHz = 42;
284 info->elevationDegrees = 0;
285 info->azimuthDegrees = 0;
286 info->carrierFrequencyHz = 1.59975e+09;
287 info->svFlag = static_cast<int>(IGnssCallback::GnssSvFlags::HAS_CARRIER_FREQUENCY);
288 }
289
290 mSink.onGnssSvStatusCb(std::move(svInfo));
291
292 return true;
293 }
294
295 } // namespace implementation
296 } // namespace gnss
297 } // namespace hardware
298 } // namespace android
299 } // namespace aidl
300