Open RF Prototyping

An HMC833 Frequency Synthesizer

Features

  • Fractional-N and integer modes.

  • 24-bit step size: 3 Hz resolution typically.

  • Exact frequency mode.

  • Wide bandwidth: 25 to 6000 MHz.

  • Good phase noise performance: -120 dBc/Hz at 10 kHz offset (1 GHz carrier, on-board reference, 150 kHz LBW).

  • Dual outputs with flexible output amplifier configuration.

  • Design files are available at https://gitlab.com/dyadic-groups/rfblocks-hardware-modules/wideband_plo_2 (licensed under CERN-OHL-S v2).

Typical Performance Characteristics

Phase noise

Phase noise measurements shown in this section where made using an HP 11729C carrier noise test set with a Rohde & Schwarz SMHU signal generator as the reference. A full description of the measurement procedure is given here: Phase Noise Measurement with the HP11729C.

HMC833 PN

Figure 1: Measured phase noise at 1, 1.5, 2, 2.5 and 3 GHz with a loop bandwidth of 150 kHz.

PN vs LBW

Figure 2: Measured phase noise at 1 GHz for loop bandwidths of 5, 35, 75, and 150 kHz. See Table 2 for loop filter component values for each of the plotted bandwidths.

PN vs Carrier

Figure 3: Measured phase noise as a function of carrier frequency at offset of 100Hz, 1kHz, 10kHz, 100kHz, 1MHz and 10MHz. PLO loop bandwidth is 150kHz.

Output power

The HMC833 VCO has a fundamental frequency range of 1500 to 3000 MHz. For frequencies below 1500 MHz a divider is used and for frequencies between 3000 and 6000 MHz a frequency doubler stage is used.

For frequencies below 1500 MHz a divider stage amplifier with a gain of approximately 3 dB may be used to compensate for losses in the divider. An output buffer amplifier may also be used across all frequencies and has four gain settings of 0, 3, 6 and 9 dB.

Figures 4a and 4b show the effect of the output buffer amplifier gain settings on module output power. For the measurements shown in Figure 4a the module is configured with the resistive splitter (R28, R29, R30) but no output amplifiers. For the measurements shown in Figure 4b the module is configured with the resistive splitter and SKY65017 output amplifiers. In both cases the divider stage amplifier is enabled to compensate for divider losses at frequencies less that 1500 MHz. (See Hardware Configurations for a description of the different module harware configurations.)

Raw output power

Figure 4a: Module output power. The integrated output power measured at the four available output buffer gain levels.

Raw output power

Figure 4b: Module output power with SKY65017 Amplifier. The integrated output power measured at the four available output buffer gain levels. An SKY65017 amplifier is used on each of the board outputs.

Equalized power

Figure 5a: Module output power with HMC833 buffer gain adjusted to reduce output power variation (no RF output amplifier).

Equalized power

Figure 5b: Module output power with HMC833 buffer gain adjusted to reduce output power variation (SKY65017 RF output amplifier).

Module Board Connector Configuration

Table 1. Board pin configuration and function descriptions

Board Pin

Type

Description

LD_SDO

Output

Lock Detect, or Serial Data,

or General Purpose (CMOS) Logic Output (GPO)

SCK

Input

Serial control data clock

SDI

Input

Serial control data

SEN

Input

Serial port enable signal

REF

Input

Synthesizer reference select signal

10V

Power Input

Main 10V DC power input

5-10V

Power Input

Output amplifier DC power input

Gnd

Power Input

Module ground

Ref. In

Signal Input

Female MMCX connector for external synthesizer

reference signal input.

Vtune In

Signal Input

Female MMCX connector for optional input of

synthesizer VCO tuning voltage.

A RF Out

Signal Output

Synthesizer RF output, channel A

B RF Out

Signal Output

Synthesizer RF output, channel B

Conn. layout

Figure 6: Synthesizer module connector layout.

Hardware Configurations

