MATLAB Examples

Walkie-Talkie Receiver Using Analog Devices AD9361/AD9364

This example shows how to use the Xilinx® Zynq-Based Radio Support Package with MATLAB® to implement a walkie-talkie receiver. The received signal can be transmitted by a compatible commercial walkie-talkie. Alternatively, the transmission can be generated by the companion Walkie-Talkie Transmitter Using Analog Devices™ AD9361/AD9364 example if you have a second SDR platform.

Refer to the Getting Started documentation for details on configuring your host computer to work with the Support Package for Xilinx® Zynq-Based Radio.



Walkie-talkies provide a subscription-free method of communicating over short distances. There are a number of different standards used around the world. This example uses MATLAB SDR system objects to implement two of these standards: Family Radio Service and Personal Mobile Radio 446.

  • Family Radio Service (FRS): Operates on 14 channels at frequencies around 462 MHz and 467 MHz. The channel spacing is 25 kHz. FRS radios are commonly found in the North and South America. More details on FRS can be found in [ 1 ].
  • Personal Mobile Radio 446 (PMR446): Operates on 8 channels around 446 MHz. The channel spacing is 12.5 kHz. PMR446 radios are commonly found in Europe. More details on PMR446 can be found in [ 2 ].

Both FRS and PMR446 use analog frequency modulation (FM) with a maximum frequency deviation of +-2.5 kHz. They also use a Continuous Tone-Code Squelch System (CTCSS) to filter unwanted signals at the receiver. CTCSS is implemented in this example.


Before running the example, ensure you have performed the following steps:

1. Configure your host computer to work with the Support Package for Xilinx® Zynq-Based Radio. See Getting Started for help.

2. Ensure that you have a suitable transmitter. This example is designed to work in conjunction with any of the following possible transmitters:

3. Ensure that your transmitter is set to the same protocol, channel and CTCSS code.

Running the Example

The example can be run by executing zynqRadioWalkieTalkieReceiverAD9361AD9364ML.m.

Once both the transmitter and receiver are active, you should hear the transmitted audio on your computers default audio output device.

Receiver Design: System Architecture

The general structure of the walkie-talkie receiver can be described as follows:

  1. Receive baseband data from the SDR hardware.
  2. Filter the signal to reduce noise from adjacent channels.
  3. Apply AGC to the signal.
  4. Filter out weak signals based on received signal power. Downstream processing is disabled if the signal is too weak.
  5. FM Demodulate the signal.
  6. Downsample the signal to a standard 8 kHz audio sample rate.
  7. Perform CTCSS decoding. If the received code matches the desired code, pass through the audio.
  8. High pass filter the audio to remove the CTCSS tone.
  9. Play the audio on the computers default audio output device.

The high level functionality of the receiver can be controlled by changing the variables in the code section below.

% Set the variables that control the receiver.
% The protocol variable should be set to 'FRS' or 'PMR446' depending on
% which standard the transmitter is using.

protocol = 'FRS';
% protocol = 'PMR446';
channel = 1;
CTCSSCode = 5;
CTCSSDecodeBlockLength = 4000; % in samples
signalPowerThreshold = 0.1;
receiverStopTime = 20; % in seconds

% Get a structure of parameters used for controlling the receiver.
rxParams = zynqRadioWalkieTalkieReceiverHelperAD9361AD9364ML_init(protocol, channel);

By default, the example is configured to run with ZC706 and ADI FMCOMMS2/3/4 hardware. You can uncomment one of the following lines, as applicable, to set the SDRDeviceName field in structure variable rxParams.

% % rxParams.SDRDeviceName='ZedBoard and FMCOMMS2/3/4';
% % rxParams.SDRDeviceName='ADI RF SOM';

rxParams.SDRDeviceName = 'ZC706 and FMCOMMS2/3/4';
radio = sdrdev(rxParams.SDRDeviceName);

Each component of the transmitter is described in more detail in the following sections.

SDR Receiver

An SDR Receiver system object is used with the named radio 'ZC706 and FMCOMMS2/3/4' to receive baseband data from the SDR hardware.

SDR Receiver

sdrReceiver = sdrrx(rxParams.SDRDeviceName,...
    'IPAddress',             '', ...
    'CenterFrequency',       rxParams.radioCenterFrequency, ...
    'ChannelMapping',        1, ...
    'GainSource',            'AGC Fast Attack', ...
    'SamplesPerFrame',       rxParams.radioFrameSize, ...
    'BasebandSampleRate',    rxParams.radioSampleRate, ...
    'OutputDataType',        'single');

Channel Filter

The channel filter is used to attenuate signals from adjacent channels. FRS uses a channel separation of 25 kHz, while PMR446 uses a channel separation of 12.5 kHz. Making the channel filter a low pass filter with a cutoff frequency of 6 kHz gives a good tradeoff between adjacent channel attenuation and filter length.

channelFilter = rxParams.channelFilter;

Automatic Gain Control (AGC)

Automatic gain controller applies a variable gain to the received signal to assure that the received signal amplitude is at a known level.

AGC = comm.AGC;

FM Demodulation

This example implements FM demodulation by taking the phase difference of consecutive complex samples. A delay system object is used to delay the received baseband signal to prepare for the phase difference calculation.

unitDelay = dsp.Delay;

Software Downsampling

A decimation filter converts the sampling rate to 8 kHz. This rate is one of the native sampling rates of your host computers output audio device. Use an FIR decimator system object to convert the 80 kHz signal to an 8 kHz signal.

