Low level API

The functions provided by UHDBindings covers two aspects: the high level API (with Julia calls and structures) and the low level API, which consists of the C functions provided by UHD. The bindings, generated by Clang.jl are not directly exported by UHDBindings but the functions can be used with

    # High level call 
    using UHDBindings
    radio = openUHD(700e6,4e6,50;nbAntennaRx=1,nbAntennaTx=1)
    # Low level call
    import UHDBindings.LibUHD as LibUHD
    LibUHD.uhd_usrp_....()

In this case, it is necessary to align the parameters with the one requested by UHD. Thus

  • To the get output parameters, it is necessary to define beforehand a Reference with the appropriate type and then deference the ref to get the value
  • uhd functions often request parameters defined in the top structure layer of UHDBindings.
    • h which is an uhd_usrp_handle is defined in radio.rx.uhd.pointerUSRP or radio.tx.uhd.pointerUSRP
    • Streamer operations require uhd_rx_streamer_handle, which is in our structure radio.rx.uhd.pointerStreamer
    • Metadata operation uses uhd_rx_metadata_handle (present by deferencing radio.rx.uhd.addressMD[]) and uhd_tx_metadata_handle (present by deferencing radio.tx.uhd.addressMD[])
    • chan corresponds to the index of the used channel. Be default (in SISO) it is 0
    • The uhd_tune_request calls are a little tricky and a Ref of the aligned Julia structure uhd_tune_requestshould be used. An example is given in the Julia function updateCarrierFreq!

For instance, to manually get the Rx gain and set up the gain without using updateGain!

    # High level call 
    using UHDBindings
    radio = openUHD(700e6,4e6,50;nbAntennaRx=1,nbAntennaTx=1)
    # Low level call
    import UHDBindings.LibUHD as LibUHD
    res = Ref{Cdouble}(0) # Define a ref to store the result, should be aligned with type in C UHD function. 
    LibUHD.uhd_usrp_get_rx_gain(radio.rx.uhd.pointerUSRP,0,"",res)
    println("The gain is $(res[])") # using res[] to deference the ref and get the value of the gain

This low level calls can, be useful (and necessary) to define specific radio configurations and handle scenarios not supported by UHDBindings. This is for example the case of the following example that redefines all the layer to support MIMO. Note that this can be done by initiating openUHD with nbAntennaRx=2.

# ----------------------------------------------------
# --- Load packages
# ---------------------------------------------------- 
using UHDBindings 
import UHDBindings.LibUHD as LibUHD
import UHDBindings.initRxUHD
import UHDBindings.@assert_uhd

# ----------------------------------------------------
# --- Parameters 
# ---------------------------------------------------- 
carrierFreq = 868e6
samplingRate= 2e6
gain        = 25
uhdArgs     = ""
nbAntennaRx = 2

# ---------------------------------------------------- 
# --- Create radio 
# ---------------------------------------------------- 
# --- Open radio, in MIMO mode 
# Note the warning about the streamer. It can not be set in MIMO mode 
# The configuration will be updated later, and most of the parameters overwritten
# To ensure we have what we want, we need to bypass the streamer, and deactivate all antennas
radio = openUHD(carrierFreq,samplingRate,gain;nbAntennaRx=0,nbAntennaTx=0,bypassStreamer = true)

# ----------------------------------------------------
# --- Set up Radio 
# ---------------------------------------------------- 
# --- Channels  & Antenna 
channelIndexes = [0,1]
antennas       = ["TX/RX","RX2"]
# --- Streamer arguments
a1			   =  Base.unsafe_convert(Cstring,"fc32");
a2			   =  Base.unsafe_convert(Cstring,"sc16");
a3			   =  Base.unsafe_convert(Cstring,uhdArgs);
channel        = pointer(channelIndexes)
uhdArgs_0	   = LibUHD.uhd_stream_args_t(a1,a2,a3,channel,nbAntennaRx);
# --- RF Configuration 
for (c,currChan) in enumerate(channelIndexes[1:nbAntennaRx])
    # Set the carrier frequencies 
    updateCarrierFreq!(radio.rx,carrierFreq,currChan)
    # Set the sampling rate 
    updateSamplingRate!(radio.rx,samplingRate,currChan)
    # Set the gains 
    updateGain!(radio.rx,gain,currChan)
    # Set the antennas
    LibUHD.uhd_usrp_set_rx_antenna(radio.rx.uhd.pointerUSRP,antennas[c],currChan)
