function [IQData, sampleRate] = hCaptureIQUsingN9010A(instrumentAddress, ...
    measureTime, centerFrequency, bandWidth, externalTrigger, varargin)
%hCaptureIQUsingN9010A Capture baseband IQ data using N9010 EXA.
%
%   [IQData, sampleRate] = hCaptureIQUsingN9010A(instrumentAddress, ...
%       measureTime, centerFrequency, bandWidth, externalTrigger)
%   connects to an Agilent N9010A Signal Analyzer at the hostname/IP
%   address specified by instrumentAddress, sets up the acquisition of the
%   baseband signal at the carrier frequency specified by
%   centerFrequency (Hz) for the period of time specified by
%   measureTime (sec). The parameter bandWidth (Hz) controls the bandwidth
%   of the analyzer and the external trigger flag (true/false) controls
%   whether the instrument uses external triggers on trigger1 or an
%   internal trigger to start acquisition immediately.
%
%   IQData is returned as a complex data vector along with the sample rate
%   (in Hz) at which it was captured.
%
%   [IQData, sampleRate] = hCaptureIQUsingN9010A(instrumentAddress, ...
%       measureTime, centerFrequency, bandWidth, externalTrigger, startFreq, ...
%       stopFreq, resBW, videoBW) sets the instrument to the spectrum
%   analyzer mode where the startFreq, stopFreq, resBW, videoBW are
%   specified in Hz.
%
%   Example: Capture data for 2ms at a center frequency of 1GHz, with a
%   bandwidth of 15MHz.
%       [IQData, sampleRate] = hCaptureIQUsingN9010A(...
%             'A-N9010A-21026.dhcp.mathworks.com', 2e-3, 1e9, 15e6, false);
%
%   Example: Capture data for 2ms at a center frequency of 1GHz, with a
%   bandwidth of 15MHz. Additionally, switch to spectrum mode to view RF
%   spectrum from 990MHz to 1010MHz with a resolution bandwidth of 20KHz
%   and video bandwidth of 300Hz.
%       [IQData, sampleRate] = hCaptureIQUsingN9010A(...
%             'A-N9010A-21026.dhcp.mathworks.com', 2e-3, 1e9, 15e6, false, ...
%             990e6, 1010e6, 20e3, 300);

%   Copyright 2013-2016, The MathWorks Inc.

narginchk(5, 9);
if nargin>5
    SAMode = 1;         % Enable spectrum analyzer mode
    startFreq = varargin{1};
    stopFreq  = varargin{2};
    resBW     = varargin{3};
    videoBW   = varargin{4};
else
    SAMode = 0;         % Disable spectrum analyzer mode
end

% Check for ICT presence
if isempty(ver('instrument')) 
    error('hCaptureIQUsingN9010A:NoICT', ...
          ['Please install Instrument Control Toolbox for ', ...
           'this function to work.']);
end

% Verify VISA installation and select VISA if available
foundVISA = instrhwinfo('visa');
if ~isempty(foundVISA.InstalledAdaptors)
    deviceObject = visa(foundVISA.InstalledAdaptors{1}, ...
                    sprintf('TCPIP0::%s::inst0::INSTR',instrumentAddress));
    usingVISA = true;
else
    deviceObject = tcpip(instrumentAddress,5025);
    usingVISA = false;
end

% Set input buffer size
set(deviceObject,'InputBufferSize',40e6);
% Set timeout to 30 seconds
set(deviceObject,'Timeout',30);
% Set object to use BigEndian format
set(deviceObject,'ByteOrder','bigEndian');

% Open connection to the instrument
fopen(deviceObject);

% Clear hardware buffers on the instrument.
if usingVISA
    % This is only possible with VISA objects
    clrdevice(deviceObject);
end

% Reset instrument and clear instrument error queue
fprintf(deviceObject,'*RST;*CLS');

% Wait till operation is complete
localWaitTillComplete;

