Index: /trunk/BNC/RTCM/RTCM2.cpp
===================================================================
--- /trunk/BNC/RTCM/RTCM2.cpp	(revision 727)
+++ /trunk/BNC/RTCM/RTCM2.cpp	(revision 728)
@@ -49,6 +49,6 @@
 //   2008/03/10  AHA  Corrected extraction of antenna serial number
 //   2008/03/10  AHA  Corrected buffer length check in getPacket()
-//   2008/03/11  AHA  Added checks for data consistency in extraction routines
 //   2008/03/11  AHA  isGPS-flag in RTCM2_Obs is now set to false on clear()
+//   2008/03/13  AHA  Added checks for data consistency in extraction routines
 //
 // (c) DLR/GSOC
@@ -296,8 +296,10 @@
    
   if (buf.size()<5) {
-    // Ignore; users should avoid this case prior to calling get()    
+    // Ignore; users should avoid this case prior to calling get()
+    
 #if ( DEBUG > 0 )    
     cerr << "Error in get(): packet too short (" << buf.size() <<")" << endl;
 #endif
+        
     return;
   };
@@ -341,10 +343,19 @@
 void ThirtyBitWord::getHeader(string& buf) {
 
-  const int wordLen = 5; // Number of bytes representing a 30-bit word
-  const int spare   = 1; // Number of spare words for resync of parity
-                         // (same value as inRTCM2packet::getPacket()) 
+  const unsigned int wordLen = 5; // Number of bytes representing a 30-bit word
+  const unsigned int spare   = 1; // Number of spare words for resync of parity
+                                  // (same value as inRTCM2packet::getPacket()) 
   unsigned int i;
   
   i=0;
+  // append spare word (to get correct parity) and first consecutive word  
+  while (i<(spare+1)*wordLen) {
+    // Process byte
+    append(buf[i]);
+    // Increment count
+    i++;
+  };
+  
+  // start searching for preamble in first word after spare word
   while (!isHeader() && i<buf.size() ) {
     // Process byte
@@ -445,7 +456,13 @@
   unsigned int n;
   
+  // Does the package content at least spare bytes and first header byte?
+  if (buf.size()<(spare+1)*wordLen) { 
+      clear();       
+      return;
+  };
+    
   // Try to read a full packet. Processed bytes are removed from the input 
   // buffer except for the latest spare*wordLen bytes to restore the parity 
-  // bytes upon subseqeunt calls of getPAcket().
+  // bytes upon subseqeunt calls of getPacket().
   
   // Locate and read the first header word
@@ -456,7 +473,9 @@
     // termination of getPacket().
     clear();
+    
 #if ( DEBUG > 0 )
     cerr << "Error in getPacket(): W.isHeader() = false  for H1" << endl;
 #endif
+    
     return; 
   };
@@ -469,7 +488,9 @@
   if (buf.size()<(spare+2)*wordLen) { 
     clear(); 
+    
 #if ( DEBUG > 0 )    
     cerr << "Error in getPacket(): buffer too short for complete H2" << endl;
 #endif
+    
     return;
   };
@@ -483,12 +504,14 @@
     clear(); 
     buf.erase(0,1); 
+    
 #if ( DEBUG > 0 )    
     cerr << "Error in getPacket(): W.validParity() = false for H2" << endl;
 #endif
+        
     return; 
   };
 
   n = nDataWords();
-
+  
   // Do we have enough bytes to read the next word? If not, the packet 
   // contents is cleared to indicate an unsuccessful termination. The
@@ -496,9 +519,11 @@
   // for use in the next call of getPacket().
   if (buf.size()<(spare+2+n)*wordLen) { 
-    clear();     
+    clear(); 
+    
 #if ( DEBUG > 0 )    
     cerr << "Error in getPacket(): buffer too short for complete " << n
          << " DWs" << endl;
 #endif
+    
     return; 
   };
@@ -513,8 +538,10 @@
       clear(); 
       buf.erase(0,1); 
+      
 #if ( DEBUG > 0 )    
     cerr << "Error in getPacket(): W.validParity() = false for DW"
          << i << endl;
 #endif
+        
       return; 
     };
@@ -527,5 +554,5 @@
   
   buf.erase(0,(n+2)*wordLen);
-  
+    
   return;
   
