function [adcp,cfg,units]=vrdadcp(ifile,interval)
%---------------------------------------------------------------------
% Description:      Function to read in a binary RDI file (ifile) into two structures, adcp and cfg. 
% Adapted from both Rich's and Jody's code by Dewey for VENUS.
% Optional input:   [ens_1 ens_end] to read a limited interval of ensembles
%                   i.e., [38000 40000], [40001 inf]: default [1 inf]
% This routine does no averaging or screening.
% Output structures:
%   adcp.range          (range to bins in m)
%       .corr           (correlation counts)
%       .intens         (backscatter counts)
%       .velocity       [4,nbins,nens] (velocities: u,v,w,e | b1,b2,b3,b4)
%       .percentGood     percent good)
%       .ens            (ensemble number)
%       .compassHeading (internal compass heading, relative to beam 3)
%       .pitch          (tilt 1, + beam 3 up)
%       .roll           (tilt 2, + beam 2 up)
%       .time           (matlab time of ensembles, assumes century 2000)
%       .temperature    (internal temperature of head, C)
%       .salinity       (fixed/set or measured salinity)
%       .soundSpeed     (speed of sound value; scalar if fixed, vector otherwise)
%       .pressure       (fixed/set or measured in db)
%       .depth          (fixed/set or calcualted from pressure)
%       .bt.*           (bottom track structure/variables)
%       .vm.*           (VMDAS structure/variables)
%    cfg.fwVer          (firmware version #, i.e. 16)
%       .fwRev          (firmware revision #, i.e. 28 --> v16.28)
%       .sysCfg         (2 bit strings coding the hardware configuration)
%       .freq          (the carrier frequency of the ADCP, i.e., 300 kHz)
%       .beamPattern    ('Concave' or 'Convex')
%       .orient         (vertical orientation, 'Down' or 'Up')
%       .beamAngle      (in degrees, i.e., 20)
%       .janusCfg       (transducer config, i.e., '4-Beam Janus')
%       .lagLength
%       .nbeams         (number of transducers, i.e. 4)
%       .nbins_WN       (number of cells/bins)
%       .npings_WP      (number of pings per ensemble)
%       .cellSize_WS    (cell/bin size in m)
%       .blank_WF       (blank after transmit, in m);
%       .profilingMode  (mode WM command, i.e., 1)
%       .corrThresh_WC  (counts, correlation minumum threshold before data is set to bad (-32768)
%       .codeReps       (number of code reps per transmission)
%       .percentGoodMin_WG  (counts, percent good minimum before data set to bad)
%       .errVelThreshold_WE (error velocity max mm/s)
%       .timePing_TP    (TPP, time per ping within ensemble)
%       .coord_EX       (string denoting the coordinate transformation)
%       .coordSys       ('Beam','Earth','Inst', or 'Ship')
%       .headingAlign_EA (Compass (beam 3) heading alignment offset, degrees)
%       .headingBias_EB (Magnetic Deviation for location, EB command)
%       .sensorSrc_EZ   (sensor source index for sensors to use, bit string)
%       .sensorAvail    (sensors that are available, bit string)
%       .bin1Dist       (distance in m to center of first bin)
%       .transmitLength_WT  (transmission pulse length in m)
%       .falseTrgt_WA   (false target threshold in counts, default 50)
%       .transmitLagDistance   (transmit lag between pulses, m)
%       .cpuSN          (CPU serial number)
%       .sysBndwdth_WB  (Bandwidth setting, either broad (0) or narrow (1))
%       .sysPower_CQ    (system power, counts (0-255), default 255)
%       .instSN         (instrument serial number, if available)
%       .ensInterval    (was dtime_ens in older code)
%   units.beamAngle
%       .blank_WF
%       .cellSize_WS
%       .corr
%       .depth
%       .ensInterval
%       .freq
%       .compassHeading
%       .intens
%       .percentGood
%       .pitch
%       .pressure
%       .range
%       .roll
%       .salinity
%       .soundSpeed 
%       .temperature
%       .time
%       .timePing_TP
%       .transmitLagDistance
%       .transmitLength_WT
%       .velocity
% Modify Date/Author:   
%           RKD 06/09
%           20091129, R. Jenkyns
%           -cleaned up, added coord & coordSys to config structure, allowed 
%           for only 1 record in rdi file
%           20100119, R.Jenkyns
%           - improved robustness of bytesens, since it failed on one occassion 
%           where there every line contained more than one 7F7F. Added a piece 
%           that checks if there is a most frequent value in bytesens0 and uses
%           that.
%           20100120, R.Jenkyns
%           -altered fields due to concensus with VENUS
%           -fixed bugs for improper conversion of pressure and tilt/roll
%           -moved soundSpeed to adcp from config
%           20100420, R.Jenkyns
%           -filled soundspeed with NaNs like other variables in gaps (this
%           was missed earlier)
%---------------------------------------------------------------------
if nargin<2
    interval=[1 inf];
end
indx_1=1;
indx_end=interval(2)-interval(1)+1;
fileinfo=dir(ifile);
if isempty(fileinfo)
    fprintf(' Error: Can''t find file %s\n',ifile);
    return
end
if (interval(1)~=1 && interval(2)==inf) || interval(2)~=inf
   fprintf(' Opening and reading a portion of file: %s\n',ifile);
   % estimate the ensemble size if we have a limited number of ensembles to read
   fid=fopen(ifile,'r','ieee-le');
   ch=uint8(fread(fid,100000,'uint8')); 
   fclose(fid);
   in0 = find(ch(1:end-1)==127 & ch(2:end)==127); % find the "7F7F" header start
   din0=diff(in0);
   bpens=max(din0);  % estimate of ensemble size
   disp([' Estimated ensemble size: ',num2str(bpens)]);
   fileinfo=dir(ifile);
   nens=fix(fileinfo.bytes/bpens);
   fid=fopen(ifile,'r','ieee-le');
   if interval(1) ~= 1
       skip=interval(1)-1;
       fseek(fid,skip*bpens,-1);
   end
   rens=interval(2)-interval(1)+2;
   ch=uint8(fread(fid,rens*bpens,'uint8'));
   fclose(fid);
else % read entire file
   fprintf(' Opening and reading entire file: %s\n',ifile);
   fid=fopen(ifile,'r','ieee-le');
   ch = uint8(fread(fid,inf,'uint8')); % read the entire file into RAM
   fclose(fid);
end

nbytes=length(ch);
disp([num2str(nbytes/(1024*1024)),' MB of data read into RAM.']);
in0 = find(ch(1:end-1)==127 & ch(2:end)==127); % find the "7F7F" header start
din0=diff(in0);
bpens=max(din0)-2;  % estimate of ensemble size
if ~isempty(din0)
    nens=fix(nbytes/bpens);  % projected number of ensembles
else 
    nens=1;
end    
% these are all the header candidates.  Some are data, some are actual
% headers.... (i.e. "7F7F" might occur in the data blocks)
bytesens0 = double(ch(in0+2))+double(ch(in0+3))*256; % bytes in each block between "7F7F"
if mode(bytesens0)>=bpens
    k=find(bytesens0==mode(bytesens0));
    in=in0(k);
    %length(in)
    bytesens=bytesens0(k);
    fprintf('Mode bytes per block greater than ensemble estimate. \n');
else    
    i=1;j=0;
    in=NaN*ones(size(in0)); % memory allocation
    iprt=ceil(nens/500)*100;

    bytesens=in;
    fprintf('Parsing into good records. \n');
    while i<length(in0) % Identify the header blocks
      if (in0(i+1)-in0(i)) == (bpens+2), % then ino(i) is the start of a header block
        j=j+1;
        in(j)=in0(i);
        bytesens(j)=bytesens0(i);
      else
        ii=i+1;
        while (in0(ii)-in0(i)) < (bpens+2) && ii < length(in0),
            ii=ii+1; % skip this false "7F7F"
        end
        j=j+1;
        in(j)=in0(i);
        bytesens(j)=bytesens0(i);
        i=ii-1; % next good
      end;
      if mod(j,iprt)==0
        fprintf('%d.',j);
      end;
      i=i+1;
    end
end    

if nens==1
    in=in0(1);
    bytesens=bytesens0(1);
end   

fprintf('\n');
indx=find(~isnan(in));
in=in(indx);
bytesens=bytesens(indx);
% make an array

if in(end)+bytesens(end)>length(ch) % check last record still long enough
  in=in(1:end-1);
  bytesens=bytesens(1:end-1);
end
dat = double(ch(in(1)+(1:bytesens(1))-1));

% preallocate arrays...
hdr = gethdr(dat,0);
ldr = getleader(dat,hdr.offsets(1));
cfg=ldr; % save cfg for return
% get some things from the leader:
adcp.range = ldr.bin1Dist+((1:ldr.nbins_WN)-1)*ldr.cellSize_WS;
adcp.corr = zeros(ldr.nbins_WN,length(in),4)+NaN;
adcp.intens = zeros(ldr.nbins_WN,length(in),4)+NaN;
adcp.velocity = zeros(ldr.nbins_WN,length(in),4)+NaN;
adcp.percentGood = zeros(ldr.nbins_WN,length(in),4)+NaN;

varl = initvarl(length(in));
istart=max([1 indx_1]);
iend=min([indx_end length(in)]);

fprintf('Decoding %d records...\n',iend-istart+1);

i=0;
for ii=istart:iend
  i=i+1;
  if mod(i,5000)==0
    fprintf('%d of %d\n',i,iend-istart+1);
  end
  dat = double(ch(in(ii)+(0:bytesens(ii)-1)));
  % check the checksum
  hexrec=dec2hex(ch(in(ii)+(0:bytesens(ii)+1)))';
  cs=checksum(hexrec); % checksum as read
  ocs=hexrec(end-3:end); % checksum in record
  if cs~=ocs
      disp(['Ensemble: ',ii,' Checksums do not match: ',ocs,' and ',cs,'  Record may be corrupt, skipping.']);
  else
     %
     hdr = gethdr(dat,0);
     % get the ids...
     ids = dat(hdr.offsets+1)+ 256*dat(hdr.offsets+2);
     varl = getvar(dat,hdr.offsets(2),varl,i);
     offsets = hdr.offsets;
     nbins = ldr.nbins_WN;
     for j=3:length(hdr.offsets)
       switch ids(j)
         case 256 
           vel = getvel(dat,offsets(j),nbins);
           adcp.velocity(:,i,:)=reshape(vel,4,nbins)';
         case 512
           corr = getcor(dat,offsets(j),nbins);
           adcp.corr(:,i,:)=reshape(corr,4,nbins)';
         case 768
           intens = getint(dat,offsets(j),nbins);
           adcp.intens(:,i,:)=reshape(intens,4,nbins)';
         case 1024
           percgood = getpercgood(dat,offsets(j),nbins);
           adcp.percentGood(:,i,:)=reshape(percgood,4,nbins)';
         case 1536
           if ~isfield(adcp,'bt'), 
               adcp = initbot(length(in),adcp); 
           end
           adcp = getbot(dat,offsets(j),adcp,i);
         case 8192
           % vmdas file with nav info in it...  From vmdas manual
           if ~isfield(adcp,'slat')
             adcp = initvmhd(length(in),adcp);
           end
           adcp = getvmhd(dat,offsets(j),adcp,i);
         otherwise % not sure either what this data type is or where it's pointing
           disp(['Ens: ',num2str(i),' Data type: ',num2str(j-2),' [vel,corr,intens,percentGood] Bad Offset:',dec2hex(ids(j))]);
           % fill these profiles with NaNs
           adcp.velocity(:,i,:)=NaN;  % :( this may not be absolutely necessary, but this record is corrupt somehow.
           adcp.corr(:,i,:)=NaN;  % :(
           adcp.intens(:,i,:)=NaN;  % :(
           adcp.percentGood(:,i,:)=NaN;  % :(
           break % out of case/switch loop
       end   
     end
  end
end
adcp.ens=varl.ens;
adcp.compassHeading=varl.compassHeading;
adcp.pitch=varl.pitch;
adcp.roll=varl.roll;
adcp.time=varl.time;
adcp.temperature=varl.temperature; 
adcp.salinity=varl.salinity;
adcp.pressure=varl.pressure;
adcp.depth=varl.depth;
adcp.soundSpeed=varl.soundSpeed;
  
% fix order of vels
todo = {'corr','intens','velocity','percentGood'};
for ii=1:length(todo)
  adcp.(todo{ii}) = permute(adcp.(todo{ii}),[3 1 2]);
end

% set flagged bad values to NaNs
in=find(adcp.velocity==-32768);adcp.velocity(in)=NaN; 
in=find(adcp.corr==-32768);adcp.corr(in)=NaN;
in=find(adcp.intens==-32768);adcp.intens(in)=NaN;
in=find(adcp.percentGood==-32768);adcp.percentGood(in)=NaN;

% apply scaling, mm/s to m/s
adcp.velocity=adcp.velocity*0.001;

%
if indx_end~=inf % then the user asked for a reduced set, truncate end NaNs
    iend=max(find(~isnan(adcp.time)));
    adcp.velocity=adcp.velocity(:,:,1:iend);
    adcp.corr=adcp.corr(:,:,1:iend);
    adcp.intens=adcp.intens(:,:,1:iend);
    adcp.percentGood=adcp.percentGood(:,:,1:iend);
    adcp.time=adcp.time(1:iend);
    adcp.ens=adcp.ens(1:iend);
    adcp.compassHeading=adcp.compassHeading(1:iend);
    adcp.pitch=adcp.pitch(1:iend);
    adcp.roll=adcp.roll(1:iend);
    adcp.temperature=adcp.temperature(1:iend);
    adcp.salinity=adcp.salinity(1:iend);
    adcp.pressure=adcp.pressure(1:iend);
    adcp.depth=adcp.depth(1:iend);
    adcp.soundSpeed=adcp.soundSpeed(1:iend);
end
% now look for gaps in the data and fill with NaNs
% there should be the right number of ensembles given a uniform ensemble interval
dtens=diff(adcp.time);
dt=round(median(dtens)*24*3600)/(24*3600);  % rounded to nearest second
cfg.ensInterval=dt*24*3600;  % ensemble interval in seconds
indx=find(dtens>(1.9*dt)) + 1;  % these are the indecices to the gaps
if ~isempty(indx)
    % Speed this up by pre-allocating mamory based on first and last time and dt
    DT=adcp.time(end)-adcp.time(1);
    N=floor(DT/dt) + 1; % this should be the new total length of the matrices
    [m,n,o]=size(adcp.velocity);
    disp(['Filling in ',num2str(length(indx)),' temporal gaps/missing ensembles with NaN profiles.']);
    for i=length(indx):-1:1 % start from the back and insert NaN Ensembles in gaps.
        j=indx(i);
        dti=adcp.time(j)-adcp.time(j-1); % this is the duration of the gap to fill
        ndt=round(dti/dt)-1; % number of ensembles to insert
        o=length(adcp.time); % check the current size/dimensions/length
        jj=j+ndt-1;
        adcp.velocity(:,:,[j:o]+ndt)=adcp.velocity(:,:,j:o);adcp.velocity(:,:,j:jj)=NaN;
        adcp.corr(:,:,[j:o]+ndt)=adcp.corr(:,:,j:o);adcp.corr(:,:,j:jj)=NaN;
        adcp.intens(:,:,[j:o]+ndt)=adcp.intens(:,:,j:o);adcp.intens(:,:,j:jj)=NaN;
        adcp.percentGood(:,:,[j:o]+ndt)=adcp.percentGood(:,:,j:o);adcp.percentGood(:,:,j:jj)=NaN;
        adcp.compassHeading([j:o]+ndt)=adcp.compassHeading(j:o);adcp.compassHeading(j:jj)=NaN;
        adcp.pitch([j:o]+ndt)=adcp.pitch(j:o);adcp.pitch(j:jj)=NaN;
        adcp.roll([j:o]+ndt)=adcp.roll(j:o);adcp.roll(j:jj)=NaN;
        adcp.temperature([j:o]+ndt)=adcp.temperature(j:o);adcp.temperature(j:jj)=NaN;
        adcp.salinity([j:o]+ndt)=adcp.salinity(j:o);adcp.salinity(j:jj)=NaN;
        adcp.pressure([j:o]+ndt)=adcp.pressure(j:o);adcp.pressure(j:jj)=NaN;
        adcp.depth([j:o]+ndt)=adcp.depth(j:o);adcp.depth(j:jj)=NaN;
        adcp.soundSpeed([j:o]+ndt)=adcp.soundSpeed(j:o);adcp.soundSpeed(j:jj)=NaN;
        adcp.time([j:o]+ndt)=adcp.time(j:o); % the times and ens should remain continuous/non-NaN
        adcp.time(j:jj)=adcp.time(j-1)+[1:ndt]*dt; % these are the approximate times of the NaN profiles
    end
    adcp.ens=[adcp.ens(1):(adcp.ens(1)+length(adcp.time)-1)];  % re-set the entire ensemble count
end
if range(varl.soundSpeed)==0
    adcp.soundSpeed=min(varl.soundSpeed); %make scalar if fixed value
end  
if range(varl.salinity)==0
    adcp.salinity=min(varl.salinity); %make scalar if fixed value
end  
disp(['Final number of consecutive ensembles returned: ',num2str(length(adcp.time))]);

units=getUnits;
% fini
%%
function units=getUnits
% create structure for units 
units.beamAngle='degrees';
units.blank_WF='m';
units.cellSize_WS='m';
units.compassHeading='degrees relative to magnetic N';
units.corr='counts';
units.depth='m';
units.ensInterval='sec';
units.freq='kHz';
units.intens='counts';
units.percentGood='%';
units.pitch='degrees';
units.pressure='decibar';
units.range='m';
units.roll='degrees';
units.salinity='ppt';
units.soundSpeed='m/s';
units.temperature='C';
units.time='Matlab time in UTC';
units.timePing_TP='sec';
units.transmitLagDistance='m';
units.transmitLength_WT='m';
units.velocity='m/s';
%%
function hdr=gethdr(dat,offset)
%
hdr.id = dat(1);
hdr.source= dat(2);
hdr.nbyte = dat(3)+256*dat(4);
hdr.ndat = dat(6);
hdr.offsets=dat(7+[0:1:hdr.ndat-1]*2) + 256*dat(8+[0:1:hdr.ndat-1]*2);

%%
function ldr=getleader(dat,off,ldr,i)
% parse fixed leader/header 
ldr = [];
off = off+1;
dd = double(dat(off:end));
ldr.fwVer=dd(3);
ldr.fwRev=dd(4);
ldr.sysCfg(1:2,:)=['00000000';'00000000'];
bitstr1=dec2bin(dd(5));
bitstr2=dec2bin(dd(6));
nb1=length(bitstr1);nb2=length(bitstr2);
ldr.sysCfg(1,(8-nb1+1):8)=bitstr1;
ldr.sysCfg(2,(8-nb2+1):8)=bitstr2;
if strcmp(ldr.sysCfg(1,6:8),'000'), ldr.freq=75; end
if strcmp(ldr.sysCfg(1,6:8),'001'), ldr.freq=150; end
if strcmp(ldr.sysCfg(1,6:8),'010'), ldr.freq=300; end
if strcmp(ldr.sysCfg(1,6:8),'011'), ldr.freq=600; end
if strcmp(ldr.sysCfg(1,6:8),'100'), ldr.freq=1200; end
if strcmp(ldr.sysCfg(1,6:8),'101'), ldr.freq=2400; end
if strcmp(ldr.sysCfg(1,5),'0'), ldr.beamPattern='Concave'; end
if ldr.sysCfg(1,5)=='1', ldr.beamPattern='Convex'; end
if ldr.sysCfg(1,1)=='0', ldr.orient='Down'; end
if ldr.sysCfg(1,1)=='1', ldr.orient='Up'; end
if strcmp(ldr.sysCfg(2,7:8),'00'), ldr.beamAngle=15; end
if strcmp(ldr.sysCfg(2,7:8),'01'), ldr.beamAngle=20; end
if strcmp(ldr.sysCfg(2,7:8),'10'), ldr.beamAngle=30; end
if strcmp(ldr.sysCfg(2,1:4),'0100'), ldr.janusCfg='4-Beam Janus'; end
if strcmp(ldr.sysCfg(2,1:4),'0101'), ldr.janusCfg='5-Beam Janus v1'; end
if strcmp(ldr.sysCfg(2,1:4),'1111'), ldr.janusCfg='5-Beam Janus v2'; end
%ldr.simflag=dd(7); % always hard set to zero?
ldr.lagLength=dd(8);
ldr.nbeams=dd(9);
ldr.nbins_WN = dd(10);
ldr.npings_WP = dd(11)+256*dd(12);
ldr.cellSize_WS = (dd(13)+256*dd(14))*0.01; % to m
ldr.blank_WF = (dd(15)+256*dd(16))*0.01;  % to m
ldr.profilingMode = dd(17);
ldr.corrThresh_WC=dd(18);
ldr.codeReps= dd(19);
ldr.percentGoodMin_WG=dd(20);
ldr.errVelThreshold_WE=dd(21)+256*dd(22);
ldr.timePing_TP=dd(23)*60 + dd(24) + dd(25)/100;
ldr.coord_EX='00000';
coord=dec2bin(dd(26));
ldr.coord_EX(end-length(coord)+1:end)=coord;
if strcmp(ldr.coord_EX(1:2),'00'),ldr.coordSys='Beam';
elseif strcmp(ldr.coord_EX(1:2),'01'),ldr.coordSys='Inst';  
elseif strcmp(ldr.coord_EX(1:2),'10'),ldr.coordSys='Ship';  
elseif strcmp(ldr.coord_EX(1:2),'11'),ldr.coordSys='Earth';  
end 
ldr.headingAlign_EA = dd(27)+256*dd(28);
if ldr.headingAlign_EA > 32768,
  ldr.headingAlign_EA = -2*32768+ldr.headingAlign_EA ;
end
ldr.headingAlign_EA =ldr.headingAlign_EA*0.01;
ldr.headingBias_EB = dd(29) + 256*dd(30);
if ldr.headingBias_EB  > 32768,
  ldr.headingBias_EB  = -2*32768+ldr.headingBias_EB ;
end
ldr.headingBias_EB =ldr.headingBias_EB *0.01;
ldr.sensorSrc_EZ ='0000000'; sensor_src=dec2bin(dd(31));
ldr.sensorSrc_EZ (end-length(sensor_src)+1:end)=sensor_src;
ldr.sensorAvail = '0000000'; sensorAvail = dec2bin(dd(32)); 
ldr.sensorAvail(end-length(sensorAvail)+1:end)=sensorAvail;
ldr.bin1Dist = (dd(33)+256*dd(34))*0.01;
ldr.transmitLength_WT= (dd(35)+256*dd(36))/100;
ldr.falseTrgt_WA=dd(39);
ldr.transmitLagDistance=(dd(41) + 256*dd(42))/100;
%ldr.cpuSN=dec2hex(dd(43:50)); % leave in HEX since matches output from PS0 command
ldr.cpuSN=[dec2hex(dd(43),2),' ',dec2hex(dd(44),2),' ',dec2hex(dd(45),2),' ',...
    dec2hex(dd(46),2),' ',dec2hex(dd(47),2),' ',dec2hex(dd(48),2),' ',dec2hex(dd(49),2),' ',...
    dec2hex(dd(50),2)]; % leave in HEX since matches output from PS0 command
ldr.sysBndwdth_WB=dd(51) + 256*dd(52);
ldr.sysPower_CQ=dd(53);
ldr.instSN([55:58]-54)=dd(55:58);
%ldr.beamAngle=dd(59);
%% 
function adcp = initvmhd(nens,adcp)
% initialize VMDAS Variables
x=ones(1,nens)*NaN;
adcp.stime = x;
adcp.etime = x;
adcp.slat = x;
adcp.slon = x;
adcp.elat = x;
adcp.elon = x;
adcp.spd= x;
adcp.headtrue = x;
adcp.headmag = x;
adcp.smg = x;
adcp.hmg = x;

%% 
function adcp = getvmhd(dat,off,adcp,i)
% parsing VMDAS Header
dd = dat((off+1):end);
time = dd(7)+dd(8)*256+dd(9)*256^2+dd(10)*256^3;
time = mod(time*0.0001,24*3600); % seconds...
year = 2000 + dd(5)+256*dd(6);
adcp.vm.stime(i) = datenum(year,dd(4),dd(3),0,0,time);
if i>1 && adcp.vm.stime(i)>adcp.vm.stime(i-1)+0.5
  adcp.vm.stime(i) = adcp.vm.stime(i)-1;
end
time = dd(23)+dd(24)*256+dd(25)*256^2+dd(26)*256^3;
time = mod(time*0.0001,24*3600); % seconds...
adcp.vm.etime(i) = datenum(year,dd(4),dd(3),0,0,time);
% believe it or not the date clicks over before the time (sigh)...

if i>1 && adcp.vm.etime(i)>adcp.vm.etime(i-1)+0.5
  adcp.vm.etime(i) = adcp.vm.etime(i)-1;
end

lat = dd(15)+dd(16)*256+dd(17)*256^2+dd(18)*256^3;
if lat>2^32/2
  lat = -2^32+lat;
end
adcp.vm.slat(i) = lat*180/2^31;
lon = dd(19)+dd(20)*256+dd(21)*256^2+dd(22)*256^3;
if lon>2^32/2
  lon = -2^32+lon;
end
adcp.vm.slon(i) = lon*180/2^31;

lat = dd(27)+dd(28)*256+dd(29)*256^2+dd(30)*256^3;
if lat>2^32/2
  lat = -2^32+lat;
end
adcp.vm.elat(i) = lat*180/2^31;

lon = dd(31)+dd(32)*256+dd(33)*256^2+dd(34)*256^3;
if lon>2^32/2
  lon = -2^32+lon;
end
adcp.vm.elon(i) = lon*180/2^31;
% the rest of this is crap for some reason...
x = dat(35)+256*dat(36);
if x > 2^16/2
  x=-2^16+x;
end
adcp.vm.spd(i) = x*1e-3;

adcp.vm.headtrue(i) = dat(37)+256*dat(38);
adcp.vm.headmag(i) = dat(39)+256*dat(40);
x = dat(41)+dat(42)*256;
if x > 32768
  x=-2*32768+x;
end
adcp.vm.smg(i) = x*1e-3;
adcp.vm.hmg(i) = dat(43)+256*dat(44);

%% 
function adcp = initbot(nens,adcp)
x=ones(4,nens)*NaN;
adcp.bt.range = x;
adcp.bt.velocity = x;
adcp.bt.corr = x;
adcp.bt.amp = x;
adcp.bt.wrlon= NaN*zeros(1,nens);
adcp.bt.wrlat= NaN*zeros(1,nens);

%%
function adcp = getbot(dat,off,adcp,i)

dd = dat(off+(1:90));
x = getshort(dd(25:32));
x(x==-32768)=NaN;
adcp.bt.velocity(:,i)=x/1000;
adcp.bt.range(:,i) = getushort(dd(17:24))/100;
bad = find(adcp.bt.range(:,i)==0);
adcp.bt.range(bad,i) = NaN;
adcp.bt.corr(:,i) = dd(33:36);
adcp.bt.amp(:,i) = dd(37:40);

% some odd winriver stuff that may or may not be here....
lat=dd(5)+dd(6)*256+dd(47)*256^2+dd(48)*256^3;
if lat>2^32/2
  lat = -2^32+lat;
end
adcp.bt.wrlon(i)=lat*180/2^31;

lat=dd(13)+dd(14)*256+dd(15)*256^2+dd(16)*256^3;
if lat>2^32/2
  lat = -2^32+lat;
end
adcp.bt.wrlat(i)=lat*180/2^31;

%% function
function x = getshort(dd)
x = dd(1:2:end)+256*dd(2:2:end);
in = find(x>32767);
x(in)=-2*32768+x(in);

%% function
function x = getushort(dd)
x = dd(1:2:end)+256*dd(2:2:end);

%% 
function varl = initvarl(nens)
x=ones(1,nens)*NaN;
varl.time = x;
varl.ens = x;
varl.soundSpeed = x;
varl.compassHeading = x;
varl.pitch = x;
varl.roll = x;
varl.depth = x;
varl.temperature = x;
varl.salinity = x;
varl.pressure = x;
varl.compassHeading_std = x;
varl.pitch_std = x;
varl.roll_std = x;

%%
function varl=getvar(dat,off,varl,i)
%
ldr = [];
off = off+1;
dd = dat(off:end);
varl.ens(i)=dd(3) + dd(4)*2^8 + dd(12)*2^16;
dd(5)=dd(5)+2000; % force to the 21 century
varl.time(i)=datenum(dd(5:10)') + dd(11)/(24*3600*100);
varl.soundSpeed(i)=dd(15) + 256*dd(16);
varl.depth(i)=(dd(17) + 256*dd(18))*0.1;
varl.compassHeading(i)=(dd(19)+256*dd(20))*0.01;

varl.pitch(i)=(dd(21)+256*dd(22));
if varl.pitch(i)>=32768,varl.pitch(i)=-2*32768+varl.pitch(i); end % for (-)ve pitch
varl.pitch(i)=varl.pitch(i)*0.01;

varl.roll(i)=(dd(23)+256*dd(24));
if varl.roll(i)>=32768,varl.roll(i)=-2*32768+varl.roll(i); end  % for (-)ve rolls
varl.roll(i)=varl.roll(i)*0.01;

varl.salinity(i)=(dd(25) + 256*dd(26));
varl.temperature(i)=(dd(27) + 256*dd(28))*0.01;
varl.compassHeading_std(i)=dd(32);
varl.pitch_std(i)=dd(33)*0.1;
varl.roll_std(i)=dd(34)*0.1;
varl.pressure(i)=(dd(49) + dd(50)*2^8 + dd(51)*2^16 + dd(52)*2^24)*.001; % to db

%%
function vel=getvel(dat,offset,nbins)
offset = offset+2;
vel = dat(offset+2*[1:4*nbins]-1)+256*(dat(offset+2*[1:4*nbins]));
vel(vel>=32768)=-2*32768+vel(vel>=32768);

%%
function vel=getcor(dat,offset,nbins)
offset = offset+2;
vel = dat(offset+[1:4*nbins]);

%%
function vel=getint(dat,offset,nbins)
offset = offset+2;
vel = dat(offset+[1:4*nbins]);

%%
function vel=getpercgood(dat,offset,nbins)
offset = offset+2;
vel = dat(offset+[1:4*nbins]);

%%
function hexcs=checksum(lineHEX)
% function checksum(lineHEX)
pline=lineHEX(1:end-4);
nhex=fix(length(pline)/2); % get the actual size of the data block read
plinebyte=reshape(pline(1:nhex*2),2,nhex); % resize HEX vector string into 2xN matrix
dec=hex2dec(plinebyte'); % use internal hex to decimal conversion for each HEX pair
ncs=mod(sum(dec),65536);
mlhexcs=dec2hex(ncs,4);
hexcs(1:2)=mlhexcs(3:4);
hexcs(3:4)=mlhexcs(1:2);

% fini