Making a Matlab Class for streaming mode

Post your MATLAB discussions here
Post Reply
edshah
Newbie
Posts: 0
Joined: Fri Apr 05, 2024 9:06 pm

Making a Matlab Class for streaming mode

Post by edshah »

Hello

This question might have been asked before. I made a class for my instrument that I am hoping to share with the community. I got everything working beautifully in the block mode but the streaming mode and the buffers seem to not like it yet.

When I run this, everything comes back az zeros. I suspect the buffers are not set right...any idea?

Code: Select all

classdef PicoScope5000A < handle
    properties
        DeviceObj
        ChannelSettings
        MaxADCCount
        overviewBufferSize
        SegmentIndex
        RatioMode
        DownSampleRatio
        DownSampleRatioMode
        PlotLiveData
        TotalSamples
        HasTriggered
        TriggeredAtIndex
        volt_chan_Enable logical =0;
        streamingGroupObj

        EnumInfo struct; % Enumeration info for the PicoScope device settings (struct)
        ps5000aConfigInfo
        ps5000aMethodinfo
        ps5000aStructs
        ps5000aThunkLibName

                pBufferChAFinal  % Final buffer for channel A
        pBufferChBFinal  % Final buffer for channel B
        plotLiveData  % Flag to indicate whether to plot live streaming data

    end

    methods

        function obj = PicoScope5000A()
            % Constructor to initialize the PicoScope
            obj = obj.clearSetup();
            obj = obj.loadConfiguration();






        end





        function obj = clearSetup(obj)
            % Clear command window and close any figures
            clc;
            close all;
        end

        function obj = loadConfiguration(obj)
            % Load configuration information
            disp('Configuration Loaded (Placeholder)');
            PS5000aConfig; % This would be the actual configuration load call

            PS5000aConfig;
            obj.EnumInfo=ps5000aEnuminfo
            obj.ps5000aConfigInfo=ps5000aConfigInfo;
            obj.ps5000aMethodinfo=ps5000aMethodinfo;
            obj.ps5000aStructs=ps5000aStructs;
            obj.ps5000aThunkLibName=ps5000aThunkLibName;
        end



        function obj = connectDevice(obj)
            % Check if an Instrument session using the device object is still open,
            % and if so, disconnect if the user chooses 'Yes' when prompted.
            if (exist('ps5000aDeviceObj', 'var') && isa(ps5000aDeviceObj, 'icdevice') && isvalid(ps5000aDeviceObj) && strcmp(ps5000aDeviceObj.status, 'open'))
                selection = questdlg('Device object ps5000aDeviceObj has an open connection. Do you wish to close the connection and continue?', ...
                    'Device Object Connection Open', ...
                    'Yes', 'No', 'Yes');
                if strcmp(selection, 'Yes')
                    % Close connection to device.
                    disconnect(ps5000aDeviceObj);
                    delete(ps5000aDeviceObj);
                else
                    % Exit method if User selects 'No'.
                    return;
                end
            end

            % Create a device object.
            obj.DeviceObj = icdevice('picotech_ps5000a_generic', '');

            % Connect device object to hardware.
            connect(obj.DeviceObj);
            disp('Device Connected');
        end


        function obj = setupChannels(obj)
            % Define channel settings based on the provided examples
            channelSettings = struct(...
                'channel', {0, 1, 2, 3}, ...  % Channel indices
                'enabled', {1, 1, 0, 0}, ...  % Enabled status for each channel
                'coupling', {1, 1, 1, 1}, ...  % Assuming '1' corresponds to 'DC' coupling
                'range', {6, 8, 8, 8}, ...  % Voltage range settings
                'offset', {0.0, 0.0, 0.0, 0.0} ...  % Analog offset for each channel
                );

            % Loop through each channel and apply settings
            for ch = 1:length(channelSettings)
                [status.setChannelStatus(ch)] = invoke(obj.DeviceObj, 'ps5000aSetChannel', ...
                    channelSettings(ch).channel, ...
                    channelSettings(ch).enabled, ...
                    channelSettings(ch).coupling, ...
                    channelSettings(ch).range, ...
                    channelSettings(ch).offset);
            end

            disp('Channels configured.');
        end


        function obj = setSimpleTrigger(obj, channel, threshold, direction, autoTriggerMs)
            % Sets a simple trigger on the specified channel with the given threshold and direction.
            % autoTriggerMs specifies the time in milliseconds to automatically trigger
            % the oscilloscope if no trigger event has occurred.

            disp('Setting up simple trigger...');

            % Access the trigger group object from the device object.
            triggerGroupObj = get(obj.DeviceObj, 'Trigger');
            triggerGroupObj = triggerGroupObj(1);  % Assuming we're using the first trigger group

            % Set the autoTriggerMs property.
            set(triggerGroupObj, 'autoTriggerMs', autoTriggerMs);

            % Set a simple trigger.
            [status.setSimpleTrigger] = invoke(triggerGroupObj, 'setSimpleTrigger', channel, threshold, direction);

            if status.setSimpleTrigger == 0  % Assuming '0' indicates success
                disp('Simple trigger set successfully.');
            else
                disp('Failed to set simple trigger.');
            end
        end

        function obj = changeResolution(obj, desiredResolution)
            % Change the device resolution and obtain the maximum ADC count value.
            % The desiredResolution parameter should be the resolution in bits you want to set.

            disp(['Setting resolution to ', num2str(desiredResolution), ' bits...']);

            % Set the device resolution.
            [status.setResolution, resolution] = invoke(obj.DeviceObj, 'ps5000aSetDeviceResolution', desiredResolution);

            if status.setResolution == 0  % Assuming '0' indicates success
                disp(['Resolution set to ', num2str(resolution), ' bits.']);
            else
                disp('Failed to set resolution.');
                return;
            end

            % Obtain the maximum ADC count value.
            obj.MaxADCCount = get(obj.DeviceObj, 'maxADCValue');

            disp(['Maximum ADC count value: ', num2str(obj.MaxADCCount)]);
        end


        function obj = setDataBuffers(obj, overviewBufferSize, segmentIndex, ratioMode)
    % Validate and set up data buffers for channels A and B.
    
    % Validate overviewBufferSize
    if isempty(overviewBufferSize) || overviewBufferSize <= 0
        error('Invalid overviewBufferSize. It must be a positive integer.');
    end

    % Validate segmentIndex
    if isempty(segmentIndex) || segmentIndex < 0
        error('Invalid segmentIndex. It must be a non-negative integer.');
    end

    % Validate ratioMode
    if isempty(ratioMode) || ~isnumeric(ratioMode)
        error('Invalid ratioMode. It must be a numeric value.');
    end

    disp('All parameters validated successfully.');

    % Initialize driver buffers for channels A and B
    pDriverBufferChA = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));
    pDriverBufferChB = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));

    % Check if driver buffers are initialized correctly
    if isempty(pDriverBufferChA) || isempty(pDriverBufferChB)
        error('Driver buffers could not be initialized.');
    end

    % Set driver buffers for channels A and B
    status.setDataBufferChA = invoke(obj.DeviceObj, 'ps5000aSetDataBuffer', 0, pDriverBufferChA, overviewBufferSize, segmentIndex, ratioMode);
    status.setDataBufferChB = invoke(obj.DeviceObj, 'ps5000aSetDataBuffer', 1, pDriverBufferChB, overviewBufferSize, segmentIndex, ratioMode);

    % Check if setting driver buffers was successful
    if status.setDataBufferChA ~= 0 || status.setDataBufferChB ~= 0
        error('Failed to set driver buffers for channels A and/or B.');
    end

    % Initialize application buffers for channels A and B
    pAppBufferChA = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));
    pAppBufferChB = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));

    % Check if application buffers are initialized correctly
    if isempty(pAppBufferChA) || isempty(pAppBufferChB)
        error('Application buffers could not be initialized.');
    end

    % Obtain the streaming group object
    streamingGroupObj = get(obj.DeviceObj, 'Streaming');
    
    % Check if streamingGroupObj is obtained correctly
    if isempty(streamingGroupObj)
        error('Failed to obtain Streaming group object.');
    end
    
    obj.streamingGroupObj = streamingGroupObj(1);  % Use the first streaming group

    % Link application and driver buffers for channel A
    status.setAppDriverBuffersA = invoke(streamingGroupObj, 'setAppAndDriverBuffers', 0, pAppBufferChA, pDriverBufferChA, overviewBufferSize);

    % Check if linking application and driver buffers for channel A was successful
    if status.setAppDriverBuffersA ~= 0  % Assuming '0' indicates success
        error('Failed to link application and driver buffers for Channel A.');
    end

    % Link application and driver buffers for channel B
    status.setAppDriverBuffersB = invoke(streamingGroupObj, 'setAppAndDriverBuffers', 1, pAppBufferChB, pDriverBufferChB, overviewBufferSize);

    % Check if linking application and driver buffers for channel B was successful
    if status.setAppDriverBuffersB ~= 0  % Assuming '0' indicates success
        error('Failed to link application and driver buffers for Channel B.');
    end

    disp('Application and driver buffers linked successfully for both channels.');