A number of board modifications are possible in order to change the hardware configuration. These are:

  • A choice of two module board variants. One makes use of SOT-89 case style output amplifiers, the other makes use of the DFN-6 case style.

  • A resistive splitter on the HMC833 RF output. This allows either a single or dual RF power output for the board.

  • Provision for Minicircuits LTCC ceramic filters in the RF output paths. If no filtering is required a 1206 style 0 ohm link or Minicircuits TPCN series LTCC thru-line can be used.

  • For the SOT-89 module board variant the output amplifer(s) may be replaced with 1206 style 0 ohm links for unamplified RF power output.

  • Optionally, the HMC833 Vtune signal may be taken from an external source by populating R24 with a 0 ohm link and ensuring that R9, C19 and C20 are left unpopulated.

SOT-89 output amplifier board variant

SOT-89 layout

Figure 7a: SOT-89 board variant RF output path layout.

SOT-89 design

Figure 7b: SOT-89 output amplifier design.

For broadband use the SKY65017 performs reasonably well with relatively flat gain of about 18 dB out to 5GHz and a OIP1 of 20 dBm (typical). See SKY65017 broadband gain block for S-parameter measurements of the standalone gain block.

For a more modest gain of about 15 dB and OIP1 of 18 dBm (typical) the SKY65014 offers better return loss characteristics. See SKY65014 general purpose gain block for S-parameter measurements of the standalone gain block.

DFN-6 output amplifier board variant

DFN-6 layout

Figure 8a: DFN-6 board variant RF output path layout.

DFN-6 design

Figure 8b: DFN-6 output amplifier design.

Applications Information

Figure 9 illustrates the minimal setup for controlling the synthesizer board. The board is connected to Pico board controller which is running the ECApp embedded control firmware. Synthesizer board pins are described in Table 1 with Figure 6 showing the layout of the board pins and RF connectors on the rear of the board.

Minimal synth.

Figure 9: Minimal synthesizer board control

The rfblocks Python package provides the HMC833Controller class which is used to control the board. The following code snippet shows a small example:

>>> from rfblocks import hmc833, HMC833Controller, create_serial

>>> ser_device = '/dev/tty.usbmodem14201'
>>> ser = create_serial(ser_device)

>>> plo = hmc833(sen='G17', ld_sdo='G16')

>>> ctl = HMC833Controller('plo1', plo)
>>> lock = ctl.initialize(ser)
>>> lock
True

>>> ctl.freq = 1500
>>> lock = ctl.configure_freq(ser)

>>> ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN
>>> ctl.configure_gains(ser)
>>> lock = ctl.check_plo_lock(ser)
>>> lock
True

>>> ctl.vco_mute = True
>>> ctl.configure_vco_mute(ser)

>>> ctl.vco_mute = False
>>> ctl.configure_vco_mute(ser)

The code does the following:

  1. Import the rfblocks HMC833Controller and hmc833 classes.

  2. Open the USB/RS-232 serial device which connects the host computer to the ECApp control firmware running on the controller board.

  3. Create an instance of the hmc833 device class. The synthesizer board's serial enable line (sen) is connected to the Pico controller board's G17 GPIO (pin 22). The board's lock status pin is connected to the G16 GPIO (pin 21). The board's REF pin is left unconnected. Since the REF pin is pulled high on the board the internal 50MHz reference is used.

  4. Create an instance of HMC833Controller and initialize the synthesizer. Initialization will set the synthesizer output signal frequency to HMC833Controller.DEFAULT_PLO_FREQ and the output gain to hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB. The status of the PLL lock is returned.

  5. The synthesizer output signal frequency is set to 1500 MHz.

  6. The synthesizer output gain is set to maximum. For output frequencies less than 1500 MHz the synthesizer output divider is used and extra gain of approximately 3dB is available by setting HMC833Controller.divider_gain to hmc833.DividerGain.MAXGAIN. However, this increases the non-linearity of the output signal chain.

  7. Lastly, the RF output level is muted and then re-enabled.

Reference source

The synthesizer is switchable between the on board TXO based reference and an external reference. Switching is done via U4 which is controlled by the Ref module pin.

Ref. layout

Figure 10a: Synthesizer reference switching layout.

