wiki:HardwareUsersGuides/RadioBoard_v1.4/Usage/TxDCOffset

Version 14 (modified by murphpo, 16 years ago) (diff)

--

WARP Radio Board TX DC Offset Calibration

The radio transceiver on the WARP Radio Board uses a direct conversion architecture. One side-effect of this is a sensitivity to DC offsets in the transmitted signal. Any DC component in the transmit waveform results in carrier leakage at RF. In addition to any DC component in the user's transmitted waveform, the radio board itself introduces a small DC offset due to variations in component values and board assembly.

To avoid these problems, users should be careful to transmit waveforms with zero DC component, and should calibrate their radios. Follow the steps below to calibrate a WARP Radio Board.

Calibrating a Radio

The residual DC offset due to component variations can be estimated using a special calibration mode of the radio board. During calibration, the algorithm estimates DC offsets for the I and Q channels on a given radio board. These values are then stored to the radio board's EEPROM. Once stored, the DC calibration values can be read by user applications each time the board is used.

Radio Calibration

Every WARP Radio is calibrated before shipment. You can re-calibrate your radios if desired, using this procedure.

The Tx DCO calibration algorithm is not publically available. However, we do distribute an FPGA configuration file which executes the calibration process automatically.

  1. Download a copy of this configuraiton file: TxDCO_Calibrate.bit
  2. Use the DIP switch on the WARP FPGA Board to select which radios should be calibrated. Each switch position corresponds to a radio. The LSB (Radio #1) is the bottom switch; MSB (Radio #4) is the top.
  3. Connect a serial cable to the FPGA board and set your terminal emulator to 57600bps
  4. Power on the FPGA board and wait for a few minutes; this allows components to warm up
  5. Configure the FPGA using TxDCO_Calibrate.bit
  6. The terminal will display the status as it runs. Wait for the calibration of each radio to complete.

Here's what the terminal output should look like. This example calibrated just radio #2.

*************Tx DC Offset Calibration*************

Calibrating radios:
Radio #1: No
Radio #2: Yes
Radio #3: No
Radio #4: No

Radio #2 calibration starting...done

Applying best DC Offsets...


Radio #2: Best I: -44, Best Q: 100
  EEPROM Successfully Updated with DC Offset Values.
  EEPROM Readback: I: -44 Q: 102

Now accepting manual input
 Type [1,2,3,4] to select a radio
 Type [a,q,s,w] for I-/I+/Q-/Q+ DC offset adjustment
 Type [e,d] = to enable/disable the transmitter

You can repeat this process whenever needed. In our experience, calibration values are fairly static over time but do vary with temperature.

Using Calibration Values

User applications should use the stored Tx DCO calibration values on power-up. The code below is an example of how this is done. This function is portable and should compile when integrated with a user application in XPS.

//Assumes your hardware platform contains:
// radio_controller_v1_08_a
// EEPROM_v1_00_a or EEPROM_v1_01_a

//Before calling this function, your application must initialize
// the radio controller using the WarpRadio_v1_Reset() function

//Your code must also include:
// #include radio_prototypes.h
// #include EEPROM.h

void CalibrateTxDCO () {
    unsigned char i;
    unsigned int thisRadio;
    int eepromStatus = 0;
    short calReadback = 0;
    signed short best_I, best_Q;
    
    //Select which radios should be used
    // Include RADION_ADDR to enable a radio, 0 to disable it
    // This array must contain 4 elements corresponding to the 4 radios
    // Use a value of RADION_ADDR (for N=[1,2,3,4]) to enable a radio
    // Use 0 to disable a radio
    //  For example, these arrys are valid:
    //   {RADIO1_ADDR, RADIO2_ADDR, RADIO3_ADDR, RADIO4_ADDR} //Calibrate all four radios
    //   {RADIO1_ADDR, 0, 0, RADIO4_ADDR} //Calibrate radios #1 and #4
    //  But these are not:
    //   {RADIO2_ADDR, RADIO1_ADDR, 0, 0} //Out of order
    //   {RADIO1_ADDR} //Not enough elements

    unsigned int selectedRadios[4] = {RADIO1_ADDR, RADIO2_ADDR, 0, 0};

    for(i=0; i<4; i++)
    {
        thisRadio = selectedRadios[i];

        //Only read values for enabled radios
        if(thisRadio > 0)
        {
            //Select the EEPROM on the current radio board
            eepromStatus = WarpEEPROM_EEPROMSelect((unsigned int*)XPAR_EEPROM_0_BASEADDR, i+1);

            //Initialize the EEPROM controller
            eepromStatus = WarpEEPROM_Initialize((unsigned int*)XPAR_EEPROM_0_BASEADDR);

            //Read the Tx DCO values
            calReadback = WarpEEPROM_ReadRadioCal((unsigned int*)XPAR_EEPROM_0_BASEADDR, 2, 0);
    
            //Scale the stored values
            best_I = (signed short)(((signed char)(calReadback & 0xFF))<<1);
            best_Q = (signed short)(((signed char)((calReadback>>8) & 0xFF))<<1);
    
            //Optional debug output; useful to check whether the values are valid
            xil_printf("\r\nRadio #%d TxDCO - I: %d Q: %d\r\n", i+1, best_I, best_Q);
    
            //Finally, write the Tx DCO values to the DAC
            WarpRadio_v1_DACOffsetAdj(ICHAN, best_I, thisRadio);
            WarpRadio_v1_DACOffsetAdj(QCHAN, best_Q, thisRadio);
        }
    }

    return;
}