end



 function obj = startDataStreaming(obj)
    % Start streaming data collection and handle live plotting if enabled.

    % Start streaming data collection
    [status.runStreaming, sampleInterval, sampleIntervalTimeUnitsStr] = ...
        invoke(obj.streamingGroupObj, 'ps5000aRunStreaming', obj.DownSampleRatio, ...
        obj.DownSampleRatioMode, obj.overviewBufferSize);

    disp('Streaming data...');
    fprintf('Click the STOP button to stop capture or wait for auto stop if enabled.\n');

    % Initialize variables for data collection
    hasAutoStopOccurred = false;  % Indicates if the device has stopped automatically
    powerChange = false;  % If the device power status has changed
    newSamples = 0;  % Number of new samples returned from the driver
    previousTotal = 0;  % The previous total number of samples
    totalSamples = 0;  % Total samples captured by the device
    startIndex = 0;  % Start index of data in the buffer returned
    hasTriggered = false;  % To indicate if trigger has occurred
    triggeredAtIndex = 0;  % The index in the overall buffer where the trigger occurred

    % Display a 'Stop' button
    stopFig = figure('Name', 'Stop Button', 'NumberTitle', 'off', 'Position', [100, 100, 200, 50]);
    uicontrol('Style', 'pushbutton', 'String', 'STOP', 'Callback', 'setappdata(gcbf, ''running'', 0);', ...
              'UserData', 1, 'Position', [50 10 100 30]);

    % Check if live plotting is enabled
    if obj.plotLiveData
        % Setup for live plotting
        obj.setupLivePlotting(sampleInterval, sampleIntervalTimeUnitsStr);
    end

     % Start collecting data
    obj.collectData();

    % After data collection is finished or stopped
    disp('Data streaming and collection finished.');
    