Ref. design

Figure 10b: Synthesizer reference switching design

The Ref module pin will be connected to a controller pin and this controller pin used when configuring the PLO device using the rfblocks.hmc833 class. The code snippet below illustrates the use of the switchable reference source and configuring different reference frequencies.

>>> from rfblocks import hmc833, HMC833Controller, create_serial, write_cmd
>>> ser_dev = '/dev/cu.usbmodem14201'
>>> ser = create_serial(ser_dev)
>>> plo = hmc833(sen='D0', ld_sdo='C4', ref='D1', fref=100.0, refdiv=1, refsrc=hmc833.ReferenceSource.EXTERNAL)
>>> ctl = HMC833Controller('plo1', plo)
>>> ctl.initialize(ser)
True
>>> ctl.freq = 500
>>> ctl.configure_freq(ser)
True
>>> ctl.refsrc = hmc833.ReferenceSource.INTERNAL
>>> ctl.ref_freq = 50.0
>>> ctl.ref_div = 1
>>> ctl.configure_refsrc(ser)
>>> ctl.refsrc = hmc833.ReferenceSource.EXTERNAL
>>> ctl.ref_freq = 160.0
>>> ctl.ref_div = 2
>>> ctl.configure_refsrc(ser)
>>> ctl.ref_freq = 70.0
>>> ctl.ref_div = 1
>>> ctl.configure_refsrc(ser)

The Low Spurious RF Signal Generator reference design uses an rfblocks application which makes use of a second HMC833 synthesizer as an external reference source. The frequency of this reference source is varied with changes in the output frequency of the signal generator so as to minimize spurs.

Phase detector frequency

In most applications the PLO phase detector frequency, \(f_{PD}\), remains constant. However, in some applications \(f_{PD}\) may vary as the PLO is tuned over it's band of operation. For example this would be the case where the PLO input reference frequency is varied in order to minimize the generation of spurious signals.

Variations in \(f_{PD}\) may cause non-linearities in the charge pump and phase detector. When operating the PLO in fractional mode these non-linearities will degrade performance. The following description is taken from the HMC832 data sheet.

In fractional mode, charge pump linearity is of paramount importance. Any nonlinearity degrades phase noise and spurious performance. These nonlinearities are eliminated by operating the phase detector with an average phase offset, either positive or negative (either the reference or the VCO edge always leads, that is, arrives first at the phase detector).

A programmable charge pump offset current source adds dc current to the loop filter and creates the desired phase offset. Positive current causes the VCO to lead, whereas negative current causes the reference to lead.

The charge pump offset is controlled via HMC833 register 0x09. Increasing the offset current causes the phase offset to scale from 0 to 360 degrees. The specific level of charge pump offset current (HMC833 register 0x09, bits[20:14]) is calculated using the equation below and shown in Figure 10.

\begin{equation*} \textrm{Required offset} = \min ((4.3\times 10^{-9}\times f_{PD}\times I_{CP}), 0.25\times I_{CP}) \end{equation*}
where: \(f_{PD}\) is the comparison frequency of the phase detector (in

Hz), \(I_{CP}\) is the full scale setting (in A) of the charge pump (set in HMC833 register 0x09[6:0] and register 0x09[13:7]).

Note that the required charge pump offset current must not exceed 25% of the programmed charge pump current. It is recommended to enable the down offset and disable the up offset by writing HMC833 register 0x09[22:21] = 10b.

HMC833 charge pump offset

Figure 10: Recommended Charge Pump Offset Current vs. Phase Detector Frequency for Typical Charge Pump Gain Currents (taken from the HMC832 data sheet)

Reference Designs

Design Notes

The board components are the synthesizer core, loop filter, PLL reference, synthesizer signal output, power supply regulation and control connections. The guiding design principle is to keep noise on the VCO tune line and power supplies to an absolute minimum. Placement of and routing between board components is critical in achieving this.

Block layout

Figure 9: Block level board layout.

Power supply regulators are kept as far from RF signal paths as is practical. In addition, separate power supply regulators are used for the synthesizer VCO supply, the on-board reference oscillator supply, the charge pump supply and the general 3.3V power supply.