@@ -762,7 +789,11 @@
 void RTCM2_23::extract(const RTCM2packet& P) {
 
-  int  nad, nas;
-  
-  // Check validity and packet type
+  unsigned int       nad, nas;
+  
+  const unsigned int nF1  = 8; // bits in first field (R,AF,SF,NAD)
+  const unsigned int nF2  =16; // bits in second field (SETUP ID,R,NAS)
+  const unsigned int nBits=24; // data bits in  30bit word
+  
+  // Check validity, packet type and number of data words
   
   validMsg = (P.valid()); 
@@ -771,17 +802,46 @@
   validMsg = (P.ID()==23);  
   if (!validMsg) return;
-  
+
+  // Check number of data words (can nad be read in?)
+  
+  validMsg = (P.nDataWords()>=1);  
+  if (!validMsg){
+    cerr << "RTCM2_23::extract: P.nDataWords()>=1" << endl;
+    return;
+  }
+
   // Antenna descriptor 
   antType = "";
   nad = P.getUnsignedBits(3,5);
-  for (int i=0;i<nad;i++) 
-    antType += (char)P.getUnsignedBits(8+i*8,8);
+  
+  // Check number of data words (can antenna description be read in?) 
+  validMsg = ( P.nDataWords() >= 
+               (unsigned int)ceil((nF1+nad*8)/(double)nBits) );
+
+  if (!validMsg) return;
+  
+  for (unsigned int i=0;i<nad;i++) 
+    antType += (char)P.getUnsignedBits(nF1+i*8,8);
 
   // Optional antenna serial numbers
   if (P.getUnsignedBits(2,1)==1) {
+
+    // Check number of data words (can nas be read in?)
+    
+    validMsg = ( P.nDataWords() >=
+                 (unsigned int)ceil((nF1+nad*8+nF2)/(double)nBits) );
+    if (!validMsg) return;
+    
     nas = P.getUnsignedBits(19+8*nad,5);
+
+    // Check number of data words (can antenna serial number be read in?)
+    
+    validMsg = ( P.nDataWords() >=
+                 (unsigned int)ceil((nF1+nad*8+nF2+nas*8)/(double)nBits) );
+    if (!validMsg) return;
+
     antSN = "";
-    for (int i=0;i<nas;i++) 
-      antSN += (char)P.getUnsignedBits(24+8*nad+i*8,8);
+    for (unsigned int i=0;i<nas;i++) 
+      antSN += (char)P.getUnsignedBits(nF1+8*nad+nF2+i*8,8);
   };
 
@@ -962,4 +1022,5 @@
          << P.nDataWords() << ") detected" << endl;
 #endif
+    
     return;
   };
@@ -972,4 +1033,5 @@
          << P.nDataWords() << ") detected" << endl;
 #endif
+    
     return;
   };
@@ -1033,4 +1095,10 @@
     if ( isL1 && !isGPS) availability.set(bit_L1cphGLO);
     if (!isL1 && !isGPS) availability.set(bit_L2cphGLO);
+    
+#if ( DEBUG > 0 )
+    cerr << "RTCM2_Obs::extract(): availability " 
+         << bitset<8>(availability) << endl;  
+#endif
+    
     
     // Process all satellites
@@ -1148,4 +1216,9 @@
     if ( isL1 && !isGPS) availability.set(bit_L1rngGLO);
     if (!isL1 && !isGPS) availability.set(bit_L2rngGLO);
+
+#if ( DEBUG > 0 )
+    cerr << "RTCM2_Obs::extract(): availability " 
+         << bitset<8>(availability) << endl;  
+#endif
 
     // Process all satellites
Index: /trunk/BNC/bncgetthread.cpp
===================================================================
--- /trunk/BNC/bncgetthread.cpp	(revision 727)
+++ /trunk/BNC/bncgetthread.cpp	(revision 728)
@@ -122,12 +122,12 @@
   // Latency interval/average
   // ------------------------
-  _latIntr = 86400;
-  if ( settings.value("latIntr").toString().isEmpty() ) { _latIntr = 0; }
-  if ( settings.value("latIntr").toString().indexOf("1 min") != -1 ) { _latIntr = 60; }
-  if ( settings.value("latIntr").toString().indexOf("5 min") != -1 ) { _latIntr = 300; }
-  if ( settings.value("latIntr").toString().indexOf("15 min") != -1 ) { _latIntr = 900; }
-  if ( settings.value("latIntr").toString().indexOf("1 hour") != -1 ) { _latIntr = 3600; }
-  if ( settings.value("latIntr").toString().indexOf("6 hours") != -1 ) { _latIntr = 21600; }
-  if ( settings.value("latIntr").toString().indexOf("1 day") != -1 ) { _latIntr = 86400; }
+  _perfIntr = 86400;
+  if ( settings.value("perfIntr").toString().isEmpty() ) { _perfIntr = 0; }
+  if ( settings.value("perfIntr").toString().indexOf("1 min") != -1 ) { _perfIntr = 60; }
+  if ( settings.value("perfIntr").toString().indexOf("5 min") != -1 ) { _perfIntr = 300; }
+  if ( settings.value("perfIntr").toString().indexOf("15 min") != -1 ) { _perfIntr = 900; }
+  if ( settings.value("perfIntr").toString().indexOf("1 hour") != -1 ) { _perfIntr = 3600; }
+  if ( settings.value("perfIntr").toString().indexOf("6 hours") != -1 ) { _perfIntr = 21600; }
+  if ( settings.value("perfIntr").toString().indexOf("1 day") != -1 ) { _perfIntr = 86400; }
 
   // RINEX writer
