Forums › BB Series Discussions › BB60C matlab bbgetiq.m example code
- This topic has 18 replies, 2 voices, and was last updated 3 years, 11 months ago by Andrew.
- AuthorPosts
jgauthierParticipantSince I’m just a noob when it comes to spectrum analyzers, I have a question when grabbing real-time IQ data from the BB60C.
In the example MATLAB code in bbgetiq.m provided in the SDK:
iqlen = 1024; % Flush IQ data filter ramp up time [status, iqdata] = bbgetiq(handle, iqlen, true); % Get actual data [status, iqdata] = bbgetiq(handle, iqlen, true);
If I’m passing true as the purge argument, then why does the example call the bbgetiq function twice? According to the BB API manual, specifying purge = TRUE discards “any samples acquired by the API since the last time and(sic) bbGetIQ function was called.
It seems that if it were set to false you’d have to call it twice, but if true wouldn’t that accomplish the intent here?
AndrewModeratorHi Jgauthier,
This is a good question. The reason this is called twice has less to do with the purge parameter, and more to do with wanting to discard the first chunk of I/Q samples in the stream. Your assumptions about the purge parameter are correct. There are potentially several FIR filters (depending on settings) that are in the signal processing chain when I/Q streaming. These filters take hundreds/thousands of samples to ‘charge’ and until they do, the I/Q samples will not be amplitude accurate. By calling the function once with the purge parameter, this ensures that the first large segment of I/Q samples subject to this charge time are discarded. This is not necessary, but if you do this, doing this only once after each configure is adequate.
Another interesting component of this, the first call to GetIQ might not purge any samples, as there may not be any samples collected yet on the PC. So the worst case scenario is that the first call is just waiting until some samples arrive on the PC, and the second call is the one that actually purges that first chunk of I/Q to arrive.
Feel free to try just 1 call and see if that affects your application.
Also, waiting some small amount of time (100ms) before calling getIQ after configuring the receiver would ensure that the first call to GetIQ purges the first samples.
We chose to give these samples to the user instead of discard them in the API as they could still contain information just at a reduced amplitude.
If you are using any sort of triggering to acquire your waveform, calling the function twice is probably not necessary.
Regards,
Andrew
jgauthierParticipantThanks! Is there any example MATLAB code to collect IQ data upon a trigger input from the VSG60 to the BB60? Going through the SDK, all I find on triggers is in the API, not any of the sample code. I.e, do I have to add to VSG60.m to get trigger output status, or should I write a dedicated function for that?
AndrewModeratorJGauthier,
You will need to write a dedicated function for that. A wrapper function around the GetIQ functionality should be adequate for performing any triggering on the I/Q data. You could perform video or external triggering with this function. (you didn’t specify trigger type in your message) If external triggering, there is a C++ example showing you how to detect an external trigger event, you would need to then align your capture on the trigger position and collect any additional samples required (with purge disabled to ensure continuity)
The API does not perform any triggering for the user, only provides the facilities for triggering to be performed.
Let me know if you have specific questions as you work through this.
Regards,
Andrew
jgauthierParticipantThanks, Andrew
I guess I wasn’t specific enough. I want the step the VSG60 output of a simple CW waveform, stepping from a begin frequency to an ending frequency, and to output a trigger to the BB60 so it can grab IQ samples at each step when it sees the trigger input…
I hadn’t seen the
iq_external_trigger.cpp example
– that certainly helps!
AndrewModeratorThanks for the clarification.
Yes, you would need to call the vsgSubmitTrigger function from Matlab which you should find easy if you glance at the other wrapper code in VSG60 matlab class.
You could create a loop like this
while(forever) {
vsgSetFreq
vsgSubmitTrigger
vsgSubmitIQ(your_iq_cw_array)
}The trigger is submitted inline with any other function calls to the API.
Hope this helps.
Regards,
Andrew
jgauthierParticipantI’m getting there, but having problems with MATLAB.
I started with the
bbgetiq.m
in the SDK, and modified it by trying to redefine the arraytriggers
and the size of ittriggerCount
as specified in the API manual forbbGetIQUnpacked
.I tried:
triggerCount = int32(70); triggers = libpointer('int32', triggerCount); calllib('bb_api','bbGetIQUnpacked',handle,iqarrayptr,iqcountint,... triggers,triggerCount,purgearg,nullptr,nullptr,nullptr,nullptr);
but MATALAB complains:
Error using calllib Array must be numeric or logical or a pointer to one
I can’t figure out how to properly define an array of integers ‘triggerCount’ long…
jgauthierParticipantBack at it after a week’s vacation…
I realized I was calling libpointer with the wrong parameters, so I changed it to:
triggerCount = int32(70); % size of the trigger array (32-bit int) % from SDK example "iq_external_trigger.cpp" triggerArray = 0:0:triggerCount; % array where we'll store trigger events... triggers = libpointer('int32', triggerArray); % pointer to array of 32-bit ints ... status = calllib('bb_api','bbGetIQUnpacked',handle,iqarrayptr,iqcountint,triggers,triggerCount,purgearg,nullptr,nullptr,nullptr,nullptr);
I still get an error, but it’s
Parameter must be scalar. Error in libpointer (line 21) ptr=lib.pointer(varargin{:}); Error in bbgetiq (line 67) triggers = libpointer('int32', triggerArray); % pointer to array of 32-bit ints
Are there any working examples of calling that C-library function from MATLAB? I’m stumped.
AndrewModeratorJon,
Here is some code to get this going.
First, you will want to ensure you configure the external port to accept a rising edge (or falling) external trigger.
calllib('bb_api', 'bbConfigureIO', handle, int32(0), int32(64));
The best time to call this would be right after opening the device and only once. The 64 is taken from the header, it is the value for BB_PORT2_IN_TRIGGER_RISING_EDGE (0x40).
Then you will want to create a trigger array and count just like the iq data.
% Trigger parameters triggerptr = libpointer('int32Ptr', zeros(4, 1)); triggercount = int32(4);
In this instance I am creating an array of 4 integers to store trigger positions. If I am only expecting 1 trigger, then an array of one will work fine.
Now I need to call bbGetIQ with these new parameters.
status = calllib('bb_api', 'bbGetIQUnpacked', handle, ... iqarrayptr, iqcountint, triggerptr, triggercount, purgearg, ... nullptr, nullptr, nullptr, nullptr);
And pull out the array when it returns
triggers = triggerptr.Value;
If a trigger occurred during this capture of I/Q, you will see it in the trigger array.
disp(triggers);
Might show
230722 0 0 0
Meaning an external trigger event occurred at sample index 230722 (zero-based).
Regards,
Andrew
jgauthierParticipantThanks, Andrew!
Yes – I was configuring the BB60 to capture on rising edge of trigger in bbconfigureiq.m…
I’ll try your code examples and get back to you
jgauthierParticipantMATLAB didn’t seem to like the type int32ptr passed in libpointer, but it seems to be case sensitive in my version – int32Ptr worked fine…
jgauthierParticipantStill having a few problems…
If I’ve done the following:
- vsgSubmitTrigger
vsgSubmitIQ
vsg.transmit(waveform, mode) which calls vsgOutputWaveform or vsgRepeatWaveform
then later call vsg.stop (which does a vsgAbort)If I call vsg.transmit again, will it still output a trigger, or do I have to submit trigger again?
Thanks,
JonG
AndrewModerator- This reply was modified 3 years, 11 months ago by Andrew.
Jon,
vsgSubmitTrigger and vsgSubmitIQ are part of the VSG60s streaming interface. If you call those functions it will push those actions into the queue to transmit ASAP. For example, if you wanted to transmit a single short waveform synchronized with a trigger, you would call in sequence
vsgSubmitTrigger
vsgSubmitIQ(yourWaveform)
vsgFlush/vsgFlushAndWaitThe vsgOutputWaveform/vsgRepeatWaveform functions are just convenience functions that cannot be combined with others. vsgOutputWaveform transmits your waveform exactly once. It simply calls vsgSubmitIQ and vsgFlushAndWait for you. No triggering is possible with this function. vsgRepeatWaveform is simply a convenience over calling vsgSubmitIQ in a loop. It spins off a thread to do this for you and returns control to your application. If you perform any other action, the waveform being transmitted by vsgRepeatWaveform is interrupted.
If you want to re-transmit a waveform with thg trigger, you will need to recall the submitTrigger/IQ/flush sequence again.
I hope this helps.
Regards
jgauthierParticipant- bbConfigureIO(int32(0), int32(64))
- bbConfigureIQCenter(CenterFreq)
- bbConfigureIQ(downsample, bandwidth)
- vsgSetFrequency(CenterFreq)
- bbConfigureIQCenter(CenterFreq)
- bbInitiate(uint32(4), uint32(0))
- vsgSubmitTrigger()
- vsgSubmitIQ(iqPtr, count)
- vsgFlushAndWait()
- GetIQData(iqCount, purge)
Andrew,
Still not seeing triggers. Could it be the order I’m executing things?
I tried switching the order of vsgFlushAndWait and GetIQData, but it makes no difference.
In GetIQData, I have:
function [status, iqArray] = GetIQData(handle, iqCount, purge) nullPtr = int32(0); purgeArg = int32(purge); iqArrayPtr = libpointer('singlePtr', zeros(iqCount*2, 1)); iqCountInt = int32(iqCount); triggerPtr = libpointer('int32Ptr', ones(4,1)); % pointer to array of 32-bit integers triggerCount = int32(4); % size of the trigger array (32-bit int) status = calllib('bb_api','bbGetIQUnpacked',handle,... iqArrayPtr,iqCountInt,triggerPtr,triggerCount,... purgeArg,nullPtr,nullPtr,nullPtr,nullPtr); calllib('vsg_api', 'vsgFlushAndWait', handle); fprintf('Triggers:\n'); triggers = triggerPtr.Value; disp(triggers); % Convert results to complex numbers iqArray = reshape(iqArrayPtr.Value, 2, iqCount); iqArray = iqArray(1,:) + 1i * iqArray(2,:); iqArray = transpose(iqArray); end % of GetIQData
And I still find no triggers – disp(triggers) shows:
Triggers: 0 0 0 0
Any hints?
Thanks,
JonG
jgauthierParticipantI should add that:
*I’m collecting at the highest resolution: decimation = 1 and bandwidth = 27MHz.
*The VSG60’s signal output is connected directly to the BB60 signal input (with a physical cable and 10dB attenuator).I’ve tried varying iqlen from 4096 [2^12] to 1048576 [2^20] with no real difference – I don’t see a trigger event. I’ve checked to make sure I have the VSG port2 (trigger out) connected to BB60 port 2, and the BB60 is set to trigger on the pulse leading edge…
I have the trigger length set to the default of 10 usec.
I’ll have to check and see if the PC’s SSD can sustain writes of 250MB/sec…
JonG
jgauthierParticipant- Avg. Sustained Write Speed 203MB/s
- Avg. Sequential Mixed IO Speed 242MB/s
The PC is a Dell laptop with an i7-5600U CPU @ 2.6GHz with 8GB RAM running Windows 10 Enterprise 1809…
The drive is a SanDisk X300 256GB MSATA SSD. According to UserBench SpeedTest, here’s the performance #’s:
Borderline, I guess?
JonG
AndrewModeratorAre you setting purge to true in that example? Effectively flushing any data and triggers that have accumulated?
I see only one GetIQData function call. The BB60C is a streaming device, so how much data do you have accumulated before the actual trigger occurs? Are you sure the trigger isn’t buffered in the API and you just haven’t polled it yet through the getIQ function?
The BB60C API only stores about 1/2 second of I/Q data. If this buffer fills, it will be forced to wrap it’s internal circular buffer. If it takes you 1/2 second to call getIQ after configuring you might have discarded that I/Q data.
Ideally, you would be polling the BB60C in another thread and waiting for the trigger to occur or transmitting the VSG60 waveform and trigger in another thread while you poll the BB60C in your main thread. That way there is no worries about any I/Q accumulation buffers overflowing. If your VSG60 waveform is short, that is probably not the case here, but worth noting.
jgauthierParticipant- This reply was modified 3 years, 11 months ago by jgauthier.
Yes – purge is set to true.
The VSG waveform for now is short – it’s the simple CW waveform (iq = complex(1, 0) ) from the MATLAB examples in the SDK. The elapsed time from the start of transmitting the waveform from the VSG to returning from the GetIQData function (calling bbGetIQUnpacked and converting the returned array to complex) is < 0.20 seconds, so I’d think I wouldn’t need a separate thread just yet… But I’ll investigate doing it if I have to.
I’ve made the attached script just about as simple as I can get it. I’ve allowed for up to 4 triggers when grabbing I/Q data.
Attachments:
You must be logged in to view attached files.
AndrewModeratorSo, you would want to set purge to false. Purge tells the API to throw away any accumulated data in the API, which would include your waveform and trigger location. Once you set purge to false, you will also need to poll the API until you see the trigger position.
Regards
- AuthorPosts
You must be logged in to reply to this topic.