% Set up signal analyzer mode to Basic/IQ mode
fprintf(deviceObject,':INSTrument:SELect BASIC');
% Set the center frequency
fprintf(deviceObject,[':SENSe:FREQuency:CENTer ' num2str(centerFrequency) 'Hz']);
% Set the resolution bandwidth
fprintf(deviceObject,[':SENSe:WAVEform:BANDwidth:RESolution ' num2str(bandWidth) 'Hz']);
% Set the time for which measurement needs to be made
fprintf(deviceObject,[':WAVeform:SWE:TIME '  num2str(measureTime) 's']);
% Turn off electrical attenuation.
fprintf(deviceObject,':SENSe:POWer:RF:EATTenuation:STATe OFF');
% Set mechanical attenuation level to 0 dB
fprintf(deviceObject,':SENSe:POWer:RF:ATTenuation 0');
% Turn IQ signal ranging to auto
fprintf(deviceObject,':SENSe:VOLTage:IQ:RANGe:AUTO ON');
% Turn off averaging
fprintf(deviceObject,':SENSe:WAVeform:AVER OFF');

% set to take one single measurement 
fprintf(deviceObject,':INIT:CONT OFF');

if externalTrigger
    % Set the trigger to external source 1 with positive slope triggering
    % When using external triggering, one needs a low-to-high transition on
    % the physical cable connected to the trigger1 in of the analyzer.
    fprintf(deviceObject,':TRIGger:WAVeform:SOURce EXTernal1');
    fprintf(deviceObject,':TRIGger:EXTERNAL1:SLOPe POSitive');
else
    % Set the trigger to immediate
    fprintf(deviceObject,':TRIGger:WAVeform:SOURce IMMediate');
end

% Set the endianness of returned data
fprintf(deviceObject,':FORMat:BORDer NORMal');
% Set the format of the returned data
fprintf(deviceObject,':FORMat:DATA REAL,32');

% Trigger the instrument and initiate measurement
fprintf(deviceObject,'*TRG');
fprintf(deviceObject,':INITiate:WAVeform');
% Wait till operation is complete
localWaitTillComplete;

% Read the IQ data
fprintf(deviceObject,':READ:WAV0?');
data=binblockread(deviceObject,'float');
% Read the additional terminator character from the instrument
fread(deviceObject,1);

% Retrieve information about the most recently acquired data
fprintf(deviceObject,':FETCH:WAV1?');
signalSpec = binblockread(deviceObject,'float'); fread(deviceObject,1);
sampleRate = 1/signalSpec(1);

% Separate the data and build the complex IQ vector.
inphase=data(1:2:end);
quadrature=data(2:2:end);
IQData=inphase+1i*quadrature;

localDisplayInstrumentErrors;

if SAMode
   % If specified turn the Instrument back to Spectrum Analyzer mode
   
    % Switch back to the spectrum analyzer view
    fprintf(deviceObject,':INSTrument:SELect SA');

    % Set mechanical attenuation level
    fprintf(deviceObject,':SENSe:POWer:RF:ATTenuation 0');

    % Set the center frequency, RBW and VBW and trigger
    fprintf(deviceObject,[':SENSe:FREQuency:CENTer ' num2str(centerFrequency)]);
    fprintf(deviceObject,[':SENSe:FREQuency:STARt ' num2str(startFreq)]);
    fprintf(deviceObject,[':SENSe:FREQuency:STOP ' num2str(stopFreq)]);
    fprintf(deviceObject,[':SENSe:BANDwidth:RESolution ' num2str(resBW)]);
    fprintf(deviceObject,[':SENSe:BANDwidth:VIDeo ' num2str(videoBW)]);

    % Continuous measurement
    fprintf(deviceObject,':INIT:CONT ON'); 

    % Trigger
    fprintf(deviceObject,'*TRG');   
end

% Clean Up
% Close and delete instrument connections
fclose(deviceObject);
delete(deviceObject);


    function localWaitTillComplete()
        % Wait until instrument operation is complete
        operationComplete = str2double(query(deviceObject,'*OPC?'));
        while ~operationComplete
            operationComplete = str2double(query(deviceObject,'*OPC?'));
        end
    end

    function localDisplayInstrumentErrors()
        % Display any instrument errors
        instrumentError = query(deviceObject,'SYSTem:ERRor?');
        while ~contains(lower(instrumentError),'no error')
            fprintf('\tInstrument Error: %s',instrumentError);
            instrumentError = query(deviceObject,'SYSTem:ERRor:NEXT?');
        end
    end

end