%
% from https://www.mathworks.com/matlabcentral/fileexchange/18999-read-binary-rigol-waveforms
% tested with Rigol DS1102D scope waveforms
%

function varargout = readRigolWaveform(filename, varargin)
%Reads a binary waveform (.wfm) file stored by a Rigol oscilloscope.
%
% readRigolWaveform(filename) Displays information about the recorded
% signal(s) in the waveform file and plots the signal(s).
%
% filename - filename of Rigol binary waveform
%
%
% [y, nfo] = readRigolWaveform(filename) Reads the signal(s) in the
% specified file.
%
% filename - filename of Rigol binary waveform
% y        - column array with data. If both channels were recorded it
%            contains two channels.
% nfo      - structure with time axis and other information
%            nfo.x0: time corresponding to first sample
%            nfo.dx: sample time (= 1 / sample_frequency)
%            nfo.notes: text field other oscilloscope settings
%
if ~exist('filename', 'var')
    error('Error using this function. For help type: help readRigolWaveform');
end
if nargout == 0
    fprintf('---\nFilename: %s\n', filename);
end
%% Open the file and check the header
if ~exist(filename, 'file')
    error('Specified file (%s) doesn''t exist.', filename);
end
fid = fopen(filename, 'r');
if fid == -1
    error('Unable to open %s for reading.', filename);
end
% Check first two bytes
header = fread(fid, 2, '*uint8');
if (header(1) ~= 165) || (header(2) ~= 165)
    error('Incorrect first two bytes. This files does not seem to be a Rigol waveform file.');
end
%% Check which channels were recorded
fseek(fid, 49, -1);
ch1Recorded = (fread(fid, 1, '*uint8') == 1);
fseek(fid, 73, -1);
ch2Recorded = (fread(fid, 1, '*uint8') == 1);
%% Nr. of data points
fseek(fid, 28, -1);
N = fread(fid, 1, 'uint32=>double');
if nargout == 0
    fprintf('Record length: %dk\n', N / 1024);
end
%% Time axis
if nargout == 0
    fprintf('- Time axis\n');
end
fseek(fid, 84, -1);
time.scale = fread(fid, 1, 'uint64=>double') * 1e-12;
time.delay = fread(fid, 1, 'int64=>double') * 1e-12;
fseek(fid, 100, -1);
time.fs = fread(fid, 1, 'single=>double');
nfo.x0 = -N / 2 / time.fs + time.delay;
nfo.dx = 1 / time.fs;
if nargout == 0
    fprintf('    scale: %ss/div\n', nr2str(time.scale));
    fprintf('    delayed: %ss\n', nr2str(time.delay));
    fprintf('    sample frequency: %sS/s\n', nr2str(time.fs));
end
%% Read ch1 and ch2 information
if ch1Recorded
    ch1Info = readChannelInfo(fid, 1, (nargout == 0));
else
    ch1Info = [];
end
if ch2Recorded
    ch2Info = readChannelInfo(fid, 2, (nargout == 0));
else
    ch2Info = [];
end
%% Read ch1 and ch2 data
fseek(fid, 272, -1);
if ch1Recorded
    ch1Data = (125 - fread(fid, N, 'uint8=>double')) / 250 * 10 * ch1Info.scale;
    ch1Data = ch1Data - ch1Info.position;
end
if ch2Recorded
    ch2Data = (125 - fread(fid, N, 'uint8=>double')) / 250 * 10 * ch2Info.scale;
    ch2Data = ch2Data - ch2Info.position;
end
%% Trigger mode
if nargout == 0
    fprintf('- Trigger\n');
end
fseek(fid, 142, -1);
triggermode = fread(fid, 1, '*uint16');
if triggermode == hex2dec('0000')
    trigger.mode = 'edge';
elseif triggermode == hex2dec('0101')
    trigger.mode = 'pulse';
elseif triggermode == hex2dec('0303')
    trigger.mode = 'vid';
elseif triggermode == hex2dec('0202')
    trigger.mode = 'slope';
elseif triggermode == hex2dec('0004')
    trigger.mode = 'alt';
elseif triggermode == hex2dec('0505')
    trigger.mode = 'pattern';
elseif triggermode == hex2dec('0606')
    trigger.mode = 'duration';
else
    error('Unknown trigger mode.');
end
if nargout == 0
    fprintf('    mode: %s\n', trigger.mode);
end
triggersource = fread(fid, 1, '*uint8');
if triggersource == 0
    trigger.source = 'ch1';
elseif triggersource == 1
    trigger.source = 'ch2';
elseif triggersource == 2
    trigger.source = 'ext';
elseif triggersource == 3
    trigger.source = 'ext/5';
elseif triggersource == 5
    trigger.source = 'acLine';
elseif triggersource == 7
    trigger.source = 'dig.ch';
else
    error('Unknown trigger source.');
