plutotx

plutotx is a very simple console application that drives Adalm Pluto to generate a CW tone on the frequency and power level selected by the user.

I hope that the information and the C source that you will read below can be a small help for all developers who want to create a new SDR project.

From the archive available for download you will also find the binaries compiled for Windows and Linux x86, so it could also be useful to those who are not developers but simply have an interest in experimenting with Pluto.

Latest Release:

plutotx v.1.1 (04 august 2022)
File size: 705,962  Bytes
MD5 bb0d3b1e42ff892f377fcf5e2cdbb7f3
SHA1 30ade07ad365b23c211f85632c6e9edcb1efdaa0
SHA256 607ae42da31bddaf1528a4dadac7b8ce711a4e2e9ee9210726c3fb2e1d6f673c

- F: 2,147GHz limit
- I: Output bursts issue
- I: Switching off any DDS second tone active
- A: Include, library and instruction to compile under Windows and Linux

Older versions:

plutotx (4 august 2022)
File size: 685,626 Bytes
MD5 e241666bd41a84e5eff1ec44dab2283f
SHA1 e775f8fce1af59c833a850635b6df135cda9ef1b
SHA256 bf1e782b704b5671c901bfb53280a7f754475eba3d0204dd5bf25f99326e393b

To compiling and execute plutotx needs libiio library from ADI. Download and install the library for your specific SO from here: libiio

To launch, plutotx requires three parameters: frequency in kHz, a power level output expressed in dBm and a URI of device to connect (optional)

eg.: plutotx 432410 -10

plutotx will connect to default URI of device ip:192.168.2.1 if the third optional parameter is not specified.

How it works

I will describe the source of plutotx in a simplified form to facilitate understanding of the steps required to generate the CW tone:

  • Connect to Pluto device and acquire the context structure
  • From the acquired context test the model of the transceiver if an AD9364
  • Find devices physical transceiver and the DAC/TX output driver (FPGA)
  • Find channels of I, Q, TX chain and TX Local Oscillator
  • Apply the default configuration
  • Set the TX attenuator value
  • Set the TX bandwidth
  • Set the frequency and phase of the I and Q channels
  • Set the frequency of the TX Local Oscillator
  • Turn on the TX output activating channels I and Q raw

First of all, we need to connect to Pluto device and acquire the context structure:

struct iio_context *ctx;
ctx = iio_create_context_from_uri("ip:192.168.2.1");

From the acquired context test the model of the transceiver if an AD9364:

if((value=iio_context_get_attr_value(ctx, "ad9361-phy,model"))!=NULL)
  {
  if(strcmp(value,"ad9364"))
    stderrandexit("Pluto not expanded",0,__LINE__);
  }else
   stderrandexit("Error retrieving phy model",0,__LINE__);

Find devices physical transceiver and the DAC/TX output driver (FPGA):

phy = iio_context_find_device(ctx, "ad9361-phy");
dds_core_lpc = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");

Find channels of I, Q, TX chain and TX Local Oscillator:

tx0_i = iio_device_find_channel(dds_core_lpc, "altvoltage0", true);
tx0_q = iio_device_find_channel(dds_core_lpc, "altvoltage2", true);
tx_chain=iio_device_find_channel(phy, "voltage0", true);
tx_lo=iio_device_find_channel(phy, "altvoltage1", true);

Apply the default configuration. This step is not necessary probably but recommended if using another SDR application before plutotx:

//enable internal TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"external",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//disable fastlock feature of TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"fastlock_store",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//power on TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"powerdown",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//full duplex mode
if((rc=iio_device_attr_write(phy,"ensm_mode","fdd"))<0)
  stderrandexit(NULL,rc,__LINE__);

//calibration mode to manual
if((rc=iio_device_attr_write(phy,"calib_mode","manual"))<0)
  stderrandexit(NULL,rc,__LINE__);

Line 18 sets the TX calibration mode to manual to avoid spikes on output over the TX power level sets by the user.

Set the TX attenuator value. The attenuator value is calculated from the value requested by the user minus the output power of Pluto that is about 10 dBm defined by REFTXPWR:

if((rc=iio_channel_attr_write_double(tx_chain,"hardwaregain",dBm-REFTXPWR))<0)
  stderrandexit(NULL,rc,__LINE__);

Set the TX bandwidth:

if((rc=iio_channel_attr_write_longlong(tx_chain,"rf_bandwidth",FBANDWIDTH))<0)
  stderrandexit(NULL,rc,__LINE__);

Set the scale, frequency and phase of the I and Q channels:

if((rc=iio_channel_attr_write_double(tx0_i,"scale",1))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_double(tx0_q,"scale",1))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_i,"frequency",FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_q,"frequency",FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_i,"phase",90000))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_q,"phase",0))<0)
  stderrandexit(NULL,rc,__LINE__);

Set the frequency of the TX Local Oscillator. The TX local oscillator frequency will be the value requested by the user minus the frequency of the CW tone.

