Before you start
The AA-30.ZERO is intended for users who require flexibility, low-cost, and small size.
It comes without on-board USB or pin headers to keep the cost down.
It’s a best choice for a measuring core you want to leave embedded in a project.
Please note that the .ZERO operates at 5V (like most Arduino or Arduino compatible boards).
Be sure to provide the correct power and use components/adaptors whose operating voltage matches .ZERO.
Connecting .ZERO to the PC
The .ZERO comes without built-in USB circuitry, so an off-board USB-to-TTL serial convertor must be used to communicate with the .ZERO.
.ZERO communicates with external devices throug UART interface.
For these purposes two UART interfaces are provided: UART1 (pins 0/TX1 and 2/RX1) and UART2 (pins 4/TX2 and 7/RX2).
By default .ZERO uses UART2 interface.
To make our first project we could get, for example, the SparkFun USB UART Serial Breakout.
Fig.1.ZERO plus USB2UART adaptor wiring diagram
Using colored wires connect USB2UART adaptor to .ZERO.
NOTE: connect USB adaptor’s TX pin to the RX pin of the .ZERO. Then connect USB adaptor’s RX pin to the TX pin of the .ZERO.
Then connect USB2UART to your PC using standard USB cable.
Please be sure that the Operating System recognizes your USB-to-UART adaptor and drivers are installed correctly:
Installing AntScope software
Then download AntScope software to measure anything you want.
You may use software installer from AA-30 product page or download standalone AntScope software using this link.
Suppose you downloaded the archive with standalone AntScope software.
Open the archive and extract files (with subdirectories) into your workplace. Then run AntScope.exe:
The AntScope program may not detect the AA-30.ZERO connected automatically.
In this case, you have to select the type of the connected device in the menu yourself:
Please be sure that the right COM port selected:
Congratulations! Now everything is ready for the first measurements.
Start measuring
First connect AA-30.ZERO to the antenna (or something else you want to measure) using flexible cable adaptor.
Then click Run Icon right under the menu bar:
then press “Set full range” button and press “OK” button:
The .ZERO starts blinking…
and after a few seconds the measurement results are displayed:
Pairing .ZERO with the Arduino Uno
Installing headers
The .ZERO supplied with traight breakaway headres kit:
If you want to pair your .ZERO with the Arduino board you should solder breakaway headers first.
After the heareds soldered simply connect .ZERO with the Arduino board:
Pins usage
- D0 – UART interface 1, TX, data out
- D1 – UART interface 1, RX, data in
- D4 – UART interface 2, TX, data out
- D7 – UART interface 2, RX, data in
You can choose which UART interface should be used by unsoldering jumpers:
By default the .ZERO uses UART2 interface.
Installing Arduino IDE and compiling your first project
After that you should compile and run very simple sketch on your Arduino board.
Download and install Arduino IDE.
// RigExpert AA-30 ZERO antenna & cable analyzer and Arduino Uno
//
// Receives from the Arduino, sends to AA-30 ZERO.
// Receives from AA-30 ZERO, sends to the Arduino.
//
// 26 June 2017, Rig Expert Ukraine Ltd.
//
#include <SoftwareSerial.h>
SoftwareSerial ZERO(4, 7); // RX, TX
void setup() {
ZERO.begin(38400); // init AA side UART
ZERO.flush();
Serial.begin(38400); // init PC side UART
Serial.flush();
}
void loop() {
if (ZERO.available()) Serial.write(ZERO.read()); // data stream from AA to PC
if (Serial.available()) ZERO.write(Serial.read()); // data stream from PC to AA
}
This simple “serial repeater” code provides two-way communication between the computer and the .ZERO.
By the way, since the Arduino borad acts as a repeater, you can implement any code in it and moderate the exchange of data between the computer and the .ZERO analyzer.
Serial communication protocol
Since the .ZERO is intended for the amateurs to construct something intersting, we had to take care to give more freedom to the users of the .ZERO device.
Using the commands below, you can not only measure the parameters of antennas and cables, but also make the .ZERO automatically perform periodic measurements immediately after power is applied.
This is convenient when the instrument is part of a more complex system, for example, an antenna tuner.
Communication protocol
Command | Description | Response |
---|---|---|
ver | returns analyzer type and firmware version | AA-30 ZERO XXX |
fqXXXXXXXXX | set center frequency to XXXXXXXXX Hz | OK |
swXXXXXXXXX | set sweep range to XXXXXXXXX Hz | OK |
frxNNNN | perform NNNN measurements in the specified range | output frequency (MHz), R and X for every meqasurement |
Example:
SW1000000\r\n
FRX10\r\n
14.000000,58.84,17.28\r\n
14.100000,69.74,16.79\r\n
14.200000,68.52,5.62\r\n
14.300000,62.49,2.79\r\n
14.400000,57.51,4.62\r\n
14.500000,55.38,9.11\r\n
14.600000,56.52,13.56\r\n
14.700000,59.40,17.41\r\n
14.800000,64.12,20.05\r\n
14.900000,71.13,22.01\r\n
15.000000,81.57,21.63\r\n
Data visualization
Quite simplest way to visualize measurement results is to make your own application 🙂
Install Processing IDE
By following this way, first we will download and install Processing IDE.
After the IDE software is installed, let’s compile this quite simple sketch:
// For RigExpert AA-30 ZERO antenna & cable analyzer with Arduino Uno
//
// Receives data from AA-30 ZERO and makes a surface
// which is a visualization of SWR as a function of time
//
// 26 June 2017, Rig Expert Ukraine Ltd.
//
import processing.serial.*;
Serial ZERO;
int step; // Communication protocol steps (0 - set Freq; 1 - set Range; 2 - start measurements)
int maxSamples = 100; // Number of point to measure
int maxSets = 50; // Time depth
float points[][]; // Measurements data
int sample; // current sample
int set; // current data set
int colors[]; // curve color
int total; // total samples acquired
boolean ready; // screen redrawn if True
int Frequency; // current Frequensy
int Range; // current Range
// Code to send command to Analyzer
void serialize(String cmd) {
int len = cmd.length();
int charPos = 0;
while (len-- != 0) {
ZERO.write(cmd.charAt(charPos));
charPos++;
}
}
// SWR computation function
// Z0 - System impedance (i.e. 50 for 50 Ohm systems)
// R - measured R value
// X - measured X value
float computeSWR(float Z0, float R, float X) {
float SWR, Gamma;
float XX = X * X;
float denominator = (R + Z0) * (R + Z0) + XX;
if (denominator == 0) {
return 1E9;
} else {
float l = (R - Z0) * (R - Z0);
float t = (l + XX);
t = t / denominator;
Gamma = sqrt(t); // always >= 0
// NOTE:
// Gamma == -1 complete negative reflection, when the line is short-circuited
// Gamma == 0 no reflection, when the line is perfectly matched
// Gamma == +1 complete positive reflection, when the line is open-circuited
if (Gamma == 1.0) {
SWR = 1E9;
} else {
SWR = (1 + Gamma) / (1 - Gamma);
}
}
// return values
if ((SWR > 200) || (Gamma > 0.99)) {
SWR = 200;
} else if (SWR < 1) {
SWR = 1;
}
return SWR;
}
void setup() {
Frequency = 115000000;
Range = 230000000;
sample = 0;
set = -1;
step = 0;
points = new float[maxSets + 1][maxSamples + 1];
colors = new int[maxSets + 1];
ready = false;
total = 0;
background(0);
stroke(120, 240, 255, 255);
strokeWeight(1);
size(640, 480, P3D);
printArray(Serial.list());
// Replace COM name with one that matches your conditions
ZERO = new Serial(this, "COM21", 38400);
ZERO.bufferUntil(13);
delay(1000);
serialize("SW0\r\n");
}
void drawSurface() {
ready = false;
lights();
float sp = 0.001 * frameCount;
camera((width / 3) * sin(sp), 0, 800, width / 2, height / 2, 0, 0, 1, 0);
background(0, 0, 0);
textSize(30);
fill(255, 255, 255);
// ---------------- Axis ---------------------
stroke(255, 255, 255, 128);
line(0, height, 0, width, height, 0);
line(0, 0, 0, 0, height, 0);
line(width, 0, 0, width, height, 0);
line(0, height, 5 * maxSets, 0, height, 0);
line(width / 2, height, 5 * maxSets, width / 2, height, 0);
line(width, height, 5 * maxSets, width, height, 0);
// ---------------- Freq. markers ----------------
stroke(255, 255, 255, 128);
line(width / 2, 0, 0, width / 2, height, 0);
textAlign(CENTER);
text(Frequency / 1E3 + " kHz", width / 2, height, 5 * maxSets);
text(((Frequency / 1E3) - (Range / 2E3)) + " kHz", 0, height, 5 * maxSets);
text(((Frequency / 1E3) + (Range / 2E3)) + " kHz", width, height, 5 * maxSets);
// ----------------- Mode title ------------------
textAlign(LEFT);
textSize(36);
text("SWR as a function of time Graph", 0, -100, 0);
textSize(30);
if (mouseY < height / 5) {
if (mouseX < width / 2) {
fill(255, 0, 0);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
fill(255, 255, 255);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
} else {
fill(255, 255, 255);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
fill(255, 0, 0);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
}
} else {
fill(255, 255, 255);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
}
// Get extremums
float minV = 1E9;
float maxV = -1E9;
for (int i = 0; i < set; i++) {
for (int j = 0; j < maxSamples + 1; j++) {
if (points[i][j] > maxV) maxV = points[i][j];
if (points[i][j] < maxV) minV = points[i][j];
}
}
println("Min = " + minV + "; Max = " + maxV);
minV = 1;
if (maxV < 2) maxV = 2;
else if (maxV < 5) maxV = 5;
else if (maxV < 10) maxV = 10;
else maxV = 100;
float hK = width / maxSamples;
float vK = height / (maxV - minV);
float zK = 2;
// ----------------- Draw horizontal markers -----------------
fill(255, 255, 255);
textAlign(RIGHT);
line(0, height - vK, 0, width, height - vK, 0); // SWR = 2
text("SWR = 2.0", 0, height - vK, 0);
line(0, height - 2 * vK, 0, width, height - 2 * vK, 0); // SWR = 3
text("SWR = 3.0", 0, height - 2 * vK, 0);
line(0, height - 4 * vK, 0, width, height - 4 * vK, 0); // SWR = 5
text("SWR = 5.0", 0, height - 4 * vK, 0);
// Plot the lines
for (int i = 0; i < set; i++) {
if (colors[i] % 5 == 0) stroke(255, 0, 0, 255 * i / set);
else stroke(120, 240, 255, 255 * i / set);
for (int j = 1; j < maxSamples + 1; j++) {
// draw only if SWR < 100.0
if (points[i][j - 1] < 100) {
line((j - 1) * hK, height - (points[i][j - 1] - 1) * vK, i * zK,
j * hK, height - (points[i][j] - 1) * vK, i * zK);
}
}
}
}
void draw() {
if (ready) {
drawSurface();
}
}
// Process incoming data
void serialEvent(Serial p) {
String inString;
inString = p.readString();
if (inString.indexOf("OK") >= 0) {
switch (step) {
case 0: serialize("FQ" + Frequency + "\r\n");
step = 1;
break;
case 1: serialize("SW" + Range + "\r\n");
step = 2;
break;
case 2: serialize("FRX"+ str(maxSamples) + "\r\n");
step = 0;
sample = 0;
if (set == maxSets) {
// shift curves back
for (int i = 1; i < maxSets + 1; i++) {
colors[i - 1] = colors[i];
for (int j = 0; j < maxSamples + 1; j++) {
points[i - 1][j] = points[i][j];
}
}
} else {
set++;
}
colors[set] = total++;
ready = true;
break;
}
} else {
float[] nums = float(split(inString, ','));
if (nums.length == 3) {
float SWR = computeSWR(50, nums[1], nums[2]);
points[set][sample] = SWR;
sample++;
}
}
}
// Change Frequency & Range values by the Mouse Wheel
void mouseWheel(MouseEvent event) {
float e = event.getCount();
if (mouseY < height / 5) {
if (mouseX < width / 2) {
// Change Freq.
if (Frequency > 1E5) {
Frequency += e * 100000;
drawSurface();
}
} else {
// Change Range
if (Range > 1E5) {
Range += e * 1E5;
drawSurface();
}
}
}
}
Important note
Please be sure that the right COM number used here:
Running the Processing sketch
After the sketch copied to the IDE editor press RUN button.
Some time later measurement results will be displayed on your screen:
Let’s compare the resulting drawings with the chart that the AntScope program draws:
To get 100% similarity, you have to play a little with a logarithmic scale.
Source files to start from
You can download source files from GitHub repository.
To be continued…