@@ -400,8 +400,12 @@
   bool begCorrupt = false;
   bool endCorrupt = false;
+  bool followSec = false;
   int oldSecGPS= 0;
   int newSecGPS = 0;
+  int numGaps = 0;
+  int diffSecGPS = 0;
   int numLat = 0;
   double sumLat = 0.;
+  double meanDiff = 0.;
   double minLat = maxDt;
   double maxLat = -maxDt;
@@ -582,23 +586,45 @@
             wrongEpoch = false;
 
-            // Latency
-            // -------
-            if (_latIntr>0) {
+            // Latency and completeness
+            // ------------------------
+            if (_perfIntr>0) {
               newSecGPS = static_cast<int>(obs->_o.GPSWeeks);
               if (newSecGPS != oldSecGPS) {
-                if (newSecGPS % _latIntr < oldSecGPS % _latIntr) {
+                if (newSecGPS % _perfIntr < oldSecGPS % _perfIntr) {
                   if (numLat>0) {
-                    emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, %5 epochs")
-                      .arg(_staID.data())
-                      .arg(int(sumLat/numLat*100)/100.)
-                      .arg(int(minLat*100)/100.)
-                      .arg(int(maxLat*100)/100.)
-                      .arg(numLat)
-                      .toAscii()) );
+                    if (meanDiff>0.) {
+                      emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, %5 epochs, %6 gaps")
+                        .arg(_staID.data())
+                        .arg(int(sumLat/numLat*100)/100.)
+                        .arg(int(minLat*100)/100.)
+                        .arg(int(maxLat*100)/100.)
+                        .arg(numLat)
+                        .arg(numGaps)
+                        .toAscii()) );
+                    } else {
+                      emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, %5 epochs")
+                        .arg(_staID.data())
+                        .arg(int(sumLat/numLat*100)/100.)
+                        .arg(int(minLat*100)/100.)
+                        .arg(int(maxLat*100)/100.)
+                        .arg(numLat)
+                        .toAscii()) );
+                    }
                   }
+                  meanDiff = diffSecGPS/numLat;
+                  diffSecGPS = 0;
+                  numGaps = 0;
                   sumLat = 0.;
                   numLat = 0;
                   minLat = maxDt;
                   maxLat = -maxDt;
+                }
+                if (followSec) {
+                  diffSecGPS += newSecGPS - oldSecGPS;
+                  if (meanDiff>0.) {
+                    if (newSecGPS - oldSecGPS > 1.5 * meanDiff) {
+                      numGaps += 1;
+                    }
+                  }
                 }
                 curLat = sec - obs->_o.GPSWeeks + leapsec;
@@ -608,4 +634,5 @@
                 numLat += 1;
                 oldSecGPS = newSecGPS;
+                followSec = true;
               }
             }
Index: /trunk/BNC/bncgetthread.h
===================================================================
--- /trunk/BNC/bncgetthread.h	(revision 727)
+++ /trunk/BNC/bncgetthread.h	(revision 728)
@@ -89,5 +89,5 @@
    int         _adviseFail;
    int         _adviseReco;
-   int         _latIntr;
+   int         _perfIntr;
    int         _timeOut;
    int         _nextSleep;
Index: /trunk/BNC/bnchelp.html
===================================================================
--- /trunk/BNC/bnchelp.html	(revision 727)
+++ /trunk/BNC/bnchelp.html	(revision 728)
@@ -86,5 +86,5 @@
 &nbsp; &nbsp; &nbsp; 3.8.4. <a href=#pause>Pause</a><br>
 &nbsp; &nbsp; &nbsp; 3.8.5. <a href=#advscript>Advisory Script</a><br>
-&nbsp; &nbsp; &nbsp; 3.8.6. <a href=#latelog>Latency Logging</a><br>
+&nbsp; &nbsp; &nbsp; 3.8.6. <a href=#perflog>Performance Logging</a><br>
 3.9. <a href=#mountpoints>Mountpoints</a><br>
 &nbsp; &nbsp; &nbsp; 3.9.1. <a href=#mountadd>Add Mountpoints</a><br>
