| 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 |