Signal output from the HMC833 synthesizer core is split using a 6dB resistive splitter (R28, R29, R30). The resulting dual signal path then passes through amplifiers U2 and U3. These are placed as far as possible from the power supply regulators and loop filter signal path.

Loop filter

The PLL charge pump output is from pin 4 of IC1. This signal is denoted as Vtune in Figure 10. In normal operation, Vtune passes through the PLL loop filter which consists of C16, R10, C24, R8, C17, C18, R9, C19 and C20 and delivered to pin 23 of IC1. This configuration allows for the use of passive filters up to 4 poles (5th order loop filter). Table 2 lists some example loop filter configurations for the HMC833. The component values shown in Figure 10 correspond to the 3rd entry in Table 2 with a loop bandwidth of 155 kHz. The other entries in the table are taken from the HMC833 data sheet (p9, Loop Filter Configuration Table).

Block layout

Figure 11: Loop Filter Component Layout

The primary factor determining the layout of the loop filter components is to reduce the possibility of spurious signals coupling to the Vtune signal path. To assist with this the final section of the Vtune signal path (shown in dark green in Figure 10) is routed via an internal copper layer on the board which is sandwiched between two ground planes. This will reduce the possibility of output RF signal energy being coupled to Vtune. In addition to this, the loop filter components are specified as 0805 size components to ease the task of modifying the loop filter configuration.

To expand the utility of the board and to make the HMC833 available for use as a 'discrete' VCO, the Vtune signal can be driven from external sources via the VTune In board connector by populating R24 and removing R9.

Table 2: Example Loop Filter Configurations

Loop Filter BW

C16

C24

C18

R10 (Ohm)

R8 (Ohm)

R9 (Ohm)

5 kHz

33n

1.0u

3.3n

59

3300

0

35 kHz

2.2n

22n

1.0n

470

1000

0

75 kHz

560p

5.6n

100p

1000

2000

0

100 kHz

270p

3.3n

82p

1500

2700

0

150 kHz

100p

3.3n

10p

1430

10000

0

Synthesizer reference

The on-board reference for the synthesizer is a reasonably good quality temperature compensated crystal oscillator. The measured phase noise for this reference is shown in Figure 12. The overall phase noise of the synthesizer is quite good with this reference at -120 dBc/Hz at 10 kHz offset for a carrier frequency of 1 GHz. The synthesizer phase noise is sensitive to the reference phase noise. Using a higher quality external reference could improve the synthesizer phase noise by 5 dB.

Ref PN

Figure 12: On-board Reference Phase Noise.

For the purposes of modelling the characteristics of the PLO design using Analog Devices ADIsimPLL PLL synthesizer design software, the phase noise model shown below in Table 3 is used.

Table 3: ECS-TXO-3225MV-50 Phase Noise Model

Freq. (Hz)

PN (dBc/Hz)

100

-100

500

-118

1.00k

-126

5.00k

-140

10.0k

-143

50.0k

-148

100k

-150

500k

-150

1.00M

-155

5.00M

-160

Signal output

Synthesizer signal output is generally via the resistive divider R28, R29, and R30. However, if only a single RF output is desired then R28 and R30 should be replaced by \(0\Omega\) links and R29 left unpopulated. The signal path is then through output amplifiers U2, U3. Three variants of the synthesizer board allow for the use of amplifiers in SOT-86, SOT-89, or DFN6 packages.

Power for amplifiers may be taken either from the primary 10V board supply (via R2) or from the separate amplifier power input via J1 and R1. Together with R11, R12, R17, and R18 this allows for some flexibility in limiting power dissipation on the board while providing the correct amplifier voltage and current supply.

Power regulation

Good power regulation is crucial for getting the most out the HMC833. In particular, very low noise regulators should be used for VCC1, VCC2, VDDLS, and VPPCP. The current supply requirements for the HMC833 is given in Table 4.

Table 4: HMC833 power supply requirements.

Supply

Voltage

Current

