Qt Virtual Chart Table (QVCT)
CDeviceGpsdAis.cpp
Go to the documentation of this file.
1 // INDENTING (emacs/vi): -*- mode:c++; tab-width:2; c-basic-offset:2; intent-tabs-mode:nil; -*- ex: set tabstop=2 expandtab:
2 
3 /*
4  * Qt Virtual Chart Table (QVCT)
5  * Copyright (C) 2012 Cedric Dufour <http://cedric.dufour.name>
6  * Author: Cedric Dufour <http://cedric.dufour.name>
7  *
8  * The Qt Virtual Chart Table (QVCT) is free software:
9  * you can redistribute it and/or modify it under the terms of the GNU General
10  * Public License as published by the Free Software Foundation, Version 3.
11  *
12  * The Qt Virtual Chart Table (QVCT) is distributed in the hope
13  * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
14  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * See the GNU General Public License for more details.
17  */
18 
19 // C/C++
20 #include <cmath>
21 
22 // QT
23 #include <QDateTime>
24 #include <QDomElement> // QtXml module
25 #include <QString>
26 #include <QXmlStreamWriter>
27 
28 // GPSD
29 #include "gps.h"
30 // #include "gpsdclient.h"
31 
32 // QVCT
33 #include "QVCTRuntime.hpp"
38 
39 
40 //------------------------------------------------------------------------------
41 // CONSTRUCTORS / DESTRUCTOR
42 //------------------------------------------------------------------------------
43 
44 CDeviceGpsdAis::CDeviceGpsdAis( const QString& _rqsName )
45  : CDevice( _rqsName )
46  , qsHost( "127.0.0.1" )
47  , iPort( 2947 )
48  , psGpsData( 0 )
49  , pqSocketNotifier( 0 )
50  , bPaused( false )
51 {}
52 
54 {
55  stop();
56 }
57 
58 
59 //------------------------------------------------------------------------------
60 // METHODS: CDevice (implement/override)
61 //------------------------------------------------------------------------------
62 
64 {
65  QVCT::EStatus __eStatus = QVCT::CRITICAL;
66  switch( _eOperatingMode )
67  {
68 
69  case CDevice::STOP:
70  __eStatus = stop();
71  break;
72 
73  case CDevice::PAUSE:
74  __eStatus = pause();
75  break;
76 
77  case CDevice::START:
78  __eStatus = start();
79  break;
80 
81  default:; // WHAT THE F*** !?!
82  }
83  if( __eStatus != QVCT::OK )
84  {
85  qCritical( "ERROR[%s]: Failed to switch operating mode; host=%s, port=%d", Q_FUNC_INFO, qPrintable( qsHost ), iPort );
86  }
87  return __eStatus;
88 }
89 
91 {
96 }
97 
99 {
100  CDeviceGpsdAisEditView* __poDeviceGpsdAisEditView = new CDeviceGpsdAisEditView( this );
101  if( __poDeviceGpsdAisEditView->exec() == QDialog::Accepted ) showDetail();
102  delete __poDeviceGpsdAisEditView;
103 }
104 
105 void CDeviceGpsdAis::parseQVCT( const QDomElement& _rqDomElement )
106 {
107  QDomElement __qDomElement = _rqDomElement.firstChildElement( "Configuration" );
108  if( __qDomElement.isNull() ) return;
109  qsHost = __qDomElement.attribute( "host", "127.0.0.1" );
110  iPort = __qDomElement.attribute( "port", "2947" ).toInt();
111 }
112 
113 void CDeviceGpsdAis::dumpQVCT( QXmlStreamWriter & _rqXmlStreamWriter ) const
114 {
115  // Device
116  _rqXmlStreamWriter.writeStartElement( "Device" );
117  // ... driver
118  _rqXmlStreamWriter.writeAttribute( "name", qsName );
119  _rqXmlStreamWriter.writeAttribute( "driver", "gpsd_ais" );
120  // ... configuration
121  _rqXmlStreamWriter.writeStartElement( "Configuration" );
122  _rqXmlStreamWriter.writeAttribute( "host", qsHost );
123  _rqXmlStreamWriter.writeAttribute( "port", QString::number( iPort ) );
124  _rqXmlStreamWriter.writeEndElement(); // Configuration
125  // ... [end]
126  _rqXmlStreamWriter.writeEndElement(); // Device
127 }
128 
129 
130 //------------------------------------------------------------------------------
131 // METHODS
132 //------------------------------------------------------------------------------
133 
134 //
135 // SLOTS
136 //
138 {
139  //qDebug( "DEBUG[%s]: Begin", Q_FUNC_INFO );
140  QMutex* __pqMutexDataChange = QVCTRuntime::useMutexDataChange();
141  __pqMutexDataChange->lock();
142  pqSocketNotifier->setEnabled( false );
143 
144  do // data-processing loop
145  {
146  //qDebug( "DEBUG[%s]: GPS data are waiting to be read", Q_FUNC_INFO );
147 
148  // Retrieve data
149  int __iStatus;
150 #if GPSD_API_MAJOR_VERSION >= 7
151  __iStatus = gps_read( psGpsData, NULL, 0 );
152 #elif GPSD_API_MAJOR_VERSION >= 5
153  __iStatus = gps_read( psGpsData );
154 #else
155  __iStatus = gps_poll( psGpsData );
156 #endif
157  if( __iStatus == 0 ) break; // No data available
158  if( __iStatus < 0 )
159  {
160  qCritical( "ERROR[%s]: Failed to read data from device; status=%d", Q_FUNC_INFO, __iStatus );
161  emit signalError( QString( tr("Failed to read data from device")+"; status=%1" ).arg( __iStatus ) );
162  stop();
163  return;
164  }
165  //qDebug( "DEBUG[%s]: GPS data successfully read", Q_FUNC_INFO );
166  //qDebug( "DEBUG[%s]: SET=%s", Q_FUNC_INFO, qPrintable( QString::number( psGpsData->set, 16 ) ) );
167 
168  // Do not process data if paused
169  if( bPaused ) continue;
170 
171  // Check data status
172  if( !( psGpsData->set & PACKET_SET ) ) continue; // We need a valid packet
173  emit signalActivity();
174 
175  // Check AIS data status
176  if( !( psGpsData->set & AIS_SET ) ) continue; // We need a valid AIS packet
177  if( !psGpsData->ais.mmsi ) continue; // We need a valid MMSI number
178  QString __qsSource = QString::number( psGpsData->ais.mmsi );
179  //qDebug( "DEBUG[%s]: GPS data are available from source %s", Q_FUNC_INFO, qPrintable( __qsSource ) );
180 
181  // Parse AIS data
182  // NOTE: we handle only message types: 1, 2, 3, 5, 18, 19, 24
183  CDeviceDataFix __oDeviceDataFix( __qsSource );
184  __oDeviceDataFix.setSourceType( CDeviceDataSource::AIS );
185  __oDeviceDataFix.setCourseFromPosition( false );
186  bool __bDataAvailable = false;
187  int __iSecond = AIS_SEC_NOT_AVAILABLE, __iLongitude = AIS_LON_NOT_AVAILABLE, __iLatitude = AIS_LAT_NOT_AVAILABLE;
188  int __iCourse = AIS_COURSE_NOT_AVAILABLE, __iSpeed = AIS_SPEED_NOT_AVAILABLE;
189  QString __qsShipname;
190  QString __qsCallsign;
191  switch( psGpsData->ais.type )
192  {
193  case 1:
194  case 2:
195  case 3:
196  if( psGpsData->ais.type1.second < 60 ) __iSecond = psGpsData->ais.type1.second;
197  if( psGpsData->ais.type1.lon != AIS_LON_NOT_AVAILABLE ) __iLongitude = psGpsData->ais.type1.lon;
198  if( psGpsData->ais.type1.lat != AIS_LAT_NOT_AVAILABLE ) __iLatitude = psGpsData->ais.type1.lat;
199  if( psGpsData->ais.type1.course < 3600 ) __iCourse = psGpsData->ais.type1.course;
200  if( psGpsData->ais.type1.speed < 1000 ) __iSpeed = psGpsData->ais.type1.speed;
201  break;
202 
203  case 5:
204  __qsShipname = psGpsData->ais.type5.shipname;
205  __qsCallsign = psGpsData->ais.type5.callsign;
206  break;
207 
208  case 18:
209  if( psGpsData->ais.type18.second < 60 ) __iSecond = psGpsData->ais.type18.second;
210  if( psGpsData->ais.type18.lon != AIS_LON_NOT_AVAILABLE ) __iLongitude = psGpsData->ais.type18.lon;
211  if( psGpsData->ais.type18.lat != AIS_LAT_NOT_AVAILABLE ) __iLatitude = psGpsData->ais.type18.lat;
212  if( psGpsData->ais.type18.course < 3600 ) __iCourse = psGpsData->ais.type18.course;
213  if( psGpsData->ais.type18.speed < 1000 ) __iSpeed = psGpsData->ais.type18.speed;
214  break;
215 
216  case 19:
217  if( psGpsData->ais.type19.second < 60 ) __iSecond = psGpsData->ais.type19.second;
218  if( psGpsData->ais.type19.lon != AIS_LON_NOT_AVAILABLE ) __iLongitude = psGpsData->ais.type19.lon;
219  if( psGpsData->ais.type19.lat != AIS_LAT_NOT_AVAILABLE ) __iLatitude = psGpsData->ais.type19.lat;
220  if( psGpsData->ais.type19.course < 3600 ) __iCourse = psGpsData->ais.type19.course;
221  if( psGpsData->ais.type19.speed < 1000 ) __iSpeed = psGpsData->ais.type19.speed;
222  break;
223 
224  case 24:
225  __qsShipname = psGpsData->ais.type24.shipname;
226  __qsCallsign = psGpsData->ais.type24.callsign;
227  break;
228 
229  default:
230  continue;
231 
232  }
233 
234  // ... time
235  if( __iSecond != AIS_SEC_NOT_AVAILABLE )
236  {
237  uint __iCurrentTime = QDateTime::currentDateTime().toTime_t();
238  int __iCurrentSecond = __iCurrentTime % 60;
239  __iCurrentTime -= __iCurrentSecond;
240  __iCurrentTime += __iSecond;
241  if( __iSecond > 45 && __iCurrentSecond < 15 ) __iCurrentTime -= 60;
242  __oDeviceDataFix.setTime( (double)__iCurrentTime );
243  __bDataAvailable = true;
244  }
245 
246  // ... position
247  if( __iLongitude != AIS_LON_NOT_AVAILABLE && __iLatitude != AIS_LAT_NOT_AVAILABLE )
248  {
249  __oDeviceDataFix.setPosition( (double)__iLongitude / 600000.0, (double)__iLatitude / 600000.0 );
250  __oDeviceDataFix.setType( CDeviceDataFix::FIX_2D );
251  __bDataAvailable = true;
252  }
253 
254  // ... course
255  if( __iCourse != AIS_COURSE_NOT_AVAILABLE )
256  {
257  __oDeviceDataFix.setBearing( (double)__iCourse / 10.0 ); // AIS: decidegrees
258  __bDataAvailable = true;
259  }
260 
261  // ... speed
262  if( __iSpeed != AIS_SPEED_NOT_AVAILABLE && __iSpeed > 0 )
263  {
264  __oDeviceDataFix.setSpeed( __iSpeed / 19.4384449244 ); // AIS: deciknot
265  __bDataAvailable = true;
266  }
267 
268  // ... textual data
269  if( !__qsShipname.isEmpty() || !__qsCallsign.isEmpty() )
270  {
271  QString __qsName = __qsShipname;
272  if( !__qsCallsign.isEmpty() ) __qsName += __qsName.isEmpty() ? __qsCallsign : " ("+__qsCallsign+")";
273  __oDeviceDataFix.setText( __qsName );
274  __bDataAvailable = true;
275  }
276 
277  // [end]
278  if( __bDataAvailable )
279  {
280  emit signalDataFix( __oDeviceDataFix );
281  }
282 
283  }
284  while( true ); // data-processing loop
285 
286  pqSocketNotifier->setEnabled( true );
287  __pqMutexDataChange->unlock();
288  //qDebug( "DEBUG[%s]: End", Q_FUNC_INFO );
289 }
290 
291 //
292 // OTHER
293 //
294 
296 {
297  bPaused = false;
298  if( pqSocketNotifier )
299  {
300  QObject::disconnect( pqSocketNotifier, 0, this, 0 );
301  pqSocketNotifier->setEnabled( false );
302  delete pqSocketNotifier;
303  pqSocketNotifier = 0;
304  }
305  if( psGpsData )
306  {
307  gps_stream( psGpsData, WATCH_DISABLE, NULL );
308  gps_close( psGpsData );
309  psGpsData = 0;
310  }
311  qDebug( "DEBUG[%s]: Device successfully stopped", Q_FUNC_INFO );
313  return QVCT::OK;
314 }
315 
317 {
318  if( psGpsData )
319  {
320  bPaused = !bPaused;
321  }
322  qDebug( "DEBUG[%s]: Device successfully paused", Q_FUNC_INFO );
324  return QVCT::OK;
325 }
326 
328 {
329  if( psGpsData ) return QVCT::OK;
330  int __iStatus = 0;
331  do // error-catching loop
332  {
333  bPaused = false;
334 
335  // Open device
336 #if GPSD_API_MAJOR_VERSION >= 5
337  __iStatus = gps_open( qsHost.toLocal8Bit().constData(), QString::number( iPort ).toLocal8Bit().constData(), &sGpsData );
338  psGpsData = &sGpsData;
339 #else
340  psGpsData = gps_open( qsHost.toLocal8Bit().constData(), QString::number( iPort ).toLocal8Bit().constData() );
341  if( !psGpsData ) __iStatus = -1;
342 #endif
343  if( __iStatus < 0 )
344  {
345  psGpsData = 0;
346  qCritical( "ERROR[%s]: Failed to open device; status=%d", Q_FUNC_INFO, __iStatus );
347  emit signalError( QString( tr("Failed to open device")+"; status=%1" ).arg( __iStatus ) );
348  break;
349  }
350 
351  // Initialize socket notifier
352 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
353  pqSocketNotifier = new QSocketNotifier( (qintptr)psGpsData->gps_fd, QSocketNotifier::Read );
354 #else
355  pqSocketNotifier = new QSocketNotifier( (quintptr)psGpsData->gps_fd, QSocketNotifier::Read );
356 #endif
357  QObject::connect( pqSocketNotifier, SIGNAL( activated(int) ), this, SLOT( slotProcessData(int) ) );
358  pqSocketNotifier->setEnabled( true );
359 
360  // Start GPS data streaming
361  __iStatus = gps_stream( psGpsData, WATCH_ENABLE|WATCH_NEWSTYLE, NULL );
362  if( __iStatus < 0 )
363  {
364  qCritical( "ERROR[%s]: Failed to stream data from device; status=%d", Q_FUNC_INFO, __iStatus );
365  emit signalError( QString( tr("Failed to stream data from device")+"; status=%1" ).arg( __iStatus ) );
366  break;
367  }
368 
369  }
370  while( false ); // error-catching loop
371  if( __iStatus < 0 )
372  {
373  stop();
374  return QVCT::ERROR;
375  }
376 #ifndef USE_QT
377  qDebug( "DEBUG[%s]: Device successfully started; socket=%d", Q_FUNC_INFO, psGpsData->gps_fd );
378 #else
379  qDebug( "DEBUG[%s]: Device successfully started; socket=%p", Q_FUNC_INFO, psGpsData->gps_fd );
380 #endif
382  return QVCT::OK;
383 }
384 
386 {
387  if( !psGpsData ) return CDevice::STOP;
388  if( bPaused ) return CDevice::PAUSE;
389  return CDevice::START;
390 }
void setBearing(double _fdBearing)
Sets this course's bearing, in degrees.
Definition: CDataCourse.hpp:96
void setSpeed(double _fdSpeed, double _fdSpeedVertical=UNDEFINED_SPEED)
Sets this course's horizontal speed, in meters per second.
void setPosition(double _fdLongitude, double _fdLatitude, double _fdElevation=UNDEFINED_ELEVATION)
Sets new coordinates.
void setTime(double _fdTime)
Sets the time, in seconds from Unix epoch.
Definition: CDataTime.cpp:60
Fix data [source,time,position,course,DOPs,...].
void setText(const QString &_rqsText)
Sets the additional textual data string.
void setType(int _eType)
Sets the fix type.
void setCourseFromPosition(bool _bCourseFromPosition)
Sets whether to use position to compute course.
void setSourceType(EType _eType)
Sets the source type.
virtual void refreshContent()
Refreshes the content of the underlying widget.
[UI] Route container's edit view
void dumpQVCT(QXmlStreamWriter &_rqXmlStreamWriter) const
Stores the device's configuration to the given QVCT destination (file)
QVCT::EStatus setOperatingMode(CDevice::EOperatingMode _eOperatingMode)
Sets the device's operating mode.
void parseQVCT(const QDomElement &_rqDomElement)
Retrieves the device's configuration from the given QVCT source (file)
QVCT::EStatus stop()
Start the device.
QString qsHost
Network host.
virtual void showDetail()
Displays the device's details (in the appropriate widget/view)
QVCT::EStatus start()
Stop the device.
int iPort
Network port.
void slotProcessData(int)
Slots to process device data.
CDeviceGpsdAis(const QString &_rqsName)
CDevice::EOperatingMode status()
Returns the device's status (operating mode)
bool bPaused
Pause status.
QVCT::EStatus pause()
Pause the device.
virtual ~CDeviceGpsdAis()
struct gps_data_t sGpsData
GPSD data.
struct gps_data_t * psGpsData
GPSD data pointer.
virtual void showEdit()
Displays the device's edit (configuration) widget/view.
QSocketNotifier * pqSocketNotifier
Socket notifier.
Generic navigation device (GPS, speedometer, compass, etc.)
Definition: CDevice.hpp:43
void signalDataFix(const CDeviceDataFix &_roDeviceDataFix)
Signal emitted by the device when an updated fix is available.
void signalActivity()
Signal emitted by the device when activity occures.
EOperatingMode
Device operating mode (stop, start, pause)
Definition: CDevice.hpp:58
@ PAUSE
Definition: CDevice.hpp:58
@ STOP
Definition: CDevice.hpp:58
@ START
Definition: CDevice.hpp:58
void signalError(const QString &_rqsErrorMessage)
Signal emitted by the device when an error occured.
void signalOperatingMode(CDevice::EOperatingMode _eOperatingMode)
Signal emitted by the device when its operating mode changed.
void switchView(EView eView)
Displays the requested container/item details (switching to the appropriate widget)
@ DEVICE
Device overlay.
void switchView(EView eView)
Displays the requested overlay (switching to the appropriate tab)
void setOverlayObject(COverlayObject *_poOverlayObject)
Sets the overlay object to be displayed (and refreshes the underlying widget)
QString qsName
Object name.
static COverlayDetailView * useOverlayDetailView()
static CDeviceDetailView * useDeviceDetailView()
static QMutex * useMutexDataChange()
static COverlayListView * useOverlayListView()
EStatus
Definition: QVCT.hpp:41
@ ERROR
Definition: QVCT.hpp:41
@ OK
Definition: QVCT.hpp:41
@ CRITICAL
Definition: QVCT.hpp:41