epoc-gps.cpp

Go to the documentation of this file.
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