VPPCP, VDDLS

5V

8mA

VCC1

5V

100mA

VCC2

5V

105mA

AVDD, VCCPD, VCCPS, VCCHF, RVDD

3.3V

52mA

Texas Instruments TPS717-Q1 series and LP38798 are used for regulating the above supplies because of their very low noise and excellent PSRR. The LP38798 has typically 5.4 \(\mu V_{(\textrm{RMS})}\) integrated noise from 10 Hz to 100 kHz (input voltage 6V, output voltage 5V) and better than 60 dB of PSRR across the same frequency range with better than 40 dB PSRR up to 2 MHz. The TPS717-Q1 has similar performance. These characteristics are of special importance for the VCO supplies since any noise on these will result in spurious signals in the synthesizer output.

The maximum output current for the TPS717-Q1 regulators is 150mA and for the LP38798, 800mA. A TPS71750 is therefore used for the VPPCP, VDDLS supply and a TPS71733 is used for the AVDD, VCCPD, VCCPS, VCCHF, RVDD supply. An LP38798 is used for both the VCC1 and VCC2 supplies. In addition, a TPS71733 is used to regulate the supply for the (optional) on-board reference oscillator.

The absolute maximum input voltage for the TPS717 regulators is 7V. An on board MIC5209YM regulator is therefore implemented to provide a regulated 6V, 500mA supply. The 6V supply also serves as the input for the LP38798 in order to minimize the overall power dissipation.

Note that the ESR of the output capacitor on the TPS717 devices must be less that \(1\Omega\). This constraint is met by using a 0603 MLCC such as CC0603KRX5R8BB105 or GCM188R71C105KA64D on the output. (Note also that X5R or X7R dielectrics should be used.)

The LP38798 is stable with an output capacitance of 1 uF to 10 uF. Since this will supply the VCO and output buffer the use of an MLCC is avoided in favour of a low ESR tantalum capacitor. This choice eliminates the possibility of microphonics on the supply rail.

Control connections

Serial programming connections:

  • SEN should be connected to a general purpose output pin. Note that this pin must be LOW on power up. When connected to a controller, the controller pin will be tristated initially (after power on or reset) and then configured as an output pin in the LOW state.

  • SCK is connected to the SPI SCK pin.

  • SDI is connected to the SPI MOSI pin.

  • It's not clear as yet which controller pin the LD_SDO should be connected to.

When using the SPI driver board for testing, the 'A' line on the SPI driver should be connected to the HMC833 SEN pin.

Scripts

Measuring output power

Figures 13a and 13b illustrates the test setup used to measure the HMC833 PLO power output. Since we don't currently have access to a spectrum analyzer which covers the full frequency range of the HMC833 the measurements are carried out in two stages. An HP8560A spectrum analyzer is used to measure the peak power of the fundamental tone for the frequency range 0.1 to 2.8 GHz. The power meter reference design is then used to measure the integrated power output from 0.1 to 6 GHz. Up to the power measurement uncertainties there is negligible difference between the integrated and peak power above approximately 1 GHz. Comparison of the measurements taken using both methods in the frequency range 0.1 to 2.8 GHz confirms this.

When using the power meter a 24 dB attenuator is connected between the power measurement sensor head and the signal source. This configuration must be calibrated before use. Previously measured calibration files are available here: referencedesigns/PowerMeter/app/cal-files. The default calibration file is 24dB-atten-48-0007-Det3-100-6000MHz.json which is used with the combination of the 24 dB attenuator with serial number 0007 together with the measurement sensor head labelled Det3. Refer to the Calibration and correction section of the power meter documentation for a description of how to generate calibration files.

The test setup makes use of the same layout as shown in Figure 9. The pin assignments and serial device may need to be changed in the script. The following code fragments can be used to make these changes before exporting the script.

PLO_SEN = 'D1'
PLO_LD_SDO = 'C4'
serial_device = '/dev/cu.usbmodem14201'
PLO Fund. Pwr Test

Figure 13a: HMC833 PLO power output test setup, 0.1 to 2.85 GHz.

PLO Fund. Pwr Test

