00001 #include "epoc-gps.hpp" 00002 00003 #include "epoc-gps-module.hpp" 00004 #include "epoc-gps-positioner.hpp" 00005 00006 #include "cf_query.h" 00007 #include "er_errors.h" 00008 #include "kr_controller_private.h" 00009 #include "sa_sensor_list_log_db.h" 00010 00011 #include "common/assertions.h" 00012 #include "common/epoc-time.h" 00013 #include "common/error_list.h" 00014 #include "common/logging.h" 00015 #include "common/platform_error.h" 00016 #include "common/utilities.h" 00017 00018 #include <e32math.h> 00019 00020 #include <stdio.h> 00021 #include <string.h> 00022 00023 // For docs, see for instance 00024 // http://www.forum.nokia.com/document/CDL_Extension_S60_3rd_Ed_FP2/GUID-759FBC7F-5384-4487-8457-A8D4B76F6AA6/html/classRPositionServer.html. 00025 // 00026 // Also worth looking at src/ext/gps of PyS60. 00027 // 00028 // Especially useful is the Location Acquisition API Specification PDF. 00029 00030 // See also: http://wiki.forum.nokia.com/index.php/KIS000850_-_RPositioner::NotifyPositionUpdate_return_KErrInUse_after_few_minutes 00031 00032 // ------------------------------------------------------------------- 00033 00034 #define DEFAULT_POSITION_SCAN_INTERVAL_SECS (5 * 60) 00035 00036 // ------------------------------------------------------------------- 00037 00038 CSensor_gps* CSensor_gps::NewL(ac_AppContext* aAppContext) 00039 { 00040 CSensor_gps* obj = new (ELeave) CSensor_gps(aAppContext); 00041 CleanupStack::PushL(obj); 00042 obj->ConstructL(); 00043 CleanupStack::Pop(); 00044 return obj; 00045 } 00046 00047 CSensor_gps::CSensor_gps(ac_AppContext* aAppContext) : 00048 iAppContext(aAppContext), 00049 iModuleId(KPositionNullModuleId) 00050 { 00051 iLogDb = ac_LogDb(aAppContext); 00052 iPositionUpdateIntervalSecs = DEFAULT_POSITION_SCAN_INTERVAL_SECS; 00053 } 00054 00055 void CSensor_gps::ConstructL() 00056 { 00057 RefreshPositionUpdateIntervalSecs(); 00058 iRetryAo = CRetryAo::NewL(*this, 30, 120); 00059 iModuleAo = CPosModuleStatAo::NewL(*this); 00060 } 00061 00062 CSensor_gps::~CSensor_gps() 00063 { 00064 Stop(); 00065 delete iRetryAo; 00066 delete iModuleAo; 00067 } 00068 00069 void CSensor_gps::Stop() 00070 { 00071 iRetryAo->Cancel(); 00072 iModuleAo->Cancel(); 00073 DELETE_Z(iPositioner); 00074 iState = EInactive; 00075 } 00076 00077 void CSensor_gps::StartL() 00078 { 00079 if (!IsActive()) { 00080 iRetryAo->ResetFailures(); 00081 00082 iModuleId = KPositionNullModuleId; 00083 00084 TPositionModuleId bestId = iModuleAo->ChooseBestPositionerL(CPosModuleStatAo::KAllowAssisted | CPosModuleStatAo::KAllowExternal); 00085 00086 if (bestId != KPositionNullModuleId) { 00087 TRAPD(errCode, CreateSpecifiedPositionerL(bestId)); 00088 if (errCode) { 00089 er_log_symbian(0, errCode, "WARNING: failed to create positioner"); 00090 } 00091 } 00092 00093 // Observe changes in module status. 00094 iModuleAo->MakeRequest(); 00095 00096 iState = EActive; 00097 } 00098 } 00099 00100 void CSensor_gps::CreateSpecifiedPositionerL(TPositionModuleId bestId) 00101 { 00102 // This is just the module we last tried to use; we do not require 00103 // we actually succeed in creating the positioner for it. 00104 iModuleId = bestId; 00105 00106 RPositionServer& server = iModuleAo->PositionServer(); 00107 iPositioner = CPositioner_gps::NewL(server, *this, 00108 bestId, iPositionUpdateIntervalSecs, 00109 0 /* timeout */, 00110 0 /* mag age */); 00111 iPositioner->MakeRequest(); 00112 } 00113 00114 void CSensor_gps::PosModChangeL() 00115 { 00116 TPositionModuleId bestId = iModuleAo->ChooseBestPositionerL(CPosModuleStatAo::KAllowAssisted | CPosModuleStatAo::KAllowExternal); 00117 if (bestId != iModuleId) { 00118 iRetryAo->Cancel(); 00119 iRetryAo->ResetFailures(); 00120 DELETE_Z(iPositioner); 00121 00122 if (bestId != KPositionNullModuleId) { 00123 TRAPD(errCode, CreateSpecifiedPositionerL(bestId)); 00124 if (errCode) { 00125 er_log_symbian(0, errCode, "WARNING: failed to create positioner"); 00126 } 00127 } 00128 } 00129 iModuleAo->MakeRequest(); 00130 } 00131 00132 TBool CSensor_gps::PosModIsCurrent(TPositionModuleId id) const 00133 { 00134 return (iModuleId != KPositionNullModuleId) && (iModuleId == id); 00135 } 00136 00137 void CSensor_gps::PosModErrorL(TInt errCode) 00138 { 00139 er_log_symbian(0, errCode, "INACTIVATE: gps: positioning module status tracking error"); 00140 Stop(); 00141 } 00142 00143 // Called if PosModChangeL or PosModErrorL leaves. 00144 void CSensor_gps::PosModLeave(TInt errCode) 00145 { 00146 er_log_symbian(er_FATAL, errCode, "gps: leave in positioning module status reporting handler"); 00147 } 00148 00149 void CSensor_gps::RetryTimerExpired(CRetryAo* src, TInt errCode) 00150 { 00151 if (errCode) { 00152 er_log_symbian(er_FATAL, errCode, "gps: retry timer error"); 00153 return; 00154 } 00155 assert(iPositioner); 00156 iPositioner->MakeRequest(); 00157 } 00158 00159 gboolean CSensor_gps::PositionerEventL(GError** error) 00160 { 00161 assert_error_unset(error); 00162 assert(iPositioner); 00163 00164 // See "lbserrors.h" for LBS-specific return codes for 00165 // NotifyPositionUpdate requests. Note that somewhat unusually there 00166 // are some positive values as well. 00167 TInt errCode = iPositioner->StatusCode(); 00168 00169 if (errCode == KErrTimedOut) { 00170 // Can expect to get this after the time period specified with 00171 // SetUpdateTimeOut. We haven't set such a time period, though, 00172 // and presumably then should not get this. 00173 logt("gps request timed out (unexpectedly), retrying"); 00174 iPositioner->MakeRequest(); 00175 } else if (errCode == KErrCancel) { 00176 // Not really expected here, but whatever. Do nothing. 00177 } else if (errCode < 0) { 00178 // Do not yet quite know what sort of errors might be getting, so 00179 // shall merely log errors and keep retrying. Do want to make 00180 // sure, however, that we do not get an awful lot of errors 00181 // growing our log file to an unreasonable size, and hence will 00182 // stop the scanner if there are lots of consecutive error 00183 // completions. We can be cleverer here once we know what sort of 00184 // errors we might be getting. But it is reasonable to assume that 00185 // there may be immediate error returns in cases such as a 00186 // positioning module being or having become unavailable. 00187 switch (errCode) { 00188 case KErrAccessDenied: // Perhaps some capability thing. 00189 // Locally severe error. Give up. 00190 er_log_symbian(0, errCode, "gps: positioner error, giving up on positioner"); 00191 // We will not reset state further, lest we be asked to switch 00192 // back to the same module again. 00193 DELETE_Z(iPositioner); 00194 break; 00195 case KErrArgument: 00196 case KErrPositionBufferOverflow: 00197 er_log_symbian(er_FATAL, errCode, "gps: unexpected positioning error"); 00198 break; 00199 default: 00200 if (!iRetryAo->Retry()) { 00201 er_log_symbian(0, errCode, "INACTIVATE: gps: stopping scanning due to too many errors"); 00202 Stop(); 00203 } 00204 } 00205 } else { 00206 iRetryAo->ResetFailures(); 00207 if (errCode == KPositionQualityLoss) { 00208 // The positioning module could not get any information. 00209 logt("no gps info available"); 00210 } else if (errCode == KErrNone || errCode == KPositionPartialUpdate) { 00211 // Now this data can get quite complicated and I am not sure it 00212 // makes sense for us to start cramming it into a variety of 00213 // fields and tables for fancy querying unless we have a clear 00214 // requirement to support certain kinds of queries efficiently. So 00215 // we shall merely record it all into a single field in some 00216 // textual format, at least for the time being. We shall choose 00217 // Lua expression syntax for this purpose. Evaluating such strings 00218 // can be done with assert(loadstring(s))(). 00219 #if __DO_LOGGING__ 00220 TBool isPartial = (errCode == KPositionPartialUpdate); 00221 logg("got %s gps reading", isPartial ? "partial" : "full"); 00222 #endif 00223 00224 // Is it so that if some module does not support satellite info 00225 // then if we try to request it we get nothing? Well, we are 00226 // only choosing GPS modules, and GPS is based on satellites, so 00227 // surely there always is satellite info. 00228 00229 const TPositionSatelliteInfo& positionInfo = iPositioner->PositionInfo(); 00230 00231 #define SAT_STRING_MAX 300 00232 #define COU_STRING_MAX 300 00233 gchar satString[SAT_STRING_MAX]; 00234 gchar couString[COU_STRING_MAX]; 00235 satString[0] = '\0'; 00236 couString[0] = '\0'; 00237 00238 if (positionInfo.PositionClassType() & EPositionSatelliteInfoClass) { 00239 TInt satellites = positionInfo.NumSatellitesInView(); 00240 TInt used_satellites = positionInfo.NumSatellitesUsed(); 00241 TReal32 horizontal_dop = positionInfo.HorizontalDoP(); 00242 TReal32 vertical_dop = positionInfo.VerticalDoP(); 00243 TReal32 time_dop = positionInfo.TimeDoP(); 00244 TTime satTime = positionInfo.SatelliteTime(); 00245 00246 #if __DO_LOGGING__ 00247 #define TIME_STRING_MAX 50 00248 _LIT(KDateTimeFormat, "%F%Y-%M-%D %H:%T:%S.%C"); 00249 // Note that KMaxTimeFormatSpec is the max size of the format 00250 // spec, and not the result, as I understand it. 00251 TBuf<TIME_STRING_MAX> satTime16; 00252 // The leave will not happen if the format string and data are 00253 // valid and the destination buffer is large enough. 00254 satTime.FormatL(satTime16, KDateTimeFormat); 00255 gchar satTime8[TIME_STRING_MAX + 1]; 00256 ConvToUtf8CString(satTime8, TIME_STRING_MAX, satTime16); 00257 00258 logg("satellite info: num satellites in view %d, num used satellites %d, hdop %g, vdop %g, tdop %g, satellite time %s", satellites, used_satellites, horizontal_dop, vertical_dop, time_dop, satTime8); 00259 #endif 00260 00261 // We convert Symbian time to Unix time for consistency. 00262 time_t satUnixTime = LocalEpocTimeToUnixTime(satTime); // assuming 32-bit int 00263 00264 // Build 'satString' for database insertion. 00265 { 00266 // "satellites": {"horizontal_dop": 2.20000004768372, 00267 // "used_satellites": 4, "vertical_dop": 1.0, "time": 00268 // 1225980390.097, "satellites": 10, "time_dop": 1.0} 00269 GString* gs = NULL; 00270 SET_TRAP_OOM(if (gs) g_string_free(gs, TRUE); 00271 User::LeaveNoMemory()); 00272 gs = g_string_sized_new(128); 00273 g_string_printf(gs, "{time: %d", (int)satUnixTime); 00274 if (!Math::IsNaN(satellites)) 00275 g_string_append_printf(gs, ", satellites: %d", satellites); 00276 if (!Math::IsNaN(used_satellites)) 00277 g_string_append_printf(gs, ", used_satellites: %d", used_satellites); 00278 if (!Math::IsNaN(horizontal_dop)) 00279 g_string_append_printf_fix(gs, ", horizontal_dop: %.6f", (double)horizontal_dop); 00280 if (!Math::IsNaN(vertical_dop)) 00281 // %f and %g seem to be a bit broken for this function at 00282 // least, and maybe also for the other GLib printf 00283 // functions. We can try a more recent Open C, or 00284 // implement a replacement using C99 or Symbian functions. 00285 // Or possibly we could backport a more recent version of 00286 // the function in question from GLib, if there are not 00287 // too many internal dependencies. 00288 g_string_append_printf_fix(gs, ", vertical_dop: %.6f", (double)vertical_dop); 00289 if (!Math::IsNaN(time_dop)) 00290 g_string_append_printf_fix(gs, ", time_dop: %.6f", (double)time_dop); 00291 g_string_append_c(gs, '}'); 00292 UNSET_TRAP_OOM(); 00293 00294 if (gs->len < SAT_STRING_MAX) 00295 // For easier memory management. 00296 strcpy(satString, gs->str); 00297 else 00298 // Did not fit fully, do not log partial data that may not 00299 // be parseable. 00300 satString[0] = '\0'; 00301 g_string_free(gs, TRUE); 00302 logt(satString); 00303 } 00304 } 00305 00306 if (positionInfo.PositionClassType() & EPositionCourseInfoClass) { 00307 TCourse course; 00308 positionInfo.GetCourse(course); 00309 TReal32 speed = course.Speed(); 00310 TReal32 heading = course.Heading(); 00311 TReal32 course_value = course.Course(); 00312 TReal32 speed_accuracy = course.SpeedAccuracy(); 00313 TReal32 heading_accuracy = course.HeadingAccuracy(); 00314 TReal32 course_accuracy = course.CourseAccuracy(); 00315 logg("course info: speed %g m/s, heading %g deg, course %g, speed_accuracy %g m/s, heading_accuracy %d deg, course_accuracy %g", speed, heading, course_value, speed_accuracy, heading_accuracy, course_accuracy); 00316 00317 // "course": {"speed": 6.0, "heading": 305.880004882812, "heading_accuracy": 5.80999994277954, "speed_accuracy": 8.35999965667725 00318 { 00319 #define APPEND_STRING(s) { int slen = strlen(s); if (space <= slen) goto fail; space -= slen; strcpy(output, s); output += slen; } 00320 #define APPEND_SEP { if (!first) APPEND_STRING(", "); } 00321 #define COU_UPDATE_STATE(r) { if (r < 0 || r >= space) goto fail; space -= r; output += r; first = EFalse; } 00322 int space = COU_STRING_MAX; 00323 int r; 00324 TBool first = ETrue; 00325 gchar* output = couString; 00326 APPEND_STRING("{"); 00327 if (!Math::IsNaN(speed)) { r = g_snprintf_fix(output, space, "speed: %.6f", (double)speed); COU_UPDATE_STATE(r); } 00328 if (!Math::IsNaN(heading)) { APPEND_SEP; r = g_snprintf_fix(output, space, "heading: %.6f", (double)heading); COU_UPDATE_STATE(r); } 00329 if (!Math::IsNaN(course_value)) { APPEND_SEP; r = g_snprintf_fix(output, space, "course: %.6f", (double)course_value); COU_UPDATE_STATE(r); } 00330 if (!Math::IsNaN(speed_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "speed_accuracy: %.6f", (double)speed_accuracy); COU_UPDATE_STATE(r); } 00331 if (!Math::IsNaN(heading_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "heading_accuracy: %.6f", (double)heading_accuracy); COU_UPDATE_STATE(r); } 00332 if (!Math::IsNaN(course_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "course_accuracy: %.6f", (double)course_accuracy); COU_UPDATE_STATE(r); } 00333 APPEND_STRING("}"); 00334 goto done; 00335 fail: 00336 couString[0] = '\0'; 00337 done: ; 00338 logt(couString); 00339 } 00340 } 00341 00342 if (positionInfo.PositionClassType() & EPositionInfoClass) { 00343 TPosition position; 00344 positionInfo.GetPosition(position); 00345 // It seems that with network based positioning we only get 00346 // lat and lon and hacc, and the rest are NaN. 00347 TReal64 lat = position.Latitude(); 00348 TReal64 lon = position.Longitude(); 00349 // To use Google Maps to look these up, can use something like http://maps.google.com/maps?q=60.187537,+24.804940&hl=en 00350 TReal32 alt = position.Altitude(); 00351 TReal32 vacc = position.VerticalAccuracy(); 00352 TReal32 hacc = position.HorizontalAccuracy(); 00353 //logg("position info: latitude %lg deg, longitude %lg deg, altitude %g m, vertical_accuracy %g m, horizontal_accuracy %g m", lat, lon, alt, vacc, hacc); 00354 guilogf("gps: lat %lg, lon %lg, alt %g, vacc %g m, hacc %g m", lat, lon, alt, vacc, hacc); 00355 if (!Math::IsNaN(lat) && 00356 !Math::IsNaN(lon)) { 00357 if (!log_db_log_position(iLogDb, lat, lon, alt, vacc, hacc, couString, satString, error)) { 00358 return FALSE; 00359 } 00360 } else { 00361 logt("supposedly a full reading has NaN value(s) in coordinates"); 00362 } 00363 } 00364 } else { 00365 dblogg("warning, unknown gps request status code (%d)", errCode); 00366 } 00367 iPositioner->MakeRequest(); 00368 } 00369 00370 return TRUE; 00371 } 00372 00373 void CSensor_gps::Reconfigure(const gchar* name, const gchar* value) 00374 { 00375 if (strcmp(name, "sensor.gps.interval") == 0) { 00376 RefreshPositionUpdateIntervalSecs(); 00377 } 00378 } 00379 00380 void CSensor_gps::RefreshPositionUpdateIntervalSecs() 00381 { 00382 // Takes effect when we next create a positioner object. 00383 try_get_ConfigDb_int("sensor.gps.interval", 00384 &iPositionUpdateIntervalSecs, 00385 NULL, NULL); 00386 } 00387 00388 /** 00389 00390 epoc-gps.cpp 00391 00392 Copyright 2009 Helsinki Institute for Information Technology (HIIT) 00393 and the authors. All rights reserved. 00394 00395 Authors: Tero Hasu <tero.hasu@hut.fi> 00396 00397 Permission is hereby granted, free of charge, to any person 00398 obtaining a copy of this software and associated documentation files 00399 (the "Software"), to deal in the Software without restriction, 00400 including without limitation the rights to use, copy, modify, merge, 00401 publish, distribute, sublicense, and/or sell copies of the Software, 00402 and to permit persons to whom the Software is furnished to do so, 00403 subject to the following conditions: 00404 00405 The above copyright notice and this permission notice shall be 00406 included in all copies or substantial portions of the Software. 00407 00408 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00409 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00410 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00411 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00412 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00413 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00414 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00415 SOFTWARE. 00416 00417 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1