How does Labview save cluster data in a binary file and how do I read it out in MATLAB? - matlab

I have a very large number of files that were saved in binary in Labview, where each column is a timestamp cluster followed by a vector of singles.
I read each data file into Matlab r2013a using
fid = fopen(filename);
data = fread(fid,[N M],'*single',0,'b');
fclose(fid);
where I pre-calculate the size of the input array N,M. Since I know what the data is supposed to look like, I have figured out that data(1:5,:) is where the timestamp data is hidden, but it looks like something like this for M = 1:
[0 -842938.0625 -1.19209289550781e-07 0 4.48415508583941e-42]
The first element is always 0, the second element decreases monotonically with a constant step size, the third seems to be bistable, flipping back and forth between two very small values, the fourth is always 0, and the fifth is also constant.
I'm assuming it has something to do with how Labview encodes dates, but my google-fu has not helped me figure that out.
To make this a more general question, then:
How does Labview encode a timestamp cluster when it saves to a binary file, and how can I read it out and translate it into a meaningful number in another programming language, such as Matlab?
EDIT:
For posterity, here is my final code (appended to the code above):
datedata = data(5:-1:1,:);
data(1:5,:) = [];
dms = typecast(reshape(datedata(2:3,:),[],1),'uint64');
dsecs = typecast(reshape(datedata(4:5,:),[],1), 'int64');
timestamp = datenum(1904,1,1) + (double(dsecs) + double(dms)*2^-64)/(3600*24);
In the code #Floris posted from Mathworks, they typecast straight to double, but when I tried that, I got garbage. In order to get the correct date, I had to first convert to integer and then to double. Since my bottleneck is in the fread line (0.3 seconds to read off of an external disk), the extra typecast step is miniscule in the grand scheme of things.
The extra column, 4.5e-42, converts to an integer value of 3200, the number of values in the subsequent vector of singles.

