A look inside the N1201SA VNA

When working on antenna projects a VNA is essential. In the field you not want to carry tons of lab equipment, so I got myself a N1201SA. My initial expectations were low. Very low. Practically it has proven to be a very handy tool, exact enough for most of HAM related projects. We’ve done some benchmark tests, comparing the S11 results to the measurements of some serious lab equipment. The results were close in in 70cm and 2m band.

Rule #1: Don’t switch it on, take it apart! First let’s take a sneak inside:

Main components I’ve identified:

ADF4350 Datasheet Wideband Synthesizer with Integrated VCO
SA612A Datasheet Double-balanced mixer and oscillator
W25Q64FW Datasheet Serial Flash Memory
MCP6021 Datasheet Rail-to-Rail Input/Output, 10 MHz Op Amps
TP4056 Datasheet Rail-to-Rail Input/Output, 10 MHz Op Amps
STM32F103CB Datasheet Medium-density performance line ARMĀ®-based 32-bit MCU with 64 or 128 KB Flash, USB, CAN, 7 timers, 2 ADCs, 9 com. interfaces


Regarding the measurements I plan to post some more details and results. Until then I’d recommend to take a moment to watch this cool video form Andreas SpiessĀ featuring the NS1201SA:

Using the RedPitaya in a Jupyther Notebook

Using Jupyter notebooks for rapid prototyping, short proof of concepts and documentation is fun. Now I wanted to introduce some signal aquisition using the RedPitaya (+ http://pavel-demin.github.io/red-pitaya-notes/sdr-transceiver/).

Pretty simple task. Two years ago I’ve contributed a short python script to fetch samples directly from the trx project. Pretty simple to include it directly into a notebook:

fs_to_indexfs = {20000:0, 50000:1, 100000:2, 250000:3, 500000:4, 1250000:5}

class Transceiver(object):
    def __init__(self, ipaddr):
        self.p_ip_address = ipaddr
        self.control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.data_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def shutdown(self):
        print("RX Shutdown")        

    def start_rx(self, freq, fs_index, corr, n_bytes, iq_filename):
        # Setup control socket
        self.control_socket.connect((self.p_ip_address, 1001))
        self.control_socket.send(struct.pack('<I', 0))

        # Setup data socket
        self.data_socket.connect((self.p_ip_address, 1001))
        self.data_socket.send(struct.pack('<I', 1))

        # Set fc and fs
        self.control_socket.send(struct.pack('<I', 0<<28 | int((1.0 + 1e-6 * corr ) * freq)))
        self.control_socket.send(struct.pack('<I', 1<<28 | fs_index))
        fs_value = list(fs_to_indexfs.keys())[list(fs_to_indexfs.values()).index(fs_index)];

        # write aquisition parameters to console
            "...receiving fc: {0} Hz fs: {1}  / correction (ppm): {2}\n".format(freq, fs_value,
        cnt_bytes = 0
        except OSError:
        f_iq_raw=open (iq_filename, "wb")
            while cnt_bytes < n_bytes: f_iq_raw.write(self.data_socket.recv(1024)) cnt_bytes+=1024; print("fetched " + str(int(n_bytes)) + " bytes") f_iq_raw.close() self.shutdown() except Exception as ex: print(ex) sys.stderr.write(">>> rx force shutdown\n")

The acquisition looks like

trx = Transceiver(arg_addr)
start = time.time()
activeReceiver = trx;

trx.start_rx(arg_fc, fs_to_indexfs[arg_fs], 0,arg_bytes_to_fetch, arg_raw_iq_file)

end = time.time() - start
print(time.strftime("%H:%M:%S", time.gmtime(end)))

samples = np.fromfile(arg_raw_iq_file, dtype=np.complex64)
x1 = np.array(samples).astype("complex")

That’s it.
Now there is some numpy / matplotlib fun ahead. Let’s quickly render e spectrogram: