Undesirable offset when using pico4444 in streaming mode

Post your MATLAB discussions here
Post Reply
Luis01
Newbie
Posts: 0
Joined: Fri May 03, 2024 4:07 pm

Undesirable offset when using pico4444 in streaming mode

Post by Luis01 »

Hello,

I am using the PicoScope to acquire two sine signals and then I am plotting them in MatLab with the streaming mode. I am using as a base the PS4000A_ID_Streaming_Example.

However, when viewing my signals I am seeing an undesirable offset. After some time this offset decays and the signal is displayed correctly at the end (Figures and code used attached). I dont know why this is happening and I would appreciate if you could help me on this.

Thanks in advance.

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

Re: Undesirable offset when using pico4444 in streaming mode

Post by NeilH »

Hi Luis

Are you able to resend the picture and code as they haven't seemed to attached to the post correctly?

Does this occur in PicoScope 7 as well or just when using the scope in MATLAB?
Neil
Technical Support Engineer

Luis01
Newbie
Posts: 0
Joined: Fri May 03, 2024 4:07 pm

Re: Undesirable offset when using pico4444 in streaming mode

Post by Luis01 »

Hi Neil,

Yes, I am re-attaching the code and fig again. I haven't had this problem in PicoScope, it occurs only in MatLab. I have set the AC coupling mode, so in theory this should not be happening. Thanks for your message.

Code: Select all

%% PicoScope 4000 Series (A API) Instrument Driver Oscilloscope Streaming Data Capture Example

%% Clear command window

clc;
close all;

%% Load configuration information

PS4000aConfig;

%% Parameter definitions
% Define any parameters that might be required throughout the script.

channelA = ps4000aEnuminfo.enPS4000AChannel.PS4000A_CHANNEL_A;
channelB = ps4000aEnuminfo.enPS4000AChannel.PS4000A_CHANNEL_B;

%% Device connection

% Check if an Instrument session using the device object 'ps4000aDeviceObj'
% is still open, and if so, disconnect if the User chooses 'Yes' when prompted.
if (exist('ps4000aDeviceObj', 'var') && ps4000aDeviceObj.isvalid && strcmp(ps4000aDeviceObj.status, 'open'))
    
    openDevice = questionDialog(['Device object ps4000aDeviceObj has an open connection. ' ...
        'Do you wish to close the connection and continue?'], ...
        'Device Object Connection Open');
    
    if (openDevice == PicoConstants.TRUE)
        
        % Close connection to device
        disconnect(ps4000aDeviceObj);
        delete(ps4000aDeviceObj);
        
    else

        % Exit script if User 
        return;
        
    end
    
end

% Create device -  specify serial number if required
% Specify serial number as 2nd argument if required.
ps4000aDeviceObj = icdevice('picotech_ps4000a_generic', ''); 

% Connect device
connect(ps4000aDeviceObj);

%% Display unit information

[infoStatus, unitInfo] = invoke(ps4000aDeviceObj, 'getUnitInfo');

disp('Device information:-')
disp(unitInfo);

%% Channel setup
%
% All channels are enabled by default - switch off all except Channels A
% and B. 
%
% If using the PicoScope 4444, select the appropriate range value for the
% probe connected to an input channel using the enumeration values
% available from the |ps4000aEnuminfo.enPicoConnectProbeRange| substructure.
%
% Channel settings are changed as shown below:

% Channel A
channelSettings(1).enabled          = PicoConstants.TRUE;
channelSettings(1).coupling         = ps4000aEnuminfo.enPS4000ACoupling.PS4000A_AC;
channelSettings(1).range            = ps4000aEnuminfo.enPS4000ARange.PS4000A_200MV;
channelSettings(1).analogueOffset   = 0.0;

% Channel B
channelSettings(2).enabled          = PicoConstants.TRUE;
channelSettings(2).coupling         = ps4000aEnuminfo.enPS4000ACoupling.PS4000A_AC;
channelSettings(2).range            = ps4000aEnuminfo.enPS4000ARange.PS4000A_200MV;
channelSettings(2).analogueOffset   = 0.0;
    

