Index: /trunk/BNC/RTCM/RTCM2.cpp
===================================================================
--- /trunk/BNC/RTCM/RTCM2.cpp	(revision 708)
+++ /trunk/BNC/RTCM/RTCM2.cpp	(revision 709)
@@ -45,4 +45,6 @@
 //   2008/03/04  AHA  Fixed problems with PRN 32
 //   2008/03/05  AHA  Implemeted fix for Trimble 4000SSI receivers
+//   2008/03/07  AHA  Major revision of input buffer handling 
+//   2008/03/07  AHA  Removed unnecessary failure flag
 //
 // (c) DLR/GSOC
@@ -63,5 +65,5 @@
 // undersized packets in get(Unsigned)Bits
 
-#define DEBUG 0    
+#define DEBUG 0
 
 // Activate (1) or deactivate (0) rounding of measurement epochs to 100ms
@@ -136,10 +138,4 @@
 void ThirtyBitWord::clear() {
   W = 0;
-};
-
-// Failure indicator for input operations
-
-bool ThirtyBitWord::fail() const {
-  return failure; 
 };
 
@@ -259,5 +255,4 @@
 
 
-
 // Append a byte with six data bits
 
@@ -275,5 +270,5 @@
   // Bits 7 and 6 (of 0..7) must be "01" for valid data bytes
   if ( (b & 0x40) != 0x40 ) {
-    failure = true;
+    // We simply skip the invalid input byte and leave the word unchanged
     return;
   };
@@ -290,10 +285,10 @@
 // Get next 30bit word from string
 
-void ThirtyBitWord::get(string& buf) {
+void ThirtyBitWord::get(const string& buf) {
 
   // Check if string is long enough
    
   if (buf.size()<5) {
-    failure = true;
+    // Ignore; users should avoid this case prior to calling get()
     return;
   };
@@ -302,13 +297,11 @@
   
   for (int i=0; i<5; i++) append(buf[i]);
-  buf.erase(0,5);
 
 #if (DEBUG>0) 
   if (!validParity()) {
-    cerr << "Parity error " 
+    cerr << "Parity error in get()" 
          << bitset<32>(all()) << endl;
   };
 #endif
-  failure = false;
 
 };
@@ -328,9 +321,8 @@
 #if (DEBUG>0) 
   if (!validParity()) {
-    cerr << "Parity error " 
+    cerr << "Parity error in get()" 
          << bitset<32>(all()) << endl;
   };
 #endif
-  failure = false;
 
 };
@@ -340,31 +332,28 @@
 void ThirtyBitWord::getHeader(string& buf) {
 
-  unsigned int W_old = W;
+  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()) 
   unsigned int i;
   
   i=0;
-  while (!isHeader() || i<5 ) {
-    // Check if string is long enough; if not restore old word and exit
-    if (buf.size()<i+1) {
-      W = W_old;
-      failure = true;
-      return;
-    };
+  while (!isHeader() && i<buf.size() ) {
     // Process byte
-    append(buf[i]); i++;
-  };
-
-  // Remove processed bytes from buffer
-  
-  buf.erase(0,i);
+    append(buf[i]);
+    // Increment count
+    i++;
+  };
+
+  // Remove processed bytes from buffer. Retain also the previous word to
+  // allow a resync if getHeader() is called repeatedly on the same buffer.
+  if (i>=(1+spare)*wordLen) buf.erase(0,i-(1+spare)*wordLen);
 
 #if (DEBUG>0) 
   if (!validParity()) {
-    cerr << "Parity error " 
+    cerr << "Parity error in getHeader()" 
          << bitset<32>(all()) << endl;
   };
 #endif
-  failure = false;
-
+  
 };
 
@@ -385,9 +374,8 @@
 #if (DEBUG>0) 
   if (!validParity()) {
-    cerr << "Parity error " 
+    cerr << "Parity error in getHeader()" 
          << bitset<32>(all()) << endl;
   };
 #endif
-  failure = false;
 
 };