Figure 13b: HMC833 PLO power output test setup, 0.1 to 6 GHz.

#
# Generated from hmc833.org
#
import sys
from time import sleep
from pathlib import Path
import json
import numpy as np
from scipy import (
    interpolate
)
import rpyc
from rfblocks import hmc833, HMC833Controller, create_serial
from tam import (
    InstrumentMgr, InstrumentInitializeException,
    UnknownInstrumentModelException, HP8560A_GPIBID,
    SMHU58_GPIBID
)

<<serial-port>>
<<pin-assignment>>
do_stage_1 = False
do_stage_2 = False
do_stage_3 = True
ref_level = 20.0

sa_cal_file = 'hmc833-pwr-sa-cal.json'
pwrmeter_cal_file = 'PowerMeter/app/cal-files/24dB-atten-48-0007-Det3-100-6000MHz.json'
stage1_measurements_file = 'hmc833-output-pwr-stage-1.json'
stage2_measurements_file = 'hmc833-output-pwr-stage-2.json'
stage3_measurements_file = 'hmc833-output-pwr-stage-3.json'
loss_fn = None

def insertion_loss(freq):
    return float(loss_fn(freq))

def measure_sigpwr():
    sig_pwr = {}
    for f in freq_list:
        print(f'{f=}:', end='')
        plo_ctl.freq = f
        is_locked = plo_ctl.configure_freq(ser)
        if is_locked:
            pwr, _ = sa.measure_pwr(f)
            sleep(0.5)
            sig_pwr['{:.1f}'.format(f)] = pwr + insertion_loss(f)
            print(f', {pwr+insertion_loss(f)}')
        else:
            print(', PLO lock failed!')
    return sig_pwr

def measure_integrated_pwr():
    sig_pwr = {}
    for f in freq_list:
        print(f'{f=}:', end='')
        plo_ctl.freq = f
        is_locked = plo_ctl.configure_freq(ser)
        if is_locked:
            chan_ctl.freq = f
            pwrmeter.root.measure(pwr_meter_chan)
            sig_pwr['{:.1f}'.format(f)] = float(chan_ctl.pwr)
            print(f', {chan_ctl.pwr}')
        else:
            print(', PLO lock failed!')
    return sig_pwr

def measure_equalized_power():
    sig_pwr = {}
    for f in freq_list:
        print(f'{f=}:', end='')
        if f < 1500.0:
            plo_ctl.divider_gain = hmc833.DividerGain.MAXGAIN
        elif f >= 3000.0:
            plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_3DB
        elif f >= 4400.0:
            plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN
        plo_ctl.freq = f
        is_locked = plo_ctl.configure(ser)
        if is_locked:
            chan_ctl.freq = f
            pwrmeter.root.measure(pwr_meter_chan)
            sig_pwr['{:.1f}'.format(f)] = float(chan_ctl.pwr)
            print(f', {chan_ctl.pwr}')
        else:
            print(', PLO lock failed!')
    return sig_pwr

def calibrate(srcpwr, start_freq, stop_freq, step_freq):
    loss = {}
    sg.output = True
    for f in np.arange(start_freq, stop_freq, step_freq):
        # Note that the property assignment here may actually
        # be a rpyc remote operation depending on the selected
        # calibration device.  Since ~f~ (and possibly ~srcpwr~)
        # may be numpy array scalars it is necessary to explicitly
        # cast these to standard Python float values.  If this is
        # not done the remote rpyc process will almost certainly
        # generate strange numpy related exceptions.
        sig_freq = float(f)
        sg.freq = sig_freq
        sg.level = float(srcpwr)
        sleep(0.5)
        meas_pwr, _ = sa.measure_pwr(sig_freq)
        loss[sig_freq] = srcpwr - meas_pwr
        sleep(0.5)
    sg.output = False
    freqs = np.array([f for f in loss.keys()])
    loss_arr = np.array([v for v in loss.values()])
    loss_interp_fn = interpolate.interp1d(
        freqs, loss_arr, kind='cubic')
    with open(sa_cal_file, 'w') as fd:
        json.dump(loss, fd)
    return loss_interp_fn