% Channel C
    channelSettings(3).enabled          = PicoConstants.FALSE;
    channelSettings(3).coupling         = ps4000aEnuminfo.enPS4000ACoupling.PS4000A_DC;
    channelSettings(3).range            = ps4000aEnuminfo.enPS4000ARange.PS4000A_2V;
    channelSettings(3).analogueOffset   = 0.0;

    % Channel D
    channelSettings(4).enabled          = PicoConstants.FALSE;
    channelSettings(4).coupling         = ps4000aEnuminfo.enPS4000ACoupling.PS4000A_DC;
    channelSettings(4).range            = ps4000aEnuminfo.enPS4000ARange.PS4000A_2V;
    channelSettings(4).analogueOffset   = 0.0;


% Obtain the number of analog channels on the device from the driver
numChannels = get(ps4000aDeviceObj, 'channelCount');

for ch = 1:numChannels
   
    status.setChannelStatus(ch) = invoke(ps4000aDeviceObj, 'ps4000aSetChannel', ...
        (ch - 1), channelSettings(ch).enabled, ...
        channelSettings(ch).coupling, channelSettings(ch).range, ...
        channelSettings(ch).analogueOffset);
    
end

% Obtain the range and units for each enabled channel. For the PicoScope
% 4824, this will be in millivolts.
[chARange, chAUnits] = invoke(ps4000aDeviceObj, 'getChannelInputRangeAndUnits', ps4000aEnuminfo.enPS4000AChannel.PS4000A_CHANNEL_A);
[chBRange, chBUnits] = invoke(ps4000aDeviceObj, 'getChannelInputRangeAndUnits', ps4000aEnuminfo.enPS4000AChannel.PS4000A_CHANNEL_B);

% Obtain the maximum Analog Digital Converter Count value from the driver
% - this is used for scaling values returned from the driver when data is
% collected.
maxADCCount = double(get(ps4000aDeviceObj, 'maxADCValue'));


%% Set resolution

status.ps4000aSetDeviceResolution=invoke(ps4000aDeviceObj, 'ps4000aSetDeviceResolution',14);

%% Set bandwidth limit

status.ps4000aSetBandwidthFilterChA= invoke(ps4000aDeviceObj, 'ps4000aSetBandwidthFilter', ...
channelA,2);

status.ps4000aSetBandwidthFilterChB= invoke(ps4000aDeviceObj, 'ps4000aSetBandwidthFilter', ...
channelB,2);


%% Set simple trigger
%
% Set a trigger on Channel A, with an auto timeout - the default value for
% delay is used.

% Trigger properties and functions are located in the Instrument
% Driver's Trigger group.

triggerGroupObj = get(ps4000aDeviceObj, 'Trigger');
triggerGroupObj = triggerGroupObj(1);

% Set the |autoTriggerMs| property in order to automatically trigger the
% oscilloscope after 1 second if a trigger event has not occurred. Set to 0
% to wait indefinitely for a trigger event.

set(triggerGroupObj, 'autoTriggerMs', 1000);
triggerThreshold = mv2adc(0, channelSettings(1).range, maxADCCount); % Convert mV to ADC counts
% Channel     : 0 (ps4000aEnuminfo.enPS4000AChannel.PS4000A_CHANNEL_A)
% Threshold   : 500 (mV)
% Direction   : 2 (ps4000aEnuminfo.enPS4000AThresholdDirection.PS4000A_RISING)

[status.setSimpleTrigger] = invoke(triggerGroupObj, 'setSimpleTrigger', 0, triggerThreshold, 2);


%% Set data buffers
%
% Data buffers for Channel A and B - buffers should be set with the
% (lib)ps400a shared library, and these *MUST* be passed with application
% buffers to the wrapper shared library. This will ensure that data is
% correctly copied from the shared library buffers for later processing.