@@ -448,7 +448,7 @@
 </p> 
 
-<p><a name="latelog"><h4>3.8.6 Latency Logging - optional </h4></p>
-<p>
-Latency is defined here by the following equation:
+<p><a name="perflog"><h4>3.8.6 Performance Logging - optional </h4></p>
+<p>
+<u>Latency:</u> Latency is defined in BNC by the following equation:
 </p>
 <pre>
@@ -459,5 +459,12 @@
   = Latency
 </pre>
-<p>BNC can average the latencies per stream over a certain period of GPS time. Mean latencies are calculated from the individual latencies of at most one (first incoming) observation per second. Mean latencies are recorded in the Log file/section at the end of each 'Latency logging' interval. Select a 'Latency logging' interval or select the empty option field if you do not want BNC to log latency information. Note that computing correct latencies requires the clock of the host computer to be properly synchronized.
+<p>
+ BNC can average the latencies per stream over a certain period of GPS time, the 'Performance logging' interval. Mean latencies are calculated from the individual latencies of at most one (first incoming) observation per second. Note that computing correct latencies requires the clock of the host computer to be properly synchronized.
+</p>
+<p>
+<u>Statistics:</u> BNC counts the number of GPS seconds covered by at least one observation. It also estimates an observation rate from all observations received throughout the first full 'Performance logging' interval. Based on this rate, BNC estimates the number of data gaps when appearing in subsequent intervals. 
+</p>
+<p>
+Latencies and statistical information can be recorded in the Log file/section at the end of each 'Performance logging' interval. Select a 'Performance logging' interval to activate this function or select the empty option field if you do not want BNC to log latencies and statistical information.
 </p>
 
Index: /trunk/BNC/bncmain.cpp
===================================================================
--- /trunk/BNC/bncmain.cpp	(revision 727)
+++ /trunk/BNC/bncmain.cpp	(revision 728)
@@ -85,5 +85,5 @@
     settings.setValue("adviseFail", "15");
     settings.setValue("adviseReco", "5");
-    settings.setValue("latIntr",    "");
+    settings.setValue("perfIntr",   "");
   }
 
Index: /trunk/BNC/bncwindow.cpp
===================================================================
--- /trunk/BNC/bncwindow.cpp	(revision 727)
+++ /trunk/BNC/bncwindow.cpp	(revision 728)
@@ -181,11 +181,11 @@
   _adviseScriptLineEdit    = new QLineEdit(settings.value("adviseScript").toString());
 
-  _latIntrComboBox    = new QComboBox();
-  _latIntrComboBox->setMaximumWidth(9*ww);
-  _latIntrComboBox->setEditable(false);
-  _latIntrComboBox->addItems(QString(",1 min,5 min,15 min,1 hour,6 hours,1 day").split(","));
-  int ll = _latIntrComboBox->findText(settings.value("latIntr").toString());
+  _perfIntrComboBox    = new QComboBox();
+  _perfIntrComboBox->setMaximumWidth(9*ww);
+  _perfIntrComboBox->setEditable(false);
+  _perfIntrComboBox->addItems(QString(",1 min,5 min,15 min,1 hour,6 hours,1 day").split(","));
+  int ll = _perfIntrComboBox->findText(settings.value("perfIntr").toString());
   if (ll != -1) {
-    _latIntrComboBox->setCurrentIndex(ll);
+    _perfIntrComboBox->setCurrentIndex(ll);
   }
 
@@ -287,5 +287,5 @@
   _logFileLineEdit->setWhatsThis(tr("Records of BNC's activities are shown in the Log section on the bottom of this window. They can be saved into a file when a valid path is specified in the 'Logfile (full path)' field."));
   _adviseScriptLineEdit->setWhatsThis(tr("<p>Specify the full path to a script or batch file to handle advisory notes generated in the event of corrupted streams or stream outages. The affected mountpoint and one of the comments 'Begin_Outage', 'End_Outage', 'Begin_Corrupted', or 'End_Corrupted' are passed on to the script as command line parameters.</p><p>The script can be configured to send an email to BNC's operator and/or to the affected stream provider. An empty option field (default) or invalid path means that you don't want to use this option.</p><p> Note that for using this function you need to specify the 'Observation rate'.</p>"));
