How to use FlexLib API IQStream?

  • 1
  • Question
  • Updated 3 years ago
Hello,
I am trying to figure out how to use the IQStream object to get raw IQ data from a Flex radio using the FlexLib API. I need some general guidance on how to use the API, which doesn't seem to have much documentation. I can figure out the specifics. I have a lot of C# experience and I have worked with .NET hardware APIs in the past. Part of my problem is that I do not fundamentally understand the exact functions of many of the objects in the API. I believe I will need to call CreateIQStream on my radio object to get an IQStream object, add a delegate to the IQStream object to handle the DataReady event, and call RequestIQStreamFromRadio on the IQStream object to begin receiving data. A few things confuse me:

1. Why do I have to give CreateIQStream a DAX channel? My understanding is that DAX is a technology for passing data streams by using virtual Windows audio devices. With the FlexLib API we should be avoiding DAX altogether, so what is this? Where do I get that number?
2. How do I set the frequency I am receiving on? My understanding of the radio is that it uses a digital downconversion to take a selected portion of the frequency spectrum down to baseband, and that the hardware responsible for this is an FPGA slice. Is this correct? Then, why is it when I create a slice in the API that I must specify a demodulation mode? All I want is IQ data. The slice, however, does have a frequency setting, so I think I do need to create a slice object. I'm just guessing at this point though.
3. Possibly unrelated to my primary question... What precisely is a panadapter? Is the panadapter just an FFT plot of a portion of the RF spectrum? Does the data used by the FFT come from a slice, or is the panadapter implemented separately from the slices? Do I even have to worry about panadapters if all I want is IQ data?

This is what I have tried: I have tried creating a slice and then creating an IQStream object from the DAX channel that slice purports to use. Nothing crashed but my DataReady callback never ran. I'm guessing it's not the right approach. I'm really not sure what the right approach is. A tiny bit of sample code or little explanation of how this all works, just to show me how to set the frequency and begin receiving IQ data, would be great.

Thanks!
- Kyle Gagner

P.S. My experience comes from being an avid hobby programmer, undergrad electrical engineering student, and intern at Keysight (formerly Agilent) last summer working on vector signal analyzers. I am not a ham radio operator, but my dad is (he bought the radio).
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes

Posted 4 years ago

  • 1
Photo of Jay / NO5J

Jay / NO5J

  • 1577 Posts
  • 228 Reply Likes
Isn't DAX just a stream of audio from one memory location to another? I'm trying to underatand this too.
Jay - NO5J
Photo of Jay / NO5J

Jay / NO5J

  • 1577 Posts
  • 228 Reply Likes
Life is just a hobby!
Just something to keep you busy between birth and death.
I love my hobbies. They save my life sometimes. Time to get to work.
Photo of Jay / NO5J

Jay / NO5J

  • 1577 Posts
  • 228 Reply Likes
Being pre-Unix, but actively programming since learning BASIC, I can usually get a computer to at least do most of what I want, but I've been a little lazy about actually creating anything in C, and beyond. I can read it, debug alot of it, and follow along for the most part, I wrote some Assembly stuff back when people still did that so, I think can figure this out. I didn't get left behind, I just forgot to keep moving ahead.
Photo of KY6LA - Howard

KY6LA - Howard, Elmer

  • 3593 Posts
  • 1446 Reply Likes
Thanks for making an old a machine language and assembler fossil feel that there may be a future.
Photo of Jay / NO5J

Jay / NO5J

  • 1577 Posts
  • 228 Reply Likes
We're not fossils yet, first you have to get buried underground, then theres still a long long time, till you become a fossil. Theres always a future, there only used to be a past, can't go there anymore. Might be someplace else to live.
(Edited)
Photo of Jay / NO5J

Jay / NO5J

  • 1577 Posts
  • 228 Reply Likes
We've been able to sync data streams remotely over a network for something like 4+ decades, kinda obvious now that it occured to my feeble mind. Hopefully someones working on this, Oh yeah, me first!
Last one across the finish lines a rotten egg!
Photo of Eric - KE5DTO

Eric - KE5DTO, Official Rep

  • 718 Posts
  • 211 Reply Likes
These are excellent questions.  Thanks for bringing them to the table.  First, let me define some terms:

1. IQ Stream.  This is a stream coming from the radio in IQ format that is tied to a particular Panadapter through the channel number.

2. Audio Stream.  This is a stream coming from the radio in 24kHz audio that is tied to particular Slice through the channel number.

3. DAX.  Stands for Digital Audio eXchange.  This is used as a bridge for one or more of the above objects to talk to the Windows Audio subsystem.  Note that this is just one application for the above streams, but it happens to be the only one we have officially implemented in an application at this point as it provides the necessary interface to legacy 3rd party programs.

It is unfortunate that we called the channels DAXChannel (and DAXIQChannel) and use "dax=<num> and "daxiq=<num>" in the over-the-wire command to request it.  Nevertheless, the above definitions give a better picture of the system as a whole.

