function [imagedata, Xplot, Yplot, Zi, settings] = ... showfan(fname, plottype, settings ); % SHOWFAN - Plot Imagenex imaging sonar data % [imagedata, headangle, prange, settings] = showfan(fname, plottype, settings) % % s.fname = file name % s.imagedata = the image as [angle, point] % s.headangle = the head position in degrees (from the Sonar) % s.fakeangle = what we think it is, in true % s.prange = profile range in cm % s.dn = Matlab datenum time % metadata: % s.settings: % settings.SectorSweep % settings.StepSize % settings.DataPoints % settings.Height % settings.Datayear % plottype = 'square' | 'polar' | 'prange' % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Use of this program is self described. % Program written in Matlab v7.1.0 SP3 % Program ran on PC with Windows XP Professional OS. % % "Although this program has been used by the USGS, no warranty, % expressed or implied, is made by the USGS or the United States % Government as to the accuracy and functioning of the program % and related program material nor shall the fact of distribution % constitute any such warranty, and no responsibility is assumed % by the USGS in connection therewith." %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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 % - added slant range, angle, and fake angle to return values; % put them in a structure. % - add polar plot % 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. % Last revised by Chris Sherwood, USGS, 3/5/04 % Last revised by Charlene Sullivan, USGS, 9/15/05 % Test values: if(nargin == 0), fname = 'E:\CSULLIVAN\SOUTH_CAROLINA\DATAFILES\752\7523im\I0213\S1013120.F20'; %fname = 'E:\CSULLIVAN\SOUTH_CAROLINA\DATAFILES\732\7323im\I1113\S1111214.F50'; %fname = 'E:\EuroStrat\701im\images_for_movies\I1117\S1111608.F20'; % don't delete these %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'}; % settings for Myrtle Beach deployment are same as Eurostrat except for % the year def = {'348','0','1','500','0.55','2003'}; 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); disp('Done reading file.') settings 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' settings.fillval = 4; % sets value of no-data region % median value is 8, mean is 12, range is 0 - 32 % rotating by 180-17 will point EuroSTRATAFORM ADCP (beam 3) to north % rot = 180-17; % during first deployment, ADCP heading was 41 deg M = 43 deg T: % rot = 180-17+43 % during second deployment, ADCP heading was 274 deg M = 276 deg T: %rot = 180-17+(276-360) %rot = settings.rot; % we don't yet know direction of north for Myrtle Beach deployment, so % for now set rot = 0; %rot = 0; % fall Myrtle Beach deployment ADCP heading was 149 deg M = 141 deg T rot = settings.rot; % spring Myrtle Beach deployment ADCP heading was 253 deg M = 245 deg T %rot = settings.rot; % settings.max_range = 5; settings.dxy = 0.01; % Key setting...determines image resolution at cost of % speed (reasonable range 0.02 to 0.005) Xplot = (- settings.max_range:settings.dxy:settings.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 = (settings.fillval)*ones(nangles+nlo+nhi,npoints+4); imagedata(imagedata<(1))=(settings.fillval); % kludge...last range values always seems to be 252 imagedata(:,449)=(settings.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; set(gca,'Fontsize',12); set(gca,'Xtick',[-5:1:5]) set(gca,'Ytick',[-5:1:5]) %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','horizontalalignment','left','fontsize',10) text(0.8,0.95,'\uparrow North',... 'units','normalized','color','y','fontsize',10) ts = datestr(datenum(gdate)); text(0.99,0.03,ts,... 'units','normalized','color','y','horizontalalignment','right','fontsize',10) 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 %return values Xplot = slantrange; Yplot = headangle; Zi = imagedata; end end keep3 imagedata Xplot Yplot Zi settings