This is not a complete answer, but it should help (I don't have either Labview or Matlab available at home so I can't check this right now).
There is an article at http://www.mathworks.com/matlabcentral/newsreader/view_thread/292060 that describes a similar question. Couple of useful bits of information I extracted from that:
Time stamp is a double (not single)
Need to flip the order of bytes (little vs big endian) to make sense of things
There is a useful comment:
Note that the LabView time convention is miliseconds since Jan 1 1904.
Here is one approach (may contain errors but will point you in the
right direction),
The following code snippet is also given:
%% Read in date information
[ fid, msg ] = fopen(FileName, 'r') ;
NColumns = 60 ; % Number of data columns - probably different for your
dataset!
[a, count] = fread(fid, [ NColumns Inf], '*single') ; % Force data to
be read into Matlab workspace as singles
a = a' ; % Convert to data in columns not rows
% The last two columns of a are the timestamp
b = fliplr(a(:, end-1:end)) ; % Must swap the order of the columns
d = typecast(reshape(b',[],1), 'double') ; % Now we can can convert to
double
time_local = datenum(1904, 1, 1) + d/(24*3600) ; % Convert from
seconds to matlab time format
fclose(fid) ;
It looks believable to me. Let me know if it works - if not, I may be able to help debug in the morning...

A LabVIEW timestamp is a 128-bit type consisting of a signed 64-bit integer measuring the offset in seconds since the LabVIEW epoch (January 1, 1904 00:00:00.00 UTC), and an unsigned 64-bit integer measuring the fractional second. Source: ni.com.
The byte order of the file however may be platform dependent. For example the time 8:02:58.147 AM July 3 2013 EDT may be stored as:
0x 00000000CDF9C372 25AA100000000000 (big/network)
or as
0x 000000000010AA25 72C3F9CD00000000 (little)

Related

How can i write a number values in powers of 10? [duplicate]

This question already has answers here:
What is the Small "e" in Scientific Notation / Double in Matlab
(2 answers)
Closed 7 years ago.
How can I write a number/Integer value to power of 10, e.g. 1000 as 10^3? I am writing code whose output is a string of very large numbers. My output in longEng format is:
4.40710646596169e+018
16.9749211806197e+186
142.220634811050e+078
508.723835280617e+204
1.15401317731033e-177
129.994388899690e+168
14.3008811642810e+153
1.25899227268954e+165
24.1450064703939e+150
627.108997290435e+144
2.03728822649372e+177
339.903986115177e-066
150.360900017430e+183
5.39003779219462e+135
183.893417489826e+198
648.544709490386e+045
19.7574461055182e+198
3.91455750674308e+102
6.41548629454028e-114
70.4943280639616e+096
19.7574461055182e+198
3.11450571506133e-009
249.857950606210e+093
4.64921904682151e+180
750.343029004712e+147
I want these results to be in a format of power of 10, so that I can easily do arithmetic operations for my next function.
you can write format shortE and see you output like this:
4.4071e+18
1.6975e+187
1.4222e+80
5.0872e+206
If you only want to print the data in scientific format, the Matlab itself can do this for you.
If you can to obtain the scientific notation form as
a * 10^b,
i.e., obtain the coefficient a and the exponent b, you can first obtain the b as:
b = floor(log10(abs(x)));
then the a as:
a = x * 10^(-b);
from my understanding you wish to take your number e.g. 4.40710646596169e+018 and split it up into:
4.40710646596169 and 018 once you have them separated you you can perform operations as you wish.
You can even join them back to look like: 4.40710646596169^018 if you so desire (although to look like that they would be strings and therefore mathematical operations on the number would be NAN).
Since e represents to the power 10 and is present in all numbers you listed this is a simple process with many solutions, here is one.
% format long is very important otherwise it will appear to you that you have
%lost precision. MATLAB hides precision from view to save screen space and to
%produce less confusing results to the viewer. (the precision is still there but
%with format long you will be able to see it.
format long
x = 4.40710646596169e+018;
%convert your number into a string, this will allow you to split the number based
%on the always present e+ 'delimiter' (not really a delimiter but looks like one')
s = num2str(x);
%use strsplit to perform the split in the required place. it will output a 1x2
%cell
D = strsplit(s, {'e+'});
%extract each cell to a separate variable. in fact D{1} can be directly used for
%the input of the next function.
D11 = D{1};
D22 = D{2};
%convert the separated strings back into numbers with double precision (keep
%maintin value accuracy)
D1 = str2double(D11)
D2 = str2double(D22)
in order to do this operation on an entire column vector it is simply a matter of using a for loop to iterate through all the numbers you have.

Integer view format in Matlab

I have a matlab code that its output will print as an input text file for other program. But the numbers format is important in input file of my desired program it should start with 0. and then the number. For example, I want to format the output of Matlab program from the 3.00E+03 to 0.30E+04.
Can anyone between you experts kindly help me?
Many tanx
Please check the reference for fprintf. You can find it in the format specifier:
http://www.mathworks.nl/help/matlab/ref/fprintf.html
Probably the format you desire is not available in MATLAB so you can create your own!
First use log 10 to obtain the power (assuming):
Assuming a number X.
% Number to convert
X = 3867;
% Number of decimals after comma
n = 2;
% Calculation of the power to print
power = floor(log10(X))+1;
% Calculation of the decimals (correctly rounded)
decimals = round(X/10^(power-n));
% The format of fprintf. 0. is static, %d represents the printed decimals, %+0.3d represents the power. + denoting the sign, 0. denoting padding with zeros, 3 denoting the number of characters printed (if less characters in the power padded with zeros).
fprintf('0.%dE%+0.3d',decimals,power)
Kind regards,
Ernst Jan

MATLAB Loop Programming

I've been stuck on a MATLAB coding problem where I needed to create market weights for many stocks from a large data file with multiple days and portfolios.
I received help from an expert the other day using 'nested loops' it worked, but I don't understand what he has done in the final line. I was wondering if anyone could shed some light and provide an explanation of the last coding line.
xp = x (where x = market value)
dates=unique(x(:,1)); (finds the unique dates in the data set Dates are column 1)
for i=1:size(dates,1) (creates an empty matrix to fill the data in)
for j=5:size(xp,2)
xp(xp(:,1)==dates(i),j)=xp(xp(:,1)==dates(i),j)./sum(xp(xp(:,1)==dates(i),j)); (help???)
end
end
Any comment are much appreciated!
To understand the code, you have to understand the colon operator, logical indexing and the difference between / and ./. If any of these is unclear, please look it up in the documentation.
The following code does the same, but is easier to read because I separated each step into a single line:
dates=unique(x(:,1));
%iterates over all dates. size(dates,1) returns the number of dates
for i=1:size(dates,1)
%iterates over the fifth to last column, which contains the data that will be normalised.
for j=5:size(xp,2)
%mdate is a logical vector, which is used to select the rows with the currently processed date.
mdate=(xp(:,1)==dates(i))
%s is the sums up all values in column j same date
s=sum(xp(mdate,j))
%divide all values in column j with the same date by s, which normalises to 1
xp(mdate,j)=xp(mdate,j)./s;
end
end
With this code, I suggest to use the debugger and step through the code.

Convert binary string greater than 52 bits to single in Matlab?

I'm trying to convert very long binary strings, often greater than 52 bits into numbers. I cannot have a fixed lookahead window because I am doing this to calculate a version of Lempel-Ziv complexity for neural data.
When I try to convert any long string, bin2dec throws and error that the binary string must be 52 bits or less.
Is there a way to get around this size limitation?
dec2bin throws that error because a single is not capable of storing that much precision. Your very question asks an impossibility. You have two choices: store the value in something other than a floating point value, or throw away some precision before you convert.
Or describe more completely what you're trying to accomplish.
EDITING:
Based on your additional information, I am even more certain that converting to floating point is not what you want to do. If you want to reduce the storage size to something more efficient, convert to a vector of bytes (uint8), which is as dense as you can get. Just split the binary string into N rows of 8 digits each, using reshape. This seems to be an accepted approach for biological data.
str = char((rand(1, 100)>0.5) + '0'); % test data
data = uint8(bin2dec(reshape(str(1:end-mod(end,8)), [], 8)));
In this code, I toss any bits that don't divide evenly into 8. Or, skip the uint8 step and just perform your processing on the resulting vector, where each double-precision float represents one 8-bit word from your sequence.
You could roll your own implementation:
len = 60;
string = [];
for i = 1:len
string = [string sprintf('%d', randi([0 1]))];
end
% error
% bin2dec(string);
% roll your own...
value = 0;
for i = length(string):-1:1
value = value + str2num(string(i))*2^(length(string)-i);
end
I'm just looping through the string and adding to some value. At the end, value will contain the decimal value of the string. Does this work for you?
Note: This solution is slow. You can speed it up a bit by preallocating the string, which I did on my own machine. Also, it's going to have issues if your number gets up to 1e6 digits. At that point, you need variable precision arithmetic to keep track of it. And adding that to the calculation really slowed things down. If I were you, I'd strongly consider compiling this from a .mex file if you need the functionality in MATLAB.
credits due to #aardvarkk, but here's a sped up version of his algorithm (+- 100x faster):
N=100;
strbin = char(randi(2,1,N)+'0'-1);
pows2 = 2.^(N-1:-1:0);
value=pows2*(strbin-'0')';
double's range goes only up to 1.79769e+308 which is 2^1024 give or take. From there on, value will be Inf or NaN. So you still need to find another way storing the resulting number.
A final pro on this algorithm: you can cache pows2 for a large number and then use a piece of it for any new strbin of length N:
Nmax = 1e8; % already 700MB for pows2, watch out!
pows2 = 2.^(Nmax-1:-1:0);
and then use
value = pows2(Nmax-N+1:end)*(strbin-'0')';
Solution to matlab's numeric upper bound
There's a tool on the File Exchange called vpi: http://www.mathworks.com/matlabcentral/fileexchange/22725
It allows you to use really big integers (2^5000? no prob). It's only slower (a lot) in calculating everything, I don't suggest using my method above with this. But hey, you can't have everything!
Download the package, addpath it and the following might work:
N=3000;
strbin = char(randi(2,1,N)+'0'-1);
binvals=strbin-'0';
val=0;
twopow=vpi(1);
for ii=1:N
val=val+twopow*binvals(N-ii+1);
twopow=twopow*2;
end

How to use DateTime or FileTime date types in Matlab

I would like to parse/convert DateTime (or FileTime) data type which is int64 and measured in "ticks" which are 100-nanoseconds units, to a datenum or a better date & time format in Matlab.
I must use a counter with a high resolution 64bit as the difference between time stamps can be a couple of microseconds and the "spacing" isn't even.
Using latest Matlab version, the "ticks" variable is read as a string..
Any recommendations?
Thank you for your help
Datenums probably won't represent this effectively. They are doubles, an approximate type, with 1.0 = 1 day; the resolution limit for recent dates is about 10 microseconds. Also, the datenum related functions aren't written for high precision processing, and probably won't work well here.
>> datenum_precision = eps(now)
datenum_precision =
1.1642e-010
>> millisecond = 1.0/(24*60*60*1000)
millisecond =
1.1574e-008
>>
You're probably better off creating your own class that wraps the "ticks" as uint64 and provides methods for converting to human-readable presentation (like datestr()), doing addition and subtraction, parsing the strings you're getting to the uint64 values, and so on. Something with an interface like this.
classdef filetime
properties
tick; % An array of uint64 ticks
end
methods (Static = true)
out = parse(str); % Parse strings to #filetime
end
methods
out = filetimestr(obj); % human-readable strings
out = minus(a,b);
out = plus(a,b);
out = sort(obj);
out = datenum(obj); % maybe a lossy conversion to datenum, for convenience
end
end
You'll need R2010b to do arithmetic on 64-bit ints; older versions don't support it.
The tricky part would be if you wanted to do plots using the filetimetick objects; the Handle Graphics plots don't support user-defined objects.
You can convert your numbers to MATLAB's serial date with a simple equation:
step = 1e-7; % 100 nanosec
ticks = int64(250000);
timenum = double(ticks)*step/24/60/60;
First, for very large integer values converting to double might loose precision. If this is the case you probably better to stay with your numbers without converting to serial date.
Second, DATESTR/DATETICK functions support precision up to milliseconds. Try:
datestr(timenum,'HH:MM:SS.FFF')
So, think about it and please answer Andrew's comment - what do you need this conversion for?
D is a uint64 value of 100 ns since 01-01-1601, the result must be a datenum. This means days passed since 00-Jan-0000.
step = 100E-9; % 100 nanoseconds
step = step/(24*60*60); %fraction of a day
D = datenum('01-01-1601')+double(D)*step;
%D is now a datenum
Modern version using datetime (matlab >2014b)
%uint64 value of 100 ns since 01-01-1601
step = 100E-9; % 100 nanoseconds
step = step/(24*60*60); %fraction of a day
D = datetime('01-01-1601')+days(double(D)*step);
As mentioned above, both methods lose some of the accuracy that we had. In the case of datetimes this can probably be circumvented. It happens at double(D)*step because D is big for current dates. Many nanoseconds have passed since 1601. By converting D to a more recent offset one could get a more accurate conversion.