|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include "qnmeapositioninfosource_p.h" |
|
42 #include "qlocationutils_p.h" |
|
43 |
|
44 #include <QIODevice> |
|
45 #include <QBasicTimer> |
|
46 #include <QTimerEvent> |
|
47 #include <QTimer> |
|
48 |
|
49 QTM_BEGIN_NAMESPACE |
|
50 |
|
51 QNmeaRealTimeReader::QNmeaRealTimeReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) |
|
52 : QNmeaReader(sourcePrivate) |
|
53 { |
|
54 } |
|
55 |
|
56 void QNmeaRealTimeReader::readAvailableData() |
|
57 { |
|
58 QGeoPositionInfo update; |
|
59 bool hasFix = false; |
|
60 |
|
61 char buf[1024]; |
|
62 qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); |
|
63 while (size > 0) { |
|
64 if (m_proxy->parsePosInfoFromNmeaData(buf, size, &update, &hasFix)) |
|
65 m_proxy->notifyNewUpdate(&update, hasFix); |
|
66 memset(buf, 0, size); |
|
67 size = m_proxy->m_device->readLine(buf, sizeof(buf)); |
|
68 } |
|
69 } |
|
70 |
|
71 |
|
72 //============================================================ |
|
73 |
|
74 QNmeaSimulatedReader::QNmeaSimulatedReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) |
|
75 : QNmeaReader(sourcePrivate), |
|
76 m_currTimerId(-1), |
|
77 m_hasValidDateTime(false) |
|
78 { |
|
79 } |
|
80 |
|
81 QNmeaSimulatedReader::~QNmeaSimulatedReader() |
|
82 { |
|
83 if (m_currTimerId > 0) |
|
84 killTimer(m_currTimerId); |
|
85 } |
|
86 |
|
87 void QNmeaSimulatedReader::readAvailableData() |
|
88 { |
|
89 if (m_currTimerId > 0) // we are already reading |
|
90 return; |
|
91 |
|
92 if (!m_hasValidDateTime) { // first update |
|
93 Q_ASSERT(m_proxy->m_device && (m_proxy->m_device->openMode() & QIODevice::ReadOnly)); |
|
94 |
|
95 if (!setFirstDateTime()) { |
|
96 //m_proxy->notifyReachedEndOfFile(); |
|
97 qWarning("QNmeaPositionInfoSource: cannot find NMEA sentence with valid date & time"); |
|
98 return; |
|
99 } |
|
100 |
|
101 m_hasValidDateTime = true; |
|
102 simulatePendingUpdate(); |
|
103 |
|
104 } else { |
|
105 // previously read to EOF, but now new data has arrived |
|
106 processNextSentence(); |
|
107 } |
|
108 } |
|
109 |
|
110 bool QNmeaSimulatedReader::setFirstDateTime() |
|
111 { |
|
112 // find the first update with valid date and time |
|
113 QGeoPositionInfo update; |
|
114 bool hasFix = false; |
|
115 while (m_proxy->m_device->bytesAvailable() > 0) { |
|
116 char buf[1024]; |
|
117 qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); |
|
118 if (size <= 0) |
|
119 continue; |
|
120 bool ok = m_proxy->parsePosInfoFromNmeaData(buf, size, &update, &hasFix); |
|
121 if (ok && update.timestamp().isValid()) { |
|
122 QPendingGeoPositionInfo pending; |
|
123 pending.info = update; |
|
124 pending.hasFix = hasFix; |
|
125 m_pendingUpdates.enqueue(pending); |
|
126 return true; |
|
127 } |
|
128 } |
|
129 return false; |
|
130 } |
|
131 |
|
132 void QNmeaSimulatedReader::simulatePendingUpdate() |
|
133 { |
|
134 if (m_pendingUpdates.size() > 0) { |
|
135 // will be dequeued in processNextSentence() |
|
136 QPendingGeoPositionInfo &pending = m_pendingUpdates.head(); |
|
137 if (pending.info.coordinate().type() != QGeoCoordinate::InvalidCoordinate) |
|
138 m_proxy->notifyNewUpdate(&pending.info, pending.hasFix); |
|
139 } |
|
140 |
|
141 processNextSentence(); |
|
142 } |
|
143 |
|
144 void QNmeaSimulatedReader::timerEvent(QTimerEvent *event) |
|
145 { |
|
146 killTimer(event->timerId()); |
|
147 m_currTimerId = -1; |
|
148 simulatePendingUpdate(); |
|
149 } |
|
150 |
|
151 void QNmeaSimulatedReader::processNextSentence() |
|
152 { |
|
153 QGeoPositionInfo info; |
|
154 bool hasFix = false; |
|
155 int timeToNextUpdate = -1; |
|
156 QTime prevTime; |
|
157 if (m_pendingUpdates.size() > 0) |
|
158 prevTime = m_pendingUpdates.head().info.timestamp().time(); |
|
159 |
|
160 // find the next update with a valid time (as long as the time is valid, |
|
161 // we can calculate when the update should be emitted) |
|
162 while (m_proxy->m_device && m_proxy->m_device->bytesAvailable() > 0) { |
|
163 char buf[1024]; |
|
164 qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); |
|
165 if (size <= 0) |
|
166 continue; |
|
167 if (m_proxy->parsePosInfoFromNmeaData(buf, size, &info, &hasFix)) { |
|
168 QTime time = info.timestamp().time(); |
|
169 if (time.isValid()) { |
|
170 if (!prevTime.isValid()) { |
|
171 timeToNextUpdate = 0; |
|
172 break; |
|
173 } |
|
174 timeToNextUpdate = prevTime.msecsTo(time); |
|
175 if (timeToNextUpdate >= 0) |
|
176 break; |
|
177 } |
|
178 } |
|
179 } |
|
180 |
|
181 if (timeToNextUpdate < 0) |
|
182 return; |
|
183 |
|
184 m_pendingUpdates.dequeue(); |
|
185 |
|
186 QPendingGeoPositionInfo pending; |
|
187 pending.info = info; |
|
188 pending.hasFix = hasFix; |
|
189 m_pendingUpdates.enqueue(pending); |
|
190 m_currTimerId = startTimer(timeToNextUpdate); |
|
191 } |
|
192 |
|
193 |
|
194 //============================================================ |
|
195 |
|
196 |
|
197 QNmeaPositionInfoSourcePrivate::QNmeaPositionInfoSourcePrivate(QNmeaPositionInfoSource *parent) |
|
198 : QObject(parent), |
|
199 m_invokedStart(false), |
|
200 m_source(parent), |
|
201 m_nmeaReader(0), |
|
202 m_updateTimer(0), |
|
203 m_requestTimer(0), |
|
204 m_noUpdateLastInterval(false), |
|
205 m_updateTimeoutSent(false), |
|
206 m_connectedReadyRead(false) |
|
207 { |
|
208 } |
|
209 |
|
210 QNmeaPositionInfoSourcePrivate::~QNmeaPositionInfoSourcePrivate() |
|
211 { |
|
212 delete m_nmeaReader; |
|
213 delete m_updateTimer; |
|
214 } |
|
215 |
|
216 bool QNmeaPositionInfoSourcePrivate::openSourceDevice() |
|
217 { |
|
218 if (!m_device) { |
|
219 qWarning("QNmeaPositionInfoSource: no QIODevice data source, call setDevice() first"); |
|
220 return false; |
|
221 } |
|
222 |
|
223 if (!m_device->isOpen() && !m_device->open(QIODevice::ReadOnly)) { |
|
224 qWarning("QNmeaPositionInfoSource: cannot open QIODevice data source"); |
|
225 return false; |
|
226 } |
|
227 |
|
228 connect(m_device, SIGNAL(aboutToClose()), SLOT(sourceDataClosed())); |
|
229 connect(m_device, SIGNAL(readChannelFinished()), SLOT(sourceDataClosed())); |
|
230 connect(m_device, SIGNAL(destroyed()), SLOT(sourceDataClosed())); |
|
231 |
|
232 return true; |
|
233 } |
|
234 |
|
235 void QNmeaPositionInfoSourcePrivate::sourceDataClosed() |
|
236 { |
|
237 if (m_nmeaReader && m_device && m_device->bytesAvailable()) |
|
238 m_nmeaReader->readAvailableData(); |
|
239 } |
|
240 |
|
241 void QNmeaPositionInfoSourcePrivate::readyRead() |
|
242 { |
|
243 if (m_nmeaReader) |
|
244 m_nmeaReader->readAvailableData(); |
|
245 } |
|
246 |
|
247 bool QNmeaPositionInfoSourcePrivate::initialize() |
|
248 { |
|
249 if (m_nmeaReader) |
|
250 return true; |
|
251 |
|
252 if (!openSourceDevice()) |
|
253 return false; |
|
254 |
|
255 if (m_updateMode == QNmeaPositionInfoSource::RealTimeMode) |
|
256 m_nmeaReader = new QNmeaRealTimeReader(this); |
|
257 else |
|
258 m_nmeaReader = new QNmeaSimulatedReader(this); |
|
259 |
|
260 return true; |
|
261 } |
|
262 |
|
263 void QNmeaPositionInfoSourcePrivate::prepareSourceDevice() |
|
264 { |
|
265 // some data may already be available |
|
266 if (m_updateMode == QNmeaPositionInfoSource::SimulationMode) { |
|
267 if (m_nmeaReader && m_device->bytesAvailable()) |
|
268 m_nmeaReader->readAvailableData(); |
|
269 } |
|
270 |
|
271 if (!m_connectedReadyRead) { |
|
272 connect(m_device, SIGNAL(readyRead()), SLOT(readyRead())); |
|
273 m_connectedReadyRead = true; |
|
274 } |
|
275 } |
|
276 |
|
277 bool QNmeaPositionInfoSourcePrivate::parsePosInfoFromNmeaData(const char *data, int size, |
|
278 QGeoPositionInfo *posInfo, bool *hasFix) |
|
279 { |
|
280 return m_source->parsePosInfoFromNmeaData(data, size, posInfo, hasFix); |
|
281 } |
|
282 |
|
283 void QNmeaPositionInfoSourcePrivate::startUpdates() |
|
284 { |
|
285 if (m_invokedStart) |
|
286 return; |
|
287 |
|
288 m_invokedStart = true; |
|
289 m_pendingUpdate = QGeoPositionInfo(); |
|
290 m_noUpdateLastInterval = false; |
|
291 |
|
292 bool initialized = initialize(); |
|
293 if (!initialized) |
|
294 return; |
|
295 |
|
296 if (m_updateMode == QNmeaPositionInfoSource::RealTimeMode) { |
|
297 // skip over any buffered data - we only want the newest data |
|
298 if (m_device->bytesAvailable()) { |
|
299 if (m_device->isSequential()) |
|
300 m_device->readAll(); |
|
301 else |
|
302 m_device->seek(m_device->bytesAvailable()); |
|
303 } |
|
304 } |
|
305 |
|
306 if (m_updateTimer) |
|
307 m_updateTimer->stop(); |
|
308 |
|
309 if (m_source->updateInterval() > 0) { |
|
310 if (!m_updateTimer) |
|
311 m_updateTimer = new QBasicTimer; |
|
312 m_updateTimer->start(m_source->updateInterval(), this); |
|
313 } |
|
314 |
|
315 if (initialized) |
|
316 prepareSourceDevice(); |
|
317 } |
|
318 |
|
319 void QNmeaPositionInfoSourcePrivate::stopUpdates() |
|
320 { |
|
321 m_invokedStart = false; |
|
322 if (m_updateTimer) |
|
323 m_updateTimer->stop(); |
|
324 m_pendingUpdate = QGeoPositionInfo(); |
|
325 m_noUpdateLastInterval = false; |
|
326 } |
|
327 |
|
328 void QNmeaPositionInfoSourcePrivate::requestUpdate(int msec) |
|
329 { |
|
330 if (m_requestTimer && m_requestTimer->isActive()) |
|
331 return; |
|
332 |
|
333 if (msec <= 0 || msec < m_source->minimumUpdateInterval()) { |
|
334 emit m_source->updateTimeout(); |
|
335 return; |
|
336 } |
|
337 |
|
338 if (!m_requestTimer) { |
|
339 m_requestTimer = new QTimer(this); |
|
340 connect(m_requestTimer, SIGNAL(timeout()), SLOT(updateRequestTimeout())); |
|
341 } |
|
342 |
|
343 bool initialized = initialize(); |
|
344 if (!initialized) { |
|
345 emit m_source->updateTimeout(); |
|
346 return; |
|
347 } |
|
348 |
|
349 m_requestTimer->start(msec); |
|
350 |
|
351 if (initialized) |
|
352 prepareSourceDevice(); |
|
353 } |
|
354 |
|
355 void QNmeaPositionInfoSourcePrivate::updateRequestTimeout() |
|
356 { |
|
357 m_requestTimer->stop(); |
|
358 emit m_source->updateTimeout(); |
|
359 } |
|
360 |
|
361 void QNmeaPositionInfoSourcePrivate::notifyNewUpdate(QGeoPositionInfo *update, bool hasFix) |
|
362 { |
|
363 // include <QDebug> before uncommenting |
|
364 //qDebug() << "QNmeaPositionInfoSourcePrivate::notifyNewUpdate()" << update->timestamp() << hasFix << m_invokedStart << (m_requestTimer && m_requestTimer->isActive()); |
|
365 |
|
366 QDate date = update->timestamp().date(); |
|
367 if (date.isValid()) { |
|
368 m_currentDate = date; |
|
369 } else { |
|
370 // some sentence have time but no date |
|
371 QTime time = update->timestamp().time(); |
|
372 if (time.isValid() && m_currentDate.isValid()) |
|
373 update->setTimestamp(QDateTime(m_currentDate, time, Qt::UTC)); |
|
374 } |
|
375 |
|
376 if (hasFix && update->isValid()) { |
|
377 if (m_requestTimer && m_requestTimer->isActive()) { |
|
378 m_requestTimer->stop(); |
|
379 emitUpdated(*update); |
|
380 } else if (m_invokedStart) { |
|
381 if (m_updateTimer && m_updateTimer->isActive()) { |
|
382 // for periodic updates, only want the most recent update |
|
383 m_pendingUpdate = *update; |
|
384 if (m_noUpdateLastInterval) { |
|
385 emitPendingUpdate(); |
|
386 m_noUpdateLastInterval = false; |
|
387 } |
|
388 } else { |
|
389 emitUpdated(*update); |
|
390 } |
|
391 } |
|
392 m_lastUpdate = *update; |
|
393 } |
|
394 } |
|
395 |
|
396 void QNmeaPositionInfoSourcePrivate::timerEvent(QTimerEvent *) |
|
397 { |
|
398 emitPendingUpdate(); |
|
399 } |
|
400 |
|
401 void QNmeaPositionInfoSourcePrivate::emitPendingUpdate() |
|
402 { |
|
403 if (m_pendingUpdate.isValid()) { |
|
404 m_updateTimeoutSent = false; |
|
405 m_noUpdateLastInterval = false; |
|
406 emitUpdated(m_pendingUpdate); |
|
407 m_pendingUpdate = QGeoPositionInfo(); |
|
408 } else { |
|
409 if (m_noUpdateLastInterval && !m_updateTimeoutSent) { |
|
410 m_updateTimeoutSent = true; |
|
411 m_pendingUpdate = QGeoPositionInfo(); |
|
412 emit m_source->updateTimeout(); |
|
413 } |
|
414 m_noUpdateLastInterval = true; |
|
415 } |
|
416 } |
|
417 |
|
418 void QNmeaPositionInfoSourcePrivate::emitUpdated(const QGeoPositionInfo &update) |
|
419 { |
|
420 m_lastUpdate = update; |
|
421 emit m_source->positionUpdated(update); |
|
422 } |
|
423 |
|
424 //========================================================= |
|
425 |
|
426 /*! |
|
427 \class QNmeaPositionInfoSource |
|
428 \brief The QNmeaPositionInfoSource class provides positional information using a NMEA data source. |
|
429 \ingroup location |
|
430 |
|
431 NMEA is a commonly used protocol for the specification of one's global |
|
432 position at a certain point in time. The QNmeaPositionInfoSource class reads NMEA |
|
433 data and uses it to provide positional data in the form of |
|
434 QGeoPositionInfo objects. |
|
435 |
|
436 A QNmeaPositionInfoSource instance operates in either \l {RealTimeMode} or |
|
437 \l {SimulationMode}. These modes allow NMEA data to be read from either a |
|
438 live source of positional data, or replayed for simulation purposes from |
|
439 previously recorded NMEA data. |
|
440 |
|
441 The source of NMEA data is set with setDevice(). |
|
442 |
|
443 Use startUpdates() to start receiving regular position updates and stopUpdates() to stop these |
|
444 updates. If you only require updates occasionally, you can call requestUpdate() to request a |
|
445 single update. |
|
446 |
|
447 In both cases the position information is received via the positionUpdated() signal and the |
|
448 last known position can be accessed with lastKnownPosition(). |
|
449 */ |
|
450 |
|
451 |
|
452 /*! |
|
453 \enum QNmeaPositionInfoSource::UpdateMode |
|
454 Defines the available update modes. |
|
455 |
|
456 \value RealTimeMode Positional data is read and distributed from the data source as it becomes available. Use this mode if you are using a live source of positional data (for example, a GPS hardware device). |
|
457 \value SimulationMode The data and time information in the NMEA source data is used to provide positional updates at the rate at which the data was originally recorded. Use this mode if the data source contains previously recorded NMEA data and you want to replay the data for simulation purposes. |
|
458 */ |
|
459 |
|
460 |
|
461 /*! |
|
462 Constructs a QNmeaPositionInfoSource instance with the given \a parent |
|
463 and \a updateMode. |
|
464 */ |
|
465 QNmeaPositionInfoSource::QNmeaPositionInfoSource(UpdateMode updateMode, QObject *parent) |
|
466 : QGeoPositionInfoSource(parent), |
|
467 d(new QNmeaPositionInfoSourcePrivate(this)) |
|
468 { |
|
469 d->m_updateMode = updateMode; |
|
470 d->m_device = 0; |
|
471 } |
|
472 |
|
473 /*! |
|
474 Destroys the position source. |
|
475 */ |
|
476 QNmeaPositionInfoSource::~QNmeaPositionInfoSource() |
|
477 { |
|
478 delete d; |
|
479 } |
|
480 |
|
481 /*! |
|
482 Parses an NMEA sentence string into a QGeoPositionInfo. |
|
483 |
|
484 The default implementation will parse standard NMEA sentences. |
|
485 This method should be reimplemented in a subclass whenever the need to deal with non-standard |
|
486 NMEA sentences arises. |
|
487 |
|
488 The parser reads \a size bytes from \a data and uses that information to setup \a posInfo and |
|
489 \a hasFix. If \a hasFix is set to false then \a posInfo may contain only the time or the date |
|
490 and the time. |
|
491 |
|
492 Returns true if the sentence was succsesfully parsed, otherwise returns false and should not |
|
493 modifiy \a posInfo or \a hasFix. |
|
494 */ |
|
495 bool QNmeaPositionInfoSource::parsePosInfoFromNmeaData(const char *data, int size, |
|
496 QGeoPositionInfo *posInfo, bool *hasFix) |
|
497 { |
|
498 return QLocationUtils::getPosInfoFromNmea(data, size, posInfo, hasFix); |
|
499 } |
|
500 |
|
501 /*! |
|
502 Returns the update mode. |
|
503 */ |
|
504 QNmeaPositionInfoSource::UpdateMode QNmeaPositionInfoSource::updateMode() const |
|
505 { |
|
506 return d->m_updateMode; |
|
507 } |
|
508 |
|
509 /*! |
|
510 Sets the NMEA data source to \a device. If the device is not open, it |
|
511 will be opened in QIODevice::ReadOnly mode. |
|
512 |
|
513 The source device can only be set once and must be set before calling |
|
514 startUpdates() or requestUpdate(). |
|
515 |
|
516 \bold {Note:} The \a device must emit QIODevice::readyRead() for the |
|
517 source to be notified when data is available for reading. |
|
518 */ |
|
519 void QNmeaPositionInfoSource::setDevice(QIODevice *device) |
|
520 { |
|
521 if (device != d->m_device) { |
|
522 if (!d->m_device) |
|
523 d->m_device = device; |
|
524 else |
|
525 qWarning("QNmeaPositionInfoSource: source device has already been set"); |
|
526 } |
|
527 } |
|
528 |
|
529 /*! |
|
530 Returns the NMEA data source. |
|
531 */ |
|
532 QIODevice *QNmeaPositionInfoSource::device() const |
|
533 { |
|
534 return d->m_device; |
|
535 } |
|
536 |
|
537 /*! |
|
538 \reimp |
|
539 */ |
|
540 void QNmeaPositionInfoSource::setUpdateInterval(int msec) |
|
541 { |
|
542 int interval = msec; |
|
543 if (interval != 0) |
|
544 interval = qMax(msec, minimumUpdateInterval()); |
|
545 QGeoPositionInfoSource::setUpdateInterval(interval); |
|
546 if (d->m_invokedStart) { |
|
547 d->stopUpdates(); |
|
548 d->startUpdates(); |
|
549 } |
|
550 } |
|
551 |
|
552 /*! |
|
553 \reimp |
|
554 */ |
|
555 void QNmeaPositionInfoSource::startUpdates() |
|
556 { |
|
557 d->startUpdates(); |
|
558 } |
|
559 |
|
560 /*! |
|
561 \reimp |
|
562 */ |
|
563 void QNmeaPositionInfoSource::stopUpdates() |
|
564 { |
|
565 d->stopUpdates(); |
|
566 } |
|
567 |
|
568 /*! |
|
569 \reimp |
|
570 */ |
|
571 void QNmeaPositionInfoSource::requestUpdate(int msec) |
|
572 { |
|
573 d->requestUpdate(msec == 0 ? 60000 * 5 : msec); |
|
574 } |
|
575 |
|
576 /*! |
|
577 \reimp |
|
578 */ |
|
579 QGeoPositionInfo QNmeaPositionInfoSource::lastKnownPosition(bool) const |
|
580 { |
|
581 // the bool value does not matter since we only use satellite positioning |
|
582 return d->m_lastUpdate; |
|
583 } |
|
584 |
|
585 /*! |
|
586 \reimp |
|
587 */ |
|
588 QGeoPositionInfoSource::PositioningMethods QNmeaPositionInfoSource::supportedPositioningMethods() const |
|
589 { |
|
590 return SatellitePositioningMethods; |
|
591 } |
|
592 |
|
593 /*! |
|
594 \reimp |
|
595 */ |
|
596 int QNmeaPositionInfoSource::minimumUpdateInterval() const |
|
597 { |
|
598 return 100; |
|
599 } |
|
600 |
|
601 #include "moc_qnmeapositioninfosource.cpp" |
|
602 #include "moc_qnmeapositioninfosource_p.cpp" |
|
603 |
|
604 QTM_END_NAMESPACE |