An example IQ Streaming client for use with streaming devices, like the MSR4.
- system with SR-IOV support
- ixgbe driver
As of writing this, only Intel® Ethernet Controller X520 and Intel® Ethernet Controller X710 have been tested and used. Though it should work with other NICs aswell provided the system is set up correctly according to the RS Jerry Setup Repo
The following versions are required to build and run:
Name | Version | Purpose | Get It |
---|---|---|---|
C++ | >17 | Compiling | --- |
CMake | >3.14 | Building | sudo apt-get install cmake |
gRPC | 1.44.0 | Configure MSR4 | Quickstart |
jsoncpp | 1.7.4 | Alternative to configure MSR4 | sudo apt-get install libjsoncpp-dev |
DPDK | 22.11* | Speed | Download + Building |
pkg-config | 0.29.1 | Finding DPDK | sudo apt-get install pkg-config |
gTest (optional) | 1.10.0 | Running UnitTests | sudo apt-get install libgtest-dev |
* Please note that you may require a different version of DPDK to match your network card driver. See also this example document for more information in case you use a network card from Intel. Also, please make sure that you work with the most recent firmware available for your NIC.
Transitive requirements will be reported by CMake and should be installed accordingly.
The unit tests are using the GoogleTest Framework.
To enable/disable building the unit tests, switch the option BUILD_TESTS
to ON/OFF in the CMakeLists.txt.
This project requires the system to be set up correctly according to the RS Jerry Setup Repo.
Before building, adapt the path to the GRPC installation to your local build path in CMakeLists.txt, for example:
set(LOCAL_GRPC_PATH /home/GnuRadio/local)
Then, create a build directory and start the installation.
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install
This installs the library libjerryDriver.a
into /usr/local/lib
as well as the headers into /usr/local/include/
.
After the installation you can #include <iqClient/iqClient.h>
in your project and use its contents.
The common approach is to instantiate a new empty iqClient and modifying it afterwards to your needs.
Example Code:
std::unique_ptr<IqClient> iqclient = std::make_unique<IqClient>();
Using gRPC requires you to first log into the MSR4 with your given credentials.
Example Code: Login
iqClient->SetMSR4Ip("some.device.net");
iqClient->SetMSR4Credentials("user", "password");
iqClient->MSR4Login();
Setting single values in the MSR4 is as simple as calling the corresponding function of the iqClient
.
The return value of Set-Functions is usually an ErrorMessage. See the errors proto file for further information.
Example Code: gRPC and Errors
iqClient->SetDestinationAddress("127.0.0.1");
RsIcpxGrpcService::ErrorMessage err = iqClient->SetSatFrequencyHz(1500000000);
if(err.errorcode() != 0)
throw InvalidValueError(err.errormessage());
Another approach to configure the MSR4 is to specify settings in a .json
file and loading it. Further details on .json
can be found in the configFiles/README.md
as well as an example.json
and schema.json
.
Example Code: JSON
iqClient->SetMSR4ByJson("my/awesome/path/to/example.json");
The created VFs from the RS Jerry Setup Repo are enumerated in order of creation starting at 0. The VF to listen to needs to be specified. The incoming samples can be divided by a norm. The norm defaults to 1.
Example Code: Specify DPDK Settings
iqClient->SetPortID(0);
iqClient->SetNorm(10);
After having everything set up correctly, start DPDK and the MSR4 IQ-streamer.
Note: MSR4 settings can only be changed while the IQ-streamer is not streaming, so make sure to call iqClient->SetStreamingStatus(false);
beforehand.
Example Code: Starting
iqClient->SetupDpdkSource();
iqClient->SetStreamingStatus(true);
Now calling int GetSamples(int number_of_samples, std::complex<float> *samples)
retrievs a maximum of number_of_samples
items into samples
, and returns the actual number of items succesfully stored in samples
.
int number_of_samples = 10;
std::complex<float> *output = (std::complex<float> *) malloc(sizeof(std::complex<float>) * number_of_samples);
int nsamples = 0;
nsamples = iqClient->GetSamples(number_of_samples, output);
GetSamples(...)
can be and probably should be called in an endless loop to keep the samples coming.
After you are done, you should call
iqClient->TeardownDpdkSource();
to free the memory.
Please note:
Due to how dpdk works, SetupDpdkSource()
(which calls rte_eal_init()
) can only be called once per process, even after TeardownDpdkSource()
since calling rte_eal_cleanup()
internally does not shutdown dpdk "cleanly".
Further information on this: Issue#263.
In essence:
[ rte_eal_cleanup() ] was added as it was required to release hugepage memory from secondary processes when shutting down. In the long term, it is intended to provide a "clean" shutdown of all of DPDK resources, however it is not that today. In current code, rte_eal_init() can still only be called once per process.
You can find more examples on how to use the iqClient either
- in the unit tests directly or
- in a little more elaborated example using gnuradio as receiver in the rs-jerry-gnuradio repo
Problem: The MSR4 sends HRZR packets to my device and the packets are visible in watch -n 0.5 -d 'ethtool -S <myDevice> | grep -v ": 0"'
but I do not get any samples by calling GetSamples(...)
.
Solution: This usually means the packets are not send to the VF. When using the Intel® Ethernet Controller X710 make sure the latest compatible i40e Driver is installed for your system.
Byte | 0 | 1 | 2 | 3 | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Content | Control | Sequence number | Total packet length (including header) | |||||||||||||||||||||||||||||
Receiver Address | ||||||||||||||||||||||||||||||||
Data |
Bit 0 | Bit 1 | Bit 2 | Bit 3 | Packet type | |||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | x | 0 | Data | |||||||||||||||||||||||||||||||||||
0 | 1 | x | 1 | Metadata |
Transmission in little-endian format, i.e. the least significant byte first. A sample consists of I and Q, each with 16 bits per component, i.e. a total of 32 bits per sample. I always comes first, then Q.
Byte | 0 | 1 | 2 | 3 | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Content | I | Q | ||||||||||||||||||||||||||||||
I | Q | |||||||||||||||||||||||||||||||
... | ... |
Metadata is sent once per second.
Byte | 0 | 1 | 2 | 3 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |||||||||||||||||||||||||||||||
Content | Version | Time sync source | Clock sync source | Zero Padding | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Zero Padding | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unix timestamp in nanosecond-precision (MSBs) * | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unix timestamp in nanosecond-precision (LSBs) * | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sample frequency | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Center frequency (max 4.2 GHz) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Preamp gain float (IEEE 754) ** | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ISO 6709 latitude*** in degree float (IEEE 754) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ISO 6709 longitude*** in degree float (IEEE 754) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ISO 6709 elevation*** in meter float (IEEE 754) |
* unix timestap in nanoseconds since 01.01.1970, 0:00
** defines the total 200MHz channel power correction value in dB
*** the default value or no GPS fix is available: -256.0
Current: 0000
How we get the timestamps:
If GPS or Network is selected but not synchronized (no GPS fix, no connection to NTP Master), internal RTC Time should be transmitted as timestamp and bit 3 should be zero.
Bit 0 | Bit 1 | Bit 2 | Bit 3 - SYNCED | Packet type | |||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | x | 0 | No time sync | |||||||||||||||||||||||||||||||||||
1 | 0 | x | 0/1 | GPS | |||||||||||||||||||||||||||||||||||
0 | 1 | x | 0/1 | Network | |||||||||||||||||||||||||||||||||||
1 | 1 | x | x | Unknown |
How we synchronize:
Bit 0 | Bit 1 | Packet type | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | Internal clock | |||||||||||||||||||||
1 | 0 | External clock | |||||||||||||||||||||
0 | 1 | Unknown | |||||||||||||||||||||
1 | 1 | Unknown |