An extension of original API for working with GPS devices.
GPSProvider.h@1:c1122f8eec82, 2014-10-31 (annotated)
- Committer:
- rgrover1
- Date:
- Fri Oct 31 13:34:59 2014 +0000
- Revision:
- 1:c1122f8eec82
- Parent:
- 0:792e9343fd39
- Child:
- 4:f35227b1f341
- Child:
- 5:900c3ab6c090
Updating GPSProvider based on input from CSR.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rgrover1 | 0:792e9343fd39 | 1 | /* mbed Microcontroller Library |
rgrover1 | 0:792e9343fd39 | 2 | * Copyright (c) 2006-2014 ARM Limited |
rgrover1 | 0:792e9343fd39 | 3 | * |
rgrover1 | 0:792e9343fd39 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
rgrover1 | 0:792e9343fd39 | 5 | * you may not use this file except in compliance with the License. |
rgrover1 | 0:792e9343fd39 | 6 | * You may obtain a copy of the License at |
rgrover1 | 0:792e9343fd39 | 7 | * |
rgrover1 | 0:792e9343fd39 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
rgrover1 | 0:792e9343fd39 | 9 | * |
rgrover1 | 0:792e9343fd39 | 10 | * Unless required by applicable law or agreed to in writing, software |
rgrover1 | 0:792e9343fd39 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
rgrover1 | 0:792e9343fd39 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
rgrover1 | 0:792e9343fd39 | 13 | * See the License for the specific language governing permissions and |
rgrover1 | 0:792e9343fd39 | 14 | * limitations under the License. |
rgrover1 | 0:792e9343fd39 | 15 | */ |
rgrover1 | 0:792e9343fd39 | 16 | |
rgrover1 | 0:792e9343fd39 | 17 | #ifndef __GPS_PROVIDER_H__ |
rgrover1 | 0:792e9343fd39 | 18 | #define __GPS_PROVIDER_H__ |
rgrover1 | 0:792e9343fd39 | 19 | |
rgrover1 | 0:792e9343fd39 | 20 | class GPSProviderImplBase; /* forward declaration */ |
rgrover1 | 0:792e9343fd39 | 21 | extern GPSProviderImplBase *createGPSProviderInstance(void); |
rgrover1 | 0:792e9343fd39 | 22 | |
rgrover1 | 0:792e9343fd39 | 23 | // |
rgrover1 | 1:c1122f8eec82 | 24 | // Here's a snippet showing how this API can be used. The handler gets invoked |
rgrover1 | 1:c1122f8eec82 | 25 | // from thread context--i.e. from the main application. |
rgrover1 | 0:792e9343fd39 | 26 | // |
rgrover1 | 1:c1122f8eec82 | 27 | // void handleGPSData(const LocationUpdateParams_t *newLocation) { |
rgrover1 | 1:c1122f8eec82 | 28 | // ... |
rgrover1 | 0:792e9343fd39 | 29 | // } |
rgrover1 | 0:792e9343fd39 | 30 | // |
rgrover1 | 0:792e9343fd39 | 31 | // GPSProvider gps; |
rgrover1 | 0:792e9343fd39 | 32 | // |
rgrover1 | 0:792e9343fd39 | 33 | // gps.setPowerMode(LOW_POWER); |
rgrover1 | 0:792e9343fd39 | 34 | // gps.onLocationUpdate(handleGPSData); |
rgrover1 | 0:792e9343fd39 | 35 | // |
rgrover1 | 1:c1122f8eec82 | 36 | // gps.reset(); |
rgrover1 | 0:792e9343fd39 | 37 | // gps.start(); |
rgrover1 | 0:792e9343fd39 | 38 | // |
rgrover1 | 1:c1122f8eec82 | 39 | // while (true) { |
rgrover1 | 1:c1122f8eec82 | 40 | // /* purely optional */ |
rgrover1 | 1:c1122f8eec82 | 41 | // static bool printedDeviceInfo = false; |
rgrover1 | 1:c1122f8eec82 | 42 | // if (!printedDeviceInfo && gps.haveDeviceInfo()) { |
rgrover1 | 1:c1122f8eec82 | 43 | // printf("%s", gps.getDeviceInfo()); |
rgrover1 | 1:c1122f8eec82 | 44 | // printedDeviceInfo = true; |
rgrover1 | 1:c1122f8eec82 | 45 | // } |
rgrover1 | 0:792e9343fd39 | 46 | // |
rgrover1 | 1:c1122f8eec82 | 47 | // /* Main message processing activity; location callbacks are called |
rgrover1 | 1:c1122f8eec82 | 48 | // * as a result of message processing. */ |
rgrover1 | 1:c1122f8eec82 | 49 | // gps.process(); |
rgrover1 | 1:c1122f8eec82 | 50 | // /* sleep(); */ |
rgrover1 | 0:792e9343fd39 | 51 | // |
rgrover1 | 1:c1122f8eec82 | 52 | // if (/* at some point in the future */) { |
rgrover1 | 1:c1122f8eec82 | 53 | // break; |
rgrover1 | 1:c1122f8eec82 | 54 | // } |
rgrover1 | 1:c1122f8eec82 | 55 | // } |
rgrover1 | 0:792e9343fd39 | 56 | // gps.stop(); |
rgrover1 | 0:792e9343fd39 | 57 | // |
rgrover1 | 0:792e9343fd39 | 58 | |
rgrover1 | 0:792e9343fd39 | 59 | class GPSProvider { |
rgrover1 | 0:792e9343fd39 | 60 | public: |
rgrover1 | 0:792e9343fd39 | 61 | /** Power mode selection */ |
rgrover1 | 0:792e9343fd39 | 62 | enum PowerMode_t { |
rgrover1 | 1:c1122f8eec82 | 63 | POWER_FULL, /**< full-power mode typically results in high-accuracy location data updated at 1Hz. */ |
rgrover1 | 1:c1122f8eec82 | 64 | POWER_LOW, /**< low-power mode involves longer periods of hibernation in between location updates. */ |
rgrover1 | 0:792e9343fd39 | 65 | }; |
rgrover1 | 0:792e9343fd39 | 66 | |
rgrover1 | 0:792e9343fd39 | 67 | /** |
rgrover1 | 0:792e9343fd39 | 68 | * GPS time starts from UTC 01/06/1980(MM/DD/YYYY) and includes 2 fields: |
rgrover1 | 0:792e9343fd39 | 69 | * week and tow. For a given GPS time, week is the passed week number since |
rgrover1 | 0:792e9343fd39 | 70 | * the GPS start time, and tow(time of week) is the passed seconds since the |
rgrover1 | 0:792e9343fd39 | 71 | * last Sunday. For example, a given UTC time [10/30/3014 |
rgrover1 | 0:792e9343fd39 | 72 | * 01:10:00](MM/DD/YYYY HH:MM:SS) can be converted into GPS time [1816, |
rgrover1 | 0:792e9343fd39 | 73 | * 4200](week, seconds). |
rgrover1 | 0:792e9343fd39 | 74 | * |
rgrover1 | 0:792e9343fd39 | 75 | * GPS time can be used as a quite accurate time once position is fixed. |
rgrover1 | 0:792e9343fd39 | 76 | */ |
rgrover1 | 0:792e9343fd39 | 77 | struct GPSTime_t { |
rgrover1 | 0:792e9343fd39 | 78 | uint16_t gps_week; |
rgrover1 | 0:792e9343fd39 | 79 | uint32_t tow; /* time of week (in seconds) */ |
rgrover1 | 0:792e9343fd39 | 80 | }; |
rgrover1 | 0:792e9343fd39 | 81 | |
rgrover1 | 0:792e9343fd39 | 82 | typedef float LocationType_t; |
rgrover1 | 0:792e9343fd39 | 83 | typedef float Altitude_t; |
rgrover1 | 0:792e9343fd39 | 84 | struct LocationUpdateParams_t { |
rgrover1 | 0:792e9343fd39 | 85 | uint32_t version; /* Layout-version for the following structure; |
rgrover1 | 0:792e9343fd39 | 86 | * this is to accommodate changes over time. */ |
rgrover1 | 1:c1122f8eec82 | 87 | bool valid; /* Does this update contain a valid location. */ |
rgrover1 | 0:792e9343fd39 | 88 | LocationType_t lat; |
rgrover1 | 0:792e9343fd39 | 89 | LocationType_t lon; |
rgrover1 | 0:792e9343fd39 | 90 | Altitude_t altitude; |
rgrover1 | 0:792e9343fd39 | 91 | |
rgrover1 | 1:c1122f8eec82 | 92 | unsigned numGPSSVs; /* num GPS Satellites */ |
rgrover1 | 1:c1122f8eec82 | 93 | unsigned numGLOSVs; /* num GLONASS Satellites */ |
rgrover1 | 1:c1122f8eec82 | 94 | |
rgrover1 | 0:792e9343fd39 | 95 | union { |
rgrover1 | 0:792e9343fd39 | 96 | GPSTime_t gpsTime; |
rgrover1 | 0:792e9343fd39 | 97 | float utcTime; |
rgrover1 | 0:792e9343fd39 | 98 | } u; |
rgrover1 | 0:792e9343fd39 | 99 | }; |
rgrover1 | 0:792e9343fd39 | 100 | |
rgrover1 | 0:792e9343fd39 | 101 | public: |
rgrover1 | 0:792e9343fd39 | 102 | /** |
rgrover1 | 0:792e9343fd39 | 103 | * Set the operating mode for power. Typically this allows the user to |
rgrover1 | 0:792e9343fd39 | 104 | * choose between various low-power modes. Entering low-power modes often |
rgrover1 | 0:792e9343fd39 | 105 | * results in a trade-off between accuracy and power consumption. |
rgrover1 | 0:792e9343fd39 | 106 | * |
rgrover1 | 0:792e9343fd39 | 107 | * The new mode takes effect upon calling start(). |
rgrover1 | 0:792e9343fd39 | 108 | * |
rgrover1 | 0:792e9343fd39 | 109 | * @param power The new power mode. |
rgrover1 | 0:792e9343fd39 | 110 | * @return true if the update was successful. |
rgrover1 | 0:792e9343fd39 | 111 | */ |
rgrover1 | 0:792e9343fd39 | 112 | bool setPowerMode(PowerMode_t power); |
rgrover1 | 0:792e9343fd39 | 113 | |
rgrover1 | 0:792e9343fd39 | 114 | /** |
rgrover1 | 0:792e9343fd39 | 115 | * HW reset to get location chip into hibernation mode, but in readiness for |
rgrover1 | 0:792e9343fd39 | 116 | * starting operation. The GPS controller emerges from hibernation when |
rgrover1 | 0:792e9343fd39 | 117 | * start() is called. |
rgrover1 | 0:792e9343fd39 | 118 | * |
rgrover1 | 0:792e9343fd39 | 119 | * The typical initialization sequence is: |
rgrover1 | 0:792e9343fd39 | 120 | * reset(); |
rgrover1 | 0:792e9343fd39 | 121 | * start(); // and thereafter receive location-update callbacks. |
rgrover1 | 0:792e9343fd39 | 122 | */ |
rgrover1 | 0:792e9343fd39 | 123 | void reset(void); |
rgrover1 | 0:792e9343fd39 | 124 | |
rgrover1 | 0:792e9343fd39 | 125 | /** |
rgrover1 | 0:792e9343fd39 | 126 | * Start the GPS operation, taking into account the operation mode set |
rgrover1 | 0:792e9343fd39 | 127 | * previously. Following this call, the user may expect to receive location |
rgrover1 | 0:792e9343fd39 | 128 | * notifications in interrupt context if a handler has previously been set. |
rgrover1 | 0:792e9343fd39 | 129 | * |
rgrover1 | 0:792e9343fd39 | 130 | * @note: calling start repeatedly doesn't hurt. |
rgrover1 | 0:792e9343fd39 | 131 | */ |
rgrover1 | 0:792e9343fd39 | 132 | void start(void); |
rgrover1 | 0:792e9343fd39 | 133 | |
rgrover1 | 0:792e9343fd39 | 134 | /** |
rgrover1 | 0:792e9343fd39 | 135 | * Stop active operation of the GPS; and put it to hibernation. |
rgrover1 | 0:792e9343fd39 | 136 | * |
rgrover1 | 0:792e9343fd39 | 137 | * @note: You don't need to call reset() afterwards to enter hibernation. |
rgrover1 | 0:792e9343fd39 | 138 | * @note: Calling stop() repeatedly doesn't hurt. |
rgrover1 | 0:792e9343fd39 | 139 | */ |
rgrover1 | 0:792e9343fd39 | 140 | void stop(void); |
rgrover1 | 0:792e9343fd39 | 141 | |
rgrover1 | 0:792e9343fd39 | 142 | /** |
rgrover1 | 1:c1122f8eec82 | 143 | * Process location data from chip and update location and satellite |
rgrover1 | 1:c1122f8eec82 | 144 | * information. This API is supposed to be called repeatedly from the |
rgrover1 | 1:c1122f8eec82 | 145 | * application in thread mode to process incoming messages as they are |
rgrover1 | 1:c1122f8eec82 | 146 | * received from the GPS controller. Arriving data is first appended to |
rgrover1 | 1:c1122f8eec82 | 147 | * something like a circular buffer by interrupts, and then parsed as |
rgrover1 | 1:c1122f8eec82 | 148 | * messages in thread mode. |
rgrover1 | 1:c1122f8eec82 | 149 | * |
rgrover1 | 1:c1122f8eec82 | 150 | * The application typically enters a loop calling process() after |
rgrover1 | 1:c1122f8eec82 | 151 | * initializing the GPS controller with start(). process() returns |
rgrover1 | 1:c1122f8eec82 | 152 | * immediately if there is no work to be done, but it must get invoked |
rgrover1 | 1:c1122f8eec82 | 153 | * frequently in order to keep pace with arriving data. |
rgrover1 | 1:c1122f8eec82 | 154 | * |
rgrover1 | 1:c1122f8eec82 | 155 | * Mbed's sleep() may be usefully thrown into the application's process() |
rgrover1 | 1:c1122f8eec82 | 156 | * loop to save power--sleep() has the effect of putting the processor to |
rgrover1 | 1:c1122f8eec82 | 157 | * sleep while waiting for an event (such as an interrupt). As always, some |
rgrover1 | 1:c1122f8eec82 | 158 | * care must be taken in employing sleep(), because there is a small |
rgrover1 | 1:c1122f8eec82 | 159 | * synchronization window where an interrupt may arrive and pend data which |
rgrover1 | 1:c1122f8eec82 | 160 | * doesn't get processed (as illustrated below). |
rgrover1 | 1:c1122f8eec82 | 161 | * |
rgrover1 | 1:c1122f8eec82 | 162 | * while (true) { |
rgrover1 | 1:c1122f8eec82 | 163 | * process(); |
rgrover1 | 1:c1122f8eec82 | 164 | * <-- interrupt arrives now and appends new data |
rgrover1 | 1:c1122f8eec82 | 165 | * sleep(); // but then we go to sleep without processing it. |
rgrover1 | 1:c1122f8eec82 | 166 | * } |
rgrover1 | 1:c1122f8eec82 | 167 | * |
rgrover1 | 1:c1122f8eec82 | 168 | * There is a way around it: if sleep() boils down to the use of ARM's WFE |
rgrover1 | 1:c1122f8eec82 | 169 | * instruction (as opposed to WFI), then its safe from the above-mentioned |
rgrover1 | 1:c1122f8eec82 | 170 | * synchronization window. |
rgrover1 | 1:c1122f8eec82 | 171 | */ |
rgrover1 | 1:c1122f8eec82 | 172 | void process(void); |
rgrover1 | 1:c1122f8eec82 | 173 | |
rgrover1 | 1:c1122f8eec82 | 174 | /** |
rgrover1 | 0:792e9343fd39 | 175 | * @return true if the initialization process has received enough |
rgrover1 | 0:792e9343fd39 | 176 | * information to determine the hardware's version/device-info. |
rgrover1 | 0:792e9343fd39 | 177 | */ |
rgrover1 | 1:c1122f8eec82 | 178 | bool haveDeviceInfo(void) const; |
rgrover1 | 0:792e9343fd39 | 179 | |
rgrover1 | 0:792e9343fd39 | 180 | /** |
rgrover1 | 0:792e9343fd39 | 181 | * Fetch the device information string. This is expected to contain the |
rgrover1 | 0:792e9343fd39 | 182 | * version number or any other device identifier. |
rgrover1 | 0:792e9343fd39 | 183 | */ |
rgrover1 | 1:c1122f8eec82 | 184 | const char *getDeviceInfo(void) const; |
rgrover1 | 0:792e9343fd39 | 185 | |
rgrover1 | 0:792e9343fd39 | 186 | /** |
rgrover1 | 0:792e9343fd39 | 187 | * This is a wildcard API for sending controller specific commands. Use of |
rgrover1 | 0:792e9343fd39 | 188 | * this API will make programs non-portable, but then there may arise a |
rgrover1 | 0:792e9343fd39 | 189 | * genuine need to access special functionality. |
rgrover1 | 0:792e9343fd39 | 190 | * |
rgrover1 | 0:792e9343fd39 | 191 | * @param command A controller specific command. |
rgrover1 | 0:792e9343fd39 | 192 | * @param arg Argument to the command; interpreted according to the command. |
rgrover1 | 0:792e9343fd39 | 193 | * @return any return from the command. |
rgrover1 | 0:792e9343fd39 | 194 | */ |
rgrover1 | 0:792e9343fd39 | 195 | uint32_t ioctl(uint32_t command, void *arg); |
rgrover1 | 0:792e9343fd39 | 196 | |
rgrover1 | 0:792e9343fd39 | 197 | /** |
rgrover1 | 0:792e9343fd39 | 198 | * @return true if we've obtained at least one valid location since last |
rgrover1 | 0:792e9343fd39 | 199 | * calling start(). |
rgrover1 | 0:792e9343fd39 | 200 | * |
rgrover1 | 0:792e9343fd39 | 201 | * @Note: This is cleared after reset(). |
rgrover1 | 0:792e9343fd39 | 202 | */ |
rgrover1 | 1:c1122f8eec82 | 203 | bool locationAvailable(void) const; |
rgrover1 | 0:792e9343fd39 | 204 | |
rgrover1 | 0:792e9343fd39 | 205 | /** |
rgrover1 | 0:792e9343fd39 | 206 | * @return the last valid location if there is any; else NULL. |
rgrover1 | 0:792e9343fd39 | 207 | */ |
rgrover1 | 1:c1122f8eec82 | 208 | const LocationUpdateParams_t *getLastLocation(void) const; |
rgrover1 | 0:792e9343fd39 | 209 | |
rgrover1 | 0:792e9343fd39 | 210 | /** |
rgrover1 | 0:792e9343fd39 | 211 | * Type declaration for a callback to be invoked from interrupt context upon |
rgrover1 | 0:792e9343fd39 | 212 | * receiving new location data. |
rgrover1 | 0:792e9343fd39 | 213 | * |
rgrover1 | 0:792e9343fd39 | 214 | * @Note: Please note that the handler gets invoked from interrupt context. |
rgrover1 | 0:792e9343fd39 | 215 | * Users *should not* do any long running or blocking operations in the |
rgrover1 | 0:792e9343fd39 | 216 | * handler. |
rgrover1 | 0:792e9343fd39 | 217 | */ |
rgrover1 | 0:792e9343fd39 | 218 | typedef void (* LocationUpdateCallback_t)(const LocationUpdateParams_t *params); |
rgrover1 | 0:792e9343fd39 | 219 | |
rgrover1 | 0:792e9343fd39 | 220 | /** |
rgrover1 | 0:792e9343fd39 | 221 | * Setup the locationUpdate callback. |
rgrover1 | 0:792e9343fd39 | 222 | * |
rgrover1 | 0:792e9343fd39 | 223 | * @Note: Please note that the handler gets invoked from interrupt context. |
rgrover1 | 0:792e9343fd39 | 224 | * Users *should not* do any long running or blocking operations in the |
rgrover1 | 0:792e9343fd39 | 225 | * handler. |
rgrover1 | 0:792e9343fd39 | 226 | */ |
rgrover1 | 0:792e9343fd39 | 227 | void onLocationUpdate(LocationUpdateCallback_t callback); |
rgrover1 | 0:792e9343fd39 | 228 | |
rgrover1 | 1:c1122f8eec82 | 229 | /** |
rgrover1 | 1:c1122f8eec82 | 230 | * In low-power operation, the GPS controller may be expected to hibernate |
rgrover1 | 1:c1122f8eec82 | 231 | * for extended periods and location updates may be infrequent. It should |
rgrover1 | 1:c1122f8eec82 | 232 | * then be possible for an application to demand location data when needed. |
rgrover1 | 1:c1122f8eec82 | 233 | * |
rgrover1 | 1:c1122f8eec82 | 234 | * This calls results in a locationCallback if there is a useful location to |
rgrover1 | 1:c1122f8eec82 | 235 | * report. |
rgrover1 | 1:c1122f8eec82 | 236 | */ |
rgrover1 | 1:c1122f8eec82 | 237 | void lpmGetImmediateLocation(void); |
rgrover1 | 1:c1122f8eec82 | 238 | |
rgrover1 | 0:792e9343fd39 | 239 | public: |
rgrover1 | 0:792e9343fd39 | 240 | /** |
rgrover1 | 0:792e9343fd39 | 241 | * Default constructor. |
rgrover1 | 0:792e9343fd39 | 242 | */ |
rgrover1 | 1:c1122f8eec82 | 243 | GPSProvider() : impl(createGPSProviderInstance()) { |
rgrover1 | 0:792e9343fd39 | 244 | /* empty */ |
rgrover1 | 0:792e9343fd39 | 245 | } |
rgrover1 | 0:792e9343fd39 | 246 | |
rgrover1 | 0:792e9343fd39 | 247 | virtual ~GPSProvider() { |
rgrover1 | 0:792e9343fd39 | 248 | stop(); |
rgrover1 | 0:792e9343fd39 | 249 | } |
rgrover1 | 0:792e9343fd39 | 250 | |
rgrover1 | 0:792e9343fd39 | 251 | private: |
rgrover1 | 0:792e9343fd39 | 252 | /** |
rgrover1 | 0:792e9343fd39 | 253 | * We use 'composition' to combine a driver-implementation object to the |
rgrover1 | 0:792e9343fd39 | 254 | * GPSProvider interface. The implementation object will come to life |
rgrover1 | 0:792e9343fd39 | 255 | * through the createGPSProviderInstance(), which must be defined by the |
rgrover1 | 0:792e9343fd39 | 256 | * driver library. The mechanics of the implementation are to be hidden |
rgrover1 | 0:792e9343fd39 | 257 | * behind the abstract interface provided by GPSProvider. |
rgrover1 | 0:792e9343fd39 | 258 | */ |
rgrover1 | 0:792e9343fd39 | 259 | GPSProviderImplBase *const impl; |
rgrover1 | 0:792e9343fd39 | 260 | |
rgrover1 | 0:792e9343fd39 | 261 | /* disallow copy constructor and assignment operators */ |
rgrover1 | 0:792e9343fd39 | 262 | private: |
rgrover1 | 0:792e9343fd39 | 263 | GPSProvider(const GPSProvider&); |
rgrover1 | 0:792e9343fd39 | 264 | GPSProvider & operator= (const GPSProvider&); |
rgrover1 | 0:792e9343fd39 | 265 | }; |
rgrover1 | 0:792e9343fd39 | 266 | |
rgrover1 | 0:792e9343fd39 | 267 | #endif /*__GPS_PROVIDER_H__*/ |