GPS subagent
authorVictor Kirhenshtein <victor@netxms.org>
Fri, 5 Feb 2016 22:05:50 +0000 (00:05 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Fri, 5 Feb 2016 22:06:12 +0000 (00:06 +0200)
configure.ac
src/agent/subagents/Makefile.am
src/agent/subagents/gps/.cproject [new file with mode: 0644]
src/agent/subagents/gps/.project [new file with mode: 0644]
src/agent/subagents/gps/.settings/language.settings.xml [new file with mode: 0644]
src/agent/subagents/gps/Makefile.am [new file with mode: 0644]
src/agent/subagents/gps/main.cpp [new file with mode: 0644]
src/agent/subagents/gps/nmea.c [new file with mode: 0644]
src/agent/subagents/gps/nmea.h [new file with mode: 0644]
src/libnetxms/serial.cpp

index 85e9046..63d38ed 100644 (file)
@@ -3103,6 +3103,7 @@ AC_CONFIG_FILES([
        src/agent/subagents/ecs/Makefile
        src/agent/subagents/filemgr/Makefile
        src/agent/subagents/freebsd/Makefile
+       src/agent/subagents/gps/Makefile
        src/agent/subagents/hpux/Makefile
        src/agent/subagents/informix/Makefile
        src/agent/subagents/ipso/Makefile
index f2579e1..4c09f27 100644 (file)
@@ -14,5 +14,5 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-SUBDIRS = dbquery filemgr winnt winperf wmi portCheck ping sms ups ecs logwatch devemu ${SUBAGENT_DIRS} 
+SUBDIRS = dbquery filemgr gps winnt winperf wmi portCheck ping sms ups ecs logwatch devemu ${SUBAGENT_DIRS} 
 EXTRA_DIST = platform-subagent.sym subagent.sym
diff --git a/src/agent/subagents/gps/.cproject b/src/agent/subagents/gps/.cproject
new file mode 100644 (file)
index 0000000..48823c7
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+       <storageModule moduleId="org.eclipse.cdt.core.settings">
+               <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1184929017">
+                       <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1184929017" moduleId="org.eclipse.cdt.core.settings" name="Default">
+                               <externalSettings/>
+                               <extensions>
+                                       <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                               </extensions>
+                       </storageModule>
+                       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                               <configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1184929017" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+                                       <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1184929017.505602232" name="/" resourcePath="">
+                                               <toolChain id="cdt.managedbuild.toolchain.gnu.base.1457379580" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.base">
+                                                       <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.1130235109" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+                                                       <builder id="cdt.managedbuild.target.gnu.builder.base.321266281" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.archiver.base.605265244" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1787954782" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
+                                                               <option id="gnu.cpp.compiler.option.include.paths.655989526" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <option id="gnu.cpp.compiler.option.preprocessor.def.1229206604" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="_THREAD_SAFE"/>
+                                                                       <listOptionValue builtIn="false" value="TRE_WCHAR=1"/>
+                                                                       <listOptionValue builtIn="false" value="UNICODE"/>
+                                                                       <listOptionValue builtIn="false" value="_GNU_SOURCE"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1655797110" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1853359711" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+                                                               <option id="gnu.c.compiler.option.include.paths.2011400972" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <option id="gnu.c.compiler.option.preprocessor.def.symbols.390423872" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="_THREAD_SAFE"/>
+                                                                       <listOptionValue builtIn="false" value="TRE_WCHAR=1"/>
+                                                                       <listOptionValue builtIn="false" value="UNICODE"/>
+                                                                       <listOptionValue builtIn="false" value="_GNU_SOURCE"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.703061226" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1729882329" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1475376712" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1339537795" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+                                                                       <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+                                                                       <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+                                                               </inputType>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.assembler.base.2063879353" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+                                                               <option id="gnu.both.asm.option.include.paths.493278279" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1080340390" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+                                                       </tool>
+                                               </toolChain>
+                                       </folderInfo>
+                               </configuration>
+                       </storageModule>
+                       <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+               </cconfiguration>
+       </storageModule>
+       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+               <project id="gps.null.722685395" name="gps"/>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+       <storageModule moduleId="scannerConfiguration">
+               <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1184929017;cdt.managedbuild.toolchain.gnu.base.1184929017.505602232;cdt.managedbuild.tool.gnu.c.compiler.base.1853359711;cdt.managedbuild.tool.gnu.c.compiler.input.703061226">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1184929017;cdt.managedbuild.toolchain.gnu.base.1184929017.505602232;cdt.managedbuild.tool.gnu.cpp.compiler.base.1787954782;cdt.managedbuild.tool.gnu.cpp.compiler.input.1655797110">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
+</cproject>
diff --git a/src/agent/subagents/gps/.project b/src/agent/subagents/gps/.project
new file mode 100644 (file)
index 0000000..966ceba
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>gps</name>
+       <comment></comment>
+       <projects>
+               <project>libnetxms</project>
+               <project>libnxagent</project>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+                       <triggers>clean,full,incremental,</triggers>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+                       <triggers>full,incremental,</triggers>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.cdt.core.cnature</nature>
+               <nature>org.eclipse.cdt.core.ccnature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+       </natures>
+       <filteredResources>
+               <filter>
+                       <id>1454686561773</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.o</arguments>
+                       </matcher>
+               </filter>
+               <filter>
+                       <id>1454686561782</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.lo</arguments>
+                       </matcher>
+               </filter>
+               <filter>
+                       <id>1454686561788</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.la</arguments>
+                       </matcher>
+               </filter>
+       </filteredResources>
+</projectDescription>
diff --git a/src/agent/subagents/gps/.settings/language.settings.xml b/src/agent/subagents/gps/.settings/language.settings.xml
new file mode 100644 (file)
index 0000000..5615ca6
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+       <configuration id="cdt.managedbuild.toolchain.gnu.base.1184929017" name="Default">
+               <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+                       <provider class="org.eclipse.cdt.core.language.settings.providers.LanguageSettingsGenericProvider" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider" name="CDT User Setting Entries" prefer-non-shared="true"/>
+                       <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+                       <provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" ref="shared-provider"/>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+               </extension>
+       </configuration>
+</project>
diff --git a/src/agent/subagents/gps/Makefile.am b/src/agent/subagents/gps/Makefile.am
new file mode 100644 (file)
index 0000000..c160c85
--- /dev/null
@@ -0,0 +1,18 @@
+SUBAGENT = gps
+
+pkglib_LTLIBRARIES = gps.la
+gps_la_SOURCES = main.cpp nmea.c
+gps_la_CPPFLAGS=-I@top_srcdir@/include
+gps_la_LDFLAGS = -module -avoid-version -export-symbols ../subagent.sym
+gps_la_LIBADD = ../../libnxagent/libnxagent.la ../../../libnetxms/libnetxms.la
+
+EXTRA_DIST = gps.h gps.vcproj
+
+if !STATIC_BUILD
+install-exec-hook:
+       if test "x`uname -s`" = "xAIX" ; then OBJECT_MODE=@OBJECT_MODE@ $(AR) x $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ ; rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a ; fi
+       mv -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ $(DESTDIR)$(pkglibdir)/$(SUBAGENT).nsm
+       rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).la
+       rm -f $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+       ln -s netxms/$(SUBAGENT).nsm $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+endif
diff --git a/src/agent/subagents/gps/main.cpp b/src/agent/subagents/gps/main.cpp
new file mode 100644 (file)
index 0000000..875a1d0
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+** NetXMS GPS receiver subagent
+** Copyright (C) 2006-2016 Raden Solutions
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: main.cpp
+**
+**/
+
+#include <nms_agent.h>
+#include <geolocation.h>
+#include <math.h>
+#include "nmea.h"
+
+/**
+ * Port name
+ */
+static TCHAR s_device[MAX_PATH];
+
+/**
+ * NMEA info
+ */
+static nmeaINFO s_nmeaInfo;
+
+/**
+ * Parsed geolocation object
+ */
+static GeoLocation s_geolocation;
+
+/**
+ * Lock for NMEA info
+ */
+static MUTEX s_nmeaInfoLock = MutexCreate();
+
+/**
+ * Serial port
+ */
+static Serial s_serial;
+
+/**
+ * Initialize serial port
+ *
+ * s_device format: portname,speed,databits,parity,stopbits
+ */
+static bool InitSerialPort()
+{
+   bool success = false;
+   TCHAR *portName;
+
+   if (s_device[0] == 0)
+   {
+#ifdef _WIN32
+      portName = _tcsdup(_T("COM1:"));
+#else
+      portName = _tcsdup(_T("/dev/ttyS0"));
+#endif
+   }
+   else
+   {
+      portName = _tcsdup(s_device);
+   }
+
+   AgentWriteDebugLog(1, _T("GPS: using serial port configuration \"%s\""), portName);
+
+   TCHAR *p;
+   const TCHAR *parityAsText;
+   int portSpeed = 4800;
+   int dataBits = 8;
+   int parity = NOPARITY;
+   int stopBits = ONESTOPBIT;
+
+   if ((p = _tcschr(portName, _T(','))) != NULL)
+   {
+      *p = 0; p++;
+      int tmp = _tcstol(p, NULL, 10);
+      if (tmp != 0)
+      {
+         portSpeed = tmp;
+
+         if ((p = _tcschr(p, _T(','))) != NULL)
+         {
+            *p = 0; p++;
+            tmp = _tcstol(p, NULL, 10);
+            if (tmp >= 5 && tmp <= 8)
+            {
+               dataBits = tmp;
+
+               // parity
+               if ((p = _tcschr(p, _T(','))) != NULL)
+               {
+                  *p = 0; p++;
+                  switch (tolower((char)*p))
+                  {
+                     case 'n': // none
+                        parity = NOPARITY;
+                        break;
+                     case 'o': // odd
+                        parity = ODDPARITY;
+                        break;
+                     case 'e': // even
+                        parity = EVENPARITY;
+                        break;
+                  }
+
+                  // stop bits
+                  if ((p = _tcschr(p, _T(','))) != NULL)
+                  {
+                     *p = 0; p++;
+
+                     if (*p == _T('2'))
+                     {
+                        stopBits = TWOSTOPBITS;
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   switch (parity)
+   {
+      case ODDPARITY:
+         parityAsText = _T("ODD");
+         break;
+      case EVENPARITY:
+         parityAsText = _T("EVEN");
+         break;
+      default:
+         parityAsText = _T("NONE");
+         break;
+   }
+   AgentWriteDebugLog(1, _T("GPS: initialize for port=\"%s\", speed=%d, data=%d, parity=%s, stop=%d"),
+                      portName, portSpeed, dataBits, parityAsText, stopBits == TWOSTOPBITS ? 2 : 1);
+
+   if (s_serial.open(portName))
+   {
+      AgentWriteDebugLog(5, _T("GPS: port opened"));
+      s_serial.setTimeout(2000);
+
+      if (s_serial.set(portSpeed, dataBits, parity, stopBits))
+      {
+         success = true;
+      }
+      else
+      {
+         AgentWriteDebugLog(5, _T("GPS: cannot set port parameters"));
+      }
+
+      AgentWriteLog(NXLOG_INFO, _T("GPS: serial port initialized"));
+   }
+   else
+   {
+      AgentWriteLog(NXLOG_WARNING, _T("GPS: Unable to open serial port"));
+   }
+
+   free(portName);
+   return success;
+}
+
+/**
+ * Convert NMEA NDEG field ([dd][mm].[s/60]) to degrees
+ */
+inline double NMEA_TO_DEG(double nm)
+{
+   double n = fabs(nm);
+   int d = (int)(n / 100.0);
+   int m = (int)(n - d * 100);
+   double s = n - (d * 100 + m);
+   double r = d + m / 60.0 + s / 60.0;
+   return (nm < 0) ? -r : r;
+}
+
+/**
+ * Poller thread
+ */
+static THREAD_RESULT THREAD_CALL PollerThread(void *arg)
+{
+   AgentWriteDebugLog(3, _T("GPS: poller thread started"));
+
+   nmeaPARSER parser;
+   nmea_zero_INFO(&s_nmeaInfo);
+   nmea_parser_init(&parser);
+
+   while(!AgentSleepAndCheckForShutdown(30))
+   {
+      if (!s_serial.restart())
+      {
+         AgentWriteDebugLog(7, _T("GPS: cannot open serial port"));
+         continue;
+      }
+
+      while(!AgentSleepAndCheckForShutdown(0))
+      {
+         static const char *marks[] = { "\r\n", NULL };
+         char *occ, buffer[128];
+         if (s_serial.readToMark(buffer, 128, marks, &occ) <= 0)
+         {
+            AgentWriteDebugLog(8, _T("GPS: serial port read failure"));
+            break;
+         }
+
+         if (occ != NULL)
+         {
+            MutexLock(s_nmeaInfoLock);
+            int count = nmea_parse(&parser, buffer, (int)strlen(buffer), &s_nmeaInfo);
+            if (count > 0)
+            {
+               s_geolocation = GeoLocation(GL_GPS, NMEA_TO_DEG(s_nmeaInfo.lat), NMEA_TO_DEG(s_nmeaInfo.lon), 0, time(NULL));
+            }
+            MutexUnlock(s_nmeaInfoLock);
+         }
+      }
+   }
+
+   nmea_parser_destroy(&parser);
+   AgentWriteDebugLog(3, _T("GPS: poller thread stopped"));
+   return THREAD_OK;
+}
+
+/**
+ * Poller thread handle
+ */
+static THREAD s_pollerThread = INVALID_THREAD_HANDLE;
+
+/**
+ * Subagent initialization
+ */
+static BOOL SubAgentInit(Config *config)
+{
+       // Parse configuration
+       const TCHAR *value = config->getValue(_T("/GPS/Device"));
+       if (value != NULL)
+       {
+               nx_strncpy(s_device, value, MAX_PATH);
+               InitSerialPort();
+               s_pollerThread = ThreadCreateEx(PollerThread, 0, NULL);
+       }
+       else
+       {
+               AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("GPS: device not specified"));
+       }
+
+       return value != NULL;
+}
+
+/**
+ * Called by master agent at unload
+ */
+static void SubAgentShutdown()
+{
+   ThreadJoin(s_pollerThread);
+}
+
+/**
+ * Handler for GPS.SerialConfig
+ */
+static LONG H_Config(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   ret_string(value, s_device);
+   return SYSINFO_RC_SUCCESS;
+}
+
+/**
+ * Handler for location information parameters
+ */
+static LONG H_LocationInfo(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   LONG rc = SYSINFO_RC_SUCCESS;
+
+   MutexLock(s_nmeaInfoLock);
+   switch(*arg)
+   {
+      case 'A': // latitude as text
+         ret_string(value, s_geolocation.getLatitudeAsString());
+         break;
+      case 'a': // latitude
+         ret_double(value, s_geolocation.getLatitude());
+         break;
+      case 'D': // direction
+         ret_int(value, s_nmeaInfo.direction);
+         break;
+      case 'L': // location as text
+         _sntprintf(value, MAX_RESULT_LENGTH, _T("%s %s"), s_geolocation.getLatitudeAsString(), s_geolocation.getLongitudeAsString());
+         break;
+      case 'O': // longitude as text
+         ret_string(value, s_geolocation.getLongitudeAsString());
+         break;
+      case 'o': // longitude
+         ret_double(value, s_geolocation.getLongitude());
+         break;
+      case 's': // number of satellites in view
+         ret_int(value, s_nmeaInfo.satinfo.inview);
+         break;
+      case 'S': // number of satellites in use
+         ret_int(value, s_nmeaInfo.satinfo.inuse);
+         break;
+      case 'X': // speed
+         ret_int(value, s_nmeaInfo.speed);
+         break;
+      default:
+         rc = SYSINFO_RC_UNSUPPORTED;
+         break;
+   }
+   MutexUnlock(s_nmeaInfoLock);
+   return rc;
+}
+
+/**
+ * Subagent parameters
+ */
+static NETXMS_SUBAGENT_PARAM m_parameters[] =
+{
+   { _T("GPS.Direction"), H_LocationInfo, _T("D"), DCI_DT_FLOAT, _T("GPS: direction") },
+       { _T("GPS.Latitude"), H_LocationInfo, _T("a"), DCI_DT_FLOAT, _T("GPS: latitude") },
+   { _T("GPS.LatitudeText"), H_LocationInfo, _T("A"), DCI_DT_STRING, _T("GPS: latitude (as text)") },
+   { _T("GPS.Location"), H_LocationInfo, _T("L"), DCI_DT_STRING, _T("GPS: location") },
+   { _T("GPS.Longitude"), H_LocationInfo, _T("o"), DCI_DT_FLOAT, _T("GPS: longitude") },
+   { _T("GPS.LongitudeText"), H_LocationInfo, _T("O"), DCI_DT_STRING, _T("GPS: longitude (as text)") },
+   { _T("GPS.Satellites.InUse"), H_LocationInfo, _T("S"), DCI_DT_INT, _T("GPS: satellites in use") },
+   { _T("GPS.Satellites.InView"), H_LocationInfo, _T("s"), DCI_DT_INT, _T("GPS: satellites in view") },
+   { _T("GPS.Speed"), H_LocationInfo, _T("X"), DCI_DT_FLOAT, _T("GPS: ground speed") },
+       { _T("GPS.SerialConfig"), H_Config, NULL, DCI_DT_STRING, _T("GPS: serial port configuration") }
+};
+
+/**
+ * Subagent information
+ */
+static NETXMS_SUBAGENT_INFO m_info =
+{
+       NETXMS_SUBAGENT_INFO_MAGIC,
+       _T("GPS"), NETXMS_VERSION_STRING,
+       SubAgentInit, SubAgentShutdown, NULL,
+       sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
+       m_parameters,
+       0, NULL,        // lists
+       0, NULL,        // tables
+   0, NULL, // actions
+       0, NULL // push parameters
+};
+
+/**
+ * Entry point for NetXMS agent
+ */
+DECLARE_SUBAGENT_ENTRY_POINT(GPS)
+{
+       *ppInfo = &m_info;
+       return TRUE;
+}
+
+#ifdef _WIN32
+
+/**
+ * DLL entry point
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+       if (dwReason == DLL_PROCESS_ATTACH)
+               DisableThreadLibraryCalls(hInstance);
+       return TRUE;
+}
+
+#endif
diff --git a/src/agent/subagents/gps/nmea.c b/src/agent/subagents/gps/nmea.c
new file mode 100644 (file)
index 0000000..eb63264
--- /dev/null
@@ -0,0 +1,1332 @@
+#include "nmea.h"
+
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: time.c 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+/*! \file time.h */
+
+#ifdef NMEA_WIN
+#   pragma warning(disable: 4201)
+#   pragma warning(disable: 4214)
+#   pragma warning(disable: 4115)
+#   include <windows.h>
+#   pragma warning(default: 4201)
+#   pragma warning(default: 4214)
+#   pragma warning(default: 4115)
+#else
+#   include <time.h>
+#endif
+
+#ifdef NMEA_WIN
+
+void nmea_time_now(nmeaTIME *stm)
+{
+    SYSTEMTIME st;
+
+    GetSystemTime(&st);
+
+    stm->year = st.wYear - 1900;
+    stm->mon = st.wMonth - 1;
+    stm->day = st.wDay;
+    stm->hour = st.wHour;
+    stm->min = st.wMinute;
+    stm->sec = st.wSecond;
+    stm->hsec = st.wMilliseconds / 10;
+}
+
+#else /* NMEA_WIN */
+
+void nmea_time_now(nmeaTIME *stm)
+{
+    time_t lt;
+    struct tm *tt;
+
+    time(&lt);
+    tt = gmtime(&lt);
+
+    stm->year = tt->tm_year;
+    stm->mon = tt->tm_mon;
+    stm->day = tt->tm_mday;
+    stm->hour = tt->tm_hour;
+    stm->min = tt->tm_min;
+    stm->sec = tt->tm_sec;
+    stm->hsec = 0;
+}
+
+#endif
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: info.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+#include <string.h>
+
+void nmea_zero_INFO(nmeaINFO *info)
+{
+    memset(info, 0, sizeof(nmeaINFO));
+    nmea_time_now(&info->utc);
+    info->sig = NMEA_SIG_BAD;
+    info->fix = NMEA_FIX_BAD;
+}
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: sentence.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+void nmea_zero_GPGGA(nmeaGPGGA *pack)
+{
+    memset(pack, 0, sizeof(nmeaGPGGA));
+    nmea_time_now(&pack->utc);
+    pack->ns = 'N';
+    pack->ew = 'E';
+    pack->elv_units = 'M';
+    pack->diff_units = 'M';
+}
+
+void nmea_zero_GPGSA(nmeaGPGSA *pack)
+{
+    memset(pack, 0, sizeof(nmeaGPGSA));
+    pack->fix_mode = 'A';
+    pack->fix_type = NMEA_FIX_BAD;
+}
+
+void nmea_zero_GPGSV(nmeaGPGSV *pack)
+{
+    memset(pack, 0, sizeof(nmeaGPGSV));
+}
+
+void nmea_zero_GPRMC(nmeaGPRMC *pack)
+{
+    memset(pack, 0, sizeof(nmeaGPRMC));
+    nmea_time_now(&pack->utc);
+    pack->status = 'V';
+    pack->ns = 'N';
+    pack->ew = 'E';
+    pack->declin_ew = 'E';
+}
+
+void nmea_zero_GPVTG(nmeaGPVTG *pack)
+{
+    memset(pack, 0, sizeof(nmeaGPVTG));
+    pack->dir_t = 'T';
+    pack->dec_m = 'M';
+    pack->spn_n = 'N';
+    pack->spk_k = 'K';
+}
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: parse.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+/**
+ * \file parse.h
+ * \brief Functions of a low level for analysis of
+ * packages of NMEA stream.
+ *
+ * \code
+ * ...
+ * ptype = nmea_pack_type(
+ *     (const char *)parser->buffer + nparsed + 1,
+ *     parser->buff_use - nparsed - 1);
+ * 
+ * if(0 == (node = malloc(sizeof(nmeaParserNODE))))
+ *     goto mem_fail;
+ * 
+ * node->pack = 0;
+ * 
+ * switch(ptype)
+ * {
+ * case GPGGA:
+ *     if(0 == (node->pack = malloc(sizeof(nmeaGPGGA))))
+ *         goto mem_fail;
+ *     node->packType = GPGGA;
+ *     if(!nmea_parse_GPGGA(
+ *         (const char *)parser->buffer + nparsed,
+ *         sen_sz, (nmeaGPGGA *)node->pack))
+ *     {
+ *         free(node);
+ *         node = 0;
+ *     }
+ *     break;
+ * case GPGSA:
+ *     if(0 == (node->pack = malloc(sizeof(nmeaGPGSA))))
+ *         goto mem_fail;
+ *     node->packType = GPGSA;
+ *     if(!nmea_parse_GPGSA(
+ *         (const char *)parser->buffer + nparsed,
+ *         sen_sz, (nmeaGPGSA *)node->pack))
+ *     {
+ *         free(node);
+ *         node = 0;
+ *     }
+ *     break;
+ * ...
+ * \endcode
+ */
+
+#include <stdio.h>
+
+int _nmea_parse_time(const char *buff, int buff_sz, nmeaTIME *res)
+{
+    int success = 0;
+
+    switch(buff_sz)
+    {
+    case sizeof("hhmmss") - 1:
+        success = (3 == nmea_scanf(buff, buff_sz,
+            "%2d%2d%2d", &(res->hour), &(res->min), &(res->sec)
+            ));
+        break;
+    case sizeof("hhmmss.s") - 1:
+    case sizeof("hhmmss.ss") - 1:
+    case sizeof("hhmmss.sss") - 1:
+        success = (4 == nmea_scanf(buff, buff_sz,
+            "%2d%2d%2d.%d", &(res->hour), &(res->min), &(res->sec), &(res->hsec)
+            ));
+        break;
+    default:
+        nmea_error("Parse of time error (format error)!");
+        success = 0;
+        break;
+    }
+
+    return (success?0:-1);        
+}
+
+/**
+ * \brief Define packet type by header (nmeaPACKTYPE).
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @return The defined packet type
+ * @see nmeaPACKTYPE
+ */
+int nmea_pack_type(const char *buff, int buff_sz)
+{
+    static const char *pheads[] = {
+        "GPGGA",
+        "GPGSA",
+        "GPGSV",
+        "GPRMC",
+        "GPVTG",
+    };
+
+    NMEA_ASSERT(buff);
+
+    if(buff_sz < 5)
+        return GPNON;
+    else if(0 == memcmp(buff, pheads[0], 5))
+        return GPGGA;
+    else if(0 == memcmp(buff, pheads[1], 5))
+        return GPGSA;
+    else if(0 == memcmp(buff, pheads[2], 5))
+        return GPGSV;
+    else if(0 == memcmp(buff, pheads[3], 5))
+        return GPRMC;
+    else if(0 == memcmp(buff, pheads[4], 5))
+        return GPVTG;
+
+    return GPNON;
+}
+
+/**
+ * \brief Find tail of packet ("\r\n") in buffer and check control sum (CRC).
+ * @param buff a constant character pointer of packets buffer.
+ * @param buff_sz buffer size.
+ * @param res_crc a integer pointer for return CRC of packet (must be defined).
+ * @return Number of bytes to packet tail.
+ */
+int nmea_find_tail(const char *buff, int buff_sz, int *res_crc)
+{
+    static const int tail_sz = 3 /* *[CRC] */ + 2 /* \r\n */;
+
+    const char *end_buff = buff + buff_sz;
+    int nread = 0;
+    int crc = 0;
+
+    NMEA_ASSERT(buff && res_crc);
+
+    *res_crc = -1;
+
+    for(;buff < end_buff; ++buff, ++nread)
+    {
+        if(('$' == *buff) && nread)
+        {
+            buff = 0;
+            break;
+        }
+        else if('*' == *buff)
+        {
+            if(buff + tail_sz <= end_buff && '\r' == buff[3] && '\n' == buff[4])
+            {
+                *res_crc = nmea_atoi(buff + 1, 2, 16);
+                nread = buff_sz - (int)(end_buff - (buff + tail_sz));
+                if(*res_crc != crc)
+                {
+                    *res_crc = -1;
+                    buff = 0;
+                }
+            }
+
+            break;
+        }
+        else if(nread)
+            crc ^= (int)*buff;
+    }
+
+    if(*res_crc < 0 && buff)
+        nread = 0;
+
+    return nread;
+}
+
+/**
+ * \brief Parse GGA packet from buffer.
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @param pack a pointer of packet which will filled by function.
+ * @return 1 (true) - if parsed successfully or 0 (false) - if fail.
+ */
+int nmea_parse_GPGGA(const char *buff, int buff_sz, nmeaGPGGA *pack)
+{
+    char time_buff[NMEA_TIMEPARSE_BUF];
+
+    NMEA_ASSERT(buff && pack);
+
+    memset(pack, 0, sizeof(nmeaGPGGA));
+
+    nmea_trace_buff(buff, buff_sz);
+
+    if(14 != nmea_scanf(buff, buff_sz,
+        "$GPGGA,%s,%f,%C,%f,%C,%d,%d,%f,%f,%C,%f,%C,%f,%d*",
+        &(time_buff[0]),
+        &(pack->lat), &(pack->ns), &(pack->lon), &(pack->ew),
+        &(pack->sig), &(pack->satinuse), &(pack->HDOP), &(pack->elv), &(pack->elv_units),
+        &(pack->diff), &(pack->diff_units), &(pack->dgps_age), &(pack->dgps_sid)))
+    {
+        nmea_error("GPGGA parse error!");
+        return 0;
+    }
+
+    if(0 != _nmea_parse_time(&time_buff[0], (int)strlen(&time_buff[0]), &(pack->utc)))
+    {
+        nmea_error("GPGGA time parse error!");
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * \brief Parse GSA packet from buffer.
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @param pack a pointer of packet which will filled by function.
+ * @return 1 (true) - if parsed successfully or 0 (false) - if fail.
+ */
+int nmea_parse_GPGSA(const char *buff, int buff_sz, nmeaGPGSA *pack)
+{
+    NMEA_ASSERT(buff && pack);
+
+    memset(pack, 0, sizeof(nmeaGPGSA));
+
+    nmea_trace_buff(buff, buff_sz);
+
+    if(17 != nmea_scanf(buff, buff_sz,
+        "$GPGSA,%C,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%f*",
+        &(pack->fix_mode), &(pack->fix_type),
+        &(pack->sat_prn[0]), &(pack->sat_prn[1]), &(pack->sat_prn[2]), &(pack->sat_prn[3]), &(pack->sat_prn[4]), &(pack->sat_prn[5]),
+        &(pack->sat_prn[6]), &(pack->sat_prn[7]), &(pack->sat_prn[8]), &(pack->sat_prn[9]), &(pack->sat_prn[10]), &(pack->sat_prn[11]),
+        &(pack->PDOP), &(pack->HDOP), &(pack->VDOP)))
+    {
+        nmea_error("GPGSA parse error!");
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * \brief Parse GSV packet from buffer.
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @param pack a pointer of packet which will filled by function.
+ * @return 1 (true) - if parsed successfully or 0 (false) - if fail.
+ */
+int nmea_parse_GPGSV(const char *buff, int buff_sz, nmeaGPGSV *pack)
+{
+    int nsen, nsat;
+
+    NMEA_ASSERT(buff && pack);
+
+    memset(pack, 0, sizeof(nmeaGPGSV));
+
+    nmea_trace_buff(buff, buff_sz);
+
+    nsen = nmea_scanf(buff, buff_sz,
+        "$GPGSV,%d,%d,%d,"
+        "%d,%d,%d,%d,"
+        "%d,%d,%d,%d,"
+        "%d,%d,%d,%d,"
+        "%d,%d,%d,%d*",
+        &(pack->pack_count), &(pack->pack_index), &(pack->sat_count),
+        &(pack->sat_data[0].id), &(pack->sat_data[0].elv), &(pack->sat_data[0].azimuth), &(pack->sat_data[0].sig),
+        &(pack->sat_data[1].id), &(pack->sat_data[1].elv), &(pack->sat_data[1].azimuth), &(pack->sat_data[1].sig),
+        &(pack->sat_data[2].id), &(pack->sat_data[2].elv), &(pack->sat_data[2].azimuth), &(pack->sat_data[2].sig),
+        &(pack->sat_data[3].id), &(pack->sat_data[3].elv), &(pack->sat_data[3].azimuth), &(pack->sat_data[3].sig));
+
+    nsat = (pack->pack_index - 1) * NMEA_SATINPACK;
+    nsat = (nsat + NMEA_SATINPACK > pack->sat_count)?pack->sat_count - nsat:NMEA_SATINPACK;
+    nsat = nsat * 4 + 3 /* first three sentence`s */;
+
+    if(nsen < nsat || nsen > (NMEA_SATINPACK * 4 + 3))
+    {
+        nmea_error("GPGSV parse error!");
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * \brief Parse RMC packet from buffer.
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @param pack a pointer of packet which will filled by function.
+ * @return 1 (true) - if parsed successfully or 0 (false) - if fail.
+ */
+int nmea_parse_GPRMC(const char *buff, int buff_sz, nmeaGPRMC *pack)
+{
+    int nsen;
+    char time_buff[NMEA_TIMEPARSE_BUF];
+
+    NMEA_ASSERT(buff && pack);
+
+    memset(pack, 0, sizeof(nmeaGPRMC));
+
+    nmea_trace_buff(buff, buff_sz);
+
+    nsen = nmea_scanf(buff, buff_sz,
+        "$GPRMC,%s,%C,%f,%C,%f,%C,%f,%f,%2d%2d%2d,%f,%C,%C*",
+        &(time_buff[0]),
+        &(pack->status), &(pack->lat), &(pack->ns), &(pack->lon), &(pack->ew),
+        &(pack->speed), &(pack->direction),
+        &(pack->utc.day), &(pack->utc.mon), &(pack->utc.year),
+        &(pack->declination), &(pack->declin_ew), &(pack->mode));
+
+    if(nsen != 13 && nsen != 14)
+    {
+        nmea_error("GPRMC parse error!");
+        return 0;
+    }
+
+    if(0 != _nmea_parse_time(&time_buff[0], (int)strlen(&time_buff[0]), &(pack->utc)))
+    {
+        nmea_error("GPRMC time parse error!");
+        return 0;
+    }
+
+    if(pack->utc.year < 90)
+        pack->utc.year += 100;
+    pack->utc.mon -= 1;
+
+    return 1;
+}
+
+/**
+ * \brief Parse VTG packet from buffer.
+ * @param buff a constant character pointer of packet buffer.
+ * @param buff_sz buffer size.
+ * @param pack a pointer of packet which will filled by function.
+ * @return 1 (true) - if parsed successfully or 0 (false) - if fail.
+ */
+int nmea_parse_GPVTG(const char *buff, int buff_sz, nmeaGPVTG *pack)
+{
+    NMEA_ASSERT(buff && pack);
+
+    memset(pack, 0, sizeof(nmeaGPVTG));
+
+    nmea_trace_buff(buff, buff_sz);
+
+    if(8 != nmea_scanf(buff, buff_sz,
+        "$GPVTG,%f,%C,%f,%C,%f,%C,%f,%C*",
+        &(pack->dir), &(pack->dir_t),
+        &(pack->dec), &(pack->dec_m),
+        &(pack->spn), &(pack->spn_n),
+        &(pack->spk), &(pack->spk_k)))
+    {
+        nmea_error("GPVTG parse error!");
+        return 0;
+    }
+
+    if( pack->dir_t != 'T' ||
+        pack->dec_m != 'M' ||
+        pack->spn_n != 'N' ||
+        pack->spk_k != 'K')
+    {
+        nmea_error("GPVTG parse error (format error)!");
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * \brief Fill nmeaINFO structure by GGA packet data.
+ * @param pack a pointer of packet structure.
+ * @param info a pointer of summary information structure.
+ */
+void nmea_GPGGA2info(nmeaGPGGA *pack, nmeaINFO *info)
+{
+    NMEA_ASSERT(pack && info);
+
+    info->utc.hour = pack->utc.hour;
+    info->utc.min = pack->utc.min;
+    info->utc.sec = pack->utc.sec;
+    info->utc.hsec = pack->utc.hsec;
+    info->sig = pack->sig;
+    info->HDOP = pack->HDOP;
+    info->elv = pack->elv;
+    info->lat = ((pack->ns == 'N')?pack->lat:-(pack->lat));
+    info->lon = ((pack->ew == 'E')?pack->lon:-(pack->lon));
+    info->smask |= GPGGA;
+}
+
+/**
+ * \brief Fill nmeaINFO structure by GSA packet data.
+ * @param pack a pointer of packet structure.
+ * @param info a pointer of summary information structure.
+ */
+void nmea_GPGSA2info(nmeaGPGSA *pack, nmeaINFO *info)
+{
+    int i, j, nuse = 0;
+
+    NMEA_ASSERT(pack && info);
+
+    info->fix = pack->fix_type;
+    info->PDOP = pack->PDOP;
+    info->HDOP = pack->HDOP;
+    info->VDOP = pack->VDOP;
+
+    for(i = 0; i < NMEA_MAXSAT; ++i)
+    {
+        for(j = 0; j < info->satinfo.inview; ++j)
+        {
+            if(pack->sat_prn[i] && pack->sat_prn[i] == info->satinfo.sat[j].id)
+            {
+                info->satinfo.sat[j].in_use = 1;
+                nuse++;
+            }
+        }
+    }
+
+    info->satinfo.inuse = nuse;
+    info->smask |= GPGSA;
+}
+
+/**
+ * \brief Fill nmeaINFO structure by GSV packet data.
+ * @param pack a pointer of packet structure.
+ * @param info a pointer of summary information structure.
+ */
+void nmea_GPGSV2info(nmeaGPGSV *pack, nmeaINFO *info)
+{
+    int isat, isi, nsat;
+
+    NMEA_ASSERT(pack && info);
+
+    if(pack->pack_index > pack->pack_count ||
+        pack->pack_index * NMEA_SATINPACK > NMEA_MAXSAT)
+        return;
+
+    if(pack->pack_index < 1)
+        pack->pack_index = 1;
+
+    info->satinfo.inview = pack->sat_count;
+
+    nsat = (pack->pack_index - 1) * NMEA_SATINPACK;
+    nsat = (nsat + NMEA_SATINPACK > pack->sat_count)?pack->sat_count - nsat:NMEA_SATINPACK;
+
+    for(isat = 0; isat < nsat; ++isat)
+    {
+        isi = (pack->pack_index - 1) * NMEA_SATINPACK + isat;
+        info->satinfo.sat[isi].id = pack->sat_data[isat].id;
+        info->satinfo.sat[isi].elv = pack->sat_data[isat].elv;
+        info->satinfo.sat[isi].azimuth = pack->sat_data[isat].azimuth;
+        info->satinfo.sat[isi].sig = pack->sat_data[isat].sig;
+    }
+
+    info->smask |= GPGSV;
+}
+
+/**
+ * \brief Fill nmeaINFO structure by RMC packet data.
+ * @param pack a pointer of packet structure.
+ * @param info a pointer of summary information structure.
+ */
+void nmea_GPRMC2info(nmeaGPRMC *pack, nmeaINFO *info)
+{
+    NMEA_ASSERT(pack && info);
+
+    if('A' == pack->status)
+    {
+        if(NMEA_SIG_BAD == info->sig)
+            info->sig = NMEA_SIG_MID;
+        if(NMEA_FIX_BAD == info->fix)
+            info->fix = NMEA_FIX_2D;
+    }
+    else if('V' == pack->status)
+    {
+        info->sig = NMEA_SIG_BAD;
+        info->fix = NMEA_FIX_BAD;
+    }
+
+    info->utc = pack->utc;
+    info->lat = ((pack->ns == 'N')?pack->lat:-(pack->lat));
+    info->lon = ((pack->ew == 'E')?pack->lon:-(pack->lon));
+    info->speed = pack->speed * NMEA_TUD_KNOTS;
+    info->direction = pack->direction;
+    info->smask |= GPRMC;
+}
+
+/**
+ * \brief Fill nmeaINFO structure by VTG packet data.
+ * @param pack a pointer of packet structure.
+ * @param info a pointer of summary information structure.
+ */
+void nmea_GPVTG2info(nmeaGPVTG *pack, nmeaINFO *info)
+{
+    NMEA_ASSERT(pack && info);
+
+    info->direction = pack->dir;
+    info->declination = pack->dec;
+    info->speed = pack->spk;
+    info->smask |= GPVTG;
+}
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: parser.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+/**
+ * \file parser.h
+ */
+
+#include <stdlib.h>
+
+typedef struct _nmeaParserNODE
+{
+    int packType;
+    void *pack;
+    struct _nmeaParserNODE *next_node;
+
+} nmeaParserNODE;
+
+/*
+ * high level
+ */
+
+/**
+ * \brief Initialization of parser object
+ * @return true (1) - success or false (0) - fail
+ */
+int nmea_parser_init(nmeaPARSER *parser)
+{
+    int resv = 0;
+    int buff_size = nmea_property()->parse_buff_size;
+
+    NMEA_ASSERT(parser);
+
+    if(buff_size < NMEA_MIN_PARSEBUFF)
+        buff_size = NMEA_MIN_PARSEBUFF;
+
+    memset(parser, 0, sizeof(nmeaPARSER));
+
+    if(0 == (parser->buffer = malloc(buff_size)))
+        nmea_error("Insufficient memory!");
+    else
+    {
+        parser->buff_size = buff_size;
+        resv = 1;
+    }    
+
+    return resv;
+}
+
+/**
+ * \brief Destroy parser object
+ */
+void nmea_parser_destroy(nmeaPARSER *parser)
+{
+    NMEA_ASSERT(parser && parser->buffer);
+    free(parser->buffer);
+    nmea_parser_queue_clear(parser);
+    memset(parser, 0, sizeof(nmeaPARSER));
+}
+
+/**
+ * \brief Analysis of buffer and put results to information structure
+ * @return Number of packets wos parsed
+ */
+int nmea_parse(    
+    nmeaPARSER *parser,
+    const char *buff, int buff_sz,
+    nmeaINFO *info
+    )
+{
+    int ptype, nread = 0;
+    void *pack = 0;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    nmea_parser_push(parser, buff, buff_sz);
+
+    while(GPNON != (ptype = nmea_parser_pop(parser, &pack)))
+    {
+        nread++;
+
+        switch(ptype)
+        {
+        case GPGGA:
+            nmea_GPGGA2info((nmeaGPGGA *)pack, info);
+            break;
+        case GPGSA:
+            nmea_GPGSA2info((nmeaGPGSA *)pack, info);
+            break;
+        case GPGSV:
+            nmea_GPGSV2info((nmeaGPGSV *)pack, info);
+            break;
+        case GPRMC:
+            nmea_GPRMC2info((nmeaGPRMC *)pack, info);
+            break;
+        case GPVTG:
+            nmea_GPVTG2info((nmeaGPVTG *)pack, info);
+            break;
+        };
+
+        free(pack);
+    }
+
+    return nread;
+}
+
+/*
+ * low level
+ */
+
+int nmea_parser_real_push(nmeaPARSER *parser, const char *buff, int buff_sz)
+{
+    int nparsed = 0, crc, sen_sz, ptype;
+    nmeaParserNODE *node = 0;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    /* clear unuse buffer (for debug) */
+    /*
+    memset(
+        parser->buffer + parser->buff_use, 0,
+        parser->buff_size - parser->buff_use
+        );
+        */
+
+    /* add */
+    if(parser->buff_use + buff_sz >= parser->buff_size)
+        nmea_parser_buff_clear(parser);
+
+    memcpy(parser->buffer + parser->buff_use, buff, buff_sz);
+    parser->buff_use += buff_sz;
+
+    /* parse */
+    for(;;node = 0)
+    {
+        sen_sz = nmea_find_tail(
+            (const char *)parser->buffer + nparsed,
+            (int)parser->buff_use - nparsed, &crc);
+
+        if(!sen_sz)
+        {
+            if(nparsed)
+                memcpy(
+                parser->buffer,
+                parser->buffer + nparsed,
+                parser->buff_use -= nparsed);
+            break;
+        }
+        else if(crc >= 0)
+        {
+            ptype = nmea_pack_type(
+                (const char *)parser->buffer + nparsed + 1,
+                parser->buff_use - nparsed - 1);
+
+            if(0 == (node = malloc(sizeof(nmeaParserNODE))))
+                goto mem_fail;
+
+            node->pack = 0;
+
+            switch(ptype)
+            {
+            case GPGGA:
+                if(0 == (node->pack = malloc(sizeof(nmeaGPGGA))))
+                    goto mem_fail;
+                node->packType = GPGGA;
+                if(!nmea_parse_GPGGA(
+                    (const char *)parser->buffer + nparsed,
+                    sen_sz, (nmeaGPGGA *)node->pack))
+                {
+                    free(node);
+                    node = 0;
+                }
+                break;
+            case GPGSA:
+                if(0 == (node->pack = malloc(sizeof(nmeaGPGSA))))
+                    goto mem_fail;
+                node->packType = GPGSA;
+                if(!nmea_parse_GPGSA(
+                    (const char *)parser->buffer + nparsed,
+                    sen_sz, (nmeaGPGSA *)node->pack))
+                {
+                    free(node);
+                    node = 0;
+                }
+                break;
+            case GPGSV:
+                if(0 == (node->pack = malloc(sizeof(nmeaGPGSV))))
+                    goto mem_fail;
+                node->packType = GPGSV;
+                if(!nmea_parse_GPGSV(
+                    (const char *)parser->buffer + nparsed,
+                    sen_sz, (nmeaGPGSV *)node->pack))
+                {
+                    free(node);
+                    node = 0;
+                }
+                break;
+            case GPRMC:
+                if(0 == (node->pack = malloc(sizeof(nmeaGPRMC))))
+                    goto mem_fail;
+                node->packType = GPRMC;
+                if(!nmea_parse_GPRMC(
+                    (const char *)parser->buffer + nparsed,
+                    sen_sz, (nmeaGPRMC *)node->pack))
+                {
+                    free(node);
+                    node = 0;
+                }
+                break;
+            case GPVTG:
+                if(0 == (node->pack = malloc(sizeof(nmeaGPVTG))))
+                    goto mem_fail;
+                node->packType = GPVTG;
+                if(!nmea_parse_GPVTG(
+                    (const char *)parser->buffer + nparsed,
+                    sen_sz, (nmeaGPVTG *)node->pack))
+                {
+                    free(node);
+                    node = 0;
+                }
+                break;
+            default:
+                free(node);
+                node = 0;
+                break;
+            };
+
+            if(node)
+            {
+                if(parser->end_node)
+                    ((nmeaParserNODE *)parser->end_node)->next_node = node;
+                parser->end_node = node;
+                if(!parser->top_node)
+                    parser->top_node = node;
+                node->next_node = 0;
+            }
+        }
+
+        nparsed += sen_sz;
+    }
+
+    return nparsed;
+
+mem_fail:
+    if(node)
+        free(node);
+
+    nmea_error("Insufficient memory!");
+
+    return -1;
+}
+
+/**
+ * \brief Analysis of buffer and keep results into parser
+ * @return Number of bytes wos parsed from buffer
+ */
+int nmea_parser_push(nmeaPARSER *parser, const char *buff, int buff_sz)
+{
+    int nparse, nparsed = 0;
+
+    do
+    {
+        if(buff_sz > parser->buff_size)
+            nparse = parser->buff_size;
+        else
+            nparse = buff_sz;
+
+        nparsed += nmea_parser_real_push(
+            parser, buff, nparse);
+
+        buff_sz -= nparse;
+
+    } while(buff_sz);
+
+    return nparsed;
+}
+
+/**
+ * \brief Get type of top packet keeped into parser
+ * @return Type of packet
+ * @see nmeaPACKTYPE
+ */
+int nmea_parser_top(nmeaPARSER *parser)
+{
+    int retval = GPNON;
+    nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    if(node)
+        retval = node->packType;
+
+    return retval;
+}
+
+/**
+ * \brief Withdraw top packet from parser
+ * @return Received packet type
+ * @see nmeaPACKTYPE
+ */
+int nmea_parser_pop(nmeaPARSER *parser, void **pack_ptr)
+{
+    int retval = GPNON;
+    nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    if(node)
+    {
+        *pack_ptr = node->pack;
+        retval = node->packType;
+        parser->top_node = node->next_node;
+        if(!parser->top_node)
+            parser->end_node = 0;
+        free(node);
+    }
+
+    return retval;
+}
+
+/**
+ * \brief Get top packet from parser without withdraw
+ * @return Received packet type
+ * @see nmeaPACKTYPE
+ */
+int nmea_parser_peek(nmeaPARSER *parser, void **pack_ptr)
+{
+    int retval = GPNON;
+    nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    if(node)
+    {
+        *pack_ptr = node->pack;
+        retval = node->packType;
+    }
+
+    return retval;
+}
+
+/**
+ * \brief Delete top packet from parser
+ * @return Deleted packet type
+ * @see nmeaPACKTYPE
+ */
+int nmea_parser_drop(nmeaPARSER *parser)
+{
+    int retval = GPNON;
+    nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;
+
+    NMEA_ASSERT(parser && parser->buffer);
+
+    if(node)
+    {
+        if(node->pack)
+            free(node->pack);
+        retval = node->packType;
+        parser->top_node = node->next_node;
+        if(!parser->top_node)
+            parser->end_node = 0;
+        free(node);
+    }
+
+    return retval;
+}
+
+/**
+ * \brief Clear cache of parser
+ * @return true (1) - success
+ */
+int nmea_parser_buff_clear(nmeaPARSER *parser)
+{
+    NMEA_ASSERT(parser && parser->buffer);
+    parser->buff_use = 0;
+    return 1;
+}
+
+/**
+ * \brief Clear packets queue into parser
+ * @return true (1) - success
+ */
+int nmea_parser_queue_clear(nmeaPARSER *parser)
+{
+    NMEA_ASSERT(parser);
+    while(parser->top_node)
+        nmea_parser_drop(parser);
+    return 1;
+}
+
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: tok.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+/*! \file tok.h */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#define NMEA_TOKS_COMPARE   (1)
+#define NMEA_TOKS_PERCENT   (2)
+#define NMEA_TOKS_WIDTH     (3)
+#define NMEA_TOKS_TYPE      (4)
+
+/**
+ * \brief Calculate control sum of binary buffer
+ */
+int nmea_calc_crc(const char *buff, int buff_sz)
+{
+    int chsum = 0,
+        it;
+
+    for(it = 0; it < buff_sz; ++it)
+        chsum ^= (int)buff[it];
+
+    return chsum;
+}
+
+/**
+ * \brief Convert string to number
+ */
+int nmea_atoi(const char *str, int str_sz, int radix)
+{
+    char *tmp_ptr;
+    char buff[NMEA_CONVSTR_BUF];
+    int res = 0;
+
+    if(str_sz < NMEA_CONVSTR_BUF)
+    {
+        memcpy(&buff[0], str, str_sz);
+        buff[str_sz] = '\0';
+        res = strtol(&buff[0], &tmp_ptr, radix);
+    }
+
+    return res;
+}
+
+/**
+ * \brief Convert string to fraction number
+ */
+double nmea_atof(const char *str, int str_sz)
+{
+    char *tmp_ptr;
+    char buff[NMEA_CONVSTR_BUF];
+    double res = 0;
+
+    if(str_sz < NMEA_CONVSTR_BUF)
+    {
+        memcpy(&buff[0], str, str_sz);
+        buff[str_sz] = '\0';
+        res = strtod(&buff[0], &tmp_ptr);
+    }
+
+    return res;
+}
+
+/**
+ * \brief Formating string (like standart printf) with CRC tail (*CRC)
+ */
+int nmea_printf(char *buff, int buff_sz, const char *format, ...)
+{
+    int retval, add = 0;
+    va_list arg_ptr;
+
+    if(buff_sz <= 0)
+        return 0;
+
+    va_start(arg_ptr, format);
+
+    retval = NMEA_POSIX(vsnprintf)(buff, buff_sz, format, arg_ptr);
+
+    if(retval > 0)
+    {
+        add = NMEA_POSIX(snprintf)(
+            buff + retval, buff_sz - retval, "*%02x\r\n",
+            nmea_calc_crc(buff + 1, retval - 1));
+    }
+
+    retval += add;
+
+    if(retval < 0 || retval > buff_sz)
+    {
+        memset(buff, ' ', buff_sz);
+        retval = buff_sz;
+    }
+
+    va_end(arg_ptr);
+
+    return retval;
+}
+
+/**
+ * \brief Analyse string (specificate for NMEA sentences)
+ */
+int nmea_scanf(const char *buff, int buff_sz, const char *format, ...)
+{
+    const char *beg_tok;
+    const char *end_buf = buff + buff_sz;
+
+    va_list arg_ptr;
+    int tok_type = NMEA_TOKS_COMPARE;
+    int width = 0;
+    const char *beg_fmt = 0;
+    int snum = 0, unum = 0;
+
+    int tok_count = 0;
+    void *parg_target;
+
+    va_start(arg_ptr, format);
+    
+    for(; *format && buff < end_buf; ++format)
+    {
+        switch(tok_type)
+        {
+        case NMEA_TOKS_COMPARE:
+            if('%' == *format)
+                tok_type = NMEA_TOKS_PERCENT;
+            else if(*buff++ != *format)
+                goto fail;
+            break;
+        case NMEA_TOKS_PERCENT:
+            width = 0;
+            beg_fmt = format;
+            tok_type = NMEA_TOKS_WIDTH;
+            /* no break */
+        case NMEA_TOKS_WIDTH:
+            if(isdigit(*format))
+                break;
+            {
+                tok_type = NMEA_TOKS_TYPE;
+                if(format > beg_fmt)
+                    width = nmea_atoi(beg_fmt, (int)(format - beg_fmt), 10);
+            }
+            /* no break */
+        case NMEA_TOKS_TYPE:
+            beg_tok = buff;
+
+            if(!width && ('c' == *format || 'C' == *format) && *buff != format[1])
+                width = 1;
+
+            if(width)
+            {
+                if(buff + width <= end_buf)
+                    buff += width;
+                else
+                    goto fail;
+            }
+            else
+            {
+                if(!format[1] || (0 == (buff = (char *)memchr(buff, format[1], end_buf - buff))))
+                    buff = end_buf;
+            }
+
+            if(buff > end_buf)
+                goto fail;
+
+            tok_type = NMEA_TOKS_COMPARE;
+            tok_count++;
+
+            parg_target = 0; width = (int)(buff - beg_tok);
+
+            switch(*format)
+            {
+            case 'c':
+            case 'C':
+                parg_target = (void *)va_arg(arg_ptr, char *);
+                if(width && 0 != (parg_target))
+                    *((char *)parg_target) = *beg_tok;
+                break;
+            case 's':
+            case 'S':
+                parg_target = (void *)va_arg(arg_ptr, char *);
+                if(width && 0 != (parg_target))
+                {
+                    memcpy(parg_target, beg_tok, width);
+                    ((char *)parg_target)[width] = '\0';
+                }
+                break;
+            case 'f':
+            case 'g':
+            case 'G':
+            case 'e':
+            case 'E':
+                parg_target = (void *)va_arg(arg_ptr, double *);
+                if(width && 0 != (parg_target))
+                    *((double *)parg_target) = nmea_atof(beg_tok, width);
+                break;
+            };
+
+            if(parg_target)
+                break;
+            if(0 == (parg_target = (void *)va_arg(arg_ptr, int *)))
+                break;
+            if(!width)
+                break;
+
+            switch(*format)
+            {
+            case 'd':
+            case 'i':
+                snum = nmea_atoi(beg_tok, width, 10);
+                memcpy(parg_target, &snum, sizeof(int));
+                break;
+            case 'u':
+                unum = nmea_atoi(beg_tok, width, 10);
+                memcpy(parg_target, &unum, sizeof(unsigned int));
+                break;
+            case 'x':
+            case 'X':
+                unum = nmea_atoi(beg_tok, width, 16);
+                memcpy(parg_target, &unum, sizeof(unsigned int));
+                break;
+            case 'o':
+                unum = nmea_atoi(beg_tok, width, 8);
+                memcpy(parg_target, &unum, sizeof(unsigned int));
+                break;
+            default:
+                goto fail;
+            };
+
+            break;
+        };
+    }
+
+fail:
+
+    va_end(arg_ptr);
+
+    return tok_count;
+}
+
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: context.c 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+nmeaPROPERTY * nmea_property()
+{
+    static nmeaPROPERTY prop = {
+        0, 0, NMEA_DEF_PARSEBUFF
+        };
+
+    return &prop;
+}
+
+void nmea_trace(const char *str, ...)
+{
+    int size;
+    va_list arg_list;
+    char buff[NMEA_DEF_PARSEBUFF];
+    nmeaTraceFunc func = nmea_property()->trace_func;
+
+    if(func)
+    {
+        va_start(arg_list, str);
+        size = NMEA_POSIX(vsnprintf)(&buff[0], NMEA_DEF_PARSEBUFF - 1, str, arg_list);
+        va_end(arg_list);
+
+        if(size > 0)
+            (*func)(&buff[0], size);
+    }
+}
+
+void nmea_trace_buff(const char *buff, int buff_size)
+{
+    nmeaTraceFunc func = nmea_property()->trace_func;
+    if(func && buff_size)
+        (*func)(buff, buff_size);
+}
+
+void nmea_error(const char *str, ...)
+{
+    int size;
+    va_list arg_list;
+    char buff[NMEA_DEF_PARSEBUFF];
+    nmeaErrorFunc func = nmea_property()->error_func;
+
+    if(func)
+    {
+        va_start(arg_list, str);
+        size = NMEA_POSIX(vsnprintf)(&buff[0], NMEA_DEF_PARSEBUFF - 1, str, arg_list);
+        va_end(arg_list);
+
+        if(size > 0)
+            (*func)(&buff[0], size);
+    }
+}
diff --git a/src/agent/subagents/gps/nmea.h b/src/agent/subagents/gps/nmea.h
new file mode 100644 (file)
index 0000000..fbf74c6
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: config.h 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_CONFIG_H__
+#define __NMEA_CONFIG_H__
+
+#define NMEA_VERSION        ("0.5.3")
+#define NMEA_VERSION_MAJOR  (0)
+#define NMEA_VERSION_MINOR  (5)
+#define NMEA_VERSION_PATCH  (3)
+
+#define NMEA_CONVSTR_BUF    (256)
+#define NMEA_TIMEPARSE_BUF  (256)
+
+#if defined(WINCE) || defined(UNDER_CE)
+#   define  NMEA_CE
+#endif
+
+#if defined(WIN32) || defined(NMEA_CE)
+#   define  NMEA_WIN
+#else
+#   define  NMEA_UNI
+#endif
+
+#if defined(NMEA_WIN) && (_MSC_VER >= 1400)
+# pragma warning(disable: 4996) /* declared deprecated */
+#endif
+
+#if defined(_MSC_VER)
+# define NMEA_POSIX(x)  _##x
+# define NMEA_INLINE    __inline
+#else
+# define NMEA_POSIX(x)  x
+# define NMEA_INLINE    inline
+#endif
+
+#if !defined(NDEBUG) && !defined(NMEA_CE)
+#   include <assert.h>
+#   define NMEA_ASSERT(x)   assert(x)
+#else
+#   define NMEA_ASSERT(x)
+#endif
+
+#endif /* __NMEA_CONFIG_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: units.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_UNITS_H__
+#define __NMEA_UNITS_H__
+
+/*
+ * Distance units
+ */
+
+#define NMEA_TUD_YARDS      (1.0936)        /**< Yeards, meter * NMEA_TUD_YARDS = yard */
+#define NMEA_TUD_KNOTS      (1.852)         /**< Knots, kilometer / NMEA_TUD_KNOTS = knot */
+#define NMEA_TUD_MILES      (1.609)         /**< Miles, kilometer / NMEA_TUD_MILES = mile */
+
+/*
+ * Speed units
+ */
+
+#define NMEA_TUS_MS         (3.6)           /**< Meters per seconds, (k/h) / NMEA_TUS_MS= (m/s) */
+
+#endif /* __NMEA_UNITS_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: time.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+/*! \file */
+
+#ifndef __NMEA_TIME_H__
+#define __NMEA_TIME_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Date and time data
+ * @see nmea_time_now
+ */
+typedef struct _nmeaTIME
+{
+    int     year;       /**< Years since 1900 */
+    int     mon;        /**< Months since January - [0,11] */
+    int     day;        /**< Day of the month - [1,31] */
+    int     hour;       /**< Hours since midnight - [0,23] */
+    int     min;        /**< Minutes after the hour - [0,59] */
+    int     sec;        /**< Seconds after the minute - [0,59] */
+    int     hsec;       /**< Hundredth part of second - [0,99] */
+
+} nmeaTIME;
+
+/**
+ * \brief Get time now to nmeaTIME structure
+ */
+void nmea_time_now(nmeaTIME *t);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_TIME_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: info.h 10 2007-11-15 14:50:15Z xtimor $
+ *
+ */
+
+/*! \file */
+
+#ifndef __NMEA_INFO_H__
+#define __NMEA_INFO_H__
+
+#define NMEA_SIG_BAD        (0)
+#define NMEA_SIG_LOW        (1)
+#define NMEA_SIG_MID        (2)
+#define NMEA_SIG_HIGH       (3)
+
+#define NMEA_FIX_BAD        (1)
+#define NMEA_FIX_2D         (2)
+#define NMEA_FIX_3D         (3)
+
+#define NMEA_MAXSAT         (12)
+#define NMEA_SATINPACK      (4)
+#define NMEA_NSATPACKS      (NMEA_MAXSAT / NMEA_SATINPACK)
+
+#define NMEA_DEF_LAT        (5001.2621)
+#define NMEA_DEF_LON        (3613.0595)
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Position data in fractional degrees or radians
+ */
+typedef struct _nmeaPOS
+{
+    double lat;         /**< Latitude */
+    double lon;         /**< Longitude */
+
+} nmeaPOS;
+
+/**
+ * Information about satellite
+ * @see nmeaSATINFO
+ * @see nmeaGPGSV
+ */
+typedef struct _nmeaSATELLITE
+{
+    int     id;         /**< Satellite PRN number */
+    int     in_use;     /**< Used in position fix */
+    int     elv;        /**< Elevation in degrees, 90 maximum */
+    int     azimuth;    /**< Azimuth, degrees from true north, 000 to 359 */
+    int     sig;        /**< Signal, 00-99 dB */
+
+} nmeaSATELLITE;
+
+/**
+ * Information about all satellites in view
+ * @see nmeaINFO
+ * @see nmeaGPGSV
+ */
+typedef struct _nmeaSATINFO
+{
+    int     inuse;      /**< Number of satellites in use (not those in view) */
+    int     inview;     /**< Total number of satellites in view */
+    nmeaSATELLITE sat[NMEA_MAXSAT]; /**< Satellites information */
+
+} nmeaSATINFO;
+
+/**
+ * Summary GPS information from all parsed packets,
+ * used also for generating NMEA stream
+ * @see nmea_parse
+ * @see nmea_GPGGA2info,  nmea_...2info
+ */
+typedef struct _nmeaINFO
+{
+    int     smask;      /**< Mask specifying types of packages from which data have been obtained */
+
+    nmeaTIME utc;       /**< UTC of position */
+
+    int     sig;        /**< GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive) */
+    int     fix;        /**< Operating mode, used for navigation (1 = Fix not available; 2 = 2D; 3 = 3D) */
+
+    double  PDOP;       /**< Position Dilution Of Precision */
+    double  HDOP;       /**< Horizontal Dilution Of Precision */
+    double  VDOP;       /**< Vertical Dilution Of Precision */
+
+    double  lat;        /**< Latitude in NDEG - +/-[degree][min].[sec/60] */
+    double  lon;        /**< Longitude in NDEG - +/-[degree][min].[sec/60] */
+    double  elv;        /**< Antenna altitude above/below mean sea level (geoid) in meters */
+    double  speed;      /**< Speed over the ground in kilometers/hour */
+    double  direction;  /**< Track angle in degrees True */
+    double  declination; /**< Magnetic variation degrees (Easterly var. subtracts from true course) */
+
+    nmeaSATINFO satinfo; /**< Satellites information */
+
+} nmeaINFO;
+
+void nmea_zero_INFO(nmeaINFO *info);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_INFO_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: tok.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_TOK_H__
+#define __NMEA_TOK_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+int     nmea_calc_crc(const char *buff, int buff_sz);
+int     nmea_atoi(const char *str, int str_sz, int radix);
+double  nmea_atof(const char *str, int str_sz);
+int     nmea_printf(char *buff, int buff_sz, const char *format, ...);
+int     nmea_scanf(const char *buff, int buff_sz, const char *format, ...);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_TOK_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: sentence.h 17 2008-03-11 11:56:11Z xtimor $
+ *
+ */
+
+/*! \file */
+
+#ifndef __NMEA_SENTENCE_H__
+#define __NMEA_SENTENCE_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * NMEA packets type which parsed and generated by library
+ */
+enum nmeaPACKTYPE
+{
+    GPNON   = 0x0000,   /**< Unknown packet type. */
+    GPGGA   = 0x0001,   /**< GGA - Essential fix data which provide 3D location and accuracy data. */
+    GPGSA   = 0x0002,   /**< GSA - GPS receiver operating mode, SVs used for navigation, and DOP values. */
+    GPGSV   = 0x0004,   /**< GSV - Number of SVs in view, PRN numbers, elevation, azimuth & SNR values. */
+    GPRMC   = 0x0008,   /**< RMC - Recommended Minimum Specific GPS/TRANSIT Data. */
+    GPVTG   = 0x0010    /**< VTG - Actual track made good and speed over ground. */
+};
+
+/**
+ * GGA packet information structure (Global Positioning System Fix Data)
+ */
+typedef struct _nmeaGPGGA
+{
+    nmeaTIME utc;       /**< UTC of position (just time) */
+       double  lat;        /**< Latitude in NDEG - [degree][min].[sec/60] */
+    char    ns;         /**< [N]orth or [S]outh */
+       double  lon;        /**< Longitude in NDEG - [degree][min].[sec/60] */
+    char    ew;         /**< [E]ast or [W]est */
+    int     sig;        /**< GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive) */
+       int     satinuse;   /**< Number of satellites in use (not those in view) */
+    double  HDOP;       /**< Horizontal dilution of precision */
+    double  elv;        /**< Antenna altitude above/below mean sea level (geoid) */
+    char    elv_units;  /**< [M]eters (Antenna height unit) */
+    double  diff;       /**< Geoidal separation (Diff. between WGS-84 earth ellipsoid and mean sea level. '-' = geoid is below WGS-84 ellipsoid) */
+    char    diff_units; /**< [M]eters (Units of geoidal separation) */
+    double  dgps_age;   /**< Time in seconds since last DGPS update */
+    int     dgps_sid;   /**< DGPS station ID number */
+
+} nmeaGPGGA;
+
+/**
+ * GSA packet information structure (Satellite status)
+ */
+typedef struct _nmeaGPGSA
+{
+    char    fix_mode;   /**< Mode (M = Manual, forced to operate in 2D or 3D; A = Automatic, 3D/2D) */
+    int     fix_type;   /**< Type, used for navigation (1 = Fix not available; 2 = 2D; 3 = 3D) */
+    int     sat_prn[NMEA_MAXSAT]; /**< PRNs of satellites used in position fix (null for unused fields) */
+    double  PDOP;       /**< Dilution of precision */
+    double  HDOP;       /**< Horizontal dilution of precision */
+    double  VDOP;       /**< Vertical dilution of precision */
+
+} nmeaGPGSA;
+
+/**
+ * GSV packet information structure (Satellites in view)
+ */
+typedef struct _nmeaGPGSV
+{
+    int     pack_count; /**< Total number of messages of this type in this cycle */
+    int     pack_index; /**< Message number */
+    int     sat_count;  /**< Total number of satellites in view */
+    nmeaSATELLITE sat_data[NMEA_SATINPACK];
+
+} nmeaGPGSV;
+
+/**
+ * RMC packet information structure (Recommended Minimum sentence C)
+ */
+typedef struct _nmeaGPRMC
+{
+    nmeaTIME utc;       /**< UTC of position */
+    char    status;     /**< Status (A = active or V = void) */
+       double  lat;        /**< Latitude in NDEG - [degree][min].[sec/60] */
+    char    ns;         /**< [N]orth or [S]outh */
+       double  lon;        /**< Longitude in NDEG - [degree][min].[sec/60] */
+    char    ew;         /**< [E]ast or [W]est */
+    double  speed;      /**< Speed over the ground in knots */
+    double  direction;  /**< Track angle in degrees True */
+    double  declination; /**< Magnetic variation degrees (Easterly var. subtracts from true course) */
+    char    declin_ew;  /**< [E]ast or [W]est */
+    char    mode;       /**< Mode indicator of fix type (A = autonomous, D = differential, E = estimated, N = not valid, S = simulator) */
+
+} nmeaGPRMC;
+
+/**
+ * VTG packet information structure (Track made good and ground speed)
+ */
+typedef struct _nmeaGPVTG
+{
+    double  dir;        /**< True track made good (degrees) */
+    char    dir_t;      /**< Fixed text 'T' indicates that track made good is relative to true north */
+    double  dec;        /**< Magnetic track made good */
+    char    dec_m;      /**< Fixed text 'M' */
+    double  spn;        /**< Ground speed, knots */
+    char    spn_n;      /**< Fixed text 'N' indicates that speed over ground is in knots */
+    double  spk;        /**< Ground speed, kilometers per hour */
+    char    spk_k;      /**< Fixed text 'K' indicates that speed over ground is in kilometers/hour */
+
+} nmeaGPVTG;
+
+void nmea_zero_GPGGA(nmeaGPGGA *pack);
+void nmea_zero_GPGSA(nmeaGPGSA *pack);
+void nmea_zero_GPGSV(nmeaGPGSV *pack);
+void nmea_zero_GPRMC(nmeaGPRMC *pack);
+void nmea_zero_GPVTG(nmeaGPVTG *pack);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_SENTENCE_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: context.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_CONTEXT_H__
+#define __NMEA_CONTEXT_H__
+
+#define NMEA_DEF_PARSEBUFF  (1024)
+#define NMEA_MIN_PARSEBUFF  (256)
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+typedef void (*nmeaTraceFunc)(const char *str, int str_size);
+typedef void (*nmeaErrorFunc)(const char *str, int str_size);
+
+typedef struct _nmeaPROPERTY
+{
+    nmeaTraceFunc   trace_func;
+    nmeaErrorFunc   error_func;
+    int             parse_buff_size;
+
+} nmeaPROPERTY;
+
+nmeaPROPERTY * nmea_property();
+
+void nmea_trace(const char *str, ...);
+void nmea_trace_buff(const char *buff, int buff_size);
+void nmea_error(const char *str, ...);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_CONTEXT_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: parse.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_PARSE_H__
+#define __NMEA_PARSE_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+int nmea_pack_type(const char *buff, int buff_sz);
+int nmea_find_tail(const char *buff, int buff_sz, int *res_crc);
+
+int nmea_parse_GPGGA(const char *buff, int buff_sz, nmeaGPGGA *pack);
+int nmea_parse_GPGSA(const char *buff, int buff_sz, nmeaGPGSA *pack);
+int nmea_parse_GPGSV(const char *buff, int buff_sz, nmeaGPGSV *pack);
+int nmea_parse_GPRMC(const char *buff, int buff_sz, nmeaGPRMC *pack);
+int nmea_parse_GPVTG(const char *buff, int buff_sz, nmeaGPVTG *pack);
+
+void nmea_GPGGA2info(nmeaGPGGA *pack, nmeaINFO *info);
+void nmea_GPGSA2info(nmeaGPGSA *pack, nmeaINFO *info);
+void nmea_GPGSV2info(nmeaGPGSV *pack, nmeaINFO *info);
+void nmea_GPRMC2info(nmeaGPRMC *pack, nmeaINFO *info);
+void nmea_GPVTG2info(nmeaGPVTG *pack, nmeaINFO *info);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_PARSE_H__ */
+/*
+ *
+ * NMEA library
+ * URL: http://nmea.sourceforge.net
+ * Author: Tim (xtimor@gmail.com)
+ * Licence: http://www.gnu.org/licenses/lgpl.html
+ * $Id: parser.h 4 2007-08-27 13:11:03Z xtimor $
+ *
+ */
+
+#ifndef __NMEA_PARSER_H__
+#define __NMEA_PARSER_H__
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/*
+ * high level
+ */
+
+typedef struct _nmeaPARSER
+{
+    void *top_node;
+    void *end_node;
+    unsigned char *buffer;
+    int buff_size;
+    int buff_use;
+
+} nmeaPARSER;
+
+int     nmea_parser_init(nmeaPARSER *parser);
+void    nmea_parser_destroy(nmeaPARSER *parser);
+
+int     nmea_parse(
+        nmeaPARSER *parser,
+        const char *buff, int buff_sz,
+        nmeaINFO *info
+        );
+
+/*
+ * low level
+ */
+
+int     nmea_parser_push(nmeaPARSER *parser, const char *buff, int buff_sz);
+int     nmea_parser_top(nmeaPARSER *parser);
+int     nmea_parser_pop(nmeaPARSER *parser, void **pack_ptr);
+int     nmea_parser_peek(nmeaPARSER *parser, void **pack_ptr);
+int     nmea_parser_drop(nmeaPARSER *parser);
+int     nmea_parser_buff_clear(nmeaPARSER *parser);
+int     nmea_parser_queue_clear(nmeaPARSER *parser);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __NMEA_PARSER_H__ */
index 3fb6073..739e98d 100644 (file)
@@ -370,9 +370,17 @@ int Serial::read(char *pBuff, int nSize)
        tv.tv_usec = 0;
        nRet = select(m_hPort + 1, &rdfs, NULL, NULL, &tv);
        if (nRet > 0)
-               nRet = ::read(m_hPort, pBuff, nSize);
+       {
+          do
+          {
+             nRet = ::read(m_hPort, pBuff, nSize);
+          }
+          while((nRet == -1) && (errno == EAGAIN));
+       }
        else
+       {
                nRet = -1;  // Timeout is an error
+       }
        
 #endif // _WIN32