• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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