overviewBufferSize  = 25000; % Size of the buffer(s) to collect data from the driver's buffer(s).
segmentIndex        = 0;   
ratioMode           = ps4000aEnuminfo.enPS4000ARatioMode.PS4000A_RATIO_MODE_NONE;

% Buffers to be passed to the driver
pDriverBufferChA = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));
pDriverBufferChB = libpointer('int16Ptr', zeros(overviewBufferSize, 1, 'int16'));

status.setDataBufferChA = invoke(ps4000aDeviceObj, 'ps4000aSetDataBuffer', ...
    channelA, pDriverBufferChA, overviewBufferSize, segmentIndex, ratioMode);

status.setDataBufferChB = invoke(ps4000aDeviceObj, 'ps4000aSetDataBuffer', ...
   channelB, pDriverBufferChB, overviewBufferSize, segmentIndex, ratioMode);

% Application Buffers - these are for temporarily copying data from the driver.
pAppBufferChA = libpointer('int16Ptr', zeros(overviewBufferSize, 1));
pAppBufferChB = libpointer('int16Ptr', zeros(overviewBufferSize, 1));

% Streaming properties and functions are located in the Instrument
% Driver's Streaming group.

streamingGroupObj = get(ps4000aDeviceObj, 'Streaming');
streamingGroupObj = streamingGroupObj(1);

% Register application buffer and driver buffers (with the wrapper driver).

status.setAppAndDriverBuffersA = invoke(streamingGroupObj, 'setAppAndDriverBuffers', channelA, ...
    pAppBufferChA, pDriverBufferChA, overviewBufferSize);

status.setAppAndDriverBuffersB = invoke(streamingGroupObj, 'setAppAndDriverBuffers', channelB, ...
   pAppBufferChB, pDriverBufferChB, overviewBufferSize);



%% Start streaming and collect data
%
% Use default value for streaming interval which is 1e-6 for 1 MS/s.
% Collect data for 1 second with auto stop. The maximum array size will
% depend on the PC's resources. For further information, type |memory| in
% the MATLAB Command Window and press Enter.
%
% To change the sample interval set the |streamingInterval| property of the
% |Streaming| group object. The call to |ps4000aRunStreaming()| will output the actual
% sampling interval used by the driver.

% For 200 kS/s, specify 5 us
%set(streamingGroupObj, 'streamingInterval', 5e-6);

% For 10 MS/s, specify 100 ns
%set(streamingGroupObj, 'streamingInterval', 100e-9);

% For 100 kS/s, specify 10 us
set(streamingGroupObj, 'streamingInterval', 10e-6);

% Set the number of pre- and post-trigger samples
% If no trigger is set 'numPreTriggerSamples' is ignored
set(ps4000aDeviceObj, 'numPreTriggerSamples', 0);
set(ps4000aDeviceObj, 'numPostTriggerSamples', 100000);

% The autoStop parameter can be set to false (0).
%set(streamingGroupObj, 'autoStop', PicoConstants.FALSE);

% Set other streaming parameters
downSampleRatio     = 1;
downSampleRatioMode = ps4000aEnuminfo.enPS4000ARatioMode.PS4000A_RATIO_MODE_NONE;

% Defined buffers to store data collected from the channels.
% If capturing data without using the autoStop flag, or if using a trigger 
% with the autoStop flag, allocate sufficient space (1.5 times the size is 
% shown below) to allow for pre-trigger data. Pre-allocating the array is 
% more efficient than using vertcat to combine data.

maxSamples = get(ps4000aDeviceObj, 'numPreTriggerSamples') + ...
    get(ps4000aDeviceObj, 'numPostTriggerSamples');

% Take into account the downsampling ratio mode - required if collecting
% data without a trigger and using the autoStop flag. 
% finalBufferLength = round(1.5 * maxSamples / downSampleRatio);

pBufferChAFinal = libpointer('int16Ptr', zeros(maxSamples, 1, 'int16'));
pBufferChBFinal = libpointer('int16Ptr', zeros(maxSamples, 1, 'int16'));

% Prompt User to indicate if they wish to plot live streaming data.
plotLiveData = questionDialog('Plot live streaming data?', 'Streaming Data Plot');

if (plotLiveData == PicoConstants.TRUE)
   
    disp('Live streaming data collection with second plot on completion.');
    
else
    
    disp('Streaming data plot on completion.');
    
end

% Start streaming data collection.
[status.runStreaming, actualSampleInterval, sampleIntervalTimeUnitsStr] = ...
    invoke(streamingGroupObj, 'ps4000aRunStreaming', downSampleRatio, ...
    downSampleRatioMode, overviewBufferSize);
    
disp('Streaming data collection...');
fprintf('Click the STOP button to stop capture or wait for auto stop if enabled.\n\n') 

% Variables to be used when collecting the data
isAutoStopSet       = PicoConstants.FALSE;
newSamples          = 0; % Number of new samples returned from the driver.
previousTotal       = 0; % The previous total number of samples.
totalSamples        = 0; % Total number of samples captured by the device.
startIndex          = 0; % Start index of data in the buffer returned (zero-based).
hasTriggered        = 0; % To indicate if trigger event has occurred.
triggeredAtIndex    = 0; % The index in the overall buffer where the trigger occurred (zero-based).

status.getStreamingLatestValues = PicoStatus.PICO_OK; % OK

% Display a 'Stop' button.
[stopFig.h, stopFig.h] = stopButton();             
             
flag = 1; % Use flag variable to indicate if the stop button has been clicked (0).
setappdata(gcf, 'run', flag);

% Plot Properties - these are for displaying data as it is collected. In
% this example, data is displayed in millivolts. For other probes,
% including when using PicoConnect 442 or current probes with the PicoScope
% 4444, use the appropriate units for the vertical axes.

if (plotLiveData == PicoConstants.TRUE)
    
    % Plot on a single figure 
    figure1 = figure('Name','PicoScope 4000 Series (A API) Example - Streaming Mode Capture', ...
         'NumberTitle','off');

     axes1 = axes('Parent', figure1);

    % Estimate x-axis limit to try and avoid using too much CPU resources
    % when drawing - use max voltage range selected if plotting multiple
    % channels on the same graph.

    xlim(axes1, [0 (actualSampleInterval * maxSamples)]);

    yRange = max(chARange, chBRange);
    ylim(axes1,[(-1 * yRange) yRange]);

    hold(axes1,'on');
    grid(axes1, 'on');

    title(axes1, 'Live Streaming Data Capture');
    xLabelStr = strcat('Time (', sampleIntervalTimeUnitsStr, ')');
    xlabel(axes1, xLabelStr);
    ylabel(axes1, getVerticalAxisLabel(chAUnits));

end

