;+
; NAME:
;     DECODE
; PURPOSE:
;     Decode a HEADER, CONFIG, or OBSEQ segment and return the
;     appropriate structure.
; CATEGORY:
;     OVRO APC DATA ANALYSIS
; CALLING SEQUENCE:
;     struct = decode(data[,incode])
; INPUTS:
;     data      the data record to be decoded
;     incode    the segment code of the expected type of segment.
;                 If the data record segment code does not match
;                 INCODE, the return value is set to -1.
; OPTIONAL (KEYWORD) INPUT PARAMETERS:
; ROUTINES CALLED:
;     tl_decode get_head_struct get_cfg_struct get_obs_struct
;     rdbyte rdlong
; OUTPUTS:
;     struct    the properly filled data structure of the
;                 appropriate type, or -1 if a failure occurred.
; COMMENTS:
;     The HEADER structure is defined as follows (see get_head_struct)
;     ================================================================
;                {tls:      {tlfield},$    ; A time/label structure
;                 aatab:  intarr(  8),$    ; Active antenna table
;                 nptab:  intarr( 10),$    ; Antenna pointer table
;                 nant:             0,$    ; Number of active antennas
;                 rdoff:            0,$    ; 2-byte word offset of raw data
;                                          ;   within a data segment record
;                 nsr:              0,$    ; Number of samples per record
;                 nws:              0,$    ; Number of 2-byte words per sample
;                 ndbw:             0,$    ; Number of db words per sample
;                 nfgw:             0,$    ; Number of fg words per sample
;                 lchoff: intarr(8,8),$    ; ij channel offsets within sample
;                 lfgptr: intarr(8,8)}     ; Order of fg values in fg words
;
;     The CONFIG structure is defined as follows (see get_cfg_struct)
;     ===============================================================
;                 {antype:  intarr(8),$  ;Antenna type (0=none,
;                                        ;  1=27m, 2=2m equatorial,
;                                        ;  3= 2m alt-az, 4=other)
;                  antxyz:lonarr(3,8),$  ;Nominal ant locations (mm)
;                  muxarr:intarr(8,8),$  ;Baseline type: 0=AB,1=CA,-1=BC
;                  bx:    dblarr(8,8),$  ;Matrix of baseline x components (nsec)
;                  by:    dblarr(8,8),$  ;Matrix of baseline y components (nsec)
;                  bz:    dblarr(8,8),$  ;Matrix of baseline z components (nsec)
;                  htdiff:dblarr(8,8),$  ;Matrix of baseline height differences (nsec)
;                  antpol:intarr(2,8),$  ;Receiver poln codes (0=rcp,
;                                        ;  1=lcp, 2=vert linear,
;                                        ;  3=hor linear)
;                  feedrot: intarr(8),$  ;Feed rotation code
;                                        ;  (0=equatorial fixed,
;                                        ;   1=alt-az fixed)
;                  intvalms:        0,$  ;Interrupt interval (ms)
;                  sampintms:       0,$  ;Sample interval (ms)
;                  msoff:   intarr(8,8),$  ;Time offset for chan ij (ms)
;                  nintvals:intarr(8,8),$  ;Number of integ for chan ij
;                  momint:  intarr(8,8),$  ;First moment of I.T> for
;                                          ;  channel ij
;                  hifrqref:       0L,$  ;High freq reference (Hz)
;                  plofrq:         0L,$  ;Phase lock reference (Hz)
;                  dlamode:         0,$  ;Delay mode (0=fixed,
;                                        ;  1=tracking,2=stepping)
;                  dlastep:         0,$  ;Delay steps if delay mode 2
;                  ndlacyc:         0,$  ;Number of cycles between
;                                        ;  delay steps
;                  dlacent: intarr(8),$  ;Antenna delay centers (ns)
;                  bedlaoff:intarr(2),$  ;Backend channel delays (A,B)
;                  ut1mutc:        0L,$  ;UT1-UTC (ms)
;                  tdtmut1:        0L,$  ;TDT-UT1 (ms)
;                  clockerr:       0L,$  ;Clock error (ms)
;                  clockdrift:     0L,$  ;Clock drift (tbd)
;                  zencoder:lonarr(2,8),$  ;Encoder/secctor boundary
;                                          ;  zeroes for each antenna
;                  antoff:  lonarr(2,8),$  ;Fixed antenna offsets (mdeg)
;                  aligntab:lonarr(4,8),$  ;Antenna alignment parameters
;                                          ;  for each ant (millideg)
;                  refractivity:   0L,  $   ;Nominal refractivity
;                  muxseqid:         0L,$  ;MUX sequence number
;                  nmux:             0L,$  ;Number of elements in MUX sequence
;                  pmuxseq:  ptr_new(0),$  ;Pointer to array of sequence entries
;                  hodo:    lonarr(2,8),$  ;HO, DO offsets for each antenna
;                  hacorr: {hacorr,     $  ;HACORR structure
;                           n:intarr(2),$  ;  Number of non-zero values for each ant
;                       ha:lonarr(2,32),$  ;  List of HA values at which to apply
;                    corr:lonarr(2,32)},$  ; List of corrections
;                  chanphz: fltarr(8,8),$  ; Channel-based phases, differences for
;                                          ;  each baseline according to AB, BC, or CA
;                  bldir:intarr(8,8)*0+1}  ; Baseline direction flag (BLDIR=+1 if
;                                          ;  increasing 1-2 or 4-5, =-1 if 2-1 or 5-4)
;
;     The OBSEQ structure is defined as follows (see get_obs_struct)
;     ==============================================================
;                 {tics:             0,$  ; No. of intgr intvl/entry
;                  id:               0,$  ; Obseq id number
;                  nt:               0,$  ; No. of times obseq repeats
;                  nx:               0,$  ; No. of entries
;                  pharm:   ptr_new(0),$  ; ptr to array of harmonics
;                  ppol:    ptr_new(0),$  ; ptr to poln array
;                  pnd:     ptr_new(0),$  ; ptr to noise diode array
;                  phflag:  ptr_new(0),$  ; ptr to harmonic flag array
;                  phord:   ptr_new(0)}   ; ptr to ordered list of harms
;
;     The TRAJECTORY structure is defined as follows (see get_traj_struct)
;     ====================================================================
;                 {srcnam:'            ',$  ; Name of source being observed
;                  id:                 0,$  ; Trajectory number
;                  nrpt:               0,$  ; No. of times trajectory repeats
;                  nx:                 0,$  ; No. of lines in trajectory
;                  pDur:      ptr_new(0),$  ; ptr to array of durations
;                  pCmd:      ptr_new(0),$  ; ptr to array of command strings
;                  pDec:      ptr_new(0),$  ; ptr to array of declinations
;                  pHA:       ptr_new(0)}   ; ptr to array of hour angles
;
;     The EPHEMERIS structure is defined as follows (see get_eph_struct)
;     ==================================================================
;           {ephem, dut:              0,$  ; UT1-UTC
;                   solar:        solar,$  ; Solar structure
;                   lunar:        lunar,$  ; Lunar structure
;                   cal:            cal,$  ; Cal structure
;                   regn:          regn}   ; Regn structure
;
;       where the sub-structure definitions are
;       (see get_sol_struct)
;           { SOLAR,RA:    LONARR(3),$  ; Right ascension for each day [0.1 mdeg]
;                   DEC:   LONARR(3),$  ; Declination for each day [0.1 mdeg]
;                   RAD:   INTARR(3),$  ; Radius for each day      [0.1 mdeg]
;                   PA:    LONARR(3),$  ; P-Angle of solar disk    [0.1 mdeg]
;                   B0:    LONARR(3),$  ; B0-Angle of solar disk   [0.1 mdeg]
;                   HP:    INTARR(3),$  ; Horizontal parallax      [0.1 mdeg]
;                   REFT:  INTARR(3),$  ; Reference time (always 0)[sec]
;                   DOY:   INTARR(3)}   ; Reference day of year
;       (see get_lun_struct)
;           { LUNAR,RA:    LONARR(3),$  ; Right ascension for each day [0.1 mdeg]
;                   DEC:   LONARR(3),$  ; Declination for each day [0.1 mdeg]
;                   RAD:   INTARR(3),$  ; Radius for each day      [0.1 mdeg]
;                   HP:    INTARR(3),$  ; Horizontal parallax      [0.1 mdeg]
;                   REFT:  INTARR(3),$  ; Reference time           [sec]
;                   DOY:   INTARR(3)}   ; Reference day of year
;       (see get_cal_struct)
;           { CAL, EPOCH:  {epoch}  ,$  ; Epoch and other info from file
;                   NCAL:          0,$  ; Number of calibrators
;                 CALEPH:{caleph}(25)}  ; Array of ephemeris info
;       (see get_regn_struct)
;           { REGN, EPOCH: {epoch}  ,$  ; Epoch and other info from file
;                  NREGN:          0,$  ; Number of active regions
;                   INFO:  {info}(25)}  ; Array of region info
;
;       and the sub-sub-structure definitions are:
;                {EPOCH,  YR:      0,$  ; Year of the calibrator coords
;                         DOY:     0,$  ; Day of year of coords
;                         SEC:     0}   ; Hour of coords
;
;                {CALEPH, NAME:'        ',$  ; Name of calibrator (A8)
;                         RA:     0L,$  ; Right ascen. of calibrator [0.1 mdeg]
;                         DEC:    0L}   ; Declination of calibrator [0.1 mdeg]
;
;                {INFO,   NOA:     0,$  ; NOAA region number
;                         LAT:     0,$  ; Solar latitude of region [deg]
;                         LNG:     0,$  ; Solar longitude of region [deg]
;                         CARRLNG: 0,$  ; Carrington longitude [deg]
;                         AREA:    0,$  ; Sunspot are of region [10-6 of disk]
;                         TYPE:'    ',$  ; McIntosh classification (A4)
;                      MAGTYPE:'            '} ; Magnetic class (eg BETA-GAMMA) (A12)
;
;     The REFCAL structure is defined as follows (see get_refcal_struct)
;     ==================================================================
;           {refcal, pfrq:      ptr_new(0), $ ; Pointer to the frequency list
;                    nant:               0, $ ; Number of antennas in REFCAL
;                     nbl:               0, $ ; Number of baselines in REFCAL
;                    npol:               0, $ ; Number of different poln in REFCAL
;                  tls27m: get_tl_struct(), $ ; Time/Label structure for 27m meas.
;                  tls02m: get_tl_struct(), $ ; Time/Label structure for  2m meas.
;                    tlsi: get_tl_struct(), $ ; Time/Label structure for   I meas.
;                   tlsrl: get_tl_struct(), $ ; Time/Label structure for R/L meas.
;                pFactors:      ptr_new(0), $ ; Ptr to the fluxcal factors for NPOL
;                                             ;   polarizations, NANT+NBL channels,
;                                             ;   and NFRQ = n_elements(*pfrq) freqs
;                    pRMS:      ptr_new(0), $ ; Pointer to the RMS error corresp. to
;                                             ;   factors.
;                  pPhase:      ptr_new(0), $ ; Pointer to phases for NPOL poln,
;                                             ;   NBL channels, and NFRQ frequencies
;                 pFacUpd:      ptr_new(0), $ ; Ptr to the fluxcal update factors for NPOL
;                                             ;   polarizations, NANT+NBL channels,
;                                             ;   and NFRQ = n_elements(*pfrq) freqs
;                 pRMSUpd:      ptr_new(0), $ ; Pointer to the RMS error corresp. to
;                                             ;   update factors.
;              PhaseUpd: {tref:0L,          $ ; Reference time for phase update
;                        paref:ptr_new(0),  $ ; Ptr to phase-freq slope at ref time
;                                             ;   for NBL baselines
;                        pdadt:ptr_new(0)}, $ ; Ptr to time deriv of phase-freq slope
;                                             ;   for NBL baselines.
;                 pOffSun:      ptr_new(0)}   ; Pointer to off-Sun RCVR IF level,
;                                             ;   for NFEED feeds and NFRQ frequencies
;       where the sub-structure definitions are
;       (see get_tpcal_struct)
;           {TPCAL, TLS27M: GET_TL_STRUCT(),$ ; The TIME/LABEL structure for 27m TPCAL
;                   TLS02M: GET_TL_STRUCT(),$ ; The TIME/LABEL structure for  2m TPCAL
;                   TPFAC:    FLTARR(10,86),$ ; Factors for 10 feeds at 86 frequencies
;                   TPRMS:    FLTARR(10,86)}  ; The errors in the factors.
;       (see get_ampcal_struct)
;           {ampcal, TLSI: GET_TL_STRUCT(), $ ; TIME/LABEL structure for I measurements
;                   TLSRL: GET_TL_STRUCT(), $ ; TIME/LABEL structure for R/L meas.
;                 FACTORS:   FLTARR(10,86), $ ; Amplitude factors for 10 feeds at 86 frq
;                     RMS:   FLTARR(10,86)}   ; RMS error for 10 feeds at 86 frequencies
;       (see get_phzcal_struct)
;           {phzcal, PHASE:  FLTARR(10,86)}   ; Phases (deg) for 10 feeds at 86 freq
;
; SIDE EFFECTS:
; RESTRICTIONS:
; MODIFICATION HISTORY:
;     Written 18-Jan-1998 by Dale E. Gary
;     22-Jan-1988  DG
;       Made incode input optional, and added unknown code error handling.
;     24-Jun-1998  DG
;       Added calculation for baseline lengths and htdiff in config segment.
;     24-Aug-1998  DG
;       Added TRAJECTORY segment
;     26-Jan-1999  DG
;       Added a time/label structure field to the HEADER structure
;     02-Jul-1999  DG
;       Added baseline corrections from 1999 Jun 18 baseline observations.
;     27-Jul-1999  DG
;       Added REFCAL et al.
;     13-Sep-1999  DG
;       Added code to handle the new TPCAL segment, which has 2 records,
;       although it still can handle the old TPCAL segment at the same time.
;     14-Sep-1999  DG
;       Had to change 27-m scale factor for TPCAL data back to 10 to prevent
;       overflow, but leave the 2-m scale factor at 100.
;     17-Nov-1999  DG
;       Add handling of TPUPDATE data (result of CTRCAL)
;     25-Nov-1999  DG
;       Updated to agree with new Epoch defn.  Add reading of GPARM segment.
;     01-Dec-1999  DG
;       Added reading of INDEX segment.
;     03-Feb-2000  DG
;       Fixed bug that did not read 10th record of INDEX segment correctly.
;     23-Mar-2000  DG
;       Entered new baseline corrections, after source coordinates are finally
;       under contol.
;     14-Apr-2000  DG
;       Get rid of C:\TEMP references (use !Defaults.workdir)
;     09-May-2000  DG
;       Needed NANT for proper decoding of baselines, so pass in HEADER
;       as a keyword, when calling DECODE to decode a CONFIG segment.
;     07-Jun-2000  DG
;       Add decoding of DAILYPHZ segment
;     28-Jun-2000  DG
;       Change MUX for 3-element in CONFIG decoding, to be completely general.
;     06-Jul-2000  DG
;       Moved baseline corrections to a new routine, BLCOR().
;     16-Jul-2000  DG
;       Added code associated with new entry MUXARR in CFG structure.
;     26-Sep-2000  DG
;       Major change--I realized that I could change the sign on the BCsin
;       component of the data (done in GAINCOR), and then I no longer need
;       to reverse the sign of the BC baseline coordinates, nor do I need
;       different closure phase relations depending on MUX mode.  This
;       simplifies a lot of things!
;     13-Jan-2001  DG
;       Decoded MUX sequence, that has been written into the data since
;       15 Aug 2000.  Also changed the index of refraction correction to
;       0.000285 to agree with value written to CONFIG segment.
;     13-Mar-2001  DG
;       Fix bug that occurred when the Segment list in the INDEX filled the record.
;     19-Mar-2001  DG
;       I added this on 2 Mar, but that version got overwritten, so here it is
;       again... DECODE now checks whether the EPHEM record is flagged with a
;       255 in the last byte (for entries earlier than day 82 of 2000, and if
;       so, skips the spawn of PRECESS.  If not flagged, it spawns PRECESS, then
;       corrects the DATA record for the new coordinates and writes a copy to
;       !DEFAULTS.WORKDIR+'EPHEM.ddd'.  This record can then be inserted into
;       the .ARC file so that subsequent readings will not require spawning
;       PRECESS.
;     26-Apr-2001  DG
;       I had a case where a non-standard frequency was included, and the
;       TPUPDATE value is zero for non-standard frequencies so when applied
;       it gave an infinite value.  Instead, missing values should be 1, so
;       that when applied they have no net effect.  This is done on decoding
;       the TPUPDATE segment.
;     15-Jan-2002  DG
;       Added decoding of new HACORR data in CONFIG segment.
;     04-Apr-2002  DG
;       Added newly discovered channel phases (phi_a = 0., phi_b = -50 degrees,
;       and phi_c = 95 degrees), in CHANPHZ array added to config segment.
;       These channel-based phases may change from time to time, but only
;       repeated measurements will tell.  The phases are applied in GAINCOR.
;     05-May-2002  DG
;       After new measurements, and finding a bug in my GAINCOR code, I changed
;       the channel phases here to baseline-based channel phases, e.g. phi_ab,
;       phi_bc, and phi_ca, and determined new values for them (which are very
;       consistent with the old values.
;     06-Jul-2002  DG
;       Added code to analyze the new MUX mode 15 channel phase cal data.
;       After analyzing new MUX mode 15 data, which allows to use the
;       Sun for channel phase calibration, I was able to update the channel
;       phases with some very good chance that it is now correct.
;     07-Oct-2002  DG
;       Recoded determination of baseline type (AB, BC, and CA), and made the
;       baseline coordinates depend on the order of antennas, which was
;       necessary in the new MUX schemes.  Our earlier MUX arrangements
;       ALWAYS used increasing antenna numbers, so it was fine to do the
;       baselines by counting up from low to high antenna numbers, but the
;       new schemes are more flexible, e.g. 721 instead of 127, so the sign
;       of the baseline has to depend explicitly on the ordering.
;     07-Nov-2002  DG
;       Introduce BLDIR entry into CONFIG structure, to keep track of which
;       order the baseline data were taken in.  The convention that BLDIR is
;       positive for increasing antenna number (e.g. 1-2 or 4-5) and negative
;       for the inverse order (2-1 or 5-4) will be applied in GAINCOR.  This
;       is necessary so that data taken with different order can be compared.
;     17-Dec-2002  DG
;       Fix the special code for chanphz calibration (MUX sequence 15), which
;       was broken in the October changes.
;     23-Dec-2002  DG
;       Add code for chanphz calibration MUX sequence 17.
;     27-Dec-2002  DG
;       The antenna position for antenna 7 was changed in the FASAR (CPC)
;       program on 26-Dec-2002, to compensate for the huge baseline errors
;       that had been placed in GET_BLCOR.  The GET_BLCOR errors were
;       subsequently reduced, but to allow GET_BLCOR to work for Ant 7 data
;       prior to 27-Dec-2002, the CONFIG segment values for Ant 7 position
;       are overridden here.
;     03-Jan-2003  DG
;       Changed channel phase values to accord with now correct (I hope)
;       values deduced from measurements.  The earlier values were wrong.
;     04-Jan-2003  DG
;       Doh! The values of yesterday were wrong, too.  Now hopefully correct.
;     11-Jan-2003  DG
;       Still some problems.  I decided to change to specify the AB, BC,
;       and CA channel offsets, rather than relative to AB.  They obey
;       the closure relation (AB + BC - CA = 0), so only two are really
;       needed, but it is conceptually easier to keep all three.
;     28-Oct-2003  DG
;       Change ANTPOL to reflect feed changes on Ants 1 and 2
;     14-Nov-2003  DG
;       Change decoding of GPARM segment to deal with case of 6-record
;       segments (used with new feeds).
;     12-Feb-2003  DG
;       Change ANTPOL to override config values for Ant 4, from 18 Nov 2003
;       onward, due to addition of dual-polarization feed
;     21-Mar-2004  DG
;       Change ANTPOL to override config values for Ant 5, from 18 Mar 2004
;       onward, due to addition of dual-polarization feed.  Also change values
;       for Ant 6, slated to start tomorrow (22 Mar 2004).
;     18-Jul-2004  DG
;       Fix bug in decoding GPARM segment, when there are 6 records.  GPARM
;       segment has both R and L data, but DECODE was putting R values in
;       both R and L locations of GAINPARM.PGCAL.
;     07-Aug-2004  DG
;       Added decoding of satellite ephemeris from TRAJECTORY segment
;     12-Aug-2004  DG
;       Change ANTPOL to override config values for Ant 7, from 11 Aug 2004
;       onward, due to addition of dual-polarization feed.
;     11-Sep-2004  DG
;       Change ANTPOL to override config values for Ant 8, from 18 Aug 2004
;       onward, due to addition of dual-polarization feed.  Start on code
;       for analysis of new TPCAL, AMPCAL, PHZCAL, TP-UPDATE segments.
;     25-Oct-2004  DG
;       Fixed bug in which I divided DOY by 100 instead of 1000 in
;       determining when new GPARM went into effect.
;     09-May-2006  DG
;       Fixed bug in TP-UPDATE section.
;     12-Nov-2006  DG
;       Slightly updated for "new" CHANPHZ values (largely the same).
;     18-Jun-2007  DG
;       Finally finished code for analysis of AMPCAL, PHZCAL, which had been
;       forgotten (so RCP was okay, but not LCP).
;     27-Feb-2010  DG
;       Fixed bug in decoding of AMPCAL and PHZCAL data that affected older
;       data when these were used in REFCAL structure by NEWSCAN.
;-

;-----------------------------------------------------------------------------

pro rdbyte,array,data,offset

; Read byte values from I*2 DATA array, starting at
; word offset OFFSET, into output array ARRAY.  The number of values
; to read from the DATA array is determined from the size of ARRAY
; on input.

   ; Get number of elements in ARRAY.  Ensure that ARRAY size is at
   ; least 2 to avoid an error.
   ntot = n_elements(array)>2

   ; Convert words from DATA into two bytes, with appropriate masking
   ; and shifting.
   for i = 0, ntot/2 - 1 do begin
      j = i*2
      array(j)   = data(offset+i) and 'FF'x     ;mask low byte
      array(j+1) = ishft(data(offset+i),-8)     ;shift high byte
   endfor

return
end

;-----------------------------------------------------------------------------

pro rdlong,array,data,offset

; Read long (I*4) values from I*2 DATA array, starting at
; word offset OFFSET, into output array ARRAY.  The number of values
; to read from the DATA array is determined from the size of ARRAY
; on input.


   ; Get number of elements in ARRAY.  Ensure that ARRAY size is at
   ; least 1, to avoid an error
   ntot = n_elements(array)>1

   ; Use W2L routine to do the work.
   for i = 0, ntot - 1 do begin
      j = i*2
      array(i)   = w2l(data,offset+j)
   endfor

return
end

;-----------------------------------------------------------------------------

function decode,data,incode,header=header

   CMM      = 2.997925D2          ; Speed of light [mm/nsec]
   CSLAT    = 0.79619623D         ; Cosine of OVRO latitude (37d13'53.8")
   SNLAT    = 0.60503848D         ; Sine of OVRO latitude (37d13'53.8")

   ; Verify that data is of expected type.  If incode was not specified,
   ; any type will be decoded.
   tls = tl_decode(data)
   IF (n_elements(incode) EQ 0) THEN incode = tls.segmentcode
   IF (tls.segmentcode NE incode) THEN return,-1

   ; Branch on segment type
   CASE tls.segmentcode OF

   !SEGM.HEADER: BEGIN

      ; Get definition of HEADER structure
      h = get_head_struct()

      ; Time/Label structure for Header record
      h.tls = tls
      ; Active antenna table
      aatab = h.aatab
      rdbyte,aatab,data,12
      h.aatab = aatab

      ; Antenna pointer table (nptab[aatab[i]] is pointer
      ; to the ith antenna's data)
      nptab = h.nptab
      rdbyte,nptab,data,16
      h.nptab = nptab

      h.nant = data(21)
      h.rdoff = data(22)
      h.nsr = data(23)
      h.nws = data(24)
      h.ndbw = data(25)
      h.nfgw = data(26)

      ; Channel offsets within each sample
      lchoff = h.lchoff
      rdbyte,lchoff,data,27
      h.lchoff = lchoff

      ; Order of FG words within each sample
      lfgptr = h.lfgptr
      rdbyte,lfgptr,data,59
      h.lfgptr = lfgptr

      return,h
      END
   !SEGM.CONFIG: BEGIN

      ; Get definition of CONFIG structure
      c = get_cfg_struct()

      ; Array of antenna types
      antype = c.antype
      rdbyte,antype,data,12
      c.antype = antype

      ; Array of antenna positions (X = East, Y = North, Z = Up)
      antxyz = c.antxyz
      rdlong,antxyz,data,16
      c.antxyz = antxyz

	  ; Correct for the position of Antenna 7, which was wrong
	  ; prior to 27-Dec-2002
	  if (tls.yrday lt 2002.361) then begin
	     ; Verify that Ant 7 is present, and wrong
	     if (c.antxyz[2,5] eq -12500L) then begin
	        c.antxyz[1,5] = 127903L
	        c.antxyz[2,5] = -12304L
	     endif
	  endif
	  ; Correct for the position of Antenna 8, which was wrong
	  ; prior to 06-Sep-2004
	  if (tls.yrday lt 2004.249) then begin
	     ; Verify that Ant 8 is present, and wrong
	     if (c.antxyz[2,6] ne -8708L) then begin
	        c.antxyz[0,6] = 1035728L
	        c.antxyz[1,6] = -91609L
	        c.antxyz[2,6] = -8708L
	     endif
	  endif

      ; MUX sequence (only found in data after 15 Aug 2000)
      if (tls.yrday gt 2000.228) then begin
         c.muxseqid = w2l(data,393)
         if (c.muxseqid gt 2000) then begin
            ; Valid Mux Sequence number, so interpret rest of MUX sequence
            ptr_free,c.pmuxseq
            c.muxseqid = c.muxseqid-2000
            c.nmux = w2l(data,397)
            muxseq = intarr(c.nmux)
            ; The locations for storage of the MUX sequence changed
            ; during changeover from 6 to 7 element, day 280 of 2002.
            seqstart = 14
            if (tls.yrday gt 2002.280) then seqstart = (c.nmux-1)*2
            for i = 0, c.nmux-1 do begin
               muxseq[i] = w2l(data,399+seqstart-i*2)
            endfor
            c.pmuxseq = ptr_new(muxseq)
         endif
      endif

      ; Get current baseline corrections, as [X,Y,Z] in nsec for each of eight
      ; antennas.  Unused antennas have zeroes.  BLCOR is of size [3,8].
      blcor = get_blcor()

      nant = header.nant
      iant = [1,2,4,5,6,7,8]

      ; Some baselines need to have their signs reversed from the nominal,
      ; due to physical arrangement of twisted pair wire connections from
      ; the backend.  Which baselines these are depend on the MUX mode.
      ; Before 15 Aug 2000, this was *not* recorded!  The following sets up the
      ; currently available MUX modes, based on antenna number.
      if (tls.yrday le 2000.228) then begin
         CASE nant OF
           2: MUX = '010'
           3: BEGIN
                antidx = intarr(3)
                for i = 0, 2 do antidx[i] = where(iant eq header.aatab[i])
                MUX = string(antidx,format='(3I1)')
              END
           5: MUX = ['012','013','014','234']
           6: MUX = ['235','234','012','013','045','145']
           ELSE: MUX = '000'
         ENDCASE
      endif else begin
         MUX = (*c.pmuxseq)
         good = where(MUX gt 0,ngood)
         if (ngood gt 0) then MUX = MUX[good] else MUX = 124
         antidx = intarr(3)
         MUX = string(MUX,format='(i3)')   ; Convert to strings
         nmux = n_elements(MUX)
         ; Convert MUX values in antenna number to MUX values in antenna index
         for j = 0, nmux-1 do begin
            for i = 0, 2 do antidx[i] = where(iant eq strmid(MUX[j],i,1))
            mux[j] = string(antidx,format='(3i1)')
         endfor
      endelse

      if (tls.yrday gt 2002.180 and c.muxseqid eq 0) then begin
         ; The data are from after June 2002, and the MUXSEQID is zero
         ; (it should be 15), so hardwire the correct mux sequence
         MUX = ['014','015','045','145']
         c.muxseqid = 15
         c.nmux = 5
         c.pmuxseq = ptr_new(long([-126,126,127,167,267]))
         ; Replace ant 4 data with ant 6 data, and ant 5 data with ant 7 data
         c.antxyz[*,2] = c.antxyz[*,4]
         c.antxyz[*,3] = c.antxyz[*,5]
         blcor[*,2] = blcor[*,4]
         blcor[*,3] = blcor[*,5]
      endif
      if (c.muxseqid eq 17) then begin
         ; Data for antennas 1, 2 and 4 are placed into locations for
         ; antennas 6, 7 and 8 in this special mode, so put coordinates
         ; from 1, 2 and 4 into locations for 6, 7 and 8.
         c.antxyz[*,4:6] = c.antxyz[*,0:2]
         blcor[*,4:6] = blcor[*,0:2]
      endif

      ; Create MUXARR entry to CONFIG structure, which will contain
      ; +1 where the baseline is CA, -1 where the baseline is BC, and
      ; zero otherwise.
      muxarr = intarr(nant,nant)

      ; Newly discovered (Apr 2002) A,B,C channel phases, which must
      ; be applied according to which channel a baseline was measured on.
      phi_ab = 25           ; Phase of AB (2003 Jan 04)
      phi_bc = 125.00       ; phase of BC (2003 Jan 04)
      phi_ca = 150.00       ; phase of CA (2003 Jan 04)
      if (tls.yrday gt 2005.180) then begin
         ; Slight change inferred from new measurements--not sure how long
         ; ago they relate to, but at least to 2005 Jul 26.
         phi_ab = 28           ; Phase of AB (2006 Nov 12)
         phi_bc = 122.00       ; phase of BC (2006 Nov 12)
      endif

      ; Create matrix of baselines, with convention that baseline
      ; Bx = ANTjx - ANTix. i.e. the baseline vector for baseline ij
      ; goes from ANTi to ANTj.

      ; Determine index pairs of ab, bc, and ca baselines, based on MUX array
      ab = strmid(mux,0,2)
      bc = strmid(mux,1,2)
      ca = strmid(mux,0,1)+strmid(mux,2,1)

      ; Additional stuff to fix in this special mode...
      if (c.muxseqid eq 15) then begin
         ; Some data channels are swapped in this MUX mode, so we have
         ; to fix a couple of baseline identifications for certain channels.
         ab[2] = '02'
         ab[3] = '12'
         ca[3] = '13'
      endif
      if (c.muxseqid eq 17) then begin
         ; Some data channels are swapped in this MUX mode, so we have
         ; to fix a couple of baseline identifications for certain channels.
         ab = ['21','56','41','03']
         bc = ['10','60','40','40']  ; These last two are irrelevant, '40' is a place holder
         ca = ['20','50','43','40']  ; The last one is irrelevant
      endif

      ; Use index pairs as indices, to set CA baseline flag to 1 and CHANPHZ to PHI_CA
      c.muxarr[strmid(ca,0,1),strmid(ca,1,1)] = (c.muxarr[strmid(ca,1,1),strmid(ca,0,1)] = 1)
      c.chanphz[strmid(ca,0,1),strmid(ca,1,1)] = (c.chanphz[strmid(ca,1,1),strmid(ca,0,1)] = phi_ca*!dtor)

      ; Use index pairs as indices, to set BC baseline flag to -1 and CHANPHZ to PHI_BC
      c.muxarr[strmid(bc,0,1),strmid(bc,1,1)] = (c.muxarr[strmid(bc,1,1),strmid(bc,0,1)] = -1)
      c.chanphz[strmid(bc,0,1),strmid(bc,1,1)] = (c.chanphz[strmid(bc,1,1),strmid(bc,0,1)] = phi_bc*!dtor)

      ; Use index pairs as indices, to set AB baseline flag to 0 and CHANPHZ to PHI_AB
      c.muxarr[strmid(ab,0,1),strmid(ab,1,1)] = (c.muxarr[strmid(ab,1,1),strmid(ab,0,1)] = 0)
      c.chanphz[strmid(ab,0,1),strmid(ab,1,1)] = (c.chanphz[strmid(ab,1,1),strmid(ab,0,1)] = phi_ab*!dtor)

      bl = [[ab],[bc],[ca]]
      nmux = n_elements(mux)

      ; Have to take care of 2-element case, when nant = 2
      if (nant eq 2) then begin
         nbl = 1
         if (bc[0] eq '10') then bl = [[bc]] ; Case of 421 mode
      endif else nbl = 3

      ; Loop over pairs of indexes
      for ibl = 0, nbl-1 do begin

         jbl = reform(bl[*,ibl])  ; JBL is list of pairs of indexes, AB first, then BC, then CA

         ; Loop over mux setting
         for i = 0, nmux-1 do begin
            j = strmid(jbl[i],0,1)
            k = strmid(jbl[i],1,1)

            ; To ensure that data are comparable when taken in different
            ; order (e.g. 1-2 vs 2-1, or 4-5 vs 5-4), we make the convention
            ; (applied in GAINCOR) that baselines are positive when the
            ; antenna numbers increase (1-2 or 4-5) and negative when in the
            ; opposite order.  Keep track of this "baseline direction" with
            ; BLDIR flag.
            if (j gt k) then begin
               c.bldir[j,k] = -1
               c.bldir[k,j] = -1
            endif

            ; First get East, North, Up coordinates for baseline ij [nsec]
            be = (c.antxyz(0,k) - c.antxyz(0,j))/CMM
            bn = (c.antxyz(1,k) - c.antxyz(1,j))/CMM
            bu = (c.antxyz(2,k) - c.antxyz(2,j))/CMM

            ; The height difference for each baseline can be used to
            ; correct for atmospheric refraction (refractive index n,
            ; where n - 1 = 0.000285).
            c.htdiff[k,j] = (c.htdiff[j,k] = (bu*0.000285D))

            ; Convert baseline coordinates from east, north, up to x,y,z
            ; system of coordinates [nsec]
            bx =   bn*SNLAT - bu*CSLAT
            by = - be
            bz = - bn*CSLAT - bu*SNLAT

            ; Add baseline corrections obtained earlier from BLCIN function.
            c.bx[j,k] = (c.bx[k,j] = (bx+(blcor[0,j]-blcor[0,k])))
            c.by[j,k] = (c.by[k,j] = (by+(blcor[1,j]-blcor[1,k])))
            c.bz[j,k] = (c.bz[k,j] = (bz+(blcor[2,j]-blcor[2,k])))

         endfor
      endfor

      ; Array of antenna polarization codes
      antpol = c.antpol
      rdbyte,antpol,data,64
      c.antpol = antpol

      ; On Sep 08, 2003, Ant 1 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2003.252 and tls.yrday lt 2003.310) then c.antpol[0,0] = 0
      ; On Sep 22, 2003, Ant 2 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2003.266 and tls.yrday lt 2003.310) then c.antpol[1,1] = 1
      ; On Nov 18, 2003, Ant 4 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2003.322) then c.antpol[*,2] = [0,1]
      ; On Mar 18, 2004, Ant 5 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2004.078) then c.antpol[*,3] = [0,1]
      ; On Mar 22, 2004, Ant 6 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2004.082) then c.antpol[*,4] = [0,1]
      ; On Aug 11, 2004, Ant 7 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2004.224) then c.antpol[*,5] = [0,1]
      ; On Aug 18, 2004, Ant 8 feed was changed to R and L, so change antpol accordingly
	  if (tls.yrday ge 2004.231) then c.antpol[*,6] = [0,1]

      ; Array of feed rotation parameters
      feedrot = c.feedrot
      rdbyte,feedrot,data,72
      c.feedrot = feedrot

      ; Interrupt interval in ms
      c.intvalms = data(76)

      ; Sample interval in ms
      c.sampintms = data(77)

      ; Array of time offsets (ms) for channels
      c.msoff = data(78:141)

      ; Number of integrations per channel
      nintvals = c.nintvals
      rdbyte,nintvals,data,142
      c.nintvals = nintvals

      ; First moment of integration time for channels
      c.momint = data(174:237)

      c.hifrqref = w2l(data,238)
      c.plofrq   = w2l(data,240)
      c.dlamode = data(242)
      c.dlastep = data(243)
      c.ndlacyc = data(244)

      ; Array of delay centers for channels (dlacent) and backend
      c.dlacent  = data(245:252)
      c.bedlaoff = data(253:254)

      c.ut1mutc    = w2l(data,255)
      c.tdtmut1    = w2l(data,257)
      c.clockerr   = w2l(data,259)
      c.clockdrift = w2l(data,261)

      zencoder = c.zencoder
      rdlong,zencoder,data,263
      c.zencoder = zencoder

      antoff = c.antoff
      rdlong,antoff,data,295
      c.antoff = antoff

      aligntab = c.aligntab
      rdlong,aligntab,data,327
      c.aligntab = aligntab

      c.refractivity = w2l(data,391)

      for i = 0, 7 do begin
         for j = 0, 1 do begin
            k = (i*2 + j)*2
            c.hodo[j,i] = w2l(data,425+k)
         endfor
      endfor
      for k = 0, 32, 2 do c.hacorr.ha[0,k/2] = w2l(data,457+k)
      for k = 0, 32, 2 do c.hacorr.ha[1,k/2] = w2l(data,521+k)
      for k = 0, 32, 2 do c.hacorr.corr[0,k/2] = w2l(data,585+k)
      for k = 0, 32, 2 do c.hacorr.corr[1,k/2] = w2l(data,649+k)

      blah = where(c.hacorr.ha[0,*] ne 0,ngood)
      c.hacorr.n[0] = ngood
      blah = where(c.hacorr.ha[1,*] ne 0,ngood)
      c.hacorr.n[1] = ngood

      return,c
      END
   !SEGM.GPARM: BEGIN

      ; Get definition of GPARM structure
      gainparm = get_gparm_struct()

      ; Fill in EPOCH part of structure
      gainparm.epoch.yr = data[12]
      gainparm.epoch.doy = data[13]
      gainparm.epoch.sec = fix(w2l(data[14:15],0)/1000L)  ; Convert [msec] to [sec]

      ; Determine number of records in GPARM segment based on date
      if (data[12]+ data[13]/1000. ge 2003.310) then n_rec=6 else n_rec=3

      ; Fill in OFFSETS part of structure (all 64 possible offsets at this stage)
      ptr_free,gainparm.poffsets
      offsets = fltarr(64)
      rdbyte,offsets,data,64
      ; Do not wrap total power offsets (at locations n*9)
      not_tp = offsets  ; Save offsets in NOT_TP
      not_tp[indgen(8)*9] = 0  ; Set TP offsets to 0 in NOT_TP
      wrap = where(not_tp gt 128,nwrap)
      if (nwrap gt 0) then offsets(wrap) = offsets(wrap)-256
      offarr = fltarr(8,8)
      offarr(0) = offsets/10.
      ; FIx problem of inverted sin/cos channels.
      blah = offarr
      for i = 0,7 do begin
         for j = i,7 do begin
            blah[i,j] = offarr[j,i]
            blah[j,i] = offarr[i,j]
         endfor
      endfor
      gainparm.poffsets = ptr_new(blah)
      ; Fill in GNP part of structure (all 8 possible antennas at this stage)
      ptr_free,gainparm.pgnp
      gainparm.pgnp = ptr_new(replicate({gnp},8))
      for i = 0, 7 do begin
         (*gainparm.pgnp)[i].nlparm = data[16+6*i]/1000.
         (*gainparm.pgnp)[i].attn   = data[17+6*i:19+6*i]/1000.
         (*gainparm.pgnp)[i].ndfac  = data[20+6*i]/1000.
         (*gainparm.pgnp)[i].resid  = data[21+6*i]/1000.
      endfor

      ; Fill in DLCAL part of structure
      gainparm.dlcal = data[927:982]/1000.

      ; Fill in GCAL part of structure (all 8 possible antennas and all 86 possible
      ; frequencies at this stage)
      ptr_free,gainparm.pgcal

      ; Declare storage according to whether there are 3 or 6 records in GPARM segment
      if (n_rec eq 6) then begin
         gainparm.pgcal = ptr_new(replicate({gcal},8,86,2))
         kmax = 1
      endif else begin
         gainparm.pgcal = ptr_new(replicate({gcal},8,86))
         kmax = 0
      endelse

      ; Convert the GCAL data block to float and replace bad values with NaN
      fdata = float(uint(data[984:*]))
      bad = where(uint(data[984:*]) eq 'FFFF'x,nbad)
      if (nbad gt 0) then fdata[bad] = !values.f_nan

      ; This assumes the three records have been concatenated and the Time/Label
      ; field of each record has been removed.
      for k = 0, kmax do begin
         for j = 0, 85 do begin
            for i = 0, 7 do begin
               koff = 3*i+24*j+2064*k
               (*gainparm.pgcal)[i,j,k].rcvr  = fdata[koff  ]/10.
               (*gainparm.pgcal)[i,j,k].nd    = fdata[koff+1]/10.
               (*gainparm.pgcal)[i,j,k].resid = fdata[koff+2]/1000.
            endfor
         endfor
      endfor

      return, gainparm
      END
   !SEGM.INDEX: BEGIN
      ; DATA should contain 10 records (entire INDEX segment)
      idx = get_index_struct()
      idx.recn = data[0]
      inext = 12
      nbytperentry = uint(data[inext]) mod 256  ; Number of fixed length bytes/entry
      idx.n_templ = ishft(uint(data[inext]),-8) ; Number of template code entries
      template_codes = intarr(idx.n_templ)
      rdbyte,template_codes,data,inext+1        ; Extract the template codes from DATA
      idx.codes[0:idx.n_templ-1] = template_codes  ; Store template codes in structure
      inext = inext+idx.n_templ/2+1             ; Increment pointer to next data
      nwdperentry = nbytperentry/2+idx.n_templ  ; Number of words/entry, incl var part

      iscn = 0
      scn = replicate({scntry},300)
      while data[inext] ne 0 do begin
         scn[iscn].scancode = uint(data[inext]) mod 256
         scn[iscn].segcode  = ishft(uint(data[inext]),-8)
         scn[iscn].srec     = data[inext+1]
         scn[iscn].erec     = data[inext+2]
         scn[iscn].syear    = data[inext+3]
         scn[iscn].sday     = data[inext+4]
         scn[iscn].smsec    = w2l(data,inext+5)
         scn[iscn].eyear    = data[inext+7]
         scn[iscn].eday     = data[inext+8]
         scn[iscn].emsec    = w2l(data,inext+9)
         scn[iscn].srcname  = string(byte(data(inext+11:inext+16),0,12))
         scn[iscn].hao      = data[inext+17]/1000.
         scn[iscn].deco     = data[inext+18]/1000.
         scn[iscn].recs     = data[inext+19:inext+19+idx.n_templ-1]
         inext = inext+nwdperentry
         ; If there is no room for another entry in this record, skip to next
         ; record
         if ((inext mod 1024) gt (1024-nwdperentry)) then $
            inext = (inext/1024 + 1)*1024+12+idx.n_templ/2+1
         iscn = iscn+1
      endwhile
      idx.nscans = iscn
      if (iscn gt 0) then begin
         ptr_free,idx.pscan
         idx.pscan = ptr_new(scn[0:iscn-1])
      endif

      ; Now fill in special segment part of index structure
      inext = 9*1024L+(idx.n_templ/2+1) + 12   ; Skip to start of data in last record
      iseg = 0
      seg = replicate({sgntry},300)
      while data[inext] ne 0 do begin
         seg[iseg].segcode  = data[inext]
         seg[iseg].srec     = data[inext+1]
         seg[iseg].syear    = data[inext+2]
         seg[iseg].sday     = data[inext+3]
         seg[iseg].smsec    = w2l(data,inext+4)
         inext = inext+6
         if (inext ge n_elements(data)) then inext = 0  ; Force exit when segment list fills record
         iseg = iseg+1
      endwhile
      idx.nsegs = iseg
      if (iseg gt 0) then begin
         ptr_free,idx.psegs
         idx.psegs = ptr_new(seg[0:iseg-1])
      endif
      return, idx
      END
   !SEGM.TPCAL: BEGIN

      ; Get definition of TPCAL structure
      tpcal = get_tpcal_struct()

      ; Get the TIME/LABEL structures
      tpcal.tls27m = tl_decode(data[12:*])
      tpcal.tls02m = tl_decode(data[24:*])
      fac = bytarr(20)

      ; Check the size of data, and react accordingly
      CASE n_elements(data) OF
       1024: begin
         ; Case of old TPCAL segment, containing only 1 record
         ; Loop over frequency
         for i = 0, 85 do begin

            ; Unpack the bytes of tpcal data
            rdbyte,fac,data,36+10*i

            ; Replace bad data with zeroes
            bad = where(fac eq 'FF'x,nbad)
            if (nbad ne 0) then fac[bad] = 0

            ; Apply scaling to convert to floating point
            tpcal.tpfac[*,i] = (float(fac[ 0: 9])^2)/10.
            tpcal.tprms[*,i] = (float(fac[10:19])^2)/10.

            ; Replace zeroes with NaN
            bad = where(tpcal.tpfac[*,i] eq 0,nbad)
            if (nbad ne 0) then tpcal.tpfac[bad,i] = !values.f_nan
            bad = where(tpcal.tprms[*,i] eq 0,nbad)
            if (nbad ne 0) then tpcal.tprms[bad,i] = !values.f_nan
            ; Simply repeat data in third index
            tpcal.tpfac[*,i,1] = tpcal.tpfac[*,i,0]
            tpcal.tprms[*,i,1] = tpcal.tprms[*,i,0]
         endfor
         end
       2028: begin
         ; Case of new TPCAL segment, containing 2 records
         ; Scale factor for conversion to floating point.  The 27-m entries
         ; (the first four) are scaled by 10, while the 2-m entries are scaled by 100
         sclfac = [10.,10.,10.,10.,100.,100.,100.,100.,100.,100.]
         ; Loop over frequency
         for i = 0, 85 do begin

            fac = uint(data(36+20*i:36+20*(i+1)-1))

            ; Replace bad data with zeroes
            bad = where(fac eq 'FFFF'x,nbad)
            if (nbad ne 0) then fac[bad] = 0

            ; Apply scaling to convert to floating point
            tpcal.tpfac[*,i] = fac[ 0: 9]/sclfac
            tpcal.tprms[*,i] = fac[10:19]/sclfac

            ; Replace zeroes with NaN
            bad = where(tpcal.tpfac[*,i] eq 0,nbad)
            if (nbad ne 0) then tpcal.tpfac[bad,i] = !values.f_nan
            bad = where(tpcal.tprms[*,i] eq 0,nbad)
            if (nbad ne 0) then tpcal.tprms[bad,i] = !values.f_nan
            ; Simply repeat data in third index
            tpcal.tpfac[*,i,1] = tpcal.tpfac[*,i,0]
            tpcal.tprms[*,i,1] = tpcal.tprms[*,i,0]
         endfor
         end
       4056: begin
         ; Case of even newer TPCAL segment, containing 4 records (first two
         ; correspond to R, second two to L polarization).
         ; Scale factor for conversion to floating point.  The 27-m entries
         ; (the first four) are scaled by 10, while the 2-m entries are scaled by 100
         sclfac = [10.,10.,10.,10.,100.,100.,100.,100.,100.,100.]
         ; Loop over frequency
         for j = 0, 1 do begin
            for i = 0, 85 do begin

               fac = uint(data[36+20*i:36+20*(i+1)-1,j])

               ; Replace bad data with zeroes
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then fac[bad] = 0

               ; Apply scaling to convert to floating point
               tpcal.tpfac[*,i,j] = fac[ 0: 9]/sclfac
               tpcal.tprms[*,i,j] = fac[10:19]/sclfac

               ; Replace zeroes with NaN
               bad = where(tpcal.tpfac[*,i,j] eq 0,nbad)
               if (nbad ne 0) then tpcal.tpfac[bad,i,j] = !values.f_nan
               bad = where(tpcal.tprms[*,i,j] eq 0,nbad)
               if (nbad ne 0) then tpcal.tprms[bad,i,j] = !values.f_nan
            endfor
         endfor
         end
      else: begin
         end
      ENDCASE
      return,tpcal
      END
   !SEGM.TPUPD: BEGIN

      ; Get definition of TPUPD structure (same as TPCAL)
      tpupd = get_tpcal_struct()

      ; Get the TIME/LABEL structures
      tpupd.tls27m = tl_decode(data[12:*])
      tpupd.tls02m = tl_decode(data[24:*])
      fac = bytarr(20)

      ; Check the size of data, and react accordingly
      CASE n_elements(data) OF
       2028: begin
         ; Case of older data with only two records in segment
         ; Loop over frequency
         for i = 0, 85 do begin

            fac = uint(data(36+20*i:36+20*(i+1)-1))

            ; Replace bad data with zeroes
            bad = where(fac eq 'FFFF'x,nbad)
            if (nbad ne 0) then fac[bad] = 0

            ; Apply scaling to convert to floating point
            tpupd.tpfac[*,i] = fac[ 0: 9]/1000.
            tpupd.tprms[*,i] = fac[10:19]/1000.

         endfor
         ; Missing TPUPD factors are zero, but should be set to 1, so that
         ; when applied they have no effect.
         bad = where(tpupd.tpfac eq 0.0,nbad)
         if (nbad ne 0) then tpupd.tpfac[bad] = 1.0
         ; Simply repeat data in third index
         tpupd.tpfac[*,*,1] = tpupd.tpfac[*,*,0]
         tpupd.tprms[*,*,1] = tpupd.tprms[*,*,0]
         end
       4056: begin
         ; Case of newer data with four records in segment
         ; Loop over polarization
         for j = 0, 1 do begin
            ; Loop over frequency
            for i = 0, 85 do begin

               fac = uint(data[36+20*i:36+20*(i+1)-1,j])

               ; Replace bad data with zeroes
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then fac[bad] = 0

               ; Apply scaling to convert to floating point
               tpupd.tpfac[*,i,j] = fac[ 0: 9]/1000.
               tpupd.tprms[*,i,j] = fac[10:19]/1000.
               ; Missing TPUPD factors may be zero, but should be set to 1, so that
               ; when applied they have no effect.
               bad = where(tpupd.tpfac[*,i,j] eq 0.0,nbad)
               if (nbad ne 0) then tpupd.tpfac[bad,i,j] = 1.0

            endfor
         endfor
         end
        ELSE:
        ENDCASE
       return,tpupd
      END
   !SEGM.AMPCAL: BEGIN

      ; Get definition of AMPCAL structure
      ampcal = get_ampcal_struct()

      ; Get the TIME/LABEL structures
      ampcal.tlsi  = tl_decode(data[12:*])
      ampcal.tlsrl = tl_decode(data[24:*])
      fac = intarr(20)

      ; Check the size of data, and react accordingly
      CASE n_elements(data) OF
       2028: begin
         ; Case of older data with only two records in segment
         ; Loop over polarization
         for j = 0, 1 do begin
            ; Loop over frequency
            for i = 0, 85 do begin
   
               fac = uint(data(36+20*i:36+20*(i+1)-1))
               ; Replace bad data with zeroes
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then fac[bad] = 0
   
               ; Apply scaling to convert to floating point
               ampcal.factors[*,i,j] = fac[ 0: 9]/100.
               ampcal.rms[*,i,j]     = fac[10:19]/100.
   
               ; Replace zeroes with NaN
               bad = where(ampcal.factors[*,i] eq 0,nbad)
               if (nbad ne 0) then begin
                  ampcal.factors[bad,i,j] = !values.f_nan
                  ampcal.rms[bad,i,j]     = !values.f_nan
               endif
            endfor
         endfor
         end
       4056: begin
         ; Case of newer data with four records in segment (RCP and LCP)
         ; Loop over polarization
         for j = 0, 1 do begin
            for i = 0, 85 do begin

               fac = uint(data[36+20*i:36+20*(i+1)-1,j])
               ; Replace bad data with zeroes
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then fac[bad] = 0

               ; Apply scaling to convert to floating point
               ampcal.factors[*,i,j] = fac[ 0: 9]/100.
               ampcal.rms[*,i,j]     = fac[10:19]/100.

               ; Replace zeroes with NaN
               bad = where(ampcal.factors[*,i,j] eq 0,nbad)
               if (nbad ne 0) then begin
                  ampcal.factors[bad,i,j] = !values.f_nan
                  ampcal.rms[bad,i,j]     = !values.f_nan
               endif
            endfor
         endfor
         end
        ELSE:
        ENDCASE
      return,ampcal
      END
   !SEGM.PHZCAL: BEGIN

      ; Get definition of PHZCAL structure
      phzcal = get_phzcal_struct()

      fac = uintarr(10)

      ; Check the size of data, and react accordingly
      CASE n_elements(data) OF
       1024: begin
         ; Case of older data with only one record in segment
         ; Loop over polarization
         for j = 0, 1 do begin
            ; Loop over frequency
            for i = 0, 85 do begin
   
               fac = uint(data(36+10*i:36+10*(i+1)-1))
               ; Replace bad data with zeroes
               good = where(fac ne 'FFFF'x,ngood)
   
               ; Apply scaling to convert to floating point
               if (ngood ne 0) then phzcal.phase[good,i,j] = fac[good]/100.
   
               ; Replace zeroes with NaN
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then phzcal.phase[bad,i,j] = !values.f_nan
            endfor
         endfor
         end
       2048: begin
         ; Case of newer data with two records in segment (RCP and LCP)
         ; Loop over polarization
         for j = 0, 1 do begin
            ; Loop over frequency
            for i = 0, 85 do begin

               fac = uint(data[36+10*i:36+10*(i+1)-1,j])
               ; Replace bad data with zeroes
               good = where(fac ne 'FFFF'x,ngood)

               ; Apply scaling to convert to floating point
               if (ngood ne 0) then phzcal.phase[good,i,j] = fac[good]/100.

               ; Replace zeroes with NaN
               bad = where(fac eq 'FFFF'x,nbad)
               if (nbad ne 0) then phzcal.phase[bad,i,j] = !values.f_nan
            endfor
         endfor
         end
        ELSE:
        ENDCASE

      return,phzcal
      END
   !SEGM.DAILYPHZ: BEGIN
      ; First read NF and NBL to ensure that there are real data in this scan
      nf = data[48]     ; Number of frequencies (in AMF table)
      nbl = data[49]    ; Number of baselines (in AMF table)
      if (nf eq 0 or nbl eq 0) then return,-1
      dailyphz = get_daily_struct()
      dailyphz.tls = tl_decode(data(12:*))  ; reference time for scan
      dailyphz.pslp = data[24:31]/10.0      ; PSLP for each antenna
      dailyphz.poff = data[32:39]           ; POFF for each antenna
      dailyphz.perr = data[40:47]           ; PERR for each antenna
      fac = [0.1,0.001,0.001,0.1,0.1]#replicate(1,nf)
      ptr_free,dailyphz.pamf
      dailyphz.pamf = ptr_new(fltarr(5,nf,nbl))
      for k = 0, nbl-1 do begin
         (*dailyphz.pamf)[*,*,k] = data[50+(5*nf)*k:49+(5*nf)*(k+1)]*fac
      endfor

      return, dailyphz
      END
   !SEGM.TRAJ: BEGIN

      ; Get definition of TRAJEC structure
      trajec = get_traj_struct()

      trajec.srcname = string(byte(data(12:17),0,12))
      for i = 0, 2 do begin
         trajec.satha[i]  = w2l(data,18+6*i)
         trajec.satdec[i] = w2l(data,20+6*i)
         trajec.satt[i]   = w2l(data,22+6*i)
      endfor
      trajec.id     = w2l(data,206)-1000    ; Strip off CPC's 1000 kludge
      trajec.nrpt   = w2l(data,208)
      trajec.nx     = w2l(data,210)/4

      cmd = strarr(trajec.nx<100)
      ha = (dec = (dur = lonarr(trajec.nx)))
      for i = 0, (trajec.nx - 1)<99 do begin
         dur(i) = w2l(data,212+i*8)+1   ; Restore decrement to stored value
         cmd(i) = string(byte(data(214+i*8:215+i*8),0,4))+'   '
         if (cmd(i) eq 'TRAC   ') then cmd(i) = 'TRACK  '
         if (cmd(i) eq 'SEQS   ') then cmd(i) = 'SEQSTOP'
         dec(i) = w2l(data,216+i*8)
         ha(i)  = w2l(data,218+i*8)
      endfor

      ; Free existing pointers in OBSEQ structure
      ptr_free,trajec.pDur
      ptr_free,trajec.pCmd
      ptr_free,trajec.pDec
      ptr_free,trajec.pHA

      ; Create new pointers to data arrays
      trajec.pDur = ptr_new(reverse(dur))
      trajec.pCmd = ptr_new(reverse(cmd))
      trajec.pDec = ptr_new(reverse(dec))
      trajec.pHA  = ptr_new(reverse( ha))

      return,trajec
   END
   !SEGM.OBSEQ: BEGIN

      ; Get definition of OBSEQ structure
      obseq = get_obs_struct()

      ; Extract information from OBSEQ segment and insert into
      ; OBSEQ structure

      obseq.tics = data(12)
      obseq.id   = data(24)
      obseq.nt   = data(25)
      obseq.nx   = data(26)

      ; These are the receiver states. Reverse them to place them
      ; into the actual order as implemented
      states = reverse(data(27:27+obseq.nx-1))

      harm =         states and    "177      ; Harmonic number
      nd   = 2-ishft(states and    "200, -7) ; Noise diode state (1=ON, 2=OFF)
      pol  =   ishft(states and   "7400, -8) ; Polarization code
      bad  =   ishft(states and "100000,-15) ; Bad data bit
      badidx = where(bad EQ 1)               ; Array of indexes of bad values
      IF (badidx(0) NE -1) THEN BEGIN
         harm(where(bad EQ 1)) = -1             ; Set bad freqs to -1
           nd(where(bad EQ 1)) =  0             ; Set bad noise diode to 0
      ENDIF

      ; Create array indicating which harmonics exist
      hflag = intarr(90)
      tmp = harm(where(harm NE -1))    ; frequency list with -1's eliminated
      hflag(tmp - 1) = 1
      hord = where(hflag NE 0)+1

      ; Free existing pointers in OBSEQ structure
      ptr_free,obseq.pHarm
      ptr_free,obseq.pPol
      ptr_free,obseq.pND
      ptr_free,obseq.pHflag
      ptr_free,obseq.pHord

      ; Create new pointers to data arrays
      obseq.pHarm  = ptr_new(harm)
      obseq.pPol   = ptr_new(pol)
      obseq.pND    = ptr_new(nd)
      obseq.pHflag = ptr_new(hflag)
      obseq.pHord  = ptr_new(hord)

      return,obseq
   END
   !SEGM.EPHEM : BEGIN
      ; Get definition of EPHEM structure
      ephem = get_eph_struct()

      ; Extract information from data record and put it into EPHEM structure
      ephem.dut = data(12)
      for i = 0, 2 do begin
         ephem.solar.ra(i)  = w2l(data,13+i*2)
         ephem.solar.dec(i) = w2l(data,19+i*2)
         ephem.solar.rad(i) =     data(25+i)
         ephem.solar.pa(i)  = w2l(data,28+i*2)
         ephem.solar.b0(i)  = w2l(data,34+i*2)
         ephem.solar.hp(i)  =     data(40+i)
         ephem.solar.reft(i)=     data(43+i)
         ephem.solar.doy(i) =     data(46+i)
         ephem.lunar.ra(i)  = w2l(data,49+i*2)
         ephem.lunar.dec(i) = w2l(data,55+i*2)
         ephem.lunar.rad(i) =     data(61+i)
         ephem.lunar.hp(i)  =     data(64+i)
         ephem.lunar.reft(i)=     data(67+i)
         ephem.lunar.doy(i) =     data(70+i)
      endfor
      for i = 0, 2 do begin
         ephem.cal.epoch.(i) = data(73+i) ; Calibrator epoch (year,doy,sec)
      endfor
      ephem.cal.ncal = data(76)           ; Number of calibrators
      ; Loop over number of calibrators
      for i = 0, ephem.cal.ncal-1 do begin
         ephem.cal.caleph(i).name = string(byte(data(77+i*8:80+i*8),0,8))
         ephem.cal.caleph(i).ra   = w2l(data,81+i*8)
         ephem.cal.caleph(i).dec  = w2l(data,83+i*8)
      endfor

      ; Interim code to spawn precess to get updated current coordinates
      calepoch = ephem.cal.epoch
      caldate = calepoch.yr+calepoch.doy/1000.
      ; Check whether this EPHEM record needs updating.  It may be flagged
      ; with 255 in last byte to indicate that it does not need updating.
      if (caldate le 2000.082 and data[1023] ne 255) then begin
         on_ioerror,nofile
         line =''
         ; Open existing calibrator file and read date from comment line
         openr,lun,/get_lun,!defaults.workdir+'coords.fth'
         readf,lun,line
         free_lun,lun
         fmo = ''
         reads,line,fda,fmo,fyr,format='(30X,I2,1X,A3,1X,I4)'
         fdoy = dayofyr(fyr,fmo,fda)
         fildate = fyr+fdoy/1000.
         if (fildate ne caldate) then begin
            ; If the file date does not agree with the cal epoch, create a new file
         nofile:
            on_ioerror,NULL
            cd,!defaults.workdir,cur=cwd
            datarr = cvdoy(calepoch.yr,calepoch.doy)
            datstr = string(datarr,format='(I4,"-",I2.2,"-",I2.2)')
            spawn,!defaults.cmddir+'prec2000 /'+datstr
            cd,cwd
         endif
         openr,lun,/get_lun,!defaults.workdir+'coords.fth'
         line = ''
         readf,lun,line
         ra = 0.D
         dec = 0.D
         for i = 0, ephem.cal.ncal-1 do begin
            readf,lun,ra,format='(F9.4)'
            readf,lun,dec,format='(F9.4)'
            ephem.cal.caleph(i).ra   = long(ra*10000)
            ephem.cal.caleph(i).dec  = long(dec*10000)
            ; Replace values in DATA record read from file
            data[81+i*8:82+i*8] = l2w(ephem.cal.caleph(i).ra)
            data[83+i*8:84+i*8] = l2w(ephem.cal.caleph(i).dec)
         endfor
         free_lun,lun
         ; Flag last byte of DATA with 255, to indicate that this is an
         ; updated EPHEM record, and write it to WORKDIR as EPHEM.ddd
         data[1023] = 255
         filename = !DEFAULTS.WORKDIR+'EPHEM.'+string(tls.day,format='(I3.3)')
         openw,lun,/get_lun,filename
         a = assoc(lun,intarr(1024))
         a(0) = data
         free_lun,lun
         print,'DECODE: Wrote updated EPHEM file '+filename
      endif

      for i = 0, 2 do begin
         ephem.regn.epoch.(i) = data(277+i)  ; Active Region EPOCH (year,doy,sec)
      endfor
      ephem.regn.nregn = data(280)        ; Number of active regions
      ; Loop over number of active regions
      for i = 0, ephem.regn.nregn-1 do begin
         ephem.regn.info(i).noa = data(281+i*13)    ; Region NOAA number
         ephem.regn.info(i).lat = data(282+i*13)       ; Region latitude (+N)
         ephem.regn.info(i).lng = data(283+i*13)       ; Region longitude (+E)
         ephem.regn.info(i).carrlng = data(284+i*13)   ; Region Carr. Longitude
         ephem.regn.info(i).area = data(285+i*13)      ; Region Spot Area
         ephem.regn.info(i).type = string(byte(data(286+i*13:287+i*13),0,4))
         ephem.regn.info(i).magtype = string(byte(data(288+i*13:293+i*13),0,12))
      endfor

      return,ephem
   END
   ELSE: BEGIN
      ans = widget_message('DECODE: Cannot decode segment type: ' $
                              +string(incode,format='(I2)'),/ERROR)
      return,-1
      END
   ENDCASE
end