# Test HMC833 PLO initialization
ser = create_serial(serial_device)
plo = hmc833(sen=PLO_SEN, ld_sdo=PLO_LD_SDO)
plo_ctl = HMC833Controller('plo1', plo)
plo_ctl.initialize(ser)

if do_stage_1 is True:
    print()
    print("""---------- Stage 1 ----------\n""")
    print("""Output power measurements from 0.1 to 2.85 GHz.""")
    print("""These measurements use the HP8560A spectrum analyzer.""")
    input("Press a key when ready... ")
    print()

    # Spectrum analyzer initialization
    instrument_mgr = InstrumentMgr()
    try:
        sa = instrument_mgr.open_instrument('HP8560A', HP8560A_GPIBID)
    except InstrumentInitializeException as iie:
        print(iie.message)
        sys.exit(-2)
    except UnknownInstrumentModelException as ume:
        print(ume.message)
        sys.exit(-3)

    sa.clear()
    sa.vavg = 2
    sa.fspan = 0.1
    sa.ref_level = ref_level

    use_existing_sa_cal = False
    cal_path = Path('./'+sa_cal_file)
    if cal_path.is_file():
        print("""There is an existing test cable calibration file: {}.
Would you like to use this?""".format(sa_cal_file))
        ch = input("Use existing cable cal. file? (Y/N): ")
        if ch[0] in ['y', 'Y']:
            use_existing_sa_cal = True

    if not use_existing_sa_cal:
        try:
            sg = instrument_mgr.open_instrument('SMHU58', SMHU58_GPIBID)
        except InstrumentInitializeException as iie:
            print(iie.message)
            sys.exit(-2)
        except UnknownInstrumentModelException as ume:
            print(ume.message)
            sys.exit(-3)

        print("""Calibration of the test cable must now be carried out.
Please connect the test cable between the RF output
of the SMHU58 signal generator and the HP8560A spectrum analyzer.""")
        input("Press a key when ready... ")

        loss_fn = calibrate(0.0, 100.0, 2850.0, 100.0)

        print("Calibration complete.")
    else:
        with open(sa_cal_file) as fd:
            loss = json.load(fd)
        freqs = np.array([float(f) for f in loss.keys()])
        loss_arr = np.array([v for v in loss.values()])
        loss_fn = interpolate.interp1d(
            freqs, loss_arr, kind='cubic')
        print("Cable calibration data loaded from '{}'".format(sa_cal_file))

    print("""Please connect the test cable between the output
of the RF signal generator under test and the input of
the HP8560A spectrum analyzer.""")
    input("Press a key when ready... ")

    freq_list = [float(f) for f in np.linspace(100.0, 2800.0, 40).round()]

    plo_ctl.divider_gain = hmc833.DividerGain.MAXGAIN
    plo_ctl.vco_mute = False

    print("Gain setting: MAXGAIN_MINUS_9DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB
    plo_ctl.configure(ser)
    sa.clear()
    sleep(2.0)
    sa.vavg = 2
    sa.fspan = 0.1
    sa.ref_level = ref_level
    max_minus_9dB = measure_sigpwr()

    print("Gain setting: MAXGAIN_MINUS_6DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_6DB
    plo_ctl.configure(ser)
    sa.clear()
    sa.vavg = 2
    sa.fspan = 0.1
    sa.ref_level = ref_level
    max_minus_6dB = measure_sigpwr()

    print("Gain setting: MAXGAIN_MINUS_3DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_3DB
    plo_ctl.configure(ser)
    sa.clear()
    sa.vavg = 2
    sa.fspan = 0.1
    sa.ref_level = ref_level
    max_minus_3dB = measure_sigpwr()

    print("Gain setting: MAXGAIN...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN
    plo_ctl.configure(ser)
    sa.clear()
    sa.vavg = 2
    sa.fspan = 0.1
    sa.ref_level = ref_level
    maxgain = measure_sigpwr()

    stage_1 = {'max_minus_9dB': max_minus_9dB,
               'max_minus_6dB': max_minus_6dB,
               'max_minus_3dB': max_minus_3dB,
               'maxgain': maxgain}

    # Stash the measurements obtained so far
    with open(stage1_measurements_file, 'w') as fd:
        json.dump(stage_1, fd)

    plo_ctl.vco_mute = True
    plo_ctl.configure(ser)

if do_stage_2 is True:
    print()
    print("""---------- Stage 2 ----------\n""")
    print("""Output power measurements from 0.1 to 6 GHz.""")
    print("""Please attach the power meter 0.1-6GHz measurement head to
the HMC833 PLO module RF output.  Ensure that the measurement head is
connected as channel 0 on the power meter base unit and that the
pwrmeter app is running.""")
    input("Press a key when ready... ")
    print()

    # Power meter initialization.
    pwrmeter = rpyc.connect('127.0.0.1', 18863)
    pwrmeter.root.initialize()
    chan_controllers = pwrmeter.root.detectors
    pwr_meter_chan = 0
    chan_ctl = chan_controllers[pwr_meter_chan]
    chan_controllers[1].enabled = False
    chan_ctl.load_caldata(pwrmeter_cal_file)
    chan_ctl.apply_correction = True

    freq_list = [float(f) for f in np.linspace(100.0, 6000.0, 50).round()]

    plo_ctl.divider_gain = int(hmc833.DividerGain.MAXGAIN)
    plo_ctl.vco_mute = False

    print("Gain setting: MAXGAIN_MINUS_9DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_9DB
    plo_ctl.configure(ser)
    max_minus_9dB = measure_integrated_pwr()

    print("Gain setting: MAXGAIN_MINUS_6DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_6DB
    plo_ctl.configure(ser)
    max_minus_6dB = measure_integrated_pwr()

    print("Gain setting: MAXGAIN_MINUS_3DB...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN_MINUS_3DB
    plo_ctl.configure(ser)
    max_minus_3dB = measure_integrated_pwr()

    print("Gain setting: MAXGAIN...")
    plo_ctl.buffer_gain = hmc833.OutputBufferGain.MAXGAIN
    plo_ctl.configure(ser)
    maxgain = measure_integrated_pwr()

    stage_2 = {'max_minus_9dB': max_minus_9dB,
               'max_minus_6dB': max_minus_6dB,
               'max_minus_3dB': max_minus_3dB,
               'maxgain': maxgain}

    # Stash the measurements obtained so far
    with open(stage2_measurements_file, 'w') as fd:
        json.dump(stage_2, fd)

    plo_ctl.vco_mute = True
    plo_ctl.configure(ser)

if do_stage_3 is True:
    print()
    print("""---------- Stage 3 ----------\n""")
    print("""Output power measurements from 0.1 to 6 GHz.""")
    print("""Please attach the power meter 0.1-6GHz measurement head to
the HMC833 PLO module RF output.  Ensure that the measurement head is
connected as channel 0 on the power meter base unit and that the
pwrmeter app is running.""")
    input("Press a key when ready... ")
    print()

    # Power meter initialization.
    pwrmeter = rpyc.connect('127.0.0.1', 18863)
    pwrmeter.root.initialize()
    chan_controllers = pwrmeter.root.detectors
    pwr_meter_chan = 0
    chan_ctl = chan_controllers[pwr_meter_chan]
    chan_controllers[1].enabled = False
    chan_ctl.load_caldata(pwrmeter_cal_file)
    chan_ctl.apply_correction = True

    freq_list = [float(f) for f in np.linspace(100.0, 6000.0, 50).round()]

    plo_ctl.divider_gain = int(hmc833.DividerGain.MAXGAIN)
    plo_ctl.vco_mute = False

    equalized_pwr = measure_equalized_power()

    stage_3 = {'equalized_pwr': equalized_pwr}

    # Stash the measurements obtained so far
    with open(stage3_measurements_file, 'w') as fd:
        json.dump(stage_3, fd)

    plo_ctl.vco_mute = True
    plo_ctl.configure(ser)