function [data,header]=read_grads(file_name,var_name,varargin) % [var,header]=read_grads(file_name,var_name) is a function to read % in binary files using their GrADS descriptor header. % If var_name is 'all' or non-existent, all variables are read and % sent to the base-workspace. % Please check before-hand the data-type (big/little endian), as well % as the data precision (single/double...), and adjust if necessary % the header at the beginning of read_grads.m. % Optionally, you can improve the indications from the header file in % the sub-function grads_name (hereafter). % % Kristof Sturm, sturm@dkrz.de or sturm@lgge.obs.ujf-grenoble.fr, 29.10.02 global l_quiet n=strmatch('quiet',{varargin{1:2:end}}); if isempty(n) l_quiet=0; else switch lower(varargin{2*(n-1)+2}) case {1,'yes'} l_quiet=1; case {0,'no'} l_quiet=0; end end header=struct('FILENAME',file_name,... 'VARSIZE',{[]},... 'NVAR',0,... 'DATANAME','-',... 'FID',{[]},... 'BINTYPE','-',... 'BINPRECISION','float32',... 'DSET','-',... 'DTYPE',1,... 'INDEX','-',... 'TITLE','-',... 'UNDEF',0,... 'OPTIONS',{[]},... 'XDEF',{[]},... 'YDEF',{[]},... 'ZDEF',{[]},... 'TDEF',{[]},... 'VARS',struct([]),... 'FILEHEADER',0,... 'THEADER',0,... 'XYHEADER',0); header.XDEF.rev=0; header.YDEF.rev=0; header.ZDEF.rev=0; % [comp,max_size,endian]=computer; endian='L'; if endian=='L' header.BINTYPE='ieee-le'; elseif endian=='B' header.BINTYPE='ieee-be'; end data=[]; file_name=deblank(file_name); if exist(file_name,'file') fid_ctl = fopen(file_name,'r'); % disp(['CTL opened: ',num2str(fid_ctl)]) % [header.FID]=deal([fid_ctl 0]); header(1).FID{1}=fid_ctl; if isunix, sep='/'; else sep='\\'; end if ~isempty(strfind(file_name,sep)) slash=max(strfind(file_name,sep)); header(1).DIR=file_name(1:slash); file_name=file_name((slash+1):end); end header(1).FILENAME=file_name; else error(['Inexistent header file ',file_name]) end % Enabling read_grads to handle netcdf files. point=strfind(file_name,'.'); suf=lower(file_name(point(end)+1:end)); if strcmp(suf,'ctl') % reading the .ctl file : while ~feof(fid_ctl) line=fgets(fid_ctl); if isempty(line(1:end-1)), break, end % cutting the line into words. line_gaps=isspace(line); % removing consecutive blancks. while line_gaps(1)==1, line_gaps(1)=[]; line(1)=[]; end if line(1)=='*' if ~l_quiet disp(line) end else no_gaps=0; while ~no_gaps line_gap2=find(line_gaps); if any(diff(line_gap2)==1) dup=1+find(diff(line_gap2)==1); line_gaps(line_gap2(dup))=[]; line(line_gap2(dup))=[]; else no_gaps=1; end end entry=line(1:(min(find(line_gaps))-1)); words={''}; for n=1:(sum(line_gaps)-1) words{n}=line((line_gap2(n)+1):(line_gap2(n+1)-1)); end header=read_header(entry,words); % disp(words) end end % Opening the binary file for later reading: fid_bin = fopen(header.DATANAME,'r', header.BINTYPE ); % disp(['BIN opened: ',num2str(fid_bin)]) [header(1).FID] = {fid_ctl, fid_bin}; fclose(fid_ctl); nvar=size(header.VARS,1); header.NVAR=nvar; if nargin>1 & isempty(var_name) if ~l_quiet disp(['Header for ',header.FILENAME,' read.']) end fclose(header.FID{2}); return end if nargout==0 assignin('caller','header',header) assignin('caller','vars',vars); end if header.DTYPE % Gridded data set % Computing the size of each variable: var_size=zeros(nvar,4); % array of the var. size [X,Y,Z,T] var_size(:,1)=header.XDEF.num; var_size(:,2)=header.YDEF.num; var_size(:,3)=cat(1,header.VARS.levs); var_size(:,4)=header.TDEF.num; header.VARSIZE={var_size}; if exist('grads_name','file') [vars,header]=grads_name(header,vars); end % reading the bin. file : if nargin == 1 | strcmp(lower(var_name),'all') read_data(header); elseif nargin == 2 t_limits=[1 header.TDEF.num]; ivar=strmatch(var_name,{header.VARS.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end z_limits=[1 header.VARS(ivar).levs]; y_limits=[1 header.YDEF.num]; x_limits=[1 header.XDEF.num]; data=read_var(header,var_name,t_limits,z_limits,y_limits,x_limits); header.XDEF.vec=header.XDEF.vec(x_limits(1):x_limits(2)); header.XDEF.num=length(header.XDEF.vec); header.YDEF.vec=header.YDEF.vec(y_limits(1):y_limits(2)); header.YDEF.num=length(header.YDEF.vec); else for n=1:2:nargin-2 switch lower(varargin{n}) case {'x'} x_limits=varargin{n+1}; case {'lon'} lon_limits=sort(varargin{n+1}); x_limits=interp1(header.XDEF.vec,1:header.XDEF.num,... lon_limits,'nearest',NaN); if any(isnan(x_limits)) if ~l_quiet disp(['The X-limits ',num2str(lon_limits),... ' exceed the data coverage ',... num2str(header.XDEF.vec([1 end]))]) end data=[]; return end case {'y'} y_limits=varargin{n+1}; case {'lat'} lat_limits=sort(varargin{n+1}); y_limits=interp1(header.YDEF.vec,1:header.YDEF.num,... lat_limits,'nearest',NaN); if any(isnan(y_limits)) if ~l_quiet disp(['The Y-limits ',num2str(lat_limits),... ' exceed the data coverage ',... num2str(header.YDEF.vec([1 end]))]) end data=[]; return end if header.YDEF.rev, y_limits=header.YDEF.num*ones(size(y_limits))-fliplr(y_limits)+1; end case {'t','time'} t_limits=varargin{n+1}; case {'z','lev'} z_limits=varargin{n+1}; ivar=strmatch(var_name,{header.VARS.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end if any(z_limits>header.VARS(ivar).levs) if ~l_quiet disp(['The variable ',var_name,' has only ',... header.VARS(ivar).levs,' levels.']) end data=[]; return end end end if ~exist('t_limits','var'), t_limits=[1 header.TDEF.num]; end if ~exist('z_limits','var') ivar=strmatch(var_name,{header.VARS.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end z_limits=[1 header.VARS(ivar).levs]; end if ~exist('y_limits','var') y_limits=[1 header.YDEF.num]; end if ~exist('x_limits','var') x_limits=[1 header.XDEF.num]; end data=read_var(header,var_name,t_limits,z_limits,y_limits,x_limits); header.XDEF.vec=header.XDEF.vec(x_limits(1):x_limits(2)); header.XDEF.num=length(header.XDEF.vec); if header.YDEF.rev, y_limits=header.YDEF.num*ones(size(y_limits))-fliplr(y_limits)+1; end header.YDEF.vec=header.YDEF.vec(y_limits(1):y_limits(2)); header.YDEF.num=length(header.YDEF.vec); end % if header.XDEF.rev, header.XDEF.vec=header.XDEF.vec([end:-1:1]); end % if header.YDEF.rev, header.YDEF.vec=header.YDEF.vec([end:-1:1]); end % if header.ZDEF.rev, header.ZDEF.vec=header.ZDEF.vec([end:-1:1]); end else % Station data set data=read_station(header); end fclose(header.FID{2}); elseif strcmp(suf,'nc') % Enabling NetCDF in read_grads, based on snctools old_dir=pwd; if isfield(header,'DIR') cd(header.DIR) end % if % Reading the header, conforming to GrADS conventions: ncheader=nc_info(file_name); header.FILENAME=ncheader.Filename; header.DATANAME=ncheader.Filename; header.NVAR=length(ncheader.DataSet); fields={{'lon','longitude','x','i'},'XDEF'; {'lat','latitude','y','j'},'YDEF'; {'lev','plev','nv','bnds'},'ZDEF'; {'time','tps','t','l'},'TDEF'}; for idim=1:size(fields,1) for dim=fields{idim,1} dim_id=strmatch(dim{1},{ncheader.DataSet.Name},'exact'); if ~isempty(dim_id), break, end end % for if isempty(dim_id) disp(['Dim ',fields{idim,2},' is missing.']) vec=1; num=1; type='LINEAR'; else num=ncheader.DataSet(dim_id).Size; vec=nc_varget(file_name,dim{1}); if length(unique(diff(vec)))==1 type='LINEAR'; else type='LEVELS'; end if all(diff(vec)<0) rev=1; vec=flipud(reshape(vec,[],1)); elseif all(diff(vec)>0) rev=0; else error(['Unsuitable vector ',dim{1}]) end end % if DEF=struct('num',num,'vec',vec,'type',type,'rev',rev); header=setfield(header,fields{idim,2},DEF); end % for idim for ivar=1:header.NVAR header.VARS(ivar).id=ivar; header.VARS(ivar).name=ncheader.DataSet(ivar).Name; header.VARS(ivar).size=ones(1,4); for idim=1:length(ncheader.DataSet(ivar).Dimension) [dim_id,j]=ind2sub(size(cat(1,fields{:,1})),... strmatch(ncheader.DataSet(ivar).Dimension(idim),... cat(1,fields{:,1}),'exact')); header.VARS(ivar).size(dim_id)=... getfield(eval(['header.',fields{dim_id,2}]),'num'); end % for header.VARS(ivar).levs=header.VARS(ivar).size(3); end % for header.VARSIZE={cat(1,header.VARS.size)}; if nargin==1 | isempty(var_name) | strcmp(var_name,'all') data=[]; if exist('old_dir','var') cd(old_dir) end return end var_id=strmatch(var_name,{ncheader.DataSet.Name}); if isempty(var_id) error(['Variable ',var_name,' is not available in ',file_name,... ': ',{ncheader.DataSet.Name}]) end % if for n=1:2:nargin-2 switch lower(varargin{n}) case {'x'} x_limits=varargin{n+1}; case {'lon'} lon_limits=sort(varargin{n+1}); x_limits=interp1(header.XDEF.vec,1:header.XDEF.num,... lon_limits,'nearest',NaN); if any(isnan(x_limits)) if ~l_quiet disp(['The X-limits ',num2str(lon_limits),... ' exceed the data coverage ',... num2str(header.XDEF.vec([1 end])')]) end data=[]; return end case {'y'} y_limits=varargin{n+1}; case {'lat'} lat_limits=sort(varargin{n+1}); y_limits=interp1(header.YDEF.vec,1:header.YDEF.num,... lat_limits,'nearest',NaN); if any(isnan(y_limits)) if ~l_quiet disp(['The Y-limits ',num2str(lat_limits),... ' exceed the data coverage ',... num2str(header.YDEF.vec([1 end])')]) end data=[]; return end if header.YDEF.rev, y_limits=header.YDEF.num*ones(size(y_limits))-fliplr(y_limits)+1; end case {'t','l','time'} t_limits=varargin{n+1}; case {'z','k','lev'} z_limits=varargin{n+1}; ivar=strmatch(var_name,{header.VARS.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end if any(z_limits>header.VARS(ivar).levs) if ~l_quiet disp(['The variable ',var_name,' has only ',... header.VARS(ivar).levs,' levels.']) end data=[]; return end end % switch end % for if ~exist('t_limits','var'), t_limits=[1 header.TDEF.num]; end if ~exist('z_limits','var') ivar=strmatch(var_name,{header.VARS.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end z_limits=[1 header.VARS(ivar).levs]; end % if if ~exist('y_limits','var') y_limits=[1 header.YDEF.num]; end if ~exist('x_limits','var') x_limits=[1 header.XDEF.num]; end % data=read_var(header,var_name,t_limits,z_limits,y_limits,x_limits); % Retrieve the corresponding variable: if all(header.VARS(var_id).size([3 4])==[1 1]) data=nc_varget(file_name,var_name,... [y_limits(1),x_limits(1)]-1,... diff(cat(1,y_limits,x_limits),1,2)'+1); elseif header.VARS(var_id).size(3)==1 data=nc_varget(file_name,var_name,... [t_limits(1),y_limits(1),x_limits(1)]-1,... diff(cat(1,t_limits,y_limits,x_limits),1,2)'+1); elseif header.VARS(var_id).size(4)==1 data=nc_varget(file_name,var_name,... [z_limits(1),y_limits(1),x_limits(1)]-1,... diff(cat(1,z_limits,y_limits,x_limits),1,2)'+1); else data=nc_varget(file_name,var_name,... [t_limits(1),z_limits(1),y_limits(1),x_limits(1)]-1,... diff(cat(1,t_limits,z_limits,y_limits,x_limits),1,2)'+1); end header.XDEF.vec=header.XDEF.vec(x_limits(1):x_limits(2)); header.XDEF.num=length(header.XDEF.vec); if header.YDEF.rev, y_limits=header.YDEF.num*ones(size(y_limits))-fliplr(y_limits)+1; end if diff(y_limits)<0, error('Unsuitable Y limits'), end header.YDEF.vec=header.YDEF.vec(y_limits(1):y_limits(2)); header.YDEF.num=length(header.YDEF.vec); header.ZDEF.vec=z_limits(1):z_limits(2); header.ZDEF.num=diff(z_limits)+1; header.TDEF.vec=t_limits(1):t_limits(2); header.TDEF.num=diff(t_limits)+1; if length(size(data))==2 data=permute(data,[2 1]); elseif length(size(data))==3 & header.ZDEF.num==1 data=permute(data,[3 2 4 1]); elseif length(size(data))==3 & header.TDEF.num==1 data=permute(data,[3 2 1]); elseif length(size(data))==4 data=permute(data,[4 3 2 1]); end % if if header.YDEF.rev data=flipdim(data,2); end if exist('old_dir','var') cd(old_dir) end else disp(['Suffix .',suf,' is not yet enabled.']) end % if return %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % function header=read_header(entry,words) % sets the header values according to what was read in the .ctl-file. % cf. GrADS documentation : http://grads.iges.org/grads/gadoc/descriptorfile.html global l_quiet header=evalin('caller','header'); fid_ctl=header.FID{1}; grid_fields={'num','type','vec','rev'}; switch upper(entry) case 'DSET' % Name of the bin. data file. param=deal(words{1}); if param(1)=='^' & isfield(header,'DIR') param(1)=[]; param=[header.DIR,param]; elseif param(1)=='^' param(1)=[]; end if exist(param,'file') header.DATANAME=param; else error(['Non-existent file ',param]) end % case 'DTYPE' % Gridded or station data-file (not used later) param=lower(words{1}); switch param case 'grib' header.DTYPE=1; case 'station' header.DTYPE=0; end % case 'STNMAP' % Name of the map file for station data (set in INDEX) if header.DTYPE warning([header.FILENAME,' cannot specify STNMAP=',param,' for a gridded data set.']) end param=deal(words{1}); if param(1)=='^' param(1)=[]; end if exist(param,'file') header.INDEX=param; else error(['Non-existent file ',param]) end % case 'INDEX' % Name of the GRIB map file (not used later) [header.INDEX]=strcat(words{:}); % case 'TITLE' % Data title (not used later) line={words{:}}; space=cell(size(line)); [space{:}]=deal(char(32)); line=cat(1,line,space); line=reshape(line,1,prod(size(line))); % Rearranging the title to keep spaces between words. header.TITLE=deblank(cat(2,line{:})); % case 'UNDEF' % Missing or undefined data value param=words{1}; if isreal(str2num(param)) header(1).UNDEF=str2num(param); else error(['Wrong missing value format : ',param]) end % case 'OPTIONS' % Possible options (not all implemented...) for param=words param=param{1}; if ~ischar(param), error(['Wrong option format : ',param]), end switch lower(param) case 'big_endian' header.BINTYPE='ieee-be'; case 'cray_32bit_ieee' header.BINTYPE='cray'; case 'little_endian' header.BINTYPE='ieee-le'; case 'binprecision' % Strange syntax to eliminate digits in BINPRECISION param=words{2}(find(isnan(str2double(cellstr(upper(words{2})'))))); if any(strmatch(param,... {'uchar','schar','int','uint','float','single','double',... 'real*','integer*','bit','ubit','char','short','ushort', ... 'long','ulong'})) header.BINPRECISION=words{2}; end case 'xrev' header.XDEF.rev=1; case 'yrev' header.YDEF.rev=1; case 'zrev' header.ZDEF.rev=1; case 'sequential' % Structure of the binary file: ([4 bit][XYHEADER bit][XDEF.num % x YDEF.num x BINPRECISION bit][4 bit]) x NVAR header.SEQ=1; otherwise fprintf('Option %s not yet supported.\n',param); end end % for param=words % % $$$ Matlab C/Fortran Description % $$$ 'uchar' 'unsigned char' unsigned character, 8 bits. % $$$ 'schar' 'signed char' signed character, 8 bits. % $$$ 'int8' 'integer*1' integer, 8 bits. % $$$ 'int16' 'integer*2' integer, 16 bits. % $$$ 'int32' 'integer*4' integer, 32 bits. % $$$ 'int64' 'integer*8' integer, 64 bits. % $$$ 'uint8' 'integer*1' unsigned integer, 8 bits. % $$$ 'uint16' 'integer*2' unsigned integer, 16 bits. % $$$ 'uint32' 'integer*4' unsigned integer, 32 bits. % $$$ 'uint64' 'integer*8' unsigned integer, 64 bits. % $$$ 'single' 'real*4' floating point, 32 bits. % $$$ 'float32' 'real*4' floating point, 32 bits. % $$$ 'double' 'real*8' floating point, 64 bits. % $$$ 'float64' 'real*8' floating point, 64 bits. case 'XDEF' % Description of the X-axis (longitude usually) % Checking the number of x-grid coordinates: if isreal(str2num(words{1})) words{1}=str2num(words{1}); elseif str2num(words{1})>1 else error(['Wrong number of x-grid arguments : ', ... num2str(words{1})]) end switch upper(words{2}) case 'LINEAR' vec(1)=str2num(words{3}); vec(2)=str2num(words{4}); vec2=vec(1) + vec(2) * [0:(words{1}-1)]; words{3}=vec2; % KS, 24.11.2003: coord vector in all cases. case 'LEVELS' % $$$ try % all on the same line % $$$ for i=1:words{1} % $$$ vec(i)=str2num(words{i+2}); % $$$ end % $$$ words{3}=vec; % $$$ catch % all levels written column-wise % $$$ for i=1:words{1} % $$$ % vec(i)=fscanf(fid_ctl,'%i',1); % $$$ vec(i)=str2num(fgetl(fid_ctl)); % $$$ end % $$$ words{3}=vec; % $$$ end % vec=str2num([words{3:end}]'); % vec=str2num(cell2mat({words{3:end}}'))'; vec=str2num(strvcat(words{3:end})); while length(vec) < words{1} point=ftell(fid_ctl); read_line=str2num(fgetl(fid_ctl)); if isempty(read_line) fseek(fid_ctl,point,'bof'); break else vec=cat(2,vec,read_line); end end if length(vec)==words{1} words{3}=vec; else error(['Unsuitable number of X-levels : ',num2str(length(vec))]) end otherwise error(['Unsuitable x-mapping method : ',words{2}]) end % if header(1).XDEF.rev, words{3}=fliplr(words{3}); end [header(1).XDEF]=cell2struct({words{1},words{2},words{3},header(1).XDEF.rev},grid_fields,2); % case 'YDEF' % Description of the Y-axis (latitude usually) % Checking the number of y-grid coordinates: if isreal(str2num(words{1})) words{1}=str2num(words{1}); elseif str2num(words{1})>1 else error(['Wrong number of y-grid arguments : ', ... num2str(words{1})]) end switch upper(words{2}) case 'LINEAR' vec(1)=str2num(words{3}); vec(2)=str2num(words{4}); vec2=vec(1) + vec(2) * [0:(words{1}-1)]; words{3}=vec2; case 'LEVELS' % $$$ try % all on the same line % $$$ for i=1:words{1} % $$$ vec(i)=str2num(words{i+2}); % $$$ end % $$$ words{3}=vec; % $$$ catch % all levels written column-wise % $$$ for i=1:words{1} % $$$ % vec(i)=fscanf(fid_ctl,'%i',1); % $$$ vec(i)=str2num(fgetl(fid_ctl)); % $$$ end % $$$ words{3}=vec; % $$$ end % vec=str2num([words{3:end}]'); % vec=str2num(cell2mat({words{3:end}}'))'; vec=str2num(strvcat(words{3:end})); while length(vec) < words{1} point=ftell(fid_ctl); read_line=str2num(fgetl(fid_ctl)); if isempty(read_line) fseek(fid_ctl,point,'bof'); break else vec=cat(2,vec,read_line); end end if length(vec)==words{1} words{3}=vec; else error(['Unsuitable number of Y-levels : ',num2str(length(vec))]) end otherwise error(['Unsuitable y-mapping method : ',words{2}]) end % if header.YDEF.rev, words{3}=fliplr(words{3}); end [header(1).YDEF]=cell2struct({words{1},words{2},words{3},header(1).YDEF.rev},grid_fields,2); % case 'ZDEF' % Description of the Z-axis % Checking the number of z-grid coordinates: if isreal(str2num(words{1})) words{1}=str2num(words{1}); elseif str2num(words{1})>1 else error(['Wrong number of z-grid arguments : ', ... num2str(words{1})]) end switch upper(words{2}) case 'LINEAR' vec(1)=str2num(words{3}); vec(2)=str2num(words{4}); vec2=vec(1) + vec(2) * [0:(words{1}-1)]; words{3}=vec2; case 'LEVELS' % $$$ try % all on the same line % $$$ for i=1:str2num(words{1}) % $$$ vec(i)=str2num(words{i+2}); % $$$ end % $$$ words{3}=vec; % $$$ catch % all levels written column-wise % $$$ for i=1:words{1} % $$$ % vec(i)=fscanf(fid_ctl,'%i',1); % $$$ vec(i)=str2num(fgetl(fid_ctl)); % $$$ end % $$$ words{3}=vec; % $$$ end % vec=str2num([words{3:end}]'); % vec=str2num(cell2mat({words{3:end}}'))'; vec=str2num(strvcat(words{3:end})); while length(vec) < words{1} point=ftell(fid_ctl); read_line=str2num(fgetl(fid_ctl)); if isempty(read_line) fseek(fid_ctl,point,'bof'); break else vec=cat(2,vec,read_line); end end if length(vec)==words{1} words{3}=vec; else error(['Unsuitable number of Z-levels : ',num2str(length(vec))]) end otherwise error(['Unsuitable z-mapping method : ',words{2}]) end % if header.ZDEF.rev, words{3}=fliplr(words{3}); end [header(1).ZDEF]=cell2struct({words{1},words{2},words{3},header(1).ZDEF.rev},grid_fields,2); % case 'TDEF' % Description of the T-axis % Checking the number of t-grid coordinates: if isreal(str2num(words{1})) words{1}=str2num(words{1}); elseif str2num(words{1})>1 else error(['Wrong number of t-scale argument : ', ... num2str(words{1})]) end if any(upper(words{2})~='LINEAR') error(['Unsuitable t-mapping method : ',words{2}]) end param=words{3}; if (param(3)~=':' | param(6)~='Z' | ... ~isreal(str2num(param([1:2 4:5 7:8 12:length(param)])))) error(['Unsuitable date format : ',param]) else vec(1)=str2num(param(1:2)); % hour vec(2)=str2num(param(4:5)); % min vec(3)=str2num(param(7:8)); % day switch lower(param(9:11)) % month case 'jan' vec(4)=1; case 'feb' vec(4)=2; case 'mar' vec(4)=3; case 'apr' vec(4)=4; case 'may' vec(4)=5; case 'jun' vec(4)=6; case 'jul' vec(4)=7; case 'aug' vec(4)=8; case 'sep' vec(4)=9; case 'oct' vec(4)=10; case 'nov' vec(4)=11; case 'dec' vec(4)=12; otherwise error(['Wrong month format : ',param(9:11)]) end vec(5)=str2num(param(12:length(param))); % year if vec(5)<10 vec(5)=vec(5)+2000; elseif vec(5)<100 vec(5)=vec(5)+1900; end % The time-axis is written in the matlab-time format % (cf. datestr, datevec...): init_time=datenum(vec(5),vec(4),vec(3),vec(1),vec(2),0); param=words{4}; if isreal(str2num(param(1:end-2))) time_increment=str2num(param(1:end-2)); param(1:end-2)=[]; else error(['Wrong time increment argument : ',param]) end switch lower(param) case 'mn' time_increment=time_increment*datenum(0,0,0,0,1,0); case 'hr' time_increment=time_increment*datenum(0,0,0,1,0,0); case 'dy' % NB: datenum(0,0,1,0,0,0) = 1. time_increment=time_increment; case 'mo' % standardised month time_increment=time_increment*365.25/12; case 'yr' % standardised year time_increment=time_increment*365.25; otherwise error(['Wrong time increment argument : ',param]) end % Writing the final time axis: clear vec vec(1)=init_time; vec(2)=time_increment; [header(1).TDEF]=cell2struct({words{1},words{2},vec,0},grid_fields,2); end % case 'VARS' % Number and size of variables if isreal(str2num(words{1})) n_var = str2num(words{1}); else error(['Wrong variable number argument : ',words{1}]) end % Reading the variables : var_format={'id','name','levs','units','descr'}; varl={}; for i=1:n_var line_var=fgetl(fid_ctl); n_blank=length(line_var); line_var=strrep(line_var,' ',' '); while length(line_var) < n_blank n_blank=length(line_var); line_var=strrep(line_var,' ',' '); end while isspace(line_var(1)), line_var(1)=[]; end var_loc={i,'',NaN,'',''}; var_hole=min(find(isspace(line_var))); var_loc{2}=line_var(1:(var_hole-1)); line_var(1:var_hole)=[]; var_hole=min(find(isspace(line_var))); var_loc{3}=str2num(line_var(1:(var_hole-1))); % cf GrADS doc: If levs is 0, the variable does not correspond % to any vertical level var_loc{3}=max([var_loc{3},1]); line_var(1:var_hole)=[]; var_hole=min(find(isspace(line_var))); var_loc{4}=line_var(1:(var_hole-1)); line_var(1:var_hole)=[]; var_loc{5}=line_var; varl=cat(1,varl,var_loc); end vars=cell2struct(varl,var_format,2); if ~l_quiet disp(['Is it the end ? ',fgets(fid_ctl)]); else endvars=deblank(fgets(fid_ctl)); if ~strcmpi((endvars),'ENDVARS') error(['Wrong end of file : ',endvars]) end end header(1).VARS=vars; assignin('caller','vars',vars); % case 'FILEHEADER' % n-byte header of binary data, not to be read [header.FILEHEADER]=str2num(words{1}); % case 'THEADER' % n-byte header for each T-block, not to be read [header.THEADER]=str2num(words{1}); % case 'XYHEADER' % n-byte header for each XY-block, not to be read [header.XYHEADER]=str2num(words{1}); % otherwise error(['Unknown entry : ',entry]) end return %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % function data=read_data(header,var_name) % data=read_data(header,var_name) is a function, usually called by % read_grads.m to use the information contained in the header file % (.ctl) for reading a binary GrADS file. % If var_name is 'all', all variables are read and sent to the base- % workspace. % Kristof Sturm, 29.10.02 global l_quiet vars=header.VARS; nvar=header.NVAR; var_size=header.VARSIZE{:}; if nargin == 1 | strcmp(lower(var_name),'all') % All variables loaded into workspace data_list={}; % initialising the data matrices : var_count=zeros(nvar,1); for ivar=1:nvar eval([vars(ivar).name,'=NaN*ones(',int2str(header.XDEF.num),',',... int2str(header.YDEF.num),',',... int2str(vars(ivar).levs),',',... int2str(header.TDEF.num),');']); fprintf('Size of %s : [%s].\n %s[X Y Z T]\n',... vars(ivar).name,int2str(size(eval(vars(ivar).name))),... char(32*ones(1,length(vars(ivar).name)))); end method = 'loop'; % mainly for debugging... switch method case 'loop' frewind(header.FID{2}); % Implementing the sequential option: if isfield(header,'SEQ') && header.SEQ fseek(header.FID{2},4,'bof'); end if header.FILEHEADER fseek(header.FID{2},header.FILEHEADER,'bof'); % $$$ if isfield(header,'SEQ') && header.SEQ % $$$ fseek(header.FID{2},8,'cof'); % $$$ end end for l=1:header.TDEF.num if header.THEADER fseek(header.FID{2},header.THEADER,'cof'); % $$$ if isfield(header,'SEQ') && header.SEQ % $$$ fseek(header.FID{2},8,'cof'); % $$$ end end for ivar=1:nvar data=eval(vars(ivar).name); count0=var_count(ivar); for k=1:vars(ivar).levs if header.XYHEADER fseek(header.FID{2},header.XYHEADER,'cof'); end [data(:,:,k,l) count1]=fread(header.FID{2},[header.XDEF.num header.YDEF.num],header.BINPRECISION); count0=count0+count1; if isfield(header,'SEQ') && header.SEQ fseek(header.FID{2},8,'cof'); end end % end k-loop data(data==header.UNDEF)=NaN; % replacing the % missing/undefined values % by NaN (not-a-number) % data(data==header.UNDEF)=0; % replacing the % missing/undefined values by % NaN (not-a-number) % data=sparse(data); eval([vars(ivar).name,'=data;']); var_count(ivar)=var_count(ivar)+count0; % bytes(ivar)=ftell(header.FID{2}); clear data end % end ivar-loop end % end l-loop % Send the relevent variables into the base workspace: % assignin('base','var_count',var_count); % assignin('base','bytes',bytes); for ivar=1:nvar data=eval(vars(ivar).name); if header.XDEF.rev, data=flipdim(data,1); end if header.YDEF.rev, data=flipdim(data,2); end if header.ZDEF.rev, data=flipdim(data,3); end assignin('base',vars(ivar).name,data); end % % end 'loop' otherwise for ivar=1:nvar data=read_var(header,vars(ivar).name); assignin('base',vars(ivar).name,data); data_list=cat(1,data_list,{data}); end data=[]; assignin('caller','data_list',data_list); end else % Individual var. reading switch var_name case {vars.name} fprintf('Variable %s extracted from %s. \n',var_name, ... header.DATANAME); data=read_var(header,var_name); otherwise error(['Variable ',var_name,' not available in ', ... header.DATANAME]); end end return %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % function data=read_var(header,ivar,t_limits,z_limits,y_limits,x_limits) % data=read_varseq(header,ivar) reads in 'ivar' from % 'header.DATANAME' and return it as a 4-D matlab matrix. % 'ivar' can be the variable''s short name or its number id. global l_quiet vars=header.VARS; nvar=header.NVAR; var_size=header.VARSIZE{:}; % different behaviour of the f* functions according to the chosen data precision : switch lower(header.BINPRECISION) case {'uchar','schar','int8','uint8'} byte_length=1; case {'int16','uint16'} byte_length=2; case {'float32','single','int32','uint32'} byte_length=4; case {'float64','double','int64','uint64'} byte_length=8; otherwise byte_length=1; end if isempty(ivar) if ~l_quiet disp(['Header for ',header.FILENAME,' read.']) end data=[]; return end if ischar(ivar) var_name=ivar; ivar=strmatch(ivar,{vars.name},'exact'); if isempty(ivar) if ~l_quiet disp(['The variable ',var_name,' cannot be found in ',header.DATANAME]) end data=[]; return end clear var_name end if ivar==1 byte_ini=0; else byte_ini = sum(prod(var_size(1:(ivar-1),1:3),2),1) ; end if isfield(header,'SEQ') && header.SEQ byte_ini=byte_ini+(sum(var_size(1:ivar-1,3))*8+4)/byte_length; end if header.FILEHEADER byte_ini = byte_ini + header.FILEHEADER/byte_length; % $$$ if isfield(header,'SEQ') && header.SEQ % $$$ byte_ini=byte_ini+8/byte_length; % $$$ end end if header.THEADER byte_ini = byte_ini + header.THEADER/byte_length; % $$$ if isfield(header,'SEQ') && header.SEQ % $$$ byte_ini=byte_ini+8/byte_length; % $$$ end end if header.XYHEADER byte_ini=byte_ini+(sum(var_size(1:ivar-1,3))+1)*header.XYHEADER/byte_length; % $$$ if isfield(header,'SEQ') && header.SEQ % $$$ byte_ini=byte_ini+(sum(var_size(1:ivar-1,3))+1)*8/byte_length; % $$$ end end block_length = sum(prod(var_size(:,1:3),2),1); if isfield(header,'SEQ') && header.SEQ block_length=block_length+sum(var_size(:,3))*8/byte_length; end if header.XYHEADER block_length=block_length+sum(var_size(:,3))*header.XYHEADER/byte_length; end if header.THEADER block_length=block_length+header.THEADER/byte_length; end slice_length = header.XDEF.num*header.YDEF.num+header.XYHEADER/byte_length; if isfield(header,'SEQ') && header.SEQ slice_length = slice_length + 8/byte_length ; end var_length = prod([header.XDEF.num header.YDEF.num diff(z_limits)+1]); if header.XYHEADER var_length = var_length + (diff(z_limits)+1)*header.XYHEADER/byte_length; end if isfield(header,'SEQ') && header.SEQ var_length=var_length+(diff(z_limits)+1)*8/byte_length; end % disp(byte_ini) % Selecting the starting time slice : byte_ini=byte_ini + (t_limits(1)-1)*block_length + (z_limits(1)-1)*slice_length ; err_stat = fseek(header.FID{2},byte_ini*byte_length,'bof'); if err_stat == -1, ferror(header.FID{2}), end % data=NaN(diff(x_limits)+1,diff(y_limits)+1,diff(z_limits)+1,diff(t_limits)+1); data=NaN*ones(diff(x_limits)+1,diff(y_limits)+1,diff(z_limits)+1,diff(t_limits)+1); % size(data) count_byte = 0; count_byte2 = 0; for l=1:(t_limits(2)-t_limits(1)+1) file_ind = ftell(header.FID{2}); for k=1:(z_limits(2)-z_limits(1)+1) if all([x_limits y_limits]==[1 header.XDEF.num 1 header.YDEF.num]) try [data(:,:,k,l),n]=fread(header.FID{2},[header.XDEF.num header.YDEF.num],header.BINPRECISION); count_byte=count_byte+n; catch warning(['Data read-in interrupted at l=',int2str(l),' and k=',int2str(k)]) break end else % Advance to the j-th row: fseek(header.FID{2},(y_limits(1)-1)*header.XDEF.num*byte_length,'cof'); for j=1:(diff(y_limits)+1) fseek(header.FID{2},(x_limits(1)-1)*byte_length,'cof'); [data(:,j,k,l),n]=fread(header.FID{2},diff(x_limits)+1,header.BINPRECISION); fseek(header.FID{2},(header.XDEF.num-x_limits(2))*byte_length,'cof'); end fseek(header.FID{2},(header.YDEF.num-y_limits(2))*header.XDEF.num*byte_length,'cof'); end if isfield(header,'SEQ') && header.SEQ fseek(header.FID{2},8,'cof'); end if header.XYHEADER fseek(header.FID{2},header.XYHEADER,'cof'); end end count_byte2=count_byte2+ftell(header.FID{2})-file_ind; if l < header.TDEF.num err_stat=fseek(header.FID{2},(block_length-var_length)*byte_length,'cof'); if err_stat == -1, ferror(header.FID{2}), end end end % disp([byte_ini block_length slice_length var_length file_ind/byte_length ftell(header.FID{2})/byte_length]) data(find(data==header.UNDEF))=NaN; % replacing the missing/undefined values by NaN (not-a-number) if header.XDEF.rev, data=flipdim(data,1); end if header.YDEF.rev, data=flipdim(data,2); end if header.ZDEF.rev, data=flipdim(data,3); end %if count_byte ~= count_byte2/byte_length % warning(['Number of elements read : ',num2str(count_byte),... % ' .NEQ. number of bytes advanced in ',header.DATANAME,': ',num2str(count_byte2/byte_length)]) %end %fprintf('%i bytes were read in %s .\n size(%s) = [%s], i.e. %i elements.\n',... % count_byte2,header.DATANAME,vars(ivar).name,int2str(size(data)),count_byte); % count_byte2,header.DATANAME,vars(ivar).name,int2str(size(data)),prod(size(data))); return %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % function data=read_station(header) % This function uses the information read from the GrADS .ctl file % to read STATION data into the matlab workspace. % No reference to the .map file is currently made. % cf. http://grads.iges.org/grads/gradoc/aboutstationdata.html % Kristof Sturm, 30.X.02 global l_quiet fid_bin = header.FID{2}; fseek(fid_bin,0,'eof'); eof=ftell(fid_bin); frewind(fid_bin); file_end=0; istat=1; data_format={'id','name','lat','lon','var'}; data_tot={}; % Reading the header : while 1 data_loc={0,'',0,0,[]}; boh=ftell(fid_bin); stid=fscanf(fid_bin,'%8c',1); lat=fread(fid_bin,1,'float32'); lon=fread(fid_bin,1,'float32'); tim=fread(fid_bin,1,'float32'); nlev=fread(fid_bin,1,'int32'); nflag=fread(fid_bin,1,'int32'); eoh=ftell(fid_bin); length_h=eoh-boh; var=NaN(header.NVAR,header.TDEF.num); % Reading all variables, skipping the headers : for t=1:header.TDEF.num var(:,t)=fread(fid_bin,header.NVAR,'float32'); if ftell(fid_bin) < (eof-2*length_h) file_err=fseek(fid_bin,2*length_h,'cof'); if file_err==-1, ferror(fid_bin), end else fprintf('EOF reached for %s.\n\n',header.DATANAME); file_end=1; break end var(find(var==header.UNDEF))=NaN; end if file_end, break, end % Rewinding for reading the first of the next headers : file_err=fseek(fid_bin,-length_h,'cof'); if file_err==-1, ferror(fid_bin), end % saving all relevant informations into the local cell : [data_loc]={istat,deblank(lower(stid)),lat,lon,var}; data_tot=cat(1,data_tot,data_loc); istat=istat+1; end data=cell2struct(data_tot,data_format,2); return