Index: /trunk/BNC/RTCM/GPSDecoder.h
===================================================================
--- /trunk/BNC/RTCM/GPSDecoder.h	(revision 35)
+++ /trunk/BNC/RTCM/GPSDecoder.h	(revision 35)
@@ -0,0 +1,143 @@
+// -*- C++ -*-
+//
+// $Id: GPSDecoder.h,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+// 2005/04/11: include 'int iPCode' into class 'GPSDecoder' (BKG)
+#if !defined(__GPSDecoder_h__)
+#define __GPSDecoder_h__
+
+#include <list>
+#include <iostream>
+
+using namespace std;
+
+
+#include "format.h"
+
+//
+// One Code/Phase - Measurement
+//
+struct Observation {
+    Observation()
+    :SVPRN(0)
+    ,GPSWeek(0)
+    ,GPSWeeks(0)
+    ,sec(0.) 
+    ,C1(0.)
+    ,P2(0.)
+    ,L1(0.)
+    ,L2(0.)
+    ,SNR1(0)
+    ,SNR2(0) 
+    ,pCodeIndicator(0)
+    ,cumuLossOfCont(0)
+    {StatID[0] = '\0';}
+
+    Observation( char _statID
+	        ,char _svprn
+	        ,short _GPSWeek
+	        ,int _GPSWeeks
+		,double _sec
+	        ,double _C1
+		,double _P2
+		,double _L1
+		,double _L2
+		,short _SNR1
+		,short _SNR2
+		,int _pCodeIndicator
+		,u_int _cumuLossOfCont)
+    :SVPRN(_svprn)
+    ,GPSWeek(_GPSWeek)
+    ,GPSWeeks(_GPSWeeks)
+    ,sec(_sec)
+    ,C1(_C1)
+    ,P2(_P2)
+    ,L1(_L1)
+    ,L2(_L2)
+    ,SNR1(_SNR1)
+    ,SNR2(_SNR2) 
+    ,pCodeIndicator(_pCodeIndicator)
+    ,cumuLossOfCont(_cumuLossOfCont)
+    {StatID[0] = _statID; StatID[1] = '\0';}
+
+    char StatID[5+1];  //< Station ID
+    char SVPRN;    //<  Satellite PRN
+    short GPSWeek; //< Week of GPS-Time
+    int GPSWeeks; //< Second of Week (GPS-Time>
+    double sec;
+    double C1;     //<  CA-code validated raw pseudorange (meters)
+    double P2;     //<  P2-code validated raw pseudorange (meters)
+    double L1;     //<  validated raw carrier phase (meters)
+    double L2;     //<  validated raw carrier phase (meters)
+    short SNR1;    //<  signal-to noise ration (0.1 dB)
+    short SNR2;    //<  signal-to noise ration (0.1 dB)
+    int   pCodeIndicator;  // 0 ... CA Code, 1 ... P Code
+    u_int cumuLossOfCont;  // 0 to 31
+
+    // Operator to write ascii formatted members of Observation to an outstream
+    friend ostream& operator<<(ostream& os, const Observation & o) {
+	os <<format("%3d%3d",(int) o.StatID,(int) o.SVPRN)
+	   <<format(" %4d %10d",(int)o.GPSWeek, o.GPSWeeks)
+	   <<format("\t%15.3f\t%15.3f\t%15.3f\t%15.3f\t%15.3f\t%15.3f"
+		    ,o.C1, o.P2, o.L1, o.L2, 0.1*o.SNR1, 0.1*o.SNR2)
+	   <<endl;
+	return os;
+    }
+} ;
+
+//
+// GPS Orbitinformation
+//
+struct Ephemeris {
+    short   svprn;    //< Satellite PRN
+    short   wn;       //< GPS - week number
+    short   aodc;     //< Age of data issue Clock
+    short   aode;     //< Age of data issue Orbit
+    double  tow;      //< Seconds of GPS week
+    double  toc;      //< Reference time, Clock (sec)
+    double  toe;      //< Ref.time for Orbit:(sec)
+    double  tgd;      //< Group delay (sec)
+    double  af2;      //< Clock parameter: (sec/sec^2)
+    double  af1;      //< Clock parameter: (sec/sec)
+    double  af0;      //< Clock parameter: (sec)
+    double  crs;      //< Sin-harmonic correction term, orbit radius:(meters)
+    double  deltan;   //< Mean anomaly correction:(semi-cirl/sec)
+    double  m0;       //< Mean anomaly @ ref.time:(semi-circle)
+    double  cuc;      //< Cos-harmonic correction term, argument of Latitude:(radians)
+    double  e;        //< Eccentricity
+    double  cus;      //< Sin-harmonic correction term, argument of Latitude:(radians)
+    double  roota;    //< Square root of semi-major axis:(m ^1/2)
+    double  cic;      //< Cos-harmonic correction term, angle of inclination:(radians)
+    double  omega0;   //< Lon. of Asc. node at weekly epoch: (semi-circle)
+    double  cis;      //< Sin-harmonic correction term, angle of Inclination:(radians)
+    double  i0;       //< Inclination angle at Ref.time: (semi-circle)
+    double  crc;      //< Cos-harmonic correction term, orbit radius:(meters)
+    double  omega;    //< Argument of Perigee:(semi-circle)
+    double  omegadot; //< Rate of right ascension: (semi-circle/sec)
+    double  idot;     //< Rate of inclination angle: (semi-circle/sec)
+    short   svaccu;   //< SV accuracy (0-15)
+    short   fit;      //< Curve fit interval (0-1)
+    short   cReserved1; //< Not used - yet
+    short   health;     //< 0 if healthy, else unhealthy
+    short   cReserved2; //< Not used - yet
+    short   SVEnable;   //< Not used - yet
+} ;
+
+const double   lambda1 = 0.1902936727984; // [m]
+const double   lambda2 = 0.2442102134241; // [m]
+const unsigned glonass_svid = 45;
+
+class GPSDecoder {
+ public:
+  int iPCode;  // pointer for CA or P code on L1
+
+  virtual void Decode(char* _ptrBuffer=NULL, int _nBufLen=0) = 0;
+  virtual ~GPSDecoder() {}
+  typedef list<Observation*> ObsList_t;
+  typedef list<Ephemeris*>   EphList_t;
+
+  ObsList_t m_lObsList;
+  EphList_t m_lEphList;
+
+} ;
+
+#endif
Index: /trunk/BNC/RTCM/RTCM.cpp
===================================================================
--- /trunk/BNC/RTCM/RTCM.cpp	(revision 35)
+++ /trunk/BNC/RTCM/RTCM.cpp	(revision 35)
@@ -0,0 +1,906 @@
+// -*- C++ -*-
+// RTCM.C
+// $Id: RTCM.cpp,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+// 2005/04/11: counter 'iPCode' for indicating CA or P Code on L1 (BKG)
+
+#include "RTCM.h"
+
+#include <cmath>
+
+#if !defined(__GNUC__)
+double rint(double val) {
+  return ((val < 0.0) ? -floor(-val+0.5) : floor(val+0.5));
+}
+#endif
+
+/* Org-ID: rtcm.c,v 1.11 1999/08/28 00:06:20 wolfgang Exp   */
+
+/* rtcm.c - decode RTCM SC-104 data from a DGPS beacon receiver */
+
+/*
+    written by John Sager john.sager@btinternet.com
+    Copyright (C) 1999  John C Sager
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+/*  History:
+
+    v0.3	22nd February 1999
+	Incorrect scale factor on range error field fixed.
+	dx,dy,dz fields in Datum message are signed.
+
+    v0.2	9th February 1999
+	Sundry improvements to the code by Wolfgang Rupprecht so
+	he could incorporate it in his dgpsip client.
+
+	Addition of decode capability for message types 4 and 5.
+
+    v0.1	4th February 1999
+	First version - reads RTCM data from standard input & sends
+	decoded output to standard output. Implements decoding of
+	types 1,3,6,7,9,16. This is original software and not
+	a modification of any other program.
+*/
+
+/* TODO
+    Add facility to decode from tty port input & write to a file
+*/
+
+/*
+ * The data comes across as bytes where the top 2 bits indicate the type
+ * of data and the bottom 6 bits are the data itself. Bytes 01xxxxxx
+ * are received RTCM data. Other bytes are other kinds of data/commands
+ * etc. The data framing can be at any bit position. Also the data is
+ * lsb first, so it needs to be bit-reversed.
+ *
+ * This program calculates parity over a 32-bit field starting at each
+ * bit position until a parity match is found. Then parity is checked
+ * at successive 30-bit boundaries until sufficient error-free words
+ * have been found. This gives word sync. Then frame sync is acquired
+ * by searching for the preamble and checking at subsequent frame boundaries
+ * until sufficient frames have been found. Then data is decoded.
+ *
+ * Parity errors are flagged in frames so that data before the parity
+ * error can be decoded, if possible. A parity error in the preamble
+ * word or the next word causes frame sync to be lost. Sufficient
+ * successive parity errors causes sync to be lost totally.
+ */
+
+#if 0
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+#include <sys/types.h>
+
+#define DEBUG
+
+
+/* state machine state */
+
+#define	NO_SYNC		0
+#define WORD_SYNCING	1
+#define WORD_SYNC	2
+#define FRAME_SYNCING	3
+#define FULL_SYNC	4
+
+#define W_SYNC_TEST	6
+#define F_SYNC_TEST	3
+#define P_FAIL_TEST	10
+
+/* message types */
+
+#define MSG_FULLCOR	1
+#define MSG_REFPARM	3
+#define MSG_DATUM	4
+#define MSG_CONHLTH	5
+#define MSG_NULL	6
+#define MSG_BEACALM	7
+#define MSG_SUBSCOR	9
+#define MSG_SPECIAL	16
+
+/* field scale factors */
+
+#define	ZCOUNT_SCALE	0.6		/* sec */
+#define	RANGE_SMALL	0.02		/* metres */
+#define	RANGE_LARGE	0.32		/* metres */
+#define	RANGERATE_SMALL	0.002		/* metres/sec */
+#define	RANGERATE_LARGE	0.032		/* metres/sec */
+#define XYZ_SCALE	0.01		/* metres */
+#define DXYZ_SCALE	0.1		/* metres */
+#define	LA_SCALE	90.0/32767.0	/* degrees */
+#define	LO_SCALE	180.0/32767.0	/* degrees */
+#define	FREQ_SCALE	0.1		/* kHz */
+#define	FREQ_OFFSET	190.0		/* kHz */
+#define CNR_OFFSET	24		/* dB */
+#define TU_SCALE	5		/* minutes */
+
+char * RTCM::state_name[] = {
+ "NO_SYNC",
+ "WORD_SYNCING",
+ "WORD_SYNC",
+ "FRAME_SYNCING",
+ "FULL_SYNC"};
+
+u_int RTCM::tx_speed[] = { 25, 50, 100, 110, 150, 200, 250, 300 };
+
+/* parity stuff */
+
+#define P_30_MASK	0x40000000
+#define P_31_MASK	0x80000000
+
+#define	PARITY_25	0xbb1f3480
+#define	PARITY_26	0x5d8f9a40
+#define	PARITY_27	0xaec7cd00
+#define	PARITY_28	0x5763e680
+#define	PARITY_29	0x6bb1f340
+#define	PARITY_30	0x8b7a89c0
+
+#define W_DATA_MASK	0x3fffffc0
+
+u_char RTCM::parity_of[] = {
+0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
+0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
+1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};
+
+u_char RTCM::reverse_bits[] = {
+0,32,16,48,8,40,24,56,4,36,20,52,12,44,28,60,
+2,34,18,50,10,42,26,58,6,38,22,54,14,46,30,62,
+1,33,17,49,9,41,25,57,5,37,21,53,13,45,29,61,
+3,35,19,51,11,43,27,59,7,39,23,55,15,47,31,63};
+
+
+#define DATA_SHIFT	6
+#define B_DATA_MASK	0x3f
+#define FILL_BASE	24
+
+#define PREAMBLE	0x19800000
+#define PREAMBLE_MASK	0x3fc00000
+
+int RTCM::preamble() {
+  return ((data_word & PREAMBLE_MASK) == PREAMBLE);
+}
+
+/* state_change - change state & print a useful message */
+
+void RTCM::state_change(u_int s) {
+#if 0
+  printf("M\tstate change: %s -> %s\n",
+	state_name[rtcm_state], state_name[s]);
+  fflush(stdout);
+#endif
+  rtcm_state = s;
+}
+
+/* check the parity on this_word. bits 31,30 are parity on previous word.
+ * bits 29-6 are data bits. Bits 5-0 are parity bits.
+ */
+
+u_int RTCM::parity_ok() {
+  u_int t, th, p;
+
+  th = this_word;
+  if (th & P_30_MASK)
+    th ^= W_DATA_MASK;
+
+  t = th & PARITY_25;
+  p = parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)];
+  t = th & PARITY_26;
+  p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
+  t = th & PARITY_27;
+  p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
+  t = th & PARITY_28;
+  p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
+  t = th & PARITY_29;
+  p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
+  t = th & PARITY_30;
+  p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
+      parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
+
+
+  if (!this_word || ((this_word &0x3f) != p)) {
+    if (rtcm_state > WORD_SYNCING)
+      pf_count++;
+    return 0;
+  }
+  
+  return th;
+}
+
+/* find word sync by a successful parity check */
+
+int RTCM::find_sync(u_char b) {
+  int i;
+
+  b <<= 2;
+  i = 1;
+
+  while (i <= DATA_SHIFT) {
+    this_word <<= 1;
+    this_word |= (b & 0x80) ? 1 : 0; /* next bit into 32 bits */
+    b <<= 1;
+    if ((data_word = parity_ok())) {
+      sync_bit = (i==DATA_SHIFT) ? 0 : i;
+      fill_shift = FILL_BASE - DATA_SHIFT + i;
+      next_bits = b >> 2;
+      return 1;
+    }
+    i++;
+  }
+  return 0;
+}
+
+void RTCM::next_word() {
+  this_word = (this_word << 30) | (next_bits << 24);
+}
+
+/* fill bits into this_word and indicate when filled. Put the residue bits in
+   next_bits.
+ */
+
+int RTCM::filled_word(u_char b) {
+
+  if (fill_shift <= 0) { /* can complete fill */
+    if (fill_shift) {
+      next_bits = b << (DATA_SHIFT + fill_shift);
+      this_word |= b >> (-fill_shift);
+    }
+    else {
+      next_bits = 0;
+      this_word |= b;
+    }
+    fill_shift += FILL_BASE;
+    word_count++;
+    return 1;
+  }
+  this_word |= b << fill_shift;
+  fill_shift -= DATA_SHIFT;
+  return 0;
+}
+
+
+//
+//
+//
+void RTCM::msgRTKUncorrectedCarrierPhases() {
+
+  //  const static char *freqIndicatorS[] = { "L1" , "?1" , "L2" , "?3" };
+  const u_int freqIndicator   = (message[2]>>28)&0x3;
+
+  const u_int gnssTimeofMeasurement = (message[2]>>6)&0xfffff;
+  const double expandedTimeOfMeasurement = z_count*ZCOUNT_SCALE + gnssTimeofMeasurement * .000001;
+
+  int m;
+  for(m= 3 ; m < msg_len+2 ; m+=2 ) {
+    const int multipleMessageIndicator = (message[m] >> 29) & 0x1;
+    const int pCodeIndicator  = (message[m] >> 28) & 0x1;
+    const int glonassIndicator = (message[m] >> 27) & 0x1;
+
+#if 0
+    const u_int svprn    = (message[m] >> 22) & 0x1f 
+      + (glonassIndicator ? glonass_svid : 0);
+#else
+    const u_int svprn    = (message[m] >> 22) & 0x1f;
+#endif
+    if(!glonassIndicator) {
+      dumpOnNewTimeTag(expandedTimeOfMeasurement);
+
+      const u_int dataQuality    = (message[m] >> 18) & 0x7;
+      const u_int cumuLossOfCont = (message[m] >> 14) & 0x1f;
+
+      obsMap[svprn].set_cumuLossOfCont(cumuLossOfCont);
+      obsMap[svprn].set_pCodeIndicator(pCodeIndicator);
+
+      const int   carrierPhase   = (int)(  (((message[m]  >>  6) & 0xff) << 24)
+	  			         | ((message[m+1]>>  6) & 0xffffff));
+      switch(freqIndicator) {
+      case 0: /* L1 */
+        // TBD unroll L1
+        obsMap[svprn].set_l1(carrierPhase);
+        if(pCodeIndicator > 0) {
+          iPCode++;
+        }
+        break;
+      case 2: /* L2 */
+        // TBD unroll L2
+        obsMap[svprn].set_l2(carrierPhase);
+        break;
+      default:
+        /* unknown frequency */;
+      }
+    }
+
+#if 0
+    printf( "RTKPhase: %s prn:%2d %s %s %f %f %f %11.3f\n"
+	   , glonassIndicator ? "GLONASS" : "GPS    "
+	   , svprn
+	   , freqIndicatorS[freqIndicator]
+	   , pCodeIndicator ? "P-Code " : "CA-Code" 
+	   , expandedTimeOfMeasurement
+	   , r.timeTag
+	   , r.clockError
+	   , carrierPhase/256.
+             );
+#endif
+  }
+}
+
+
+//
+//
+//
+void RTCM::msgRTKUncorrectedPseudoranges() {
+  // const static char *freqIndicatorS[] = { "L1" , "?1" , "L2" , "?3" };
+  const u_int freqIndicator   = (message[2]>>28)&0x3;
+  const u_int smoothingIntervall = (message[2]>>26)&0x3;
+  const u_int gnssTimeofMeasurement = (message[2]>>6)&0xfffff;
+  const double expandedTimeOfMeasurement = z_count*ZCOUNT_SCALE + gnssTimeofMeasurement * .000001;
+
+  int m;
+  for(m= 3 ; m < msg_len+2 ; m+=2 ) {
+    const int multipleMessageIndicator = (message[m] >> 29) & 0x1;
+    const int pCodeIndicator  = (message[m] >> 28) & 0x1;
+    const int glonassIndicator = (message[m] >> 27) & 0x1;
+    if(!glonassIndicator) {
+      dumpOnNewTimeTag(expandedTimeOfMeasurement);
+      const u_int svprn    = (message[m] >> 22) & 0x1f 
+                             + (glonassIndicator ? glonass_svid : 0) ;
+      const u_int dataQual   = (message[m] >> 18) & 0xf;
+      const u_int multiPathError = (message[m] >> 14) &0xf;
+      const u_int pseudoRange    = (  (((message[m]  >>  6) & 0xff) << 24)
+			           | ((message[m+1]>>  6) & 0xffffff));
+      switch(freqIndicator) {
+      case 0: /* L1 */
+        obsMap[svprn].set_C1(pseudoRange*0.02);
+        if(pCodeIndicator > 0) {
+          iPCode++;
+        }
+        break;
+      case 2: /* L2 */
+        obsMap[svprn].set_P2(pseudoRange*0.02);
+        break;
+      default:
+        /* unknown frequency */;
+      }
+    }
+
+#if 0
+    printf( "RTKRange: %s prn:%2d %s %s %f %f %f %11.3f\n"
+	   , glonassIndicator ? "GLONASS" : "GPS    "
+	   , svprn
+	   , freqIndicatorS[freqIndicator]
+	   , pCodeIndicator ? "P-Code " : "CA-Code" 
+	   , expandedTimeOfMeasurement
+	   , r.timeTag
+	   , r.clockError
+	   , pseudoRange*0.02
+	    );
+#endif
+  }
+}
+
+
+/* printcor - print differential corrections - full or subset */
+
+void RTCM::printcor() {
+  int i, w;
+  u_int m, n;
+  int scale, udre, sat, range, rangerate, iod;
+
+  i = 0;
+  w = 2;
+  m = 0;
+  if (pfptr) {
+    msg_len = (pfptr-message) - 2;
+    n = msg_len % 5;
+    if (n == 1 || n == 3) msg_len--;
+    if (msg_len < 2)
+      return;
+  }
+  while (w < msg_len+2) {
+    if ((i & 0x3) == 0){
+      m = message[w++] & W_DATA_MASK;
+      scale = m >> 29 & 0x1;
+      udre = (m >>27) & 0x3;
+      sat = (m >>22) & 0x1f;
+      range = (m >>6) & 0xffff;
+      if (range > 32767) range -= 65536;
+      m = message[w++] & W_DATA_MASK;
+      rangerate = (m >>22) & 0xff;
+      if (rangerate > 127) rangerate -= 256;
+      iod = (m >>14) & 0xff;
+      i++;
+    }
+    else if ((i & 0x3) == 1){
+      scale = m >> 13 & 0x1;
+      udre = (m >>11) & 0x3;
+      sat = (m >>6) & 0x1f;
+      m = message[w++] & W_DATA_MASK;
+      range = (m >>14) & 0xffff;
+      if (range > 32767) range -= 65536;
+      rangerate = (m >>6) & 0xff;
+      if (rangerate > 127) rangerate -= 256;
+      m = message[w++] & W_DATA_MASK;
+      iod = (m >>22) & 0xff;
+      i++;
+    }
+    else {
+      scale = m >> 21 & 0x1;
+      udre = (m >>19) & 0x3;
+      sat = (m >>14) & 0x1f;
+      range = (m <<2) & 0xff00;
+      m = message[w++] & W_DATA_MASK;
+      range |= (m >>22) & 0xff;
+      if (range > 32767) range -= 65536;
+      rangerate = (m >>14) & 0xff;
+      if (rangerate > 127) rangerate -= 256;
+      iod = (m >>6) & 0xff;
+      i+= 2;
+    }
+#if 0
+    printf("S\t%d\t%d\t%d\t%.1f\t%.3f\t%.3f\n", sat, udre, iod,
+	z_count*ZCOUNT_SCALE, range*(scale?RANGE_LARGE:RANGE_SMALL),
+	rangerate*(scale?RANGERATE_LARGE:RANGERATE_SMALL));
+#endif
+  }
+}
+
+/* printref - print reference position. The transmitted xyz quantities are
+   integers scaled in units of 0.01m
+*/
+
+void RTCM::printref(void) {
+  int x, y, z;
+
+  if (pfptr)
+    return;
+  x = ((message[2] & W_DATA_MASK) << 2) | ((message[3] & W_DATA_MASK) >> 22);
+  y = ((message[3] & W_DATA_MASK) << 10) | ((message[4] & W_DATA_MASK) >> 14);
+  z = ((message[4] & W_DATA_MASK) << 18) | ((message[5] & W_DATA_MASK) >> 6);
+
+  onPosition(x*XYZ_SCALE, y*XYZ_SCALE, z*XYZ_SCALE);
+ 
+#if 0
+  printf("R\t%.2f\t%.2f\t%.2f\n", x*XYZ_SCALE, y*XYZ_SCALE, z*XYZ_SCALE);
+#endif
+}
+
+/* printba - print beacon almanac
+*/
+
+void RTCM::printba() {
+  int la, lo, range, freq, hlth, id, bitrate;
+
+  if (pfptr)
+    return;
+  la = ((message[2] >> 14) & 0xffff);
+  if (la > 32767) la -= 65536;
+  lo = ((message[2] << 2) & 0xff00) | ((message[3] >> 22) & 0xff);
+  if (lo > 32767) lo -= 65536;
+  range = ((message[3] >> 12) & 0x3ff);
+  freq = ((message[3]) & 0xfc0) | ((message[4] >> 24) & 0x3f);
+  hlth = ((message[4] >> 22) & 0x3);
+  id = ((message[4] >> 12) & 0x3ff);
+  bitrate = ((message[4] >> 9) & 0x7);
+
+#if 0
+  printf("A\t%.4f\t%.4f\t%d\t%.1f\t%d\t%d\t%d\n", (la*LA_SCALE), (lo*LO_SCALE),
+	range, (freq*FREQ_SCALE+FREQ_OFFSET), hlth, id, tx_speed[bitrate]);
+#endif
+}
+
+/* printspec - print text of special message
+*/
+
+void RTCM::printspec() {
+  u_int i, d, c;
+#if 0
+  if (pfptr)
+    msg_len = (pfptr-message)>>2;
+  printf("T\t");
+  for (i=0; i< msg_len; i++) {
+    d = message[i+2] & W_DATA_MASK;
+    if ((c=d>>22)) putchar(c); else break;
+    if ((c=((d>>14) & 0xff))) putchar(c); else break;
+    if ((c=((d>>6) & 0xff))) putchar(c); else break;
+  }
+  printf("\n");
+#endif
+}
+
+/* printnull - print a marker for a null message
+*/
+
+void RTCM::printnull() {
+#if 0
+    printf("N\n");
+#endif
+}
+
+/* printdatum - print datum message
+*/
+
+void RTCM::printdatum() {
+  char dname[6];
+  char *dn;
+  u_int d, dgnss, dat;
+  int dx, dy, dz;
+
+  if (pfptr)
+    return;
+  d = message[2] & W_DATA_MASK;
+  dgnss = d>>27;
+  dat = (d>>26) & 1;
+  dname[0] = (d>>14) & 0xff;
+  if (dname[0]) { /* not null */
+    dname[1] = (d>>6) & 0xff;
+    d = message[3] & W_DATA_MASK;
+    dname[2] = (d>>22) & 0xff;
+    dname[3] = (d>>14) & 0xff;
+    dname[4] = (d>>6) & 0xff;
+    dname[5] = '\0';
+    dn = dname;
+  }
+  else
+    dn = "NUL";
+#if 0
+  printf("D\t%s\t%1d\t%s", (dgnss==0)?"GPS":((dgnss==1)?"GLONASS":"???"),
+	dat, dn);
+#endif
+  if (msg_len > 2) {
+    d = message[4] & W_DATA_MASK;
+    dx = (d>>14) & 0xffff;
+    if (dx > 32767) dx -= 65536;
+    dy = (d<<2) & 0xff00;
+    d = message[5] & W_DATA_MASK;
+    dy |= (d>>22) & 0xff;
+    if (dy > 32767) dy -= 65536;
+    dz = (d>>6) & 0xffff;
+    if (dz > 32767) dz -= 65536;
+#if 0
+    printf("\t%.1f\t%.1f\t%.1f", dx*DXYZ_SCALE, dy*DXYZ_SCALE, dz*DXYZ_SCALE);
+#endif
+  }
+#if 0
+  printf("\n");
+#endif
+}
+
+/* printconh - print constellation health message
+*/
+
+void RTCM::printconh() {
+  u_int i, d, sat, iodl, hlth, cnr, he, nd, lw, tu;
+
+  if (pfptr) {
+    msg_len = (pfptr-message) - 2;
+    if (!msg_len)
+      return;
+  }
+  for (i=0; i< msg_len; i++) {
+    d = message[i+2] & W_DATA_MASK;
+    sat = (d>>24) & 0x1f;
+    if (!sat) sat = 32;
+    iodl = (d>>23) & 1;
+    hlth = (d>>20) & 0x7;
+    cnr = (d>>15) & 0x1f;
+    he = (d>>14) & 1;
+    nd = (d>>13) & 1;
+    lw = (d>>12) & 1;
+    tu = (d>>8) & 0x0f;
+#if 0
+    printf("C\t%2d\t%1d  %1d\t%2d\t%1d  %1d  %1d\t%2d\n",
+	sat, iodl, hlth, (cnr?(cnr+CNR_OFFSET):-1), he, nd, lw, tu*TU_SCALE);
+#endif
+  } 
+}
+
+/* new_frame - called when a new frame is complete */
+
+void RTCM::new_frame() {
+  char s[8];
+
+  frame_count++;
+  if (pfptr == message) /* dud frame */
+    return;
+
+  msg_type = (message[0]>>16)&0x3f;
+  station_id = (message[0]>>6)&0x3ff;
+  z_count = (message[1]>>17)&0x1fff;
+  health = (message[1]>>6)&0x7;
+#if 0
+  if (pfptr)
+    sprintf(s, "\tT\t%d", (pfptr-message)-2);
+
+  printf("H\t%d\t%d\t%.1f\t%d\t%d\t%d%s\n", msg_type, station_id,
+		(z_count*ZCOUNT_SCALE), seqno, msg_len, health,
+		(pfptr)?s:"");
+#endif
+  switch (msg_type) {
+
+    case MSG_FULLCOR:
+    case MSG_SUBSCOR:
+      printcor();
+      break;
+
+    case MSG_REFPARM:
+      printref();
+      break;
+
+    case MSG_DATUM:
+      printdatum();
+      break;
+
+    case MSG_CONHLTH:
+      printconh();
+      break;
+
+    case MSG_NULL:
+      printnull();
+      break;
+
+    case MSG_BEACALM:
+      printba();
+      break;
+
+    case MSG_SPECIAL:
+      printspec();
+      break;
+
+    case 18: /* RTK Uncorrected Carrier Phases */
+      msgRTKUncorrectedCarrierPhases();
+      break;
+
+    case 19: /* RTK Uncorrected Pseudoranges */
+      msgRTKUncorrectedPseudoranges();
+      break;
+
+    default:
+#if 0
+      printf("?\t%d\n", msg_type);
+#endif
+      break;
+  }
+}
+
+void RTCM::buffer(u_int w) {
+  *fillptr++ = w;
+}
+
+void RTCM::frame_start() {
+  frame_fill = -1;
+  fillptr = message;
+  pfptr = NULL;
+  buffer(data_word);
+}
+
+void RTCM::find_start() {
+  if ((data_word = parity_ok()))
+    p_fail = 0;
+  else if (++p_fail >= P_FAIL_TEST) { /* too many consecutive parity fails */
+    state_change(NO_SYNC);
+    return;
+  }
+  next_word();
+  if (preamble()) {
+    seqno = -1; /* resync at next word */
+    frame_start();
+    state_change(FRAME_SYNCING);
+  }
+}
+
+void RTCM::fill_frame() {
+  int seq;
+
+  if ((data_word = parity_ok()))
+    p_fail=0;
+  else if (++p_fail >= P_FAIL_TEST) {
+    state_change(NO_SYNC);
+    return;
+  }
+  next_word();
+  frame_fill++; /* another word */
+  if (frame_fill == 0) { /* this is second header word */
+    if (!data_word) { /* parity fail - bad news! */
+      state_change(WORD_SYNC); /* lost frame sync */
+      frame_sync = F_SYNC_TEST-1; /* resync rapidly */
+      return;
+    }
+    buffer(data_word);
+    seq = (data_word>>14)&0x7;
+    msg_len = (data_word>>9)&0x1f;
+#if 0
+#ifdef DEBUG
+    if (debug)
+	fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
+		(u_int)fillptr,
+	data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
+#endif
+#endif
+    if ((seqno < 0) || (((seqno +1) & 0x7) == seq))
+      seqno = seq; /* resync */
+    else { /* sequence error */
+      state_change(WORD_SYNC); /* to be on the safe side */
+#if 0
+fprintf(stderr,"2\n");
+#endif
+      return;
+    }
+  }
+  else if (frame_fill > msg_len) { /* should be next preamble */
+#if 0
+#ifdef DEBUG
+    if (debug)
+	fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
+		(u_int)fillptr,
+	data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
+#endif
+#endif
+    if (rtcm_state == FRAME_SYNCING) { /* be very tough */
+      if (!(data_word && preamble())) {
+	state_change(WORD_SYNC); /* start again */
+      }
+      else if (++frame_sync >= F_SYNC_TEST) {
+/*	frame_count = 0; */
+	state_change(FULL_SYNC);
+	new_frame(); /* output the last frame acquired before we start a new one */
+      }
+      frame_start(); /* new frame here */
+    }
+    else {
+      frame_start(); /* new frame here */
+      if (!data_word) /* parity error on preamble - keep sync but lose message */
+	pfptr = message; /* indicates dud message */
+      else if (!preamble()) { /* good word but no preamble! */
+	state_change(WORD_SYNC); /* can't carry on */
+      }
+    }
+  }
+  else { /* other message words */
+#if 0
+#ifdef DEBUG
+    if (debug)
+	fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
+		(u_int)fillptr,
+	data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
+#endif
+#endif
+    if (!data_word && !pfptr)
+      pfptr = fillptr; /* mark the (first) error */
+    buffer(data_word);
+    if ((frame_fill == msg_len) && (rtcm_state == FULL_SYNC)) /* frame completed */
+      new_frame();
+  }
+}
+
+void RTCM::status_byte(u_char b) {
+#if 0
+#if defined(PRINTSTATUS)
+    printf("-\tstatus\t-\t%d 0x%x\n", b, b);
+#endif
+#endif
+}
+
+
+/* take a data byte and process it. This will change state according to
+ * the data, place parity-checked data in a buffer and call a function to
+ * process completed frames.
+ */
+
+void RTCM::data_byte(u_char b) {
+
+  b = reverse_bits[b&0x3f];
+
+  if (rtcm_state == NO_SYNC) {
+    if(find_sync(b)) {
+      state_change(WORD_SYNCING);
+#if 0
+      printf("M\tsync_bit: %d\n", sync_bit);
+#endif
+      word_sync = 1;
+      next_word();
+    }
+  }
+  else if (filled_word(b)) {
+    switch (rtcm_state) {
+
+    case WORD_SYNCING:
+      data_word = parity_ok();
+      next_word();
+      if (data_word) {
+        if (++word_sync >= W_SYNC_TEST) {
+	  state_change(WORD_SYNC);
+	  p_fail = 0;
+	  frame_sync = 1;
+	  if (preamble()) { /* just in case we hit one immediately */
+	    frame_start();
+	    state_change(FRAME_SYNCING);
+	  }
+	}
+      }
+      else {
+     	if (--word_sync <= 0) {
+	  state_change(NO_SYNC);
+	  return;
+	}
+      }
+      break;
+
+    case WORD_SYNC: /* look for frame start */
+      find_start();
+      break;
+
+    case FRAME_SYNCING:
+      fill_frame();
+      break;
+
+    case FULL_SYNC:
+      fill_frame();
+      break;
+    }
+  }
+}
+
+
+void RTCM::new_byte(u_char b) {
+
+  switch (b >> DATA_SHIFT) {
+
+    case 0:
+    case 2:
+#if 0
+#ifdef DEBUG
+	fprintf(stderr, "unknown byte type %d (%d 0x%0x)\n", b >> DATA_SHIFT,
+		b, b);
+#endif
+#endif
+      return;
+
+    case 3: /* status */
+      status_byte(b);
+      return;
+
+    case 1: /* data */
+      data_byte(b);
+      return;
+  }
+}
+
+#if 0
+main()
+{
+  int b;
+
+  initialise();
+  while ((b=getchar()) != EOF)
+    new_byte(b);
+
+  printf("M\tword count: %d\tparity failures: %d\tframe count: %d\n",
+	  word_count, pf_count, frame_count);
+}
+#endif
+
+  
Index: /trunk/BNC/RTCM/RTCM.h
===================================================================
--- /trunk/BNC/RTCM/RTCM.h	(revision 35)
+++ /trunk/BNC/RTCM/RTCM.h	(revision 35)
@@ -0,0 +1,294 @@
+// -*- C++ -*-
+//
+// $Id: RTCM.h,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+
+#if defined(__GNUC__)
+using namespace std;
+#endif
+
+#include "GPSDecoder.h"
+#include "m_date.h"
+#include <cmath>
+#include <map>
+#include <iostream>
+#include <sys/types.h>
+
+class RTCM : public GPSDecoder {
+  public:
+    RTCM(char _StationID,bool fullRange)
+    :GPSDecoder()
+    ,StationID(_StationID)
+    ,lowerBound(fullRange ? -715827883 :  715827882  )
+    ,upperBound(fullRange ?  715827882 : 1431655764  )
+    ,rolloverIntervall(fullRange ?       4294967296. /* pow(2,32) */ 
+		                 :       2147483648. /* pow(2,31) */)
+    ,debug(0)
+    ,this_word(0)
+    ,data_word(0)
+    ,next_bits(0)
+    ,rtcm_state(/* NO_SYNC */ 0)
+    ,fail_count(0)
+    ,p_fail(0)
+    ,preamble_flag(0)
+    ,sync_bit(0)
+    ,fill_shift(0)
+    ,word_sync(0)
+    ,frame_sync(0)
+    ,frame_fill(0)
+    ,word_count(0)
+    ,frame_count(0)
+    ,pf_count(0)
+    ,msg_type(0)
+    ,station_id(0)
+    ,z_count(0)
+    ,seqno(0)
+    ,msg_len(0)
+    ,health(0)
+    ,fillptr(message)
+    ,pfptr(NULL) {}
+
+    // defined as required by baseclass
+    void Decode(char* _ptrBuffer=NULL, int _nBufLen=0) {
+      while(_nBufLen--) {
+	new_byte((u_char)*_ptrBuffer);
+	_ptrBuffer++;
+      }
+    }
+
+  protected:
+    // Station ID used in m_lObsList
+    char StationID;
+
+    typedef u_int SVPRN;
+
+    struct Rollover {
+      Rollover():lastValue(0),rollOver(0) {}
+      int lastValue;
+      int rollOver;
+    } ;
+
+    struct RolloverChannel {
+      Rollover l1;
+      Rollover l2;
+    } ;
+
+    typedef map<SVPRN,RolloverChannel> RolloverMap;
+    RolloverMap rolloverMap;
+
+    // Collector for Measurement
+    struct ObsSort {
+      ObsSort():C1(0.),P2(0.),l1(0),l2(0),assigned(0) {}
+      void set_C1(double _C1) { assigned|=1; C1= _C1; }
+      void set_P2(double _P2) { assigned|=2; P2= _P2; }
+      void set_l1(int _l1) { assigned|=4; l1= _l1; }
+      void set_l2(int _l2) { assigned|=8; l2= _l2; }
+      void set_pCodeIndicator(int _pCodeIndicator){ assigned|=16; pCodeIndicator = _pCodeIndicator;}
+      void set_cumuLossOfCont(u_char _cumuLossOfCont){ assigned|=32; cumuLossOfCont = _cumuLossOfCont;}
+      bool allAssigned() const { return assigned == 63; }
+      double C1; // m
+      double P2; // m
+      int l1; // [1/256 Cycles mod int/half int ]
+      int l2; // [1/256 Cycles mod int/half int ]
+      int   pCodeIndicator;
+      u_int cumuLossOfCont;
+      u_int assigned;
+    } ;
+
+    typedef map<SVPRN,ObsSort> ObsMap;
+    ObsMap obsMap;
+
+    // GPStime mod 3600 of current epoch
+    // GLONASS measurements/epochs are ignored
+    double obsMapTimeTagMod3600;   
+
+    // Complete mod 3600s time with the help of system time
+    // to timetag. Assume that the diffence is below 20min.
+    static long completeMod3600BySysTime(int mod3600time) {
+      const long sysTimeTag= Date::timeNow().tag_get();
+      const long offset = ((sysTimeTag%3600) < 1200) && (mod3600time > 2400) 
+                           ? -1
+	                   : ((sysTimeTag%3600) > 2400) && (mod3600time < 1200)
+                             ? 1
+	                     : 0;
+      return ((sysTimeTag/3600)+offset)*3600 + mod3600time;
+    }
+
+
+    //
+    //
+    //
+    virtual void endOfEpoch(Date d) {}
+
+    //
+    //
+    // called if reference position arises in datastream
+    virtual void onPosition(double x,double y,double z) {}
+ 
+    // Rollover managment
+    int lowerBound;
+    int upperBound;
+    double rolloverIntervall;
+
+    int rollOverCondition(int oldv,int newv) {
+      return (oldv < lowerBound && newv > upperBound )
+	     ? -1
+	     : (newv < lowerBound && oldv > upperBound )
+	       ? 1
+	       : 0;
+    }
+
+    //
+    //
+    //
+    void updateRollovers() {
+      ObsMap::iterator i;
+      for(i= obsMap.begin();i != obsMap.end();i++) {
+	RolloverMap::iterator rm(rolloverMap.find(i->first));
+	if(rm != rolloverMap.end()) {
+	  rm->second.l1.rollOver +=
+	    rollOverCondition( rm->second.l1.lastValue
+		  	      ,i->second.l1 );
+	  rm->second.l2.rollOver +=
+	    rollOverCondition( rm->second.l2.lastValue
+			      ,i->second.l2 );
+	} else {
+	  rolloverMap[i->first].l1.rollOver= 0;
+	  rolloverMap[i->first].l2.rollOver= 0;
+	}
+	rolloverMap[i->first].l1.lastValue= i->second.l1;
+	rolloverMap[i->first].l2.lastValue= i->second.l2;
+      }
+    }
+
+    //
+    //
+    //
+  virtual void debugOut( SVPRN svprn,long timetag,double mod3600,long mod3600int
+			  ,double C1,int l1,int roll_l1,double unrolled_l1
+                          ,int l2,int roll_l2,double unrolled_l2) {}
+
+    //
+    //
+    //
+    void dumpObsMap() {
+      const int obsMapTimeTagMod3600int=int(rint(obsMapTimeTagMod3600));
+      const long timeTag = completeMod3600BySysTime(obsMapTimeTagMod3600int);
+      Date d;
+      d.tag_set(timeTag);
+
+      updateRollovers();
+
+      endOfEpoch(d);
+
+      ObsMap::iterator i;
+      for(i= obsMap.begin();i != obsMap.end();i++) {
+	if(!i->second.allAssigned() ) {
+          // cerr << "incomplete measurements" << endl;
+	} else {
+	  Observation  *op = new Observation;
+
+	  op->StatID[0] = StationID;
+	  op->StatID[1] = '\0';
+	  op->SVPRN= i->first;
+	  op->GPSWeek=  timeTag / Date::SECONDS_PER_WEEK;
+	  op->GPSWeeks= timeTag % Date::SECONDS_PER_WEEK;
+          op->sec = obsMapTimeTagMod3600;
+	  op->C1= i->second.C1;
+	  op->P2= i->second.P2;
+          op->pCodeIndicator = i->second.pCodeIndicator;
+          op->cumuLossOfCont = i->second.cumuLossOfCont;
+	  const double l1_offset(  rolloverMap[i->first].l1.rollOver 
+				 * rolloverIntervall                 );
+	  const double unrolled_l1 = i->second.l1 + l1_offset;
+	  op->L1=(unrolled_l1 / -256.) * lambda1;
+
+	  const double l2_offset(  rolloverMap[i->first].l2.rollOver 
+				 * rolloverIntervall                 );
+	  const double unrolled_l2 = i->second.l2 + l2_offset;
+	  op->L2=(unrolled_l2 / -256.) * lambda2;
+	  
+	  op->SNR1= 0;
+	  op->SNR2= 0;
+          m_lObsList.push_back(op);
+	  debugOut( i->first,timeTag,obsMapTimeTagMod3600,obsMapTimeTagMod3600int
+                   ,op->C1
+		   ,i->second.l1,rolloverMap[i->first].l1.rollOver,unrolled_l1
+		    ,i->second.l2,rolloverMap[i->first].l2.rollOver,unrolled_l2);
+	}
+      }
+      obsMap.clear();
+    }
+
+    // SC-104 V2.3 4-42 Note 1 4.
+    // Recover TimeTag ( modulo 3600s ) GPS/GLONASS Time and ClockError
+    // from expandedTimeOfMeasurement
+    // Assume receiver measurements at hard edges at a grid of 10ms 
+    // and clock error as less 5 ms (1.1ms max recommended).
+    // 
+    struct RecoveredTimeTagAndClockError {
+      static double fastestUpdatePeriod() { return 0.01; }
+      static double shift() { return fastestUpdatePeriod() / 2.; }
+      RecoveredTimeTagAndClockError(double expandedTimeOfMeasurement /* [s] */)
+      :clockError(fmod(expandedTimeOfMeasurement+shift(),fastestUpdatePeriod()) - shift())
+      ,timeTagMod3600(expandedTimeOfMeasurement - clockError) {}
+      double clockError; /* [s] */
+      double timeTagMod3600;    /* [s] */
+    } ;
+
+  virtual void debugTime(double timeTagMod3600,double clockError) {}
+
+    void dumpOnNewTimeTag(double expandedTimeOfMeasurement) {
+      const RecoveredTimeTagAndClockError r(expandedTimeOfMeasurement);
+      if(r.timeTagMod3600 != obsMapTimeTagMod3600) {
+        debugTime(r.timeTagMod3600,r.clockError);
+	if(obsMap.size()) dumpObsMap();
+	obsMapTimeTagMod3600= r.timeTagMod3600;
+      }
+    }
+
+    int preamble();
+    void state_change(u_int s);
+    u_int parity_ok();
+    int find_sync(u_char b);
+    void next_word();
+    int filled_word(u_char b);
+
+    void msgRTKUncorrectedCarrierPhases();
+    void msgRTKUncorrectedPseudoranges();
+
+    void printcor();
+    void printref();
+    void printba();
+    void printspec();
+    void printnull();
+    void printdatum();
+    void printconh();
+
+    void new_frame();
+    void buffer(u_int w);
+    void frame_start();
+    void find_start();
+    void fill_frame();
+    void status_byte(u_char b);
+    void data_byte(u_char b);
+    void new_byte(u_char b);
+
+    int	debug;
+
+    u_int this_word, data_word;
+    u_char next_bits;
+
+    int rtcm_state, fail_count, p_fail, preamble_flag;
+    int sync_bit, fill_shift, word_sync, frame_sync, frame_fill;
+    int word_count, frame_count, pf_count;
+    int msg_type, station_id, z_count, seqno, msg_len, health;
+
+    u_int message[40];	        /* message buffer */
+    u_int *fillptr;		/* pointer to fill message */
+    u_int *pfptr;		/* pointer to first parity error */
+
+    static char *state_name[];
+    static u_int tx_speed[];
+    static u_char parity_of[];
+    static u_char reverse_bits[];
+} ;
Index: /trunk/BNC/RTCM/format.h
===================================================================
--- /trunk/BNC/RTCM/format.h	(revision 35)
+++ /trunk/BNC/RTCM/format.h	(revision 35)
@@ -0,0 +1,57 @@
+// -*- C++ -*-
+//
+// $Id
+
+#include<cmath>
+
+#if !defined(__format_h__)
+#define __format_h__
+
+#if !defined(u_char)
+#define u_char unsigned char
+#endif
+
+#if !defined(u_int)
+#define u_int unsigned int
+#endif
+
+#if !defined(__GNUC__)
+double rint(double val);
+#endif
+
+#include <cstdio>
+#include <cstdarg>
+#include <string>
+
+using namespace std;
+
+//
+// string format(const char * form , ... );
+//
+// Purpose:
+//   To generate fprintf-like formatted strings
+//
+#if defined(__GNUC__)
+static inline
+string format(const char * form , ... ) __attribute__ ((format (printf, 1, 2)));
+#endif
+
+static inline
+string format(const char * form , ... ) {
+  const int myBUFF_SIZE = 1024;
+  char most2long1s2short[myBUFF_SIZE];
+  va_list ap;
+  va_start(ap,form);
+#if defined(__GNUC__)
+  // have vsnprintf
+  if(vsnprintf(most2long1s2short,myBUFF_SIZE,form,ap) == -1)
+    most2long1s2short[myBUFF_SIZE] =0x00;
+  return string(most2long1s2short);
+#else
+  most2long1s2short[myBUFF_SIZE] =0x00;
+  vsprintf(most2long1s2short,form,ap);
+  return string(most2long1s2short);
+#endif
+}
+
+#endif
Index: /trunk/BNC/RTCM/m_data.h
===================================================================
--- /trunk/BNC/RTCM/m_data.h	(revision 35)
+++ /trunk/BNC/RTCM/m_data.h	(revision 35)
@@ -0,0 +1,139 @@
+// -*- C++ -*-
+//
+// $Id: m_data.h,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+//
+
+#if !defined(__m_data_h__)
+// protects the header from been included more than once
+#define __m_data_h__
+
+#include <time.h>
+#include <ctype.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <typeinfo>
+
+#include <cstdarg>
+#include <iomanip>
+
+
+#include <iostream>
+#include <fstream>
+
+#if defined(__GNUC_MINOR__) && ( __GNUC_MINOR__ < 91 )
+#else
+using namespace std;
+#endif
+
+template<class T>
+void WriteNative(ostream &os,const T & data) {
+#if 1
+  os.write(reinterpret_cast<const char *>(&data),sizeof(data));
+#else
+  int i;
+  os << typeid(data).name() << " " << sizeof(data) << " ";
+  for(i= 0;i<sizeof(data);i++) {
+    int byte  (  ((unsigned char *)(&data))[i] );
+    os << hex << " " << byte;
+  }
+  os << endl;
+#endif
+}
+
+template<class T1,class T2>
+void WriteNative(ostream & os,const map<T1,T2> &m) {
+  WriteNative(os,m.size());
+  typedef typename map<T1,T2>::iterator I;
+  I i;
+  for(i=m.begin();i<m.end();i++) {
+    WriteNative(os,*i);
+  }
+}
+
+
+// C_Data<T> Wrapper for Scalar types T
+
+template<class T>
+class C_Data {
+public:
+  typedef T DataType;
+
+  explicit C_Data(DataType _data= 0):data(_data) {}
+  C_Data(const C_Data & org):data(org.get()) {}
+  C_Data &operator= ( const C_Data &newValue) {
+    set(newValue.get());
+    return *this;
+  }
+  // bool operator == (T other) { return get() == other.get(); }
+  // bool operator != (T other) { return get() != other.get(); }
+
+  ~C_Data() {}
+
+  void writeNative(ostream &os) const {
+    WriteNative(os,data);
+  }
+  
+  void set(const DataType &newValue) {
+    data = newValue;
+  }
+
+  const DataType &get() const {
+    return data;
+  }
+
+protected:
+  DataType data;
+} ;
+
+template <class T>
+bool operator<(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return d1.get() < d2.get();
+}
+
+template <class T>
+bool operator>(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return d1.get() > d2.get();
+}
+
+template <class T>
+bool operator==(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return d1.get() == d2.get();
+}
+
+template <class T>
+bool operator!=(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return d1.get() != d2.get();
+}
+
+template <class T>
+bool operator<=(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return     ( d1.get() == d2.get() ) 
+	      || ( d1.get() < d2.get()  ) ;
+}
+ 
+template <class T>
+C_Data<T> operator-(const C_Data<T> &d1,const C_Data<T> &d2) {
+  return C_Data<T>(d1.get() - d2.get());
+}
+
+template <class T>
+void WriteNative(ostream &os,const C_Data<T> c_data) {
+  c_data.writeNative(os);
+}
+
+
+
+
+template <class T2,class T3>
+void divmod(const long numer,const long denom,T2 &quot,T3 &rem) {
+  ldiv_t res;
+  ldiv(numer,denom);
+  quot = res.quot;
+  rem  = res.rem;
+}
+
+
+#endif
Index: /trunk/BNC/RTCM/m_date.cpp
===================================================================
--- /trunk/BNC/RTCM/m_date.cpp	(revision 35)
+++ /trunk/BNC/RTCM/m_date.cpp	(revision 35)
@@ -0,0 +1,234 @@
+// -*- C++ -*-
+//
+// $Id: m_date.cpp,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+//
+// Comment: c.f. m_date.h
+
+
+#include "m_date.h"
+
+// for memset
+#include <string.h>
+
+//
+// Initialize
+//
+StructDate::StructDate() {
+  memset(&tms,0,sizeof(tms));
+}
+
+//
+// construct from Date-Instance
+// 
+void StructDate::set(const Date &date) {
+  // is not 100% thread save ...
+  tms = *gmtime(&date.data);
+}
+
+//
+// calculate a date-Instance
+//
+Date StructDate::date() const {
+  Date d;
+  // breaks constness: tms can be normalized by mktime
+#if !defined(__CYGWIN32__)
+  d.data = mktime((BaseType *)&tms) - timezone; 
+#else
+  d.data = mktime((BaseType *)&tms); 
+#endif
+  return d;
+}
+
+//
+// convert via well-known strftime timeformat string into a string
+// 
+string StructDate::form(const char *fmt) const {
+  char buffer[1024];
+  strftime(buffer,sizeof(buffer),fmt,&tms);
+  return string(buffer);
+}
+
+//
+// Construct from day,month year ...
+//
+void StructDate::set(int day,int month,int year,int h,int m,int s) {
+  StructDate sd(Date::timeNow());
+  sd.tms.tm_sec= s;
+  sd.tms.tm_min= m;
+  sd.tms.tm_hour= h;
+  sd.tms.tm_mday = day;
+  sd.tms.tm_mon = month-1;
+  sd.tms.tm_year = ((year >= 1900) ? -1900 : 0) +  year + ((year < 80) ? 100 : 0);
+  *this = sd;
+}
+
+
+const char *StructDate::_monthNames[] 
+= { "jan","feb","mar","apr","may","jun"
+    ,"jul","aug","sep","oct","nov","dez" } ;
+
+const char *StructDate::monthStr() {
+  return _monthNames[tms.tm_mon];
+}
+
+// ------------------------------------------------------------------------------------
+
+Date::Date(int day,int month,int year) {
+  StructDate sd(day,month,year);
+  set(sd.date().get());
+}
+
+Date::Date(int day,int month,int year,int h,int m,int s) {
+  StructDate sd(day,month,year,h,m,s);
+  set(sd.date().get());
+}
+
+void Date::doy_set(short doy) {
+  const StructDate current(timeNow());
+  const StructDate sd2(doy,1,current.year4());
+  set(sd2.date().get());
+}
+
+short Date::doy_get() const {
+  const StructDate sd(*this);
+  return sd.dayOfYear1();
+}
+
+
+
+// Converting to string and streaming out is more a matter of DateStruct 
+// but Date was the first.
+
+string Date::form(int s,bool trail0,const char *fmt) const {
+  const StructDate sd(*this);
+  string buffer(sd.form(fmt));
+  if(s>0) { 
+    buffer.resize(s,' ');
+    if(trail0) buffer[s-1]= '\0';
+  }
+  return buffer;
+}
+
+
+//
+// prints out the date on the given ostream
+//
+ostream & Date::print(ostream &os,const char *fmt) const {
+  const StructDate sd(*this);
+  os << sd.form(fmt);
+  return os;
+}
+
+istream & Date::read_some(istream &is) {
+  string formS("");
+  if(is.peek() == '('
+	 && is.get()) {
+	while(is.peek() != ')') {
+		formS = formS + char(is.get());
+	}
+	is.get();
+	if(formS == "rinex") return read_rinex(is);
+	else if(formS == "gps_tag") {
+		long tag;
+		if(is >> tag) {
+		  tag_set(tag);
+		  return is;
+		}
+	} else if (formS == "gps") {
+		short week;
+		long weekSec;
+		if (   is >> week 
+			&& is >> weekSec) {
+			gps_set(week,weekSec);
+			return is;
+		}
+	} 
+  } else {
+	get_i(is);
+  } 
+  return is;
+}
+
+
+//
+double Date::rinex_default_secondsFraction;
+
+//
+istream & Date::read_rinex(istream &is,double &fraction) {
+  StructDate sd;
+  float fsec;
+
+  if (   is   >> sd.tms.tm_year
+      && is   >> sd.tms.tm_mon
+      && is   >> sd.tms.tm_mday
+      && is   >> sd.tms.tm_hour
+      && is   >> sd.tms.tm_min
+      && is   >> fsec           ) {
+    sd.tms.tm_sec = int(fsec);
+	sd.tms.tm_mon--;
+	fraction = fsec - sd.tms.tm_sec;
+    // y2k !!!
+    if(sd.tms.tm_year <= 80) sd.tms.tm_year += 100;
+	Date d2(sd.date());
+    set(d2.get());
+  }
+  return is;
+}
+
+//
+bool Date::get_i(istream &is) {
+  int day;
+  int month;
+  int year;
+  
+  if(   get_i2(is,day)
+     && get_i2(is,month)
+     && get_i2(is,year)) {
+    const StructDate sd(day,month,year);
+    *this =  sd.date();
+    return true;
+  }
+  return false;
+}
+
+//
+// get2 - misplaces decimal to number converter and reader reads exactly 2 digits
+// and writes them into result.
+
+bool Date::get_i2(istream &is,int &result) {
+  char c1;
+  result= 0;
+  
+  do {
+    is >> c1;
+  } while(is && (c1 == ' '));
+
+  if(isdigit(c1)) {
+    result = 10 * ( c1 - '0' );
+    char c2;
+    is >> c2;
+    if(isdigit(c2)) {
+      result += (c2 - '0');
+      return true;
+    } else is.putback(c2);
+  } else is.putback(c1);
+  return false;
+}
+
+// arquire the current time ...
+
+// DateType Date::_timeNow()
+// gets the current time
+DateType Date::_timeNow() { 
+  return time(NULL);
+} 
+
+//
+// (static) Date Date::time()
+// Constructs a Date from the system time at time of invocation.
+Date Date::timeNow() {
+  Date d;
+  d.data = _timeNow();
+  return d;
+}
+
Index: /trunk/BNC/RTCM/m_date.h
===================================================================
--- /trunk/BNC/RTCM/m_date.h	(revision 35)
+++ /trunk/BNC/RTCM/m_date.h	(revision 35)
@@ -0,0 +1,201 @@
+// -*- C++ -*-
+//
+// $Id: m_date.h,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
+//
+
+#if !defined(__m_date_h__)
+// protects the header from been included more than once
+#define __m_date_h_
+#if defined(__GNUC__)
+using namespace std;
+#endif
+//
+// Date & StructDate
+// Wrapper around c-lib and system service for date and time.
+// class Date is a wrapper around time_t.
+// class StructDate is a wrapper around struct tm.
+//
+#include "m_data.h"
+ 
+class Date;
+ 
+typedef time_t DateType;
+
+// Date - Date -> DateDiff ...
+class DateDiff : public  C_Data<DateType> { 
+public:
+  friend class Date;
+  explicit DateDiff(DateType t= 0):C_Data<DateType>(t) {} ;
+  DateDiff operator+(const DateDiff &arg2) { return DateDiff(get()+arg2.get()); }
+  DateDiff operator*(int factor) { return DateDiff(get()*factor); }
+};
+
+class Date : public C_Data<DateType> {
+public:
+ 
+  typedef C_Data<DateType> BaseType;
+  typedef DateDiff DiffType;
+
+  friend class StructDate;
+
+  Date():C_Data<DateType>(0) {}
+
+  Date(int day,int month,int year);
+  Date(int day,int month,int year,int h,int m,int s);
+
+
+  enum {
+    GPS_START = 315964800,             // 6.1.1980 00:00 UTC 
+    SECONDS_PER_WEEK = 60 * 60 * 24 * 7
+  } ;
+
+  // convient conversion to some stream format
+  friend ostream &operator<<(ostream &os,const Date &d) { return d.print(os);}
+
+  // convient conversion from yymmdd
+  friend istream &operator>>(istream &is,Date &d) {
+#if 1
+	d.read_some(is);
+#else
+    d.get_i(is);
+#endif
+    return is;
+  }
+
+  void tag_set(long tag) {
+    set(tag + GPS_START);
+  }
+
+  long tag_get() const {
+    return get() - GPS_START;
+  }
+
+
+  void gps_set(short gpsWeek,long gpsSeconds) {
+    // GPS-Zeit ist linear wie UNIX-Zeit, faengt am an.
+    tag_set(gpsWeek * SECONDS_PER_WEEK + gpsSeconds);
+  }
+
+  void gps_get(short &gpsWeek,long &gpsSeconds) const {
+#if 1
+	gpsWeek = tag_get() / SECONDS_PER_WEEK;
+	gpsSeconds = tag_get() % SECONDS_PER_WEEK;
+#else
+    divmod(tag_get(),(long)SECONDS_PER_WEEK,gpsWeek,gpsSeconds);
+#endif
+  }
+
+  void doy_set(short doy);
+  short doy_get() const;
+
+  istream &read_rinex(istream &is,double &fraction = rinex_default_secondsFraction);
+  istream &read_some(istream &is);
+  ostream &write_rinex(ostream &os);
+
+  // Translate the Date into a string ...
+  // with some extentions for special needs ...
+  string form(int s= 64,bool trail0=true,const char *fmt="%Y %m %d %H %M %S") const;
+
+  ostream &print(ostream &os,const char *fmt="%d%m%y") const ;
+
+  // getDate reads a day in the form "ttmmyy"
+  // for the current system time, but with the day as in the stream.
+  // expects that the time is present in the stream.
+  //
+  // For a complete missing date the function returns false leaving the
+  // stream unchanged via putback().
+  // For a partial malformed date the character get lost.
+
+  bool get_i(istream &is);
+
+
+  // returns the time of invocation: unix system time
+  static Date timeNow();
+
+  DateDiff operator-(const Date &arg2) const {
+    const DateType diff(get() - arg2.get());
+    return DateDiff(diff);
+  }
+
+  Date operator+(const DateDiff &arg2) const {
+    return Date(get() + arg2.get());
+  }
+
+private:
+  Date(DateType dt):C_Data<DateType>(dt) {}
+
+  bool get_i2(istream &is,int &result);
+
+  static DateType _timeNow();
+  static double rinex_default_secondsFraction;
+} ;
+
+//
+// StructDate
+// wrapper around gmtime,mktime and strftime
+//
+class StructDate {
+public:
+  typedef struct tm BaseType;
+
+  StructDate();
+  // default copy-constructor
+  // default assignment
+  // default destructor
+
+  StructDate(const Date &date) { set(date); }
+  StructDate(int day,int month,int year) { set(day,month,year); }
+  StructDate(int day,int month,int year,int h,int m,int s) { set(day,month,year,h,m,s); }
+
+  Date date() const;
+
+
+  string form(const char *fmt) const;
+
+  void set(int day,int month,int year,int h= 12,int m= 0,int s= 0);
+  void set(const Date &date);
+
+  int sec() const { 
+    return tms.tm_sec; 
+  } 
+
+  int min() const { 
+    return tms.tm_min; 
+  }
+
+  int hour() const { 
+    return tms.tm_hour; 
+  }
+
+  int day() const { 
+    return tms.tm_mday; 
+  }
+
+  int month() const {
+    return tms.tm_mon+1; 
+  }
+  const static char *_monthNames[];
+  const char *monthStr();
+
+  int year4() const {
+    return tms.tm_year + 1900;
+  }
+
+  int year2() const {
+    return tms.tm_year%100;
+  }
+
+  // returns the day of year 0-based.
+  int dayOfYear0() const {
+	return tms.tm_yday;
+  }
+
+  // return the day of year 1-based.
+  int dayOfYear1() const {
+	return tms.tm_yday+1;
+  }
+
+  BaseType tms;
+} ;
+
+#endif
Index: /trunk/BNC/bnc.bck
===================================================================
--- /trunk/BNC/bnc.bck	(revision 35)
+++ /trunk/BNC/bnc.bck	(revision 35)
@@ -0,0 +1,13 @@
+
+fileName="bnc`date +%y%m%d`"
+
+tar -cvf ${fileName}.tar bnc.bck    \
+                         *.cpp      \
+                         *.h        \
+                         *.pro      \
+                         RTCM/*.cpp \
+                         RTCM/*.h
+
+gzip      ${fileName}.tar
+mv        ${fileName}.tar.gz ${fileName}.tgz
+chmod 440 ${fileName}.tgz
Index: /trunk/BNC/bnc.pro
===================================================================
--- /trunk/BNC/bnc.pro	(revision 35)
+++ /trunk/BNC/bnc.pro	(revision 35)
@@ -0,0 +1,11 @@
+
+HEADERS =             bncgetthread.h    bncwindow.h   bnctabledlg.h   \
+          bnccaster.h \
+          RTCM/format.h RTCM/GPSDecoder.h RTCM/m_data.h RTCM/RTCM.h
+
+SOURCES = bncmain.cpp bncgetthread.cpp  bncwindow.cpp bnctabledlg.cpp \
+          bnccaster.cpp \
+          RTCM/m_date.cpp RTCM/RTCM.cpp
+
+QT += network
+
Index: /trunk/BNC/bnccaster.cpp
===================================================================
--- /trunk/BNC/bnccaster.cpp	(revision 35)
+++ /trunk/BNC/bnccaster.cpp	(revision 35)
@@ -0,0 +1,190 @@
+
+/* -------------------------------------------------------------------------
+ * Bernese NTRIP Client
+ * -------------------------------------------------------------------------
+ *
+ * Class:      bncCaster
+ *
+ * Purpose:    buffers and disseminates the data
+ *
+ * Author:     L. Mervart
+ *
+ * Created:    24-Dec-2005
+ *
+ * Changes:    
+ *
+ * -----------------------------------------------------------------------*/
+
+#include "bnccaster.h"
+#include "bncgetthread.h"
+#include "RTCM/RTCM.h"
+
+// Constructor
+////////////////////////////////////////////////////////////////////////////
+bncCaster::bncCaster(const QString& outFileName, int port) {
+
+  if ( !outFileName.isEmpty() ) {
+    _outFile = new QFile(outFileName); 
+    _outFile->open(QIODevice::WriteOnly);
+    _out = new QTextStream(_outFile);
+    _out->setRealNumberNotation(QTextStream::FixedNotation);
+    _out->setRealNumberPrecision(5);
+  }
+  else {
+    _outFile = 0;
+    _out     = 0;
+  }
+
+  _port = port;
+
+  if (_port != 0) {
+    _server = new QTcpServer;
+    _server->listen(QHostAddress::Any, _port);
+    connect(_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
+    _sockets = new QList<QTcpSocket*>;
+  }
+  else {
+    _server  = 0;
+    _sockets = 0;
+  }
+
+  _epochs = new QMultiMap<long, Observation*>;
+
+  _lastDumpSec   = 0; 
+}
+
+// Destructor
+////////////////////////////////////////////////////////////////////////////
+bncCaster::~bncCaster() {
+  delete _out;
+  delete _outFile;
+}
+
+// Run
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::run() {
+  exec();
+}
+
+// New Observations
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::slotNewObs(const QByteArray& mountPoint, Observation* obs) {
+
+  long newTime = obs->GPSWeek * 7*24*3600 + obs->GPSWeeks;
+
+  // First time, set the _lastDumpSec immediately
+  // --------------------------------------------
+  if (_lastDumpSec == 0) {
+    _lastDumpSec = newTime - 1;
+  }
+
+  // An old observation - throw it away
+  // ----------------------------------
+  if (newTime <= _lastDumpSec) {
+    delete obs;
+    return;
+  }
+
+  // Rename the station and save the observation
+  // -------------------------------------------
+  strncpy(obs->StatID, mountPoint.constData(),sizeof(obs->StatID));
+  _epochs->insert(newTime, obs);
+
+  // Dump older epochs
+  // -----------------
+  const long waitTime = 5;
+  dumpEpochs(_lastDumpSec + 1, newTime - waitTime);
+  _lastDumpSec = newTime - waitTime;
+}
+
+// New Connection
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::slotNewConnection() {
+  _sockets->push_back( _server->nextPendingConnection() );
+}
+
+// Add New Thread
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::addGetThread(bncGetThread* getThread) {
+  connect(getThread, SIGNAL(newObs(const QByteArray&, Observation*)),
+          this, SLOT(slotNewObs(const QByteArray&, Observation*)));
+
+  connect(getThread, SIGNAL(error(const QByteArray&)), 
+          this, SLOT(slotGetThreadError(const QByteArray&)));
+
+  _mountPoints.push_back(getThread->mountPoint());
+}
+
+// Error in get thread
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::slotGetThreadError(const QByteArray& mountPoint) {
+  _mountPoints.removeAll(mountPoint);
+  qWarning("Mountpoint size %d", _mountPoints.size());
+  if (_mountPoints.size() == 0) {
+    qWarning("bncCaster:: last get thread terminated\n");
+    emit getThreadErrors();
+  }
+}
+
+// Dump Complete Epochs
+////////////////////////////////////////////////////////////////////////////
+void bncCaster::dumpEpochs(long minTime, long maxTime) {
+
+  const char begEpoch = 'A';
+  const char begObs   = 'B';
+  const char endEpoch = 'C';
+
+  for (long sec = minTime; sec <= maxTime; sec++) {
+   
+    bool first = true;
+    QList<Observation*> allObs = _epochs->values(sec);
+    QListIterator<Observation*> it(allObs);
+    while (it.hasNext()) {
+      Observation* obs = it.next();
+
+      // Output into the file
+      // --------------------
+      if (_out) {
+        if (first) {
+          *_out << begEpoch << endl;;
+        }
+        *_out <<       obs->StatID    << " "
+              << (int) obs->SVPRN     << " "
+              << (int) obs->GPSWeek   << " "
+              <<       obs->GPSWeeks  << " "
+              <<       obs->sec       << " "
+              <<       obs->pCodeIndicator << " "
+              <<       obs->cumuLossOfCont << " "
+              <<       obs->C1        << " "
+              <<       obs->P2        << " "
+              <<       obs->L1        << " "
+              <<       obs->L2        << endl;
+        if (!it.hasNext()) {
+          *_out << endEpoch << endl;
+        }
+      }
+
+      // Output into the socket
+      // ----------------------
+      if (_sockets) {
+        int numBytes = sizeof(*obs); 
+        QListIterator<QTcpSocket*> is(*_sockets);
+        while (is.hasNext()) {
+          QTcpSocket* sock = is.next();
+          if (first) {
+            sock->write(&begEpoch, 1);
+          }
+          sock->write(&begObs, 1);
+          sock->write((char*) obs, numBytes);
+          if (!it.hasNext()) {
+            sock->write(&endEpoch, 1);
+          }
+        }
+      }
+
+      delete obs;
+      _epochs->remove(sec);
+      first = false;
+    }
+  }
+}
Index: /trunk/BNC/bnccaster.h
===================================================================
--- /trunk/BNC/bnccaster.h	(revision 35)
+++ /trunk/BNC/bnccaster.h	(revision 35)
@@ -0,0 +1,48 @@
+
+#ifndef BNCCASTER_H
+#define BNCCASTER_H
+
+#include <QFile>
+#include <QThread>
+#include <QtNetwork>
+#include <QMultiMap>
+
+#include "RTCM/GPSDecoder.h"
+
+class bncGetThread;
+
+class bncCaster : public QThread {
+ Q_OBJECT
+
+ public:
+   bncCaster(const QString& outFileName, int port);
+   ~bncCaster();
+   void addGetThread(bncGetThread* getThread);
+
+ signals:
+   void getThreadErrors();   
+
+ public slots:
+   void slotNewObs(const QByteArray& mountPoint, Observation* obs);
+
+ private slots:
+   void slotNewConnection();
+   void slotGetThreadError(const QByteArray& mountPoint);
+
+ protected:
+   virtual void run();
+
+ private:
+   void dumpEpochs(long minTime, long maxTime);
+
+   QFile*                         _outFile;
+   int                            _port;
+   QTextStream*                   _out;
+   QMultiMap<long, Observation*>* _epochs;
+   long                           _lastDumpSec;
+   QTcpServer*                    _server;
+   QList<QTcpSocket*>*            _sockets;
+   QList<QByteArray>              _mountPoints;
+};
+
+#endif
Index: /trunk/BNC/bncgetthread.cpp
===================================================================
--- /trunk/BNC/bncgetthread.cpp	(revision 35)
+++ /trunk/BNC/bncgetthread.cpp	(revision 35)
@@ -0,0 +1,166 @@
+
+/* -------------------------------------------------------------------------
+ * Bernese NTRIP Client
+ * -------------------------------------------------------------------------
+ *
+ * Class:      bncGetThread
+ *
+ * Purpose:    Thread that retrieves data from NTRIP caster
+ *
+ * Author:     L. Mervart
+ *
+ * Created:    24-Dec-2005
+ *
+ * Changes:    
+ *
+ * -----------------------------------------------------------------------*/
+
+#include <QFile>
+#include <QTextStream>
+#include <QtNetwork>
+
+#include "bncgetthread.h"
+#include "RTCM/RTCM.h"
+
+using namespace std;
+
+const int timeOut = 10*1000;
+
+// Constructor
+////////////////////////////////////////////////////////////////////////////
+bncGetThread::bncGetThread(const QString& host, int port,
+                           const QString& proxyHost, int proxyPort,
+                           const QByteArray& mountPoint,
+                           const QByteArray& user, 
+                           const QByteArray& password) {
+  _host       = host;
+  _port       = port;
+  _proxyHost  = proxyHost;
+  _proxyPort  = proxyPort;
+  _mountPoint = mountPoint;
+  _user       = user;
+  _password   = password;
+  _socket     = 0;
+}
+
+// Destructor
+////////////////////////////////////////////////////////////////////////////
+bncGetThread::~bncGetThread() {
+  delete _socket;
+}
+
+// Connect to Caster, send the Request (static)
+////////////////////////////////////////////////////////////////////////////
+QTcpSocket* bncGetThread::request(const QString& host, int port,
+                                  const QString& proxyHost, int proxyPort,
+                                  const QByteArray& mountPoint,
+                                  const QByteArray& user, 
+                                  const QByteArray& password) {
+
+  QTcpSocket* socket = new QTcpSocket();
+
+  QByteArray l_mountPoint = mountPoint;
+
+  if ( proxyHost.isEmpty() ) {
+    socket->connectToHost(host, port);
+  }
+  else {
+    socket->connectToHost(proxyHost, proxyPort);
+    if (!proxyHost.isEmpty()) {
+      QUrl proxyUrl;
+      proxyUrl.setScheme("http");
+      proxyUrl.setHost(host);
+      proxyUrl.setPort(port);
+      l_mountPoint = proxyUrl.resolved(QUrl(mountPoint)).toEncoded();
+    }
+  }
+
+  if (!socket->waitForConnected(timeOut)) {
+    qWarning("Connect timeout");
+    delete socket;
+    return 0;
+  }
+
+  // Send Request
+  // ------------
+  QByteArray userAndPwd = user + ":" + password;
+  QByteArray reqStr = "GET " + l_mountPoint + " HTTP/1.0\r\n"
+                      "User-Agent: NTRIP BNC 1.0\r\n"
+                      "Authorization: Basic " + userAndPwd.toBase64() + 
+                      "\r\n\r\n";
+
+  qWarning(reqStr);
+
+  socket->write(reqStr, reqStr.length());
+
+  if (!socket->waitForBytesWritten(timeOut)) {
+    qWarning("Write timeout");
+    delete socket;
+    return 0;
+  }
+
+  return socket;
+}
+
+// Run
+////////////////////////////////////////////////////////////////////////////
+void bncGetThread::run() {
+
+  // Send the Request
+  // ----------------
+  _socket = bncGetThread::request(_host, _port, _proxyHost, _proxyPort, 
+                                  _mountPoint, _user, _password);
+  if (!_socket) {
+    return exit(1);
+  }
+
+  // Read Caster Response
+  // --------------------
+  _socket->waitForReadyRead(timeOut);
+  if (_socket->canReadLine()) {
+    QString line = _socket->readLine();
+    if (line.indexOf("ICY 200 OK") != 0) {
+      qWarning(("Wrong Caster Response:\n" + line).toAscii());
+      return exit(1);
+    }
+  }
+  else {
+    qWarning("Response Timeout");
+    return exit(1);
+  }
+
+  // Instantiate the filter
+  // ----------------------
+  RTCM rtcmFilter('A',true);
+
+  // Read Incoming Data
+  // ------------------
+  while (true) {
+    _socket->waitForReadyRead(timeOut);
+    qint64 nBytes = _socket->bytesAvailable();
+    if (nBytes > 0) {
+      char* data = new char[nBytes];
+      _socket->read(data, nBytes);
+      rtcmFilter.Decode(data, nBytes);
+      delete data;
+      for (list<Observation*>::iterator it = rtcmFilter.m_lObsList.begin(); 
+           it != rtcmFilter.m_lObsList.end(); it++) {
+        emit newObs(_mountPoint, *it);
+      }
+      rtcmFilter.m_lObsList.clear();
+    }
+    else {
+      qWarning("Data Timeout");
+      return exit(1);
+    }
+  }
+}
+
+// Exit
+////////////////////////////////////////////////////////////////////////////
+void bncGetThread::exit(int exitCode) {
+  if (exitCode!= 0) {
+    emit error(_mountPoint);
+  }
+  QThread::exit(exitCode);
+}
Index: /trunk/BNC/bncgetthread.h
===================================================================
--- /trunk/BNC/bncgetthread.h	(revision 35)
+++ /trunk/BNC/bncgetthread.h	(revision 35)
@@ -0,0 +1,45 @@
+
+#ifndef BNCGETTHREAD_H
+#define BNCGETTHREAD_H
+
+#include <QThread>
+#include <QtNetwork>
+
+#include "RTCM/GPSDecoder.h"
+
+class bncGetThread : public QThread {
+ Q_OBJECT
+
+ public:
+   bncGetThread(const QString& host, int port,
+                const QString& proxyHost, int proxyPort,
+                const QByteArray& mountPoint,
+                const QByteArray& user, 
+                const QByteArray& password);
+   ~bncGetThread();
+   static QTcpSocket* bncGetThread::request(const QString& host, int port,
+                                      const QString& proxyHost, int proxyPort,
+                                      const QByteArray& mountPoint,
+                                      const QByteArray& user, 
+                                      const QByteArray& password);
+   QByteArray mountPoint() {return _mountPoint;}
+
+ signals:
+   void newObs(const QByteArray& mountPoint, Observation* obs);
+   void error(const QByteArray& mountPoint);
+
+ protected:
+   virtual void run();
+ private:
+   void exit(int exitCode = 0);
+   QTcpSocket* _socket;
+   QString     _host;
+   int         _port;
+   QString     _proxyHost;
+   int         _proxyPort;
+   QByteArray  _mountPoint;
+   QByteArray  _user;
+   QByteArray  _password;
+};
+
+#endif
Index: /trunk/BNC/bncmain.cpp
===================================================================
--- /trunk/BNC/bncmain.cpp	(revision 35)
+++ /trunk/BNC/bncmain.cpp	(revision 35)
@@ -0,0 +1,95 @@
+
+/* -------------------------------------------------------------------------
+ * Bernese NTRIP Client
+ * -------------------------------------------------------------------------
+ *
+ * Class:      main
+ *
+ * Purpose:    Application starts here
+ *
+ * Author:     L. Mervart
+ *
+ * Created:    24-Dec-2005
+ *
+ * Changes:    
+ *
+ * -----------------------------------------------------------------------*/
+
+#include <windows.h>
+
+#include <QApplication>
+#include <QFile>
+#include <iostream>
+
+#include "bncwindow.h"
+
+using namespace std;
+
+#ifdef WIN32
+QFile       logFile("BNC.LOG");
+QTextStream logStream;
+#endif
+
+void myMessageOutput(QtMsgType, const char *msg) {
+#ifdef WIN32
+  logStream << msg << endl;
+  logStream.flush();
+#else
+  cerr << msg << endl;
+#endif
+}
+
+int main(int argc, char *argv[]) {
+
+  bool GUIenabled = true;
+  if (argc > 1 && QString(argv[1]) == "-nw") {
+    GUIenabled = false;
+  }
+
+  QApplication app(argc, argv, GUIenabled);
+
+#ifdef WIN32
+  logFile.open(QIODevice::WriteOnly);
+  logStream.setDevice(&logFile);
+#endif
+  qInstallMsgHandler(myMessageOutput);
+
+  QCoreApplication::setOrganizationName("AIUB");
+  QCoreApplication::setOrganizationDomain("www.aiub.unibe.ch");
+  QCoreApplication::setApplicationName("Bernese NTRIP Client");
+
+  bncWindow* bncWin = 0;
+
+  if (GUIenabled) {
+    bncWin = new bncWindow();
+    bncWin->show();
+  }
+  else {
+    QSettings settings;
+    QString    proxyHost = settings.value("proxyHost").toString();
+    int        proxyPort = settings.value("proxyPort").toInt();
+    QByteArray user      = settings.value("user").toString().toAscii();
+    QByteArray password  = settings.value("password").toString().toAscii();
+    
+    bncCaster* caster = new bncCaster(settings.value("outFile").toString(),
+                                      settings.value("outPort").toInt());
+
+    app.connect(caster, SIGNAL(getThreadErrors()), &app, SLOT(quit()));
+
+    caster->start();
+    
+    QListIterator<QString> it(settings.value("mountPoints").toStringList());
+    while (it.hasNext()) {
+      QUrl url(it.next());
+      QByteArray mountPoint = url.path().mid(1).toAscii();
+
+      bncGetThread* getThread = new bncGetThread(url.host(), url.port(),
+                                                 proxyHost, proxyPort, 
+                                                 mountPoint, user, password);
+      caster->addGetThread(getThread);
+
+      getThread->start();
+    }
+  }
+  return app.exec();
+}
Index: /trunk/BNC/bnctabledlg.cpp
===================================================================
--- /trunk/BNC/bnctabledlg.cpp	(revision 35)
+++ /trunk/BNC/bnctabledlg.cpp	(revision 35)
@@ -0,0 +1,180 @@
+
+/* -------------------------------------------------------------------------
+ * Bernese NTRIP Client
+ * -------------------------------------------------------------------------
+ *
+ * Class:      bncTableDlg
+ *
+ * Purpose:    Displays the source table, allows mountpoints selection
+ *
+ * Author:     L. Mervart
+ *
+ * Created:    24-Dec-2005
+ *
+ * Changes:    
+ *
+ * -----------------------------------------------------------------------*/
+
+#include "bnctabledlg.h"
+#include "bncgetthread.h"
+
+// Constructor
+////////////////////////////////////////////////////////////////////////////
+bncTableDlg::bncTableDlg(QWidget* parent, const QString& proxyHost,
+                         int proxyPort) : QDialog(parent) {
+
+  _proxyHost = proxyHost;
+  _proxyPort = proxyPort;
+
+  setMinimumSize(600,400);
+
+  QVBoxLayout* mainLayout = new QVBoxLayout(this);
+
+  _casterHostLabel    = new QLabel(tr("caster host"));
+  _casterPortLabel    = new QLabel(tr("caster port"));
+  QSettings settings;
+  _casterHostLineEdit = new QLineEdit(settings.value("casterHost").toString());
+  _casterPortLineEdit = new QLineEdit(settings.value("casterPort").toString());
+
+  QHBoxLayout* editLayout = new QHBoxLayout;
+  editLayout->addWidget(_casterHostLabel);
+  editLayout->addWidget(_casterHostLineEdit);
+  editLayout->addWidget(_casterPortLabel);
+  editLayout->addWidget(_casterPortLineEdit);
+  mainLayout->addLayout(editLayout);
+
+  _table = new QTableWidget(this);
+  mainLayout->addWidget(_table);
+
+  _buttonGet = new QPushButton(tr("Get Table"), this);
+  connect(_buttonGet, SIGNAL(clicked()), this, SLOT(slotGetTable()));
+
+  _buttonCancel = new QPushButton(tr("Cancel"), this);
+  connect(_buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
+
+  _buttonOK = new QPushButton(tr("OK"), this);
+  connect(_buttonOK, SIGNAL(clicked()), this, SLOT(accept()));
+
+  QHBoxLayout* buttonLayout = new QHBoxLayout;
+  buttonLayout->addStretch(1);
+  buttonLayout->addWidget(_buttonGet);
+  buttonLayout->addWidget(_buttonCancel);
+  buttonLayout->addWidget(_buttonOK);
+
+  mainLayout->addLayout(buttonLayout);
+}
+
+// Destructor
+////////////////////////////////////////////////////////////////////////////
+bncTableDlg::~bncTableDlg() {
+  if (_table) {
+    for (int ir = 0; ir < _table->rowCount(); ir++) {
+      for (int ic = 0; ic < _table->columnCount(); ic++) {
+        delete _table->item(ir,ic);
+      }
+    }
+  }
+}
+
+// Read Table from Caster
+////////////////////////////////////////////////////////////////////////////
+void bncTableDlg::slotGetTable() {
+
+  QString    host       = _casterHostLineEdit->text();
+  int        port       = _casterPortLineEdit->text().toInt();
+  QByteArray mountPoint = "/";
+  QByteArray user;
+  QByteArray password;
+
+  // Send the Request
+  // ----------------
+  QTcpSocket* socket = bncGetThread::request(host, port, _proxyHost, 
+                                             _proxyPort, mountPoint, 
+                                             user, password);
+  if (!socket) {
+    return;
+  }
+
+  // Read Caster Response
+  // --------------------
+  QStringList allLines;
+  bool first = true;
+  while (true) {
+    if (socket->canReadLine()) {
+      QString line = socket->readLine();
+      if (first) {
+        first = false;
+        if (line.indexOf("SOURCETABLE 200 OK") != 0) {
+	  QMessageBox::warning(0, "BNC", "Wrong Caster Response:\n" + line);
+          break;
+        }
+      }
+      else {
+        if (line.indexOf("ENDSOURCETABLE") == 0) {
+          break;
+        }
+        if (line.indexOf("STR") == 0) {
+          allLines.push_back(line);
+        }
+      }
+    }
+    else {
+      const int timeOut = 10*1000;
+      socket->waitForReadyRead(timeOut);
+      if (socket->bytesAvailable() > 0) {
+        continue;
+      }
+      else {
+	QMessageBox::warning(0, "BNC", "Data Timeout");
+        break;
+      }
+    }
+  }
+  delete socket;
+
+  if (allLines.size() > 0) {
+    _table->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    _table->setSelectionBehavior(QAbstractItemView::SelectRows);
+
+    QStringList hlp = allLines[0].split(";");
+    _table->setColumnCount(hlp.size()-1);
+    _table->setRowCount(allLines.size());
+
+    QListIterator<QString> it(allLines);
+    int nRow = -1;
+    while (it.hasNext()) {
+      QStringList columns = it.next().split(";");
+      ++nRow;
+      for (int ic = 0; ic < columns.size()-1; ic++) {
+        _table->setItem(nRow, ic, new QTableWidgetItem(columns[ic+1]));
+      }
+    }
+  }
+}
+
+// Accept slot
+////////////////////////////////////////////////////////////////////////////
+void bncTableDlg::accept() {
+
+  QSettings settings;
+  settings.setValue("casterHost", _casterHostLineEdit->text());
+  settings.setValue("casterPort", _casterPortLineEdit->text());
+
+  QStringList* mountPoints = new QStringList;
+
+  if (_table) {
+    for (int ir = 0; ir < _table->rowCount(); ir++) {
+      QTableWidgetItem* item = _table->item(ir,0);
+      if (_table->isItemSelected(item)) {
+        QUrl url;
+        url.setHost(_casterHostLineEdit->text());
+        url.setPort(_casterPortLineEdit->text().toInt());
+        url.setPath(item->text());
+        mountPoints->push_back(url.toString());
+      }
+    }
+  }
+  emit newMountPoints(mountPoints);
+
+  QDialog::accept();
+}
Index: /trunk/BNC/bnctabledlg.h
===================================================================
--- /trunk/BNC/bnctabledlg.h	(revision 35)
+++ /trunk/BNC/bnctabledlg.h	(revision 35)
@@ -0,0 +1,37 @@
+
+#ifndef BNCTABLEDLG_H
+#define BNCTABLEDLG_H
+
+#include <QtGui>
+
+class bncTableDlg : public QDialog {
+  Q_OBJECT
+
+  public:
+    bncTableDlg(QWidget* parent, const QString& proxyHost, int proxyPort);
+    ~bncTableDlg();
+
+  signals:
+    void newMountPoints(QStringList* mountPoints);
+
+  private slots:
+    virtual void accept();
+    void slotGetTable();
+
+  private:
+    QString      _proxyHost;
+    int          _proxyPort;
+
+    QLabel*      _casterHostLabel;
+    QLabel*      _casterPortLabel;
+    QLineEdit*   _casterHostLineEdit;
+    QLineEdit*   _casterPortLineEdit;
+
+    QPushButton* _buttonGet;
+    QPushButton* _buttonCancel;
+    QPushButton* _buttonOK;
+
+    QTableWidget* _table;
+};
+
+#endif
Index: /trunk/BNC/bncwindow.cpp
===================================================================
--- /trunk/BNC/bncwindow.cpp	(revision 35)
+++ /trunk/BNC/bncwindow.cpp	(revision 35)
@@ -0,0 +1,231 @@
+
+/* -------------------------------------------------------------------------
+ * Bernese NTRIP Client
+ * -------------------------------------------------------------------------
+ *
+ * Class:      bncWindow
+ *
+ * Purpose:    This class implements the main application window.
+ *
+ * Author:     L. Mervart
+ *
+ * Created:    24-Dec-2005
+ *
+ * Changes:    
+ *
+ * -----------------------------------------------------------------------*/
+
+#include "bncwindow.h" 
+#include "bncgetthread.h" 
+#include "bnctabledlg.h" 
+
+using namespace std;
+
+// Constructor
+////////////////////////////////////////////////////////////////////////////
+bncWindow::bncWindow() {
+
+  setMinimumSize(600,400);
+
+  // Create Actions
+  // --------------
+  _actAbout = new QAction(tr("&About"),this);
+  _actAbout->setEnabled(false);
+
+  _actSaveOpt = new QAction(tr("&Save Options"),this);
+  connect(_actSaveOpt, SIGNAL(triggered()), SLOT(slotSaveOptions()));
+
+  _actQuit  = new QAction(tr("&Quit"),this);
+  connect(_actQuit, SIGNAL(triggered()), SLOT(close()));
+
+  _actAddMountPoints = new QAction(tr("&Add Mount Points"),this);
+  connect(_actAddMountPoints, SIGNAL(triggered()), SLOT(slotAddMountPoints()));
+
+  _actDeleteMountPoints = new QAction(tr("&Delete Mount Points"),this);
+  connect(_actDeleteMountPoints, SIGNAL(triggered()), SLOT(slotDeleteMountPoints()));
+
+  _actGetData = new QAction(tr("&Get Data"),this);
+  connect(_actGetData, SIGNAL(triggered()), SLOT(slotGetData()));
+
+  // Create Menus
+  // ------------
+  _menuFile = menuBar()->addMenu(tr("&File"));
+  _menuFile->addAction(_actSaveOpt);
+  _menuFile->addSeparator();
+  _menuFile->addAction(_actQuit);
+
+  _menuHlp = menuBar()->addMenu(tr("&Help"));
+  _menuHlp->addAction(_actAbout);
+
+  // Tool (Command) Bar
+  // ------------------
+  QToolBar* toolBar = new QToolBar;
+  addToolBar(Qt::BottomToolBarArea, toolBar); 
+  toolBar->setMovable(false);
+  toolBar->addAction(_actAddMountPoints);
+  toolBar->addAction(_actDeleteMountPoints);
+  toolBar->addAction(_actGetData);
+
+  // Canvas with Editable Fields
+  // ---------------------------
+  _canvas = new QWidget;
+  setCentralWidget(_canvas);
+
+  QGridLayout* layout = new QGridLayout;
+  _canvas->setLayout(layout);
+
+  _proxyHostLabel     = new QLabel("proxy host");
+  _proxyPortLabel     = new QLabel("proxy port");
+  _userLabel          = new QLabel("user");
+  _passwordLabel      = new QLabel("password");
+  _outFileLabel       = new QLabel("output file");
+  _outPortLabel       = new QLabel("output port");
+  _mountPointsLabel   = new QLabel("mount points");
+
+  QSettings settings;
+
+  _proxyHostLineEdit  = new QLineEdit(settings.value("proxyHost").toString());
+  _proxyPortLineEdit  = new QLineEdit(settings.value("proxyPort").toString());
+  _userLineEdit       = new QLineEdit(settings.value("user").toString());
+  _passwordLineEdit   = new QLineEdit(settings.value("password").toString());
+  _passwordLineEdit->setEchoMode(QLineEdit::Password);
+  _outFileLineEdit    = new QLineEdit(settings.value("outFile").toString());
+  _outPortLineEdit    = new QLineEdit(settings.value("outPort").toString());
+  _mountPointsTable   = new QTableWidget(0,1);
+  _mountPointsTable->setMaximumHeight(100);
+  _mountPointsTable->horizontalHeader()->hide();
+  _mountPointsTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
+  _mountPointsTable->verticalHeader()->hide();
+  _mountPointsTable->setGridStyle(Qt::NoPen);
+  _mountPointsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+  _mountPointsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+  QListIterator<QString> it(settings.value("mountPoints").toStringList());
+  if (!it.hasNext()) {
+    _actGetData->setEnabled(false);
+  }
+  int iRow = 0;
+  while (it.hasNext()) {
+    QString mPoint = it.next();
+    _mountPointsTable->insertRow(iRow);
+    _mountPointsTable->setItem(iRow, 0, new QTableWidgetItem(mPoint));
+    iRow++;
+  }
+
+  layout->addWidget(_proxyHostLabel,     0, 0);
+  layout->addWidget(_proxyHostLineEdit,  0, 1);
+  layout->addWidget(_proxyPortLabel,     0, 2);
+  layout->addWidget(_proxyPortLineEdit,  0, 3);
+  layout->addWidget(_userLabel,          1, 0);
+  layout->addWidget(_userLineEdit,       1, 1);
+  layout->addWidget(_passwordLabel,      1, 2);
+  layout->addWidget(_passwordLineEdit,   1, 3);
+  layout->addWidget(_outFileLabel,       2, 0);
+  layout->addWidget(_outFileLineEdit,    2, 1);
+  layout->addWidget(_outPortLabel,       2, 2);
+  layout->addWidget(_outPortLineEdit,    2, 3);
+
+  layout->addWidget(_mountPointsLabel,   3, 0);
+  layout->addWidget(_mountPointsTable,   3, 1, 1, 3);
+
+  _bncCaster = 0;
+}
+
+// Destructor
+////////////////////////////////////////////////////////////////////////////
+bncWindow::~bncWindow() {
+}
+
+// Retrieve Table
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotAddMountPoints() {
+  bncTableDlg* dlg = new bncTableDlg(this, _proxyHostLineEdit->text(),
+                                     _proxyPortLineEdit->text().toInt());
+  connect(dlg, SIGNAL(newMountPoints(QStringList*)), 
+          this, SLOT(slotNewMountPoints(QStringList*)));
+  dlg->exec();
+  delete dlg;
+
+}
+
+// Delete Selected Mount Points
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotDeleteMountPoints() {
+  _mountPointsTable->clear();
+  _mountPointsTable->setRowCount(0);
+}
+
+// New Mount Points Selected
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotNewMountPoints(QStringList* mountPoints) {
+  int iRow = 0;
+  QListIterator<QString> it(*mountPoints);
+  while (it.hasNext()) {
+    QString mPoint = it.next();
+    _mountPointsTable->insertRow(iRow);
+    _mountPointsTable->setItem(iRow, 0, new QTableWidgetItem(mPoint));
+    iRow++;
+  }
+  if (mountPoints->count() > 0) {
+    _actGetData->setEnabled(true);
+  }
+  delete mountPoints;
+}
+
+// Save Options
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotSaveOptions() {
+  QSettings settings;
+  settings.setValue("proxyHost",   _proxyHostLineEdit->text());
+  settings.setValue("proxyPort",   _proxyPortLineEdit->text());
+  settings.setValue("user",        _userLineEdit->text());
+  settings.setValue("password",    _passwordLineEdit->text());
+  settings.setValue("outFile",     _outFileLineEdit->text());
+  settings.setValue("outPort",     _outPortLineEdit->text());
+  QStringList mountPoints;
+  for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
+    mountPoints.append(_mountPointsTable->item(iRow, 0)->text());
+  }
+  settings.setValue("mountPoints", mountPoints);
+}
+
+// All get slots terminated
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotGetThreadErrors() {
+  QMessageBox::warning(0, "BNC", "All Get Threads Terminated");
+  _actAddMountPoints->setEnabled(true);
+  _actDeleteMountPoints->setEnabled(true);
+  _actGetData->setEnabled(true);
+}
+
+// Retrieve Data
+////////////////////////////////////////////////////////////////////////////
+void bncWindow::slotGetData() {
+  _actAddMountPoints->setEnabled(false);
+  _actDeleteMountPoints->setEnabled(false);
+  _actGetData->setEnabled(false);
+
+  QString    proxyHost = _proxyHostLineEdit->text();
+  int        proxyPort = _proxyPortLineEdit->text().toInt();
+  QByteArray user      = _userLineEdit->text().toAscii();
+  QByteArray password  = _passwordLineEdit->text().toAscii();
+
+  _bncCaster = new bncCaster(_outFileLineEdit->text(), 
+                             _outPortLineEdit->text().toInt());
+
+  connect(_bncCaster, SIGNAL(getThreadErrors()), 
+          this, SLOT(slotGetThreadErrors()));
+
+  _bncCaster->start();
+
+  for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
+    QUrl url(_mountPointsTable->item(iRow, 0)->text());
+    QByteArray mountPoint = url.path().mid(1).toAscii();
+
+    bncGetThread* getThread = new bncGetThread(url.host(), url.port(),
+                                               proxyHost, proxyPort, 
+                                               mountPoint, user, password);
+    _bncCaster->addGetThread(getThread);
+
+    getThread->start();
+  }
+}
Index: /trunk/BNC/bncwindow.h
===================================================================
--- /trunk/BNC/bncwindow.h	(revision 35)
+++ /trunk/BNC/bncwindow.h	(revision 35)
@@ -0,0 +1,57 @@
+
+#ifndef BNCWINDOW_H
+#define BNCWINDOW_H
+
+#include <QtGui>
+
+#include "bncgetthread.h"
+#include "bnccaster.h"
+
+class bncWindow : public QMainWindow {
+  Q_OBJECT
+
+  public:
+    bncWindow();
+    ~bncWindow();  
+
+  private slots:
+    void slotSaveOptions();
+    void slotAddMountPoints();
+    void slotGetData();
+    void slotNewMountPoints(QStringList* mountPoints);
+    void slotDeleteMountPoints();
+    void slotGetThreadErrors();
+
+  private:
+    QMenu*     _menuHlp;
+    QMenu*     _menuFile;
+
+    QAction*   _actAbout;
+    QAction*   _actSaveOpt;
+    QAction*   _actQuit; 
+    QAction*   _actGetData;
+    QAction*   _actAddMountPoints;
+    QAction*   _actDeleteMountPoints;
+
+    QLabel*    _proxyHostLabel;
+    QLabel*    _proxyPortLabel;
+    QLabel*    _userLabel;
+    QLabel*    _passwordLabel;
+    QLabel*    _mountPointsLabel;
+    QLabel*    _outFileLabel;
+    QLabel*    _outPortLabel;
+
+    QLineEdit* _proxyHostLineEdit;
+    QLineEdit* _proxyPortLineEdit;
+    QLineEdit* _userLineEdit;
+    QLineEdit* _passwordLineEdit;
+    QLineEdit* _outFileLineEdit;
+    QLineEdit* _outPortLineEdit;
+
+    QWidget*   _canvas;
+
+    QTableWidget* _mountPointsTable;
+
+    bncCaster* _bncCaster;
+};
+#endif
Index: /trunk/BNC/test_bnc.cpp
===================================================================
--- /trunk/BNC/test_bnc.cpp	(revision 35)
+++ /trunk/BNC/test_bnc.cpp	(revision 35)
@@ -0,0 +1,104 @@
+
+#include <iostream>
+#include <iomanip>
+
+#ifdef WIN32
+#include <winsock2.h>  // link with ws2_32.lib
+#endif
+
+using namespace std;
+
+const char begEpoch = 'A';
+const char begObs   = 'B';
+const char endEpoch = 'C';
+
+struct Observation {
+  char     StatID[5+1];    // Station ID
+  char     SVPRN;          // Satellite PRN
+  short    GPSWeek;        // Week of GPS-Time
+  int      GPSWeeks;       // Second of Week (GPS-Time)
+  double   sec;            // second 
+  double   C1;             // CA-code validated raw pseudorange (meters)
+  double   P2;             // P2-code validated raw pseudorange (meters)
+  double   L1;             // validated raw carrier phase (meters)
+  double   L2;             // validated raw carrier phase (meters)
+  short    SNR1;           // signal-to noise ration (0.1 dB)
+  short    SNR2;           // signal-to noise ration (0.1 dB)
+  int      pCodeIndicator; // 0 ... CA Code, 1 ... P Code
+  unsigned cumuLossOfCont; // 0 to 31
+} ;
+
+int main(int argc, char* argv[]) {
+
+  // Initialize Winsock
+  // ------------------
+  WSADATA wsaData;
+  int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+  if (iResult != NO_ERROR) {
+    cerr << "Error at WSAStartup" << endl;
+        return 1;
+  }
+
+  // Create a SOCKET for connecting to server
+  // ----------------------------------------
+  SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (ConnectSocket == INVALID_SOCKET) {
+    cerr << "Error at socket: " << WSAGetLastError() << endl;
+    WSACleanup();
+    return 1;
+  }
+
+  // The sockaddr_in structure specifies the address family,
+  // IP address, and port of the server to be connected to.
+  // -------------------------------------------------------
+  sockaddr_in clientService; 
+  clientService.sin_family = AF_INET;
+  clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
+  clientService.sin_port = htons(1968);
+
+  // Connect to server
+  // -----------------
+  if (connect( ConnectSocket, (SOCKADDR*) &clientService, 
+          sizeof(clientService) ) == SOCKET_ERROR) {
+    cerr << "Failed to connect" << endl;
+    WSACleanup();
+    return 1;
+  }
+
+  // Receive Data
+  // ------------
+  int bytesRecv = 0;
+  Observation obs;
+  char flag = ' ';
+  cout.setf(ios::showpoint | ios::fixed);
+  while (true) {
+    if (bytesRecv == SOCKET_ERROR) {
+      cerr << "recv failed: " << WSAGetLastError() << endl;
+      break;
+    }
+    else {
+      bytesRecv = recv( ConnectSocket, &flag, 1, 0 );
+      if (flag == begObs) {
+        bytesRecv = recv( ConnectSocket, (char*) &obs, sizeof(obs), 0);
+        cout << setw(5)                     << obs.StatID         << " "
+             << setw(2)                     << (int) obs.SVPRN    << " "
+             << setw(4)                     << obs.GPSWeek        << " "
+             << setw(6)                     << obs.GPSWeeks       << " "
+             << setw(8)  << setprecision(4) << obs.sec            << " "
+             << setw(14) << setprecision(4) << obs.C1             << " "
+             << setw(14) << setprecision(4) << obs.P2             << " "
+             << setw(14) << setprecision(4) << obs.L1             << " "
+             << setw(14) << setprecision(4) << obs.L2             << " "
+             << setw(4)                     << obs.SNR1           << " "
+             << setw(4)                     << obs.SNR2           << " "
+             << setw(4)                     << obs.pCodeIndicator << " "
+             << setw(4)                     << obs.cumuLossOfCont << endl;
+      }
+    }
+  }
+
+  WSACleanup();
+  return 0;
+}
+
+