Now that we have the terms down, I think what you're wanting is an IQ Stream without having to deal with the Windows Audio side of things.  This makes complete sense and is where we'd like to see things move.

You are on the right track.  First call the radio.CreateIQStream function and specify which channel. Then subscribe to the PropertyChanged event and call the iqStream.RequestIQStreamFromRadio function waiting for the RadioAck property to go true.  This signifies that the radio has accept the request and sent any necessary configuration data back to the client.

The other end of things is that there needs to be a Panadapter with that same channel selected.  IQStreams go with Panadapters (today) and AudioStreams go with Slices.  This is a convention we may change at some point to give more flexibility to developers.  But today, that is how it works.  In order to "tune" the IQStream, you simply update the CenterFrequency property on the Panadapter.

With the IQStream in place and the Panadapter on the same channel, you should begin receiving DataReady events with new data.  This is exactly how the radio side of DAX works.  

Panadapters and Slices are completely independent objects -- or as independent as we could possibly make them though they sometimes shares FPGA resources.  This is all hidden from view though and can be treated as completely separate in the APIs.  

As such, the only things that are shared in terms of our objects are that Slices always use the RX Antenna of the Panadapter in which they are shown.  Changing the antenna in one place (Slices or Panadapter) will update the others.  Beyond that, there should be no changes to a Panadapter due to a change in a Slice and vice versa.

Let me know if you need more info and thanks again for asking the questions.
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes
This is very clarifying. I will keep working on it this weekend and see where I get with this new information; I think I should be able to make it work now. Thank you for the detailed response.
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes
Hello, I was delayed a bit in getting back to this (research projects, midterms, etc.) but I have some time today so I've been giving it another go. I can get the radio to acknowledge my request for an IQStream, but I am having difficulty with the Panadapter. Does it matter when I request the Panadapter? Currently I am requesting it after the IQStream request is acknowledged, like so:
void sdrstream_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "RadioAck" && sdrstream.RadioAck)
{
Debug.WriteLine("IQ Stream Acknowledged");
panadapter = radio.CreatePanadapter(new System.Windows.Size());
panadapter.PropertyChanged += panadapter_PropertyChanged;
panadapter.DAXIQChannel = sdrstream.DAXIQChannel;
panadapter.RequestPanadapterFromRadio();
}
}

I see two puzzling messages in the debug console before the acknowledgement for the IQStream. I'm going to try to dive into the API and figure out what they mean next, but it might be a red herring.

Radio::ParseStatus: Unparsed status (S79D2CCD0|daxiq 1 pan=0x0 rate=48000 capacity=16 available=16)
IQStream::StatusUpdate: Key not parsed (pan=0x0)
IQ Stream Acknowledged

I wasn't sure what to put for window size as I'm not really using the Panadapter, just the IQStream, so I put nothing. I've also tried 512, 512.
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes
Well, I've got it working now. It's super buggy, so I'll keep working on it and post, some time in the next two weeks, what I've got, for anyone interested in trying to demod IQ data.
Photo of James Whiteway

James Whiteway

  • 905 Posts
  • 222 Reply Likes
Definately interested in seeing your code. IQ data is just one of several things I'm trying to understand and work with.
james
WD5GWY
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes
I'm not sure I know what I'm doing yet, but I'll continue working on this over spring break next week and post my code when I'm more confident that I'm using the API correctly.
Photo of Mark Erbaugh

Mark Erbaugh

  • 395 Posts
  • 34 Reply Likes
Could someone explain the RadioAck property and the sequencing?  I see in Kyle's code above that it waits for a property change callback. Is this the best way to determine that radio has accepted a command?  In my code, I've been sending commands and assuming that the radio accepted them. Are there certain commands that must be ack'ed before proceeding?
Photo of Kyle Gagner

Kyle Gagner

  • 8 Posts
  • 0 Reply Likes
In my observation, it took some amount time for the radio to acknowledge my request for an IQ stream. If it is imperative that your setup sequence occur in a certain order, a race condition can occur if you don't wait for RadioACK, but one way to check that would be to rewrite the sequence out of order and see if it breaks. I don't understand the API well enough yet to say whether things necessarily need to come in some order. I'm back at school for finals week and my dad has all of the project files on his computer at home, so I can't do it myself now, but if I were you, I'd go into the source code for the API and see where RadioACK is set to false. For functions that set RadioACK to false, one of two things is true:
1. The resulting change of state in the radio is not guaranteed immediately after the function is used, so RadioACK must be observed to determine when the radio's state is valid
2. The instruction may be one which the radio is not guaranteed to act on and acknowledge at all, so RadioACK must be observed to determine whether the radio has decided to change state
My first instinct is to go with 1, but after using the API this weekend, I'm not sure whether the sequence of events is critical. It may be that race conditions are not a concern. I've also made requests to the radio (for panadapters) that never went serviced. These things make me think option 2 may be the case. That makes the API harder to use.
One feature of Visual Studio that I have found exceedingly useful is the debugger's watch window. You can see the state of all of the objects in your program change and track what is going wrong as the flow of control passes on events, rather than a linear procedure.
One last observation. I do not think the API is consistent in raising the PropertyChanged event. For example. Radio.Connected changing to true never raised a PropertyChanged on Radio for me.
(Edited)
Photo of Wayne, W5XD