softwareDecimator = dsp.FIRDecimator(rxParams.softwareDecimationFactor, ...

Continuous Tone-Coded Squelch System (CTCSS)

The CTCSS [ 3 ] decoder computes the power at each CTCSS tone frequency using the Goertzel algorithm [ 4 ] and outputs the code with the largest power. The Goertzel algorithm provides an efficient method to compute the frequency components at predetermined frequencies, i.e. the tone code frequencies used by CTCSS. The CTCSS decoder is implemented as a custom system object, sdrzWalkieTalkieReceiverHelper_CTCSSDecoder.m.

CTCSSDecoder = sdrzWalkieTalkieReceiverHelper_CTCSSDecoder(...
    'MinimumBlockLength', CTCSSDecodeBlockLength, ...
    'SampleRate',         rxParams.audioSampleRate);

CTCSS Filter

A high pass filter with a cutoff frequency of 260 Hz filters out the CTCSS tones, which have a maximum frequency of 250 Hz.

CTCSSFilter = rxParams.CTCSSFilter;

Audio Output

An audio device writer system object is used to play the received signal through your computers speakers. If you do not hear any sound, select another device using the Device property of the audio player object, audioPlayer.

audioPlayer = audioDeviceWriter(rxParams.audioSampleRate);

Reception and Baseband Processing

The actual reception and processing of the data is enclosed in a try/catch block. This means that if there is an error, the system objects still get released properly.

By enclosing the data reception and processing in a while loop controlled using a tic/toc pair, the receiver will run for approximately the desired real world time.

    % The first time the SDR receiver object is used to receive data, it
    % performs some initial setup and takes about 4s. The data received
    % during this initial setup is discarded. This means the setup time is
    % not included as part of the desired run time.
    [~, ~] = sdrReceiver();
    display('Starting reception')
    tic; % start timing the transmission
    while toc < receiverStopTime
        [data, len, lostSamples] = sdrReceiver();

        if lostSamples ~= 0
            disp('Unable process data in real time!')
        if len > 0
            % Channel filter
            outChanFilt = channelFilter(data);
            % AGC
            outAGC = AGC(outChanFilt);
            % Check the received signal power. Only attempt to decode if
            % the received signal is strong enough.
            rxAmp       = mean(abs(outAGC));
            if rxAmp > signalPowerThreshold
                % FM demodulate
                outDelay  = unitDelay(outAGC);
                demodulatedSignal = angle(outDelay .* conj(outAGC));
                % Downsample to 8 kHz
                outRawAudio   = softwareDecimator(demodulatedSignal);

                % CTCSS decoder
                receivedCode = CTCSSDecoder(outRawAudio);
                %                 fprintf('Detected CTCSS Code %d\n',receivedCode);
                if (receivedCode == CTCSSCode) || (CTCSSCode == 0)
                    % Output to audio device
                    audioSignal = CTCSSFilter(outRawAudio);
    disp('Finished reception')
catch ME
% Release System objects associated with hardware

Receiver Design: System Sample Rates

The system has two different sample rates:

  1. The SDR hardware baseband rate, 640 kHz
  2. The audio sample rate, 8 kHz

The downsample by 40 from 640 kHz to 8 kHz is done in software using a dsp.FIRDecimator object, softwareDecimator. The radio baseband rate of 640 kHz is used for two reasons:

  1. By Carson's rule, the approximate passband bandwidth of the desired FM signal is 2*(frequencyDeviation + peakAudioFrequency). In this example, that equates to a signal bandwidth of 2*(2.5e3 + 4e3) = 13 kHz. This means we need to use a sample rate greater than 13 kHz.
  2. The sample rates in software have no relation to the real world transmission rates. The actually reception rate is determined entirely by the SDR hardware baseband sample rate. To make the software sample rates meaningful, the sample rates at the software/hardware interface must match. For the AD9361/AD9364, one of the possible baseband sample rates that is a multiple of 8 kHz and greater than 13 kHz is 640 kHz. Using an integer downsampling factor means a smaller decimation filter (less taps) can be used.

Things to Try

To modify the example, save a local copy of zynqRadioWalkieTalkieReceiverAD9361AD9364ML.m. Some possible modifications include:

  • Try changing the channel by changing the channel variable.
  • Try changing the CTCSS code by changing the CTCSSCode variable. Note that the receiver will not play the transmission out loud unless it has the same CTCSS code as the transmitter, or it has CTCSS disabled by setting the code to 0.
  • Try changing the CTCSSDecodeBlockLength variable, which is used to control the number of samples used in the Goertzel algorithm for detecting the CTCSS tones. Larger values result in a finer frequency resoultion and therefore better tone detection, at the expense of increased latency. For example, the default value of 4000 results in a delay of 0.5s.

Alternative Implementations

This example implements a walkie-talkie receiver in MATLAB. You can also view the equivalent system implemented using Simulink in the Walkie-Talkie Receiver Using Analog Devices AD9361/AD9364 using Simulink example.

Troubleshooting the Example

If you cannot hear the transmitted signal on your receiver, try the following:

  • Make sure that the transmitter and the receiver are set to the same protocol, channel and CTCSS code.
  • Disable CTCSS on the receiver by setting the code to 0. Note that codes higher than 38 use Digital Coded Squelch, which is not implemented in this example. You can also uncomment the fprintf statement in the baseband processing loop to display the detected CTCSS code in the MATLAB command window.
  • Set the signalPowerThreshold variable to 0 to disable filtering out weak signals. Try adjusting the value to achieve a good tradeoff between playing valid received signals and blocking noise when the received signal is too weak.
  • Try varying the gain of the radio object depending on your received signal strength.

General tips for troubleshooting SDR hardware can be found in Xilinx Zynq-Based Radio Processing Errors and Fixes.

List of Example Helper Files

This example uses the following helper functions


  1. Family Radio Service on Wikipedia®
  2. PMR446 on Wikipedia
  3. Continuous Tone-Coded Squelch System on Wikipedia
  4. Goertzel Algorithm on Wikipedia