@@ -443,33 +431,69 @@
 void RTCM2packet::getPacket(std::string& buf) {
 
-  int           n;
-  ThirtyBitWord W_old = W;
-  string        buf_old = buf;
-  
-  // Try to read a full packet. If the input buffer is too short
-  // clear all data and restore the latest 30-bit word prior to 
-  // the getPacket call. The empty header word will indicate
-  // an invalid message, which signals an unsuccessful getPacket()
-  // call.
-   
-  W.getHeader(buf); 
-  H1 = W.value(); 
-  if (W.fail()) { clear(); W=W_old; buf=buf_old; return; };
-  if (!W.validParity()) { clear(); return; };
-  
-  W.get(buf);       
-  H2 = W.value(); 
-  if (W.fail()) { clear(); W=W_old; buf=buf_old; return; };
-  if (!W.validParity()) { clear(); return; };
+  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 used in ThirtyBitWord::getHeader)
+  unsigned int n;
+  
+  // 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().
+  
+  // Locate and read the first header word
+  W.getHeader(buf);
+  if (!W.isHeader()) { 
+    // No header found; try again next time. buf retains only the spare
+    // words. The packet contents is cleared to indicate an unsuccessful
+    // termination of getPacket().
+    clear();
+    return; 
+  };
+  H1 = W.value();
+  
+  // Do we have enough bytes to read the next word? If not, the packet 
+  // contents is cleared to indicate an unsuccessful termination. The
+  // previously read spare and header bytes are retained in the buffer
+  // for use in the next call of getPacket().
+  if (buf.size()<(spare+2)*wordLen) { clear(); return; };
+  
+  // Read the second header word
+  W.get(buf.substr((spare+1)*wordLen,buf.size()-1-(spare+1)*wordLen));  
+  H2 = W.value();
+  if (!W.validParity()) { 
+    // Invalid H2 word; delete first buffer byte and try to resynch next time.
+    // The packet contents is cleared to indicate an unsuccessful termination.
+    clear(); 
+    buf.erase(0,1); 
+    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
+  // previously read spare and header bytes are retained in the buffer
+  // for use in the next call of getPacket().
+  if (buf.size()<(spare+2+n)*wordLen) { clear(); return; };
+  
   DW.resize(n);
-  for (int i=0; i<n; i++) {
-    W.get(buf); 
-    DW[i] = W.value(); 
-    if (W.fail()) { clear(); W=W_old; buf=buf_old; return; };
-    if (!W.validParity()) { clear(); return; };
-  };
-
+  for (unsigned int i=0; i<n; i++) {
+    W.get(buf.substr((spare+2+i)*wordLen,buf.size()-1-(spare+2+i)*wordLen)); 
+    DW[i] = W.value();
+    if (!W.validParity()) { 
+      // Invalid data word; delete first byte and try to resynch next time.
+      // The packet contents is cleared to indicate an unsuccessful termination.
+      clear(); 
+      buf.erase(0,1); 
+      return; 
+    };
+  };
+
+  // Successful packet extraction; delete total number of message bytes 
+  // from buffer. 
+  // Note: a total of "spare" words remain in the buffer to enable a
+  // parity resynchronization when searching the next header.
+  
+  buf.erase(0,(n+2)*wordLen);
+  
   return;
   
@@ -487,9 +511,9 @@
   W.getHeader(inp); 
   H1 = W.value(); 
-  if (W.fail() || !W.validParity()) { clear(); return; }
+  if (inp.fail() || !W.isHeader()) { clear(); return; }
   
   W.get(inp);       
   H2 = W.value(); 
-  if (W.fail() || !W.validParity()) { clear(); return; }
+  if (inp.fail() || !W.validParity()) { clear(); return; }
 
   n = nDataWords();
@@ -498,5 +522,5 @@
     W.get(inp); 
     DW[i] = W.value(); 
-    if (W.fail() || !W.validParity()) { clear(); return; }
+    if (inp.fail() || !W.validParity()) { clear(); return; }
   };
 
