function [imagedata, headangle, prange, settings] = ... showfan(fname, plottype, settings); % SHOWFAN - Plot Imagenex imaging sonar data % [imagedata, headangle, prange, settings] = showfan(fname, plottype, settings) % % fname = file name % imagedata = the image as [angle, point] % headangle = the head position in degrees % prange = profile range in cm % metadata: % settings.SectorSweep % settings.StepSize % settings.DataPoints % settings.Height % settings.datayear % plottype = 'square' | 'polar' | 'prange' % Written by Marinna Martini 8/28/02 % for the U.S. Geological Survey, WOods Hole Field Center % updated 2/18/03 on the PASTA turn around % Last revised by Chris Sherwood, USGS, 4/29/03 % Notes to Marinna and Peter Howd: % There are a couple of weird things: % 1) imagedata(:,449) = 252 all the time. I just blank it out...is that % one point too far out the range? % 2) the real head angle goes retrograde sometimes. This is either a weird reading % in the file, or maybe an integer math thing with the angle calcs. I % make fake angles. % Test values: if(nargin == 0), %fname = 'G:\Feb03EuroStrat\701-Particle\701im\I1124\S1112313.F50' fname = 'C:\crs\data\EuroStrat_0203\701-Particle\701im\I1124\S1112313.F50' plottype = 'polar'; %plottype = 'square'; end if exist('fname') ~= 1, [fname, fpath] = uigetfile('c:\projects\euro\701im\*.f*','Fan beam data'); if fname == 0, return, end else [fpath,fname,ext,ver] = fileparts(fname); fname = [fname ext]; fpath = [fpath '\']; end if exist('plottype') ~= 1, plottype = 'square'; end if exist('settings') ~= 1, % get the metadata from the user prompt = {'Enter the sector that was swept:',... 'Enter the center to sweep around:',... 'Enter the Step size used:',... 'Enter the data points setting (points/10):',... 'Enter the sonar''s height off the bottom, m:',... 'Enter the year the instrument was deployed:',... }; % settings for Eurostrat fall deployment, mooring 701 def = {'348','0','1','500','0.55','2002'}; dlgtitle = 'FAN BEAM Input metadata for the setup'; lineNo = 1; dlgresult = inputdlg(prompt,dlgtitle,lineNo,def); settings.SectorSweep = str2num(dlgresult{1}); settings.AngleSweepAround = str2num(dlgresult{2}); settings.StepSize = str2num(dlgresult{3}); settings.DataPoints = str2num(dlgresult{4}); % number of 'ranges' recorded in each ping settings.Height = str2num(dlgresult{5}); settings.datayear = str2num(dlgresult{6}); end % fan beam specific things %settings.DegPerStep = 0.3; settings.DegPerStep = 0.15; pingsperrec = ((settings.SectorSweep./(settings.StepSize.*settings.DegPerStep))+1)*2; fid = fopen([fpath, fname],'r'); if fid == -1, disp(['error reading file ',fpath, fname]); return else disp(['Now reading file ', fpath, fname]); end % standard filename format is S1MMDDHH.PMM = S1082715.p30 gdate = [settings.datayear str2num(fname(3:4)) str2num(fname(5:6)) str2num(fname(7:8)) str2num(fname(11:12)) 0]; jdate = julian(gdate); nheaderbytes = 13; % skip the 61 bytes of file header [fheader, numread] = fread(fid,61,'uchar'); % decipher the first 13 bytes of sonar header so we know what we are reading [sheader, numread] = fread(fid,nheaderbytes,'uchar'); disp('The header') sheader' % imagenex return data header will tell us the total number of return data bytes % conver the first three bytes into an ASCII string return_data_header = char(sheader(1:3,:))'; disp(sprintf('Return Data Header = %s',return_data_header)); head_ID = dec2hex(sheader(4)); if ~strcmp(head_ID,'10'), disp('This is not a Model 881-000-100 360 degree scanning head sonar!') disp('This program cannot read this data.') return else disp('Verified that data is from a Model 881-000-100 360 degree scanning head sonar') end % we can skip the serial status for now % head position headposhi = bitand(63,sheader(7)); headposlo = bitand(127,sheader(6)); headpos = headposhi*127+headposlo; stepdir = bitget(sheader(7),6); % translate from 0 to 2800 meaning -210 to 210 in 0.15 degree steps headangle = 0.15*(headpos-1400); disp(sprintf('Head angle = %f', headangle)) % sonar head range settings.SonarRange = sheader(8); disp(sprintf('Sonar Head Range setting = %d m',settings.SonarRange)); % profile range prangehi = bitand(127,sheader(10)); prangelo = bitand(127,sheader(9)); prange = prangehi*127+prangelo; disp(sprintf('First Digitized Range Value Above Threshold = %d cm', prange)) % Number of echo data bytes returned ndatabyteshi = bitand(127,sheader(12)); ndatabyteslo = bitand(127,sheader(11)); ndatabytes = ndatabyteshi*127+ndatabyteslo; disp(sprintf('Number of data bytes = %d', ndatabytes)) % given the header info, figure out how many data point we're dealing with % the number of data bits is hardwired to 8 in the logger source code (new_bits) ndatabits = 8; switch(return_data_header), case {'IMX'}, % switch data command for data points = 25 if ndatabits == 4, nreturnbytes = 141; end % 128 Data Byes, 256 Points if ndatabits == 8, nreturnbytes = 265; end % 252 Data Byes, 252 Points if ndatabits == 14, nreturnbytes = 513; end % 500 Data Byes, 250 Points case {'IGX'}, % switch data command for data points = 50 if ndatabits == 4, nreturnbytes = 265; % 252 Data Byes, 504 Points else nreturnbytes = 513; % 500 Data Byes, 500 Points % 500 Data Byes, 250 Points end case {'IQX'}, % switch data command for data points = 200 switch(range), case 5, if ndatabits == 4, nreturnbytes = 263; end % 250 Data Byes, 500 Points if ndatabits == 8, nreturnbytes = 513; end % 500 Data Byes, 500 Points if ndatabits == 14, nreturnbytes = 1013; end % 1000 Data Byes, 500 Points case 10, if ndatabits == 4, nreturnbytes = 513; end % 500 Data Byes, 1000 Points if ndatabits == 8, nreturnbytes = 1013; end % 1000 Data Byes, 1000 Points if ndatabits == 14, nreturnbytes = 2013; end % 2000 Data Byes, 1000 Points case 15, if ndatabits == 4, nreturnbytes = 763; end % 750 Data Byes, 1500 Points if ndatabits == 8, nreturnbytes = 1513; end % 1500 Data Byes, 1500 Points if ndatabits == 14, nreturnbytes = 3013; end % 3000 Data Byes, 5500 Points otherwise, if ndatabits == 4, nreturnbytes = 1013; end % 1000 Data Byes, 2000 Points if ndatabits == 8, nreturnbytes = 2013; end % 2000 Data Byes, 2000 Points if ndatabits == 14, nreturnbytes = 4013; end % 4000 Data Byes, 2000 Points end case {'IPX'}, % Profile = ON nreturnbytes = 13; % 0 Data Byes, 0 Points end disp(sprintf('Number of return bytes per ping = %d',nreturnbytes)); npoints = nreturnbytes - nheaderbytes; % now figure out how many pings of data are in this file from the file size and header info. % 61 bytes of file header fseek(fid,0,'bof'); filebeg = ftell(fid); fseek(fid,0,'eof'); % go to the end of the file filebytes = ftell(fid)-filebeg-118; % byte from beginning of file nangles = round(filebytes./nreturnbytes); disp(sprintf('Number of useable pings = %d (maybe)',nangles)) frewind(fid); % skip the 61 bytes of file header [fheader, numread] = fread(fid,61,'uchar'); % guess at a dataholdersize imagedata = zeros(nangles, nreturnbytes-nheaderbytes); headangle = zeros(nangles, 1); prange = zeros(nangles,1); pingidx = 0; while 1, pingstart = ftell(fid); if pingstart < 0, break; end pingidx = pingidx+1; % read header [sheader, numread] = fread(fid,nheaderbytes,'uchar'); if numread < nheaderbytes, break; end % grab the position % head position headposhi = bitand(63,sheader(7)); headposlo = bitand(127,sheader(6)); headpos = headposhi*127+headposlo; stepdir = bitget(sheader(7),6); % translate from 0 to 2800 meaning -210 to 210 in 0.15 degree steps headangle(pingidx) = 0.15*(headpos-1400); % profile range prangehi = bitand(127,sheader(10)); prangelo = bitand(127,sheader(9)); prange(pingidx) = prangehi*127+prangelo; % read image data [pingdata, numread] = fread(fid,nreturnbytes-nheaderbytes,'uint8'); if numread < nreturnbytes-nheaderbytes, break; end imagedata(pingidx,:) = pingdata'; end fclose(fid); slantrange = settings.SonarRange/npoints:(settings.SonarRange/npoints):settings.SonarRange; sectorswept = 1:settings.DegPerStep:settings.SectorSweep+settings.DegPerStep+1; % trim the image to useable area [nangles, npoints] = size(imagedata); % trim the redundant first and last ping imagedata = imagedata(2:nangles-1,:); headangle = headangle(2:nangles-1); prange = prange(2:nangles-1); sectorswept = sectorswept(2:nangles-1); % trim range to real area of data nearfieldcutoff = 52; farfieldcutoff = npoints; imagedata = imagedata(:,nearfieldcutoff:farfieldcutoff); slantrange = slantrange(nearfieldcutoff:farfieldcutoff); % now get the new size for plotting [nangles, npoints] = size(imagedata); % other calculations %horizrange = slantrange.*acos(asin((settings.Height./slantrange))); %horizrange = settings.Height.*cos(asin(slantrange./settings.Height)); horizrange = real((slantrange.^2 - settings.Height^2).^(1/2)); if exist('plottype') == 1, switch plottype case 'square' % plot linear set(gcf,'name','Square') set(gcf,'numbertitle','off') clims = [0 190]; imagesc(headangle,horizrange,imagedata',clims) colormap(bone); colorbar ylabel('Horizontal Range, meters') xlabel('Head Position, degrees') text(0.01,0.05,[fname,' Fan beam image taken ',... datestr(datenum(gdate))],... 'units','normalized','color','w') case 'polar' fillval = 4; % sets value of no-data region % median value is 8, mean is 12, range is 0 - 32 rot = 302-90; % rotate image to true north (trust me on this one) max_range = 5; dxy = 0.01; % Key setting...determines image resolution at cost of % speed (reasonable range 0.02 to 0.005) figure(1) clf set(gcf,'name','Polar') set(gcf,'numbertitle','off') Xplot = (- max_range:dxy:max_range); Yplot = Xplot; [Xi, Yi] = meshgrid(Xplot,Yplot); [thi, ri]=cart2pol(Xi,Yi); thi = thi*180/pi; thi = thi+rot; thi(thi>180)=thi(thi>180)-360; thi(thi< -180)=thi(thi< -180)+360; fakeheadangle = linspace(headangle(1), headangle(nangles), ... nangles)'; fakeheadangle = -fakeheadangle; % because sonar scans from -160 to +170 % pad ranges slantrange = [0,slantrange(1,1)-.1,... slantrange(1,:), slantrange(1,npoints)+.1, 9]; % pad angles to get complete circle lopad = [180; fakeheadangle(1)+.01]; hipad = [fakeheadangle(nangles)-.01; -179.999]; nlo = length(lopad); nhi = length(hipad); fakeheadangle = [lopad(:); fakeheadangle; hipad(:)]; % pad data Zpad = (fillval)*ones(nangles+nlo+nhi,npoints+4); imagedata(imagedata<(1))=(fillval); % kludge...last range values always seems to be 252 imagedata(:,449)=(fillval); Zpad(nlo+1:nangles+nlo,3:npoints+2)=imagedata; Zpad = log10(Zpad); % interpolate [ripad,thpad] = meshgrid(slantrange,fakeheadangle); Zi = interp2( ripad,thpad,Zpad, ri, thi); % Zi(Zi<1)=(8); % produces weird contrasty plot imagesc(Xplot,Yplot,Zi) set(gca,'ydir','normal') % because imagesc is sidedownup colormap gray; axis equal tight if(1), % plot range circles hold on for icirc = 1:5, h = circle(icirc,0,0); set(h,'color',[.7 .7 .6]) end end text(0.01,0.03,fname,... 'units','normalized','color','y','fontsize',14) text(0.8,0.95,'\uparrow North',... 'units','normalized','color','y','fontsize',14) text(0.6,0.03,datestr(datenum(gdate)),... 'units','normalized','color','y','fontsize',14) case 'glacial' % this is really slow set(gcf,'name','Polar') set(gcf,'numbertitle','off') [th, r] = meshgrid(sectorswept.*(pi/180), horizrange); [X, Y] = pol2cart(th, r); %Z = imagedata'; Z = log10(imagedata+eps)'; surf(X, Y, Z) view([0 90]); % az, el shading flat colormap jet text(0.01,0.05,[fname,' Fan beam image taken ',datestr(datenum(gdate))],... 'units','normalized','color','k','fontsize',9) %axis equal tight %set(gca,'xlim',[0 settings.Height+0.5]); case 'prange' % plot headposition and prange [ax, h1, h2] = plotyy(1:nangles,headangle,1:nangles,prange); title([fname,' Fan beam image taken ',datestr(datenum(gdate))]) xlabel('Head position and profile range by ping') ylabel('Deg') %set(ax(2),'ylabel',text(0,0,'string','Prange')) otherwise % no plot end end