% Collect samples as long as the autoStop flag has not been set or the call
% to getStreamingLatestValues does not return an error code (check for STOP
% button push inside loop).
while (isAutoStopSet == PicoConstants.FALSE && status.getStreamingLatestValues == PicoStatus.PICO_OK)
    
    ready = PicoConstants.FALSE;
   
    while (ready == PicoConstants.FALSE)

       status.getStreamingLatestValues = invoke(streamingGroupObj, 'getStreamingLatestValues');
       
       ready = invoke(streamingGroupObj, 'isReady');

       % Give option to abort from here
       flag = getappdata(gcf, 'run');
       drawnow;

       if (flag == 0)

            disp('STOP button clicked - aborting data collection.')
            break;

       end

       if (plotLiveData == PicoConstants.TRUE)

            drawnow;

        end

    end
    
    % Check for new data values
    [newSamples, startIndex] = invoke(streamingGroupObj, 'availableData');

    if (newSamples > 0)
        
        % Check if the scope has triggered
        [triggered, triggeredAt] = invoke(streamingGroupObj, 'isTriggerReady');

        if (triggered == PicoConstants.TRUE)

            % Adjust trigger position as MATLAB does not use zero-based
            % indexing.
            bufferTriggerPosition = triggeredAt + 1;
            
            fprintf('Triggered - index in buffer: %d\n', bufferTriggerPosition);

            hasTriggered = triggered;

            % Set the total number of samples at which the device
            % triggered.
            triggeredAtIndex = totalSamples + bufferTriggerPosition;

        end

        previousTotal = totalSamples;
        totalSamples  = totalSamples + newSamples;

        % Printing to console can slow down acquisition - use for
        % demonstration.
        fprintf('Collected %d samples, startIndex: %d total: %d.\n', newSamples, startIndex, totalSamples);
        
        % Position indices of data in the buffer(s).
        firstValuePosn = startIndex + 1;
        lastValuePosn = startIndex + newSamples;
        
        % Convert data values from the application buffer(s) - in this
        % example
        bufferChAmV = adc2mv(pAppBufferChA.Value(firstValuePosn:lastValuePosn), chARange, maxADCCount);
        bufferChBmV = adc2mv(pAppBufferChB.Value(firstValuePosn:lastValuePosn), chBRange, maxADCCount);

        % Process collected data further if required - this example plots
        % the data if the User has selected 'Yes' at the prompt.
        
        % Copy data into the final buffer(s).
        pBufferChAFinal.Value(previousTotal + 1:totalSamples) = bufferChAmV;
        pBufferChBFinal.Value(previousTotal + 1:totalSamples) = bufferChBmV;
        
        if (plotLiveData == PicoConstants.TRUE)
            
            % Time axis
            % Multiply by ratio mode as samples get reduced.
            time = (double(actualSampleInterval) * double(downSampleRatio)) * (previousTotal:(totalSamples - 1));

            plot(time, bufferChAmV, time, bufferChBmV);

        end

        % Clear variables.
        clear bufferChAmV;
        clear bufferChBmV;
        clear firstValuePosn;
        clear lastValuePosn;
        clear startIndex;
        clear triggered;
        clear triggerAt;
   
    end
   
    % Check if auto stop has occurred.
    isAutoStopSet = invoke(streamingGroupObj, 'autoStopped');

    if (isAutoStopSet == PicoConstants.TRUE)

       disp('AutoStop: TRUE - exiting loop.');
       break;

    end
   
    % Check if 'STOP' button pressed.
    flag = getappdata(gcf, 'run');
    drawnow;

    if (flag == 0)

        disp('STOP button clicked - aborting data collection.')
        break;
        
    end
 
end

% Close the STOP button window
if (exist('stopFig', 'var'))
    
    close('Stop Button');
    clear stopFig;
        
end

if (plotLiveData == PicoConstants.TRUE)
    
    drawnow;
    
end

if (hasTriggered == PicoConstants.TRUE)
   
    fprintf('Triggered at overall index: %d\n', triggeredAtIndex);
    
end

if (plotLiveData == PicoConstants.TRUE)
    
    % Take hold off the current figure
    hold off;
    movegui(figure1, 'west');
    
end

fprintf('\n');

%% Stop the device
% This function should be called regardless of whether the |autoStop|
% property is enabled or not.

status.stop = invoke(ps4000aDeviceObj, 'ps4000aStop');

%% Find the number of samples
% This is the number of samples held in the |(lib)ps4000a| shared library itself. The actual
% number of samples collected when using a trigger is likely to be greater.
[status.noOfStreamingValues, numStreamingValues] = invoke(streamingGroupObj, 'ps4000aNoOfStreamingValues');

fprintf('Number of samples available from the driver: %u.\n\n', numStreamingValues);

%% Process data
% Process all data if required

% Reduce size of arrays if required.
if (totalSamples < maxSamples)
    
    pBufferChAFinal.Value(totalSamples + 1:end) = [];
    pBufferChBFinal.Value(totalSamples + 1:end) = [];
 