-  _latIntrComboBox->setWhatsThis(tr("<p>BNC can average all latencies per stream over a certain period of GPS time. The resulting mean latencies are recorded in the Log file/section at the end of each 'Latency logging' interval.</p><p>Select a 'Latency logging' interval or select the empty option field if you do not want BNC to log latency information.</p>"));
+  _perfIntrComboBox->setWhatsThis(tr("<p>BNC can average all latencies per stream over a certain period of GPS time. The resulting mean latencies are recorded in the Log file/section at the end of each 'Performance logging' interval together with results of a statistical evaluation (number of covered epochs, data gaps).</p><p>Select a 'Performance logging' interval or select the empty option field if you do not want BNC to log latencies and statistical information.</p>"));
   _mountPointsTable->setWhatsThis(tr("<p>Streams selected for retrieval are listed in the 'Mountpoints' section. Clicking on 'Add Mountpoints' button will open a window that allows the user to select data streams from an NTRIP broadcaster according to their mountpoints. To remove a stream from the 'Mountpoints' list, highlight it by clicking on it and hit the 'Delete Mountpoints' button. You can also remove multiple mountpoints by highlighting them using +Shift and +Ctrl.</p><p>BNC automatically allocates one of its internal decoders to a stream based on the stream's 'format' and 'format-details' as given in the sourcetable. However, there might be cases where you need to override the automatic selection due to incorrect sourcetable for example. BNC allows users to manually select the required decoder by editing the decoder string. Double click on the 'decoder' field, enter your preferred decoder and then hit Enter. The accepted decoder strings are 'RTCM_2.x', 'RTCM_3.x', and 'RTIGS'.</p><p>In case you need to log the raw data as is, BNC allows users to by-pass its decoders and and directly save the input in daily log files. To do this specify the decoder string as 'ZERO'.</p><p>BNC can also retrieve streams from virtual reference stations (VRS). To initiate these streams, an approximate rover position needs to be sent in NMEA GGA message to the NTRIP broadcaster. In return, a user-specific data stream is generated, typically by a Network-RTK software. This stream is customized to the exact latitude and longitude as shown in the 'lat' and 'long' columns under 'Mountpoints'. These VRS streams are indicated by a 'yes' in the 'nmea' column under 'Mountpoints' as well as in the sourcetable. The default 'lat' and 'long' values are taken from the sourcetable. However, in most cases you would probably want to change this according to your requirement. Double click on 'lat' and 'long' fields, enter the values you wish to send and then hit Enter. The format is in positive north latitude degrees (e.g. for northern hemisphere: 52.436, for southern hemisphere: -24.567) and eastern longitude degrees (e.g.: 358.872 or -1.128). Only mountpoints with a 'yes' in its 'nmea' column can be edited. The position should preferably be a point within the coverage of the network.</p>"));
   _log->setWhatsThis(tr("Records of BNC's activities are shown in the Log section. The message log covers the communication status between BNC and the NTRIP broadcaster as well as any problems that occur in the communication link, stream availability, stream delay, stream conversion etc."));
@@ -378,7 +378,7 @@
   aLayout->addWidget(new QLabel("Script (full path)"),            3, 0);
   aLayout->addWidget(_adviseScriptLineEdit,                       3, 1,1,3);
-  aLayout->addWidget(new QLabel("Latency logging"),               4, 0);
-  aLayout->addWidget(_latIntrComboBox,                            4, 1);
-  aLayout->addWidget(new QLabel("Network monitoring, outages, handling of corrupted streams, mean latency."),5,0,1,4,Qt::AlignLeft);
+  aLayout->addWidget(new QLabel("Performance logging"),           4, 0);
+  aLayout->addWidget(_perfIntrComboBox,                           4, 1);
+  aLayout->addWidget(new QLabel("Network monitoring, outages, handling of corrupted streams, latencies, statistics."),5,0,1,4,Qt::AlignLeft);
   agroup->setLayout(aLayout);
 
@@ -540,5 +540,5 @@
   settings.setValue("makePause",   _makePauseCheckBox->checkState());
   settings.setValue("outFile",     _outFileLineEdit->text());
-  settings.setValue("latIntr",     _latIntrComboBox->currentText());
+  settings.setValue("perfIntr",    _perfIntrComboBox->currentText());
   settings.setValue("outPort",     _outPortLineEdit->text());
   settings.setValue("outEphPort",  _outEphPortLineEdit->text());
Index: /trunk/BNC/bncwindow.h
===================================================================
--- /trunk/BNC/bncwindow.h	(revision 727)
+++ /trunk/BNC/bncwindow.h	(revision 728)
@@ -107,5 +107,5 @@
     QSpinBox*  _adviseRecoSpinBox;
     QLineEdit* _adviseScriptLineEdit;
-    QComboBox* _latIntrComboBox;
+    QComboBox* _perfIntrComboBox;
     QTableWidget* _mountPointsTable;
 
