No BSD License  

Highlights from
Samplify Sampled Data Compression

image thumbnail
from Samplify Sampled Data Compression by Al Wegener
Samplify compresses samples used by A/D and D/A converters

samplifyFile(infile, outfile, opt, fmt, delim)
function samplifyFile(infile, outfile, opt, fmt, delim)
%samplifyFile Compress a signal file using Samplify codec
%   samplifyFile(infile,outfile,opt,fmt,delim) parses the file referenced
%   by infile, whose format is determined by fmt and delim, samplifies the
%   signal using the settings contained in the samplify configuration 
%   structure opt, and finally stores the compressed data in the Samplify file
%   format (.sfy file).
%   
%   infile can point to a text or binary file. If fmt is 'dec' or 'hex',
%   then infile is assumed to reference an ASCII text file with samples 
%   in decimal or hexadecimal (a preceding '0x' or '0X') format,
%   respectively. In the case of a decimal or hexadecimal text file,
%   samples are seperated by the delimiter contained in delim. Internally, 
%   samplifyFile uses DLMREAD to parse text files and if delim isn't specified, 
%   DLMREAD will guess the delimiter.
%
%   If you do not provide a delimiter for text-based parsing of signal files, 
%   SAMPLIFYFILE will choose amongst one of the following:
%
%   1) tab ('\t')
%   2) comma (',')
%   3) semi-colon (';')
%   4) colon (':')
%   5) pipe ('|')
%   6) newline ('\r' or '\r\n')
%
%   You can specify any delimiter, but bear in mind that DESAMPLIFYFILE
%   only understands the following delimiters:
%
%   1) tab
%   2) comma
%   3) space
%   4) newline ('\n')
%   5) CRLF ('\r\n')
%
%   What this means is that if you specify a delimiter that DESAMPLIFY does
%   not about, you will still be able to compress an ASCII text signal
%   file, but upon desamplification you will notice that the desamplified
%   file (.dsy) will have a different delimiter. If in SAMPLIFYFILE you
%   choose an unknown delimiter, the default delimiter in the .dsy file is
%   #4, the new-line
%
%   opt.InputDataType is mandatory and is used to tell SAMPLIFYFILE how it
%   should interpret the samples contained in an ASCII file. That is, if
%   fmt is 'dec' and opt.InputDataType is 'int16', the data after being
%   read in via DLMREAD will be casted using INT16. Likewise for hex data,
%   SAMPLIFYFILE needs know if the data is signed or not, and it uses the
%   InputDataType field in the Samplify options struct to determine this
%   information. Hexadecimal formatting of floating-point data is not supported.
%   
%   infile points to a binary file if fmt is 'int8', 'uint8', 'int16', or
%   'uint16', 'float', or 'double'. In the case of a binary file the final 
%   input argument delim is either ignored or need not be specified. The
%   data will be read in using FREAD and then casted to the type specified
%   in opt.InputDataType.
%   
%   Examples
%   --------
%       % losslessly compress a binary file with signed 16-bit samples
%       % (note here it is NOT necessary to provide opt.InputDataType)
%       opt = samplifyOptions('Mode','lossless','InputDataType', 'int16');
%       infile = 'test_f2_b5_80dB.dat';
%       outfile = 'test_f2_b5_80dB.sfy';
%       samplifyFile(infile, outfile, opt, 'int16');
%   
%       % 2:1 fixed-rate compression of 16-bit signed signal in decimal text file
%       opt = samplifyOptions('Mode','fixedRate','CompressionParameter',2.0,'InputDataType','int16');
%       infile = 'test_f2_b5_80dB.txt';
%       outfile = 'test_f2_b5_80dB.sfy';
%       samplifyFile(infile, outfile, opt, 'dec');
%   
%       % same as above, but this time for a CSV (comma separated value)
%       % file, with samples stored in hexadecimal format
%       opt = samplifyOptions('Mode','fixedRate','CompressionParameter',2.0,'InputDataType','int16');
%       infile = 'test_f2_b5_80dB.csv';
%       outfile = 'test_f2_b5_80dB.sfy';
%       samplifyFile(infile, outfile, opt, 'hex', ','); 
%   
%   See also SAMPLIFYOPTIONS, DESAMPLIFYFILE

%   Copyright 2005 Samplify Systems LLC
%   Revision: 1.0 (2005/11/23)

% sanity check - # args
if nargin < 4
    error('samplifyFile requires at least 4 input arguments.');
end

% sanity check - all args must be of type char
if ~ischar(infile) | ~ischar(outfile) | ~ischar(fmt)
    error('All input arguments except samplify options must be of type char.');
end
if 5 == nargin
    if ~ischar(delim)
        error('All input arguments must be of type char.');
    end
else
    delim = [];
end