end

% Retrieve data for the Channels.
channelAFinal = pBufferChAFinal.Value();
channelBFinal = pBufferChBFinal.Value();

% Plot the total data collected on another figure.
finalFigure = figure('Name','PicoScope 4000 Series (A API) Example - Streaming Mode Capture', ...
    'NumberTitle','off');
finalFigureAxes = axes('Parent', finalFigure);
hold(finalFigureAxes, 'on');

title('Streaming Data Capture (Final)');
xLabelStr = strcat('Time (', sampleIntervalTimeUnitsStr, ')');
xlabel(finalFigureAxes, xLabelStr);
ylabel(finalFigureAxes, 'Voltage (mV)');

% Find the maximum voltage range.
maxYRange = max(chARange, chBRange);
ylim(finalFigureAxes,[(-1 * maxYRange) maxYRange]);

% Calculated values for time axis, then plot.
timeAxis = (double(actualSampleInterval) * double(downSampleRatio)) * (0:length(channelAFinal) - 1);
plot(finalFigureAxes, timeAxis, channelAFinal, timeAxis, channelBFinal);

grid(finalFigureAxes, 'on');
legend(finalFigureAxes, 'Channel A', 'Channel B');
hold(finalFigureAxes, 'off');

movegui(finalFigureAxes, 'east');


%% Disconnect device
% Disconnect device object from hardware.

disconnect(ps4000aDeviceObj);
delete(ps4000aDeviceObj);
Attachments
4a.jpg

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

Re: Undesirable offset when using pico4444 in streaming mode

Post by NeilH »

Are you able to give more details about exactly what the singla being inputted is in terms of voltage levels as it looks like the effect is due to the DC blocking capacitor charging up to remove the DC component of the signal.

You can get around this effect but running a dummy capture to do the charging before doing the proper capture, without closing the scope in between the two captures. The dummy capture doesn't need to have any data collected from the scope, just having the runBlock function called for it
Neil
Technical Support Engineer

Luis01
Newbie
Posts: 0
Joined: Fri May 03, 2024 4:07 pm

Re: Undesirable offset when using pico4444 in streaming mode

Post by Luis01 »

Hello Neil, thanks for your response.

The signal I am obtaining is a sine wave at 50 Hz that goes from -120mV to 120mV.

Thanks for the suggestion for the dummy capture, I have implemented it and I was able to get rid of that unwanted offset. However, now the trigger does not activate when I want (at 0V, rising condition), as you can see in the next pic. Any suggestion for this?

This is the code added to do the dummy capture before start streaming:

Code: Select all

%% Dummy Capture
%
% This section performs a dummy capture to charge the scope before the actual data capture.
% No data is collected or processed during this dummy capture.

% Set the number of pre- and post-trigger samples for dummy capture
set(ps4000aDeviceObj, 'numPreTriggerSamples', 0);
set(ps4000aDeviceObj, 'numPostTriggerSamples', 1); % Minimal number to perform a runBlock

% Obtain the Block group object from the device object
blockGroupObj = get(ps4000aDeviceObj, 'Block');
blockGroupObj = blockGroupObj(1);

% Start the dummy capture
disp('Starting dummy capture...');
status.runBlock = invoke(blockGroupObj, 'ps4000aRunBlock', ...
    get(ps4000aDeviceObj, 'numPreTriggerSamples'), get(ps4000aDeviceObj, 'numPostTriggerSamples'), ...
    1e6, 0, 0, 0); % Sample interval set to 1 MHz, adjust as needed

% Wait until the dummy capture is complete
[status.blockReady, ready] = invoke(blockGroupObj, 'ps4000aIsReady');
while (~ready)
    [status.blockReady, ready] = invoke(blockGroupObj, 'ps4000aIsReady');
end

% Stop the dummy capture
status.stopDummy = invoke(ps4000aDeviceObj, 'ps4000aStop');
disp('Dummy capture complete.');
Attachments
live.JPG

Post Reply