end
if nargout == 0
    fprintf('    source: %s\n', trigger.source);
end
%% Close file
fclose(fid);
%% Plot signals
if nargout == 0
    figure;
    tf = nfo.x0 + (N - 1) * nfo.dx;
    t = linspace(nfo.x0, tf, N);
    
    if exist('ch1Data', 'var') && exist('ch2Data', 'var')
        plot(t, ch1Data, t, ch2Data);
        legend('Ch1', 'Ch2');
    elseif exist('ch1Data', 'var')
        plot(t, ch1Data);
        legend('Ch1');
    else
        plot(t, ch2Data);
        legend('Ch2');
    end
    grid on;
    xlabel('Time (s)');
    ylabel('Voltage (V)');
end
%% Assign output arguments
if nargout >= 1
    % Assign signal y
    if ch1Recorded && ~ch2Recorded
        y = ch1Data;
    elseif ~ch1Recorded && ch2Recorded
        y = ch2Data;
    else
        y = [ch1Data ch2Data];
    end
    varargout{1} = y;
end
if nargout >= 2
    nfo.notes = createNotes(time, ch1Info, ch2Info, trigger);
    varargout{2} = nfo;
end
end  % end of main function
%% Function: readChannelInfo
function [ info ] = readChannelInfo(fid, chNr, printInfo)
    % Data addresses (decimal) for ch1 and ch2
    % [scale, position, probe attenuation]
    allAddresses = [
        36 40 46
        60 64 70
    ];
    adrs = allAddresses(chNr, :);
    
    if printInfo
        fprintf('- Ch%d\n', chNr);
    end
       
    % Vertical scaling
    fseek(fid, adrs(1), -1);
    info.scale = fread(fid, 1, 'uint32=>double') * 1e-6;
    if printInfo
        fprintf('    scale: %sV/div\n', nr2str(info.scale));
    end
    
    % Vertical position
    fseek(fid, adrs(2), -1);
    info.position = fread(fid, 1, 'int16=>double') / 25 * info.scale;
    if printInfo
        fprintf('    position: %sV\n', nr2str(info.position));
    end
    % Probe attenuation
    fseek(fid, adrs(3), -1);
    probeatten = fread(fid, 1, '*uint16');
    if probeatten == hex2dec('3F80')
        info.probeatten = 1;
    elseif probeatten == hex2dec('4120')
        info.probeatten = 10;
    elseif probeatten == hex2dec('42C8')
        info.probeatten = 100;
    elseif probeatten == hex2dec('447A')
        info.probeatten = 1000;
    else
        error('Unknown ch%d probe attenuation.', chNr);
    end
    if printInfo
        fprintf('    probe attenuation: %gx\n', info.probeatten);
    end
end
%% Function: createNotes
function [ notes ] = createNotes(timeInfo, ch1Info, ch2Info, triggerInfo)
    notes = sprintf('time.scale = %ss/div\n', nr2str(timeInfo.scale));
    notes = sprintf('%stime.delay = %ss\n', notes, nr2str(timeInfo.delay));
    notes = sprintf('%stime.fs = %sS/s\n', notes, nr2str(timeInfo.fs));
    if ~isempty(ch1Info)
        notes = sprintf('%sch1.scale = %sV/div\n', notes, nr2str(ch1Info.scale));
        notes = sprintf('%sch1.position = %sV\n', notes, nr2str(ch1Info.position));
        notes = sprintf('%sch1.probe_attenuation = %gx\n', notes, ch1Info.probeatten);
    end
    if ~isempty(ch2Info)
        notes = sprintf('%sch2.scale = %sV/div\n', notes, nr2str(ch2Info.scale));
        notes = sprintf('%sch2.position = %sV\n', notes, nr2str(ch2Info.position));
        notes = sprintf('%sch2.probe_attenuation = %gx\n', notes, ch2Info.probeatten);
    end
    notes = sprintf('%strigger.mode = %s\n', notes, triggerInfo.mode);
    notes = sprintf('%strigger.source = %s\n', notes, triggerInfo.source);
end
%% Function: nr2str
function [ nrString ] = nr2str(number)
    absnr = abs(number);
    if absnr == 0
        nrString = sprintf('%g ', number);
    elseif absnr < 1e-9
        nrString = sprintf('%g p', number / 1e-12);
    elseif absnr < 1e-6
        nrString = sprintf('%g n', number / 1e-9);
    elseif absnr < 1e-3
        nrString = sprintf('%g u', number / 1e-6);
    elseif absnr < 1
        nrString = sprintf('%g m', number / 1e-3);
    elseif absnr < 1e3
        nrString = sprintf('%g ', number);
    elseif absnr < 1e6
        nrString = sprintf('%g k', number / 1e3);
    elseif absnr < 1e9
        nrString = sprintf('%g M', number / 1e6);
    else
        nrString = sprintf('%g G', number / 1e9);
    end
end