Wayne, W5XD

  • 18 Posts
  • 6 Reply Likes
I too would like to construct an application to read an I/Q stream, but I would prefer not to go through DAX. (My aversion to DAX is simply that I have found the DAX audio channels are buffered about 200msec behind real time in both transmit and receive--I don't want my application to be that far behind and hope--without knowing for sure--that the VITA-49 data for the I/Q streams are not buffered that deeply.) This page says, "Streaming data is sent from the radio in the VITA-49 format from UDP/IP port 4991 to targeted clients." But I haven't found the right incantation to become a "targeted client", presumably to be sent via IP port 4992 via the command API?
Photo of Eric - KE5DTO

Eric - KE5DTO, Official Rep

  • 718 Posts
  • 211 Reply Likes
Today the radio requires a Panadapter in order to run an IQStream.  The command to create the IQ stream over ethernet is "stream create daxiq=<channel> ip=<ip> port=<port>"  Note that is the IP and Port are left off, it will use sensible defaults.

Once that stream has been created and a Panadapter object has set it's daxiq to the same channel, the VITA-49 data should appear at the requested port.  That's the nuts and bolts.
Photo of Wayne, W5XD

Wayne, W5XD

  • 18 Posts
  • 6 Reply Likes
Ah, thank you. I'll try that. Looking back at the wiki, I believe I have an excellent excuse for having failed to find the documentation. There is a broken link here for the stream command.
Photo of Wayne, W5XD

Wayne, W5XD

  • 18 Posts
  • 6 Reply Likes
I got far enough along to try this. I get buffers delivered to my UDP port, but I cannot tell what they are. That is, once my application has send the specified command over an IP connection to port 4992:
stream create daxiq=1 ip=<ip> port=<port>
and once I have used SmartSDR's "DAX" button on one of its Panadapters to set "DAXIQ" channel to  "1", thereafter, I receive buffers at my UDP server at my ip/port. The buffers stop coming if I use SmartSDR to change its Panadapter DAXIQ to anything other than "1". So far, so good.

I get roughly 95 packets per second, and each is of size 4128 bytes. Nothing I do in the "DAX Control Panel" application appears to affect the buffer size or frequency (I am hoping to be able to select the sample rate int the 24K to 192K range. How might I do that?) The only sensible deduction I can make from the received buffers is that their rate is roughly equivalent to 48K samples-per-second times 2 channels times 4 bytes per sample.

My primary reference for the format of the buffers I expected is the source code at hal_listener.c, from the published documentation for the "SmartSDR Waveform API". I am now 99% certain I am not getting VITA-49 data and that instead I have wandered off the trail and am lost in the deep weeds. Can anyone lead me back? It feels like there must be a book or web page somewhere that explains all this, but I have so far failed to find it.

While I am asking... For my current application, I would much rather have the FFT'd I/Q data rather than the time data. Does the radio source FFT'd data? and, if so, what is the appropriate incantation?
Photo of Eric - KE5DTO

Eric - KE5DTO, Official Rep

  • 718 Posts
  • 211 Reply Likes
In a situation like this, I usually fire up Wireshark to look at what the data looks like coming across the wire to see if I can make heads or tails of things.

You can set the sample rate with the command "dax iq set <channel> rate=<new rate>".  The allowed settings are those shown in the DAX application (24, 49, 96, 192kHz).

For FFT data, you should probably look at the Panadapter options.  We actually render the FFT for the display size.  In the ethernet API, look at the display command.  In FlexLib, you can take a look at the class in Panadapter.cs.
Photo of Wayne, W5XD

Wayne, W5XD

  • 18 Posts
  • 6 Reply Likes
Eric, your help is appreciated yet again. Thank you. I was not quite as far off in the deep weeds as I had feared. I didn't use wireshark--looking at the received buffers in the debugger was good enough--and figured out that I had simply confused myself between the network-byte-reversal and the fact that I was mislead by a google search showing how Vita-49 fit into a single ethernet frame, so I got confused by a 4K byte packet. All makes sense now.

I am now fishing around looking for how to set the panadapter's center frequency from the ethernet command processor. I see how to do it via the .NET API, and I can make do with a mixed mode client for now (mixing .NET calls with ethernet command strings and VITA-49) but I would like to move my code that deals with the I/Q channel to work purely through the ethernet API.

Editing after submitting: didn't read the doc's carefully enough before my previous post. I see the "display" command and how to set the panadapter via the ethernet command API.
(Edited)
Photo of Eric - KE5DTO

Eric - KE5DTO, Official Rep

  • 718 Posts
  • 211 Reply Likes
Note that you can also grab the FlexLib source and just reference the .NET class and look at the string it uses to send the commands since it is all built on top of the ethernet API.

For others wondering, to tune a panadapter, use the "display pan set <Stream ID> center=<MHz>" command.