00001 /* 00002 00003 References: 00004 00005 http://www.forum.nokia.com/document/Cpp_Developers_Library/GUID-759FBC7F-5384-4487-8457-A8D4B76F6AA6/html/Location_Acquisition_API4.html 00006 00007 */ 00008 00009 #include "epoc-cellpos.hpp" 00010 00011 #include "epoc-gps-module.hpp" 00012 #include "epoc-gps-positioner.hpp" 00013 00014 #include "cf_query.h" 00015 #include "er_errors.h" 00016 #include "kr_controller_private.h" 00017 #include "sa_sensor_list_log_db.h" 00018 00019 #include "common/assertions.h" 00020 #include "common/epoc-time.h" 00021 #include "common/error_list.h" 00022 #include "common/logging.h" 00023 #include "common/platform_error.h" 00024 #include "common/utilities.h" 00025 00026 #include <e32math.h> 00027 00028 #include <stdio.h> 00029 #include <string.h> 00030 00031 // ------------------------------------------------------------------- 00032 // internal parameters... 00033 00034 // We will never ask the positioner for service more frequently than 00035 // this. This value is runtime adjustable. 00036 #define MIN_SCAN_REQUEST_INTERVAL_SECS (3 * 60) 00037 00038 // We will not drain the battery for longer than this if we do not get 00039 // a position fix, and we call Cancel. If we do get a position, it 00040 // should be enough to not call MakeRequest. This value is runtime 00041 // adjustable. 00042 #define SATELLITE_QUERY_TIMEOUT_SECS (60) 00043 00044 // This is given as a parameter to the positioner, and it indicates 00045 // how frequently it should be prepared to provide periodic updates. 00046 // In our case such time periods are irregular, and it probably makes 00047 // sense not to set this to avoid possibly spending battery on 00048 // needless guarantees. It seems this must be shorter than the timeout 00049 // interval, and indeed anything else does not make much sense. 00050 #define POSITIONER_SCAN_INTERVAL_SECS (0) 00051 00052 #define POSITIONER_MAX_AGE_SECS (60) 00053 00054 // ------------------------------------------------------------------- 00055 00056 CSensor_cellpos* CSensor_cellpos::NewL(ac_AppContext* aAppContext) 00057 { 00058 CSensor_cellpos* obj = new (ELeave) CSensor_cellpos(aAppContext); 00059 CleanupStack::PushL(obj); 00060 obj->ConstructL(); 00061 CleanupStack::Pop(); 00062 return obj; 00063 } 00064 00065 CSensor_cellpos::CSensor_cellpos(ac_AppContext* aAppContext) : 00066 iAppContext(aAppContext), 00067 iModuleId(KPositionNullModuleId) 00068 { 00069 iLogDb = ac_LogDb(aAppContext); 00070 } 00071 00072 void CSensor_cellpos::ConstructL() 00073 { 00074 ReadConfig(); 00075 00076 bb_Blackboard* bb = ac_get_Blackboard(iAppContext); 00077 bb_Board* bd = bb_Blackboard_board(bb); 00078 iAllowAssisted = bd->netpos_allowed; 00079 00080 iRetryAo = CRetryAo::NewL(*this, 4, 5); // 4 tries, 5 secs 00081 iModuleAo = CPosModuleStatAo::NewL(*this); 00082 iCellChangeHandle.Register(bb, bb_dt_cell_id, this); 00083 iNetposChangeHandle.Register(bb, bb_dt_netpos_allowed, this); 00084 } 00085 00086 CSensor_cellpos::~CSensor_cellpos() 00087 { 00088 Stop(); 00089 delete iRetryAo; 00090 delete iModuleAo; 00091 } 00092 00093 void CSensor_cellpos::Stop() 00094 { 00095 iRetryAo->Cancel(); 00096 iModuleAo->Cancel(); 00097 DELETE_Z(iSatPositioner); 00098 iState = EInactive; 00099 } 00100 00101 void CSensor_cellpos::StartL() 00102 { 00103 if (!IsActive()) { 00104 iRetryAo->ResetFailures(); 00105 00106 iModuleId = KPositionNullModuleId; 00107 00108 PosModChangeL(); 00109 00110 iState = EActive; 00111 } 00112 } 00113 00114 void CSensor_cellpos::CreateSpecifiedPositionerL(TPositionModuleId bestId) 00115 { 00116 // This is just the module we last tried to use; we do not require 00117 // we actually succeed in creating the positioner for it. 00118 iModuleId = bestId; 00119 00120 RPositionServer& server = iModuleAo->PositionServer(); 00121 iSatPositioner = CPositioner_gps::NewL(server, *this, 00122 bestId, 00123 POSITIONER_SCAN_INTERVAL_SECS, 00124 iSatelliteQueryTimeoutSecs, 00125 iDataMaxAgeSecs); 00126 } 00127 00128 void CSensor_cellpos::PosModChangeL() 00129 { 00130 TInt modif = 0; 00131 if (iAllowAssisted) 00132 modif |= CPosModuleStatAo::KAllowAssisted; 00133 TPositionModuleId bestId = iModuleAo->ChooseBestPositionerL(modif); 00134 if (bestId != iModuleId) { 00135 iRetryAo->Cancel(); 00136 iRetryAo->ResetFailures(); 00137 DELETE_Z(iSatPositioner); 00138 00139 if (bestId != KPositionNullModuleId) { 00140 TRAPD(errCode, CreateSpecifiedPositionerL(bestId)); 00141 if (errCode) { 00142 er_log_symbian(0, errCode, "WARNING: failed to create positioner"); 00143 } 00144 } 00145 } 00146 iModuleAo->MakeRequest(); 00147 } 00148 00149 TBool CSensor_cellpos::PosModIsCurrent(TPositionModuleId id) const 00150 { 00151 return (iModuleId != KPositionNullModuleId) && (iModuleId == id); 00152 } 00153 00154 void CSensor_cellpos::PosModErrorL(TInt errCode) 00155 { 00156 er_log_symbian(0, errCode, 00157 "INACTIVATE: cellpos: positioning module status tracking error"); 00158 Stop(); 00159 } 00160 00161 // Called if PosModChangeL or PosModErrorL leaves. 00162 void CSensor_cellpos::PosModLeave(TInt errCode) 00163 { 00164 er_log_symbian(er_FATAL, errCode, "cellpos: leave in positioning module status reporting handler"); 00165 } 00166 00167 void CSensor_cellpos::RetryTimerExpired(CRetryAo* src, TInt errCode) 00168 { 00169 if (errCode) { 00170 er_log_symbian(er_FATAL, errCode, "cellpos: retry timer error"); 00171 return; 00172 } 00173 assert(iSatPositioner); 00174 iSatPositioner->MakeRequest(); 00175 } 00176 00177 void CSensor_cellpos::BbChangedL(bb::RHandle* self, enum bb_DataType dt, 00178 gpointer data, int len) 00179 { 00180 if (!IsActive()) 00181 return; // stopped 00182 switch (dt) 00183 { 00184 case bb_dt_cell_id: 00185 { 00186 guilogf("cellpos: cell changed"); 00187 if (!iSatPositioner) { 00188 guilogf("cellpos: no positioner"); 00189 return; // no positioner 00190 } 00191 if (iSatPositioner->IsActive() || iRetryAo->IsActive()) { 00192 guilogf("cellpos: already positioning"); 00193 return; // doing positioning 00194 } 00195 TTime now; 00196 now.UniversalTime(); 00197 TTime earliestTime(iLastScanTime.Int64()); 00198 earliestTime += TTimeIntervalSeconds(iMinScanRequestIntervalSecs); 00199 if (now <= earliestTime) { 00200 guilogf("cellpos: too early"); 00201 return; // too early 00202 } 00203 guilogf("cellpos: positioning..."); 00204 iSatPositioner->MakeRequest(); 00205 break; 00206 } 00207 00208 case bb_dt_netpos_allowed: 00209 { 00210 gboolean value = GPOINTER_TO_INT(data); 00211 guilogf("cellpos: %s network access", value ? "allow" : "disallow"); 00212 if (value != iAllowAssisted) { 00213 iAllowAssisted = value; 00214 PosModChangeL(); 00215 } 00216 break; 00217 } 00218 00219 default: 00220 { 00221 assert(0); 00222 break; 00223 } 00224 } 00225 00226 } 00227 00228 // xxx this could be shared with other satellite sensors 00229 static void LogSatelliteInfoL(LogDb* aLogDb, const char* aSensorName, 00230 const TPositionSatelliteInfo& positionInfo) 00231 { 00232 #define SAT_STRING_MAX 300 00233 #define COU_STRING_MAX 300 00234 gchar satString[SAT_STRING_MAX]; 00235 gchar couString[COU_STRING_MAX]; 00236 satString[0] = '\0'; 00237 couString[0] = '\0'; 00238 00239 if (positionInfo.PositionClassType() & EPositionSatelliteInfoClass) { 00240 TInt satellites = positionInfo.NumSatellitesInView(); 00241 TInt used_satellites = positionInfo.NumSatellitesUsed(); 00242 TReal32 horizontal_dop = positionInfo.HorizontalDoP(); 00243 TReal32 vertical_dop = positionInfo.VerticalDoP(); 00244 TReal32 time_dop = positionInfo.TimeDoP(); 00245 TTime satTime = positionInfo.SatelliteTime(); 00246 00247 #if __DO_LOGGING__ 00248 #define TIME_STRING_MAX 50 00249 _LIT(KDateTimeFormat, "%F%Y-%M-%D %H:%T:%S.%C"); 00250 // Note that KMaxTimeFormatSpec is the max size of the format 00251 // spec, and not the result, as I understand it. 00252 TBuf<TIME_STRING_MAX> satTime16; 00253 // The leave will not happen if the format string and data are 00254 // valid and the destination buffer is large enough. 00255 satTime.FormatL(satTime16, KDateTimeFormat); 00256 gchar satTime8[TIME_STRING_MAX + 1]; 00257 ConvToUtf8CString(satTime8, TIME_STRING_MAX, satTime16); 00258 00259 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); 00260 #endif 00261 00262 // We convert Symbian time to Unix time for consistency. 00263 time_t satUnixTime = LocalEpocTimeToUnixTime(satTime); // assuming 32-bit int 00264 00265 // Build 'satString' for database insertion. 00266 { 00267 // "satellites": {"horizontal_dop": 2.20000004768372, 00268 // "used_satellites": 4, "vertical_dop": 1.0, "time": 00269 // 1225980390.097, "satellites": 10, "time_dop": 1.0} 00270 GString* gs = NULL; 00271 SET_TRAP_OOM(if (gs) g_string_free(gs, TRUE); 00272 User::LeaveNoMemory()); 00273 gs = g_string_sized_new(128); 00274 g_string_printf(gs, "{time: %d", (int)satUnixTime); 00275 if (!Math::IsNaN(satellites)) 00276 g_string_append_printf(gs, ", satellites: %d", satellites); 00277 if (!Math::IsNaN(used_satellites)) 00278 g_string_append_printf(gs, ", used_satellites: %d", used_satellites); 00279 if (!Math::IsNaN(horizontal_dop)) 00280 g_string_append_printf_fix(gs, ", horizontal_dop: %.6f", (double)horizontal_dop); 00281 if (!Math::IsNaN(vertical_dop)) 00282 // %f and %g seem to be a bit broken for this function at 00283 // least, and maybe also for the other GLib printf 00284 // functions. We can try a more recent Open C, or 00285 // implement a replacement using C99 or Symbian functions. 00286 // Or possibly we could backport a more recent version of 00287 // the function in question from GLib, if there are not 00288 // too many internal dependencies. 00289 g_string_append_printf_fix(gs, ", vertical_dop: %.6f", (double)vertical_dop); 00290 if (!Math::IsNaN(time_dop)) 00291 g_string_append_printf_fix(gs, ", time_dop: %.6f", (double)time_dop); 00292 g_string_append_c(gs, '}'); 00293 UNSET_TRAP_OOM(); 00294 00295 if (gs->len < SAT_STRING_MAX) 00296 // For easier memory management. 00297 strcpy(satString, gs->str); 00298 else 00299 // Did not fit fully, do not log partial data that may not 00300 // be parseable. 00301 satString[0] = '\0'; 00302 g_string_free(gs, TRUE); 00303 logt(satString); 00304 } 00305 } 00306 00307 if (positionInfo.PositionClassType() & EPositionCourseInfoClass) { 00308 TCourse course; 00309 positionInfo.GetCourse(course); 00310 TReal32 speed = course.Speed(); 00311 TReal32 heading = course.Heading(); 00312 TReal32 course_value = course.Course(); 00313 TReal32 speed_accuracy = course.SpeedAccuracy(); 00314 TReal32 heading_accuracy = course.HeadingAccuracy(); 00315 TReal32 course_accuracy = course.CourseAccuracy(); 00316 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); 00317 00318 // "course": {"speed": 6.0, "heading": 305.880004882812, "heading_accuracy": 5.80999994277954, "speed_accuracy": 8.35999965667725 00319 { 00320 #define APPEND_STRING(s) { int slen = strlen(s); if (space <= slen) goto fail; space -= slen; strcpy(output, s); output += slen; } 00321 #define APPEND_SEP { if (!first) APPEND_STRING(", "); } 00322 #define COU_UPDATE_STATE(r) { if (r < 0 || r >= space) goto fail; space -= r; output += r; first = EFalse; } 00323 int space = COU_STRING_MAX; 00324 int r; 00325 TBool first = ETrue; 00326 gchar* output = couString; 00327 APPEND_STRING("{"); 00328 if (!Math::IsNaN(speed)) { r = g_snprintf_fix(output, space, "speed: %.6f", (double)speed); COU_UPDATE_STATE(r); } 00329 if (!Math::IsNaN(heading)) { APPEND_SEP; r = g_snprintf_fix(output, space, "heading: %.6f", (double)heading); COU_UPDATE_STATE(r); } 00330 if (!Math::IsNaN(course_value)) { APPEND_SEP; r = g_snprintf_fix(output, space, "course: %.6f", (double)course_value); COU_UPDATE_STATE(r); } 00331 if (!Math::IsNaN(speed_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "speed_accuracy: %.6f", (double)speed_accuracy); COU_UPDATE_STATE(r); } 00332 if (!Math::IsNaN(heading_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "heading_accuracy: %.6f", (double)heading_accuracy); COU_UPDATE_STATE(r); } 00333 if (!Math::IsNaN(course_accuracy)) { APPEND_SEP; r = g_snprintf_fix(output, space, "course_accuracy: %.6f", (double)course_accuracy); COU_UPDATE_STATE(r); } 00334 APPEND_STRING("}"); 00335 goto done; 00336 fail: 00337 couString[0] = '\0'; 00338 done: ; 00339 logt(couString); 00340 } 00341 } 00342 00343 if (positionInfo.PositionClassType() & EPositionInfoClass) { 00344 TPosition position; 00345 positionInfo.GetPosition(position); 00346 // It seems that with network based positioning we only get 00347 // lat and lon and hacc, and the rest are NaN. 00348 TReal64 lat = position.Latitude(); 00349 TReal64 lon = position.Longitude(); 00350 // To use Google Maps to look these up, can use something like http://maps.google.com/maps?q=60.187537,+24.804940&hl=en 00351 TReal32 alt = position.Altitude(); 00352 TReal32 vacc = position.VerticalAccuracy(); 00353 TReal32 hacc = position.HorizontalAccuracy(); 00354 //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); 00355 guilogf("%s: lat %lg, lon %lg, alt %g, vacc %g m, hacc %g m", aSensorName, lat, lon, alt, vacc, hacc); 00356 if (!Math::IsNaN(lat) && 00357 !Math::IsNaN(lon)) { 00358 log_db_log_position(aLogDb, lat, lon, alt, vacc, hacc, couString, satString, NULL); 00359 } else { 00360 logt("supposedly full reading has NaN value(s) in coordinates"); 00361 } 00362 } 00363 } 00364 00365 gboolean CSensor_cellpos::PositionerEventL(GError** error) 00366 { 00367 assert_error_unset(error); 00368 assert(iSatPositioner); 00369 00370 iLastScanTime.UniversalTime(); 00371 00372 // See "lbserrors.h" for LBS-specific return codes for 00373 // NotifyPositionUpdate requests. Note that somewhat unusually there 00374 // are some positive values as well. 00375 TInt errCode = iSatPositioner->StatusCode(); 00376 00377 if (errCode < 0) { 00378 // Do not yet quite know what sort of errors might be getting, so 00379 // shall merely log errors and keep retrying. Do want to make 00380 // sure, however, that we do not get an awful lot of errors 00381 // growing our log file to an unreasonable size, and hence will 00382 // stop the scanner if there are lots of consecutive error 00383 // completions. We can be cleverer here once we know what sort of 00384 // errors we might be getting. But it is reasonable to assume that 00385 // there may be immediate error returns in cases such as a 00386 // positioning module being or having become unavailable. 00387 switch (errCode) { // see <lbserrors.h> 00388 case KErrTimedOut: 00389 // Can expect to get this after the time period specified with 00390 // SetUpdateTimeOut. If we didn't get the position in that time, 00391 // then we did not. 00392 guilogf("cellpos: GPS timeout"); 00393 break; 00394 00395 case KErrServerBusy: 00396 // If the server is busy, let us then just wait for the next 00397 // cell ID change to avoid burdening it further. 00398 guilogf("cellpos: positioner busy"); 00399 break; 00400 00401 guilogf("cellpos: positioning error"); 00402 00403 // Used positioning module became unavailable. (Before we had 00404 // time to stop using it.) 00405 case KErrNotFound: 00406 // Perhaps some capability thing. 00407 case KErrAccessDenied: 00408 // Could apparently happen if the user refuses to connect an 00409 // external Bluetooth GPS device, for example. 00410 case KErrCancel: 00411 er_log_symbian(0, errCode, 00412 "cellpos: positioner error, giving up on positioner"); 00413 // We will not reset state further, lest we be asked to switch 00414 // back to the same module again. 00415 DELETE_Z(iSatPositioner); 00416 break; 00417 00418 case KErrArgument: 00419 case KErrPositionBufferOverflow: 00420 er_log_symbian(er_FATAL, errCode, "cellpos: unexpected positioning error"); 00421 break; 00422 00423 case KErrBadHandle: // positioning service session dead 00424 // We should really recreate session, but why was it close in 00425 // the first place, something not right here. 00426 er_log_symbian(0, errCode, 00427 "INACTIVATE: cellpos: position server session died"); 00428 Stop(); 00429 break; 00430 00431 default: 00432 // We may not actually need the retry AO at all. The cell ID 00433 // changes will determine when to retry. 00434 er_log_symbian(0, errCode, 00435 "cellpos: positioner error, not retrying"); 00436 /* 00437 if (!iRetryAo->Retry()) { 00438 er_log_symbian(0, errCode, "INACTIVATE: cellpos: stopping scanning due to too many errors"); 00439 Stop(); 00440 } 00441 */ 00442 } 00443 } else { // errCode >= 0 (yes, there are _positive_ codes as well) 00444 iRetryAo->ResetFailures(); 00445 00446 switch (errCode) { // see <lbserrors.h> 00447 case KPositionQualityLoss: // no data 00448 { 00449 guilogf("cellpos: got no GPS data"); 00450 break; 00451 } 00452 00453 case KErrNone: // full data 00454 case KPositionPartialUpdate: // partial data 00455 { 00456 #if __DO_LOGGING__ 00457 TBool isPartial = (errCode == KPositionPartialUpdate); 00458 logg("got %s gps reading", isPartial ? "partial" : "full"); 00459 #endif 00460 00461 // Is it so that if some module does not support satellite info 00462 // then if we try to request it we get nothing? Well, we are 00463 // only choosing GPS modules, and GPS is based on satellites, so 00464 // surely there always is satellite info. 00465 const TPositionSatelliteInfo& positionInfo = iSatPositioner->PositionInfo(); 00466 00467 LogSatelliteInfoL(iLogDb, "cellpos", positionInfo); 00468 00469 break; 00470 } 00471 00472 default: // unknown completion code 00473 { 00474 guilogf("cellpos: unknown completion code %d", errCode); 00475 log_db_log_status(iLogDb, NULL, 00476 "cellpos: unknown completion code %d", errCode); 00477 } 00478 } 00479 } 00480 00481 return TRUE; 00482 } 00483 00484 void CSensor_cellpos::Reconfigure(const gchar* name, const gchar* value) 00485 { 00486 if ((strcmp(name, "sensor.cellpos.frequency") == 0) || 00487 (strcmp(name, "sensor.cellpos.timeout") == 0)) { 00488 ReadConfig(); 00489 } 00490 } 00491 00492 // The paremeter updates do not necessarily take effect immediately. 00493 void CSensor_cellpos::ReadConfig() 00494 { 00495 iMinScanRequestIntervalSecs = 00496 force_get_ConfigDb_int("sensor.cellpos.frequency", 00497 MIN_SCAN_REQUEST_INTERVAL_SECS); 00498 iSatelliteQueryTimeoutSecs = 00499 force_get_ConfigDb_int("sensor.cellpos.timeout", 00500 SATELLITE_QUERY_TIMEOUT_SECS); 00501 iDataMaxAgeSecs = 00502 force_get_ConfigDb_int("sensor.cellpos.ttl", 00503 POSITIONER_MAX_AGE_SECS); 00504 } 00505 00506 /** 00507 00508 epoc-cellpos.cpp 00509 00510 Copyright 2009-2011 Helsinki Institute for Information Technology 00511 (HIIT) and the authors. All rights reserved. 00512 00513 Authors: Tero Hasu <tero.hasu@hut.fi> 00514 00515 Permission is hereby granted, free of charge, to any person 00516 obtaining a copy of this software and associated documentation files 00517 (the "Software"), to deal in the Software without restriction, 00518 including without limitation the rights to use, copy, modify, merge, 00519 publish, distribute, sublicense, and/or sell copies of the Software, 00520 and to permit persons to whom the Software is furnished to do so, 00521 subject to the following conditions: 00522 00523 The above copyright notice and this permission notice shall be 00524 included in all copies or substantial portions of the Software. 00525 00526 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00527 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00528 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00529 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00530 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00531 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00532 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00533 SOFTWARE. 00534 00535 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1