OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/geolocation/gps_location_provider_linux.h" | |
6 | |
7 #include <errno.h> | |
8 | |
9 #include <algorithm> | |
10 #include <cmath> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/compiler_specific.h" | |
14 #include "base/logging.h" | |
15 #include "base/memory/scoped_ptr.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "content/public/common/geoposition.h" | |
19 | |
20 namespace content { | |
21 namespace { | |
22 | |
23 const int kGpsdReconnectRetryIntervalMillis = 10 * 1000; | |
24 | |
25 // As per http://gpsd.berlios.de/performance.html#id374524, poll twice per sec. | |
26 const int kPollPeriodMovingMillis = 500; | |
27 | |
28 // Poll less frequently whilst stationary. | |
29 const int kPollPeriodStationaryMillis = kPollPeriodMovingMillis * 3; | |
30 | |
31 // GPS reading must differ by more than this amount to be considered movement. | |
32 const int kMovementThresholdMeters = 20; | |
33 | |
34 // This algorithm is reused from the corresponding code in the Gears project. | |
35 // The arbitrary delta is decreased (Gears used 100 meters); if we need to | |
36 // decrease it any further we'll likely want to do some smarter filtering to | |
37 // remove GPS location jitter noise. | |
38 bool PositionsDifferSiginificantly(const Geoposition& position_1, | |
39 const Geoposition& position_2) { | |
40 const bool pos_1_valid = position_1.Validate(); | |
41 if (pos_1_valid != position_2.Validate()) | |
42 return true; | |
43 if (!pos_1_valid) { | |
44 DCHECK(!position_2.Validate()); | |
45 return false; | |
46 } | |
47 double delta = std::sqrt( | |
48 std::pow(std::fabs(position_1.latitude - position_2.latitude), 2) + | |
49 std::pow(std::fabs(position_1.longitude - position_2.longitude), 2)); | |
50 // Convert to meters. 1 minute of arc of latitude (or longitude at the | |
51 // equator) is 1 nautical mile or 1852m. | |
52 delta *= 60 * 1852; | |
53 return delta > kMovementThresholdMeters; | |
54 } | |
55 | |
56 } // namespace | |
57 | |
58 #if defined(USE_LIBGPS) | |
59 | |
60 // See http://crbug.com/103751. | |
61 COMPILE_ASSERT(GPSD_API_MAJOR_VERSION == 5, GPSD_API_version_is_not_5); | |
62 | |
63 namespace { | |
64 | |
65 const char kLibGpsName[] = "libgps.so.20"; | |
66 | |
67 } // namespace | |
68 | |
69 LibGps::LibGps() | |
70 : gps_data_(new gps_data_t), | |
71 is_open_(false) { | |
72 } | |
73 | |
74 LibGps::~LibGps() { | |
75 Stop(); | |
76 } | |
77 | |
78 LibGps* LibGps::New() { | |
79 scoped_ptr<LibGps> libgps(new LibGps); | |
80 if (!libgps->libgps_loader_.Load(kLibGpsName)) | |
81 return NULL; | |
82 | |
83 return libgps.release(); | |
84 } | |
85 | |
86 bool LibGps::Start() { | |
87 if (is_open_) | |
88 return true; | |
89 | |
90 errno = 0; | |
91 if (libgps_loader_.gps_open(GPSD_SHARED_MEMORY, 0, gps_data_.get()) != 0) { | |
92 // See gps.h NL_NOxxx for definition of gps_open() error numbers. | |
93 DLOG(WARNING) << "gps_open() failed " << errno; | |
94 return false; | |
95 } | |
96 | |
97 is_open_ = true; | |
98 return true; | |
99 } | |
100 | |
101 void LibGps::Stop() { | |
102 if (is_open_) | |
103 libgps_loader_.gps_close(gps_data_.get()); | |
104 is_open_ = false; | |
105 } | |
106 | |
107 bool LibGps::Read(Geoposition* position) { | |
108 DCHECK(position); | |
109 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; | |
110 if (!is_open_) { | |
111 DLOG(WARNING) << "No gpsd connection"; | |
112 position->error_message = "No gpsd connection"; | |
113 return false; | |
114 } | |
115 | |
116 if (libgps_loader_.gps_read(gps_data_.get()) < 0) { | |
117 DLOG(WARNING) << "gps_read() fails"; | |
118 position->error_message = "gps_read() fails"; | |
119 return false; | |
120 } | |
121 | |
122 if (!GetPositionIfFixed(position)) { | |
123 DLOG(WARNING) << "No fixed position"; | |
124 position->error_message = "No fixed position"; | |
125 return false; | |
126 } | |
127 | |
128 position->error_code = Geoposition::ERROR_CODE_NONE; | |
129 position->timestamp = base::Time::Now(); | |
130 if (!position->Validate()) { | |
131 // GetPositionIfFixed returned true, yet we've not got a valid fix. | |
132 // This shouldn't happen; something went wrong in the conversion. | |
133 NOTREACHED() << "Invalid position from GetPositionIfFixed: lat,long " | |
134 << position->latitude << "," << position->longitude | |
135 << " accuracy " << position->accuracy << " time " | |
136 << position->timestamp.ToDoubleT(); | |
137 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; | |
138 position->error_message = "Bad fix from gps"; | |
139 return false; | |
140 } | |
141 return true; | |
142 } | |
143 | |
144 bool LibGps::GetPositionIfFixed(Geoposition* position) { | |
145 DCHECK(position); | |
146 if (gps_data_->status == STATUS_NO_FIX) { | |
147 DVLOG(2) << "Status_NO_FIX"; | |
148 return false; | |
149 } | |
150 | |
151 if (isnan(gps_data_->fix.latitude) || isnan(gps_data_->fix.longitude)) { | |
152 DVLOG(2) << "No valid lat/lon value"; | |
153 return false; | |
154 } | |
155 | |
156 position->latitude = gps_data_->fix.latitude; | |
157 position->longitude = gps_data_->fix.longitude; | |
158 | |
159 if (!isnan(gps_data_->fix.epx) && !isnan(gps_data_->fix.epy)) { | |
160 position->accuracy = std::max(gps_data_->fix.epx, gps_data_->fix.epy); | |
161 } else if (isnan(gps_data_->fix.epx) && !isnan(gps_data_->fix.epy)) { | |
162 position->accuracy = gps_data_->fix.epy; | |
163 } else if (!isnan(gps_data_->fix.epx) && isnan(gps_data_->fix.epy)) { | |
164 position->accuracy = gps_data_->fix.epx; | |
165 } else { | |
166 // TODO(joth): Fixme. This is a workaround for http://crbug.com/99326 | |
167 DVLOG(2) << "libgps reported accuracy NaN, forcing to zero"; | |
168 position->accuracy = 0; | |
169 } | |
170 | |
171 if (gps_data_->fix.mode == MODE_3D && !isnan(gps_data_->fix.altitude)) { | |
172 position->altitude = gps_data_->fix.altitude; | |
173 if (!isnan(gps_data_->fix.epv)) | |
174 position->altitude_accuracy = gps_data_->fix.epv; | |
175 } | |
176 | |
177 if (!isnan(gps_data_->fix.track)) | |
178 position->heading = gps_data_->fix.track; | |
179 if (!isnan(gps_data_->fix.speed)) | |
180 position->speed = gps_data_->fix.speed; | |
181 return true; | |
182 } | |
183 | |
184 #else // !defined(USE_LIBGPS) | |
185 | |
186 // Stub implementation of LibGps. | |
187 LibGps::LibGps() { | |
188 } | |
189 | |
190 LibGps::~LibGps() { | |
191 } | |
192 | |
193 LibGps* LibGps::New() { | |
194 return NULL; | |
195 } | |
196 | |
197 bool LibGps::Start() { | |
198 return false; | |
199 } | |
200 | |
201 void LibGps::Stop() { | |
202 } | |
203 | |
204 bool LibGps::Read(Geoposition* position) { | |
205 return false; | |
206 } | |
207 | |
208 bool LibGps::GetPositionIfFixed(Geoposition* position) { | |
209 return false; | |
210 } | |
211 | |
212 #endif // !defined(USE_LIBGPS) | |
213 | |
214 GpsLocationProviderLinux::GpsLocationProviderLinux(LibGpsFactory libgps_factory) | |
215 : gpsd_reconnect_interval_millis_(kGpsdReconnectRetryIntervalMillis), | |
216 poll_period_moving_millis_(kPollPeriodMovingMillis), | |
217 poll_period_stationary_millis_(kPollPeriodStationaryMillis), | |
218 libgps_factory_(libgps_factory), | |
219 weak_factory_(this) { | |
220 DCHECK(libgps_factory_); | |
221 } | |
222 | |
223 GpsLocationProviderLinux::~GpsLocationProviderLinux() { | |
224 } | |
225 | |
226 bool GpsLocationProviderLinux::StartProvider(bool high_accuracy) { | |
227 if (!high_accuracy) { | |
228 StopProvider(); | |
229 return true; // Not an error condition, so still return true. | |
230 } | |
231 if (gps_ != NULL) { | |
232 DCHECK(weak_factory_.HasWeakPtrs()); | |
233 return true; | |
234 } | |
235 position_.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; | |
236 gps_.reset(libgps_factory_()); | |
237 if (gps_ == NULL) { | |
238 DLOG(WARNING) << "libgps could not be loaded"; | |
239 return false; | |
240 } | |
241 ScheduleNextGpsPoll(0); | |
242 return true; | |
243 } | |
244 | |
245 void GpsLocationProviderLinux::StopProvider() { | |
246 weak_factory_.InvalidateWeakPtrs(); | |
247 gps_.reset(); | |
248 } | |
249 | |
250 void GpsLocationProviderLinux::GetPosition(Geoposition* position) { | |
251 DCHECK(position); | |
252 *position = position_; | |
253 DCHECK(position->Validate() || | |
254 position->error_code != Geoposition::ERROR_CODE_NONE); | |
255 } | |
256 | |
257 void GpsLocationProviderLinux::RequestRefresh() { | |
258 ScheduleNextGpsPoll(0); | |
259 } | |
260 | |
261 void GpsLocationProviderLinux::OnPermissionGranted() { | |
262 } | |
263 | |
264 void GpsLocationProviderLinux::DoGpsPollTask() { | |
265 if (!gps_->Start()) { | |
266 DLOG(WARNING) << "Couldn't start GPS provider."; | |
267 ScheduleNextGpsPoll(gpsd_reconnect_interval_millis_); | |
268 return; | |
269 } | |
270 | |
271 Geoposition new_position; | |
272 if (!gps_->Read(&new_position)) { | |
273 ScheduleNextGpsPoll(poll_period_stationary_millis_); | |
274 return; | |
275 } | |
276 | |
277 DCHECK(new_position.Validate() || | |
278 new_position.error_code != Geoposition::ERROR_CODE_NONE); | |
279 const bool differ = PositionsDifferSiginificantly(position_, new_position); | |
280 ScheduleNextGpsPoll(differ ? poll_period_moving_millis_ : | |
281 poll_period_stationary_millis_); | |
282 if (differ || new_position.error_code != Geoposition::ERROR_CODE_NONE) { | |
283 // Update if the new location is interesting or we have an error to report. | |
284 position_ = new_position; | |
285 NotifyCallback(position_); | |
286 } | |
287 } | |
288 | |
289 void GpsLocationProviderLinux::ScheduleNextGpsPoll(int interval) { | |
290 weak_factory_.InvalidateWeakPtrs(); | |
291 base::MessageLoop::current()->PostDelayedTask( | |
292 FROM_HERE, | |
293 base::Bind(&GpsLocationProviderLinux::DoGpsPollTask, | |
294 weak_factory_.GetWeakPtr()), | |
295 base::TimeDelta::FromMilliseconds(interval)); | |
296 } | |
297 | |
298 LocationProvider* NewSystemLocationProvider() { | |
299 return new GpsLocationProviderLinux(LibGps::New); | |
300 } | |
301 | |
302 } // namespace content | |
OLD | NEW |