% sanity check - does infile exist?
ifid = fopen(infile, 'r');
if -1 == ifid
    error(['Could not open ' infile]);
end
fclose(ifid);

% sanity check - require InputDataType field
if ~isfield(opt, 'InputDataType')
    error('samplifyFile requires InputDataType field to be specified in options struct.');
end

% sanity check - floating point & hex incompatible
if strcmpi('hex',fmt) & ...
        (strcmpi('float',opt.InputDataType) | strcmpi('double',opt.InputDataType))
    error('Cannot read floating-point input data with hexadecimal ASCII files.');
end

[samples, btxt, delim] = read_input_data_from_file(infile, fmt, delim, opt);
[compressed, opt] = samplify(samples, opt);
% tell samplify file numerical format (either decimal, hex, or binary)
if btxt
    if strcmpi('dec', fmt)
        num_fmt = 0;
    else
        num_fmt = 1; % hex, see samp_lib.h
    end
else
    num_fmt = -1; % binary, so key off of class(samples)
end
write_sfy_file(compressed, opt, outfile, length(samples), num_fmt, class(samples), delim);

function [samples, btxt, delim] = read_input_data_from_file(infile, fmt, delim, opt)
%
% READ_INPUT_DATA_FROM_FILE
%

if strcmpi('dec', fmt) | strcmpi('hex', fmt)

    [samples, delim] = read_txt_data_from_file(infile, fmt, delim, opt);
    btxt = 1;

elseif strcmpi('int8', fmt) | strcmpi('uint8', fmt) | ...
        strcmpi('int16', fmt) | strcmpi('uint16', fmt) | ...
        strcmpi(fmt, 'float') | strcmpi(fmt, 'double')
   
    samples = read_binary_data_from_file(infile, fmt, opt);
    btxt = 0;
    delim = [];

else
    error(['Unknown format specifier ' fmt]);
end

function [samples, delim] = read_txt_data_from_file(infile, fmt, delim, opt)
%
% READ_INPUT_DATA_FROM_FILE
%

if ~isempty(delim)
    if strcmpi(fmt,'dec')
        samples = dlmread(infile, delim);
    else
        samples = read_hex_data_from_file(infile, opt.InputDataType, delim);
    end
else
    if strcmpi(fmt,'dec')
        samples = dlmread(infile);
    else
        samples = read_hex_data_from_file(infile, opt.InputDataType);
    end
    % even though the delimiter was inferred, we still need it
    % (see delim.m and iofun/private/guessdelim.m)
    fid = fopen(infile);
    str = fread(fid, 4096,'*char')';
    fclose(fid);
    delim = guessdelim(str);
end
samples = handle_data_type(samples, opt);

function samples = read_hex_data_from_file(infile, data_type, delim)

if 2 == nargin
    records = importdata(infile);
else
    records = importdata(infile, delim);
end

% get rid of the '0x' or '0X'
N = length(records);
records = char(records);
records = records(:,3:end);
% cycle thru & convert to decimal representation
for ii=1:N
    samples(ii) = hex2dec(records(ii,:));
end
% take care of signed/unsigned if necessary
if 'i'==data_type(1) | 'I'==data_type(1)
    [cutoff, minval] = twoscomp(data_type);
    inegative = find(samples>cutoff);
    samples(inegative) = minval + samples(inegative);
end

function samples = read_binary_data_from_file(infile, fmt, opt)
%
% READ_BINARY_DATA_FROM_FILE
%

fid = fopen(infile, 'rb');
data = fread(fid, inf, fmt);
samples = handle_data_type(data, opt);
fclose(fid);

function delim = guessdelim(str)
%GUESSDELIM Take stab at default delim for this string.

%   Copyright 1984-2002 The MathWorks, Inc. 
%   $Revision: 1.5 $  $Date: 2002/06/17 13:25:28 $

% bail if str is empty
if isempty(str)
    delim = '';
    return;
end

% count num lines
numLines = length(find(str == sprintf('\n')));

% set of column delimiters to try - ordered by quality as delim
delims = {sprintf('\t'), ',', ';', ':', '|', ' '};

% remove any delims which don't appear at all
% need to rethink based on headers and footers which are plain text
goodDelims = {};
goodDelimCounts = [];
for i = 1:length(delims)
    numDelims(i) = length(find(str == sprintf(delims{i})));
    if numDelims(i) ~= 0
        % this could be a delim
        goodDelims{end+1} = delims{i};
    end
end

% if no delims were found, return newline
if isempty(goodDelims)
    delim = sprintf('\n');
    return;
end

% if the num delims is greater or equal to num lines,
% this will be the default (so return)
for i = 1:length(delims)
    delim = delims{i};
    if numDelims(i) > numLines
        return;
    end
end

% no delimiter was a clear win from above, choose the first
% in the delimiter list
delim = goodDelims{1};

Contact us at files@mathworks.com