if((rc=iio_channel_attr_write_longlong(tx_lo,"frequency",freq-FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

Turn on the TX output activating channels I and Q raw:

int rc;

if((rc=iio_channel_attr_write_bool(
		tx0_i,
		"raw",
		1))<0)
 stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_bool(
		tx0_q,
		"raw",
		1))<0)
 stderrandexit(NULL,rc,__LINE__);

Entire source code of plutotx:

/*
 Author: Alberto Ferraris IU1KVL - https://www.albfer.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the version 3 GNU General Public License as
 published by the Free Software Foundation.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "iio.h"

#define URIPLUTO "ip:192.168.2.1"
#define MINFREQ 50000000
#define MAXFREQ 6000000000
#define MINDBM -89
#define MAXDBM 10
#define REFTXPWR 10
#define FBANDWIDTH 4000000
#define FSAMPLING 4000000
#define FCW 1000000

struct iio_channel *tx0_i, *tx0_q;

void stderrandexit(const char *msg, int errcode, int line)
{
if(errcode<0)
  fprintf(stderr, "Error:%d, program terminated (line:%d)\n", errcode, line);
  else
  fprintf(stderr, "%s, program terminated (line:%d)\n",msg, line);
exit(-1);
}

void CWOnOff(int onoff)
{
int rc;

if((rc=iio_channel_attr_write_bool(
		tx0_i,
		"raw",
		onoff))<0)
 stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_bool(
		tx0_q,
		"raw",
		onoff))<0)
 stderrandexit(NULL,rc,__LINE__);
}

int main(int argc, char* argv[])
{
struct iio_context *ctx;
struct iio_device *phy;
struct iio_device *dds_core_lpc;
struct iio_channel *tx_chain;
struct iio_channel *tx_lo;
const char *value;
long long freq;
double dBm;
int rc;
int ch;

if(argc<3)
  {
  printf("Usage: plutotx kHz dBm [uri]\n");
  return  0;
  }

freq=atol(argv[1])*1000;

if(freq<MINFREQ || freq>MAXFREQ)
  stderrandexit("Frequency is not in range",0,__LINE__);

dBm=atof(argv[2]);

if(dBm<MINDBM || dBm>MAXDBM)
  stderrandexit("dBm is not in range",0,__LINE__);

if(argc>3)
  ctx = iio_create_context_from_uri(argv[3]);
  else
  ctx = iio_create_context_from_uri(URIPLUTO);

if(ctx==NULL)
  stderrandexit("Connection failed",0,__LINE__);

if((value=iio_context_get_attr_value(ctx, "ad9361-phy,model"))!=NULL)
  {
  if(strcmp(value,"ad9364"))
    stderrandexit("Pluto is not expanded",0,__LINE__);
  }else
   stderrandexit("Error retrieving phy model",0,__LINE__);

phy = iio_context_find_device(ctx, "ad9361-phy");
dds_core_lpc = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");  
tx0_i = iio_device_find_channel(dds_core_lpc, "altvoltage0", true);
tx0_q = iio_device_find_channel(dds_core_lpc, "altvoltage2", true);
tx_chain=iio_device_find_channel(phy, "voltage0", true);
tx_lo=iio_device_find_channel(phy, "altvoltage1", true);

if(!phy || !dds_core_lpc || !tx0_i || !tx0_q || !tx_chain || !tx_lo)
  stderrandexit("Error finding device or channel",0,__LINE__);

//enable internal TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"external",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//disable fastlock feature of TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"fastlock_store",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//power on TX local oscillator
if((rc=iio_channel_attr_write_bool(tx_lo,"powerdown",false))<0)
  stderrandexit(NULL,rc,__LINE__);

//full duplex mode
if((rc=iio_device_attr_write(phy,"ensm_mode","fdd"))<0)
  stderrandexit(NULL,rc,__LINE__);

//calibration mode to manual
if((rc=iio_device_attr_write(phy,"calib_mode","manual"))<0)
  stderrandexit(NULL,rc,__LINE__);

CWOnOff(0);  

if((rc=iio_channel_attr_write_double(tx_chain,"hardwaregain",dBm-REFTXPWR))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx_chain,"rf_bandwidth",FBANDWIDTH))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx_chain,"sampling_frequency",FSAMPLING))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_double(tx0_i,"scale",1))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_double(tx0_q,"scale",1))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_i,"frequency",FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_q,"frequency",FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_i,"phase",90000))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx0_q,"phase",0))<0)
  stderrandexit(NULL,rc,__LINE__);

if((rc=iio_channel_attr_write_longlong(tx_lo,"frequency",freq-FCW))<0)
  stderrandexit(NULL,rc,__LINE__);

CWOnOff(1);

printf("TX ON! Q to exit or E to keep TX ON and exit\n");

while(1)
     {
     ch=getchar();
     if(ch=='q' || ch=='Q')
       {
       CWOnOff(0);
       break;
       }
     if(ch=='e' || ch=='E')
       break;
     };

iio_context_destroy(ctx);

return 0;
}

SATSAGEN

SATSAGEN is a Windows application that allows you to use an SDR device as a Spectrum Analyzer. SATSAGEN supports the ADALM-PLUTO device only, at the moment. and other SDR devices with the latest SATSAGEN releases, like RTL-SDR, HackRF, RSP1, and many others!

SATSAGEN is provided free of charge to the HAM Radio community, with the hope that SATSAGEN can be appreciated as a useful tool for our radio experimentation.

SATSAGEN news:

https://www.albfer.com/en/satsagen-news/

Download SATSAGEN from this link:

SATSAGEN Download Page

The prerequisites of the application are:

  • OS: From Windows 7 to Windows 10
  • Drivers for ADALM-PLUTO installed: PlutoSDR-M2k-USB-Drivers
  • ADALM-PLUTO device with firmware > = 0.31
    • or other SDR devices with the latest SATSAGEN releases!

WARNING: At the first start, the application will perform on the device the frequency and bandwidth extension needed for the use of the 70MHZ-6000MHZ range, forcing the firmware to “see” the AD9363 transceiver as an AD9364. The extension is required for the application to work, but if you don’t want it to happen, don’t start SATSAGEN.

I would like to thank my friends Gianni IW1EPY, Domenico I1BOC and Mauro IZ1OTT for giving me the idea, the support in every sense, the radio components and the equipment necessary for the realization of the project!

A special thanks goes to Boian Mitov for the GREATS libraries www.mitov.com used in SATSAGEN!

Below you will found another valuable contribution by Gianni and at the end of the post you will find a short video that illustrates the application basics.

Alberto IU1KVL

As Adalm Pluto owner I become acquainted to this device using radio programs (SDR Console, SDRAngel) to link Oscar 100.
But for this kind of hardware my asking was for a measurement system. I have test cheap network analyzer in the range of 4,4 GHz, vector analyzer up to 900 MHz and my idea was to set Pluto in this class of instruments using the extended range 70 MHz to 6 GHz.
After some encouraging trials from the RF point of view but disappointing for the measurement time delay in Matlab, I drag my friend Alberto into this adventure to have an acceptable measurement time using C libraries.
Apart the nice Alberto’s program I add some Hardware notes.
Adalm Pluto it is born for sure not for a professional measurement instruments so some drawbacks can be expected.
Due to the large bandwidth usage forced by the program (original Pluto frequency usage spans from 325 MHz to 3,8 GHz) the input and output impedance for sure are not 50 ohms.
A pair of attenuators on input and output mitigate the problem, for sure reducing the usable dynamic range but acceptable for HamRadio users. Using two 10 dB attenuators remain 40 dB down to the calibration level and 20 up in case of insertion of an active device under test.
The missing metal box generate some crosstalk problems in the upper range of frequency specially if Pluto is moved around metal frames or touched by fingers… some people have reboxed it.
The present structure (Pluto plus optional attenuators) allows a direct measurement of transfer function of filters, amplifiers, a directional coupler or a reflection bridge is mandatory for impedance measurement.
Is possible to attach a file to correct the deviation of output power over the frequency sweep, unluckily every Pluto have its own variance. By now I have analyzed 5 devices and the correction curve are available.
With the linearization file the output power can maintain an error of 1 dB versus 10 : 12 dB of the unleveled , most of this nonlinearity is located in the range from 50 to 300 MHz end 4.5 to 6 GHz obviously where Pluto was not designed for.
The receiver gain and the generator attenuator do not increase the linearization error, so one linearization file is enough. Pay attention to not overload the receiver or saturate the generator but this behavior become immediately evident.
To enhance the dynamic linearization is possible to apply a -40 dB calibration using a correct attenuator.
This linearization performs in the range thill -40 db but almost kill the response from -40 to -60, in any case due to cross talk end receiver erroring this range is severely degraded even without this correction.
All the level of the RX Gain and the Output Power and the attenuators that you have inserted at the I/O are programmable.
Any idea of improvement ?
This is the list of future enhancement:
Calibration using a directional coupler or bridge averaging open and short.
Offset between transmitter and receiver in order to test conversion systems.
Harmonic response of devices in order to test amplifiers or multipliers.
Open to suggestions.
I think that Adalm Pluto with SATSAGEN, covering 7 Ham bands, will be useful in designing and testing to every ones involved in RF field.
IW1EPY

SA TSA GEN for ADALM-PLUTO

The project consists in the develop of a Windows application for the use of ADALM-PLUTO (recently received as a gift from a dear friend) as a spectrum analyzer.

I hope to write a post about this soon as well, anyway I list the highlights of the project now:

  • Spectrum analyzer with full span operating range 70MHz-6GHz and representation of signal amplitude in dBm.
  • Spectrum analyzer with tracking generator. Resolution up to 1024 points.
  • Generator with 1 KHz of frequency resolution

The software and hardware prerequisites are:

  • CPU: an old 1.7GHz Pentium M is more than enough!
  • OS:> = Windows 7
  • ADALM-PLUTO extended to let the FW “see” the AD9363 as an AD9364
  • Analog Devices drivers installed (PlutoSDR-M2k-USB-Drivers)

See you soon!