function Chnl()
{
   this.ch;
   this.ct;
   this.am;
   this.ts;
   this.lb;
   this.cn0;
   this.cr;
   this.st;
   this.mm;
}
function Sat()
{
   this.prn;
   this.az;
   this.el;
   this.chnl = [];
}

var GPSIDX =
{
   L1CA :0,
   L1P1 :1,
   L2   :2,
   L2C  :3,
   L5   :4
};
var GLNIDX =
{
   G1C  :0,
   G2C  :1,
   G1P  :2,
   G2P  :3
};

var XCODE =
{                    // list of supported codes (code-less) and frequencies 
   CW_L1       : 0,  // codeless L1
   CW_L2       : 1,  // codeless L2
   CW_L5       : 2,  // codeless L5
   CA_L1       : 3,  // C/A on L1
   CA_L2       : 4,  // C/A on L2
   L2CM        : 5,  // L2C medium code
   L2CL        : 6,  // L2C long code
   L5I         : 7,  // L5 I code
   L5Q         : 8,  // L5 Q code
   MEM_CODE_4  : 9,  // 4*1023 bit Memory code
   MEM_CODE_5  :10,  // 5*1023 bit Memory code
   P_L1        :11,  // clear P on L1
   P_L2        :12,  // clear P on L2
   Y_L1        :13,  // P(Y) and C/A on L1 P channel
   Y_L2Acq     :14,  // P(Y) acqisition on L2 x L1 on Y channel
   Y_L1Acq     :15,  // P(Y) acqisition on L1 x L1 on Y channel
   Y_L2Trk     :16,  // P(Y) Tracking on L2 x L1 on Y channel
   Y_L1Trk     :17,  // P(Y) Tracking on L1 x L1 on Y channel
   E5AI        :18,  // Galileo E5A I code (data messages)
   E5AQ        :19,  // Galileo E5A Q code (no data messages)
   E5BI        :20,  // Galileo E5B I code (data messages)
   E5BQ        :21,  // Galileo E5B Q code (no data messages)
   E1B         :22,  // Galileo E1 B code (data messages)
   E1C         :23,  // Galileo E1 C code (no data messages)
   E6B         :24,  // Galileo E6 B code (data messages)
   E6C         :25,  // Galileo E6 C code (no data messages)
   G1C         :26,  // BCG - GLONASS Civil G1 code (data messages)
   G2C         :27,  // BCG - GLONASS Civil G2 code (no data messages)
   G1P         :28,  // 28 WY  - GLONASS Military G1 Code
   G2P         :29,  // 29 WY  - GLONASS Military G2 Code
   // sphinx
      L1CD     :30,     // 30 WY  - L1C BOC(11)+BOC(61) for data channel
      L1CP     :31      // 31 WY  - L1C BOC(11)+BOC(61) for pilot channel
};

var trackings =
{
   IDLE               : 0,  // Channel is disabled (OFF)
   INVALID_START      : 1,  // The requested start was invalid or too far in future
   SIGDET_FAIL        : 2,  // Signal detection fail
   FREQ_VERIFY_FAIL   : 3,  // Frequency Verify fail
   BIT_SYNC_FAIL      : 4,  // Bit synchronization failed.
   WAIT_TO_START      : 5,  // Waiting for ASIC channel to start
   STARTING           : 6,  // Start command written to ASIC wait for CT to start
   STARTED            : 7,  // Channel starting
   MOVECH_START       : 8,  // Coherent Move channel Start-up
   HANDOVER_START     : 9,  // Coherent Handover Start-up   
   NCH_SIGDET_PROC    :10,  // Non-coherent Signal detection
   SIGDET_RESUME      :11,  // Resume signal detection from Frequency Verify
   COH_SIGDET_PROC    :12,  // Coherent Signal detection
   FREQ_VERIFY        :13,  // Verify detection frequency               
   Y_P_PULL_IN        :14,  // Pull-in of only PxP coders in Y mode
   Y_Y_PULL_IN        :15,  // Pull-in of PxY code and phase in Y mode
   AFC_INIT_PROC      :16,  // First cycle of AFC processing
   AFC_PROC           :17,  // AFC processing
   AFC_COSTAS_PROC    :18,  // AFC + Costas processing
   NCH_COSTAS_PROC    :19,  // Non-coherent Costas Loop
   BIT_SYNC_PROC      :20,  // Bit synchronization in progress
   FRAME_SYNC_PROC    :21,  // Frame synchronization in progress
   COSTAS_PROC        :22,  // Coherent Costas pull-in after synchronous start
   W_CODE             :23,  // w code tracking pull in
   CA_LOCKED          :24,  // Locked up for C/A satellites measurements ready
   COH_Y_TRACK        :25,  // Coherent Y Tracking measurements ready
   NOISE_TRACK        :26   // Dedicated noise tracking only state
};

