function [buf, config] = read_buf(instclass, fid, params, config)
% read a chunk of BB binary data
%

out = params.out;
id = params.id;

if (nargin == 3)
   config = configinit(instclass);
end


start = ftell(fid);
[hdr, count] = fread(fid, 2, 'uint16');
if count ~= 2,
   buf = sprintf('error 1, only %d bytes at start of buffer\n', count);
   return
end
if hdr(1) ~= id.header % 2 bytes of 7Fh each
   buf = 'error 2, Header ID/Data Source not found in read_buf';

   return
end

config.nbytes = hdr(2);  % Total bytes up to, not including, checksum.
fseek(fid, start, 'bof'); %go back to same start location


%read the data into rec
[rec, count] = fread(fid, config.nbytes, 'uint8');
if count ~= config.nbytes,
   buf = sprintf('error 3, only %d bytes read, %d expected\n', ...
                 count, config.nbytes);
   return
end

%%% is this right???
test_cs = mod(sum(rec), 65536);
cs = fread(fid, 1, 'uint16');
if test_cs ~= cs,
   buf = ['error 4, Checksum failure; CS is ', int2str(cs),...
          ' versus calculated ', int2str(test_cs)];
   return
end

if nargin == 3, %still  set up config
   buf = [];

   config.ndata = rec(6);
   ii = 7 + 2 * (0:(config.ndata-1));
   config.offsets = rec(ii) + 256 * rec(ii+1);
   config.ids = rec(config.offsets + 1) + rec(config.offsets + 2) * 256;


   %%% VMDAS BUG (wherein config.ids(end) should have been navigation (8192) but is zero)
   if config.ids(end)==0;
      config.ids(end) = 8192;
      config.error = 'warning 1, fixed leader incorrect';
      fprintf('WARNING: fixed leader broken. Replacing last ID with navigation ID\n');
   end



   %   %%% fixed leader:
   %   lsb_base =                 5; 1           %least significant base
   %   msb_base =                 6; 1           %most significant base
   %   nbeams_used_base =         9; 1
   %   ncells_base =             10; 1
   %   pings_per_ens_base =      11; 2
   %   cell_base =               13; 2
   %   blank_base =              15; 2
   %   mode_base =               17; 1
   %   code_reps_base =          19; 1
   %   coord_transf_base =       26; 1
   %   heading_align_base =      27; 2
   %   heading_bias_base =       29; 2
   %   EZ_base =                 31; 1
   %   SA_base =                 32; 1
   %   bin1distance_base =       33; 2
   %   pulse_base =              35; 2
   %   lag_base =                41; 2

   ofs = config.offsets(find(config.ids == 0));

   config.nbeams_used =        rec(ofs + 9);
   config.ncells =             rec(ofs + 10);
   config.num_pings =          rec(ofs + 11) + 256 * rec(ofs + 12);
   config.cell =               0.01 * (rec(ofs + 13) + 256 * rec(ofs + 14));
   config.blank =              0.01 * (rec(ofs + 15) + 256 * rec(ofs + 16));
   config.mode =               rec(ofs + 17);
   config.COR_threshold =      rec(ofs + 18);
   config.code_reps =          rec(ofs + 19);
   config.PG_threshold =       rec(ofs + 20);
   config.EV_threshold =       .001*(rec(ofs + 21) + 256 * rec(ofs + 22));
   config.tpp_secs =           rec(ofs + 23)*60 + ...
                               rec(ofs + 24) + rec(ofs + 25)/100;
   config.coord_transf =       rec(ofs + 26);
   config.head_align =         0.01 * (rec(ofs + 27) + 256 * rec(ofs + 28));
   config.head_bias =          0.01 * (rec(ofs + 29) + 256 * rec(ofs + 30));
   if config.head_align > 655.35 / 2
      config.head_align = 655.36 - config.head_align;
   end
   if config.head_bias > 655.35 / 2
      config.head_bias = 655.36 - config.head_bias;
   end

   config.EZ =                 rec(ofs + 31);
   config.SA =                 rec(ofs + 32);
   config.pulse =              0.01 * (rec(ofs + 35) + 256 * rec(ofs + 36));
   config.bin1distance =       0.01 * (rec(ofs + 33) + 256 * rec(ofs + 34));
   config.lag =                0.01 * (rec(ofs + 41) + 256 * rec(ofs + 42));
   config.cpu_serialnumber =   2^(0*8) * rec(ofs + 43) + ...
                               2^(1*8) * rec(ofs + 44) + ...
                               2^(2*8) * rec(ofs + 45) + ...
                               2^(3*8) * rec(ofs + 46) + ...
                               2^(4*8) * rec(ofs + 47) + ...
                               2^(5*8) * rec(ofs + 48) + ...
                               2^(6*8) * rec(ofs + 49) + ...
                               2^(7*8) * rec(ofs + 50);
   config.WB =                 rec(ofs + 51) + 256 * rec(ofs + 52);
   config.pingtype = 'bb';


   lsb = rec(ofs + 5);
   msb = rec(ofs + 6);
   %%% need to fix this
   config.vel_multiplier = .001;  % m/s per count
   config.sysconfig = decode_sysconfig(lsb, msb);

   %find out whether BT was on or not
   ofs = min(config.offsets(find(config.ids == id.bt)));
   if isemptys(ofs)
      config.bt_was_on = 0;
      config.bt.BP = NaN;
      config.bt.BC = NaN;
      config.bt.BA = NaN;
      config.bt.BX = NaN;
   else
      config.bt_was_on = 1;
      config.bt.BP = rec(ofs + 3) + 256 * rec(ofs + 4);
      config.bt.BC = rec(ofs + 7);
      config.bt.BA = rec(ofs + 8);
      config.bt.BX = rec(ofs + 71) + 256 * rec(ofs + 72);
   end

   % make these have a more uniform nomenclature
   config.convex  = config.sysconfig.convex;
   if (config.sysconfig.up)
      config.orientation = 'up';
   else
      config.orientation = 'down';
   end

   config.beamangle = config.sysconfig.angle;
   config.frequency = config.sysconfig.frequency;

   % coordinate system
   instrument = 2^3;
   ship = 2^4;
   earth = 2^3+2^4;
   if (bitand(config.coord_transf, instrument) == 0) & ...
          (bitand(config.coord_transf, ship) == 0)
      config.coordsystem = 'beam';  % no transformation   xxx00xxx
   elseif (bitand(config.coord_transf, earth) == earth)
      config.coordsystem = 'earth';                    %  xxx11xxx
   elseif (bitand(config.coord_transf, instrument) == instrument)
      config.coordsystem = 'xyz';                      %  xxx10xxx
   elseif (bitand(config.coord_transf,ship) == ship)
      config.coordsystem = 'ship';                    %  xxx11xxx
   else
      fprintf('coordinate system error (EX) in %s\n', mfilename)
      config
   end

   config.sysconfig.cal_sspeed =  bitand(config.EZ, 2^6)/(2^6);

