Mercurial > hg > ltpda
diff m-toolbox/classes/@ao/fromDatafile.m @ 0:f0afece42f48
Import.
author | Daniele Nicolodi <nicolodi@science.unitn.it> |
---|---|
date | Wed, 23 Nov 2011 19:22:13 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/m-toolbox/classes/@ao/fromDatafile.m Wed Nov 23 19:22:13 2011 +0100 @@ -0,0 +1,428 @@ +% FROMDATAFILE Construct an ao from filename AND parameter list +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% FUNCTION: fromDatafile +% +% DESCRIPTION: Construct an ao from filename AND parameter list +% +% CALL: a = fromFilenameAndPlist(a, pli) +% +% PARAMETER: a: empty ao-object +% pli: plist-object (must contain the filename) +% +% VERSION: $Id: fromDatafile.m,v 1.51 2011/10/12 20:30:51 mauro Exp $ +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function a = fromDatafile(ain, pli) + + utils.helper.msg(utils.const.msg.PROC1, 'constructing from filename and/or plist'); + + VERSION = '$Id: fromDatafile.m,v 1.51 2011/10/12 20:30:51 mauro Exp $'; + + % get AO info + mi = ao.getInfo('ao', 'From ASCII File'); + + % Set the method version string in the minfo object + mi.setMversion([VERSION '-->' mi.mversion]); + + % Get filename + file_name = find(pli, 'filename'); + file_path = find(pli, 'filepath'); + + [filePath, fileName, ext] = fileparts(file_name); + + % Add the file extenstion to the fileName + fileName = strcat(fileName, ext); + + % Define the path of the + if ~isempty(file_path) && ~isempty(filePath) + % Do nothing because we will use filePath + elseif ~isempty(file_path) + filePath = file_path; + elseif ~isempty(filePath) + % Do nothing because we will use filePath + else + filePath = pwd(); + end + + absolutePathname = fullfile(filePath, fileName); + + % Check if the abolute Pathname exist + if ~exist(absolutePathname, 'file') + absolutePathname = fileName; + end + + %%%%%%%%%% Get default parameter list %%%%%%%%%% + dpl = ao.getDefaultPlist('From ASCII File'); + pl = applyDefaults(dpl, pli); + + pl = pset(pl, 'filename', fileName); + pl = pset(pl, 'filepath', filePath); + + data_type = find(pl, 'type'); + columns = find(pl, 'columns'); + maxLines = find(pl, 'maxlines'); + comment_char = find(pl, 'comment_char'); + delimiter = find(pl, 'delimiter'); + use_fs = find(pl, 'fs'); + a = []; + orig_col = columns; % necessary for the history + t0 = find(pl, 't0'); + + %%%% + if strcmpi(data_type, 'cdata') + use_fs = 1; + end + + %%%%%%%%%% read file %%%%%%%%%% + [fid,msg] = fopen (absolutePathname, 'r'); + if (fid < 0) + error ('### can not open file: %s \n### error msg: %s', absolutePathname, msg); + end + + + try + + %%%%%%%%%% create scan format: '%f %f %f %f %f %*[^\n]' %%%%%%%%%% + scan_format = ''; + read_col = 0; + + %%%%%%%%%% Read first comment and empty lines %%%%%%%%%% + while ~feof(fid) + fidPos = ftell(fid); + fline = fgetl(fid); + % check if there was an error reading the file + [errmsg, errnum] = ferror(fid); + if errnum ~= 0 + error('### an error happened reading the first part of the file: %s', errmsg) + end + if fline == -1 + % we hit the end of file + error('### the file is empty!') + end + fline = strtrim(fline); + if ~isempty(fline) && ~(~isempty(comment_char) && strncmp(fline, comment_char, numel(comment_char))) + fseek(fid, fidPos, 'bof'); + break; + end + end + + %%%%%%%%%% Get/Count max number of lines %%%%%%%%%% + if isempty(maxLines) + maxLines = numLines(fid); + utils.helper.msg(utils.const.msg.PROC2, 'Counting lines: %d', maxLines); + end + + %%%%%%%%%% Check max number of columns %%%%%%%%%% + maxColumns = numCols(fid, delimiter); + if isempty(columns) && strcmpi(data_type, 'cdata') + columns = 1:maxColumns; + orig_col = columns; + end + if maxColumns < max(columns) + error('### The file doesn''t have more than [%d] columns. But you want to read the column [%d].', maxColumns, max(columns)); + end + + %%%%%%%%%% Check number of columns %%%%%%%%%% + if isempty(columns) && ~strcmpi(data_type, 'cdata') + error('### Please specify at least one column number to read the data file.'); + end + + %%% preallocate data array + f_data = zeros(maxLines, numel(unique(columns))); + + %%% check if using robust read: 'yes'/'no' or true/false or 'true'/'false' + robust = utils.prog.yes2true(find(pl, 'Robust')); + + if robust + f_data = robustRead(fid, f_data, columns, orig_col); + else + + %%% Based on skipping the not used columns we have to transform the columns. + %%% We must transform the columns [ 2 5 2 6 5 7] to [ 1 2 1 3 2 4] + %%% In each loop we have to replace the corresponding value. In the first loop + %%% the first minimum, in the second loop the second minimum, ... with the + %%% current loop number. + sort_col = sort(columns); + for jj = 1:max(columns) + if ismember(jj, columns) + scan_format = [scan_format '%n']; + read_col = read_col + 1; + replace = min(sort_col); + + columns (columns == replace) = read_col; + sort_col(sort_col == replace) = []; + else + scan_format = [scan_format '%*n']; + end + end + scan_format = [deblank(scan_format) '%*[^\n]']; + + %%%%%%%%%% Read data %%%%%%%%%% + readlines = min(50000, maxLines); + nlines = 0; + + %%% read file to end + while ~feof(fid) && nlines < maxLines + + if isempty(comment_char) && isempty(delimiter) + C = textscan(fid, scan_format, readlines); + elseif isempty(comment_char) && ~isempty(delimiter) + C = textscan(fid, scan_format, readlines, 'Delimiter', delimiter); + elseif ~isempty(comment_char) && isempty(delimiter) + C = textscan(fid, scan_format, readlines, 'CommentStyle', comment_char); + else + C = textscan(fid, scan_format, readlines, 'CommentStyle', comment_char, 'Delimiter', delimiter); + end + + if isempty(C{1}) && nlines == 0 + error('\n### There are no data.\n### Did you use the right comment character?\n### The current comment character is: [%s]\n### Use a parameter list with the parameter:\n### plist(''comment_char'', ''%%'')', comment_char); + end + + f_data(nlines+1:nlines+size(C{1},1),:) = cell2mat(C); + nlines = nlines + length(C{1}); + + utils.helper.msg(utils.const.msg.PROC2, 'read %09d lines of %09d', nlines, maxLines); + end + + %%% get only the data we want + if size(f_data,1) > nlines + f_data = f_data(1:nlines, :); + end + end + + catch ME + % An error occurred during the scan + fclose(fid); + rethrow(ME); + end + + % The scan was successful + fclose(fid); + + + %%%%%%%%%% Create for each column pair the data object %%%%%%%%%% + + % This is a list of keys which support multiple values. We need to + % process the history plist to account for this so that the hisotry of a + % single object only contains the value that the user intended that + % object to get. + % TODO: find a nicer way to specify this? + keys = {'name', 'description', 'xunits', 'yunits', 't0'}; + + if isempty(use_fs) + + %%%%%%%%%% The numbers in columns must be even %%%%%%%%%% + if length(columns) == 1 + if ~strcmp(data_type, 'cdata') + error('A single column file with no sample rate set can only be used to build a cdata AO'); + end + else + if mod(length(columns),2) ~= 0 + error('### The numbers in columns must be even or you forgot to specify the ''fs'' parameter'); + end + end + + N = length(columns)/2; + for lauf = 1:N + + data_x_axes = f_data(:, columns(lauf*2-1)); + data_y_axes = f_data(:, columns(lauf*2)); + + % create data object corresponding to the parameter list + ao_data = []; + switch lower(data_type) + case 'tsdata' + ao_data = tsdata(data_x_axes, data_y_axes); + case 'fsdata' + ao_data = fsdata(data_x_axes, data_y_axes); + case 'cdata' + error('### This should not happen!'); + case 'xydata' + ao_data = xydata(data_x_axes, data_y_axes); + otherwise + error('### unknown data type ''%s''', data_type); + end + aa = ao(ao_data); + + % set up the history plist for this object + plhist = pl.processForHistory(N, lauf, keys); + plhist.pset('columns', [orig_col(lauf*2-1) orig_col(lauf*2)]); + + + % Add history + aa.addHistory(mi, plhist, [], []); + + a = [a aa]; + + end + + %%%%%%%%%% Create for each column AND fs a data object %%%%%%%%%% + else % isempty(use_fs) + + N = length(columns); + for lauf = 1:N + + data_y_axes = f_data(:, columns(lauf)); + + % create data object corresponding to the parameter list + ao_data = []; + switch lower(data_type) + case 'tsdata' + ao_data = tsdata(data_y_axes, use_fs); + case 'fsdata' + ao_data = fsdata(data_y_axes, use_fs); + case 'cdata' + % Special case for cdata-objects. + % Is the user specify some columns then he will get for each + % column an AO. If don't specify a columns then he will all data + % in a single AO. + if isempty(pl.find('columns')) + % Create only one AO with all data + a = ao(f_data); + a.name = fileName; + a.addHistory(mi, pl, [], []); + break + else + % Create for each column a single AO + ao_data = cdata(data_y_axes); + end + case 'xydata' + ao_data = xydata(data_y_axes); + otherwise + error('### unknown data type ''%s''', data_type); + end + aa = ao(ao_data); + + % set up the history plist for this object + plhist = pl.processForHistory(N, lauf, keys); + plhist.pset('columns', orig_col(lauf)); + + + % Add history + aa.addHistory(mi, plhist, [], []); + + a = [a aa]; + + end + + end + + % for tsdata and fsdata we support setting the t0 + if any(strcmpi(data_type, {'tsdata', 'fsdata'})) + a.setT0(t0); + end + + % set xunits if we don't have a cdata + if ~strcmpi(data_type, 'cdata') + xunits = pl.find('xunits'); + if isempty(xunits) + if strcmpi(data_type, 'tsdata') + xunits = 's'; + elseif strcmpi(data_type, 'fsdata') + xunits = 'Hz'; + else + % do nothing + end + end + a.setXunits(xunits); + end + + % set yunits + a.setYunits(pl.find('yunits')); + + % Set object properties + a.setObjectProperties(pl); + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Local Functions % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% FUNCTION: numLines +% +% SYNTAX: count = numLines(fid); +% +% DESCRIPTION: Returns the number of lines in an ASCII file. This method +% doesn't change the position of the file identifier (fid) +% +% HISTORY: 02-08-2002 Peter Acklam, CSSM post +% Creation. +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function lines = numLines(fid) + + fidPos = ftell(fid); + block = []; + lines = 0; % number of lines in file + nlchr = uint8(sprintf('\n')); % newline chr as uint8 + bsize = 4 * 256 * 8192; % block size to read + + while ~feof(fid) + block = fread(fid, bsize, '*uint8'); + lines = lines + sum(block == nlchr); + end + if ~isempty(block) % in case file is empty + lines = lines + double(block(end) ~= nlchr); + end + fseek(fid, fidPos, 'bof'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% FUNCTION: numCols +% +% SYNTAX: count = numCols(fid); +% +% DESCRIPTION: Returns the number of columns in an ASCII file. This method +% doesn't change the position of the file identifier (fid) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function ncols = numCols(fid, delimiter) + + fidPos = ftell(fid); + line = fgetl(fid); + if ischar(line) + if nargin == 1 || isempty(delimiter) + C = textscan(line, '%s'); + else + C = textscan(line, '%s', 'Delimiter', delimiter); + end + C = C{1}; + ncols = numel(C); + else + ncols = 0; + end + fseek(fid, fidPos, 'bof'); +end + + +% A robust and slow data reader +function f_data = robustRead(fid, f_data, columns, orig_cols) + + cols = unique(columns); + ocols = unique(orig_cols); + Nline = 1; + while ~feof(fid) + % read and parse line + tokens = sscanf(fgets(fid), '%f'); + % parse tokens + if ~isempty(tokens) + f_data(Nline, cols) = tokens(ocols); + if mod(Nline, 1000) == 0 + utils.helper.msg(utils.const.msg.PROC2, 'lines read: %d', Nline); + end + Nline = Nline + 1; + end + end + + % drop empty lines + f_data = f_data(1:Nline-1, :); + +end