Index: /trunk/BNC/RTCM/RTCM2.h
===================================================================
--- /trunk/BNC/RTCM/RTCM2.h	(revision 708)
+++ /trunk/BNC/RTCM/RTCM2.h	(revision 709)
@@ -28,4 +28,5 @@
 //   2006/10/17  OMO  Removed obsolete check of multiple message indicator
 //   2006/11/25  OMO  Revised check for presence of GLONASS data
+//   2008/03/07  AHA  Removed unnecessary failure flag
 //
 // (c) DLR/GSOC
@@ -81,5 +82,5 @@
     // Input
     
-    void         get(std::string& buf);
+    void         get(const std::string& buf);
     void         get(std::istream& inp);
     void         getHeader(std::string& buf);
@@ -94,5 +95,5 @@
   private:
 
-    bool         failure;
+//    bool         failure;
 
     //
Index: /trunk/BNC/bncgetthread.cpp
===================================================================
--- /trunk/BNC/bncgetthread.cpp	(revision 708)
+++ /trunk/BNC/bncgetthread.cpp	(revision 709)
@@ -117,4 +117,15 @@
   _adviseScript = settings.value("adviseScript").toString();
   expandEnvVar(_adviseScript);
+
+  // 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; }
 
   // RINEX writer
@@ -377,4 +388,5 @@
 void bncGetThread::run() {
 
+  const double maxDt = 600.0;  // Check observation epoch
   bool wrongEpoch = false;
   bool decode = true;
@@ -382,8 +394,16 @@
   int secSucc = 0;
   int secFail = 0;
-  int initPause = 30;
+  int initPause = 30;  // Initial pause for corrupted streams
   int currPause = 0;
   bool begCorrupt = false;
   bool endCorrupt = false;
+  int oldSec= 0;
+  int newSec = 0;
+  int numLat = 0;
+  double sumLat = 0.;
+  double minLat = maxDt;
+  double maxLat = -maxDt;
+  double curLat = 0.;
+  double leapsec = 14.;  // Leap second for latency estimation
 
   _decodeTime = QDateTime::currentDateTime();
@@ -539,5 +559,4 @@
           
           const double secPerWeek = 7.0 * 24.0 * 3600.0;
-          const double maxDt      = 600.0;            
 
           if (week < obs->_o.GPSWeek) {
@@ -560,4 +579,32 @@
           else {
             wrongEpoch = false;
+
+            // Latency
+            // -------
+            if (_latIntr>0) {
+              newSec = static_cast<int>(sec);
+              if (newSec != oldSec) {
+                if (newSec % _latIntr < oldSec % _latIntr) {
+                  if (numLat>0) {
+                    emit( newMessage(QString("%1: %2 sec mean latency, min %3, max %4")
+                      .arg(_staID.data())
+                      .arg(int(sumLat/numLat*100)/100.)
+                      .arg(int(minLat*100)/100.)
+                      .arg(int(maxLat*100)/100.)
+                      .toAscii()) );
+                  }
+                  sumLat = 0.;
+                  numLat = 0;
+                  minLat = maxDt;
+                  maxLat = -maxDt;
+                }
+                curLat = sec - obs->_o.GPSWeeks + leapsec;
+                sumLat += curLat;
+                if (curLat < minLat) minLat = curLat;
+                if (curLat >= maxLat) maxLat = curLat;
+                numLat += 1;
+                oldSec = newSec;
+              }
+            }
           }
 
Index: /trunk/BNC/bncgetthread.h
===================================================================
--- /trunk/BNC/bncgetthread.h	(revision 708)
+++ /trunk/BNC/bncgetthread.h	(revision 709)
@@ -88,4 +88,5 @@
    int         _adviseFail;
    int         _adviseReco;
+   int         _latIntr;
    int         _timeOut;
    int         _nextSleep;
Index: /trunk/BNC/bnchelp.html
===================================================================
--- /trunk/BNC/bnchelp.html	(revision 708)
+++ /trunk/BNC/bnchelp.html	(revision 709)
@@ -85,4 +85,5 @@
 &nbsp; &nbsp; &nbsp; 3.8.3. <a href=#advreco>Recovery Threshold</a><br>
 &nbsp; &nbsp; &nbsp; 3.8.4. <a href=#advscript>Advisory Script</a><br>
+&nbsp; &nbsp; &nbsp; 3.8.5. <a href=#meanlate>Mean Latency</a><br>
 3.9. <a href=#mountpoints>Mountpoints</a><br>
 &nbsp; &nbsp; &nbsp; 3.9.1. <a href=#mountadd>Add Mountpoints</a><br>
@@ -438,4 +439,16 @@
 </p> 
 
+<p><a name="meanlate"><h4>3.8.5 Mean Latency - optional </h4></p>
+<p>
+Latency is defined here through the following equation:
+</p>
+<p>
+(a) UTC time provided by BNC's host, minus<br>
+(b) GPS time of currently processed epoch, plus<br>
+(c) Number of leap seconds between UTC and GPS time (hard-coded to 14).
+</p>
+<p>BNC can average all latencies per stream over a certain period. The resulting mean latencies are recorded in the Log file/section at the end of each 'Mean latency' interval. Select a 'Mean latency' interval or select the empty option field if you do not want BNC to log latency information.
+</p>
+
 <p><a name="mountpoints"><h4>3.9. Mountpoints</h4></p>
 <p>
Index: /trunk/BNC/bncmain.cpp
===================================================================
--- /trunk/BNC/bncmain.cpp	(revision 708)
+++ /trunk/BNC/bncmain.cpp	(revision 709)
@@ -84,4 +84,5 @@
     settings.setValue("adviseFail", "15");
     settings.setValue("adviseReco", "5");
+    settings.setValue("latIntr",    "");
   }
 
Index: /trunk/BNC/bncwindow.cpp
===================================================================
--- /trunk/BNC/bncwindow.cpp	(revision 708)
+++ /trunk/BNC/bncwindow.cpp	(revision 709)
@@ -177,4 +177,14 @@
   _logFileLineEdit    = new QLineEdit(settings.value("logFile").toString());
   _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());