end


function obj = collectData(obj)
    % Collect samples as long as the hasAutoStopOccurred flag has not been
    % set or the call to getStreamingLatestValues() does not return an error
    % code (check for STOP button push inside loop).
    hasAutoStopOccurred = false;
    status.getStreamingLatestValuesStatus = 'PICO_OK';  % Assume PICO_OK is a placeholder for actual success status

    while ~hasAutoStopOccurred && strcmp(status.getStreamingLatestValuesStatus, 'PICO_OK')
        ready = false;

        while ~ready
            status.getStreamingLatestValuesStatus = invoke(obj.streamingGroupObj, 'getStreamingLatestValues');
            ready = invoke(obj.streamingGroupObj, 'isReady');

            % Check if the STOP button has been clicked (assuming you have implemented a stop button mechanism)
            if ~getappdata(gcf, 'running')
                disp('STOP button clicked - aborting data collection.');
                break;
            end

            drawnow;
        end

        % Retrieve and process new samples...
        [newSamples, startIndex] = invoke(obj.streamingGroupObj, 'availableData');

        if newSamples > 0
            % Your data processing code here...

            % Example: Copy new data into final buffers
            % obj.pBufferChAFinal.Value(startIndex+1:startIndex+newSamples) = newDataChA;
            % obj.pBufferChBFinal.Value(startIndex+1:startIndex+newSamples) = newDataChB;

            % If live plotting is enabled, update plot here...
        end

        % Check if auto stop has occurred
        hasAutoStopOccurred = invoke(obj.streamingGroupObj, 'autoStopped');

        if hasAutoStopOccurred
            disp('AutoStop: TRUE - exiting data collection loop.');
            break;
        end

        % Additional loop checks or operations...
    end