end
# --- Subdev with 2 boards
pointerSubDev = Ref{LibUHD.uhd_subdev_spec_handle}()
@assert_uhd LibUHD.uhd_subdev_spec_make(pointerSubDev,"A:0 A:1")
@assert_uhd LibUHD.uhd_usrp_set_rx_subdev_spec(radio.rx.uhd.pointerUSRP,pointerSubDev[],0)
LibUHD.uhd_subdev_spec_free(pointerSubDev)
# --- Internal streamer and buffer config
pointerArgs	  = Ref{LibUHD.uhd_stream_args_t}(uhdArgs_0);
pointerSamples = Ref{Csize_t}(0);
@assert_uhd LibUHD.uhd_usrp_get_rx_stream(radio.rx.uhd.pointerUSRP,pointerArgs,radio.rx.uhd.pointerStreamer)
@assert_uhd LibUHD.uhd_rx_streamer_max_num_samps(radio.rx.uhd.pointerStreamer,pointerSamples)
println("Internal buffer size is $(pointerSamples[])")
radio.rx.packetSize= pointerSamples[]
# --- Clock source 
@assert_uhd LibUHD.uhd_usrp_set_clock_source(radio.rx.uhd.pointerUSRP,"internal",0)
@assert_uhd LibUHD.uhd_usrp_set_time_now(radio.rx.uhd.pointerUSRP,0,0,0)

# ----------------------------------------------------
# --- Set up streamer 
# ---------------------------------------------------- 
streamCmd	= UHDBindings.uhd_stream_cmd_t(UHDBindings.UHD_STREAM_MODE_NUM_SAMPS_AND_MORE,radio.rx.packetSize*10_000,false,1,0.5);
pointerCmd	= Ref{UHDBindings.uhd_stream_cmd_t}(streamCmd);
LibUHD.uhd_rx_streamer_issue_stream_cmd(radio.rx.uhd.pointerStreamer,pointerCmd)
sleep(0.1)
# ---------------------------------------------------- 
# --- Julia buffers 
# ---------------------------------------------------- 	
# Define an array to get all the buffers from all the channels 
nbSamples             = getBufferSize(radio)
sig                   = [zeros(Complex{Cfloat},radio.rx.packetSize) for n ∈ 1:nbAntennaRx]
listBuffer            = [pointer(sig[n],1) for n ∈ 1 : nbAntennaRx]
ptr                   = listBuffer
pointerCounterSamples = Ref{Csize_t}(0);

# ----------------------------------------------------
# --- Receive data 
# ---------------------------------------------------- 
pointerCounterSamples = Ref{Csize_t}(0);
LibUHD.uhd_rx_streamer_recv(radio.rx.uhd.pointerStreamer,ptr,nbSamples,radio.rx.uhd.addressMD,1.6,true,pointerCounterSamples)
nbReceivedSamples = pointerCounterSamples[]
println("Receive $nbReceivedSamples samples")

# ----------------------------------------------------
# --- Second call 
# ---------------------------------------------------- 
accum = 0
for iN = 1 : 1 : 10_000
    pointerCounterSamples = Ref{Csize_t}(0);
    LibUHD.uhd_rx_streamer_recv(radio.rx.uhd.pointerStreamer,ptr,nbSamples,radio.rx.uhd.addressMD,0,true,pointerCounterSamples)
    nbReceivedSamples = pointerCounterSamples[]
    global accum += nbReceivedSamples
end
println("End of transmission, received $accum samples")
# ----------------------------------------------------
# --- Close 
# ---------------------------------------------------- 
close(radio)