+  if (ll != -1) {
+    _latIntrComboBox->setCurrentIndex(ll);
+  }
+
   _mountPointsTable   = new QTableWidget(0,7);
 
@@ -273,4 +283,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>"));
+  _latIntrComboBox->setWhatsThis(tr("<p>BNC can average all latencies per stream over a certain period. The resulting mean latencies are recorded in the Log file/section at the end of each 'Mean latency' interval.</p><p>Select a 'Mean latency' interval or select the empty option field if you do not want BNC to log latency 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."));
@@ -358,6 +369,7 @@
   aLayout->addWidget(new QLabel("Script (full path)"),            3, 0);
   aLayout->addWidget(_adviseScriptLineEdit,                       3, 1);
-  aLayout->addWidget(new QLabel("Network monitoring, handling of corrupted streams."),4,0,1,2,Qt::AlignLeft);
-  aLayout->addWidget(new QLabel("    "),5,0);
+  aLayout->addWidget(new QLabel("Mean latency"),                  4, 0);
+  aLayout->addWidget(_latIntrComboBox,                            4, 1);
+  aLayout->addWidget(new QLabel("Network monitoring, handling of corrupted streams, latency logging."),5,0,1,2,Qt::AlignLeft);
   agroup->setLayout(aLayout);
 
@@ -518,4 +530,5 @@
   settings.setValue("adviseReco",  _adviseRecoSpinBox->value());
   settings.setValue("outFile",     _outFileLineEdit->text());
+  settings.setValue("latIntr",     _latIntrComboBox->currentText());
   settings.setValue("outPort",     _outPortLineEdit->text());
   settings.setValue("outEphPort",  _outEphPortLineEdit->text());
Index: /trunk/BNC/bncwindow.h
===================================================================
--- /trunk/BNC/bncwindow.h	(revision 708)
+++ /trunk/BNC/bncwindow.h	(revision 709)
@@ -106,4 +106,5 @@
     QSpinBox*  _adviseRecoSpinBox;
     QLineEdit* _adviseScriptLineEdit;
+    QComboBox* _latIntrComboBox;
     QTableWidget* _mountPointsTable;
 