end

%otherwise, get data

%%% fixed leader, but possibly variable data:
ofs = config.offsets(find(config.ids == id.flead));
buf.num_pings = rec(ofs + 11) + 256 * rec(ofs + 12);

%%% variable leader:
ofs = config.offsets(find(config.ids == id.vlead));
if nargin == 3,
   config.tr_pressure = (rec(ofs + 17) + 256 * rec(ofs + 18))/10;
end
buf.ens_num = rec(ofs + 3) + 256 * rec(ofs + 4) + 65536 * rec(ofs + 12);

ymdhms = rec(ofs + (5:11));
ymdhms(1) = ymdhms(1) + 1900;
if (ymdhms(1) < 1980)  ymdhms(1) = ymdhms(1) + 100; end


if isemptys(params.yearbase)
   config.yearbase = ymdhms(1);
else
   config.yearbase = params.yearbase;
end
if isemptys(params.proc_yearbase)
    config.proc_yearbase = config.yearbase;
else
    config.proc_yearbase = params.proc_yearbase;
end



buf.dday = to_day( config.yearbase, ymdhms(1:6).') + ymdhms(7) / 8640000;


if (nargin == 3)
   return
end

%% otherwise, get data ============================================

if out.ts
   buf.heading =        rec(ofs + 19) + 256 * rec(ofs + 20);
   buf.pitch   =        rec(ofs + 21) + 256 * rec(ofs + 22);
   buf.roll    =        rec(ofs + 23) + 256 * rec(ofs + 24);
   buf.temperature =    rec(ofs + 27) + 256 * rec(ofs + 28);
   buf.soundspeed =     rec(ofs + 15) + 256 * rec(ofs + 16);
   buf.tr_pressure =       (rec(ofs + 17) + 256 * rec(ofs + 18))/10;
end
% In the following blocks, the "min" should not be needed
% when finding the offsets; we may eliminate it.
% The empty check is needed, however.

%%% velocity:
if out.vel
   ofs = min(config.offsets(find(config.ids == id.vel)));
   ii = 2 + (1:2:(config.ncells*8));
   if isemptys(ofs),
      a = NaN * ii;
   else
      a = rec(ofs + ii) + 256 * rec(ofs + 1 + ii);
   end
   buf.vel = reshape(a, 4, config.ncells).';
   % We are deferring the handling of negative numbers and NaNs.
end


%%% correlation:
if out.cor
   ofs = min(config.offsets(find(config.ids == id.cor)));
   ii = 2 + (1:(config.ncells*4));
   if isemptys(ofs)
      buf.cor = reshape(ii, 4, config.ncells).';
   else
      buf.cor = reshape(rec(ofs + ii), 4, config.ncells).';
   end
end

%%% amp:
if out.amp
   ofs = min(config.offsets(find(config.ids == id.amp)));
   ii = 2 + (1:(config.ncells*4));
   if isemptys(ofs)
      buf.amp = reshape(ii, 4, config.ncells).';
   else
      buf.amp = reshape(rec(ofs + ii), 4, config.ncells).';
   end
end


%%% percent good:
if out.pg
   ofs = min(config.offsets(find(config.ids == id.pg)));
   ii = 2 + (1:(config.ncells*4));
   if isemptys(ofs)
      buf.pg = reshape(ii, 4, config.ncells).';
   else
      buf.pg = reshape(rec(ofs + ii), 4, config.ncells).';
   end
end

%%% status starts at 1280


%% BT:
if out.bt & config.bt_was_on
   ofs = min(config.offsets(find(config.ids == id.bt)));
   ii = (0:2:6); iii = 0:3;

   buf.bt.range = rec(ofs + ii + 17) + 256 * rec(ofs + ii + 18) + ...
       65536 * rec(ofs + iii + 78);

   buf.bt.vel = rec(ofs + ii + 25) + 256 * rec(ofs + ii + 26);
   buf.bt.cor = rec(ofs + iii + 33);
   buf.bt.amp = rec(ofs + iii + 37);
   buf.bt.pg  = rec(ofs + iii + 41);


   badi = find(buf.bt.pg)==0;
   if ~isempty(badi)
      buf.bt.range(badi)=0;
      buf.bt.cor(badi)=0;
      buf.bt.amp(badi)=0;
   end


end

if out.nav
   ofs = min(config.offsets(find(config.ids == id.nav)));
   if isemptys(ofs)
      buf.nav.sec_pc_minus_utc = NaN;
      buf.nav.txy1 = NaN * ones(3,1);
      buf.nav.txy2 = buf.nav.txy1;
   else
      buf.nav.sec_pc_minus_utc = 1e-3 * buf_long(rec(ofs + (11:14)));

      %why are we doing this when buf.ymdhms is already defined above?
      %yymd = [config.yearbase, rec(ofs + 5) + 256 * rec(ofs + 6),...
      %        rec(ofs + 4) , rec(ofs + 3)];
      day_base = to_day(config.yearbase, ymdhms(1:3)');
      t1d = day_base + (1e-4 / 86400) * buf_long(rec(ofs + (7:10)));
      t2d = day_base + (1e-4 / 86400) * buf_long(rec(ofs + (23:26)));

      buf.nav.txy1(1) = t1d + round(buf.dday - t1d);
      buf.nav.txy2(1) = t2d + round(buf.dday - t2d);
      buf.nav.txy1(2:3) = decode_fix(rec(ofs + (15:22)));
      buf.nav.txy2(2:3) = decode_fix(rec(ofs + (27:34)));
   end
end


%% look for a bug in navigation:
ofs = min(config.offsets(find(config.ids == id.nav)));
if ~isemptys(ofs)
   hms1 = buf_long(rec(ofs + (7:10)));
   hms2 = buf_long(rec(ofs + (23:26)));

   nav1 =  decode_fix(rec(ofs + (15:22)));
   nav2 =  decode_fix(rec(ofs + (27:34)));

   % flag ensemble if VmDAS has screwed up

   %(this is the signature of a bad serial time stamp)
   if (((hms1 == 0) & (nav1(1) == 0) & (nav1(2)==0)) | ...
       ((hms2 == 0) & (nav2(1) == 0) & (nav2(2)==0)) )
      fprintf('WARNING: bad navigation data likely at ensemble %d (%s)\n',...
              buf.ens_num, to_date(config.yearbase,buf.dday,'s'))
      buf.nav.txy1(1:3) = NaN;
      buf.nav.txy2(1:3) = NaN;
   end
end

%---------------------------------------------
function s = to_signed(u)
s = u;
ineg = u > 32767;
s(ineg) = s(ineg) - 65536;
ibad = s == -32768;
s(ibad) = NaN;

%---------------------------------------------
function s = to_signed_long(u)
s = u;
ineg = u > 2147483647 ;
s(ineg) = s(ineg) - 4294967296;
ibad = s == -2147483648;
s(ibad) = NaN;

%---------------------------------------------
function s = buf_long(a)
s = a(1) + 256 * a(2) + 65536 * a(3) + 16777216 * a(4);
s = to_signed_long(s);

%---------------------------------------------
function xy = decode_fix(r)
xy = [buf_long(r(5:8)); buf_long(r(1:4))] * 180 / 2147483648;

%---------------------------------------------