end







        function obj = stopDevice(obj)
            % Method to stop the Pico device

            if isempty(obj.ps5000aDeviceObj) || ~isa(obj.ps5000aDeviceObj, 'instrument')
                error('Pico device object is not correctly initialized or is invalid.');
            end

            try
                % Attempt to stop the Pico device. The invoke method might be expecting
                % a certain number of output arguments, so ensure you provide the correct count.
                % If the method does not return a second argument, it should not be captured.
                status = invoke(obj.ps5000aDeviceObj, 'ps5000aStop');

                % Check if the stop command was successful
                % Here, you might need to check the value of 'status' or use another
                % method to determine if the stop was successful, depending on the
                % API's behavior and documentation.

                % Properly handle the device object after stopping
                disconnect(obj.ps5000aDeviceObj);
                delete(obj.ps5000aDeviceObj);

                % Clear the device object property to reflect that it's no longer connected
                obj.ps5000aDeviceObj = [];

            catch err
                % Handle any errors that occur during the stop process
                error('Error stopping Pico device: %s', err.message);
            end
        end

function obj = setTriggerSampleSizes(obj, numPreTriggerSamples, numPostTriggerSamples)
    % Set the number of pre- and post-trigger samples.
    
    % Validate sample sizes
    if numPreTriggerSamples < 0 || numPostTriggerSamples < 0
        error('Sample sizes must be non-negative.');
    end
    
    % Set pre- and post-trigger samples
    set(obj.DeviceObj, 'numPreTriggerSamples', numPreTriggerSamples);
    set(obj.DeviceObj, 'numPostTriggerSamples', numPostTriggerSamples);
    
    disp(['Pre-trigger samples set to ', num2str(numPreTriggerSamples)]);
    disp(['Post-trigger samples set to ', num2str(numPostTriggerSamples)]);
end


function obj = prepareFinalBuffers(obj, downSampleRatio)
    % Calculate the final buffer length and initialize final buffers for channels A and B.
    
    % Validate downSampleRatio
    if isempty(downSampleRatio) || downSampleRatio <= 0
        error('Invalid downSampleRatio. It must be a positive integer.');
    end

    % Calculate maxSamples based on pre-trigger and post-trigger samples
    maxSamples = get(obj.DeviceObj, 'numPreTriggerSamples') + ...
                 get(obj.DeviceObj, 'numPostTriggerSamples');

    % Calculate the final buffer length, taking into account the downsampling ratio
    finalBufferLength = round(1.5 * maxSamples / downSampleRatio);

    % Initialize final buffers for channels A and B
    obj.pBufferChAFinal = libpointer('int16Ptr', zeros(finalBufferLength, 1, 'int16'));
    obj.pBufferChBFinal = libpointer('int16Ptr', zeros(finalBufferLength, 1, 'int16'));

    disp(['Final buffers initialized with length: ', num2str(finalBufferLength)]);

    % Prompt User to indicate if they wish to plot live streaming data
    choice = questdlg('Plot live streaming data?', 'Streaming Data Plot', 'Yes', 'No', 'Yes');
    obj.plotLiveData = strcmp(choice, 'Yes');

    if obj.plotLiveData
        disp('User opted to plot live streaming data.');
    else
        disp('User opted not to plot live streaming data.');
    end
end






        function obj = processData(obj)
            % Process data post-capture
        end

        function obj = disconnectDevice(obj)
            % Disconnect device object from hardware
        end
    end
end


NeilH
PICO STAFF
PICO STAFF
Posts: 283
Joined: Tue Jul 18, 2017 8:28 am

Re: Making a Matlab Class for streaming mode

Post by NeilH »

Hi

I think this being dealt with over email by our team. If this isn't the case let me know
Neil
Technical Support Engineer

Post Reply