var trackingStr = 
[   
   "IDLE",
   "INVD",
   "SGDF",
   "FRQF",
   "BTSF",
   "WAIT",
   "STRT",
   "STRD",
   "MOVE",
   "HAND",
   "NCHS",
   "SGDR",
   "COHS",
   "FREQ",
   "YPIN",
   "YYIN",
   "AFCI",
   "AFCP",
   "AFCC",
   "NCHC",
   "BTSP",
   "FRMS",
   "CSTS",
   "WCDE",
   "LOCK",
   "COHY",
   "NOIS"
];

function ISGPS(id)       { return (id >= 1) && (id <= 37); }
function ISGLONASS(id)   { return (id >= 37) && (id <= ( 37 + 24 )); }
function ISGALILEO(id)   { return (id >= 37 + 24) && (id <= (37 + 24 + 50)); }
function ISSBAS(id)      { return (id >= 120) && (id <= 138); }
function getSatType(prn)
{
   if(ISGPS(prn))
      return "GPS";
   else if(ISGLONASS(prn))
      return "GLONASS";
   else if(ISGALILEO(prn))
      return "GALILEO";
   else if(ISSBAS(prn))
      return "SBAS";
   else
      return "UNKNOWN";
}
function chnlstatus1b_obj()
{
   var MsgName = "[CHNLSTATUS1B]"
   
   this.update = function(input)
   {
      var start = input.indexOf(MsgName);

      //don't start parsing if this is not the right message
      if(start < 0)
      {
         return false;
      }
      this.name = MsgName;

      //init the parser
      var p = new parser(input, start + MsgName.length);

      //get the pvt1b info
      this.length       = p.fetchU16();
      this.week       = p.fetchU16();
      this.TOWms      = p.fetchU32();
      this.TOW        = this.TOWms/1000;

      this.ver        = p.fetchU04();
      this.conf       = p.fetchU04();
      
      tmp               = p.fetchU08();
      this.coopTrack    = (tmp >> 7) & 0x01;
      this.sfcn0        = (tmp & 0x3F);
      
      this.sfState      = p.fetchU08();
      this.sfSatID      = p.fetchU32();
      
      tmp               = p.fetchU08();
      this.startType    = (tmp >> 6) & 0x03;
      this.satsVisible  = (tmp & 0x3F);
      
      tmp               = p.fetchU08();
      this.posStat      = (tmp >> 6) & 0x03;
      this.satsTracked  = (tmp & 0x3F);
      
      tmp               = p.fetchU08();
      this.almAvailable = (tmp >> 7) & 0x01;
      this.satsInMsg    = (tmp & 0x3F);
      
      this.sats = [];
      
      if(this.satsInMsg > 0)
      {
         for(i=0; i<this.satsInMsg; i++)
         {
            this.sats[i] = new Sat;
            this.sats[i].prn = p.fetchU08();
            this.sats[i].satType = getSatType(this.sats[i].prn);
            
            num_ch_block = p.fetchU08();
            azel = p.fetchU16();
            this.sats[i].az = (azel >> 7) & 0x1FF;
            this.sats[i].el = (azel & 0x7F);
            
            for(j=0; j<num_ch_block; j++)
            {
               this.sats[i].chnl[j] = new Chnl();
               ch       = p.fetchU08();
               ct_am    = p.fetchU08();
               ts_lb    = p.fetchU08();
               cn0      = p.fetchU08();
               cr       = p.fetchU08();
               st_mm    = p.fetchU08();
               
               this.sats[i].chnl[j].ch    = ch;
               this.sats[i].chnl[j].ct    = (ct_am >> 2) & 0x3F;
               this.sats[i].chnl[j].am    = ct_am & 0x03;
               this.sats[i].chnl[j].ts    = (ts_lb >> 3) & 0x1F;
               this.sats[i].chnl[j].lb    = ts_lb & 0x07;
               this.sats[i].chnl[j].cn0   = cn0;
               this.sats[i].chnl[j].cr    = cr;
               this.sats[i].chnl[j].st    = (st_mm >> 2) & 0x3F;
               this.sats[i].chnl[j].mm    = st_mm & 0x03;
               if (this.sats[i].chnl[j].ct & 0x20)
               {
                  this.sats[i].chnl[j].ct &= 0x1F;
                  this.sats[i].chnl[j].ct |= 0x80; //hack
               }
               
               //is the P Code valid?
               this.sats[i].chnl[j].isP1 = ( (this.sats[i].chnl[j].ct & 0x80) != 0 );
               
               if(this.sats[i].chnl[j].ts >= 0 && this.sats[i].chnl[j].ts < trackingStr.length)
                  this.sats[i].chnl[j].trackingStr = trackingStr[this.sats[i].chnl[j].ts];
               else
                  this.sats[i].chnl[j].trackingStr = "NA";
               
               var idx = -1;
               switch(this.sats[i].chnl[j].ct & 0x7F)
               {
                  case XCODE.CW_L1:                       break;
                  case XCODE.CW_L2:                       break;
                  case XCODE.CW_L5: idx = GPSIDX.L5;      break;
                  case XCODE.CA_L1: idx = GPSIDX.L1CA;    break;
                  case XCODE.CA_L2:                       break;
                  case XCODE.L2CM:  idx = GPSIDX.L2C;     break;
                  case XCODE.L2CL:  idx = GPSIDX.L2C;     break;
                  case XCODE.L5I:   idx = GPSIDX.L5;      break;
                  case XCODE.L5Q:   idx = GPSIDX.L5;      break;
                  case XCODE.MEM_CODE_4:                  break;
                  case XCODE.MEM_CODE_5:                  break;
                  case XCODE.P_L1:     idx = GPSIDX.L1P1; break;
                  case XCODE.P_L2:     idx = GPSIDX.L2;   break;
                  case XCODE.Y_L1:
                     if(this.sats[i].chnl[j].isP1) idx = GPSIDX.L1P1;
                     else                          idx = GPSIDX.L1CA;
                     break;
                  case XCODE.Y_L2Acq: idx = GPSIDX.L2;    break;
                  case XCODE.Y_L1Acq: idx = GPSIDX.L1P1;  break;
                  case XCODE.Y_L2Trk: idx = GPSIDX.L2;    break;
                  case XCODE.Y_L1Trk: idx = GPSIDX.L1P1;  break;
                  case XCODE.E5AI:                        break;
                  case XCODE.E5AQ:                        break;
                  case XCODE.E5BI:                        break;
                  case XCODE.E5BQ:                        break;
                  case XCODE.E1B:                         break;
                  case XCODE.E1C:                         break;
                  case XCODE.E6B:                         break;
                  case XCODE.E6C:                         break;
                  case XCODE.G1C:  idx = GLNIDX.G1C;      break;
                  case XCODE.G2C:  idx = GLNIDX.G2C;      break;
                  case XCODE.G1P:  idx = GLNIDX.G1P;      break;
                  case XCODE.G2P:  idx = GLNIDX.G2P;      break;
               //sphinx
                  case XCODE.L1CD:                        break;
                  case XCODE.L1CP:                        break;
                  default:                          break;
               }
               this.sats[i].chnl[j].idx = idx;
            }
         }
      }
      
      
      return true;
   }
}